/* * tkMacOSXHLEvents.c -- * * Implements high level event support for the Macintosh. Currently, * the only event that really does anything is the Quit event. * * Copyright (c) 1995-1997 Sun Microsystems, Inc. * Copyright 2001, Apple Computer, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * RCS: @(#) $Id: tkMacOSXHLEvents.c,v 1.5.2.4 2004/03/17 19:35:41 wolfsuit Exp $ */ #include "tkMacOSXPort.h" #include "tkMacOSXInt.h" #include /* * This is a Tcl_Event structure that the Quit AppleEvent handler * uses to schedule the tkReallyKillMe function. */ typedef struct KillEvent { Tcl_Event header; /* Information that is standard for * all events. */ Tcl_Interp *interp; /* Interp that was passed to the * Quit AppleEvent */ } KillEvent; /* * Static functions used only in this file. */ static OSErr QuitHandler (const AppleEvent * event, AppleEvent * reply, long handlerRefcon); static OSErr OappHandler (const AppleEvent * event, AppleEvent * reply, long handlerRefcon); static OSErr RappHandler (const AppleEvent * event, AppleEvent * reply, long handlerRefcon); static OSErr OdocHandler (const AppleEvent * event, AppleEvent * reply, long handlerRefcon); static OSErr PrintHandler (const AppleEvent * event, AppleEvent * reply, long handlerRefcon); static OSErr ScriptHandler (const AppleEvent * event, AppleEvent * reply, long handlerRefcon); static OSErr PrefsHandler (const AppleEvent * event, AppleEvent * reply, long handlerRefcon); static int MissedAnyParameters _ANSI_ARGS_((const AppleEvent *theEvent)); static int ReallyKillMe _ANSI_ARGS_((Tcl_Event *eventPtr, int flags)); static OSErr FSRefToDString _ANSI_ARGS_((const FSRef *fsref, Tcl_DString *ds)); OSStatus ApplicationCarbonEventsHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData); OSStatus ApplicationCarbonEventsHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData) { Tcl_CmdInfo dummy; Tcl_Interp *interp = (Tcl_Interp *) inUserData; /* * This is a bit of a hack. We get "show" events both when we come back * from being hidden, and whenever we are activated. I only want to run the * "show" proc when we have been hidden already, not as a substitute for * . So I use this toggle... */ static int toggleHide = 0; switch(GetEventKind (inEvent)) { case kEventAppHidden: /* * Don't bother if we don't have an interp or * the show preferences procedure doesn't exist. */ toggleHide = 1; if ((interp == NULL) || (Tcl_GetCommandInfo(interp, "::tk::mac::OnHide", &dummy)) == 0) { return eventNotHandledErr; } Tcl_GlobalEval(interp, "::tk::mac::OnHide"); break; case kEventAppShown: if (toggleHide == 1) { toggleHide = 0; if ((interp == NULL) || (Tcl_GetCommandInfo(interp, "::tk::mac::OnShow", &dummy)) == 0) { return eventNotHandledErr; } Tcl_GlobalEval(interp, "::tk::mac::OnShow"); } break; default: break; } return eventNotHandledErr; } /* *---------------------------------------------------------------------- * * TkMacOSXInitAppleEvents -- * * Initilize the Apple Events on the Macintosh. This registers the * core event handlers. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ void TkMacOSXInitAppleEvents( Tcl_Interp *interp) /* Interp to handle basic events. */ { OSErr err; AEEventHandlerUPP OappHandlerUPP, RappHandlerUPP, OdocHandlerUPP, PrintHandlerUPP, QuitHandlerUPP, ScriptHandlerUPP, PrefsHandlerUPP; const EventTypeSpec inAppEventTypes[] = { {kEventClassApplication, kEventAppHidden}, {kEventClassApplication, kEventAppShown}}; UInt32 inNumTypes = 2; /* * Install event handlers for the core apple events. */ QuitHandlerUPP = NewAEEventHandlerUPP(QuitHandler); err = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, QuitHandlerUPP, (long) interp, false); OappHandlerUPP = NewAEEventHandlerUPP(OappHandler); err = AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, OappHandlerUPP, (long) interp, false); RappHandlerUPP = NewAEEventHandlerUPP(RappHandler); err = AEInstallEventHandler(kCoreEventClass, kAEReopenApplication, RappHandlerUPP, (long) interp, false); OdocHandlerUPP = NewAEEventHandlerUPP(OdocHandler); err = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, OdocHandlerUPP, (long) interp, false); PrintHandlerUPP = NewAEEventHandlerUPP(PrintHandler); err = AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, PrintHandlerUPP, (long) interp, false); PrefsHandlerUPP = NewAEEventHandlerUPP(PrefsHandler); err = AEInstallEventHandler(kCoreEventClass, kAEShowPreferences, PrefsHandlerUPP, (long) interp, false); if (interp != NULL) { ScriptHandlerUPP = NewAEEventHandlerUPP(ScriptHandler); err = AEInstallEventHandler(kAEMiscStandards, kAEDoScript, ScriptHandlerUPP, (long) interp, false); } InstallEventHandler(GetApplicationEventTarget(), NewEventHandlerUPP (ApplicationCarbonEventsHandler), inNumTypes, inAppEventTypes, (void *) interp, NULL); } /* *---------------------------------------------------------------------- * * TkMacOSXDoHLEvent -- * * Dispatch incomming highlevel events. * * Results: * None. * * Side effects: * Depends on the incoming event. * *---------------------------------------------------------------------- */ int TkMacOSXDoHLEvent(EventRecord *theEvent) { return AEProcessAppleEvent(theEvent); } /* *---------------------------------------------------------------------- * * QuitHandler, OappHandler, etc. -- * * These are the core Apple event handlers. Only the Quit event does * anything interesting. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ OSErr QuitHandler (const AppleEvent * event, AppleEvent * reply, long handlerRefcon) { Tcl_Interp *interp = (Tcl_Interp *) handlerRefcon; KillEvent *eventPtr; /* * Call the exit command from the event loop, since you are not supposed * to call ExitToShell in an Apple Event Handler. We put this at the head * of Tcl's event queue because this message usually comes when the Mac is * shutting down, and we want to kill the shell as quickly as possible. */ eventPtr = (KillEvent *) ckalloc(sizeof(KillEvent)); eventPtr->header.proc = ReallyKillMe; eventPtr->interp = interp; Tcl_QueueEvent((Tcl_Event *) eventPtr, TCL_QUEUE_HEAD); return noErr; } static OSErr OappHandler (const AppleEvent * event, AppleEvent * reply, long handlerRefcon) { return noErr; } static OSErr RappHandler (const AppleEvent * event, AppleEvent * reply, long handlerRefcon) { ProcessSerialNumber thePSN = {0, kCurrentProcess}; return SetFrontProcess(&thePSN); } /* Called when the user selects 'Preferences...' in MacOS X */ static OSErr PrefsHandler (const AppleEvent * event, AppleEvent * reply, long handlerRefcon) { Tcl_CmdInfo dummy; Tcl_Interp *interp = (Tcl_Interp *) handlerRefcon; /* * Don't bother if we don't have an interp or * the show preferences procedure doesn't exist. */ if ((interp == NULL) || (Tcl_GetCommandInfo(interp, "::tk::mac::ShowPreferences", &dummy)) == 0) { return noErr; } Tcl_GlobalEval(interp, "::tk::mac::ShowPreferences"); return noErr; } static OSErr OdocHandler (const AppleEvent * event, AppleEvent * reply, long handlerRefcon) { Tcl_Interp *interp = (Tcl_Interp *) handlerRefcon; AEDescList fileSpecList; FSRef file; OSErr err; DescType type; Size actual; long count; AEKeyword keyword; long index; Tcl_DString command; Tcl_DString pathName; Tcl_CmdInfo dummy; /* * Don't bother if we don't have an interp or * the open document procedure doesn't exist. */ if ((interp == NULL) || (Tcl_GetCommandInfo(interp, "::tk::mac::OpenDocument", &dummy)) == 0) { return noErr; } /* * If we get any errors wil retrieving our parameters * we just return with no error. */ err = AEGetParamDesc(event, keyDirectObject, typeAEList, &fileSpecList); if (err != noErr) { return noErr; } err = MissedAnyParameters(event); if (err != noErr) { return noErr; } err = AECountItems(&fileSpecList, &count); if (err != noErr) { return noErr; } Tcl_DStringInit(&command); Tcl_DStringAppend(&command, "::tk::mac::OpenDocument", -1); for (index = 1; index <= count; index++) { err = AEGetNthPtr(&fileSpecList, index, typeFSRef, &keyword, &type, (Ptr) &file, sizeof(FSRef), &actual); if ( err != noErr ) { continue; } err = FSRefToDString(&file, &pathName); if (err == noErr) { Tcl_DStringAppendElement(&command, Tcl_DStringValue(&pathName)); Tcl_DStringFree(&pathName); } } Tcl_GlobalEval(interp, Tcl_DStringValue(&command)); Tcl_DStringFree(&command); return noErr; } static OSErr PrintHandler (const AppleEvent * event, AppleEvent * reply, long handlerRefcon) { return noErr; } /* *---------------------------------------------------------------------- * * ScriptHandler -- * * This handler process the script event. * * Results: * Schedules the given event to be processed. * * Side effects: * None. * *---------------------------------------------------------------------- */ static OSErr ScriptHandler (const AppleEvent * event, AppleEvent * reply, long handlerRefcon) { OSErr theErr; AEDescList theDesc; int tclErr = -1; Tcl_Interp *interp; char errString[128]; interp = (Tcl_Interp *) handlerRefcon; /* * The do script event receives one parameter that should be data or a file. */ theErr = AEGetParamDesc(event, keyDirectObject, typeWildCard, &theDesc); if (theErr != noErr) { sprintf(errString, "AEDoScriptHandler: GetParamDesc error %d", theErr); theErr = AEPutParamPtr(reply, keyErrorString, typeChar, errString, strlen(errString)); } else if (MissedAnyParameters(event)) { sprintf(errString, "AEDoScriptHandler: extra parameters"); AEPutParamPtr(reply, keyErrorString, typeChar, errString, strlen(errString)); theErr = -1771; } else { if (theDesc.descriptorType == (DescType)typeChar) { Tcl_DString encodedText; short i; Size size; char * data; size = AEGetDescDataSize(&theDesc); data = (char *)ckalloc(size + 1); if ( !data ) { theErr = -1771; } else { AEGetDescData(&theDesc,data,size); data [ size ] = 0; for (i = 0; i < size; i++) if (data[i] == '\r') data[i] = '\n'; AEReplaceDescData(theDesc.descriptorType, data, size + 1, &theDesc); } Tcl_ExternalToUtfDString(NULL, data, size, &encodedText); tclErr = Tcl_GlobalEval(interp, Tcl_DStringValue(&encodedText)); Tcl_DStringFree(&encodedText); } else if (theDesc.descriptorType == (DescType)typeAlias) { Boolean dummy; FSRef file; AliasPtr alias; Size theSize; theSize = AEGetDescDataSize(&theDesc); alias = (AliasPtr) ckalloc(theSize); if (alias) { AEGetDescData (&theDesc, alias, theSize); theErr = FSResolveAlias(NULL, &alias, &file, &dummy); ckfree((char*)alias); } else { theErr = memFullErr; } if (theErr == noErr) { Tcl_DString scriptName; theErr = FSRefToDString(&file, &scriptName); if (theErr == noErr) { Tcl_EvalFile(interp, Tcl_DStringValue(&scriptName)); Tcl_DStringFree(&scriptName); } } else { sprintf(errString, "AEDoScriptHandler: file not found"); AEPutParamPtr(reply, keyErrorString, typeChar, errString, strlen(errString)); } } else { sprintf(errString, "AEDoScriptHandler: invalid script type '%-4.4s', must be 'alis' or 'TEXT'", (char *)(&theDesc.descriptorType)); AEPutParamPtr(reply, keyErrorString, typeChar, errString, strlen(errString)); theErr = -1770; } } /* * If we actually go to run Tcl code - put the result in the reply. */ if (tclErr >= 0) { if (tclErr == TCL_OK) { AEPutParamPtr(reply, keyDirectObject, typeChar, Tcl_GetStringResult(interp), strlen(Tcl_GetStringResult(interp))); } else { AEPutParamPtr(reply, keyErrorString, typeChar, Tcl_GetStringResult(interp), strlen(Tcl_GetStringResult(interp))); AEPutParamPtr(reply, keyErrorNumber, typeInteger, (Ptr) &tclErr, sizeof(int)); } } AEDisposeDesc(&theDesc); return theErr; } /* *---------------------------------------------------------------------- * * ReallyKillMe -- * * This proc tries to kill the shell by running exit, * called from an event scheduled by the "Quit" AppleEvent handler. * * Results: * Runs the "exit" command which might kill the shell. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int ReallyKillMe(Tcl_Event *eventPtr, int flags) { Tcl_Interp *interp = ((KillEvent *) eventPtr)->interp; if (interp != NULL) { Tcl_GlobalEval(interp, "exit"); } return 1; } /* *---------------------------------------------------------------------- * * MissedAnyParameters -- * * Checks to see if parameters are still left in the event. * * Results: * True or false. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int MissedAnyParameters( const AppleEvent *theEvent) { DescType returnedType; Size actualSize; OSErr err; err = AEGetAttributePtr(theEvent, keyMissedKeywordAttr, typeWildCard, &returnedType, NULL, 0, &actualSize); return (err != errAEDescNotFound); } /* *---------------------------------------------------------------------- * * FSRefToDString -- * * Get a POSIX path from an FSRef. * * Results: * In the parameter ds. * * Side effects: * None. * *---------------------------------------------------------------------- */ static OSErr FSRefToDString(const FSRef *fsref, Tcl_DString *ds) { UInt8 fileName[PATH_MAX+1]; OSErr err; err = FSRefMakePath(fsref, fileName, sizeof(fileName)); if (err == noErr) { Tcl_ExternalToUtfDString(NULL, fileName, -1, ds); } return err; }