summaryrefslogtreecommitdiffstats
path: root/macosx/tkMacOSXNotify.c
diff options
context:
space:
mode:
Diffstat (limited to 'macosx/tkMacOSXNotify.c')
-rw-r--r--macosx/tkMacOSXNotify.c279
1 files changed, 202 insertions, 77 deletions
diff --git a/macosx/tkMacOSXNotify.c b/macosx/tkMacOSXNotify.c
index d368292..2274a31 100644
--- a/macosx/tkMacOSXNotify.c
+++ b/macosx/tkMacOSXNotify.c
@@ -2,41 +2,127 @@
* 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$
*/
#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 +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.
+ 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 +181,134 @@ Tk_MacOSXSetupTkNotifier(void)
*/
static void
-TkMacOSXNotifyExitHandler(clientData)
- ClientData clientData; /* Not used. */
+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 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 Tcl_Time zeroBlockTime = { 0, 0 };
- if (!(flags & TCL_WINDOW_EVENTS)) {
- return;
- }
-
- 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(clientData, flags)
- ClientData clientData;
- int flags;
+TkMacOSXEventsCheckProc(
+ ClientData clientData,
+ int flags)
{
- int numFound;
- OSStatus err = noErr;
-
- if (!(flags & TCL_WINDOW_EVENTS)) {
- return;
- }
+ if (flags & TCL_WINDOW_EVENTS &&
+ ![[NSRunLoop currentRunLoop] currentMode]) {
+ NSEvent *currentEvent = nil;
+ NSAutoreleasePool *pool = nil;
+ NSModalSession modalSession;
- 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:
+ */