TweetFollow Us on Twitter

MACINTOSH C

Demonstration Program

Go to Contents
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// LowEvents.c
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// 
// This program:
//
// „  Contains a main event loop function, together with subsidiary functions which
//    perform nominal handling only of low-level and Operating System events.
//
// „  Opens a window in which the types of all received low-level and Operating System
//    events are displayed.
//
// „  Terminates when the user clicks the window's close box.
//
// Event handling is only nominal in this program because its main purpose is to
// demonstrate the basics of an application's main event loop.
//
// Programs in later chapters demonstrate the full gamut of individual event handling. 
//
// The program utilises the following resources:
//
// „  A 'WIND' resource (purgeable).
//
// „  A 'SIZE' resource with the acceptSuspendResumeEvents, doesActivateOnFGSwitch, and
//    is32BitCompatible flags set.
//  
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

// ............................................................................. includes

#include <DiskInit.h>
#include <Sound.h>
#include <ToolUtils.h>

// .............................................................................. defines

#define rWindowResource  128

#define topLeft(r)    (((Point *) &(r))[0])
#define botRight(r)   (((Point *) &(r))[1])

// ..................................................................... global variables

Boolean    gDone;
Boolean    gInBackground;
RgnHandle  gCursorRegionHdl;

// .................................................................. function prototypes

void  main            (void);
void  doInitManagers  (void);
void  doNewWindow     (void);
void  eventLoop       (void);
void  doEvents        (EventRecord *);
void  doMouseDown     (EventRecord *);
void  doUpdate        (EventRecord *);
void  doDisk          (EventRecord *);
void  doOSEvent       (EventRecord *);
void  drawEventString (Str255);
void  doAdjustCursor  (WindowPtr);

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× main

