Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
HLE.c
/* A simple (real simple) High Level Event sample */ |
#include <Types.h> |
#include <memory.h> |
#include <Packages.h> |
#include <Errors.h> |
#include <quickdraw.h> |
#include <fonts.h> |
#include <dialogs.h> |
#include <windows.h> |
#include <menus.h> |
#include <events.h> |
#include <OSEvents.h> |
#include <Desk.h> |
#include <diskinit.h> |
#include <OSUtils.h> |
#include <resources.h> |
#include <toolutils.h> |
#include <AppleEvents.h> |
#include <EPPC.h> |
#include <GestaltEqu.h> |
#include <PPCToolbox.h> |
#include <Processes.h> |
/* prototypes */ |
void DoDiskEvents(long dinfo); /* hi word is error code, lo word is drive number */ |
void DrawMain(WindowPtr drawIt); |
Boolean DoSelected(long val); |
pascal Boolean MyGenericFilter(Ptr myData,HighLevelEventMsgPtr theBuffer,const TargetID *sender); |
void SendHLE(void); |
void InitAEStuff(void); |
void DoHighLevel(EventRecord *AERecord); |
void DoDaCall(MenuHandle themenu, long theit); |
pascal OSErr AEOpenHandler(AppleEvent *messagein, AppleEvent *reply, long refIn); |
Boolean BrowseForMe(void); |
pascal OSErr AEOpenDocHandler(AppleEvent *messagein, AppleEvent *reply, long refIn); |
pascal OSErr AEPrintHandler(AppleEvent *messagein, AppleEvent *reply, long refIn); |
pascal OSErr AEQuitHandler(AppleEvent *messagein, AppleEvent *reply, long refIn); |
ProcessSerialNumber gOurSN; |
#define kMBarID 128 |
#define kAppleMenu 128 |
#define kFileMenu 129 |
#define kEditMenu 130 |
#define kToolsMenu 131 |
#define kSendButton 128 |
#define kResumeMask 1 /* bit of message field for resume vs. suspend */ |
#define kBadCombo 129 |
#define kNoFind 130 |
#define kSearch 200 |
#define kFirstEvent 'MYEV' |
#define kSecondEvent '2YEV' |
MenuHandle gAppleMenuHandle, gFileMenuHandle, gEditMenuHandle, gToolMenuHandle; |
Handle gMymenu; /* my menu bar handle */ |
ControlHandle sendButton; |
typedef struct MyHLEGetter{ |
short whichData; |
Ptr specificData; |
}MyHLEGetter; |
enum{kMessageField =1,kRawData,kRefCon}; |
AEAddressDesc targetAddress; /* address of the person to get the data from */ |
TargetID theID,theID2; |
Boolean gQuit, gInBackground; |
EventRecord gERecord; |
AEDesc gTheAddress; |
WindowPtr myWindow; |
LocationNameRec theLoc; |
PortInfoRec theRec; |
PPCPortRec myPortName; |
LocationNameRec myLoc; |
PPCPortRefNum gPortRef = 0; |
PPCParamBlockRec myParamBlock; |
GetSpecificFilterUPP gGetSpecificFilterUPP; /* filter for GetSpecificHighLevelEvent */ |
Str255 displayString; |
#ifdef powerc |
QDGlobals qd; |
#endif |
main() |
{ |
WindowPtr twindow; |
ControlHandle returnedControl; |
MaxApplZone(); |
InitGraf((Ptr)&qd.thePort); |
InitFonts(); |
InitWindows(); |
InitMenus(); |
TEInit(); |
InitDialogs(nil); |
InitCursor(); |
InitAEStuff(); |
/* create the UPP for the filter to GetSpecificHighLevelEvent */ |
gGetSpecificFilterUPP = NewGetSpecificFilterProc(MyGenericFilter); |
/* set up my menu junk */ |
gMymenu = GetNewMBar(kMBarID); |
SetMenuBar(gMymenu); |
gAppleMenuHandle = GetMenuHandle(kAppleMenu); |
gFileMenuHandle = GetMenuHandle(kFileMenu); |
gEditMenuHandle = GetMenuHandle(kEditMenu); |
gToolMenuHandle = GetMenuHandle(kToolsMenu); |
AppendResMenu(gAppleMenuHandle, 'DRVR'); |
DrawMenuBar(); |
myWindow = GetNewWindow(128, nil, (WindowPtr)-1); |
do { |
WaitNextEvent(everyEvent, &gERecord, 30, nil); |
switch (gERecord.what) { |
case nullEvent: |
/* no nul processing in this sample */ |
break; |
case updateEvt: |
DrawMain((WindowPtr)gERecord.message); /* draw whatever window needs an update */ |
break; |
case mouseDown: |
/* first see where the hit was */ |
switch (FindWindow(gERecord.where, &twindow)) { |
case inDesk: /* if they hit in desk, then the process manager */ |
break; /* will switch us out, we don't need to do anything */ |
case inMenuBar: |
DoSelected(MenuSelect(gERecord.where)); |
break; |
case inSysWindow: |
/* pass to the system */ |
SystemClick(&gERecord, twindow); |
break; |
case inContent: |
GlobalToLocal(&gERecord.where); |
/* track my button as needed */ |
if (FindControl(gERecord.where, twindow, &returnedControl)) { |
if (TrackControl(returnedControl, gERecord.where, nil)) { |
} |
} |
break; |
case inDrag: |
if (twindow == FrontWindow()) |
DragWindow(twindow, gERecord.where, &qd.screenBits.bounds); |
break; |
case inGrow: |
case inGoAway: |
/* don't care */ |
break; |
} |
case mouseUp: |
/* don't care */ |
break; |
/* same action for key or auto key */ |
case keyDown: |
case autoKey: |
if (gERecord.modifiers & cmdKey) |
DoSelected(MenuKey(gERecord.message & charCodeMask)); |
break; |
case keyUp: |
/* don't care */ |
break; |
case diskEvt: |
/* I don't do anything special for disk events, this just passes them */ |
/* to a function that checks for an error on the mount */ |
DoDiskEvents(gERecord.message); |
break; |
case activateEvt: |
if (gERecord.modifiers & activeFlag) |
DrawMain((WindowPtr)gERecord.message); |
break; |
case networkEvt: |
/* don't care */ |
break; |
case driverEvt: |
/* don't care */ |
break; |
case app4Evt: |
switch ((gERecord.message >> 24) & 0x0FF) { /* high byte of message */ |
case suspendResumeMessage: /* suspend/resume is also an activate/deactivate */ |
gInBackground = (gERecord.message & kResumeMask) == 0; |
break; |
} |
break; |
case kHighLevelEvent: |
/* This dispatches high level events (AppleEvents, for example) */ |
/* to our dispatch routine. This is NEW in the event loop for */ |
/* System 7 */ |
DoHighLevel(&gERecord); |
break; |
default: |
break; |
} |
} while (gQuit != true); |
} |
/* DoDaCall opens the requested DA. It's here as a seperate routine if you'd */ |
/* like to perform some action or just know when a DA is opened in your */ |
/* layer. Can be handy to track memory problems when a DA is opened */ |
/* with an Option-open */ |
void DoDaCall(MenuHandle themenu, long theit) |
{ |
long qq; |
char DAname[255]; |
GetMenuItemText(themenu, theit, &DAname); |
qq = OpenDeskAcc(DAname); |
} |
/* end DoDaCall */ |
/* DoDiskEvents just checks the error code from the disk mount, */ |
/* and puts up the 'Format' dialog (through DIBadMount) if need be */ |
/* You can do much more here if you care about what disks are */ |
/* in the drive */ |
void DoDiskEvents(long dinfo) /* hi word is error code, lo word is drive number */ |
{ |
short hival, loval, tommy; |
Point fredpoint = { |
40, 40 |
}; |
hival = HiWord(dinfo); |
loval = LoWord(dinfo); |
if (hival != noErr) /* something happened */ { |
tommy = DIBadMount(fredpoint, dinfo); |
} |
} |
/* draws my window. Pretty simple */ |
void DrawMain(WindowPtr drawIt) |
{ |
BeginUpdate(drawIt); |
SetPort(drawIt); |
EraseRect(&drawIt->portRect); |
MoveTo(20,20); |
DrawString("\pData passed:"); |
if(displayString[0]){ |
DrawString(displayString); |
} else { |
DrawString("\p no data sent yet "); |
} |
EndUpdate(drawIt); |
} |
/* my menu action taker. It returns a Boolean which I usually ignore, but it */ |
/* mught be handy someday */ |
Boolean DoSelected(long val) |
{ |
short loval, hival; |
Boolean temp = false; |
loval = LoWord(val); |
hival = HiWord(val); |
switch (hival) { /* switch off the menu number selected */ |
case kAppleMenu: /* Apple menu */ |
if (loval != 1) { /* if this was not About, it's a DA */ |
DoDaCall(gAppleMenuHandle, loval); |
} else { |
Alert(128, nil); /* do about box */ |
} |
break; |
case kFileMenu: /* File menu */ |
gQuit = true; /* only item */ |
break; |
case kEditMenu: |
/* edit menu junk */ |
/* don't care */ |
break; |
case kToolsMenu: |
/* add all your test stuff here */ |
SendHLE(); |
break; |
} |
HiliteMenu(0); |
} |
/* InitAEStuff installs my appleevent handlers */ |
void InitAEStuff(void) |
{ |
OSErr aevtErr = noErr; |
long aLong = 0; |
Boolean gHasAppleEvents = false; |
/* Check this machine for AppleEvents. If they are not here (ie not 7.0) |
* then we exit */ |
gHasAppleEvents = (Gestalt(gestaltAppleEventsAttr, &aLong) == noErr); |
/* The following series of calls installs all our AppleEvent Handlers. |
* These handlers are added to the application event handler list that |
* the AppleEvent manager maintains. So, whenever an AppleEvent happens |
* and we call AEProcessEvent, the AppleEvent manager will check our |
* list of handlers and dispatch to it if there is one. |
*/ |
if (gHasAppleEvents) { |
aevtErr = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, |
NewAEEventHandlerProc(AEOpenHandler),0, false); |
if (aevtErr) ExitToShell(); |
aevtErr = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, |
NewAEEventHandlerProc(AEOpenDocHandler),0, false); |
if (aevtErr) ExitToShell(); |
aevtErr = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, |
NewAEEventHandlerProc(AEQuitHandler), 0, false); |
if (aevtErr) ExitToShell(); |
aevtErr = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, |
NewAEEventHandlerProc(AEPrintHandler),0, false); |
if (aevtErr) ExitToShell(); |
} |
else ExitToShell(); |
} |
/* end InitAEStuff */ |
/* I'm not doing error handling in this sample for clarities sake, you should. Hah, */ |
/* easy for me to say, huh? */ |
void DoHighLevel(EventRecord *AERecord) |
{ |
if(noErr != AEProcessAppleEvent(AERecord)){ |
/* if it was not an AppleEvent, it must be my HLE. Since in this sample I send two at once, |
we'll filter for the second one */ |
OSErr myErr; |
MyHLEGetter myData; |
OSType theMessageNow ; |
theMessageNow = kSecondEvent; |
/* set up the myData structure for the type of event I sent second */ |
myData.whichData = kMessageField; |
myData.specificData = (Ptr)&theMessageNow; |
GetSpecificHighLevelEvent(gGetSpecificFilterUPP,&myData,&myErr); |
} |
} |
/* end DoHighLevel */ |
/* This is the standard Open Application event. */ |
pascal OSErr AEOpenHandler(AppleEvent *messagein, AppleEvent *reply, long refIn) |
{ |
#pragma unused (messagein,reply,refIn) |
/* we of course don't do anything here in this simple app */ |
return(noErr); |
} |
/* end AEOpenHandler */ |
/* Open Doc, opens our documents. Remember, this can happen at application start AND */ |
/* anytime else. If your app is up and running and the user goes to the desktop, hilites one */ |
/* of your files, and double-clicks or selects Open from the finder File menu this event */ |
/* handler will get called. Which means you don't do any initialization of globals here, or */ |
/* anything else except open then doc. */ |
/* SO-- Do NOT assume that you are at app start time in this */ |
/* routine, or bad things will surely happen to you. */ |
pascal OSErr AEOpenDocHandler(AppleEvent *messagein, AppleEvent *reply, long refIn) |
{ |
#pragma unused (reply, refIn) |
/* we of course don't do anything here */ |
return(errAEEventNotHandled); /* we have no docs, so no odoc events should come to us */ |
} |
pascal OSErr AEPrintHandler(AppleEvent *messagein, AppleEvent *reply, long refIn) |
{ /* no printing handler in yet, so we'll ignore this */ |
/* the operation is functionally identical to the ODOC event, with the additon */ |
/* of calling your print routine. */ |
#pragma unused (reply,refIn) |
/* we of course don't do anything here */ |
return(errAEEventNotHandled); /* we have no docs, so no pdoc events should come to us */ |
} |
/* Standard Quit event handler, to handle a Quit event from the Finder, for example. */ |
/* ¥¥¥¥¥ DO NOT CALL EXITTOSHELL HERE ¥¥¥¥¥ or you will never have a happy life. */ |
pascal OSErr AEQuitHandler(AppleEvent *messagein, AppleEvent *reply, long refIn) |
{ |
#pragma unused (messagein,refIn) |
/* prepQuit sets the Stop flag for us. It does _NOT_ quit, you */ |
/* should NEVER quit from an AppleEvent handler. Calling */ |
/* ExitToShell here would blow things up */ |
gQuit = true; |
return(noErr); |
} |
Boolean BrowseForMe(void) |
{Str255 tWext = "\p Find Target Application"; |
Str255 txWext = "\p apps"; |
return(noErr == PPCBrowser(tWext, |
&txWext, |
false, |
&theLoc, |
&theRec, |
nil, |
nil)); |
} |
/* Send two high level events */ |
void SendHLE(void) |
{OSErr myErr; |
EventRecord eventToSend; |
EventRecord eventToSend2; |
Str255 dataToSend = "\p sample data"; |
Str255 dataToSend2 = "\p sample data2"; |
/* search for a target address. */ |
if(BrowseForMe()){ |
theID.name = theRec.name; |
theID2.name = theRec.name; |
eventToSend.what = kHighLevelEvent; |
eventToSend.message = kFirstEvent; |
eventToSend.where.h = 'IN'; |
eventToSend.where.v = 'IT'; |
myErr =PostHighLevelEvent(&eventToSend,(unsigned long)&theID,nil,(Ptr)&dataToSend,(dataToSend[0])+1,receiverIDisTargetID); |
eventToSend2.what = kHighLevelEvent; |
eventToSend2.message = kSecondEvent; |
eventToSend2.where.h = '2M'; |
eventToSend2.where.v = 'VT'; |
myErr |= PostHighLevelEvent(&eventToSend2,(unsigned long)&theID2,nil,(Ptr)&dataToSend2,(dataToSend2[0])+1,receiverIDisTargetID); |
if(myErr)DebugStr("\perr on Post"); |
} |
} |
/* |
Your filter is specified like as below, with these clarifications to the IM VI words |
The data pointer you pass (myData) is COMPLETELY up to you. It's purpose is to give your |
filter the ability to be very generic, you pass it whatever information you need to |
decide if this is the event you want. My structure for the data in this is, as a sample, |
typedef struct MyHLEGetter{ |
short whichData; |
Ptr specificData; |
}MyHLEGetter; |
The data passed in the HighLevelEventMsgPtr is as follows; |
struct HighLevelEventMsg { |
unsigned short HighLevelEventMsgHeaderLength; |
unsigned short version; |
unsigned long reserved1; |
EventRecord theMsgEvent; |
unsigned long userRefcon; |
unsigned long postingOptions; |
unsigned long msgLength; |
}; |
so you can examine any of these fields to make a determination about whether to accept the event |
or not. By changing the selector in whichData and the data in specificData, you can tell yourself |
to examine any of the fields in the HLE record passed. |
*/ |
pascal Boolean MyGenericFilter(Ptr myData,HighLevelEventMsgPtr theBuffer,const TargetID *sender) |
{ |
OSType myCheckOSType; |
Boolean acceptIt = false; |
MyHLEGetter *theSelectionData = (MyHLEGetter *)myData; /* cast for easier reading */ |
/* so since I may use this many different times, I'd add a switch... */ |
switch(theSelectionData->whichData){ |
case kMessageField: |
/* this is the only one I have implemented, tells me that this pass through the */ |
/* filter we are looking for an event record that has the message field set to */ |
/* the same value as I have in the pointer part of the struct I created and passed */ |
/* as myData */ |
/* so it's a simple check */ |
myCheckOSType = *((OSType *)theSelectionData->specificData); |
if(myCheckOSType == theBuffer->theMsgEvent.message)acceptIt = true; |
break; |
case kRawData: |
break; |
case kRefCon: |
break; |
/* and so on, switching on whatever you need to recognized the event you want. */ |
} |
/* if it is the event I want, then I'll call AcceptHighLevel inside the filter. */ |
/* you don't need to do this, you can just return a TRUE and expect the caller of */ |
/* GetSpecificHighLevelEvent to do the actual event retreival */ |
if(acceptIt){ |
TargetID sender; |
unsigned long theRefCon; |
Ptr theBuffer = nil; |
unsigned long theLength = 0; |
OSErr myErr; |
do{ |
myErr = AcceptHighLevelEvent(&sender, |
&theRefCon, |
theBuffer, |
&theLength); |
if(myErr == bufferIsSmall){ |
/* small buffer, get the size and do it again */ |
theBuffer = NewPtr(theLength); |
} else { |
WindowPtr temp; |
/* move the data into my string display area */ |
BlockMove(theBuffer,(Ptr)displayString,theLength); |
GetPort(&temp); |
SetPort(myWindow); |
InvalRect(&myWindow->portRect); |
SetPort(temp); |
break; /* on any other error, or noErr, exit the thing */ |
} |
}while(true); |
} |
return(acceptIt); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14