diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2018-12-25 17:48:54 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2018-12-25 17:48:54 (GMT) |
commit | 8eb0f61e2e27ef6594eee8bcf68d574fb087fe66 (patch) | |
tree | fc0f3692516c8c3e8090df20223d342a1b64df93 /tk8.6/macosx/tkMacOSXNotify.c | |
parent | 5f5fd2864a3193a8d5da12fcb92ba7379084c286 (diff) | |
download | blt-8eb0f61e2e27ef6594eee8bcf68d574fb087fe66.zip blt-8eb0f61e2e27ef6594eee8bcf68d574fb087fe66.tar.gz blt-8eb0f61e2e27ef6594eee8bcf68d574fb087fe66.tar.bz2 |
update tcl/tk
Diffstat (limited to 'tk8.6/macosx/tkMacOSXNotify.c')
-rw-r--r-- | tk8.6/macosx/tkMacOSXNotify.c | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/tk8.6/macosx/tkMacOSXNotify.c b/tk8.6/macosx/tkMacOSXNotify.c new file mode 100644 index 0000000..7cbd248 --- /dev/null +++ b/tk8.6/macosx/tkMacOSXNotify.c @@ -0,0 +1,395 @@ +/* + * tkMacOSXNotify.c -- + * + * This file contains the implementation of a tcl event source + * for the AppKit event loop. + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * Copyright 2001-2009, Apple Inc. + * Copyright (c) 2005-2009 Daniel A. Steffen <das@users.sourceforge.net> + * Copyright 2015 Marc Culler. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkMacOSXPrivate.h" +#include "tkMacOSXEvent.h" +#include "tkMacOSXConstants.h" +#include <tclInt.h> +#import <objc/objc-auto.h> + +/* This is not used for anything at the moment. */ +typedef struct ThreadSpecificData { + int initialized; +} ThreadSpecificData; +static Tcl_ThreadDataKey dataKey; + +#define TSD_INIT() ThreadSpecificData *tsdPtr = \ + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)) + +static void TkMacOSXNotifyExitHandler(ClientData clientData); +static void TkMacOSXEventsSetupProc(ClientData clientData, int flags); +static void TkMacOSXEventsCheckProc(ClientData clientData, int flags); + +#ifdef TK_MAC_DEBUG_EVENTS +static char* Tk_EventName[39] = { + "", + "", + "KeyPress", /*2*/ + "KeyRelease", /*3*/ + "ButtonPress", /*4*/ + "ButtonRelease", /*5*/ + "MotionNotify", /*6*/ + "EnterNotify", /*7*/ + "LeaveNotify", /*8*/ + "FocusIn", /*9*/ + "FocusOut", /*10*/ + "KeymapNotify", /*11*/ + "Expose", /*12*/ + "GraphicsExpose", /*13*/ + "NoExpose", /*14*/ + "VisibilityNotify", /*15*/ + "CreateNotify", /*16*/ + "DestroyNotify", /*17*/ + "UnmapNotify", /*18*/ + "MapNotify", /*19*/ + "MapRequest", /*20*/ + "ReparentNotify", /*21*/ + "ConfigureNotify", /*22*/ + "ConfigureRequest", /*23*/ + "GravityNotify", /*24*/ + "ResizeRequest", /*25*/ + "CirculateNotify", /*26*/ + "CirculateRequest", /*27*/ + "PropertyNotify", /*28*/ + "SelectionClear", /*29*/ + "SelectionRequest", /*30*/ + "SelectionNotify", /*31*/ + "ColormapNotify", /*32*/ + "ClientMessage", /*33*/ + "MappingNotify", /*34*/ + "VirtualEvent", /*35*/ + "ActivateNotify", /*36*/ + "DeactivateNotify", /*37*/ + "MouseWheelEvent" /*38*/ +}; + +static Tk_RestrictAction +InspectQueueRestrictProc( + ClientData arg, + XEvent *eventPtr) +{ + XVirtualEvent* ve = (XVirtualEvent*) eventPtr; + const char *name; + long serial = ve->serial; + long time = eventPtr->xkey.time; + + if (eventPtr->type == VirtualEvent) { + name = ve->name; + } else { + name = Tk_EventName[eventPtr->type]; + } + fprintf(stderr, " > %s;serial = %lu; time=%lu)\n", + name, serial, time); + return TK_DEFER_EVENT; +} + +/* + * Debugging tool which prints the current Tcl queue. + */ + +void DebugPrintQueue(void) +{ + ClientData oldArg; + Tk_RestrictProc *oldProc; + + oldProc = Tk_RestrictEvents(InspectQueueRestrictProc, NULL, &oldArg); + fprintf(stderr, "Current queue:\n"); + while (Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT)) {}; + Tk_RestrictEvents(oldProc, oldArg, &oldArg); +} +# endif + +#pragma mark TKApplication(TKNotify) + +@interface NSApplication(TKNotify) +/* We need to declare this hidden method. */ +- (void) _modalSession: (NSModalSession) session sendEvent: (NSEvent *) event; +@end + +@implementation TKApplication(TKNotify) +/* + * Earlier versions of Tk would override nextEventMatchingMask here, adding a + * call to displayIfNeeded on all windows after calling super. This would cause + * windows to be redisplayed (if necessary) each time that an event was + * received. This was intended to replace Apple's default autoDisplay + * mechanism, which the earlier versions of Tk would disable. When autoDisplay + * is set to the default value of YES, the Apple event loop will call + * displayIfNeeded on all windows at the beginning of each iteration of their + * event loop. Since Tk does not call the Apple event loop, it was thought + * that the autoDisplay behavior needed to be replicated. + * + * However, as of OSX 10.14 (Mojave) the autoDisplay property became + * deprecated. Luckily it turns out that, even though we don't ever start the + * Apple event loop, the Apple window manager still calls displayIfNeeded on + * all windows on a regular basis, perhaps each time the queue is empty. So we + * no longer, and perhaps never did need to set autoDisplay to NO, nor call + * displayIfNeeded on our windows. We can just leave all of that to the window + * manager. + */ + +/* + * Call super then check the pasteboard. + */ +- (void) sendEvent: (NSEvent *) theEvent +{ + [super sendEvent:theEvent]; + [NSApp tkCheckPasteboard]; +#ifdef TK_MAC_DEBUG_EVENTS + fprintf(stderr, "Sending event of type %d\n", (int)[theEvent type]); + DebugPrintQueue(); +#endif +} +@end + +#pragma mark - + +/* + *---------------------------------------------------------------------- + * + * GetRunLoopMode -- + * + * Results: + * RunLoop mode that should be passed to -nextEventMatchingMask: + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +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 TkAqua events. + * + * Results: + * None. + * + * Side effects: + * A new event source is created. + * + *---------------------------------------------------------------------- + */ + +void +Tk_MacOSXSetupTkNotifier(void) +{ + TSD_INIT(); + + if (!tsdPtr->initialized) { + tsdPtr->initialized = 1; + + /* + * Install TkAqua event source in main event loop thread. + */ + + if (CFRunLoopGetMain() == CFRunLoopGetCurrent()) { + if (![NSThread isMainThread]) { + /* + * 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(TkMacOSXEventsSetupProc, + TkMacOSXEventsCheckProc, + NULL); + TkCreateExitHandler(TkMacOSXNotifyExitHandler, NULL); + Tcl_SetServiceMode(TCL_SERVICE_ALL); + TclMacOSXNotifierAddRunLoopMode(NSEventTrackingRunLoopMode); + TclMacOSXNotifierAddRunLoopMode(NSModalPanelRunLoopMode); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXNotifyExitHandler -- + * + * This function is called during finalization to clean up the + * TkMacOSXNotify module. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +TkMacOSXNotifyExitHandler( + ClientData clientData) /* Not used. */ +{ + TSD_INIT(); + + Tcl_DeleteEventSource(TkMacOSXEventsSetupProc, + TkMacOSXEventsCheckProc, + NULL); + tsdPtr->initialized = 0; +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXEventsSetupProc -- + * + * This procedure implements the setup part of the MacOSX event + * source. It is invoked by Tcl_DoOneEvent before calling + * TkMacOSXEventsProc to process all queued NSEvents. In our + * case, all we need to do is to set the Tcl MaxBlockTime to + * 0 before starting the loop to process all queued NSEvents. + * + * Results: + * None. + * + * Side effects: + * + * If NSEvents are queued, then the maximum block time will be set + * to 0 to ensure that control returns immediately to Tcl. + * + *---------------------------------------------------------------------- + */ + +static void +TkMacOSXEventsSetupProc( + ClientData clientData, + int flags) +{ + NSString *runloopMode = [[NSRunLoop currentRunLoop] currentMode]; + /* runloopMode will be nil if we are in a Tcl event loop. */ + if (flags & TCL_WINDOW_EVENTS && !runloopMode) { + static const Tcl_Time zeroBlockTime = { 0, 0 }; + [NSApp _resetAutoreleasePool]; + /* Call this with dequeue=NO -- just checking if the queue is empty. */ + NSEvent *currentEvent = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:GetRunLoopMode(TkMacOSXGetModalSession()) + dequeue:NO]; + if (currentEvent) { + if (currentEvent.type > 0) { + Tcl_SetMaxBlockTime(&zeroBlockTime); + } + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXEventsCheckProc -- + * + * This procedure loops through all NSEvents waiting in the + * TKApplication event queue, generating X events from them. + * + * Results: + * None. + * + * Side effects: + * NSevents are used to generate X events, which are added to the + * Tcl event queue. + * + *---------------------------------------------------------------------- + */ +static void +TkMacOSXEventsCheckProc( + ClientData clientData, + int flags) +{ + NSString *runloopMode = [[NSRunLoop currentRunLoop] currentMode]; + /* runloopMode will be nil if we are in a Tcl event loop. */ + if (flags & TCL_WINDOW_EVENTS && !runloopMode) { + NSEvent *currentEvent = nil; + NSEvent *testEvent = nil; + NSModalSession modalSession; + /* It is possible for the SetupProc to be called before this function + * returns. This happens, for example, when we process an event which + * opens a modal window. To prevent premature release of our + * application-wide autorelease pool by a nested call to the SetupProc, + * we must lock it here. + */ + [NSApp _lockAutoreleasePool]; + do { + modalSession = TkMacOSXGetModalSession(); + testEvent = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:GetRunLoopMode(modalSession) + dequeue:NO]; + /* We must not steal any events during LiveResize. */ + if (testEvent && [[testEvent window] inLiveResize]) { + break; + } + currentEvent = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:GetRunLoopMode(modalSession) + dequeue:YES]; + if (currentEvent) { + /* Generate Xevents. */ + int oldServiceMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); + NSEvent *processedEvent = [NSApp tkProcessEvent:currentEvent]; + Tcl_SetServiceMode(oldServiceMode); + if (processedEvent) { /* Should always be non-NULL. */ +#ifdef TK_MAC_DEBUG_EVENTS + TKLog(@" event: %@", currentEvent); +#endif + if (modalSession) { + [NSApp _modalSession:modalSession sendEvent:currentEvent]; + } else { + [NSApp sendEvent:currentEvent]; + } + } + } else { + break; + } + } while (1); + /* Now we can unlock the pool. */ + [NSApp _unlockAutoreleasePool]; + } +} + + +/* + * Local Variables: + * mode: objc + * c-basic-offset: 4 + * fill-column: 79 + * coding: utf-8 + * End: + */ |