diff options
Diffstat (limited to 'macosx/tkMacOSXWindowEvent.c')
-rw-r--r-- | macosx/tkMacOSXWindowEvent.c | 379 |
1 files changed, 146 insertions, 233 deletions
diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index 9402cbb..461a94c 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -6,6 +6,8 @@ * * Copyright 2001-2009, Apple Inc. * Copyright (c) 2005-2009 Daniel A. Steffen <das@users.sourceforge.net> + * Copyright (c) 2015 Kevin Walzer/WordTech Communications LLC. + * Copyright (c) 2015 Marc Culler. * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. @@ -27,7 +29,7 @@ * Declaration of functions used only in this file */ -static int GenerateUpdates(HIMutableShapeRef updateRgn, +static int GenerateUpdates(HIShapeRef updateRgn, CGRect *updateBounds, TkWindow *winPtr); static int GenerateActivateEvents(TkWindow *winPtr, int activeFlag); @@ -46,7 +48,7 @@ extern NSString *NSWindowDidOrderOffScreenNotification; #endif #endif -extern NSString *opaqueTag; +extern BOOL opaqueTag; @implementation TKApplication(TKWindowEvent) @@ -163,6 +165,10 @@ extern NSString *opaqueTag; if (winPtr) { TkGenWMDestroyEvent((Tk_Window) winPtr); + if (_windowWithMouse == w) { + _windowWithMouse = nil; + [w release]; + } } /* @@ -266,9 +272,8 @@ extern NSString *opaqueTag; const char *cmd = ([[notification name] isEqualToString: NSApplicationDidUnhideNotification] ? "::tk::mac::OnShow" : "::tk::mac::OnHide"); - Tcl_CmdInfo dummy; - if (_eventInterp && Tcl_GetCommandInfo(_eventInterp, cmd, &dummy)) { + if (_eventInterp && Tcl_FindCommand(_eventInterp, cmd, NULL, 0)) { int code = Tcl_EvalEx(_eventInterp, cmd, -1, TCL_EVAL_GLOBAL); if (code != TCL_OK) { @@ -314,7 +319,7 @@ extern NSString *opaqueTag; static int GenerateUpdates( - HIMutableShapeRef updateRgn, + HIShapeRef updateRgn, CGRect *updateBounds, TkWindow *winPtr) { @@ -357,11 +362,12 @@ GenerateUpdates( event.xexpose.width = damageBounds.size.width; event.xexpose.height = damageBounds.size.height; event.xexpose.count = 0; - Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); -#ifdef TK_MAC_DEBUG_DRAWING - TKLog(@"Expose %p {{%d, %d}, {%d, %d}}", event.xany.window, event.xexpose.x, + Tk_HandleEvent(&event); + + #ifdef TK_MAC_DEBUG_DRAWING + NSLog(@"Expose %p {{%d, %d}, {%d, %d}}", event.xany.window, event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height); -#endif + #endif /* * Generate updates for the children of this window @@ -743,15 +749,16 @@ TkWmProtocolEventProc( int Tk_MacOSXIsAppInFront(void) { - OSStatus err; - ProcessSerialNumber frontPsn, ourPsn = {0, kCurrentProcess}; Boolean isFrontProcess = true; +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 + ProcessSerialNumber frontPsn, ourPsn = {0, kCurrentProcess}; - err = ChkErr(GetFrontProcess, &frontPsn); - if (err == noErr) { - ChkErr(SameProcess, &frontPsn, &ourPsn, &isFrontProcess); + if (noErr == GetFrontProcess(&frontPsn)){ + SameProcess(&frontPsn, &ourPsn, &isFrontProcess); } - +#else + isFrontProcess = [NSRunningApplication currentApplication].active; +#endif return (isFrontProcess == true); } @@ -760,27 +767,25 @@ Tk_MacOSXIsAppInFront(void) #import <ApplicationServices/ApplicationServices.h> /* - * Custom content view for Tk NSWindows, containing standard NSView subviews. - * The goal is to emulate X11-style drawing in response to Expose events: - * during the normal AppKit drawing cycle, we supress drawing of all subviews - * (using a technique adapted from WebKit's WebHTMLView) and instead send - * Expose events about the subviews that would be redrawn. Tk Expose event - * handling and drawing handlers then draw the subviews manually via their - * -displayRectIgnoringOpacity: + * Custom content view for use in Tk NSWindows. + * + * Since Tk handles all drawing of widgets, we only use the AppKit event loop + * as a source of input events. To do this, we overload the NSView drawRect + * method with a method which generates Expose events for Tk but does no + * drawing. The redrawing operations are then done when Tk processes these + * events. + * + * Earlier versions of Mac Tk used subclasses of NSView, e.g. NSButton, as the + * basis for Tk widgets. These would then appear as subviews of the + * TKContentView. To prevent the AppKit from redrawing and corrupting the Tk + * Widgets it was necessary to use Apple private API calls. In order to avoid + * using private API calls, the NSView-based widgets have been replaced with + * normal Tk widgets which draw themselves as native widgets by using the + * HITheme API. + * */ -@interface TKContentView(TKWindowEvent) -- (void) drawRect: (NSRect) rect; -- (void) generateExposeEvents: (HIMutableShapeRef) shape; -- (BOOL) isOpaque; -- (BOOL) wantsDefaultClipping; -- (BOOL) acceptsFirstResponder; -- (void) keyDown: (NSEvent *) theEvent; -@end - -@implementation TKContentView -@end - +/*Restrict event processing to Expose events.*/ static Tk_RestrictAction ExposeRestrictProc( ClientData arg, @@ -790,6 +795,15 @@ ExposeRestrictProc( ? TK_PROCESS_EVENT : TK_DEFER_EVENT); } +/*Restrict event processing to ConfigureNotify events.*/ +static Tk_RestrictAction +ConfigureRestrictProc( + ClientData arg, + XEvent *eventPtr) +{ + return (eventPtr->type==ConfigureNotify ? TK_PROCESS_EVENT : TK_DEFER_EVENT); +} + @implementation TKContentView(TKWindowEvent) - (void) drawRect: (NSRect) rect @@ -798,6 +812,7 @@ ExposeRestrictProc( NSInteger rectsBeingDrawnCount; [self getRectsBeingDrawn:&rectsBeingDrawn count:&rectsBeingDrawnCount]; + #ifdef TK_MAC_DEBUG_DRAWING TKLog(@"-[%@(%p) %s%@]", [self class], self, _cmd, NSStringFromRect(rect)); [[NSColor colorWithDeviceRed:0.0 green:1.0 blue:0.0 alpha:.1] setFill]; @@ -805,73 +820,135 @@ ExposeRestrictProc( NSCompositeSourceOver); #endif - NSWindow *w = [self window]; - - if ([self isOpaque] && [w showsResizeIndicator]) { - NSRect bounds = [self convertRect:[w _growBoxRect] fromView:nil]; - - if ([self needsToDrawRect:bounds]) { - NSEraseRect(bounds); - } - } - CGFloat height = [self bounds].size.height; HIMutableShapeRef drawShape = HIShapeCreateMutable(); while (rectsBeingDrawnCount--) { CGRect r = NSRectToCGRect(*rectsBeingDrawn++); - r.origin.y = height - (r.origin.y + r.size.height); HIShapeUnionWithRect(drawShape, &r); } if (CFRunLoopGetMain() == CFRunLoopGetCurrent()) { - [self generateExposeEvents:drawShape]; + [self generateExposeEvents:(HIShapeRef)drawShape]; } else { [self performSelectorOnMainThread:@selector(generateExposeEvents:) withObject:(id)drawShape waitUntilDone:NO modes:[NSArray arrayWithObjects:NSRunLoopCommonModes, + NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, nil]]; } + CFRelease(drawShape); } -- (void) generateExposeEvents: (HIMutableShapeRef) shape +-(void) setFrameSize: (NSSize)newsize +{ + [super setFrameSize: newsize]; + if ([self inLiveResize]) { + NSWindow *w = [self window]; + TkWindow *winPtr = TkMacOSXGetTkWindow(w); + Tk_Window tkwin = (Tk_Window) winPtr; + unsigned int width = (unsigned int)newsize.width; + unsigned int height=(unsigned int)newsize.height; + ClientData oldArg; + Tk_RestrictProc *oldProc; + + /* This can be called from outside the Tk event loop. + * Since it calls Tcl_DoOneEvent, we need to make sure we + * don't clobber the AutoreleasePool set up by the caller. + */ + [NSApp setPoolProtected:YES]; + + /* + * Try to prevent flickers and flashes. + */ + [w disableFlushWindow]; + NSDisableScreenUpdates(); + + /* Disable Tk drawing until the window has been completely configured.*/ + TkMacOSXSetDrawingEnabled(winPtr, 0); + + /* Generate and handle a ConfigureNotify event for the new size.*/ + TkGenWMConfigureEvent(tkwin, Tk_X(tkwin), Tk_Y(tkwin), width, height, + TK_SIZE_CHANGED | TK_MACOSX_HANDLE_EVENT_IMMEDIATELY); + oldProc = Tk_RestrictEvents(ConfigureRestrictProc, NULL, &oldArg); + while (Tk_DoOneEvent(TK_X_EVENTS|TK_DONT_WAIT)) {} + Tk_RestrictEvents(oldProc, oldArg, &oldArg); + + /* Now that Tk has configured all subwindows we can create the clip regions. */ + TkMacOSXSetDrawingEnabled(winPtr, 1); + TkMacOSXInvalClipRgns(tkwin); + TkMacOSXUpdateClipRgn(winPtr); + + /* Finally, generate and process expose events to redraw the window. */ + HIRect bounds = NSRectToCGRect([self bounds]); + HIShapeRef shape = HIShapeCreateWithRect(&bounds); + [self generateExposeEvents: shape]; + while (Tk_DoOneEvent(TK_ALL_EVENTS|TK_DONT_WAIT)) {} + [w enableFlushWindow]; + [w flushWindowIfNeeded]; + NSEnableScreenUpdates(); + [NSApp setPoolProtected:NO]; + } +} + +/* + * As insurance against bugs that might cause layout glitches during a live + * resize, we redraw the window one more time at the end of the resize + * operation. + */ + +- (void)viewDidEndLiveResize +{ + HIRect bounds = NSRectToCGRect([self bounds]); + HIShapeRef shape = HIShapeCreateWithRect(&bounds); + [super viewDidEndLiveResize]; + [self generateExposeEvents: shape]; +} + +/* Core method of this class: generates expose events for redrawing. If the + * Tcl_ServiceMode is set to TCL_SERVICE_ALL then the expose events will be + * immediately removed from the Tcl event loop and processed. Typically, they + * should be queued, however. + */ +- (void) generateExposeEvents: (HIShapeRef) shape +{ + [self generateExposeEvents:shape childrenOnly:0]; +} + +- (void) generateExposeEvents: (HIShapeRef) shape + childrenOnly: (int) childrenOnly { TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); unsigned long serial; CGRect updateBounds; + int updatesNeeded; if (!winPtr) { - return; + return; } + + /* Generate Tk Expose events. */ HIShapeGetBounds(shape, &updateBounds); + /* All of these events will share the same serial number. */ serial = LastKnownRequestProcessed(Tk_Display(winPtr)); - if (GenerateUpdates(shape, &updateBounds, winPtr) && - ![[NSRunLoop currentRunLoop] currentMode] && - Tcl_GetServiceMode() != TCL_SERVICE_NONE) { - /* - * Ensure there are no pending idle-time redraws that could prevent the - * just posted Expose events from generating new redraws. - */ - - while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} - - /* - * For smoother drawing, process Expose events and resulting redraws - * immediately instead of at idle time. - */ + updatesNeeded = GenerateUpdates(shape, &updateBounds, winPtr); + /* Process the Expose events if the service mode is TCL_SERVICE_ALL */ + if (updatesNeeded && Tcl_GetServiceMode() == TCL_SERVICE_ALL) { ClientData oldArg; - Tk_RestrictProc *oldProc = Tk_RestrictEvents(ExposeRestrictProc, - UINT2PTR(serial), &oldArg); - - while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {} - Tk_RestrictEvents(oldProc, oldArg, &oldArg); - while (Tcl_DoOneEvent(TCL_IDLE_EVENTS|TCL_DONT_WAIT)) {} + Tk_RestrictProc *oldProc = Tk_RestrictEvents(ExposeRestrictProc, + UINT2PTR(serial), &oldArg); + while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {} + Tk_RestrictEvents(oldProc, oldArg, &oldArg); } } +/* + * This is no-op on 10.7 and up because Apple has removed this widget, + * but we are leaving it here for backwards compatibility. + */ - (void) tkToolbarButton: (id) sender { #ifdef TK_MAC_DEBUG_EVENTS @@ -881,7 +958,6 @@ ExposeRestrictProc( int x, y; TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); Tk_Window tkwin = (Tk_Window) winPtr; - bzero(&event, sizeof(XVirtualEvent)); event.type = VirtualEvent; event.serial = LastKnownRequestProcessed(Tk_Display(tkwin)); @@ -899,27 +975,11 @@ ExposeRestrictProc( Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL); } -#ifdef TK_MAC_DEBUG_DRAWING -- (void) setFrameSize: (NSSize) newSize -{ - TKLog(@"-[%@(%p) %s%@]", [self class], self, _cmd, - NSStringFromSize(newSize)); - [super setFrameSize:newSize]; -} - -- (void) setNeedsDisplayInRect: (NSRect) invalidRect -{ - TKLog(@"-[%@(%p) %s%@]", [self class], self, _cmd, - NSStringFromRect(invalidRect)); - [super setNeedsDisplayInRect:invalidRect]; -} -#endif - - (BOOL) isOpaque { NSWindow *w = [self window]; - if (opaqueTag != NULL) { + if (opaqueTag) { return YES; } else { @@ -947,153 +1007,6 @@ ExposeRestrictProc( @end -#pragma mark TKContentViewPrivate - -/* - * Technique adapted from WebKit/WebKit/mac/WebView/WebHTMLView.mm to supress - * normal AppKit subview drawing and make all drawing go through us. - * Overrides NSView internals. - */ - -@interface TKContentView(TKContentViewPrivate) -- (id) initWithFrame: (NSRect) frame; -- (void) _setAsideSubviews; -- (void) _restoreSubviews; -@end - -@interface NSView(TKContentViewPrivate) -- (void) _recursiveDisplayRectIfNeededIgnoringOpacity: (NSRect) rect - isVisibleRect: (BOOL) isVisibleRect - rectIsVisibleRectForView: (NSView *) visibleView - topView: (BOOL) topView; -- (void) _recursiveDisplayAllDirtyWithLockFocus: (BOOL) needsLockFocus - visRect: (NSRect) visRect; -- (void) _recursive: (BOOL) recurse - displayRectIgnoringOpacity: (NSRect) displayRect - inContext: (NSGraphicsContext *) context topView: (BOOL) topView; -- (void) _lightWeightRecursiveDisplayInRect: (NSRect) visRect; -- (BOOL) _drawRectIfEmpty; -- (void) _drawRect: (NSRect) inRect clip: (BOOL) clip; -- (void) _setDrawsOwnDescendants: (BOOL) drawsOwnDescendants; -@end - -@implementation TKContentView(TKContentViewPrivate) - -- (id) initWithFrame: (NSRect) frame -{ - self = [super initWithFrame:frame]; - if (self) { - _savedSubviews = nil; - _subviewsSetAside = NO; - [self _setDrawsOwnDescendants:YES]; - } - return self; -} - -- (void) _setAsideSubviews -{ -#ifdef TK_MAC_DEBUG - if (_subviewsSetAside || _savedSubviews) { - Tcl_Panic("TKContentView _setAsideSubviews called incorrectly"); - } -#endif - _savedSubviews = _subviews; - _subviews = nil; - _subviewsSetAside = YES; -} - -- (void) _restoreSubviews -{ -#ifdef TK_MAC_DEBUG - if (!_subviewsSetAside || _subviews) { - Tcl_Panic("TKContentView _restoreSubviews called incorrectly"); - } -#endif - _subviews = _savedSubviews; - _savedSubviews = nil; - _subviewsSetAside = NO; -} - -- (void) _recursiveDisplayRectIfNeededIgnoringOpacity: (NSRect) rect - isVisibleRect: (BOOL) isVisibleRect - rectIsVisibleRectForView: (NSView *) visibleView - topView: (BOOL) topView -{ - [self _setAsideSubviews]; - [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect - isVisibleRect:isVisibleRect rectIsVisibleRectForView:visibleView - topView:topView]; - [self _restoreSubviews]; -} - -- (void) _recursiveDisplayAllDirtyWithLockFocus: (BOOL) needsLockFocus - visRect: (NSRect) visRect -{ - BOOL needToSetAsideSubviews = !_subviewsSetAside; - - if (needToSetAsideSubviews) { - [self _setAsideSubviews]; - } - [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus - visRect:visRect]; - if (needToSetAsideSubviews) { - [self _restoreSubviews]; - } -} - -- (void) _recursive: (BOOL) recurse - displayRectIgnoringOpacity: (NSRect) displayRect - inContext: (NSGraphicsContext *) context topView: (BOOL) topView -{ - [self _setAsideSubviews]; - [super _recursive:recurse - displayRectIgnoringOpacity:displayRect inContext:context - topView:topView]; - [self _restoreSubviews]; -} - -- (void) _lightWeightRecursiveDisplayInRect: (NSRect) visRect -{ - BOOL needToSetAsideSubviews = !_subviewsSetAside; - - if (needToSetAsideSubviews) { - [self _setAsideSubviews]; - } - [super _lightWeightRecursiveDisplayInRect:visRect]; - if (needToSetAsideSubviews) { - [self _restoreSubviews]; - } -} - -- (BOOL) _drawRectIfEmpty -{ - /* - * Our -drawRect manages subview drawing directly, so it needs to be called - * even if the area to be redrawn is completely obscured by subviews. - */ - - return YES; -} - -- (void) _drawRect: (NSRect) inRect clip: (BOOL) clip -{ -#ifdef TK_MAC_DEBUG_DRAWING - TKLog(@"-[%@(%p) %s%@]", [self class], self, _cmd, - NSStringFromRect(inRect)); -#endif - BOOL subviewsWereSetAside = _subviewsSetAside; - - if (subviewsWereSetAside) { - [self _restoreSubviews]; - } - [super _drawRect:inRect clip:clip]; - if (subviewsWereSetAside) { - [self _setAsideSubviews]; - } -} - -@end - /* * Local Variables: * mode: objc |