diff options
Diffstat (limited to 'macosx/tkMacOSXHLEvents.c')
-rw-r--r-- | macosx/tkMacOSXHLEvents.c | 434 |
1 files changed, 251 insertions, 183 deletions
diff --git a/macosx/tkMacOSXHLEvents.c b/macosx/tkMacOSXHLEvents.c index 9b874a5..e2ff0f1 100644 --- a/macosx/tkMacOSXHLEvents.c +++ b/macosx/tkMacOSXHLEvents.c @@ -1,13 +1,13 @@ /* * tkMacOSXHLEvents.c -- * - * Implements high level event support for the Macintosh. Currently, the - * only event that really does anything is the Quit event. + * Implements high level event support for the Macintosh. * * Copyright (c) 1995-1997 Sun Microsystems, Inc. - * Copyright 2001-2009, Apple Inc. + * Copyright (c) 2001-2009, Apple Inc. * Copyright (c) 2006-2009 Daniel A. Steffen <das@users.sourceforge.net> - * Copyright (c) 2015 Marc Culler + * Copyright (c) 2015-2019 Marc Culler + * Copyright (c) 2019 Kevin Walzer/WordTech Communications LLC. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. @@ -30,26 +30,47 @@ typedef struct KillEvent { } KillEvent; /* + * When processing an AppleEvent as an idle task, a pointer to one + * of these structs is passed as the clientData. + */ + +typedef struct AppleEventInfo { + Tcl_Interp *interp; + const char *procedure; + Tcl_DString command; + NSAppleEventDescriptor *replyEvent; /* Only used for DoScriptText. */ +} AppleEventInfo; + +/* * Static functions used only in this file. */ -static void tkMacOSXProcessFiles(NSAppleEventDescriptor* event, - NSAppleEventDescriptor* replyEvent, - Tcl_Interp *interp, - const char* procedure); static int MissedAnyParameters(const AppleEvent *theEvent); static int ReallyKillMe(Tcl_Event *eventPtr, int flags); +static void ProcessAppleEvent(ClientData clientData); + +/* + * Names of the procedures which can be used to process AppleEvents. + */ + +static const char* openDocumentProc = "::tk::mac::OpenDocument"; +static const char* launchURLProc = "::tk::mac::LaunchURL"; +static const char* printDocProc = "::tk::mac::PrintDocument"; +static const char* scriptFileProc = "::tk::mac::DoScriptFile"; +static const char* scriptTextProc = "::tk::mac::DoScriptText"; #pragma mark TKApplication(TKHLEvents) @implementation TKApplication(TKHLEvents) - (void) terminate: (id) sender { + (void)sender; [self handleQuitApplicationEvent:Nil withReplyEvent:Nil]; } - (void) preferences: (id) sender { + (void)sender; [self handleShowPreferencesEvent:Nil withReplyEvent:Nil]; } @@ -57,6 +78,8 @@ static int ReallyKillMe(Tcl_Event *eventPtr, int flags); withReplyEvent: (NSAppleEventDescriptor *)replyEvent { KillEvent *eventPtr; + (void)event; + (void)replyEvent; if (_eventInterp) { /* @@ -67,7 +90,7 @@ static int ReallyKillMe(Tcl_Event *eventPtr, int flags); * quickly as possible. */ - eventPtr = ckalloc(sizeof(KillEvent)); + eventPtr = (KillEvent *)ckalloc(sizeof(KillEvent)); eventPtr->header.proc = ReallyKillMe; eventPtr->interp = _eventInterp; @@ -78,9 +101,10 @@ static int ReallyKillMe(Tcl_Event *eventPtr, int flags); - (void) handleOpenApplicationEvent: (NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent { - Tcl_Interp *interp = _eventInterp; + (void)event; + (void)replyEvent; - if (interp && + if (_eventInterp && Tcl_FindCommand(_eventInterp, "::tk::mac::OpenApplication", NULL, 0)){ int code = Tcl_EvalEx(_eventInterp, "::tk::mac::OpenApplication", -1, TCL_EVAL_GLOBAL); @@ -93,6 +117,9 @@ static int ReallyKillMe(Tcl_Event *eventPtr, int flags); - (void) handleReopenApplicationEvent: (NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent { + (void)event; + (void)replyEvent; + [NSApp activateIgnoringOtherApps: YES]; if (_eventInterp && Tcl_FindCommand(_eventInterp, "::tk::mac::ReopenApplication", NULL, 0)) { @@ -107,6 +134,9 @@ static int ReallyKillMe(Tcl_Event *eventPtr, int flags); - (void) handleShowPreferencesEvent: (NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent { + (void)event; + (void)replyEvent; + if (_eventInterp && Tcl_FindCommand(_eventInterp, "::tk::mac::ShowPreferences", NULL, 0)){ int code = Tcl_EvalEx(_eventInterp, "::tk::mac::ShowPreferences", @@ -120,160 +150,6 @@ static int ReallyKillMe(Tcl_Event *eventPtr, int flags); - (void) handleOpenDocumentsEvent: (NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent { - tkMacOSXProcessFiles(event, replyEvent, _eventInterp, "::tk::mac::OpenDocument"); -} - -- (void) handlePrintDocumentsEvent: (NSAppleEventDescriptor *)event - withReplyEvent: (NSAppleEventDescriptor *)replyEvent -{ - tkMacOSXProcessFiles(event, replyEvent, _eventInterp, "::tk::mac::PrintDocument"); -} - -- (void) handleDoScriptEvent: (NSAppleEventDescriptor *)event - withReplyEvent: (NSAppleEventDescriptor *)replyEvent -{ - OSStatus err; - const AEDesc *theDesc = nil; - DescType type = 0, initialType = 0; - Size actual; - int tclErr = -1; - char URLBuffer[1 + URL_MAX_LENGTH]; - char errString[128]; - char typeString[5]; - - /* - * The DoScript event receives one parameter that should be text data or a - * fileURL. - */ - - theDesc = [event aeDesc]; - if (theDesc == nil) { - return; - } - - err = AEGetParamPtr(theDesc, keyDirectObject, typeWildCard, &initialType, - NULL, 0, NULL); - if (err != noErr) { - sprintf(errString, "AEDoScriptHandler: GetParamDesc error %d", (int)err); - AEPutParamPtr((AppleEvent*)[replyEvent aeDesc], keyErrorString, typeChar, - errString, strlen(errString)); - return; - } - - if (MissedAnyParameters((AppleEvent*)theDesc)) { - sprintf(errString, "AEDoScriptHandler: extra parameters"); - AEPutParamPtr((AppleEvent*)[replyEvent aeDesc], keyErrorString, typeChar, - errString, strlen(errString)); - return; - } - - if (initialType == typeFileURL || initialType == typeAlias) { - /* - * The descriptor can be coerced to a file url. Source the file, or - * pass the path as a string argument to ::tk::mac::DoScriptFile if - * that procedure exists. - */ - err = AEGetParamPtr(theDesc, keyDirectObject, typeFileURL, &type, - (Ptr) URLBuffer, URL_MAX_LENGTH, &actual); - if (err == noErr && actual > 0){ - URLBuffer[actual] = '\0'; - NSString *urlString = [NSString stringWithUTF8String:(char*)URLBuffer]; - NSURL *fileURL = [NSURL URLWithString:urlString]; - Tcl_DString command; - Tcl_DStringInit(&command); - if (Tcl_FindCommand(_eventInterp, "::tk::mac::DoScriptFile", NULL, 0)){ - Tcl_DStringAppend(&command, "::tk::mac::DoScriptFile", -1); - } else { - Tcl_DStringAppend(&command, "source", -1); - } - Tcl_DStringAppendElement(&command, [[fileURL path] UTF8String]); - tclErr = Tcl_EvalEx(_eventInterp, Tcl_DStringValue(&command), - Tcl_DStringLength(&command), TCL_EVAL_GLOBAL); - } - } else if (noErr == AEGetParamPtr(theDesc, keyDirectObject, typeUTF8Text, &type, - NULL, 0, &actual)) { - if (actual > 0) { - /* - * The descriptor can be coerced to UTF8 text. Evaluate as Tcl, or - * or pass the text as a string argument to ::tk::mac::DoScriptText - * if that procedure exists. - */ - char *data = ckalloc(actual + 1); - if (noErr == AEGetParamPtr(theDesc, keyDirectObject, typeUTF8Text, &type, - data, actual, NULL)) { - if (Tcl_FindCommand(_eventInterp, "::tk::mac::DoScriptText", NULL, 0)){ - Tcl_DString command; - Tcl_DStringInit(&command); - Tcl_DStringAppend(&command, "::tk::mac::DoScriptText", -1); - Tcl_DStringAppendElement(&command, data); - tclErr = Tcl_EvalEx(_eventInterp, Tcl_DStringValue(&command), - Tcl_DStringLength(&command), TCL_EVAL_GLOBAL); - } else { - tclErr = Tcl_EvalEx(_eventInterp, data, actual, TCL_EVAL_GLOBAL); - } - } - ckfree(data); - } - } else { - /* - * The descriptor can not be coerced to a fileURL or UTF8 text. - */ - for (int i = 0; i < 4; i++) { - typeString[i] = ((char*)&initialType)[3-i]; - } - typeString[4] = '\0'; - sprintf(errString, "AEDoScriptHandler: invalid script type '%s', " - "must be coercable to 'furl' or 'utf8'", typeString); - AEPutParamPtr((AppleEvent*)[replyEvent aeDesc], keyErrorString, typeChar, errString, - strlen(errString)); - } - /* - * If we ran some Tcl code, put the result in the reply. - */ - if (tclErr >= 0) { - int reslen; - const char *result = - Tcl_GetStringFromObj(Tcl_GetObjResult(_eventInterp), &reslen); - if (tclErr == TCL_OK) { - AEPutParamPtr((AppleEvent*)[replyEvent aeDesc], keyDirectObject, typeChar, - result, reslen); - } else { - AEPutParamPtr((AppleEvent*)[replyEvent aeDesc], keyErrorString, typeChar, - result, reslen); - AEPutParamPtr((AppleEvent*)[replyEvent aeDesc], keyErrorNumber, typeSInt32, - (Ptr) &tclErr,sizeof(int)); - } - } - return; -} -@end - -#pragma mark - - -/* - *---------------------------------------------------------------------- - * - * TkMacOSXProcessFiles -- - * - * Extract a list of fileURLs from an AppleEvent and call the specified - * procedure with the file paths as arguments. - * - * Results: - * None. - * - * Side effects: - * The event is handled by running the procedure. - * - *---------------------------------------------------------------------- - */ - -static void -tkMacOSXProcessFiles( - NSAppleEventDescriptor* event, - NSAppleEventDescriptor* replyEvent, - Tcl_Interp *interp, - const char* procedure) -{ Tcl_Encoding utf8; const AEDesc *fileSpecDesc = nil; AEDesc contents; @@ -283,14 +159,14 @@ tkMacOSXProcessFiles( Size actual; long count, index; AEKeyword keyword; - Tcl_DString command, pathName; - int code; + Tcl_DString pathName; + (void)replyEvent; /* - * Do nothing if we don't have an interpreter or the procedure doesn't exist. + * Do nothing if we don't have an interpreter. */ - if (!interp || !Tcl_FindCommand(interp, procedure, NULL, 0)) { + if (!_eventInterp) { return; } @@ -320,12 +196,14 @@ tkMacOSXProcessFiles( } /* - * Construct a Tcl command which calls the procedure, passing the - * paths contained in the AppleEvent as arguments. + * Construct a Tcl expression which calls the ::tk::mac::OpenDocument + * procedure, passing the paths contained in the AppleEvent as arguments. */ - Tcl_DStringInit(&command); - Tcl_DStringAppend(&command, procedure, -1); + AppleEventInfo *AEInfo = (AppleEventInfo *)ckalloc(sizeof(AppleEventInfo)); + Tcl_DString *openCommand = &AEInfo->command; + Tcl_DStringInit(openCommand); + Tcl_DStringAppend(openCommand, openDocumentProc, -1); utf8 = Tcl_GetEncoding(NULL, "utf-8"); for (index = 1; index <= count; index++) { @@ -342,23 +220,207 @@ tkMacOSXProcessFiles( continue; } Tcl_ExternalToUtfDString(utf8, [[fileURL path] UTF8String], -1, &pathName); - Tcl_DStringAppendElement(&command, Tcl_DStringValue(&pathName)); + Tcl_DStringAppendElement(openCommand, Tcl_DStringValue(&pathName)); Tcl_DStringFree(&pathName); } Tcl_FreeEncoding(utf8); AEDisposeDesc(&contents); + AEInfo->interp = _eventInterp; + AEInfo->procedure = openDocumentProc; + AEInfo->replyEvent = nil; + Tcl_DoWhenIdle(ProcessAppleEvent, (ClientData)AEInfo); +} + +- (void) handlePrintDocumentsEvent: (NSAppleEventDescriptor *)event + withReplyEvent: (NSAppleEventDescriptor *)replyEvent +{ + NSString* file = [[event paramDescriptorForKeyword:keyDirectObject] + stringValue]; + const char *printFile = [file UTF8String]; + AppleEventInfo *AEInfo = (AppleEventInfo *)ckalloc(sizeof(AppleEventInfo)); + Tcl_DString *printCommand = &AEInfo->command; + (void)replyEvent; + + Tcl_DStringInit(printCommand); + Tcl_DStringAppend(printCommand, printDocProc, -1); + Tcl_DStringAppendElement(printCommand, printFile); + AEInfo->interp = _eventInterp; + AEInfo->procedure = printDocProc; + AEInfo->replyEvent = nil; + Tcl_DoWhenIdle(ProcessAppleEvent, (ClientData)AEInfo); +} + +- (void) handleDoScriptEvent: (NSAppleEventDescriptor *)event + withReplyEvent: (NSAppleEventDescriptor *)replyEvent +{ + OSStatus err; + const AEDesc *theDesc = nil; + DescType type = 0, initialType = 0; + Size actual; + char URLBuffer[1 + URL_MAX_LENGTH]; + char errString[128]; /* - * Handle the event by evaluating the Tcl expression we constructed. + * The DoScript event receives one parameter that should be text data or a + * fileURL. */ - code = Tcl_EvalEx(interp, Tcl_DStringValue(&command), - Tcl_DStringLength(&command), TCL_EVAL_GLOBAL); + theDesc = [event aeDesc]; + if (theDesc == nil) { + return; + } + + err = AEGetParamPtr(theDesc, keyDirectObject, typeWildCard, &initialType, + NULL, 0, NULL); + if (err != noErr) { + sprintf(errString, "AEDoScriptHandler: GetParamDesc error %d", (int)err); + AEPutParamPtr((AppleEvent*)[replyEvent aeDesc], keyErrorString, + typeChar, errString, strlen(errString)); + return; + } + + if (MissedAnyParameters((AppleEvent*)theDesc)) { + sprintf(errString, "AEDoScriptHandler: extra parameters"); + AEPutParamPtr((AppleEvent*)[replyEvent aeDesc], keyErrorString, + typeChar,errString, strlen(errString)); + return; + } + + if (initialType == typeFileURL || initialType == typeAlias) { + + /* + * This descriptor can be coerced to a file url. Construct a Tcl + * expression which passes the file path as a string argument to + * ::tk::mac::DoScriptFile. + */ + + if (noErr == AEGetParamPtr(theDesc, keyDirectObject, typeFileURL, &type, + (Ptr) URLBuffer, URL_MAX_LENGTH, &actual)) { + if (actual > 0) { + URLBuffer[actual] = '\0'; + NSString *urlString = [NSString stringWithUTF8String:(char*)URLBuffer]; + NSURL *fileURL = [NSURL URLWithString:urlString]; + AppleEventInfo *AEInfo = (AppleEventInfo *)ckalloc(sizeof(AppleEventInfo)); + Tcl_DString *scriptFileCommand = &AEInfo->command; + Tcl_DStringInit(scriptFileCommand); + Tcl_DStringAppend(scriptFileCommand, scriptFileProc, -1); + Tcl_DStringAppendElement(scriptFileCommand, [[fileURL path] UTF8String]); + AEInfo->interp = _eventInterp; + AEInfo->procedure = scriptFileProc; + AEInfo->replyEvent = nil; + Tcl_DoWhenIdle(ProcessAppleEvent, (ClientData)AEInfo); + } + } + } else if (noErr == AEGetParamPtr(theDesc, keyDirectObject, typeUTF8Text, &type, + NULL, 0, &actual)) { + /* + * The descriptor cannot be coerced to a file URL but can be coerced to + * text. Construct a Tcl expression which passes the text as a string + * argument to ::tk::mac::DoScriptText. + */ + + if (actual > 0) { + char *data = (char *)ckalloc(actual + 1); + if (noErr == AEGetParamPtr(theDesc, keyDirectObject, + typeUTF8Text, &type, + data, actual, NULL)) { + AppleEventInfo *AEInfo = (AppleEventInfo *)ckalloc(sizeof(AppleEventInfo)); + Tcl_DString *scriptTextCommand = &AEInfo->command; + Tcl_DStringInit(scriptTextCommand); + Tcl_DStringAppend(scriptTextCommand, scriptTextProc, -1); + Tcl_DStringAppendElement(scriptTextCommand, data); + AEInfo->interp = _eventInterp; + AEInfo->procedure = scriptTextProc; + if (Tcl_FindCommand(AEInfo->interp, AEInfo->procedure, NULL, 0)) { + AEInfo->replyEvent = replyEvent; + ProcessAppleEvent((ClientData)AEInfo); + } else { + AEInfo->replyEvent = nil; + Tcl_DoWhenIdle(ProcessAppleEvent, (ClientData)AEInfo); + } + } + } + } +} + +- (void)handleURLEvent:(NSAppleEventDescriptor*)event + withReplyEvent:(NSAppleEventDescriptor*)replyEvent +{ + NSString* url = [[event paramDescriptorForKeyword:keyDirectObject] + stringValue]; + const char *cURL=[url UTF8String]; + AppleEventInfo *AEInfo = (AppleEventInfo *)ckalloc(sizeof(AppleEventInfo)); + Tcl_DString *launchCommand = &AEInfo->command; + (void)replyEvent; + + Tcl_DStringInit(launchCommand); + Tcl_DStringAppend(launchCommand, launchURLProc, -1); + Tcl_DStringAppendElement(launchCommand, cURL); + AEInfo->interp = _eventInterp; + AEInfo->procedure = launchURLProc; + AEInfo->replyEvent = nil; + Tcl_DoWhenIdle(ProcessAppleEvent, (ClientData)AEInfo); +} + +@end + +#pragma mark - + +/* + *---------------------------------------------------------------------- + * + * ProcessAppleEvent -- + * + * Usually used as an idle task which evaluates a Tcl expression generated + * from an AppleEvent. If the AppleEventInfo passed as the client data + * has a non-null replyEvent, the result of evaluating the expression will + * be added to the reply. This must not be done when this function is + * called as an idle task, but is done when handling DoScriptText events + * when this function is called directly. + * + * Results: + * None. + * + * Side effects: + * The expression will be evaluated and the clientData will be freed. + * The replyEvent may be modified to contain the result of evaluating + * a Tcl expression. + * + *---------------------------------------------------------------------- + */ + +static void ProcessAppleEvent( + ClientData clientData) +{ + int code; + AppleEventInfo *AEInfo = (AppleEventInfo*) clientData; + if (!AEInfo->interp || + !Tcl_FindCommand(AEInfo->interp, AEInfo->procedure, NULL, 0)) { + return; + } + code = Tcl_EvalEx(AEInfo->interp, Tcl_DStringValue(&AEInfo->command), + Tcl_DStringLength(&AEInfo->command), TCL_EVAL_GLOBAL); if (code != TCL_OK) { - Tcl_BackgroundException(interp, code); + Tcl_BackgroundException(AEInfo->interp, code); } - Tcl_DStringFree(&command); + + if (AEInfo->replyEvent && code >= 0) { + int reslen; + const char *result = Tcl_GetStringFromObj(Tcl_GetObjResult(AEInfo->interp), + &reslen); + if (code == TCL_OK) { + AEPutParamPtr((AppleEvent*)[AEInfo->replyEvent aeDesc], + keyDirectObject, typeChar, result, reslen); + } else { + AEPutParamPtr((AppleEvent*)[AEInfo->replyEvent aeDesc], + keyErrorString, typeChar, result, reslen); + AEPutParamPtr((AppleEvent*)[AEInfo->replyEvent aeDesc], + keyErrorNumber, typeSInt32, (Ptr) &code, sizeof(int)); + } + } + Tcl_DStringFree(&AEInfo->command); + ckfree(clientData); } /* @@ -380,10 +442,11 @@ tkMacOSXProcessFiles( void TkMacOSXInitAppleEvents( - Tcl_Interp *interp) /* not used */ + Tcl_Interp *dummy) /* not used */ { NSAppleEventManager *aeManager = [NSAppleEventManager sharedAppleEventManager]; static Boolean initialized = FALSE; + (void)dummy; if (!initialized) { initialized = TRUE; @@ -409,12 +472,17 @@ TkMacOSXInitAppleEvents( forEventClass:kCoreEventClass andEventID:kAEOpenDocuments]; [aeManager setEventHandler:NSApp - andSelector:@selector(handleOpenDocumentsEvent:withReplyEvent:) + andSelector:@selector(handlePrintDocumentsEvent:withReplyEvent:) forEventClass:kCoreEventClass andEventID:kAEPrintDocuments]; [aeManager setEventHandler:NSApp andSelector:@selector(handleDoScriptEvent:withReplyEvent:) forEventClass:kAEMiscStandards andEventID:kAEDoScript]; + + [aeManager setEventHandler:NSApp + andSelector:@selector(handleURLEvent:withReplyEvent:) + forEventClass:kInternetEventClass andEventID:kAEGetURL]; + } } @@ -490,6 +558,7 @@ ReallyKillMe( Tcl_Interp *interp = ((KillEvent *) eventPtr)->interp; int quit = Tcl_FindCommand(interp, "::tk::mac::Quit", NULL, 0)!=NULL; int code = Tcl_EvalEx(interp, quit ? "::tk::mac::Quit" : "exit", -1, TCL_EVAL_GLOBAL); + (void)flags; if (code != TCL_OK) { /* @@ -529,8 +598,7 @@ MissedAnyParameters( typeWildCard, &returnedType, NULL, 0, &actualSize); return (err != errAEDescNotFound); -} - +} /* * Local Variables: |