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

Dispatch is bringing its superhero shena...
If you were anywhere online last year, then you would have no doubt heard of Dispatch, and been flooded with videos of it. With its star-studded cast and unrelenting humour, it fast became a hit, racking up over four million downloads. And it is a... | Read more »
5 reasons to sink your teeth into VAMPIR...
On October 28, Netmarble will globally launch VAMPIR, its latest and most macabre MMORPG, plunging players into a Gothic world of vampires, sorcerers and cursed creatures of the night. Built around blood, fear and large scale battles, the MMORPG... | Read more »
Gundam Blitz Order opens registration fo...
It would appear that video game developers are delving into my childhood and making idle games from my favourite TV shows. First, Bandai completely whiffed with Digimon Up, perhaps the single biggest miss of a beloved franchise in recent memory. Now... | Read more »
The Traitors Interactive Game lets you l...
With the second season of The Celebrity Traitors in full swing, the world is once more enthralled by just how good a liar our heroes are. I don’t watch it, but I hope James Blunt wins. The premise does intrigue me, however, and thanks to Fusebox... | Read more »
Dissidia Duellum Final Fantasy brings in...
For all his successful manipulations and successes through Final Fantasy 9, I never really liked Kuja. And it's not like I dislike him; more I nothing him. But that's just me; the vast majority of the fandom adore him, and they are in luck, for Kuja... | Read more »
Combo Quest (Games)
Combo Quest 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: Combo Quest is an epic, time tap role-playing adventure. In this unique masterpiece, you are a knight on a heroic quest to retrieve... | Read more »
Hero Emblems (Games)
Hero Emblems 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: ** 25% OFF for a limited time to celebrate the release ** ** Note for iPhone 6 user: If it doesn't run fullscreen on your device... | Read more »
Puzzle Blitz (Games)
Puzzle Blitz 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Puzzle Blitz is a frantic puzzle solving race against the clock! Solve as many puzzles as you can, before time runs out! You have... | Read more »
Sky Patrol (Games)
Sky Patrol 1.0.1 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0.1 (iTunes) Description: 'Strategic Twist On The Classic Shooter Genre' - Indie Game Mag... | Read more »
The Princess Bride - The Official Game...
The Princess Bride - The Official Game 1.1 Device: iOS Universal Category: Games Price: $3.99, Version: 1.1 (iTunes) Description: An epic game based on the beloved classic movie? Inconceivable! Play the world of The Princess Bride... | Read more »

Price Scanner via MacPrices.net

Amazon Offers 14″ M5 Pro MacBook Pro for $206...
Amazon is offering another opportunity to save on Apple’s 14-inch M5 Pro MacBook Pro, with new offers on the Space Black configuration with 24GB RAM and a 1TB SSD starting at just $2069.10. Apple... Read more
Amazon Takes $900 Off 16″ M5 Max MacBook Pro,...
Amazon has expanded its latest sale on Apple’s 16-inch M5 Max MacBook Pro, dropping the Space Black configuration with 36GB RAM and a 2TB SSD to just $3499. Apple sells this configuration for $4399,... Read more
B&H Slashes Up to $1400 Off M4 Pro Mac mi...
B&H is offering new clearance discounts on select previous-generation Apple Mac minis, with savings of up to $1400 on high-end M4 Pro configurations. The sale includes the standard M4 Mac mini... Read more
B&H Slashes $400 Off 14″ M5 Pro MacBook P...
B&H has launched a substantial new discount on Apple’s 14-inch M5 Pro MacBook Pro, taking $400 off the Space Black configuration with 24GB RAM and a 1TB SSD. Regularly priced at $2499, the 14-... Read more
Take $50 off iPad at Amazon, prices start at...
Amazon again has 11th-generation A16 WiFi iPads in stock on sale for $50 off MSRP, starting at $399. Shipping is free: – 11″ 11th-generation 128GB WiFi iPads: $399 $50 off MSRP – 11″ 11th-generation... Read more
Amazon Lowers 13″ M5 MacBook Air to $1149, No...
Amazon has increased its discounts on Apple’s 13-inch M5 MacBook Air, dropping the standard 16GB/512GB configuration to just $1149. Apple sells this configuration for $1299, so Amazon’s latest price... Read more
Amazon and B&H Slash Mac mini Prices, M6...
Amazon and B&H are offering new discounts on Apple’s M6 and M5 Pro Mac minis, with savings reaching $149.01 and prices starting at just $779. Configuration Apple MSRP Retailer Sale Price Savings... Read more
Amazon Takes Up to $150 Off 15″ M5 MacBook Ai...
Amazon has launched new discounts on Apple’s 15-inch M5 MacBook Airs, with savings of up to $150 and prices starting at just $1399. Configuration Apple MSRP Amazon Price Savings 15″ M5 16GB/512GB $... Read more
Amazon Takes Up to $150 Off 13″ M5 MacBook Ai...
Amazon has launched a new round of discounts on Apple’s 13-inch M5 MacBook Airs, with savings of up to $150 and prices starting at just $1199. Configuration Apple MSRP Amazon Price Savings 13″ M5... Read more
Verizon Launches iPhone Duo Pre-Orders With N...
Verizon has opened pre-orders for Apple’s new iPhone Duo, the company’s first foldable iPhone, with prices starting at $1999.99 and a new no-trade-in promotion available through its Simplicity Plan.... Read more

Jobs Board

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