void  main(void)
{
  doInitManagers();
  doNewWindow();
  eventLoop();
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doInitManagers

void  doInitManagers(void)
{
  MaxApplZone();
  MoreMasters();

  InitGraf(&qd.thePort);
  InitFonts();
  InitWindows();
  InitMenus();
  TEInit();
  InitDialogs(NULL);

  InitCursor();
  FlushEvents(everyEvent,0);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doNewWindow

void  doNewWindow(void)
{
  WindowPtr   windowPtr;

  if(!(windowPtr = GetNewCWindow(rWindowResource,NULL,(WindowPtr) -1)))
  {
    SysBeep(10);
    ExitToShell();
  }

  SetPort(windowPtr);
  TextSize(10);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× eventLoop

void  eventLoop(void)
{
  EventRecord  eventStructure;
  Boolean      gotEvent;
  
  gDone = false;
  gCursorRegionHdl = NewRgn();
  doAdjustCursor(FrontWindow());
  
  while(!gDone)
  {
    gotEvent = WaitNextEvent(everyEvent,&eventStructure,180,gCursorRegionHdl);
    if(gotEvent)
      doEvents(&eventStructure);
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doEvent

void  doEvents(EventRecord *eventStrucPtr)
{
  switch(eventStrucPtr->what)
  {
    case mouseDown:
      drawEventString("\p   „ mouseDown");
      doMouseDown(eventStrucPtr);
      break;

    case mouseUp:
      drawEventString("\p   „ mouseUp");
      break;

    case keyDown:
      drawEventString("\p   „ keyDown");
      break;

    case autoKey:
      drawEventString("\p   „ autoKey");
      break;

    case updateEvt:
      drawEventString("\p   „ updateEvt");
      doUpdate(eventStrucPtr);
      break;

    case diskEvt:
      drawEventString("\p   „ diskEvt");
      doDisk(eventStrucPtr);
      break;

    case activateEvt:
      drawEventString("\p   „ activateEvt");
      break;

    case osEvt:
      drawEventString("\p   „ osEvt - ");
      doOSEvent(eventStrucPtr);
      break;
  }
}
  
// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doMouseDown

void  doMouseDown(EventRecord *eventStrucPtr)
{
  SInt16     partCode;
  WindowPtr  windowPtr;

  partCode = FindWindow(eventStrucPtr->where,&windowPtr);
  
  switch(partCode)
  {
    case inContent:
      if(windowPtr != FrontWindow())
        SelectWindow(windowPtr);
      break;

    case inDrag:
      DragWindow(windowPtr,eventStrucPtr->where,&qd.screenBits.bounds);
      doAdjustCursor(windowPtr);
      break;

    case inGoAway:
      if(TrackGoAway(windowPtr,eventStrucPtr->where))
        gDone = true;
      break;
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doUpdate

void  doUpdate(EventRecord *eventStrucPtr)
{
  BeginUpdate((WindowPtr)eventStrucPtr->message);
  EndUpdate((WindowPtr)eventStrucPtr->message);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doDisk

void  doDisk(EventRecord *eventStrucPtr)
{
  Point  thePoint;
  OSErr  osErr;

  if(HiWord(eventStrucPtr->message) != noErr)
  {
    SetPt(&thePoint,120,120);
    osErr = DIBadMount(thePoint,eventStrucPtr->message);
  }
  else
  {
    // Attempt to mount was successful.  Record drive number for accessing the disk, etc.
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doOSEvent

void  doOSEvent(EventRecord *eventStrucPtr)
{
  switch((eventStrucPtr->message >> 24) & 0x000000FF)
  {
    case suspendResumeMessage:
      if((eventStrucPtr->message & resumeFlag) == 1)
      {        
        SetCursor(&qd.arrow);
        gInBackground = false;
        DrawString("\pResume event");
      }
      else
      {
        gInBackground = true;
        DrawString("\pSuspend event");
      }
      break;
        
    case mouseMovedMessage:
      doAdjustCursor(FrontWindow());
      DrawString("\pMouse-moved event");
      break;
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× drawEventString

void  drawEventString(Str255 eventString)
{
  RgnHandle  tempRegion;
  WindowPtr  windowPtr;
  
  windowPtr = FrontWindow();
  tempRegion = NewRgn();

  ScrollRect(&windowPtr->portRect,0,-15,tempRegion);
  DisposeRgn(tempRegion);
  
  MoveTo(8,291);
  DrawString(eventString);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doAdjustCursor

void  doAdjustCursor(WindowPtr frontWindow)
{
  RgnHandle  myArrowRegion;
  RgnHandle  myIBeamRegion;
  Rect       cursorRect;
  Point      mousePt;

  myArrowRegion = NewRgn();
  myIBeamRegion = NewRgn();  
  SetRectRgn(myArrowRegion,-32768,-32768,32766,32766);
  
  cursorRect = frontWindow->portRect;
  LocalToGlobal(&topLeft(cursorRect));
  LocalToGlobal(&botRight(cursorRect));  

  RectRgn(myIBeamRegion,&cursorRect);
  DiffRgn(myArrowRegion,myIBeamRegion,myArrowRegion);
  
  GetMouse(&mousePt);
  LocalToGlobal(&mousePt);
  if(PtInRgn(mousePt,myIBeamRegion))
  {
    SetCursor(*(GetCursor(iBeamCursor)));
    CopyRgn(myIBeamRegion,gCursorRegionHdl);
  }
  else
  {
    SetCursor(&qd.arrow);
    CopyRgn(myArrowRegion,gCursorRegionHdl);
  }
  
  DisposeRgn(myArrowRegion);
  DisposeRgn(myIBeamRegion);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

Demonstration Program Comments

When the program is run, the user should move the mouse cursor inside and outside the
window, click the mouse inside and outside the window, drag the window, press and release
keyboard keys, and insert initialised and uninitialised disks, noting the types of events
generated by these actions as printed on the scrolling display inside the window.

The user should also note the basic window deactivation and activation which occurs when
the mouse is clicked outside, and then inside the window.

The program may be terminated by a click in the window's go-away box.

The general "flow" of the program is illustrated in the flow chart at Fig 4.

(LowEvents flowchart)

#define

rWindowResource establishes a constant for the ID of the 'WIND' resource.

The remaining two lines define two common macros.  The first converts the top and left
fields of a Rect to a Point.  The second converts the bottom and right field of a Rect to
a Point.

Global Variables

The global variable gDone controls the termination of the main event loop and thus of the
program.  gInBackground will be set to true when the application is about to move to the
background and to false when the application returns to the foreground. gCursorRegionHdl
will be assigned the handle to a region to be passed in the mouseRgn parameter of the
WaitNextEvent function.

main

The main function calls the application-defined functions for initialising the system
software managers and creating the window.  It then calls the function containing the
main event loop.

doInitManagers

doInitManagers is the standard system software managers initialisation function which
will be used in all demonstration programs.

Note that the call to FlushEvents at has now been added to this function.  FlushEvents
empties the Operating System event queue of any low-level events left unprocessed by
another application, for example, any mouse-down or keyboard events that the user may
have entered while this program was being launched.

doNewWindow

The function doNewWindow opens the window in which the types of low-level and Operating
System events will be printed as they occur.  The 'WIND' resource passed as the first
parameter specifies that the window has a go-away box and a title (drag) bar.  The
window's graphics port is set as the current port for drawing and the text size is set to
10 points.

eventLoop

eventLoop is the main event loop.

The global variable gDone is set to false before the event loop is entered.  This
variable will be set to true when the user clicks on the window's go-away box.  The event
loop (the while loop) terminates when gDone is set to true.

The calls to NewRgn and doAdjustCursor have to do with the generation of mouse-moved
events.  The NewRgn call allocates storage for a Region structure and initializes the
contents of the region to make it an empty region.  As will be seen, this first call to
doAdjustCursor defines two regions (one for the arrow cursor and one for the I-Beam
cursor) and copies the handle to one of them (depending on the current position of the
mouse cursor) to the global variable gCursorRegionHandle.

In the call to WaitNextEvent:

*   The event mask everyEvent ensures that all types of low-level and Operating System
    events will be returned to the application (except keyUp events, which are masked 
    out by the system event mask).

*   eventStructure is the EventRecord structure which, when WaitNextEvent returns, will
    contain information about the event.

*   180 represents the number of ticks for which the application agrees to relinquish 
    the processor if no events are pending for it.  180 ticks equates to about three
    seconds.

*   If the cursor is now not within the region passed in the cursorRegion parameter, 
    a mouse-moved event will be generated immediately.

WaitNextEvent returns true if an event was pending, otherwise it returns NULL.  If an
event was pending, the program branches to doEvent to determine the type of event and
handle the event according to its type.

doEvents

doEvents handles some events to finality and performs initial handling of others.

On return from WaitNextEvent, the what field of the event structure contains an unsigned
short integer which indicates the type of event received.  The doEvent function isolates
the type of event and switches according to that type.

In this demonstration, the action taken in every case is to print the type of event in
the window.  In addition, and in the case of mouse-down, update, disk and Operating
System events only, calls to individual event handling functions are made.

Note that, in the case of an Operating System event, doEvent will only print "osEvt - "
in the window.  At this stage, the program has not yet established whether the event is a
suspend, resume or mouse-moved event.

Note also that:

*   The inclusion of the key-up event handling would be pointless, since key-up events
    are masked out by the Operating System.

*   Only one activate event will ever be received when the program is run (that is, 
    when the window opens), the reasons being that only one window is ever open and the
    doesActivateOnFGSwitch flag in the 'SIZE' resource is set.  This latter means that
    activate events will not accompany suspend and resume events.

doMouseDown

The function doMouseDown handles mouse-down events to completion.

FindWindow is called to get a pointer to the window in which the event occurred and a
"part code" which indicates the part of that window in which the mouse-down occurred. 
The function then switches according to that part code.

The inContent case deals with a mouse-down in a window's content region.  FrontWindow
returns a pointer to the frontmost window.  If this is not the same as the pointer in the
event structure's message field, SelectWindow is called to generate activate events and
to perform basic window activation and deactivation.  (Actually, SelectWindow will never
be called in this demonstration because the program only opens one window, which is
always the front window.)

The inDrag case deals a mouse-down in the window's drag bar.  In this case, control is
handed over to DragWindow, which tracks the mouse and drags the window according to mouse
movement until the mouse button is released.  DragWindow requires a boundary rectangle
limiting the area in which the window can be dragged.  This is supplied in the third
argument which, in this case, is established by the bounds field of the QuickDraw global
variable screenBits.  screenBits.bounds contains a rectangle which encloses the main
screen.

The regions controlling the generation of mouse-moved events are defined in global
coordinates.  The region for the I-Beam cursor is based on the window's port rectangle. 
Accordingly, when the window is moved, the new location of the port rectangle, in global
coordinates, must be re-calculated so that the arrow cursor and I-Beam cursor regions may
be re-defined.  The call to doAdjustCursor re-defines these regions for the new window
location and copies the handle to one of them, depending on the current location of the
mouse cursor, to the global variable gCursorRegionHandle.  (Note that this call to
doAdjustCursor is also required, for the same reason, when a window is re-sized or
zoomed.)

The inGoAway case deals with the case of a mouse-down in the go-away box.  In this case,
control is handed over to TrackGoAway, which tracks the mouse while the button remains
down.  When the button is released, TrackGoAway returns true if the cursor is still
inside the go-away box, in which case the global variable gDone is set to true,
terminating the event loop and the program.

doUpdate

The function doUpdate handles update events to completion.

Although no window updating is performed by this program, it is nonetheless necessary to
call BeginUpdate because, amongst other things, BeginUpdate clears the update region,
thus preventing the generation of an unending stream of update events.  The call to
EndUpdate always concludes a call to BeginUpdate, undoing the results of the
visible/update region manipulations of the latter.

doDisk

doDisk further processes a disk event.  Many applications quite reasonably ignore
unexpected disk-inserted events; however, the function doDisk is included in this
demonstration to illustrate the basics of dealing with such occurrences.

In the case of a diskEvt event, the message field of the event structure contains the
drive number in bits 0-15 and the File Manager result code in bits 16-31.  At the first
line, the high word is tested.  If it indicates that the volume was not successfully
mounted, DIBadMount is called to inform the user via dialog box shown at Fig 5.

(Dialog box invoked by DIBadMount)

DIBadMount retains control until the disk is formatted (if the user clicks in the OK box)
or until the user clicks in the Cancel box.

The call to SetPt controls the positioning of the top left corner of the dialog box on
the screen.

doOSEvent

doOSEvent first determines whether the Operating System event passed to it is a
suspend/resume event or a mouse-moved event by examining bits 24-31 of the message field. 
It then switches according to that determination.

In the case of a suspend/resume event, a further examination of the message field
establishes whether the event was a suspend event or a resume event.  The global
variable gInBackground is set to true or false accordingly.    In the case of a
resume event, the call to SetCursor ensures that the cursor will be set to the arrow
cursor shape when the application comes to the foreground.

In the case of a mouse-moved event (which occurs when the mouse cursor has moved outside
the region whose handle is currently being passed in WaitNextEvent's mouseRgn parameter),
doAdjustCursor is called to change the region passed in the mouseRgn parameter according
to the current location of the mouse.

drawEventString

drawEventString is incidental to the demonstration.  It simply prints text in the window
indicating when the call to WaitNextEvent is made and when the various types of events
are received.  ScrollRect scrolls the contents of the current graphics port within the
rectangle specified in the first parameter.  The second parameter specifies the number of
pixels to be scrolled to the right and the third parameter specifies the number of pixels
to scroll vertically, in this case 15 up.

doAdjustCursor

doAdjustCursor's primary purpose in this particular demonstration is to force the
generation of mouse-moved events.  The fact that it also changes the cursor shape simply
reflects the fact that changing the cursor shape is usually the sole reason for
generating mouse-moved events in the first place.

Basically, the function establishes two regions (the calls to NewRgn), one describing the
content area of the window (in global coordinates) and the other everything outside that. 
The location of the cursor is then ascertained by the call to GetMouse.  If the cursor is
in the content area of the window (the I-Beam region), the cursor is set to the I-Beam
shape and the handle to the I-Beam region is copied to the global variable passed in the
mouseRgn parameter in the WaitNextEvent call in the eventLoop function  If the cursor is
in the other region (the arrow region), the cursor is set to the normal arrow shape and
the arrow region is copied to the global variable passed in the mouseRgn parameter.

GetCursor reads in the system 'CURS' resource specified by the constant iBeamCursor and
returns a handle to the 68-byte Cursor structure created by the call.  The parameter for
a SetCursor call is required to be the address of a Cursor structure.  Dereferencing the
handle once provides that address.

WaitNextEvent, of course, returns a mouse-moved event only when the cursor moves outside
the "current" region, the handle to which is passed in the mouseRgn parameter of the
WaitNextEvent call.  Only one mouse-moved event, rather than a stream of mouse-moved
events, will be generated when the cursor is moved outside the "current" region because: 

*   The mouse-moved event will cause doAdjustCursor to be called.

*   doAdjustCursor will thus reset the "current" region to the region in which the 
    cursor is now located.

The cursor and cursor adjustment aspects, as opposed to the region-swapping aspects, of
the doAdjustCursor function are incidental to the demonstration.  These aspects are
addressed in more detail at Chapter 13 - Offscreen Graphics Worlds, Pictures, Cursors,
and Icons.
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Six fantastic ways to spend National Vid...
As if anyone needed an excuse to play games today, I am about to give you one: it is National Video Games Day. A day for us to play games, like we no doubt do every day. Let’s not look a gift horse in the mouth. Instead, feast your eyes on this... | Read more »
Old School RuneScape players turn out in...
The sheer leap in technological advancements in our lifetime has been mind-blowing. We went from Commodore 64s to VR glasses in what feels like a heartbeat, but more importantly, the internet. It can be a dark mess, but it also brought hundreds of... | Read more »
Today's Best Mobile Game Discounts...
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »
Nintendo and The PokƩmon Company's...
Unless you have been living under a rock, you know that Nintendo has been locked in an epic battle with Pocketpair, creator of the obvious PokĆ©mon rip-off Palworld. Nintendo often resorts to legal retaliation at the drop of a hat, but it seems this... | Read more »
Apple exclusive mobile games don’t make...
If you are a gamer on phones, no doubt you have been as distressed as I am on one huge sticking point: exclusivity. For years, Xbox and PlayStation have done battle, and before this was the Sega Genesis and the Nintendo NES. On console, it makes... | Read more »
Regionally exclusive events make no sens...
Last week, over on our sister site AppSpy, I babbled excitedly about the PokĆ©mon GO Safari Days event. You can get nine Eevees with an explorer hat per day. Or, can you? Specifically, you, reader. Do you have the time or funds to possibly fly for... | Read more »
As Jon Bellamy defends his choice to can...
Back in March, Jagex announced the appointment of a new CEO, Jon Bellamy. Mr Bellamy then decided to almost immediately paint a huge target on his back by cancelling the Runescapes Pride event. This led to widespread condemnation about his perceived... | Read more »
Marvel Contest of Champions adds two mor...
When I saw the latest two Marvel Contest of Champions characters, I scoffed. Mr Knight and Silver Samurai, thought I, they are running out of good choices. Then I realised no, I was being far too cynical. This is one of the things that games do best... | Read more »
Grass is green, and water is wet: PokƩmo...
It must be a day that ends in Y, because PokĆ©mon Trading Card Game Pocket has kicked off its Zoroark Drop Event. Here you can get a promo version of another card, and look forward to the next Wonder Pick Event and the next Mass Outbreak that will be... | Read more »
Enter the Gungeon review
It took me a minute to get around to reviewing this game for a couple of very good reasons. The first is that Enter the Gungeon's style of roguelike bullet-hell action is teetering on the edge of being straight-up malicious, which made getting... | Read more »

Price Scanner via MacPrices.net

Take $150 off every Apple 11-inch M3 iPad Air
Amazon is offering a $150 discount on 11-inch M3 WiFi iPad Airs right now. Shipping is free: – 11″ 128GB M3 WiFi iPad Air: $449, $150 off – 11″ 256GB M3 WiFi iPad Air: $549, $150 off – 11″ 512GB M3... Read more
Apple iPad minis back on sale for $100 off MS...
Amazon is offering $100 discounts (up to 20% off) on Apple’s newest 2024 WiFi iPad minis, each with free shipping. These are the lowest prices available for new minis among the Apple retailers we... Read more
Apple’s 16-inch M4 Max MacBook Pros are on sa...
Amazon has 16-inch M4 Max MacBook Pros (Silver and Black colors) on sale for up to $410 off Apple’s MSRP right now. Shipping is free. Be sure to select Amazon as the seller, rather than a third-party... Read more
Red Pocket Mobile is offering a $150 rebate o...
Red Pocket Mobile has new Apple iPhone 17’s on sale for $150 off MSRP when you switch and open up a new line of service. Red Pocket Mobile is a nationwide MVNO using all the major wireless carrier... Read more
Switch to Verizon, and get any iPhone 16 for...
With yesterday’s introduction of the new iPhone 17 models, Verizon responded by running ā€œon usā€ promos across much of the iPhone 16 lineup: iPhone 16 and 16 Plus show as $0/mo for 36 months with bill... Read more
Here is a summary of the new features in Appl...
Apple’s September 2025 event introduced major updates across its most popular product lines, focusing on health, performance, and design breakthroughs. The AirPods Pro 3 now feature best-in-class... Read more
Apple’s Smartphone Lineup Could Use A Touch o...
COMMENTARY – Whatever happened to the old adage, ā€œless is moreā€? Apple’s smartphone lineup. — which is due for its annual refresh either this month or next (possibly at an Apple Event on September 9... Read more
Take $50 off every 11th-generation A16 WiFi i...
Amazon has Apple’s 11th-generation A16 WiFi iPads in stock on sale for $50 off MSRP right now. Shipping is free: – 11″ 11th-generation 128GB WiFi iPads: $299 $50 off MSRP – 11″ 11th-generation 256GB... Read more
Sunday Sale: 14-inch M4 MacBook Pros for up t...
Don’t pay full price! Amazon has Apple’s 14-inch M4 MacBook Pros (Silver and Black colors) on sale for up to $220 off MSRP right now. Shipping is free. Be sure to select Amazon as the seller, rather... Read more
Mac mini with M4 Pro CPU back on sale for $12...
B&H Photo has Apple’s Mac mini with the M4 Pro CPU back on sale for $1259, $140 off MSRP. B&H offers free 1-2 day shipping to most US addresses: – Mac mini M4 Pro CPU (24GB/512GB): $1259, $... Read more

Jobs Board

All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.