summaryrefslogtreecommitdiffstats
path: root/macosx/tkMacOSXHLEvents.c
diff options
context:
space:
mode:
authorKevin Walzer <kw@codebykevin.com>2019-09-02 01:24:33 (GMT)
committerKevin Walzer <kw@codebykevin.com>2019-09-02 01:24:33 (GMT)
commit2413f540b4709f4ffade95c9ed42ac90ceced283 (patch)
tree5204b78d9819257caf0dd9971cb6b93ad881bc7d /macosx/tkMacOSXHLEvents.c
parentf1ae193bddf887239d80171051a16f7ada5be8b5 (diff)
downloadtk-2413f540b4709f4ffade95c9ed42ac90ceced283.zip
tk-2413f540b4709f4ffade95c9ed42ac90ceced283.tar.gz
tk-2413f540b4709f4ffade95c9ed42ac90ceced283.tar.bz2
Additional refinements by Marc Culler to streamline the execution of the DoScript Apple Event
Diffstat (limited to 'macosx/tkMacOSXHLEvents.c')
-rw-r--r--macosx/tkMacOSXHLEvents.c443
1 files changed, 175 insertions, 268 deletions
diff --git a/macosx/tkMacOSXHLEvents.c b/macosx/tkMacOSXHLEvents.c
index a84e274..7801fc3 100644
--- a/macosx/tkMacOSXHLEvents.c
+++ b/macosx/tkMacOSXHLEvents.c
@@ -1,11 +1,10 @@
/*
* 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-2019 Marc Culler
* Copyright (c) 2019 Kevin Walzer/WordTech Communications LLC.
@@ -39,27 +38,19 @@ typedef struct AppleEventInfo {
Tcl_Interp *interp;
const char *procedure;
Tcl_DString command;
+ NSAppleEventDescriptor *replyEvent; /* Only used for DoScriptText. */
} AppleEventInfo;
/*
- * We can get by with one static AppleEventInfo struct.
- */
-
-AppleEventInfo staticAEInfo = {0};
-
-/*
* Static functions used only in this file.
*/
-static void tkMacOSXProcessFiles(NSAppleEventDescriptor* event,
- NSAppleEventDescriptor* replyEvent,
- Tcl_Interp *interp);
static int MissedAnyParameters(const AppleEvent *theEvent);
static int ReallyKillMe(Tcl_Event *eventPtr, int flags);
-static void ProcessAppleEventEventually(ClientData clientData);
+static void ProcessAppleEvent(ClientData clientData);
/*
- * Names of procedures which can be used to process AppleEvents as idle tasks.
+ * Names of the procedures which can be used to process AppleEvents.
*/
static char* openDocumentProc = "::tk::mac::OpenDocument";
@@ -106,9 +97,7 @@ static char* scriptTextProc = "::tk::mac::DoScriptText";
- (void) handleOpenApplicationEvent: (NSAppleEventDescriptor *)event
withReplyEvent: (NSAppleEventDescriptor *)replyEvent
{
- Tcl_Interp *interp = _eventInterp;
-
- if (interp &&
+ if (_eventInterp &&
Tcl_FindCommand(_eventInterp, "::tk::mac::OpenApplication", NULL, 0)){
int code = Tcl_EvalEx(_eventInterp, "::tk::mac::OpenApplication",
-1, TCL_EVAL_GLOBAL);
@@ -148,39 +137,103 @@ static char* scriptTextProc = "::tk::mac::DoScriptText";
- (void) handleOpenDocumentsEvent: (NSAppleEventDescriptor *)event
withReplyEvent: (NSAppleEventDescriptor *)replyEvent
{
- tkMacOSXProcessFiles(event, replyEvent, _eventInterp);
+ Tcl_Encoding utf8;
+ const AEDesc *fileSpecDesc = nil;
+ AEDesc contents;
+ char URLString[1 + URL_MAX_LENGTH];
+ NSURL *fileURL;
+ DescType type;
+ Size actual;
+ long count, index;
+ AEKeyword keyword;
+ Tcl_DString pathName;
+ int code;
+
+ /*
+ * Do nothing if we don't have an interpreter.
+ */
+
+ if (!_eventInterp) {
+ return;
+ }
+
+ fileSpecDesc = [event aeDesc];
+ if (fileSpecDesc == nil ) {
+ return;
+ }
+
+ /*
+ * The AppleEvent's descriptor should either contain a value of
+ * typeObjectSpecifier or typeAEList. In the first case, the descriptor
+ * can be treated as a list of size 1 containing a value which can be
+ * coerced into a fileURL. In the second case we want to work with the list
+ * itself. Values in the list will be coerced into fileURL's if possible;
+ * otherwise they will be ignored.
+ */
+
+ /* Get a copy of the AppleEvent's descriptor. */
+ AEGetParamDesc(fileSpecDesc, keyDirectObject, typeWildCard, &contents);
+ if (contents.descriptorType == typeAEList) {
+ fileSpecDesc = &contents;
+ }
+
+ if (AECountItems(fileSpecDesc, &count) != noErr) {
+ AEDisposeDesc(&contents);
+ return;
+ }
+
+ /*
+ * Construct a Tcl expression which calls the ::tk::mac::OpenDocument
+ * procedure, passing the paths contained in the AppleEvent as arguments.
+ */
+
+ AppleEventInfo *AEInfo = 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++) {
+ if (noErr != AEGetNthPtr(fileSpecDesc, index, typeFileURL, &keyword,
+ &type, (Ptr) URLString, URL_MAX_LENGTH, &actual)) {
+ continue;
+ }
+ if (type != typeFileURL) {
+ continue;
+ }
+ URLString[actual] = '\0';
+ fileURL = [NSURL URLWithString:[NSString stringWithUTF8String:(char*)URLString]];
+ if (fileURL == nil) {
+ continue;
+ }
+ Tcl_ExternalToUtfDString(utf8, [[fileURL path] UTF8String], -1, &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];
- Tcl_DString *printCommand = &staticAEInfo.command;
+ const char *printFile = [file UTF8String];
+ AppleEventInfo *AEInfo = ckalloc(sizeof(AppleEventInfo));
+ Tcl_DString *printCommand = &AEInfo->command;
Tcl_DStringInit(printCommand);
Tcl_DStringAppend(printCommand, printDocProc, -1);
Tcl_DStringAppendElement(printCommand, printFile);
- if (Tcl_FindCommand(_eventInterp, printDocProc, NULL, 0)) {
- int tclErr = Tcl_EvalEx(_eventInterp, Tcl_DStringValue(printCommand),
- Tcl_DStringLength(printCommand), TCL_EVAL_GLOBAL);
- if (tclErr!= TCL_OK) {
- Tcl_BackgroundException(_eventInterp, tclErr);
- }
- Tcl_DStringFree(printCommand);
- } else {
-
- /*
- * The PrintDocument procedure may not be defined because the
- * Apple Event is being processed before Tk has been fully
- * initialized. So try to run the procedure as an idle task.
- */
-
- staticAEInfo.interp = _eventInterp;
- staticAEInfo.procedure = printDocProc;
- Tcl_DoWhenIdle(ProcessAppleEventEventually, (ClientData)&staticAEInfo);
- }
+ AEInfo->interp = _eventInterp;
+ AEInfo->procedure = printDocProc;
+ AEInfo->replyEvent = nil;
+ Tcl_DoWhenIdle(ProcessAppleEvent, (ClientData)AEInfo);
}
- (void) handleDoScriptEvent: (NSAppleEventDescriptor *)event
@@ -190,10 +243,8 @@ static char* scriptTextProc = "::tk::mac::DoScriptText";
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
@@ -222,133 +273,77 @@ static char* scriptTextProc = "::tk::mac::DoScriptText";
}
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.
+ * 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.
*/
- 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 *fileScriptCommand = &staticAEInfo.command;
- Tcl_DStringInit(fileScriptCommand);
- if (Tcl_FindCommand(_eventInterp, scriptFileProc, NULL, 0)){
- Tcl_DStringAppend(fileScriptCommand, scriptFileProc, -1);
- } else {
- Tcl_DStringAppend(fileScriptCommand, "source", -1);
- }
- Tcl_DStringAppendElement(fileScriptCommand, [[fileURL path] UTF8String]);
- int tclErr = Tcl_EvalEx(_eventInterp, Tcl_DStringValue(fileScriptCommand), Tcl_DStringLength(fileScriptCommand), TCL_EVAL_GLOBAL);
- if (tclErr!= TCL_OK) {
- Tcl_BackgroundException(_eventInterp, tclErr);
- }
- Tcl_DStringFree(fileScriptCommand);
- } else {
- /*
- * The DoScriptFile procedure may not be defined because
- * the Apple Event is being processed before Tk has been
- * fully initialized. So try
- * to run the procedure as an idle task.
- */
- staticAEInfo.interp = _eventInterp;
- staticAEInfo.procedure = scriptFileProc;
- Tcl_DoWhenIdle(ProcessAppleEventEventually, (ClientData)&staticAEInfo);
- }
- }
- if (noErr == AEGetParamPtr(theDesc, keyDirectObject, typeUTF8Text, &type,
+
+ 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 = 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 can be coerced to UTF8 text. Evaluate as Tcl, * or pass the text as a string argument to ::tk::mac::DoScriptText
- * if that procedure exists.
- */
+ /*
+ * 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 = ckalloc(actual + 1);
-
if (noErr == AEGetParamPtr(theDesc, keyDirectObject,
typeUTF8Text, &type,
data, actual, NULL)) {
- Tcl_DString *textScriptCommand = &staticAEInfo.command;
- Tcl_DStringInit(textScriptCommand);
- Tcl_DStringAppend(textScriptCommand, scriptTextProc, -1);
- Tcl_DStringAppendElement(textScriptCommand, data);
- if (Tcl_FindCommand(_eventInterp, scriptTextProc, NULL, 0)) {
- tclErr = Tcl_EvalEx(_eventInterp,Tcl_DStringValue(textScriptCommand),Tcl_DStringLength(textScriptCommand), TCL_EVAL_GLOBAL);
- if (tclErr!= TCL_OK) {
- Tcl_BackgroundException(_eventInterp, tclErr);
- }
- Tcl_DStringFree(textScriptCommand);
- ckfree(data);
- } else {
- /*
- * The DoScript procedure may not be defined because
- * the Apple Event is being processed before Tk has
- * been fully initialized. So try
- * to run the procedure as an idle task.
- */
- staticAEInfo.interp = _eventInterp;
- staticAEInfo.procedure = scriptTextProc;
- Tcl_DoWhenIdle(ProcessAppleEventEventually, (ClientData)&staticAEInfo);
- }
- }
-
- /*
- * 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));
- }
+ AppleEventInfo *AEInfo = 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);
+ }
}
- return;
}
}
}
-
-
- (void)handleURLEvent:(NSAppleEventDescriptor*)event
withReplyEvent:(NSAppleEventDescriptor*)replyEvent
{
NSString* url = [[event paramDescriptorForKeyword:keyDirectObject]
stringValue];
const char *cURL=[url UTF8String];
- Tcl_DString *launchCommand = &staticAEInfo.command;
+ AppleEventInfo *AEInfo = ckalloc(sizeof(AppleEventInfo));
+ Tcl_DString *launchCommand = &AEInfo->command;
Tcl_DStringInit(launchCommand);
Tcl_DStringAppend(launchCommand, launchURLProc, -1);
Tcl_DStringAppendElement(launchCommand, cURL);
- if (Tcl_FindCommand(_eventInterp, launchURLProc, NULL, 0)) {
- int tclErr = Tcl_EvalEx(_eventInterp, Tcl_DStringValue(launchCommand),
- Tcl_DStringLength(launchCommand), TCL_EVAL_GLOBAL);
- if (tclErr!= TCL_OK) {
- Tcl_BackgroundException(_eventInterp, tclErr);
- }
- Tcl_DStringFree(launchCommand);
- } else {
-
- /*
- * The LaunchURL procedure may not be defined because the AppleEvent
- * is being processed before Tk has been fully initialized. So try
- * to run the procedure as an idle task.
- */
-
- staticAEInfo.interp = _eventInterp;
- staticAEInfo.procedure = launchURLProc;
- Tcl_DoWhenIdle(ProcessAppleEventEventually, (ClientData)&staticAEInfo);
- }
+ AEInfo->interp = _eventInterp;
+ AEInfo->procedure = launchURLProc;
+ AEInfo->replyEvent = nil;
+ Tcl_DoWhenIdle(ProcessAppleEvent, (ClientData)AEInfo);
}
@end
@@ -356,147 +351,59 @@ static char* scriptTextProc = "::tk::mac::DoScriptText";
#pragma mark -
/*
- * Idle handler used to process an AppleEvent in case the event
- * is delivered before the Tcl event handling procedure has been
- * defined.
- */
-
-static void ProcessAppleEventEventually(
- ClientData clientData)
-{
- int code;
- AppleEventInfo *event = (AppleEventInfo*) clientData;
- if (!event->interp ||
- !Tcl_FindCommand(event->interp, event->procedure, NULL, 0)) {
- return;
- }
- code = Tcl_EvalEx(event->interp, Tcl_DStringValue(&event->command),
- Tcl_DStringLength(&event->command), TCL_EVAL_GLOBAL);
- if (code != TCL_OK) {
- Tcl_BackgroundException(event->interp, code);
- }
- Tcl_DStringFree(&event->command);
- event->interp = NULL;
- event->procedure = NULL; /* Not freed - should be global. */
-}
-
-/*
*----------------------------------------------------------------------
*
- * TkMacOSXProcessFiles --
+ * ProcessAppleEvent --
*
- * Extract a list of fileURLs from an AppleEvent and call
- * ::tk::mac::OpenDocument with the file paths as arguments.
+ * 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 event is handled by running ::tk::mac::OpenDocument.
+ * 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
-tkMacOSXProcessFiles(
- NSAppleEventDescriptor* event,
- NSAppleEventDescriptor* replyEvent,
- Tcl_Interp *interp)
+static void ProcessAppleEvent(
+ ClientData clientData)
{
- Tcl_Encoding utf8;
- const AEDesc *fileSpecDesc = nil;
- AEDesc contents;
- char URLString[1 + URL_MAX_LENGTH];
- NSURL *fileURL;
- DescType type;
- Size actual;
- long count, index;
- AEKeyword keyword;
- Tcl_DString pathName, *command=&staticAEInfo.command;
int code;
-
- /*
- * Do nothing if we don't have an interpreter.
- */
-
- if (!interp) {
+ AppleEventInfo *AEInfo = (AppleEventInfo*) clientData;
+ if (!AEInfo->interp ||
+ !Tcl_FindCommand(AEInfo->interp, AEInfo->procedure, NULL, 0)) {
return;
}
-
- fileSpecDesc = [event aeDesc];
- if (fileSpecDesc == nil ) {
- return;
- }
-
- /*
- * The AppleEvent's descriptor should either contain a value of
- * typeObjectSpecifier or typeAEList. In the first case, the descriptor
- * can be treated as a list of size 1 containing a value which can be
- * coerced into a fileURL. In the second case we want to work with the list
- * itself. Values in the list will be coerced into fileURL's if possible;
- * otherwise they will be ignored.
- */
-
- /* Get a copy of the AppleEvent's descriptor. */
- AEGetParamDesc(fileSpecDesc, keyDirectObject, typeWildCard, &contents);
- if (contents.descriptorType == typeAEList) {
- fileSpecDesc = &contents;
- }
-
- if (AECountItems(fileSpecDesc, &count) != noErr) {
- AEDisposeDesc(&contents);
- return;
- }
-
- /*
- * Construct a Tcl command which calls the procedure
- * ::tk::mac::OpenDocuent, passing the paths contained in the AppleEvent as
- * arguments.
- */
-
- Tcl_DStringInit(command);
- Tcl_DStringAppend(command, openDocumentProc, -1);
- utf8 = Tcl_GetEncoding(NULL, "utf-8");
-
- for (index = 1; index <= count; index++) {
- if (noErr != AEGetNthPtr(fileSpecDesc, index, typeFileURL, &keyword,
- &type, (Ptr) URLString, URL_MAX_LENGTH, &actual)) {
- continue;
- }
- if (type != typeFileURL) {
- continue;
- }
- URLString[actual] = '\0';
- fileURL = [NSURL URLWithString:[NSString stringWithUTF8String:(char*)URLString]];
- if (fileURL == nil) {
- continue;
- }
- Tcl_ExternalToUtfDString(utf8, [[fileURL path] UTF8String], -1, &pathName);
- Tcl_DStringAppendElement(command, Tcl_DStringValue(&pathName));
- Tcl_DStringFree(&pathName);
+ code = Tcl_EvalEx(AEInfo->interp, Tcl_DStringValue(&AEInfo->command),
+ Tcl_DStringLength(&AEInfo->command), TCL_EVAL_GLOBAL);
+ if (code != TCL_OK) {
+ Tcl_BackgroundException(AEInfo->interp, code);
}
- Tcl_FreeEncoding(utf8);
- AEDisposeDesc(&contents);
-
- /*
- * Handle the event by evaluating the Tcl expression we constructed.
- * If the procedure has not been defined yet it might mean that the
- * AppleEvent is being processed before Tk has been initialized. So
- * try to evaluate the expression in an idle task.
- */
- if (Tcl_FindCommand(interp, openDocumentProc, NULL, 0)) {
- code = Tcl_EvalEx(interp, Tcl_DStringValue(command),
- Tcl_DStringLength(command), TCL_EVAL_GLOBAL);
- if (code != TCL_OK) {
- Tcl_BackgroundException(interp, code);
+ 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(command);
- } else {
- staticAEInfo.interp = interp;
- staticAEInfo.procedure = openDocumentProc;
- Tcl_DoWhenIdle(ProcessAppleEventEventually, (ClientData)&staticAEInfo);
}
+ Tcl_DStringFree(&AEInfo->command);
+ ckfree(clientData);
}
/*