From 9a2585491c7164eb5d4de469786d3c935ab3dcf0 Mon Sep 17 00:00:00 2001 From: marc_culler Date: Sun, 27 Sep 2020 19:14:42 +0000 Subject: Cherrypick the fixes for macOS MapNotify and UnmapNotify from mac_styles --- generic/tkWindow.c | 6 +-- macosx/tkMacOSXPort.h | 8 ++++ macosx/tkMacOSXSubwindows.c | 78 ++++++++++------------------------- macosx/tkMacOSXWindowEvent.c | 3 +- macosx/tkMacOSXWm.c | 96 ++++++++++++++++++++++++++++++++++++++++++-- unix/tkUnixPort.h | 6 +++ win/tkWinPort.h | 6 +++ 7 files changed, 140 insertions(+), 63 deletions(-) diff --git a/generic/tkWindow.c b/generic/tkWindow.c index 5e5e836..92c4018 100644 --- a/generic/tkWindow.c +++ b/generic/tkWindow.c @@ -13,7 +13,7 @@ */ #include "tkInt.h" - +#include "tkPort.h" #ifdef _WIN32 #include "tkWinInt.h" #elif !defined(MAC_OSX_TK) @@ -1648,7 +1648,7 @@ Tk_MapWindow( event.xmap.event = winPtr->window; event.xmap.window = winPtr->window; event.xmap.override_redirect = winPtr->atts.override_redirect; - Tk_HandleEvent(&event); + TkpHandleMapOrUnmap((Tk_Window)winPtr, &event); } /* @@ -1810,7 +1810,7 @@ Tk_UnmapWindow( event.xunmap.event = winPtr->window; event.xunmap.window = winPtr->window; event.xunmap.from_configure = False; - Tk_HandleEvent(&event); + TkpHandleMapOrUnmap((Tk_Window)winPtr, &event); } } diff --git a/macosx/tkMacOSXPort.h b/macosx/tkMacOSXPort.h index ba47566..76dd974 100644 --- a/macosx/tkMacOSXPort.h +++ b/macosx/tkMacOSXPort.h @@ -177,4 +177,12 @@ MODULE_SCOPE unsigned long TkMacOSXRGBPixel(unsigned long red, unsigned long gre unsigned long blue); #define TkpGetPixel(p) (TkMacOSXRGBPixel(p->red >> 8, p->green >> 8, p->blue >> 8)) +/* + * Used by tkWindow.c + */ + +MODULE_SCOPE void TkMacOSXHandleMapOrUnmap(Tk_Window tkwin, XEvent *event); + +#define TkpHandleMapOrUnmap(tkwin, event) TkMacOSXHandleMapOrUnmap(tkwin, event) + #endif /* _TKMACPORT */ diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index 6ac766c..e840cea 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -119,14 +119,18 @@ XDestroyWindow( * * XMapWindow -- * - * Map the given X Window to the screen. See X window documentation for - * more details. + * This X11 stub maps the given X11 Window but does not update any of + * the Tk structures describing the window. Tk applications should + * never call this directly, but it is called by Tk_MapWindow and + * Tk_WmMapWindow. * * Results: - * None. + * Returns Success or BadWindow. * * Side effects: - * The subwindow or toplevel may appear on the screen. + * The subwindow or toplevel may appear on the screen. VisibilityNotify + * events are generated. + * * *---------------------------------------------------------------------- */ @@ -142,7 +146,6 @@ XMapWindow( MacDrawable *macWin = (MacDrawable *)window; TkWindow *winPtr = macWin->winPtr; NSWindow *win = TkMacOSXGetNSWindowForDrawable(window); - XEvent event; static Bool initialized = NO; /* @@ -158,7 +161,6 @@ XMapWindow( } display->request++; - winPtr->flags |= TK_MAPPED; if (Tk_IsTopLevel(winPtr)) { if (!Tk_IsEmbedded(winPtr)) { TKContentView *view = [win contentView]; @@ -193,30 +195,7 @@ XMapWindow( TkMacOSXInvalClipRgns((Tk_Window)contWinPtr); TkMacOSXInvalidateWindow(macWin, TK_PARENT_WINDOW); } - TkMacOSXInvalClipRgns((Tk_Window)winPtr); - - /* - * We only need to send the MapNotify event for toplevel windows. - */ - - event.xany.serial = LastKnownRequestProcessed(display); - event.xany.send_event = False; - event.xany.display = display; - - event.xmap.window = window; - event.xmap.type = MapNotify; - event.xmap.event = window; - event.xmap.override_redirect = winPtr->atts.override_redirect; - - /* - * To update the mapped status of packed or placed subwindows - * we handle this event immediately and then process the idle - * events that it generates. - */ - - Tk_HandleEvent(&event); - while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} } else { /* @@ -227,6 +206,11 @@ XMapWindow( TkMacOSXInvalClipRgns((Tk_Window)winPtr->parentPtr); } + /* + * Mark the toplevel as needing to be redrawn, unless the window is being + * mapped while drawing is taking place. + */ + TKContentView *view = [win contentView]; if (view != [NSView focusView]) { [view addTkDirtyRect:[view bounds]]; @@ -237,6 +221,7 @@ XMapWindow( */ if (initialized) { + XEvent event; event.xany.send_event = False; event.xany.display = display; event.xvisibility.type = VisibilityNotify; @@ -287,11 +272,13 @@ NotifyVisibility( * * XUnmapWindow -- * - * Unmap the given X Window to the screen. See X window documentation for - * more details. + * This X11 stub maps the given X11 Window but does not update any of + * The Tk structures describing the window. Tk applications should + * never call this directly, but it is called by Tk_UnmapWindow and + * Tk_WmUnmapWindow. * * Results: - * None. + * Always returns Success or BadWindow. * * Side effects: * The subwindow or toplevel may be removed from the screen. @@ -308,8 +295,10 @@ XUnmapWindow( TkWindow *winPtr = macWin->winPtr; TkWindow *parentPtr = winPtr->parentPtr; NSWindow *win = TkMacOSXGetNSWindowForDrawable(window); - XEvent event; + if (!window) { + return BadWindow; + } display->request++; if (Tk_IsTopLevel(winPtr)) { if (!Tk_IsEmbedded(winPtr) && @@ -318,28 +307,6 @@ XUnmapWindow( [win setExcludedFromWindowsMenu:YES]; } TkMacOSXInvalClipRgns((Tk_Window)winPtr); - - /* - * We only need to send the UnmapNotify event for toplevel windows. - */ - - event.xany.serial = LastKnownRequestProcessed(display); - event.xany.send_event = False; - event.xany.display = display; - - event.xunmap.type = UnmapNotify; - event.xunmap.window = window; - event.xunmap.event = window; - event.xunmap.from_configure = false; - - /* - * To update the mapped status of packed or placed subwindows - * we handle this event immediately and then process the idle - * events that it generates. - */ - - Tk_HandleEvent(&event); - while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {} } else { /* * Rebuild the visRgn clip region for the parent so it will be allowed @@ -355,7 +322,6 @@ XUnmapWindow( TkMacOSXInvalClipRgns((Tk_Window)parentPtr); TkMacOSXUpdateClipRgn(parentPtr); } - winPtr->flags &= ~TK_MAPPED; TKContentView *view = [win contentView]; if (view != [NSView focusView]) { [view addTkDirtyRect:[view bounds]]; diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index fcbd134..5a2809c 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -1086,7 +1086,7 @@ ConfigureRestrictProc( } /* - * In macOS 10.14 and later his method is called when a user changes between + * In macOS 10.14 and later this method is called when a user changes between * light and dark mode or changes the accent color. The implementation * generates two virtual events. The first is either <> or * <>, depending on the view's current effective appearance. The @@ -1134,6 +1134,7 @@ static const char *const accentNames[] = { } if (!defaultColor) { defaultColor = [NSApp macOSVersion] < 110000 ? "Blue" : "Multicolor"; + preferences = [[NSUserDefaults standardUserDefaults] retain]; /* * AppKit calls this method when the user changes the Accent Color diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index 705648d..4a3f561 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -746,6 +746,74 @@ TkWmNewWindow( /* *---------------------------------------------------------------------- * + * TkMacOSXHandleMapOrUnmap -- + * + * The mechanism used by a geometry manager to propogate the information + * about which of its content widgets are mapped is to call Tk_MapWindow + * or Tk_UnmapNotify. Those functions generate MapNotify or UnmapNotify + * events and then handle them immediately. Other platforms use + * Tk_HandleEvent to do this. But that does not work correctly on macOS + * due to the fact that the calls to Tk_MapNotify or Tk_UnmapNotify can + * occur in display procedures which are being run in the drawRect method + * of a TKContentView. The events will be processed after drawRect + * returns, but they need to be processed immediately in some cases. + + * This function operates as a macOS alternative to Tk_HandleEvent, for + * processing MapNotify or UnmapNotify events only. It is called by + * Tk_MapWindow, Tk_UnmapWindow, TkWmMapWindow and TkWmUnmapWindow. + * Rather than using Tk_HandleEvent it installs a filter which restricts + * to the MapNotify or UnmapNotify events, it queues the event and then + * processes window events with the filter installed. This allows the + * event to be handled immediately even from within the drawRect method. + * + * Results: + * None. + * + * Side effects: + * Handles a MapNotify or UnMapNotify event. + * + *---------------------------------------------------------------------- + */ +static Tk_RestrictAction +MapUnmapRestrictProc( + ClientData arg, + XEvent *eventPtr) +{ + return (eventPtr->type==MapNotify || eventPtr->type==UnmapNotify ? + TK_PROCESS_EVENT : TK_DEFER_EVENT); +} + +MODULE_SCOPE +void TkMacOSXHandleMapOrUnmap( + Tk_Window tkwin, + XEvent *event) +{ + ClientData oldArg; + Tk_RestrictProc *oldProc; + TkWindow *winPtr = (TkWindow *) tkwin; + const Tk_GeomMgr *geomMgrPtr = winPtr->geomMgrPtr; + + /* + * Sadly, this approach does not work with the "text" geometry manager. + * The mysterious unexplained crash elicited by textDisp-5.2 occurs. So we + * have to check for the "text" manager and revert to using Tk_HandleEvent + * in that case. Hopefully this can be removed when the revised text + * widget is in place. + */ + + if (geomMgrPtr && strcmp(geomMgrPtr->name, "text") == 0) { + Tk_HandleEvent(event); + return; + } + oldProc = Tk_RestrictEvents(MapUnmapRestrictProc, NULL, &oldArg); + Tk_QueueWindowEvent(event, TCL_QUEUE_TAIL); + while (Tcl_DoOneEvent(TCL_WINDOW_EVENTS|TCL_DONT_WAIT)) {} + Tk_RestrictEvents(oldProc, oldArg, &oldArg); +} + +/* + *---------------------------------------------------------------------- + * * TkWmMapWindow -- * * This procedure is invoked to map a top-level window. This module gets @@ -772,6 +840,8 @@ TkWmMapWindow( * mapped. */ { WmInfo *wmPtr = winPtr->wmInfoPtr; + XEvent event; + if (wmPtr->flags & WM_NEVER_MAPPED) { /* * Create the underlying Mac window for this Tk window. @@ -835,10 +905,19 @@ TkWmMapWindow( wmPtr->flags &= ~WM_ABOUT_TO_MAP; /* - * Map the window. + * Map the window and process a MapNotify event for it. */ + winPtr->flags |= TK_MAPPED; XMapWindow(winPtr->display, winPtr->window); + event.xany.serial = LastKnownRequestProcessed(winPtr->display); + event.xany.send_event = False; + event.xany.display = winPtr->display; + event.xmap.window = winPtr->window; + event.xmap.type = MapNotify; + event.xmap.event = winPtr->window; + event.xmap.override_redirect = winPtr->atts.override_redirect; + TkpHandleMapOrUnmap((Tk_Window)winPtr, &event); } /* @@ -863,7 +942,18 @@ TkWmUnmapWindow( TkWindow *winPtr) /* Top-level window that's about to be * unmapped. */ { + XEvent event; + + event.xany.serial = LastKnownRequestProcessed(winPtr->display); + event.xany.send_event = False; + event.xany.display = winPtr->display; + event.xunmap.type = UnmapNotify; + event.xunmap.window = winPtr->window; + event.xunmap.event = winPtr->window; + event.xunmap.from_configure = false; + winPtr->flags &= ~TK_MAPPED; XUnmapWindow(winPtr->display, winPtr->window); + TkpHandleMapOrUnmap((Tk_Window)winPtr, &event); } /* @@ -6969,9 +7059,9 @@ ApplyContainerOverrideChanges( if (parentWindow && parentWindow != containerMacWin) { [parentWindow removeChildWindow:macWindow]; } - + [macWindow orderFront:NSApp]; [containerMacWin addChildWindow:macWindow - ordered:NSWindowAbove]; + ordered:NSWindowAbove]; } } } else { diff --git a/unix/tkUnixPort.h b/unix/tkUnixPort.h index 09ff558..7c6177e 100644 --- a/unix/tkUnixPort.h +++ b/unix/tkUnixPort.h @@ -191,4 +191,10 @@ sprintf((buf), "%#08lx", (unsigned long) (w)) #endif +/* + * Used by tkWindow.c + */ + +#define TkpHandleMapOrUnmap(tkwin, event) Tk_HandleEvent(event) + #endif /* _UNIXPORT */ diff --git a/win/tkWinPort.h b/win/tkWinPort.h index 4311b09..337a866 100644 --- a/win/tkWinPort.h +++ b/win/tkWinPort.h @@ -121,6 +121,12 @@ | ((p)->green & 0xff00) | (((p)->blue << 8) & 0xff0000)) | 0x20000000) /* + * Used by tkWindow.c + */ + +#define TkpHandleMapOrUnmap(tkwin, event) Tk_HandleEvent(event) + +/* * These calls implement native bitmaps which are not currently * supported under Windows. The macros eliminate the calls. */ -- cgit v0.12