From e9abcde031f201bc41e80381970e72259d3c0bf6 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 28 Feb 2022 13:57:29 -0800 Subject: [PATCH 01/14] macos: check for best_monitor before using Make sure we have a monitor to enqueue/dequeue from before using it. That also allows us to use this from destroy and what-not. --- gdk/macos/gdkmacossurface.c | 41 +++++++++++++++---------------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index cb900a7328..442ea485ca 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -72,9 +72,12 @@ _gdk_macos_surface_request_frame (GdkMacosSurface *self) if (self->awaiting_frame) return; - self->awaiting_frame = TRUE; - _gdk_macos_monitor_add_frame_callback (GDK_MACOS_MONITOR (self->best_monitor), self); - gdk_surface_freeze_updates (GDK_SURFACE (self)); + if (self->best_monitor != NULL) + { + self->awaiting_frame = TRUE; + _gdk_macos_monitor_add_frame_callback (GDK_MACOS_MONITOR (self->best_monitor), self); + gdk_surface_freeze_updates (GDK_SURFACE (self)); + } } static void @@ -85,9 +88,12 @@ _gdk_macos_surface_cancel_frame (GdkMacosSurface *self) if (!self->awaiting_frame) return; - self->awaiting_frame = FALSE; - _gdk_macos_monitor_remove_frame_callback (GDK_MACOS_MONITOR (self->best_monitor), self); - gdk_surface_thaw_updates (GDK_SURFACE (self)); + if (self->best_monitor != NULL) + { + self->awaiting_frame = FALSE; + _gdk_macos_monitor_remove_frame_callback (GDK_MACOS_MONITOR (self->best_monitor), self); + gdk_surface_thaw_updates (GDK_SURFACE (self)); + } } void @@ -199,7 +205,9 @@ gdk_macos_surface_hide (GdkSurface *surface) self->show_on_next_swap = FALSE; - was_mapped = GDK_SURFACE_IS_MAPPED (GDK_SURFACE (self)); + _gdk_macos_surface_cancel_frame (self); + + was_mapped = GDK_SURFACE_IS_MAPPED (surface); was_key = [self->window isKeyWindow]; seat = gdk_display_get_default_seat (surface->display); @@ -222,13 +230,6 @@ gdk_macos_surface_hide (GdkSurface *surface) [parentWindow showAndMakeKey:YES]; } } - - if (self->awaiting_frame) - { - self->awaiting_frame = FALSE; - _gdk_macos_monitor_remove_frame_callback (GDK_MACOS_MONITOR (self->best_monitor), self); - gdk_surface_freeze_updates (surface); - } } static int @@ -469,16 +470,8 @@ gdk_macos_surface_destroy (GdkSurface *surface, GdkMacosWindow *window = g_steal_pointer (&self->window); GdkFrameClock *frame_clock; - if (self->best_monitor) - { - if (self->awaiting_frame) - { - _gdk_macos_monitor_remove_frame_callback (GDK_MACOS_MONITOR (self->best_monitor), self); - self->awaiting_frame = FALSE; - } - - g_clear_object (&self->best_monitor); - } + _gdk_macos_surface_cancel_frame (self); + g_clear_object (&self->best_monitor); if ((frame_clock = gdk_surface_get_frame_clock (GDK_SURFACE (self)))) { From e5238bf54f42036f76e2b6d227f5c83a74e35251 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 2 Mar 2022 00:32:50 -0800 Subject: [PATCH 02/14] macos: add re-entrancy check when monitors change --- gdk/macos/gdkmacossurface-private.h | 1 + gdk/macos/gdkmacossurface.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/gdk/macos/gdkmacossurface-private.h b/gdk/macos/gdkmacossurface-private.h index 15a7442c17..5f1f551f93 100644 --- a/gdk/macos/gdkmacossurface-private.h +++ b/gdk/macos/gdkmacossurface-private.h @@ -75,6 +75,7 @@ struct _GdkMacosSurface guint geometry_dirty : 1; guint next_frame_set : 1; guint show_on_next_swap : 1; + guint in_change_monitor : 1; guint in_frame : 1; guint awaiting_frame : 1; }; diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index 442ea485ca..cd376f64c3 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -1061,6 +1061,11 @@ _gdk_macos_surface_monitor_changed (GdkMacosSurface *self) g_return_if_fail (GDK_IS_MACOS_SURFACE (self)); + if (self->in_change_monitor) + return; + + self->in_change_monitor = TRUE; + _gdk_macos_surface_cancel_frame (self); rect.x = self->root_x; @@ -1129,6 +1134,8 @@ _gdk_macos_surface_monitor_changed (GdkMacosSurface *self) _gdk_macos_surface_configure (self); gdk_surface_invalidate_rect (GDK_SURFACE (self), NULL); + + self->in_change_monitor = FALSE; _gdk_macos_surface_request_frame (self); } From 8c0df66d5fff21e8e2c8c09df8a6e726a688781a Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 2 Mar 2022 00:33:13 -0800 Subject: [PATCH 03/14] macos: style cleanup --- gdk/macos/gdkmacospopupsurface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gdk/macos/gdkmacospopupsurface.c b/gdk/macos/gdkmacospopupsurface.c index 5a2d7ed481..dc6610b398 100644 --- a/gdk/macos/gdkmacospopupsurface.c +++ b/gdk/macos/gdkmacospopupsurface.c @@ -275,7 +275,7 @@ _gdk_macos_popup_surface_class_init (GdkMacosPopupSurfaceClass *klass) object_class->get_property = _gdk_macos_popup_surface_get_property; object_class->set_property = _gdk_macos_popup_surface_set_property; - gdk_popup_install_properties (object_class, 1); + gdk_popup_install_properties (object_class, LAST_PROP); } static void From 27b87ebec53e1af8bc819bfdabc2bc8deaea4749 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 2 Mar 2022 00:34:27 -0800 Subject: [PATCH 04/14] macos: add clamp helper to keep rectangle in workarea This helper is useful to ensure we are consistent with how we keep a window clamped to the workarea of a monitor when placing windows on screen. (This does not affect snap-to-edges). --- gdk/macos/gdkmacosdisplay-wm.c | 41 +++++++---------------------- gdk/macos/gdkmacosmonitor-private.h | 2 ++ gdk/macos/gdkmacosmonitor.c | 26 ++++++++++++++++++ 3 files changed, 37 insertions(+), 32 deletions(-) diff --git a/gdk/macos/gdkmacosdisplay-wm.c b/gdk/macos/gdkmacosdisplay-wm.c index 7b508a4a9e..4f0672013c 100644 --- a/gdk/macos/gdkmacosdisplay-wm.c +++ b/gdk/macos/gdkmacosdisplay-wm.c @@ -20,7 +20,7 @@ #include "config.h" #include "gdkmacosdisplay-private.h" -#include "gdkmacosmonitor.h" +#include "gdkmacosmonitor-private.h" #include "gdkmacossurface-private.h" #include "gdkmacostoplevelsurface-private.h" @@ -36,47 +36,28 @@ _gdk_macos_display_position_toplevel_with_parent (GdkMacosDisplay *self, { GdkRectangle surface_rect; GdkRectangle parent_rect; - GdkRectangle workarea; GdkMonitor *monitor; g_assert (GDK_IS_MACOS_DISPLAY (self)); g_assert (GDK_IS_MACOS_TOPLEVEL_SURFACE (surface)); g_assert (GDK_IS_MACOS_TOPLEVEL_SURFACE (parent)); - /* If x/y is set, we should place relative to parent */ - if (GDK_SURFACE (surface)->x != 0 || GDK_SURFACE (surface)->y != 0) - { - *x = parent->root_x + GDK_SURFACE (surface)->x; - *y = parent->root_y + GDK_SURFACE (surface)->y; - return; - } + monitor = _gdk_macos_surface_get_best_monitor (parent); /* Try to center on top of the parent but also try to make the whole thing * visible in case that lands us under the topbar/panel/etc. */ - surface_rect.x = surface->root_x + surface->shadow_left; - surface_rect.y = surface->root_y + surface->shadow_top; + parent_rect.x = parent->root_x + parent->shadow_left; + parent_rect.y = parent->root_y + parent->shadow_top; + parent_rect.width = GDK_SURFACE (parent)->width - parent->shadow_left - parent->shadow_right; + parent_rect.height = GDK_SURFACE (parent)->height - parent->shadow_top - parent->shadow_bottom; + surface_rect.width = GDK_SURFACE (surface)->width - surface->shadow_left - surface->shadow_right; surface_rect.height = GDK_SURFACE (surface)->height - surface->shadow_top - surface->shadow_bottom; - - parent_rect.x = parent->root_x + surface->shadow_left; - parent_rect.y = parent->root_y + surface->shadow_top; - parent_rect.width = GDK_SURFACE (parent)->width - surface->shadow_left - surface->shadow_right; - parent_rect.height = GDK_SURFACE (parent)->height - surface->shadow_top - surface->shadow_bottom; - - /* Try to place centered atop parent */ surface_rect.x = parent_rect.x + ((parent_rect.width - surface_rect.width) / 2); surface_rect.y = parent_rect.y + ((parent_rect.height - surface_rect.height) / 2); - /* Now make sure that we don't overlap the top-bar */ - monitor = _gdk_macos_surface_get_best_monitor (parent); - gdk_macos_monitor_get_workarea (monitor, &workarea); - - if (surface_rect.x < workarea.x) - surface_rect.x = workarea.x; - - if (surface_rect.y < workarea.y) - surface_rect.y = workarea.y; + _gdk_macos_monitor_clamp (GDK_MACOS_MONITOR (monitor), &surface_rect); *x = surface_rect.x - surface->shadow_left; *y = surface_rect.y - surface->shadow_top; @@ -123,11 +104,7 @@ _gdk_macos_display_position_toplevel (GdkMacosDisplay *self, surface_rect.x = workarea.x + ((workarea.width - surface_rect.width) / 2); surface_rect.y = workarea.y + ((workarea.height - surface_rect.height) / 2); - if (surface_rect.x < workarea.x) - surface_rect.x = workarea.x; - - if (surface_rect.y < workarea.y) - surface_rect.y = workarea.y; + _gdk_macos_monitor_clamp (GDK_MACOS_MONITOR (surface->best_monitor), &surface_rect); *x = surface_rect.x - surface->shadow_left; *y = surface_rect.y - surface->shadow_top; diff --git a/gdk/macos/gdkmacosmonitor-private.h b/gdk/macos/gdkmacosmonitor-private.h index dfde4142c0..1a4e197f76 100644 --- a/gdk/macos/gdkmacosmonitor-private.h +++ b/gdk/macos/gdkmacosmonitor-private.h @@ -41,6 +41,8 @@ void _gdk_macos_monitor_add_frame_callback (GdkMacosMonitor * GdkMacosSurface *surface); void _gdk_macos_monitor_remove_frame_callback (GdkMacosMonitor *self, GdkMacosSurface *surface); +void _gdk_macos_monitor_clamp (GdkMacosMonitor *self, + GdkRectangle *area); G_END_DECLS diff --git a/gdk/macos/gdkmacosmonitor.c b/gdk/macos/gdkmacosmonitor.c index bfec76b7ef..a9aba3634b 100644 --- a/gdk/macos/gdkmacosmonitor.c +++ b/gdk/macos/gdkmacosmonitor.c @@ -431,3 +431,29 @@ _gdk_macos_monitor_remove_frame_callback (GdkMacosMonitor *self, gdk_display_link_source_pause (self->display_link); } } + +void +_gdk_macos_monitor_clamp (GdkMacosMonitor *self, + GdkRectangle *area) +{ + GdkRectangle workarea; + GdkRectangle geom; + + g_return_if_fail (GDK_IS_MACOS_MONITOR (self)); + g_return_if_fail (area != NULL); + + gdk_macos_monitor_get_workarea (GDK_MONITOR (self), &workarea); + gdk_monitor_get_geometry (GDK_MONITOR (self), &geom); + + if (area->x + area->width > workarea.x + workarea.width) + area->x = workarea.x + workarea.width - area->width; + + if (area->x < workarea.x) + area->x = workarea.x; + + if (area->y + area->height > workarea.y + workarea.height) + area->y = workarea.y + workarea.height - area->height; + + if (area->y < workarea.y) + area->y = workarea.y; +} From bdd5393084fa9118049d3871030457f81e02a175 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 2 Mar 2022 00:36:17 -0800 Subject: [PATCH 05/14] macos: use GdkMacosBuffer for storing damage region The GdkMacosBuffer object already has storage for tracking the damage region as it is used in GdkMacosCairoContext to manually copy regions from the front buffer to the back buffer. This makes the GdkMacosGLContext also use that field so that we can easily drop old damage regions when the buffer is lost. This happens during resizes, monitor changes, etc. --- gdk/macos/gdkmacosglcontext-private.h | 2 -- gdk/macos/gdkmacosglcontext.c | 19 ++++++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/gdk/macos/gdkmacosglcontext-private.h b/gdk/macos/gdkmacosglcontext-private.h index 8b3eac2ca6..7355ffef90 100644 --- a/gdk/macos/gdkmacosglcontext-private.h +++ b/gdk/macos/gdkmacosglcontext-private.h @@ -38,8 +38,6 @@ struct _GdkMacosGLContext { GdkGLContext parent_instance; - cairo_region_t *damage; - G_GNUC_BEGIN_IGNORE_DEPRECATIONS CGLContextObj cgl_context; G_GNUC_END_IGNORE_DEPRECATIONS diff --git a/gdk/macos/gdkmacosglcontext.c b/gdk/macos/gdkmacosglcontext.c index 5baff95a9b..ff7ae975c8 100644 --- a/gdk/macos/gdkmacosglcontext.c +++ b/gdk/macos/gdkmacosglcontext.c @@ -469,6 +469,7 @@ gdk_macos_gl_context_begin_frame (GdkDrawContext *context, buffer = _gdk_macos_surface_get_buffer (GDK_MACOS_SURFACE (surface)); _gdk_macos_buffer_set_flipped (buffer, TRUE); + _gdk_macos_buffer_set_damage (buffer, region); /* Create our render target and bind it */ gdk_gl_context_make_current (GDK_GL_CONTEXT (self)); @@ -476,9 +477,6 @@ gdk_macos_gl_context_begin_frame (GdkDrawContext *context, GDK_DRAW_CONTEXT_CLASS (gdk_macos_gl_context_parent_class)->begin_frame (context, prefers_high_depth, region); - g_clear_pointer (&self->damage, cairo_region_destroy); - self->damage = g_steal_pointer (©); - gdk_gl_context_make_current (GDK_GL_CONTEXT (self)); CHECK_GL (NULL, glBindFramebuffer (GL_FRAMEBUFFER, self->fbo)); } @@ -531,8 +529,6 @@ gdk_macos_gl_context_surface_resized (GdkDrawContext *draw_context) g_assert (GDK_IS_MACOS_GL_CONTEXT (self)); - g_clear_pointer (&self->damage, cairo_region_destroy); - if (self->cgl_context != NULL) CGLUpdateContext (self->cgl_context); } @@ -587,9 +583,16 @@ static cairo_region_t * gdk_macos_gl_context_get_damage (GdkGLContext *context) { GdkMacosGLContext *self = (GdkMacosGLContext *)context; + const cairo_region_t *damage; + GdkMacosBuffer *buffer; + GdkSurface *surface; - if (self->damage) - return cairo_region_copy (self->damage); + g_assert (GDK_IS_MACOS_GL_CONTEXT (self)); + + if ((surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (context))) && + (buffer = GDK_MACOS_SURFACE (surface)->front) && + (damage = _gdk_macos_buffer_get_damage (buffer))) + return cairo_region_copy (damage); return GDK_GL_CONTEXT_CLASS (gdk_macos_gl_context_parent_class)->get_damage (context); } @@ -619,8 +622,6 @@ gdk_macos_gl_context_dispose (GObject *gobject) CGLDestroyContext (cgl_context); } - g_clear_pointer (&self->damage, cairo_region_destroy); - G_OBJECT_CLASS (gdk_macos_gl_context_parent_class)->dispose (gobject); } From 4cdb07fa0276b736c89178b31b89a532a80654f0 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 2 Mar 2022 00:37:07 -0800 Subject: [PATCH 06/14] macos: leave a note about monitor configuration It can be helpful to see what the range of monitor values is when emulating the GDK coordinate system. --- gdk/macos/gdkmacosdisplay.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gdk/macos/gdkmacosdisplay.c b/gdk/macos/gdkmacosdisplay.c index ddcc274372..798633f33a 100644 --- a/gdk/macos/gdkmacosdisplay.c +++ b/gdk/macos/gdkmacosdisplay.c @@ -156,6 +156,10 @@ gdk_macos_display_update_bounds (GdkMacosDisplay *self) self->width = self->max_x - self->min_x; self->height = self->max_y - self->min_y; + GDK_NOTE (MISC, + g_message ("Displays reconfigured to bounds %d,%d %dx%d", + self->min_x, self->min_y, self->width, self->height)); + GDK_END_MACOS_ALLOC_POOL; } From 82f1eaacc92c040c5c825241d435eb691c29ef55 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 2 Mar 2022 00:38:39 -0800 Subject: [PATCH 07/14] macos: improve initial placement of toplevels with parent This doesn't appear to happen much, but if it does it is nice to setup the window placement initially. Generally, transient-for is set after the creation of the toplevel rather than here. --- gdk/macos/gdkmacostoplevelsurface.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/gdk/macos/gdkmacostoplevelsurface.c b/gdk/macos/gdkmacostoplevelsurface.c index d89434593b..1759077563 100644 --- a/gdk/macos/gdkmacostoplevelsurface.c +++ b/gdk/macos/gdkmacostoplevelsurface.c @@ -648,10 +648,10 @@ _gdk_macos_toplevel_surface_new (GdkMacosDisplay *display, GdkMacosWindow *window; GdkMacosSurface *self; - NSScreen *screen; NSUInteger style_mask; NSRect content_rect; - NSRect screen_rect; + NSRect visible_frame; + NSScreen *screen; int nx; int ny; @@ -664,14 +664,17 @@ _gdk_macos_toplevel_surface_new (GdkMacosDisplay *display, NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable); - _gdk_macos_display_to_display_coords (display, x, y, &nx, &ny); + if (parent != NULL) + { + x += GDK_MACOS_SURFACE (parent)->root_x; + y += GDK_MACOS_SURFACE (parent)->root_y; + } + + _gdk_macos_display_to_display_coords (display, x, y + height, &nx, &ny); screen = _gdk_macos_display_get_screen_at_display_coords (display, nx, ny); - screen_rect = [screen visibleFrame]; - nx -= screen_rect.origin.x; - ny -= screen_rect.origin.y; - content_rect = NSMakeRect (nx, ny - height, width, height); - + visible_frame = [screen visibleFrame]; + content_rect = NSMakeRect (nx - visible_frame.origin.x, ny - visible_frame.origin.y, width, height); window = [[GdkMacosWindow alloc] initWithContentRect:content_rect styleMask:style_mask backing:NSBackingStoreBuffered @@ -707,13 +710,21 @@ _gdk_macos_toplevel_surface_attach_to_parent (GdkMacosToplevelSurface *self) { NSWindow *parent = _gdk_macos_surface_get_native (GDK_MACOS_SURFACE (surface->transient_for)); NSWindow *window = _gdk_macos_surface_get_native (GDK_MACOS_SURFACE (self)); + int x, y; [parent addChildWindow:window ordered:NSWindowAbove]; if (GDK_SURFACE (self)->modal_hint) [window setLevel:NSModalPanelWindowLevel]; + surface->x = 0; + surface->y = 0; + _gdk_macos_display_clear_sorting (GDK_MACOS_DISPLAY (surface->display)); + _gdk_macos_display_position_surface (GDK_MACOS_DISPLAY (surface->display), + GDK_MACOS_SURFACE (surface), + &x, &y); + _gdk_macos_surface_move (GDK_MACOS_SURFACE (surface), x, y); } } From ddf07ffe2203baed6dcd34e91e065461b7b0fe24 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 2 Mar 2022 00:40:21 -0800 Subject: [PATCH 08/14] macos: reduce chances for layout cycles We need to be more careful about when we request a layout because it can cause us to get into a layout cycle at maximum frame rate. --- gdk/macos/gdkmacossurface.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index cd376f64c3..4bf2601f45 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -158,9 +158,6 @@ _gdk_macos_surface_reposition_children (GdkMacosSurface *self) if (GDK_IS_MACOS_POPUP_SURFACE (child)) _gdk_macos_popup_surface_reposition (GDK_MACOS_POPUP_SURFACE (child)); } - - if (GDK_IS_POPUP (self) && self->did_initial_present) - gdk_surface_request_layout (GDK_SURFACE (self)); } static void @@ -877,7 +874,6 @@ _gdk_macos_surface_configure (GdkMacosSurface *self) g_clear_object (&self->front); _gdk_surface_update_size (surface); - gdk_surface_request_layout (surface); gdk_surface_invalidate_rect (surface, NULL); } @@ -1136,7 +1132,6 @@ _gdk_macos_surface_monitor_changed (GdkMacosSurface *self) gdk_surface_invalidate_rect (GDK_SURFACE (self), NULL); self->in_change_monitor = FALSE; - _gdk_macos_surface_request_frame (self); } GdkMonitor * From cf25f2c04b8da91bb8d232f783e58873e0439b10 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 2 Mar 2022 00:42:09 -0800 Subject: [PATCH 09/14] macos: use parent frame clock again We do actually need the parent frame clock here because it is the way we ensure that we get layout called for our popup surfaces at the same time as the parent surface. --- gdk/macos/gdkmacossurface.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index 4bf2601f45..1c971d9e95 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -626,7 +626,10 @@ _gdk_macos_surface_new (GdkMacosDisplay *display, g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (display), NULL); - frame_clock = _gdk_frame_clock_idle_new (); + if (parent != NULL) + frame_clock = g_object_ref (parent->frame_clock); + else + frame_clock = _gdk_frame_clock_idle_new (); switch (surface_type) { From 50b96dcdd5a92f504e6b746288ff67bdcb26bd7e Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 2 Mar 2022 00:43:33 -0800 Subject: [PATCH 10/14] macos: handle transient-for from configure We failed to handle the toplevel with transient-for case here which could cause our X/Y calculations to be off in other areas such as best monitor detection. --- gdk/macos/gdkmacossurface.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index 1c971d9e95..c8df8533fd 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -837,8 +837,9 @@ _gdk_macos_surface_update_fullscreen_state (GdkMacosSurface *self) void _gdk_macos_surface_configure (GdkMacosSurface *self) { - GdkMacosDisplay *display; GdkSurface *surface = (GdkSurface *)self; + GdkMacosDisplay *display; + GdkMacosSurface *parent; NSRect frame_rect; NSRect content_rect; @@ -847,6 +848,13 @@ _gdk_macos_surface_configure (GdkMacosSurface *self) if (GDK_SURFACE_DESTROYED (self)) return; + if (surface->parent != NULL) + parent = GDK_MACOS_SURFACE (surface->parent); + else if (surface->transient_for != NULL) + parent = GDK_MACOS_SURFACE (surface->transient_for); + else + parent = NULL; + display = GDK_MACOS_DISPLAY (GDK_SURFACE (self)->display); frame_rect = [self->window frame]; content_rect = [self->window contentRectForFrameRect:frame_rect]; @@ -856,10 +864,10 @@ _gdk_macos_surface_configure (GdkMacosSurface *self) content_rect.origin.y + content_rect.size.height, &self->root_x, &self->root_y); - if (surface->parent != NULL) + if (parent != NULL) { - surface->x = self->root_x - GDK_MACOS_SURFACE (surface->parent)->root_x; - surface->y = self->root_y - GDK_MACOS_SURFACE (surface->parent)->root_y; + surface->x = self->root_x - parent->root_x; + surface->y = self->root_y - parent->root_y; } else { From ed4f0de2b02a35d1ded163f623caabc68cad2d84 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 2 Mar 2022 00:45:44 -0800 Subject: [PATCH 11/14] macos: make move_resize possibly idempotent We need to handle the case where we might be racing against an incoming configure event due to how notifications are queued from the display server. Rather than calling configure (and possibly causing other things to move around) this just queries the display server directly for the coordinates that we care about. Additionally, we can display:NO as we are in control of all the display process now using CALayer. --- gdk/macos/gdkmacossurface.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index c8df8533fd..bd7ca61d48 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -965,13 +965,27 @@ _gdk_macos_surface_move_resize (GdkMacosSurface *self, NSRect frame_rect; gboolean ignore_move; gboolean ignore_size; + GdkRectangle current; g_return_if_fail (GDK_IS_MACOS_SURFACE (self)); - ignore_move = (x == -1 || (x == self->root_x)) && - (y == -1 || (y == self->root_y)); - ignore_size = (width == -1 || (width == surface->width)) && - (height == -1 || (height == surface->height)); + /* Query for up-to-date values in case we're racing against + * an incoming frame notify which could be queued behind whatever + * we're processing right now. + */ + frame_rect = [self->window frame]; + content_rect = [self->window contentRectForFrameRect:frame_rect]; + _gdk_macos_display_from_display_coords (GDK_MACOS_DISPLAY (GDK_SURFACE (self)->display), + content_rect.origin.x, content_rect.origin.y, + ¤t.x, ¤t.y); + current.width = content_rect.size.width; + current.height = content_rect.size.height; + + /* Check if we can ignore the operation all together */ + ignore_move = (x == -1 || (x == current.x)) && + (y == -1 || (y == current.y)); + ignore_size = (width == -1 || (width == current.width)) && + (height == -1 || (height == current.height)); if (ignore_move && ignore_size) return; @@ -979,23 +993,21 @@ _gdk_macos_surface_move_resize (GdkMacosSurface *self, display = gdk_surface_get_display (surface); if (width == -1) - width = surface->width; + width = current.width; if (height == -1) - height = surface->height; + height = current.height; if (x == -1) - x = self->root_x; + x = current.x; if (y == -1) - y = self->root_y; + y = current.y; _gdk_macos_display_to_display_coords (GDK_MACOS_DISPLAY (display), x, y + height, &x, &y); - content_rect = [self->window contentRectForFrameRect:[self->window frame]]; - if (!ignore_move) content_rect.origin = NSMakePoint (x, y); @@ -1003,7 +1015,7 @@ _gdk_macos_surface_move_resize (GdkMacosSurface *self, content_rect.size = NSMakeSize (width, height); frame_rect = [self->window frameRectForContentRect:content_rect]; - [self->window setFrame:frame_rect display:YES]; + [self->window setFrame:frame_rect display:NO]; } void From f7d0b912679fd0565f43f8264db6a84be56973a8 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 2 Mar 2022 00:47:27 -0800 Subject: [PATCH 12/14] macos: move children when monitor changes We can rely on other code to move monitors, but specifically with children we want to try harder to move them as a group and keep positioning in tact. --- gdk/macos/gdkmacossurface.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index bd7ca61d48..948f08c187 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -1086,6 +1086,7 @@ _gdk_macos_surface_monitor_changed (GdkMacosSurface *self) self->in_change_monitor = TRUE; _gdk_macos_surface_cancel_frame (self); + _gdk_macos_surface_configure (self); rect.x = self->root_x; rect.y = self->root_y; @@ -1149,9 +1150,39 @@ _gdk_macos_surface_monitor_changed (GdkMacosSurface *self) g_message ("Surface \"%s\" moved to monitor \"%s\"", self->title ? self->title : "unknown", gdk_monitor_get_connector (best))); + + _gdk_macos_surface_configure (self); + + if (GDK_SURFACE_IS_MAPPED (GDK_SURFACE (self))) + { + _gdk_macos_surface_request_frame (self); + gdk_surface_request_layout (GDK_SURFACE (self)); + } + + for (const GList *iter = GDK_SURFACE (self)->children; + iter != NULL; + iter = iter->next) + { + GdkMacosSurface *child = iter->data; + GdkRectangle area; + + g_set_object (&child->best_monitor, best); + + area.x = self->root_x + GDK_SURFACE (child)->x + child->shadow_left; + area.y = self->root_y + GDK_SURFACE (child)->y + child->shadow_top; + area.width = GDK_SURFACE (child)->width - child->shadow_left - child->shadow_right; + area.height = GDK_SURFACE (child)->height - child->shadow_top - child->shadow_bottom; + + _gdk_macos_monitor_clamp (GDK_MACOS_MONITOR (best), &area); + + area.x -= child->shadow_left; + area.y -= child->shadow_top; + + _gdk_macos_surface_move (child, area.x, area.y); + gdk_surface_invalidate_rect (GDK_SURFACE (child), NULL); + } } - _gdk_macos_surface_configure (self); gdk_surface_invalidate_rect (GDK_SURFACE (self), NULL); self->in_change_monitor = FALSE; From efa8f903c23a0c1328c8972718390ab204c69815 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 2 Mar 2022 00:48:06 -0800 Subject: [PATCH 13/14] macos: invalidate surface contents when mapping --- gdk/macos/gdkmacossurface.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index 948f08c187..add5f15db7 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -910,6 +910,7 @@ _gdk_macos_surface_show (GdkMacosSurface *self) { gdk_surface_set_is_mapped (GDK_SURFACE (self), TRUE); gdk_surface_request_layout (GDK_SURFACE (self)); + gdk_surface_invalidate_rect (GDK_SURFACE (self), NULL); gdk_surface_thaw_updates (GDK_SURFACE (self)); } } From ca79688f52fc7c173325bc1efe955912139231a6 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 2 Mar 2022 01:04:53 -0800 Subject: [PATCH 14/14] macos: clear window stack before requesting motion We want to ensure that we recalculate the sort order of windows before processing the motion. Generally this would be done in response from the display server in GdkMacosWindow, but I've seen it possible to race there. --- gdk/macos/gdkmacossurface.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index add5f15db7..f6140deafa 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -1270,11 +1270,15 @@ _gdk_macos_surface_get_buffer (GdkMacosSurface *self) static void _gdk_macos_surface_do_delayed_show (GdkMacosSurface *self) { + GdkSurface *surface = (GdkSurface *)self; + g_assert (GDK_IS_MACOS_SURFACE (self)); self->show_on_next_swap = FALSE; [self->window showAndMakeKey:YES]; - gdk_surface_request_motion (GDK_SURFACE (self)); + + _gdk_macos_display_clear_sorting (GDK_MACOS_DISPLAY (surface->display)); + gdk_surface_request_motion (surface); } void