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