diff options
Diffstat (limited to 'macosx/tkMacOSXNotify.c')
-rw-r--r-- | macosx/tkMacOSXNotify.c | 258 |
1 files changed, 187 insertions, 71 deletions
diff --git a/macosx/tkMacOSXNotify.c b/macosx/tkMacOSXNotify.c index 71dfd27..c186342 100644 --- a/macosx/tkMacOSXNotify.c +++ b/macosx/tkMacOSXNotify.c @@ -2,35 +2,119 @@ * 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. * - * RCS: @(#) $Id: tkMacOSXNotify.c,v 1.21 2008/11/08 18:44:40 dkf Exp $ + * RCS: @(#) $Id: tkMacOSXNotify.c,v 1.22 2009/06/29 14:35:01 das Exp $ */ #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; +} /* *---------------------------------------------------------------------- @@ -38,7 +122,7 @@ static void CarbonEventsCheckProc(ClientData clientData, int flags); * 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. @@ -52,40 +136,29 @@ 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. - */ - GetMainEventQueue(); - tsdPtr->initialized = 1; /* - * Install Carbon events event source in main event loop thread. + * Install TkAqua 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); } } } @@ -111,20 +184,18 @@ static void TkMacOSXNotifyExitHandler( ClientData clientData) /* Not used. */ { - ThreadSpecificData *tsdPtr = Tcl_GetThreadData(&dataKey, - sizeof(ThreadSpecificData)); - - Tcl_DeleteEventSource(CarbonEventsSetupProc, - CarbonEventsCheckProc, GetMainEventQueue()); + TSD_INIT(); + Tcl_DeleteEventSource(TkMacOSXEventsSetupProc, + TkMacOSXEventsCheckProc, GetMainEventQueue()); tsdPtr->initialized = 0; } /* *---------------------------------------------------------------------- * - * CarbonEventsSetupProc -- + * TkMacOSXEventsSetupProc -- * - * This procedure implements the setup part of the Carbon Events event + * 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. * @@ -132,67 +203,112 @@ TkMacOSXNotifyExitHandler( * None. * * Side effects: - * If Carbon events are queued, then the maximum block time will be set + * 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( +TkMacOSXEventsSetupProc( ClientData clientData, int flags) { - static Tcl_Time blockTime = { 0, 0 }; - - if (!(flags & TCL_WINDOW_EVENTS)) { - return; - } + if (flags & TCL_WINDOW_EVENTS && + ![[NSRunLoop currentRunLoop] currentMode]) { + static const Tcl_Time zeroBlockTime = { 0, 0 }; - if (GetNumEventsInQueue((EventQueueRef) clientData)) { - Tcl_SetMaxBlockTime(&blockTime); + TSD_INIT(); + if (!tsdPtr->currentEvent) { + NSEvent *currentEvent = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:GetRunLoopMode(TkMacOSXGetModalSession()) + dequeue:YES]; + 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( +TkMacOSXEventsCheckProc( ClientData clientData, int flags) { - int numFound; - OSStatus err = noErr; + if (flags & TCL_WINDOW_EVENTS && + ![[NSRunLoop currentRunLoop] currentMode]) { + NSEvent *currentEvent = nil; + NSAutoreleasePool *pool = nil; + NSModalSession modalSession; - if (!(flags & TCL_WINDOW_EVENTS)) { - return; - } - - numFound = GetNumEventsInQueue((EventQueueRef) clientData); - - /* - * 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: c + * c-basic-offset: 4 + * fill-column: 79 + * coding: utf-8 + * End: + */ |