diff options
Diffstat (limited to 'macosx/tkMacOSXNotify.c')
-rw-r--r-- | macosx/tkMacOSXNotify.c | 282 |
1 files changed, 208 insertions, 74 deletions
diff --git a/macosx/tkMacOSXNotify.c b/macosx/tkMacOSXNotify.c index d368292..689f45e 100644 --- a/macosx/tkMacOSXNotify.c +++ b/macosx/tkMacOSXNotify.c @@ -2,11 +2,11 @@ * tkMacOSXNotify.c -- * * This file contains the implementation of a tcl event source - * for the Carbon event loop. + * for the AppKit event loop. * * Copyright (c) 1995-1997 Sun Microsystems, Inc. - * Copyright 2001, Apple Computer, Inc. - * Copyright (c) 2005-2007 Daniel A. Steffen <das@users.sourceforge.net> + * Copyright 2001-2009, Apple Inc. + * Copyright (c) 2005-2009 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. @@ -14,29 +14,121 @@ #include "tkMacOSXPrivate.h" #include "tkMacOSXEvent.h" +#include <tclInt.h> #include <pthread.h> - -/* - * The following static indicates whether this module has been initialized - * in the current thread. - */ +#import <objc/objc-auto.h> typedef struct ThreadSpecificData { - int initialized; + int initialized, sendEventNestingLevel; + NSEvent *currentEvent; } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; +#define TSD_INIT() ThreadSpecificData *tsdPtr = \ + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)) + static void TkMacOSXNotifyExitHandler(ClientData clientData); -static void CarbonEventsSetupProc(ClientData clientData, int flags); -static void CarbonEventsCheckProc(ClientData clientData, int flags); +static void TkMacOSXEventsSetupProc(ClientData clientData, int flags); +static void TkMacOSXEventsCheckProc(ClientData clientData, int flags); + +#pragma mark TKApplication(TKNotify) -/* +@interface NSApplication(TKNotify) +- (void) _modalSession: (NSModalSession) session sendEvent: (NSEvent *) event; +@end + +@implementation NSWindow(TKNotify) +- (id) tkDisplayIfNeeded +{ + if (![self isAutodisplay]) { + [self displayIfNeeded]; + } + return nil; +} +@end + +@implementation TKApplication(TKNotify) +- (NSEvent *) nextEventMatchingMask: (NSUInteger) mask + untilDate: (NSDate *) expiration inMode: (NSString *) mode + dequeue: (BOOL) deqFlag +{ + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + + [NSApp makeWindowsPerform:@selector(tkDisplayIfNeeded) inOrder:NO]; + + int oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); + NSEvent *event = [[super nextEventMatchingMask:mask untilDate:expiration + inMode:mode dequeue:deqFlag] retain]; + + Tcl_SetServiceMode(oldMode); + if (event) { + TSD_INIT(); + if (tsdPtr->sendEventNestingLevel) { + if (![NSApp tkProcessEvent:event]) { + [event release]; + event = nil; + } + } + } + [pool drain]; + return [event autorelease]; +} + +- (void) sendEvent: (NSEvent *) theEvent +{ + TSD_INIT(); + int oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); + + tsdPtr->sendEventNestingLevel++; + [super sendEvent:theEvent]; + tsdPtr->sendEventNestingLevel--; + Tcl_SetServiceMode(oldMode); + [NSApp tkCheckPasteboard]; +} +@end + +#pragma mark - + +/* + *---------------------------------------------------------------------- + * + * GetRunLoopMode -- + * + * Results: + * RunLoop mode that should be passed to -nextEventMatchingMask: + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static NSString * +GetRunLoopMode(NSModalSession modalSession) +{ + NSString *runLoopMode = nil; + + if (modalSession) { + runLoopMode = NSModalPanelRunLoopMode; + } else if (TkMacOSXGetCapture()) { + runLoopMode = NSEventTrackingRunLoopMode; + } + if (!runLoopMode) { + runLoopMode = [[NSRunLoop currentRunLoop] currentMode]; + } + if (!runLoopMode) { + runLoopMode = NSDefaultRunLoopMode; + } + return runLoopMode; +} + +/* *---------------------------------------------------------------------- * * Tk_MacOSXSetupTkNotifier -- * * This procedure is called during Tk initialization to create - * the event source for Carbon events. + * the event source for TkAqua events. * * Results: * None. @@ -50,35 +142,30 @@ static void CarbonEventsCheckProc(ClientData clientData, int flags); void Tk_MacOSXSetupTkNotifier(void) { - ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey, - sizeof(ThreadSpecificData)); + TSD_INIT(); if (!tsdPtr->initialized) { - /* HACK ALERT: There is a bug in Jaguar where when it goes to make - * the event queue for the Main Event Loop, it stores the Current - * event loop rather than the Main Event Loop in the Queue structure. - * So we have to make sure that the Main Event Queue gets set up on - * the main thread. Calling GetMainEventQueue will force this to - * happen. + tsdPtr->initialized = 1; + + /* + * Install TkAqua event source in main event loop thread. */ - GetMainEventQueue(); - tsdPtr->initialized = 1; - /* Install Carbon events event source in main event loop thread. */ - if (GetCurrentEventLoop() == GetMainEventLoop()) { + if (CFRunLoopGetMain() == CFRunLoopGetCurrent()) { if (!pthread_main_np()) { /* - * Panic if the Carbon main event loop thread (i.e. the - * thread where HIToolbox was first loaded) is not the - * main application thread, as Carbon does not support - * this properly. + * Panic if main runloop is not on the main application thread. */ + Tcl_Panic("Tk_MacOSXSetupTkNotifier: %s", "first [load] of TkAqua has to occur in the main thread!"); } - Tcl_CreateEventSource(CarbonEventsSetupProc, - CarbonEventsCheckProc, GetMainEventQueue()); + Tcl_CreateEventSource(TkMacOSXEventsSetupProc, + TkMacOSXEventsCheckProc, GetMainEventQueue()); TkCreateExitHandler(TkMacOSXNotifyExitHandler, NULL); + Tcl_SetServiceMode(TCL_SERVICE_ALL); + TclMacOSXNotifierAddRunLoopMode(NSEventTrackingRunLoopMode); + TclMacOSXNotifierAddRunLoopMode(NSModalPanelRunLoopMode); } } } @@ -101,89 +188,136 @@ Tk_MacOSXSetupTkNotifier(void) */ static void -TkMacOSXNotifyExitHandler(clientData) - ClientData clientData; /* Not used. */ +TkMacOSXNotifyExitHandler( + ClientData clientData) /* Not used. */ { - ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey, - sizeof(ThreadSpecificData)); + TSD_INIT(); - Tcl_DeleteEventSource(CarbonEventsSetupProc, - CarbonEventsCheckProc, GetMainEventQueue()); + Tcl_DeleteEventSource(TkMacOSXEventsSetupProc, + TkMacOSXEventsCheckProc, GetMainEventQueue()); tsdPtr->initialized = 0; } /* *---------------------------------------------------------------------- * - * CarbonEventsSetupProc -- + * TkMacOSXEventsSetupProc -- * - * This procedure implements the setup part of the Carbon Events - * event source. It is invoked by Tcl_DoOneEvent before entering - * the notifier to check for events. + * This procedure implements the setup part of the TkAqua Events event + * source. It is invoked by Tcl_DoOneEvent before entering the notifier + * to check for events. * * Results: * None. * * Side effects: - * If Carbon events are queued, then the maximum block time will be - * set to 0 to ensure that the notifier returns control to Tcl. + * If TkAqua events are queued, then the maximum block time will be set + * to 0 to ensure that the notifier returns control to Tcl. * *---------------------------------------------------------------------- */ static void -CarbonEventsSetupProc(clientData, flags) - ClientData clientData; - int flags; +TkMacOSXEventsSetupProc( + ClientData clientData, + int flags) { - static Tcl_Time blockTime = { 0, 0 }; + if (flags & TCL_WINDOW_EVENTS && + ![[NSRunLoop currentRunLoop] currentMode]) { + static const Tcl_Time zeroBlockTime = { 0, 0 }; + TSD_INIT(); - if (!(flags & TCL_WINDOW_EVENTS)) { - return; - } + if (!tsdPtr->currentEvent) { + NSEvent *currentEvent = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:GetRunLoopMode(TkMacOSXGetModalSession()) + dequeue:YES]; - if (GetNumEventsInQueue((EventQueueRef)clientData)) { - Tcl_SetMaxBlockTime(&blockTime); + if (currentEvent) { + tsdPtr->currentEvent = + TkMacOSXMakeUncollectableAndRetain(currentEvent); + } + } + if (tsdPtr->currentEvent) { + Tcl_SetMaxBlockTime(&zeroBlockTime); + } } } /* *---------------------------------------------------------------------- * - * CarbonEventsCheckProc -- + * TkMacOSXEventsCheckProc -- * - * This procedure processes events sitting in the Carbon event - * queue. + * This procedure processes events sitting in the TkAqua event queue. * * Results: * None. * * Side effects: - * Moves applicable queued Carbon events onto the Tcl event queue. + * Moves applicable queued TkAqua events onto the Tcl event queue. * *---------------------------------------------------------------------- */ static void -CarbonEventsCheckProc(clientData, flags) - ClientData clientData; - int flags; +TkMacOSXEventsCheckProc( + ClientData clientData, + int flags) { - int numFound; - OSStatus err = noErr; - - if (!(flags & TCL_WINDOW_EVENTS)) { - return; - } - - numFound = GetNumEventsInQueue((EventQueueRef)clientData); + if (flags & TCL_WINDOW_EVENTS && + ![[NSRunLoop currentRunLoop] currentMode]) { + NSEvent *currentEvent = nil; + NSAutoreleasePool *pool = nil; + NSModalSession modalSession; - /* Avoid starving other event sources: */ - if (numFound > 4) { - numFound = 4; - } - while (numFound > 0 && err == noErr) { - err = TkMacOSXReceiveAndDispatchEvent(); - numFound--; + TSD_INIT(); + if (tsdPtr->currentEvent) { + currentEvent = TkMacOSXMakeCollectableAndAutorelease( + tsdPtr->currentEvent); + } + do { + modalSession = TkMacOSXGetModalSession(); + if (!currentEvent) { + currentEvent = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:GetRunLoopMode(modalSession) dequeue:YES]; + } + if (!currentEvent) { + break; + } + [currentEvent retain]; + pool = [NSAutoreleasePool new]; + if (tkMacOSXGCEnabled) { + objc_clear_stack(0); + } + if (![NSApp tkProcessEvent:currentEvent]) { + [currentEvent release]; + currentEvent = nil; + } + if (currentEvent) { +#ifdef TK_MAC_DEBUG_EVENTS + TKLog(@" event: %@", currentEvent); +#endif + if (modalSession) { + [NSApp _modalSession:modalSession sendEvent:currentEvent]; + } else { + [NSApp sendEvent:currentEvent]; + } + [currentEvent release]; + currentEvent = nil; + } + [pool drain]; + pool = nil; + } while (1); } } + +/* + * Local Variables: + * mode: objc + * c-basic-offset: 4 + * fill-column: 79 + * coding: utf-8 + * End: + */ |