Files
cpython-source-deps/macosx/tkMacOSXNotify.c
2017-09-04 14:25:47 -05:00

313 lines
7.6 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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>
*
* 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 <tclInt.h>
#include <pthread.h>
#import <objc/objc-auto.h>
typedef struct ThreadSpecificData {
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 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 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 (!pthread_main_np()) {
/*
* 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, GetMainEventQueue());
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, GetMainEventQueue());
tsdPtr->initialized = 0;
}
/*
*----------------------------------------------------------------------
*
* TkMacOSXEventsSetupProc --
*
* 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 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
TkMacOSXEventsSetupProc(
ClientData clientData,
int flags)
{
if (flags & TCL_WINDOW_EVENTS &&
![[NSRunLoop currentRunLoop] currentMode]) {
static Tcl_Time zeroBlockTime = { 0, 0 };
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);
}
}
}
/*
*----------------------------------------------------------------------
*
* TkMacOSXEventsCheckProc --
*
* This procedure processes events sitting in the TkAqua event queue.
*
* Results:
* None.
*
* Side effects:
* Moves applicable queued TkAqua events onto the Tcl event queue.
*
*----------------------------------------------------------------------
*/
static void
TkMacOSXEventsCheckProc(
ClientData clientData,
int flags)
{
if (flags & TCL_WINDOW_EVENTS &&
![[NSRunLoop currentRunLoop] currentMode]) {
NSEvent *currentEvent = nil;
NSAutoreleasePool *pool = nil;
NSModalSession modalSession;
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:
*/