1/* 2 * tkMacOSXHLEvents.c -- 3 * 4 * Implements high level event support for the Macintosh. Currently, 5 * the only event that really does anything is the Quit event. 6 * 7 * Copyright (c) 1995-1997 Sun Microsystems, Inc. 8 * Copyright 2001, Apple Computer, Inc. 9 * Copyright (c) 2006-2009 Daniel A. Steffen <das@users.sourceforge.net> 10 * 11 * See the file "license.terms" for information on usage and redistribution 12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 13 * 14 * RCS: @(#) $Id: tkMacOSXHLEvents.c,v 1.5.2.11 2007/06/29 03:22:02 das Exp $ 15 */ 16 17#include "tkMacOSXPrivate.h" 18 19/* 20 * This is a Tcl_Event structure that the Quit AppleEvent handler 21 * uses to schedule the ReallyKillMe function. 22 */ 23 24typedef struct KillEvent { 25 Tcl_Event header; /* Information that is standard for 26 * all events. */ 27 Tcl_Interp *interp; /* Interp that was passed to the 28 * Quit AppleEvent */ 29} KillEvent; 30 31/* 32 * Static functions used only in this file. 33 */ 34 35static OSErr QuitHandler(const AppleEvent * event, AppleEvent * reply, 36 long handlerRefcon); 37static OSErr OappHandler(const AppleEvent * event, AppleEvent * reply, 38 long handlerRefcon); 39static OSErr RappHandler(const AppleEvent * event, AppleEvent * reply, 40 long handlerRefcon); 41static OSErr OdocHandler(const AppleEvent * event, AppleEvent * reply, 42 long handlerRefcon); 43static OSErr PrintHandler(const AppleEvent * event, AppleEvent * reply, 44 long handlerRefcon); 45static OSErr ScriptHandler(const AppleEvent * event, AppleEvent * reply, 46 long handlerRefcon); 47static OSErr PrefsHandler(const AppleEvent * event, AppleEvent * reply, 48 long handlerRefcon); 49 50static int MissedAnyParameters(const AppleEvent *theEvent); 51static int ReallyKillMe(Tcl_Event *eventPtr, int flags); 52static OSStatus FSRefToDString(const FSRef *fsref, Tcl_DString *ds); 53 54/* 55 *---------------------------------------------------------------------- 56 * 57 * TkMacOSXInitAppleEvents -- 58 * 59 * Initilize the Apple Events on the Macintosh. This registers the 60 * core event handlers. 61 * 62 * Results: 63 * None. 64 * 65 * Side effects: 66 * None. 67 * 68 *---------------------------------------------------------------------- 69 */ 70 71void 72TkMacOSXInitAppleEvents( 73 Tcl_Interp *interp) /* Interp to handle basic events. */ 74{ 75 AEEventHandlerUPP OappHandlerUPP, RappHandlerUPP, OdocHandlerUPP, 76 PrintHandlerUPP, QuitHandlerUPP, ScriptHandlerUPP, PrefsHandlerUPP; 77 static Boolean initialized = FALSE; 78 79 if (!initialized) { 80 initialized = TRUE; 81 82 /* 83 * Install event handlers for the core apple events. 84 */ 85 QuitHandlerUPP = NewAEEventHandlerUPP(QuitHandler); 86 ChkErr(AEInstallEventHandler, kCoreEventClass, kAEQuitApplication, 87 QuitHandlerUPP, (long) interp, false); 88 89 OappHandlerUPP = NewAEEventHandlerUPP(OappHandler); 90 ChkErr(AEInstallEventHandler, kCoreEventClass, kAEOpenApplication, 91 OappHandlerUPP, (long) interp, false); 92 93 RappHandlerUPP = NewAEEventHandlerUPP(RappHandler); 94 ChkErr(AEInstallEventHandler, kCoreEventClass, kAEReopenApplication, 95 RappHandlerUPP, (long) interp, false); 96 97 OdocHandlerUPP = NewAEEventHandlerUPP(OdocHandler); 98 ChkErr(AEInstallEventHandler, kCoreEventClass, kAEOpenDocuments, 99 OdocHandlerUPP, (long) interp, false); 100 101 PrintHandlerUPP = NewAEEventHandlerUPP(PrintHandler); 102 ChkErr(AEInstallEventHandler, kCoreEventClass, kAEPrintDocuments, 103 PrintHandlerUPP, (long) interp, false); 104 105 PrefsHandlerUPP = NewAEEventHandlerUPP(PrefsHandler); 106 ChkErr(AEInstallEventHandler, kCoreEventClass, kAEShowPreferences, 107 PrefsHandlerUPP, (long) interp, false); 108 109 if (interp) { 110 ScriptHandlerUPP = NewAEEventHandlerUPP(ScriptHandler); 111 ChkErr(AEInstallEventHandler, kAEMiscStandards, kAEDoScript, 112 ScriptHandlerUPP, (long) interp, false); 113 } 114 } 115} 116 117/* 118 *---------------------------------------------------------------------- 119 * 120 * TkMacOSXDoHLEvent -- 121 * 122 * Dispatch incomming highlevel events. 123 * 124 * Results: 125 * None. 126 * 127 * Side effects: 128 * Depends on the incoming event. 129 * 130 *---------------------------------------------------------------------- 131 */ 132 133int 134TkMacOSXDoHLEvent( 135 EventRecord *theEvent) 136{ 137 return AEProcessAppleEvent(theEvent); 138} 139 140/* 141 *---------------------------------------------------------------------- 142 * 143 * QuitHandler -- 144 * 145 * This is the 'quit' core Apple event handler. 146 * 147 * Results: 148 * None. 149 * 150 * Side effects: 151 * None. 152 * 153 *---------------------------------------------------------------------- 154 */ 155 156OSErr 157QuitHandler( 158 const AppleEvent * event, 159 AppleEvent * reply, 160 long handlerRefcon) 161{ 162 Tcl_Interp *interp = (Tcl_Interp *) handlerRefcon; 163 KillEvent *eventPtr; 164 165 if (interp) { 166 /* 167 * Call the exit command from the event loop, since you are not supposed 168 * to call ExitToShell in an Apple Event Handler. We put this at the head 169 * of Tcl's event queue because this message usually comes when the Mac is 170 * shutting down, and we want to kill the shell as quickly as possible. 171 */ 172 173 eventPtr = (KillEvent *) ckalloc(sizeof(KillEvent)); 174 eventPtr->header.proc = ReallyKillMe; 175 eventPtr->interp = interp; 176 177 Tcl_QueueEvent((Tcl_Event *) eventPtr, TCL_QUEUE_HEAD); 178 } 179 return noErr; 180} 181 182/* 183 *---------------------------------------------------------------------- 184 * 185 * OappHandler -- 186 * 187 * This is the 'oapp' core Apple event handler. 188 * 189 * Results: 190 * None. 191 * 192 * Side effects: 193 * None. 194 * 195 *---------------------------------------------------------------------- 196 */ 197 198OSErr 199OappHandler( 200 const AppleEvent * event, 201 AppleEvent * reply, 202 long handlerRefcon) 203{ 204 Tcl_CmdInfo dummy; 205 Tcl_Interp *interp = (Tcl_Interp *) handlerRefcon; 206 207 if (interp && 208 Tcl_GetCommandInfo(interp, "::tk::mac::OpenApplication", &dummy)) { 209 Tcl_GlobalEval(interp, "::tk::mac::OpenApplication"); 210 } 211 return noErr; 212} 213 214/* 215 *---------------------------------------------------------------------- 216 * 217 * RappHandler -- 218 * 219 * This is the 'rapp' core Apple event handler. 220 * 221 * Results: 222 * None. 223 * 224 * Side effects: 225 * None. 226 * 227 *---------------------------------------------------------------------- 228 */ 229 230OSErr 231RappHandler( 232 const AppleEvent * event, 233 AppleEvent * reply, 234 long handlerRefcon) 235{ 236 Tcl_CmdInfo dummy; 237 Tcl_Interp *interp = (Tcl_Interp *) handlerRefcon; 238 ProcessSerialNumber thePSN = {0, kCurrentProcess}; 239 OSStatus err = ChkErr(SetFrontProcess, &thePSN); 240 241 if (interp && 242 Tcl_GetCommandInfo(interp, "::tk::mac::ReopenApplication", &dummy)) { 243 Tcl_GlobalEval(interp, "::tk::mac::ReopenApplication"); 244 } 245 return err; 246} 247 248/* 249 *---------------------------------------------------------------------- 250 * 251 * PrefsHandler -- 252 * 253 * This is the 'pref' core Apple event handler. 254 * Called when the user selects 'Preferences...' in MacOS X 255 * 256 * Results: 257 * None. 258 * 259 * Side effects: 260 * None. 261 * 262 *---------------------------------------------------------------------- 263 */ 264 265OSErr 266PrefsHandler( 267 const AppleEvent * event, 268 AppleEvent * reply, 269 long handlerRefcon) 270{ 271 Tcl_CmdInfo dummy; 272 Tcl_Interp *interp = (Tcl_Interp *) handlerRefcon; 273 274 if (interp && 275 Tcl_GetCommandInfo(interp, "::tk::mac::ShowPreferences", &dummy)) { 276 Tcl_GlobalEval(interp, "::tk::mac::ShowPreferences"); 277 } 278 return noErr; 279} 280 281/* 282 *---------------------------------------------------------------------- 283 * 284 * OdocHandler -- 285 * 286 * This is the 'odoc' core Apple event handler. 287 * 288 * Results: 289 * None. 290 * 291 * Side effects: 292 * None. 293 * 294 *---------------------------------------------------------------------- 295 */ 296 297OSErr 298OdocHandler( 299 const AppleEvent * event, 300 AppleEvent * reply, 301 long handlerRefcon) 302{ 303 Tcl_Interp *interp = (Tcl_Interp *) handlerRefcon; 304 AEDescList fileSpecList; 305 FSRef file; 306 OSStatus err; 307 DescType type; 308 Size actual; 309 long count; 310 AEKeyword keyword; 311 long index; 312 Tcl_DString command; 313 Tcl_DString pathName; 314 Tcl_CmdInfo dummy; 315 316 /* 317 * Don't bother if we don't have an interp or 318 * the open document procedure doesn't exist. 319 */ 320 321 if ((interp == NULL) || 322 (Tcl_GetCommandInfo(interp, "::tk::mac::OpenDocument", &dummy)) == 0) { 323 return noErr; 324 } 325 326 /* 327 * If we get any errors wil retrieving our parameters 328 * we just return with no error. 329 */ 330 331 err = ChkErr(AEGetParamDesc, event, keyDirectObject, typeAEList, 332 &fileSpecList); 333 if (err != noErr) { 334 return noErr; 335 } 336 337 err = MissedAnyParameters(event); 338 if (err != noErr) { 339 return noErr; 340 } 341 342 err = ChkErr(AECountItems, &fileSpecList, &count); 343 if (err != noErr) { 344 return noErr; 345 } 346 347 Tcl_DStringInit(&command); 348 Tcl_DStringAppend(&command, "::tk::mac::OpenDocument", -1); 349 for (index = 1; index <= count; index++) { 350 err = ChkErr(AEGetNthPtr, &fileSpecList, index, typeFSRef, 351 &keyword, &type, (Ptr) &file, sizeof(FSRef), &actual); 352 if ( err != noErr ) { 353 continue; 354 } 355 356 err = ChkErr(FSRefToDString, &file, &pathName); 357 if (err == noErr) { 358 Tcl_DStringAppendElement(&command, Tcl_DStringValue(&pathName)); 359 Tcl_DStringFree(&pathName); 360 } 361 } 362 363 Tcl_EvalEx(interp, Tcl_DStringValue(&command), Tcl_DStringLength(&command), 364 TCL_EVAL_GLOBAL); 365 366 Tcl_DStringFree(&command); 367 return noErr; 368} 369 370/* 371 *---------------------------------------------------------------------- 372 * 373 * PrintHandler -- 374 * 375 * This is the 'pdoc' core Apple event handler. 376 * 377 * Results: 378 * None. 379 * 380 * Side effects: 381 * None. 382 * 383 *---------------------------------------------------------------------- 384 */ 385 386OSErr 387PrintHandler( 388 const AppleEvent * event, 389 AppleEvent * reply, 390 long handlerRefcon) 391{ 392 Tcl_Interp *interp = (Tcl_Interp *) handlerRefcon; 393 AEDescList fileSpecList; 394 FSRef file; 395 OSStatus err; 396 DescType type; 397 Size actual; 398 long count; 399 AEKeyword keyword; 400 long index; 401 Tcl_DString command; 402 Tcl_DString pathName; 403 Tcl_CmdInfo dummy; 404 405 /* 406 * Don't bother if we don't have an interp or 407 * the print document procedure doesn't exist. 408 */ 409 410 if ((interp == NULL) || 411 (Tcl_GetCommandInfo(interp, "::tk::mac::PrintDocument", &dummy)) == 0) { 412 return noErr; 413 } 414 415 /* 416 * If we get any errors wil retrieving our parameters 417 * we just return with no error. 418 */ 419 420 err = ChkErr(AEGetParamDesc, event, keyDirectObject, typeAEList, 421 &fileSpecList); 422 if (err != noErr) { 423 return noErr; 424 } 425 426 err = ChkErr(MissedAnyParameters, event); 427 if (err != noErr) { 428 return noErr; 429 } 430 431 err = ChkErr(AECountItems, &fileSpecList, &count); 432 if (err != noErr) { 433 return noErr; 434 } 435 436 Tcl_DStringInit(&command); 437 Tcl_DStringAppend(&command, "::tk::mac::PrintDocument", -1); 438 for (index = 1; index <= count; index++) { 439 err = ChkErr(AEGetNthPtr, &fileSpecList, index, typeFSRef, &keyword, 440 &type, (Ptr) &file, sizeof(FSRef), &actual); 441 if ( err != noErr ) { 442 continue; 443 } 444 445 err = ChkErr(FSRefToDString, &file, &pathName); 446 if (err == noErr) { 447 Tcl_DStringAppendElement(&command, Tcl_DStringValue(&pathName)); 448 Tcl_DStringFree(&pathName); 449 } 450 } 451 452 Tcl_EvalEx(interp, Tcl_DStringValue(&command), Tcl_DStringLength(&command), 453 TCL_EVAL_GLOBAL); 454 455 Tcl_DStringFree(&command); 456 return noErr; 457} 458 459/* 460 *---------------------------------------------------------------------- 461 * 462 * ScriptHandler -- 463 * 464 * This handler process the script event. 465 * 466 * Results: 467 * Schedules the given event to be processed. 468 * 469 * Side effects: 470 * None. 471 * 472 *---------------------------------------------------------------------- 473 */ 474 475OSErr 476ScriptHandler( 477 const AppleEvent * event, 478 AppleEvent * reply, 479 long handlerRefcon) 480{ 481 OSStatus theErr; 482 AEDescList theDesc; 483 int tclErr = -1; 484 Tcl_Interp *interp; 485 char errString[128]; 486 487 interp = (Tcl_Interp *) handlerRefcon; 488 489 /* 490 * The do script event receives one parameter that should be data or a file. 491 */ 492 theErr = AEGetParamDesc(event, keyDirectObject, typeWildCard, 493 &theDesc); 494 if (theErr != noErr) { 495 sprintf(errString, "AEDoScriptHandler: GetParamDesc error %d", 496 (int)theErr); 497 theErr = AEPutParamPtr(reply, keyErrorString, typeChar, errString, 498 strlen(errString)); 499 } else if (MissedAnyParameters(event)) { 500 sprintf(errString, "AEDoScriptHandler: extra parameters"); 501 AEPutParamPtr(reply, keyErrorString, typeChar, errString, 502 strlen(errString)); 503 theErr = -1771; 504 } else { 505 Size size; 506 507 if (theDesc.descriptorType == (DescType)typeAlias && 508 AEGetParamPtr(event, keyDirectObject, typeFSRef, NULL, NULL, 509 0, &size) == noErr && size == sizeof(FSRef)) { 510 FSRef file; 511 512 theErr = AEGetParamPtr(event, keyDirectObject, typeFSRef, NULL, 513 &file, size, NULL); 514 if (theErr == noErr) { 515 Tcl_DString scriptName; 516 517 theErr = FSRefToDString(&file, &scriptName); 518 if (theErr == noErr) { 519 tclErr = Tcl_EvalFile(interp, 520 Tcl_DStringValue(&scriptName)); 521 Tcl_DStringFree(&scriptName); 522 } 523 } else { 524 sprintf(errString, "AEDoScriptHandler: file not found"); 525 AEPutParamPtr(reply, keyErrorString, typeChar, 526 errString, strlen(errString)); 527 } 528 } else if (AEGetParamPtr(event, keyDirectObject, typeUTF8Text, NULL, 529 NULL, 0, &size) == noErr && size) { 530 char *data = ckalloc(size + 1); 531 532 theErr = AEGetParamPtr(event, keyDirectObject, typeUTF8Text, NULL, 533 data, size, NULL); 534 if (theErr == noErr) { 535 tclErr = Tcl_EvalEx(interp, data, size, TCL_EVAL_GLOBAL); 536 } 537 } else { 538 sprintf(errString, 539 "AEDoScriptHandler: invalid script type '%-4.4s', " 540 "must be 'alis' or coercable to 'utf8'", 541 (char *)(&theDesc.descriptorType)); 542 AEPutParamPtr(reply, keyErrorString, typeChar, 543 errString, strlen(errString)); 544 theErr = -1770; 545 } 546 } 547 548 /* 549 * If we actually go to run Tcl code - put the result in the reply. 550 */ 551 if (tclErr >= 0) { 552 if (tclErr == TCL_OK) { 553 AEPutParamPtr(reply, keyDirectObject, typeChar, 554 Tcl_GetStringResult(interp), 555 strlen(Tcl_GetStringResult(interp))); 556 } else { 557 AEPutParamPtr(reply, keyErrorString, typeChar, 558 Tcl_GetStringResult(interp), 559 strlen(Tcl_GetStringResult(interp))); 560 AEPutParamPtr(reply, keyErrorNumber, typeSInt32, 561 (Ptr) &tclErr, sizeof(int)); 562 } 563 } 564 565 AEDisposeDesc(&theDesc); 566 567 return theErr; 568} 569 570/* 571 *---------------------------------------------------------------------- 572 * 573 * ReallyKillMe -- 574 * 575 * This proc tries to kill the shell by running exit, 576 * called from an event scheduled by the "Quit" AppleEvent handler. 577 * 578 * Results: 579 * Runs the "exit" command which might kill the shell. 580 * 581 * Side effects: 582 * None. 583 * 584 *---------------------------------------------------------------------- 585 */ 586 587static int 588ReallyKillMe( 589 Tcl_Event *eventPtr, 590 int flags) 591{ 592 Tcl_Interp *interp = ((KillEvent *) eventPtr)->interp; 593 Tcl_CmdInfo dummy; 594 if (Tcl_GetCommandInfo(interp, "::tk::mac::Quit", &dummy)) { 595 Tcl_GlobalEval(interp, "::tk::mac::Quit"); 596 } else { 597 Tcl_GlobalEval(interp, "exit"); 598 } 599 return 1; 600} 601 602/* 603 *---------------------------------------------------------------------- 604 * 605 * MissedAnyParameters -- 606 * 607 * Checks to see if parameters are still left in the event. 608 * 609 * Results: 610 * True or false. 611 * 612 * Side effects: 613 * None. 614 * 615 *---------------------------------------------------------------------- 616 */ 617 618int 619MissedAnyParameters( 620 const AppleEvent *theEvent) 621{ 622 DescType returnedType; 623 Size actualSize; 624 OSStatus err; 625 626 err = AEGetAttributePtr(theEvent, keyMissedKeywordAttr, 627 typeWildCard, &returnedType, NULL, 0, &actualSize); 628 629 return (err != errAEDescNotFound); 630} 631 632/* 633 *---------------------------------------------------------------------- 634 * 635 * FSRefToDString -- 636 * 637 * Get a POSIX path from an FSRef. 638 * 639 * Results: 640 * In the parameter ds. 641 * 642 * Side effects: 643 * None. 644 * 645 *---------------------------------------------------------------------- 646 */ 647 648OSStatus 649FSRefToDString( 650 const FSRef *fsref, 651 Tcl_DString *ds) 652{ 653 UInt8 fileName[PATH_MAX+1]; 654 OSStatus err; 655 656 err = ChkErr(FSRefMakePath, fsref, fileName, sizeof(fileName)); 657 if (err == noErr) { 658 Tcl_ExternalToUtfDString(NULL, (char*) fileName, -1, ds); 659 } 660 return err; 661} 662