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

Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »
Top 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 »
Marvel Future Fight celebrates nine year...
Announced alongside an advertising image I can only assume was aimed squarely at myself with the prominent Deadpool and Odin featured on it, Netmarble has revealed their celebrations for the 9th anniversary of Marvel Future Fight. The Countdown... | Read more »
HoYoFair 2024 prepares to showcase over...
To say Genshin Impact took the world by storm when it was released would be an understatement. However, I think the most surprising part of the launch was just how much further it went than gaming. There have been concerts, art shows, massive... | Read more »
Explore some of BBCs' most iconic s...
Despite your personal opinion on the BBC at a managerial level, it is undeniable that it has overseen some fantastic British shows in the past, and now thanks to a partnership with Roblox, players will be able to interact with some of these... | Read more »
Play Together teams up with Sanrio to br...
I was quite surprised to learn that the massive social network game Play Together had never collaborated with the globally popular Sanrio IP, it seems like the perfect team. Well, this glaring omission has now been rectified, as that instantly... | Read more »
Dark and Darker Mobile gets a new teaser...
Bluehole Studio and KRAFTON have released a new teaser trailer for their upcoming loot extravaganza Dark and Darker Mobile. Alongside this look into the underside of treasure hunting, we have received a few pieces of information about gameplay... | Read more »

Price Scanner via MacPrices.net

Amazon is offering a $200 discount on 14-inch...
Amazon has 14-inch M3 MacBook Pros in stock and on sale for $200 off MSRP. Shipping is free. Note that Amazon’s stock tends to come and go: – 14″ M3 MacBook Pro (8GB RAM/512GB SSD): $1399.99, $200... Read more
Sunday Sale: 13-inch M3 MacBook Air for $999,...
Several Apple retailers have the new 13″ MacBook Air with an M3 CPU in stock and on sale today for only $999 in Midnight. These are the lowest prices currently available for new 13″ M3 MacBook Airs... Read more
Multiple Apple retailers are offering 13-inch...
Several Apple retailers have 13″ MacBook Airs with M2 CPUs in stock and on sale this weekend starting at only $849 in Space Gray, Silver, Starlight, and Midnight colors. These are the lowest prices... Read more
Roundup of Verizon’s April Apple iPhone Promo...
Verizon is offering a number of iPhone deals for the month of April. Switch, and open a new of service, and you can qualify for a free iPhone 15 or heavy monthly discounts on other models: – 128GB... Read more
B&H has 16-inch MacBook Pros on sale for...
Apple 16″ MacBook Pros with M3 Pro and M3 Max CPUs are in stock and on sale today for $200-$300 off MSRP at B&H Photo. Their prices are among the lowest currently available for these models. B... Read more
Updated Mac Desktop Price Trackers
Our Apple award-winning Mac desktop price trackers are the best place to look for the lowest prices and latest sales on all the latest computers. Scan our price trackers for the latest information on... Read more
9th-generation iPads on sale for $80 off MSRP...
Best Buy has Apple’s 9th generation 10.2″ WiFi iPads on sale for $80 off MSRP on their online store for a limited time. Prices start at only $249. Sale prices for online orders only, in-store prices... Read more
15-inch M3 MacBook Airs on sale for $100 off...
Best Buy has Apple 15″ MacBook Airs with M3 CPUs on sale for $100 off MSRP on their online store. Prices valid for online orders only, in-store prices may vary. Order online and choose free shipping... Read more
24-inch M3 iMacs now on sale for $150 off MSR...
Amazon is now offering a $150 discount on Apple’s new M3-powered 24″ iMacs. Prices start at $1149 for models with 8GB of RAM and 256GB of storage: – 24″ M3 iMac/8-core GPU/8GB/256GB: $1149.99, $150... Read more
15-inch M3 MacBook Airs now on sale for $150...
Amazon is now offering a $150 discount on Apple’s new M3-powered 15″ MacBook Airs. Prices start at $1149 for models with 8GB of RAM and 256GB of storage: – 15″ M3 MacBook Air/8GB/256GB: $1149.99, $... Read more

Jobs Board

Early Preschool Teacher - Glenda Drive/ *Appl...
Early Preschool Teacher - Glenda Drive/ Apple ValleyTeacher Share by Email Share on LinkedIn Share on Twitter Read more
Retail Assistant Manager- *Apple* Blossom Ma...
Retail Assistant Manager- APPLE BLOSSOM MALL Brand: Bath & Body Works Location: Winchester, VA, US Location Type: On-site Job ID: 04225 Job Area: Store: Management Read more
Housekeeper, *Apple* Valley Village - Cassi...
Apple Valley Village Health Care Center, a senior care campus, is hiring a Part-Time Housekeeper to join our team! We will train you for this position! In this role, Read more
Sonographer - *Apple* Hill Imaging Center -...
Sonographer - Apple Hill Imaging Center - Evenings Location: York Hospital, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now See Read more
Senior Software Engineer - *Apple* Fundamen...
…center of Microsoft's efforts to empower our users to do more. The Apple Fundamentals team focused on defining and improving the end-to-end developer experience in Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.