diff options
Diffstat (limited to 'macosx/tkMacOSXCarbonEvents.c')
-rw-r--r-- | macosx/tkMacOSXCarbonEvents.c | 428 |
1 files changed, 343 insertions, 85 deletions
diff --git a/macosx/tkMacOSXCarbonEvents.c b/macosx/tkMacOSXCarbonEvents.c index 9b45c19..855ab7b 100644 --- a/macosx/tkMacOSXCarbonEvents.c +++ b/macosx/tkMacOSXCarbonEvents.c @@ -2,12 +2,19 @@ * tkMacOSXCarbonEvents.c -- * * This file implements functions that register for and handle - * various Carbon Events. The reason a separate set of handlers - * is necessary is that not all interesting events get delivered - * directly to the event queue through ReceiveNextEvent. Some only - * get delivered if you register a Carbon Event Handler for the event. + * various Carbon Events and Timers. Most carbon events of interest + * to TkAqua are processed in a handler registered on the dispatcher + * event target so that we get first crack at them before HIToolbox + * dispatchers/processes them further. + * As some events are sent directly to the focus or app event target + * and not dispatched normally, we also register a handler on the + * application event target. * - * Copyright 2001, Apple Computer, Inc. + * Copyright 2001, Apple Computer, Inc. + * Copyright (c) 2005 Daniel A. Steffen <das@users.sourceforge.net> + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. * * The following terms apply to all files originating from Apple * Computer, Inc. ("Apple") and associated with the software @@ -53,7 +60,7 @@ * software in accordance with the terms specified in this * license. * - * RCS: @(#) $Id: tkMacOSXCarbonEvents.c,v 1.3.2.4 2005/09/10 15:11:29 das Exp $ + * RCS: @(#) $Id: tkMacOSXCarbonEvents.c,v 1.3.2.5 2005/11/27 02:36:46 das Exp $ */ #include "tkInt.h" @@ -62,69 +69,83 @@ #include "tkMacOSXDebug.h" /* -#ifdef TK_MAC_DEBUG +#ifdef TK_MAC_DEBUG #define TK_MAC_DEBUG_CARBON_EVENTS #endif */ -#ifdef TK_MAC_DEBUG_CARBON_EVENTS -/* Carbon-internal event debugging routines (c.f. Technote 2124): */ -void _DebugPrintEvent(EventRef inEvent); -void _TraceEventByName(CFStringRef eventName); -#endif /* TK_MAC_DEBUG_CARBON_EVENTS */ - /* Declarations of functions used only in this file */ -static OSStatus CarbonEventHandlerProc ( - EventHandlerCallRef callRef, - EventRef inEvent, - void *userData); +static OSStatus CarbonEventHandlerProc(EventHandlerCallRef callRef, + EventRef event, void *userData); +static OSStatus InstallStandardApplicationEventHandler(); +static void ExitRaelEventHandlerProc (EventHandlerCallRef, EventRef, void*) + __attribute__ ((__noreturn__)); +static void CarbonTimerProc(EventLoopTimerRef timer, void *userData); + +/* Static data used by several functions in this file */ +static jmp_buf exitRaelJmpBuf; +static EventLoopTimerRef carbonTimer = NULL; +static int carbonTimerEnabled = 0; /* *---------------------------------------------------------------------- * * CarbonEventHandlerProc -- * - * This procedure is the handler for all registered CarbonEvents. + * This procedure is the handler for all registered CarbonEvents. * * Results: - * None. + * OS status code. * * Side effects: - * Dispatches CarbonEvents. + * Dispatches CarbonEvents. * *---------------------------------------------------------------------- */ static OSStatus CarbonEventHandlerProc ( - EventHandlerCallRef callRef, - EventRef inEvent, - void *inUserData) + EventHandlerCallRef callRef, + EventRef event, + void *userData) { - OSStatus result = eventNotHandledErr; + OSStatus result = eventNotHandledErr; TkMacOSXEvent macEvent; MacEventStatus eventStatus; + macEvent.eventRef = event; + macEvent.eClass = GetEventClass(macEvent.eventRef); + macEvent.eKind = GetEventKind(macEvent.eventRef); + macEvent.interp = (Tcl_Interp *) userData; + bzero(&eventStatus, sizeof(eventStatus)); + #ifdef TK_MAC_DEBUG_CARBON_EVENTS char buf [256]; - CarbonEventToAscii(inEvent, buf); - fprintf(stderr, "CarbonEventHandlerProc started handling %s\n", buf); - _DebugPrintEvent(inEvent); + if (macEvent.eKind != kEventMouseMoved && + macEvent.eKind != kEventMouseDragged) { + CarbonEventToAscii(event, buf); + fprintf(stderr, "CarbonEventHandlerProc started handling %s\n", buf); + TkMacOSXInitNamedSymbol(HIToolbox, void, _DebugPrintEvent, + EventRef inEvent); + if (_DebugPrintEvent) { + /* Carbon-internal event debugging (c.f. Technote 2124) */ + _DebugPrintEvent(event); + } + } #endif /* TK_MAC_DEBUG_CARBON_EVENTS */ - macEvent.eventRef = inEvent; - macEvent.eClass = GetEventClass(macEvent.eventRef); - macEvent.eKind = GetEventKind(macEvent.eventRef); - macEvent.interp = (Tcl_Interp *) inUserData; - bzero(&eventStatus, sizeof(eventStatus)); TkMacOSXProcessEvent(&macEvent,&eventStatus); if (eventStatus.stopProcessing) { - result = noErr; + result = noErr; } #ifdef TK_MAC_DEBUG_CARBON_EVENTS - fprintf(stderr, "CarbonEventHandlerProc finished handling %s: %s handled\n", buf, - eventStatus.stopProcessing ? " " : "not"); + if (macEvent.eKind != kEventMouseMoved && + macEvent.eKind != kEventMouseDragged) { + fprintf(stderr, + "CarbonEventHandlerProc finished handling %s: %s handled\n", + buf, eventStatus.stopProcessing ? " " : "not"); + } #endif /* TK_MAC_DEBUG_CARBON_EVENTS */ return result; } @@ -134,75 +155,312 @@ CarbonEventHandlerProc ( * * TkMacOSXInitCarbonEvents -- * - * This procedure initializes all CarbonEvent handlers. + * This procedure initializes all CarbonEvent handlers. * * Results: - * None. + * None. * * Side effects: - * Handlers for Carbon Events are registered. + * Handlers for Carbon Events are registered. * *---------------------------------------------------------------------- */ void TkMacOSXInitCarbonEvents ( - Tcl_Interp *interp) + Tcl_Interp *interp) { + OSStatus err; const EventTypeSpec dispatcherEventTypes[] = { - {kEventClassMouse, kEventMouseDown}, - {kEventClassMouse, kEventMouseUp}, - {kEventClassMouse, kEventMouseMoved}, - {kEventClassMouse, kEventMouseDragged}, - {kEventClassMouse, kEventMouseWheelMoved}, - {kEventClassWindow, kEventWindowUpdate}, - {kEventClassWindow, kEventWindowActivated}, - {kEventClassWindow, kEventWindowDeactivated}, - {kEventClassKeyboard, kEventRawKeyDown}, - {kEventClassKeyboard, kEventRawKeyRepeat}, - {kEventClassKeyboard, kEventRawKeyUp}, - {kEventClassKeyboard, kEventRawKeyModifiersChanged}, - {kEventClassKeyboard, kEventRawKeyRepeat}, - {kEventClassApplication, kEventAppActivated}, - {kEventClassApplication, kEventAppDeactivated}, - {kEventClassApplication, kEventAppQuit}, - {kEventClassAppleEvent, kEventAppleEvent}, + {kEventClassMouse, kEventMouseDown}, + {kEventClassMouse, kEventMouseUp}, + {kEventClassMouse, kEventMouseMoved}, + {kEventClassMouse, kEventMouseDragged}, + {kEventClassMouse, kEventMouseWheelMoved}, + {kEventClassWindow, kEventWindowUpdate}, + {kEventClassWindow, kEventWindowActivated}, + {kEventClassWindow, kEventWindowDeactivated}, + {kEventClassKeyboard, kEventRawKeyDown}, + {kEventClassKeyboard, kEventRawKeyRepeat}, + {kEventClassKeyboard, kEventRawKeyUp}, + {kEventClassKeyboard, kEventRawKeyModifiersChanged}, + {kEventClassKeyboard, kEventRawKeyRepeat}, + {kEventClassApplication, kEventAppActivated}, + {kEventClassApplication, kEventAppDeactivated}, + {kEventClassApplication, kEventAppQuit}, }; const EventTypeSpec applicationEventTypes[] = { - {kEventClassWindow, kEventWindowExpanded}, - {kEventClassApplication, kEventAppHidden}, - {kEventClassApplication, kEventAppShown}, + {kEventClassMenu, kEventMenuBeginTracking}, + {kEventClassMenu, kEventMenuEndTracking}, + {kEventClassCommand, kEventCommandProcess}, + {kEventClassWindow, kEventWindowExpanded}, + {kEventClassApplication, kEventAppHidden}, + {kEventClassApplication, kEventAppShown}, }; EventHandlerUPP handler = NewEventHandlerUPP(CarbonEventHandlerProc); - InstallEventHandler(GetEventDispatcherTarget(), handler, - GetEventTypeCount(dispatcherEventTypes), dispatcherEventTypes, - (void *) interp, NULL); - InstallEventHandler(GetApplicationEventTarget(), handler, - GetEventTypeCount(applicationEventTypes), applicationEventTypes, - (void *) interp, NULL); + err = InstallStandardApplicationEventHandler(); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "InstallStandardApplicationEventHandler failed, %d\n", + (int) err); +#endif + } + err = InstallEventHandler(GetEventDispatcherTarget(), handler, + GetEventTypeCount(dispatcherEventTypes), dispatcherEventTypes, + (void *) interp, NULL); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "InstallEventHandler failed, %d\n", (int) err); +#endif + } + err = InstallEventHandler(GetApplicationEventTarget(), handler, + GetEventTypeCount(applicationEventTypes), applicationEventTypes, + (void *) interp, NULL); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "InstallEventHandler failed, %d\n", (int) err); +#endif + } + +#ifdef TK_MAC_DEBUG_CARBON_EVENTS + TkMacOSXInitNamedSymbol(HIToolbox, void, _TraceEventByName, CFStringRef); + if (_TraceEventByName) { + /* Carbon-internal event debugging (c.f. Technote 2124) */ + _TraceEventByName(CFSTR("kEventMouseDown")); + _TraceEventByName(CFSTR("kEventMouseUp")); + _TraceEventByName(CFSTR("kEventMouseWheelMoved")); + _TraceEventByName(CFSTR("kEventWindowUpdate")); + _TraceEventByName(CFSTR("kEventWindowActivated")); + _TraceEventByName(CFSTR("kEventWindowDeactivated")); + _TraceEventByName(CFSTR("kEventRawKeyDown")); + _TraceEventByName(CFSTR("kEventRawKeyRepeat")); + _TraceEventByName(CFSTR("kEventRawKeyUp")); + _TraceEventByName(CFSTR("kEventRawKeyModifiersChanged")); + _TraceEventByName(CFSTR("kEventRawKeyRepeat")); + _TraceEventByName(CFSTR("kEventAppActivated")); + _TraceEventByName(CFSTR("kEventAppDeactivated")); + _TraceEventByName(CFSTR("kEventAppQuit")); + _TraceEventByName(CFSTR("kEventMenuBeginTracking")); + _TraceEventByName(CFSTR("kEventMenuEndTracking")); + _TraceEventByName(CFSTR("kEventCommandProcess")); + _TraceEventByName(CFSTR("kEventWindowExpanded")); + _TraceEventByName(CFSTR("kEventAppHidden")); + _TraceEventByName(CFSTR("kEventAppShown")); + } +#endif /* TK_MAC_DEBUG_CARBON_EVENTS */ +} + +/* + *---------------------------------------------------------------------- + * + * InstallStandardApplicationEventHandler -- + * + * This procedure installs the carbon standard application event + * handler. + * + * Results: + * OS status code. + * + * Side effects: + * Standard handlers for application Carbon Events are registered. + * + *---------------------------------------------------------------------- + */ + +static OSStatus +InstallStandardApplicationEventHandler() +{ + /* + * This is a hack to workaround missing Carbon API to install the standard + * application event handler (InstallStandardEventHandler() does not work + * on the application target). The only way to install the standard app + * handler is to call RunApplicationEventLoop(), but since we are running + * our own event loop, we'll immediately need to break out of RAEL again: + * we do this via longjmp out of the ExitRaelEventHandlerProc event handler + * called first off from RAEL by posting a high priority dummy event. + * This workaround is derived from a similar approach in Technical Q&A 1061. + */ + enum { kExitRaelEvent = 'ExiT' }; + const EventTypeSpec exitRaelEventType = + { kExitRaelEvent, kExitRaelEvent}; + EventHandlerUPP exitRaelEventHandler; + EventHandlerRef exitRaelEventHandlerRef = NULL; + EventRef exitRaelEvent = NULL; + OSStatus err = memFullErr; + + exitRaelEventHandler = NewEventHandlerUPP( + (EventHandlerProcPtr) ExitRaelEventHandlerProc); + if (exitRaelEventHandler) { + err = InstallEventHandler(GetEventDispatcherTarget(), + exitRaelEventHandler, 1, &exitRaelEventType, NULL, + &exitRaelEventHandlerRef); + } + if (err == noErr) { + err = CreateEvent(NULL, kExitRaelEvent, kExitRaelEvent, + GetCurrentEventTime(), kEventAttributeNone, &exitRaelEvent); + } + if (err == noErr) { + err = PostEventToQueue(GetMainEventQueue(), exitRaelEvent, + kEventPriorityHigh); + } + if (err == noErr) { + if (!setjmp(exitRaelJmpBuf)) { + RunApplicationEventLoop(); + /* This point should never be reached ! */ + Tcl_Panic("RunApplicationEventLoop exited !"); + } + } + if (exitRaelEvent) { + ReleaseEvent(exitRaelEvent); + } + if (exitRaelEventHandlerRef) { + RemoveEventHandler(exitRaelEventHandlerRef); + } + if (exitRaelEventHandler) { + DisposeEventHandlerUPP(exitRaelEventHandler); + } + return err; +} + +/* + *---------------------------------------------------------------------- + * + * ExitRaelEventHandlerProc -- + * + * This procedure is the dummy event handler used to break out of + * RAEL via longjmp, it is called as the first ever event handler + * in RAEL by posting a high priority dummy event. + * + * Results: + * None. Never returns ! + * + * Side effects: + * longjmp back to InstallStandardApplicationEventHandler(). + * + *---------------------------------------------------------------------- + */ + +static void +ExitRaelEventHandlerProc ( + EventHandlerCallRef callRef, + EventRef event, void *userData) +{ + longjmp(exitRaelJmpBuf, 1); +} + +/* + *---------------------------------------------------------------------- + * + * CarbonTimerProc -- + * + * This procedure is the carbon timer handler that runs the tcl + * event loop periodically. It does not process TCL_WINDOW_EVENTS + * to avoid reentry issues with Carbon, nor TCL_IDLE_EVENTS since + * it is only intended to be called during short periods of busy + * time such as during menu tracking. + * + * Results: + * None. + * + * Side effects: + * Runs the Tcl event loop. + * + *---------------------------------------------------------------------- + */ +static void +CarbonTimerProc ( + EventLoopTimerRef timer, + void *userData) +{ + while(carbonTimerEnabled && Tcl_DoOneEvent( + TCL_FILE_EVENTS|TCL_TIMER_EVENTS|TCL_DONT_WAIT)) { #ifdef TK_MAC_DEBUG_CARBON_EVENTS - _TraceEventByName(CFSTR("kEventMouseDown")); - _TraceEventByName(CFSTR("kEventMouseUp")); - _TraceEventByName(CFSTR("kEventMouseMoved")); - _TraceEventByName(CFSTR("kEventMouseDragged")); - _TraceEventByName(CFSTR("kEventMouseWheelMoved")); - _TraceEventByName(CFSTR("kEventWindowUpdate")); - _TraceEventByName(CFSTR("kEventWindowActivated")); - _TraceEventByName(CFSTR("kEventWindowDeactivated")); - _TraceEventByName(CFSTR("kEventRawKeyDown")); - _TraceEventByName(CFSTR("kEventRawKeyRepeat")); - _TraceEventByName(CFSTR("kEventRawKeyUp")); - _TraceEventByName(CFSTR("kEventRawKeyModifiersChanged")); - _TraceEventByName(CFSTR("kEventRawKeyRepeat")); - _TraceEventByName(CFSTR("kEventAppActivated")); - _TraceEventByName(CFSTR("kEventAppDeactivated")); - _TraceEventByName(CFSTR("kEventAppQuit")); - _TraceEventByName(CFSTR("kEventAppleEvent")); - _TraceEventByName(CFSTR("kEventWindowExpanded")); - _TraceEventByName(CFSTR("kEventAppHidden")); - _TraceEventByName(CFSTR("kEventAppShown")); + fprintf(stderr, "Processed tcl event from carbon timer\n"); #endif /* TK_MAC_DEBUG_CARBON_EVENTS */ + } } + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXStartTclEventLoopCarbonTimer -- + * + * This procedure installs (if necessary) and starts a carbon + * event timer that runs the tcl event loop periodically. + * It should be called whenever a nested carbon event loop is + * run by HIToolbox (e.g. during menutracking) to ensure that + * non-window non-idle tcl events are processed. + * + * Results: + * OS status code. + * + * Side effects: + * Carbon event timer is installed and started. + * + *---------------------------------------------------------------------- + */ + +OSStatus +TkMacOSXStartTclEventLoopCarbonTimer() +{ + OSStatus err; + + if(!carbonTimer) { + EventLoopTimerUPP timerUPP = NewEventLoopTimerUPP(CarbonTimerProc); + err = InstallEventLoopTimer(GetMainEventLoop(), kEventDurationNoWait, + 5 * kEventDurationMillisecond, timerUPP, NULL, &carbonTimer); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "InstallEventLoopTimer failed, %d\n", (int) err); +#endif + } + } else { + err = SetEventLoopTimerNextFireTime(carbonTimer, kEventDurationNoWait); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "SetEventLoopTimerNextFireTime failed, %d\n", + (int) err); +#endif + } + } + carbonTimerEnabled = 1; + return err; +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXStopTclEventLoopCarbonTimer -- + * + * This procedure stops the carbon event timer started by + * TkMacOSXStartTclEventLoopCarbonTimer(). + * + * Results: + * OS status code. + * + * Side effects: + * Carbon event timer is stopped. + * + *---------------------------------------------------------------------- + */ + +OSStatus +TkMacOSXStopTclEventLoopCarbonTimer() +{ + OSStatus err = noErr; + if(carbonTimer) { + err = SetEventLoopTimerNextFireTime(carbonTimer, kEventDurationForever); + if (err != noErr) { +#ifdef TK_MAC_DEBUG + fprintf(stderr, "SetEventLoopTimerNextFireTime failed, %d\n", + (int) err); +#endif + } + } + carbonTimerEnabled = 0; + return err; +} + |