summaryrefslogtreecommitdiffstats
path: root/macosx/tkMacOSXHLEvents.c
diff options
context:
space:
mode:
Diffstat (limited to 'macosx/tkMacOSXHLEvents.c')
-rw-r--r--macosx/tkMacOSXHLEvents.c434
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: