diff options
author | Matthias Clasen <mclasen@redhat.com> | 2022-03-16 20:03:38 +0000 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2022-03-16 20:03:38 +0000 |
commit | 17e0618ff65cb301e6aad08af3a0fe544c1c6026 (patch) | |
tree | 536b1f999199ee46dd10749da6f620177e9de599 | |
parent | 38bbcb74115b69263a63fae3a70bc6c11e701814 (diff) | |
parent | 73d5f7061b3021ccf0c66807ed33630b4fc6ac2e (diff) | |
download | gtk+-17e0618ff65cb301e6aad08af3a0fe544c1c6026.tar.gz |
Merge branch 'wip/chergert/for-4-6' into 'gtk-4-6'
backport macOS fixes to gtk-4-6
See merge request GNOME/gtk!4578
24 files changed, 1055 insertions, 534 deletions
diff --git a/gdk/macos/GdkMacosView.c b/gdk/macos/GdkMacosView.c index 52a55a7cef..18de78aa19 100644 --- a/gdk/macos/GdkMacosView.c +++ b/gdk/macos/GdkMacosView.c @@ -24,6 +24,7 @@ #import "GdkMacosLayer.h" #import "GdkMacosView.h" +#import "GdkMacosWindow.h" @implementation GdkMacosView @@ -56,6 +57,19 @@ return NO; } +-(void)mouseDown:(NSEvent *)nsevent +{ + if ([(GdkMacosWindow *)[self window] needsMouseDownQuirk]) + /* We should only hit this when we are trying to click through + * the shadow of a window into another window. Just request + * that the application not activate this window on mouseUp. + * See gdkmacosdisplay-translate.c for the other half of this. + */ + [NSApp preventWindowOrdering]; + else + [super mouseDown:nsevent]; +} + -(void)setFrame:(NSRect)rect { [super setFrame:rect]; diff --git a/gdk/macos/GdkMacosWindow.c b/gdk/macos/GdkMacosWindow.c index f879decbc9..23b3f5a3f3 100644 --- a/gdk/macos/GdkMacosWindow.c +++ b/gdk/macos/GdkMacosWindow.c @@ -253,7 +253,8 @@ typedef NSString *CALayerContentsGravity; -(BOOL)canBecomeKeyWindow { - return GDK_IS_TOPLEVEL (gdk_surface) || GDK_IS_POPUP (gdk_surface); + return GDK_IS_TOPLEVEL (gdk_surface) || + (GDK_IS_POPUP (gdk_surface) && GDK_SURFACE (gdk_surface)->input_region != NULL); } -(void)showAndMakeKey:(BOOL)makeKey @@ -261,9 +262,12 @@ typedef NSString *CALayerContentsGravity; inShowOrHide = YES; if (makeKey && [self canBecomeKeyWindow]) - [self makeKeyAndOrderFront:nil]; + [self makeKeyAndOrderFront:self]; else - [self orderFront:nil]; + [self orderFront:self]; + + if (makeKey && [self canBecomeMainWindow]) + [self makeMainWindow]; inShowOrHide = NO; @@ -373,9 +377,17 @@ typedef NSString *CALayerContentsGravity; _gdk_macos_surface_configure ([self gdkSurface]); } -- (void)windowDidResize:(NSNotification *)notification +-(void)windowDidResize:(NSNotification *)notification { - _gdk_macos_surface_configure ([self gdkSurface]); + _gdk_macos_surface_configure (gdk_surface); + + /* If we're using server-side decorations, this notification is coming + * in from a display-side change. We need to request a layout in + * addition to the configure event. + */ + if (GDK_IS_MACOS_TOPLEVEL_SURFACE (gdk_surface) && + GDK_MACOS_TOPLEVEL_SURFACE (gdk_surface)->decorated) + gdk_surface_request_layout (GDK_SURFACE (gdk_surface)); } /* Used by gdkmacosdisplay-translate.c to decide if our sendEvent() handler @@ -668,7 +680,12 @@ typedef NSString *CALayerContentsGravity; is_opaque = (([self styleMask] & NSWindowStyleMaskTitled) != 0); if (was_fullscreen != is_fullscreen) - _gdk_macos_surface_update_fullscreen_state (gdk_surface); + { + if (was_fullscreen) + [self setFrame:lastUnfullscreenFrame display:NO]; + + _gdk_macos_surface_update_fullscreen_state (gdk_surface); + } if (was_opaque != is_opaque) { @@ -753,7 +770,6 @@ typedef NSString *CALayerContentsGravity; -(void)windowWillExitFullScreen:(NSNotification *)aNotification { - [self setFrame:lastUnfullscreenFrame display:NO]; } -(void)windowDidExitFullScreen:(NSNotification *)aNotification @@ -814,4 +830,10 @@ typedef NSString *CALayerContentsGravity; [(GdkMacosView *)[self contentView] swapBuffer:buffer withDamage:damage]; } +-(BOOL)needsMouseDownQuirk +{ + return GDK_IS_MACOS_TOPLEVEL_SURFACE (gdk_surface) && + !GDK_MACOS_TOPLEVEL_SURFACE (gdk_surface)->decorated; +} + @end diff --git a/gdk/macos/GdkMacosWindow.h b/gdk/macos/GdkMacosWindow.h index cb8b2efad1..3a514ea857 100644 --- a/gdk/macos/GdkMacosWindow.h +++ b/gdk/macos/GdkMacosWindow.h @@ -69,5 +69,6 @@ -(BOOL)trackManualResize; -(void)setDecorated:(BOOL)decorated; -(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage; +-(BOOL)needsMouseDownQuirk; @end diff --git a/gdk/macos/gdkdisplaylinksource.c b/gdk/macos/gdkdisplaylinksource.c index 292b8be519..6a613b40a4 100644 --- a/gdk/macos/gdkdisplaylinksource.c +++ b/gdk/macos/gdkdisplaylinksource.c @@ -26,7 +26,9 @@ #include "gdkdisplaylinksource.h" +#include "gdkdebug.h" #include "gdkmacoseventsource-private.h" +#include "gdkmacosmonitor-private.h" #include "gdk-private.h" static gint64 host_to_frame_clock_time (gint64 val); @@ -65,7 +67,7 @@ gdk_display_link_source_dispatch (GSource *source, impl->needs_dispatch = FALSE; - if (callback != NULL) + if (!impl->paused && callback != NULL) ret = callback (user_data); return ret; @@ -76,7 +78,9 @@ gdk_display_link_source_finalize (GSource *source) { GdkDisplayLinkSource *impl = (GdkDisplayLinkSource *)source; - CVDisplayLinkStop (impl->display_link); + if (!impl->paused) + CVDisplayLinkStop (impl->display_link); + CVDisplayLinkRelease (impl->display_link); } @@ -90,12 +94,18 @@ static GSourceFuncs gdk_display_link_source_funcs = { void gdk_display_link_source_pause (GdkDisplayLinkSource *source) { + g_return_if_fail (source->paused == FALSE); + + source->paused = TRUE; CVDisplayLinkStop (source->display_link); } void gdk_display_link_source_unpause (GdkDisplayLinkSource *source) { + g_return_if_fail (source->paused == TRUE); + + source->paused = FALSE; CVDisplayLinkStart (source->display_link); } @@ -147,6 +157,7 @@ gdk_display_link_source_frame_cb (CVDisplayLinkRef display_link, /** * gdk_display_link_source_new: + * @display_id: the identifier of the monitor * * Creates a new `GSource` that will activate the dispatch function upon * notification from a CVDisplayLink that a new frame should be drawn. @@ -159,41 +170,61 @@ gdk_display_link_source_frame_cb (CVDisplayLinkRef display_link, * Returns: (transfer full): A newly created `GSource` */ GSource * -gdk_display_link_source_new (void) +gdk_display_link_source_new (CGDirectDisplayID display_id, + CGDisplayModeRef mode) { GdkDisplayLinkSource *impl; GSource *source; - CVReturn ret; - double period; + char *name; source = g_source_new (&gdk_display_link_source_funcs, sizeof *impl); impl = (GdkDisplayLinkSource *)source; + impl->display_id = display_id; + impl->paused = TRUE; - /* - * Create our link based on currently connected displays. - * If there are multiple displays, this will be something that tries - * to work for all of them. In the future, we may want to explore multiple - * links based on the connected displays. + /* Create DisplayLink for timing information for the display in + * question so that we can produce graphics for that display at whatever + * rate it can provide. */ - ret = CVDisplayLinkCreateWithActiveCGDisplays (&impl->display_link); - if (ret != kCVReturnSuccess) + if (CVDisplayLinkCreateWithCGDisplay (display_id, &impl->display_link) != kCVReturnSuccess) { g_warning ("Failed to initialize CVDisplayLink!"); - return source; + goto failure; } - /* - * Determine our nominal period between frames. - */ - period = CVDisplayLinkGetActualOutputVideoRefreshPeriod (impl->display_link); - if (period == 0.0) - period = 1.0 / 60.0; - impl->refresh_interval = period * 1000000L; - impl->refresh_rate = 1.0 / period * 1000L; - - /* - * Wire up our callback to be executed within the high-priority thread. - */ + impl->refresh_rate = CGDisplayModeGetRefreshRate (mode) * 1000.0; + + if (impl->refresh_rate == 0) + { + const CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod (impl->display_link); + if (!(time.flags & kCVTimeIsIndefinite)) + impl->refresh_rate = (double)time.timeScale / (double)time.timeValue * 1000.0; + } + + if (impl->refresh_rate != 0) + { + impl->refresh_interval = 1000000.0 / (double)impl->refresh_rate * 1000.0; + } + else + { + double period = CVDisplayLinkGetActualOutputVideoRefreshPeriod (impl->display_link); + + if (period == 0.0) + period = 1.0 / 60.0; + + impl->refresh_rate = 1.0 / period * 1000L; + impl->refresh_interval = period * 1000000L; + } + + name = _gdk_macos_monitor_get_connector_name (display_id); + GDK_NOTE (MISC, + g_message ("Monitor \"%s\" discovered with Refresh Rate %d and Interval %"G_GINT64_FORMAT, + name ? name : "unknown", + impl->refresh_rate, + impl->refresh_interval)); + g_free (name); + + /* Wire up our callback to be executed within the high-priority thread. */ CVDisplayLinkSetOutputCallback (impl->display_link, gdk_display_link_source_frame_cb, source); @@ -201,6 +232,10 @@ gdk_display_link_source_new (void) g_source_set_static_name (source, "[gdk] quartz frame clock"); return source; + +failure: + g_source_unref (source); + return NULL; } static gint64 diff --git a/gdk/macos/gdkdisplaylinksource.h b/gdk/macos/gdkdisplaylinksource.h index ed769b59f8..6f465907a2 100644 --- a/gdk/macos/gdkdisplaylinksource.h +++ b/gdk/macos/gdkdisplaylinksource.h @@ -30,17 +30,20 @@ G_BEGIN_DECLS typedef struct { - GSource source; + GSource source; - CVDisplayLinkRef display_link; - gint64 refresh_interval; - guint refresh_rate; + CGDirectDisplayID display_id; + CVDisplayLinkRef display_link; + gint64 refresh_interval; + guint refresh_rate; + guint paused : 1; - volatile gint64 presentation_time; - volatile guint needs_dispatch; + volatile gint64 presentation_time; + volatile guint needs_dispatch; } GdkDisplayLinkSource; -GSource *gdk_display_link_source_new (void); +GSource *gdk_display_link_source_new (CGDirectDisplayID display_id, + CGDisplayModeRef mode); void gdk_display_link_source_pause (GdkDisplayLinkSource *source); void gdk_display_link_source_unpause (GdkDisplayLinkSource *source); diff --git a/gdk/macos/gdkmacosbuffer-private.h b/gdk/macos/gdkmacosbuffer-private.h index 6be201147c..4b446a7212 100644 --- a/gdk/macos/gdkmacosbuffer-private.h +++ b/gdk/macos/gdkmacosbuffer-private.h @@ -41,6 +41,8 @@ GdkMacosBuffer *_gdk_macos_buffer_new (int width IOSurfaceRef _gdk_macos_buffer_get_native (GdkMacosBuffer *self); void _gdk_macos_buffer_lock (GdkMacosBuffer *self); void _gdk_macos_buffer_unlock (GdkMacosBuffer *self); +void _gdk_macos_buffer_read_lock (GdkMacosBuffer *self); +void _gdk_macos_buffer_read_unlock (GdkMacosBuffer *self); guint _gdk_macos_buffer_get_width (GdkMacosBuffer *self); guint _gdk_macos_buffer_get_height (GdkMacosBuffer *self); guint _gdk_macos_buffer_get_stride (GdkMacosBuffer *self); diff --git a/gdk/macos/gdkmacosbuffer.c b/gdk/macos/gdkmacosbuffer.c index ac99302ee4..eb8a719dbc 100644 --- a/gdk/macos/gdkmacosbuffer.c +++ b/gdk/macos/gdkmacosbuffer.c @@ -192,6 +192,45 @@ _gdk_macos_buffer_unlock (GdkMacosBuffer *self) IOSurfaceUnlock (self->surface, 0, NULL); } +/** + * _gdk_macos_buffer_lock_readonly: + * + * Like _gdk_macos_buffer_lock() but uses the read-only flag to + * indicate we are not interested in retrieving the updates from + * the GPU before modifying the CPU-side cache. + * + * Must be used with _gdk_macos_buffer_unlock_readonly(). + */ +void +_gdk_macos_buffer_read_lock (GdkMacosBuffer *self) +{ + kern_return_t ret; + + g_return_if_fail (GDK_IS_MACOS_BUFFER (self)); + g_return_if_fail (self->lock_count == 0); + + self->lock_count++; + + ret = IOSurfaceLock (self->surface, kIOSurfaceLockReadOnly, NULL); + + g_return_if_fail (ret == KERN_SUCCESS); +} + +void +_gdk_macos_buffer_read_unlock (GdkMacosBuffer *self) +{ + kern_return_t ret; + + g_return_if_fail (GDK_IS_MACOS_BUFFER (self)); + g_return_if_fail (self->lock_count == 1); + + self->lock_count--; + + ret = IOSurfaceUnlock (self->surface, kIOSurfaceLockReadOnly, NULL); + + g_return_if_fail (ret == KERN_SUCCESS); +} + guint _gdk_macos_buffer_get_width (GdkMacosBuffer *self) { @@ -242,7 +281,7 @@ _gdk_macos_buffer_set_damage (GdkMacosBuffer *self, return; g_clear_pointer (&self->damage, cairo_region_destroy); - self->damage = cairo_region_reference (damage); + self->damage = cairo_region_copy (damage); } gpointer diff --git a/gdk/macos/gdkmacoscairocontext.c b/gdk/macos/gdkmacoscairocontext.c index 041f1193e6..31eecd5df6 100644 --- a/gdk/macos/gdkmacoscairocontext.c +++ b/gdk/macos/gdkmacoscairocontext.c @@ -42,19 +42,6 @@ struct _GdkMacosCairoContextClass G_DEFINE_TYPE (GdkMacosCairoContext, _gdk_macos_cairo_context, GDK_TYPE_CAIRO_CONTEXT) -static const cairo_user_data_key_t buffer_key; - -static void -unlock_buffer (gpointer data) -{ - GdkMacosBuffer *buffer = data; - - g_assert (GDK_IS_MACOS_BUFFER (buffer)); - - _gdk_macos_buffer_unlock (buffer); - g_clear_object (&buffer); -} - static cairo_t * _gdk_macos_cairo_context_cairo_create (GdkCairoContext *cairo_context) { @@ -106,12 +93,9 @@ _gdk_macos_cairo_context_cairo_create (GdkCairoContext *cairo_context) stride); cairo_surface_set_device_scale (image_surface, scale, scale); - /* Lock the buffer so we can modify it safely */ - _gdk_macos_buffer_lock (buffer); - cairo_surface_set_user_data (image_surface, - &buffer_key, - g_object_ref (buffer), - unlock_buffer); + /* The buffer should already be locked at this point, and will + * be unlocked as part of end_frame. + */ if (!(cr = cairo_create (image_surface))) goto failure; @@ -159,40 +143,120 @@ failure: } static void +copy_surface_data (GdkMacosBuffer *from, + GdkMacosBuffer *to, + const cairo_region_t *region, + int scale) +{ + const guint8 *from_base; + guint8 *to_base; + guint from_stride; + guint to_stride; + guint n_rects; + + g_assert (GDK_IS_MACOS_BUFFER (from)); + g_assert (GDK_IS_MACOS_BUFFER (to)); + g_assert (region != NULL); + g_assert (!cairo_region_is_empty (region)); + + from_base = _gdk_macos_buffer_get_data (from); + from_stride = _gdk_macos_buffer_get_stride (from); + + to_base = _gdk_macos_buffer_get_data (to); + to_stride = _gdk_macos_buffer_get_stride (to); + + n_rects = cairo_region_num_rectangles (region); + + for (guint i = 0; i < n_rects; i++) + { + cairo_rectangle_int_t rect; + int y2; + + cairo_region_get_rectangle (region, i, &rect); + + rect.y *= scale; + rect.height *= scale; + rect.x *= scale; + rect.width *= scale; + + y2 = rect.y + rect.height; + + for (int y = rect.y; y < y2; y++) + memcpy (&to_base[y * to_stride + rect.x * 4], + &from_base[y * from_stride + rect.x * 4], + rect.width * 4); + } +} + +static void _gdk_macos_cairo_context_begin_frame (GdkDrawContext *draw_context, gboolean prefers_high_depth, cairo_region_t *region) { GdkMacosCairoContext *self = (GdkMacosCairoContext *)draw_context; GdkMacosBuffer *buffer; - GdkSurface *surface; + GdkMacosSurface *surface; g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (self)); [CATransaction begin]; [CATransaction setDisableActions:YES]; - surface = gdk_draw_context_get_surface (draw_context); - buffer = _gdk_macos_surface_get_buffer (GDK_MACOS_SURFACE (surface)); + surface = GDK_MACOS_SURFACE (gdk_draw_context_get_surface (draw_context)); + buffer = _gdk_macos_surface_get_buffer (surface); _gdk_macos_buffer_set_damage (buffer, region); _gdk_macos_buffer_set_flipped (buffer, FALSE); + + _gdk_macos_buffer_lock (buffer); + + /* If there is damage that was on the previous frame that is not on + * this frame, we need to copy that rendered region over to the back + * buffer so that when swapping buffers, we still have that content. + * This is done with a read-only lock on the IOSurface to avoid + * invalidating the buffer contents. + */ + if (surface->front != NULL) + { + const cairo_region_t *previous = _gdk_macos_buffer_get_damage (surface->front); + + if (previous != NULL) + { + cairo_region_t *copy; + + copy = cairo_region_copy (previous); + cairo_region_subtract (copy, region); + + if (!cairo_region_is_empty (copy)) + { + int scale = gdk_surface_get_scale_factor (GDK_SURFACE (surface)); + + _gdk_macos_buffer_read_lock (surface->front); + copy_surface_data (surface->front, buffer, copy, scale); + _gdk_macos_buffer_read_unlock (surface->front); + } + + cairo_region_destroy (copy); + } + } } static void _gdk_macos_cairo_context_end_frame (GdkDrawContext *draw_context, cairo_region_t *painted) { + GdkMacosCairoContext *self = (GdkMacosCairoContext *)draw_context; + GdkMacosSurface *surface; GdkMacosBuffer *buffer; - GdkSurface *surface; - g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (draw_context)); + g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (self)); - surface = gdk_draw_context_get_surface (draw_context); - buffer = _gdk_macos_surface_get_buffer (GDK_MACOS_SURFACE (surface)); + surface = GDK_MACOS_SURFACE (gdk_draw_context_get_surface (draw_context)); + buffer = _gdk_macos_surface_get_buffer (surface); + + _gdk_macos_buffer_unlock (buffer); - _gdk_macos_surface_swap_buffers (GDK_MACOS_SURFACE (surface), painted); - _gdk_macos_buffer_set_damage (buffer, NULL); + _gdk_macos_surface_swap_buffers (surface, painted); [CATransaction commit]; } diff --git a/gdk/macos/gdkmacosdisplay-feedback.c b/gdk/macos/gdkmacosdisplay-feedback.c new file mode 100644 index 0000000000..868ac0fbcd --- /dev/null +++ b/gdk/macos/gdkmacosdisplay-feedback.c @@ -0,0 +1,105 @@ +/* + * Copyright © 2022 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include <AppKit/AppKit.h> + +#include "gdkmacosdisplay-private.h" +#include "gdkmacossurface-private.h" + +static void +gdk_macos_display_user_defaults_changed_cb (CFNotificationCenterRef center, + void *observer, + CFStringRef name, + const void *object, + CFDictionaryRef userInfo) +{ + GdkMacosDisplay *self = observer; + + g_assert (GDK_IS_MACOS_DISPLAY (self)); + + _gdk_macos_display_reload_settings (self); +} + +static void +gdk_macos_display_monitors_changed_cb (CFNotificationCenterRef center, + void *observer, + CFStringRef name, + const void *object, + CFDictionaryRef userInfo) +{ + GdkMacosDisplay *self = observer; + + g_assert (GDK_IS_MACOS_DISPLAY (self)); + + _gdk_macos_display_reload_monitors (self); + + /* Now we need to update all our surface positions since they + * probably just changed origins. + */ + for (const GList *iter = _gdk_macos_display_get_surfaces (self); + iter != NULL; + iter = iter->next) + { + GdkMacosSurface *surface = iter->data; + + g_assert (GDK_IS_MACOS_SURFACE (surface)); + + _gdk_macos_surface_monitor_changed (surface); + } +} + + +void +_gdk_macos_display_feedback_init (GdkMacosDisplay *self) +{ + g_return_if_fail (GDK_IS_MACOS_DISPLAY (self)); + + CFNotificationCenterAddObserver (CFNotificationCenterGetLocalCenter (), + self, + gdk_macos_display_monitors_changed_cb, + CFSTR ("NSApplicationDidChangeScreenParametersNotification"), + NULL, + CFNotificationSuspensionBehaviorDeliverImmediately); + + CFNotificationCenterAddObserver (CFNotificationCenterGetDistributedCenter (), + self, + gdk_macos_display_user_defaults_changed_cb, + CFSTR ("NSUserDefaultsDidChangeNotification"), + NULL, + CFNotificationSuspensionBehaviorDeliverImmediately); +} + +void +_gdk_macos_display_feedback_destroy (GdkMacosDisplay *self) +{ + g_return_if_fail (GDK_IS_MACOS_DISPLAY (self)); + + CFNotificationCenterRemoveObserver (CFNotificationCenterGetDistributedCenter (), + self, + CFSTR ("NSApplicationDidChangeScreenParametersNotification"), + NULL); + + CFNotificationCenterRemoveObserver (CFNotificationCenterGetDistributedCenter (), + self, + CFSTR ("NSUserDefaultsDidChangeNotification"), + NULL); + +} diff --git a/gdk/macos/gdkmacosdisplay-private.h b/gdk/macos/gdkmacosdisplay-private.h index be2290b89a..83ae435e49 100644 --- a/gdk/macos/gdkmacosdisplay-private.h +++ b/gdk/macos/gdkmacosdisplay-private.h @@ -43,6 +43,8 @@ G_BEGIN_DECLS #define GIC_FILTER_PASSTHRU 0 #define GIC_FILTER_FILTERED 1 +#define GDK_MACOS_EVENT_DROP (GdkEvent *)GSIZE_TO_POINTER(1) + struct _GdkMacosDisplay { GdkDisplay parent_instance; @@ -68,16 +70,6 @@ struct _GdkMacosDisplay */ GQueue sorted_surfaces; - /* Our CVDisplayLink based GSource which we use to freeze/thaw the - * GdkFrameClock for the surface. - */ - GSource *frame_source; - - /* A queue of surfaces which we know are awaiting frames to be drawn. This - * uses the GdkMacosSurface.frame link. - */ - GQueue awaiting_frames; - /* The surface that is receiving keyboard events */ GdkMacosSurface *keyboard_surface; @@ -92,6 +84,14 @@ struct _GdkMacosDisplay int min_y; int max_x; int max_y; + + /* A GSource to select a new main/key window */ + guint select_key_in_idle; + + /* Note if we have a key window that is not a GdkMacosWindow + * such as a NSPanel used for native dialogs. + */ + guint key_window_is_foregin : 1; }; struct _GdkMacosDisplayClass @@ -124,6 +124,8 @@ GdkMonitor *_gdk_macos_display_get_monitor_at_display_coords (GdkMacosDisp int y); GdkEvent *_gdk_macos_display_translate (GdkMacosDisplay *self, NSEvent *event); +void _gdk_macos_display_feedback_init (GdkMacosDisplay *self); +void _gdk_macos_display_feedback_destroy (GdkMacosDisplay *self); void _gdk_macos_display_break_all_grabs (GdkMacosDisplay *self, guint32 time); GdkModifierType _gdk_macos_display_get_current_keyboard_modifiers (GdkMacosDisplay *self); @@ -136,10 +138,6 @@ GdkMacosSurface *_gdk_macos_display_get_surface_at_display_coords (GdkMacosDisp void _gdk_macos_display_reload_monitors (GdkMacosDisplay *self); void _gdk_macos_display_surface_removed (GdkMacosDisplay *self, GdkMacosSurface *surface); -void _gdk_macos_display_add_frame_callback (GdkMacosDisplay *self, - GdkMacosSurface *surface); -void _gdk_macos_display_remove_frame_callback (GdkMacosDisplay *self, - GdkMacosSurface *surface); NSWindow *_gdk_macos_display_find_native_under_pointer (GdkMacosDisplay *self, int *x, int *y); @@ -155,7 +153,6 @@ void _gdk_macos_display_surface_resigned_key (GdkMacosDisp GdkMacosSurface *surface); void _gdk_macos_display_surface_became_key (GdkMacosDisplay *self, GdkMacosSurface *surface); -int _gdk_macos_display_get_nominal_refresh_rate (GdkMacosDisplay *self); void _gdk_macos_display_clear_sorting (GdkMacosDisplay *self); const GList *_gdk_macos_display_get_surfaces (GdkMacosDisplay *self); void _gdk_macos_display_send_button_event (GdkMacosDisplay *self, diff --git a/gdk/macos/gdkmacosdisplay-settings.c b/gdk/macos/gdkmacosdisplay-settings.c index e6714f0a14..53d6df0672 100644 --- a/gdk/macos/gdkmacosdisplay-settings.c +++ b/gdk/macos/gdkmacosdisplay-settings.c @@ -34,7 +34,7 @@ typedef struct const char *font_name; int xft_dpi; int double_click_time; - int cursor_blink_timeout; + int cursor_blink_time; guint enable_animations : 1; guint shell_shows_desktop : 1; guint shell_shows_menubar : 1; @@ -65,9 +65,9 @@ _gdk_macos_settings_load (GdkMacosSettings *settings) ival = [defaults integerForKey:@"NSTextInsertionPointBlinkPeriod"]; if (ival > 0) - settings->cursor_blink_timeout = ival; + settings->cursor_blink_time = ival; else - settings->cursor_blink_timeout = 1000; + settings->cursor_blink_time = 1000; settings->primary_button_warps_slider = [[NSUserDefaults standardUserDefaults] boolForKey:@"AppleScrollerPagingBehavior"] == YES; @@ -124,9 +124,9 @@ _gdk_macos_display_get_setting (GdkMacosDisplay *self, g_value_set_int (value, current_settings.xft_dpi); ret = TRUE; } - else if (strcmp (setting, "gtk-cursor-blink-timeout") == 0) + else if (strcmp (setting, "gtk-cursor-blink-time") == 0) { - g_value_set_int (value, current_settings.cursor_blink_timeout); + g_value_set_int (value, current_settings.cursor_blink_time); ret = TRUE; } else if (strcmp (setting, "gtk-double-click-time") == 0) diff --git a/gdk/macos/gdkmacosdisplay-translate.c b/gdk/macos/gdkmacosdisplay-translate.c index 8e3cd90b82..7a0b84ccdb 100644 --- a/gdk/macos/gdkmacosdisplay-translate.c +++ b/gdk/macos/gdkmacosdisplay-translate.c @@ -612,6 +612,8 @@ fill_scroll_event (GdkMacosDisplay *self, GdkModifierType state; GdkDevice *pointer; GdkEvent *ret = NULL; + NSEventPhase phase; + NSEventPhase momentumPhase; GdkSeat *seat; double dx; double dy; @@ -619,11 +621,31 @@ fill_scroll_event (GdkMacosDisplay *self, g_assert (GDK_IS_MACOS_SURFACE (surface)); g_assert (nsevent != NULL); + phase = [nsevent phase]; + momentumPhase = [nsevent momentumPhase]; + + /* Ignore kinetic scroll events from the display server as we already + * handle those internally. + */ + if (phase == 0 && momentumPhase != 0) + return GDK_MACOS_EVENT_DROP; + seat = gdk_display_get_default_seat (GDK_DISPLAY (self)); pointer = gdk_seat_get_pointer (seat); state = _gdk_macos_display_get_current_mouse_modifiers (self) | _gdk_macos_display_get_current_keyboard_modifiers (self); + /* If we are starting a new phase, send a stop so any previous + * scrolling immediately stops. + */ + if (phase == NSEventPhaseMayBegin) + return gdk_scroll_event_new (GDK_SURFACE (surface), + pointer, + NULL, + get_time_from_ns_event (nsevent), + state, + 0.0, 0.0, TRUE); + dx = [nsevent deltaX]; dy = [nsevent deltaY]; @@ -667,34 +689,32 @@ fill_scroll_event (GdkMacosDisplay *self, dy = 0.0; } - if (dx != 0.0 || dy != 0.0) + if ((dx != 0.0 || dy != 0.0) && ![nsevent hasPreciseScrollingDeltas]) { - if ([nsevent hasPreciseScrollingDeltas]) - { - GdkEvent *emulated; - - emulated = gdk_scroll_event_new_discrete (GDK_SURFACE (surface), - pointer, - NULL, - get_time_from_ns_event (nsevent), - state, - direction, - TRUE); - _gdk_event_queue_append (GDK_DISPLAY (self), emulated); - } - else - { - g_assert (ret == NULL); - - ret = gdk_scroll_event_new (GDK_SURFACE (surface), - pointer, - NULL, - get_time_from_ns_event (nsevent), - state, - -dx * 32, - -dy * 32, - FALSE); - } + g_assert (ret == NULL); + + ret = gdk_scroll_event_new_discrete (GDK_SURFACE (surface), + pointer, + NULL, + get_time_from_ns_event (nsevent), + state, + direction, + FALSE); + } + + if (phase == NSEventPhaseEnded || phase == NSEventPhaseCancelled) + { + /* The user must have released their fingers in a touchpad + * scroll, so try to send a scroll is_stop event. + */ + if (ret != NULL) + _gdk_event_queue_append (GDK_DISPLAY (self), g_steal_pointer (&ret)); + ret = gdk_scroll_event_new (GDK_SURFACE (surface), + pointer, + NULL, + get_time_from_ns_event (nsevent), + state, + 0.0, 0.0, TRUE); } return g_steal_pointer (&ret); @@ -1066,6 +1086,7 @@ _gdk_macos_display_translate (GdkMacosDisplay *self, GdkMacosSurface *surface; GdkMacosWindow *window; NSEventType event_type; + NSWindow *event_window; GdkEvent *ret = NULL; int x; int y; @@ -1108,6 +1129,15 @@ _gdk_macos_display_translate (GdkMacosDisplay *self, return NULL; } + /* If the event was delivered to NSWindow that is foreign (or rather, + * Cocoa native), then we should pass the event along to that window. + */ + if ((event_window = [nsevent window]) && !GDK_IS_MACOS_WINDOW (event_window)) + return NULL; + + /* If we can't find a GdkSurface to deliver the event to, then we + * should pass it along to the NSApp. + */ if (!(surface = find_surface_for_ns_event (self, nsevent, &x, &y))) return NULL; @@ -1139,15 +1169,31 @@ _gdk_macos_display_translate (GdkMacosDisplay *self, if (test_resize (nsevent, surface, x, y)) return NULL; - if ((event_type == NSEventTypeRightMouseDown || - event_type == NSEventTypeOtherMouseDown || - event_type == NSEventTypeLeftMouseDown)) + if (event_type == NSEventTypeRightMouseDown || + event_type == NSEventTypeOtherMouseDown || + event_type == NSEventTypeLeftMouseDown) { if (![NSApp isActive]) [NSApp activateIgnoringOtherApps:YES]; if (![window isKeyWindow]) - [window makeKeyWindow]; + { + NSWindow *orig_window = [nsevent window]; + + /* To get NSApp to supress activating the window we might + * have clicked through the shadow of, we need to dispatch + * the event and handle it in GdkMacosView:mouseDown to call + * [NSApp preventWindowOrdering]. Calling it here will not + * do anything as the event is not registered. + */ + if (orig_window && + GDK_IS_MACOS_WINDOW (orig_window) && + [(GdkMacosWindow *)orig_window needsMouseDownQuirk]) + [NSApp sendEvent:nsevent]; + + [window showAndMakeKey:YES]; + _gdk_macos_display_clear_sorting (self); + } } switch ((int)event_type) @@ -1180,7 +1226,11 @@ _gdk_macos_display_translate (GdkMacosDisplay *self, GdkDevice *pointer = gdk_seat_get_pointer (seat); GdkDeviceGrabInfo *grab = _gdk_display_get_last_device_grab (GDK_DISPLAY (self), pointer); - if (grab == NULL) + if ([(GdkMacosWindow *)window isInManualResizeOrMove]) + { + ret = GDK_MACOS_EVENT_DROP; + } + else if (grab == NULL) { if (event_type == NSEventTypeMouseExited) [[NSCursor arrowCursor] set]; diff --git a/gdk/macos/gdkmacosdisplay-wm.c b/gdk/macos/gdkmacosdisplay-wm.c index a0dd6dddae..4f0672013c 100644 --- a/gdk/macos/gdkmacosdisplay-wm.c +++ b/gdk/macos/gdkmacosdisplay-wm.c @@ -20,10 +20,13 @@ #include "config.h" #include "gdkmacosdisplay-private.h" -#include "gdkmacosmonitor.h" +#include "gdkmacosmonitor-private.h" #include "gdkmacossurface-private.h" #include "gdkmacostoplevelsurface-private.h" +#define WARP_OFFSET_X 15 +#define WARP_OFFSET_Y 15 + static void _gdk_macos_display_position_toplevel_with_parent (GdkMacosDisplay *self, GdkMacosSurface *surface, @@ -33,52 +36,49 @@ _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; } +static inline gboolean +has_surface_at_origin (const GList *surfaces, + int x, + int y) +{ + for (const GList *iter = surfaces; iter; iter = iter->next) + { + GdkMacosSurface *surface = iter->data; + + if (surface->root_x == x && surface->root_y == y) + return TRUE; + } + + return FALSE; +} + static void _gdk_macos_display_position_toplevel (GdkMacosDisplay *self, GdkMacosSurface *surface, @@ -87,6 +87,7 @@ _gdk_macos_display_position_toplevel (GdkMacosDisplay *self, { cairo_rectangle_int_t surface_rect; GdkRectangle workarea; + const GList *surfaces; GdkMonitor *monitor; CGPoint mouse; @@ -103,16 +104,29 @@ _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; - - /* TODO: If there is another window at this same position, perhaps we should move it */ + _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; + + /* Try to see if there are any other surfaces at this origin and if so, + * adjust until we get something better. + */ + surfaces = _gdk_macos_display_get_surfaces (self); + while (has_surface_at_origin (surfaces, *x, *y)) + { + *x += WARP_OFFSET_X; + *y += WARP_OFFSET_Y; + + /* If we reached the bottom right, just bail and try the workspace origin */ + if (*x + surface->shadow_left + WARP_OFFSET_X > workarea.x + workarea.width || + *y + surface->shadow_top + WARP_OFFSET_Y > workarea.y + workarea.height) + { + *x = workarea.x - surface->shadow_left; + *y = workarea.y - surface->shadow_top; + return; + } + } } /*<private> diff --git a/gdk/macos/gdkmacosdisplay.c b/gdk/macos/gdkmacosdisplay.c index 74095504a2..d85a744563 100644 --- a/gdk/macos/gdkmacosdisplay.c +++ b/gdk/macos/gdkmacosdisplay.c @@ -156,49 +156,11 @@ gdk_macos_display_update_bounds (GdkMacosDisplay *self) self->width = self->max_x - self->min_x; self->height = self->max_y - self->min_y; - GDK_END_MACOS_ALLOC_POOL; -} - -static void -gdk_macos_display_monitors_changed_cb (CFNotificationCenterRef center, - void *observer, - CFStringRef name, - const void *object, - CFDictionaryRef userInfo) -{ - GdkMacosDisplay *self = observer; - - g_assert (GDK_IS_MACOS_DISPLAY (self)); - - _gdk_macos_display_reload_monitors (self); - - /* Now we need to update all our surface positions since they - * probably just changed origins. - */ - for (const GList *iter = _gdk_macos_display_get_surfaces (self); - iter != NULL; - iter = iter->next) - { - GdkMacosSurface *surface = iter->data; - - g_assert (GDK_IS_MACOS_SURFACE (surface)); - - _gdk_macos_surface_monitor_changed (surface); - } -} + GDK_NOTE (MISC, + g_message ("Displays reconfigured to bounds %d,%d %dx%d", + self->min_x, self->min_y, self->width, self->height)); -static void -gdk_macos_display_user_defaults_changed_cb (CFNotificationCenterRef center, - void *observer, - CFStringRef name, - const void *object, - CFDictionaryRef userInfo) -{ - GdkMacosDisplay *self = observer; - - g_assert (GDK_IS_MACOS_DISPLAY (self)); - - _gdk_macos_display_reload_settings (self); + GDK_END_MACOS_ALLOC_POOL; } void @@ -273,56 +235,6 @@ gdk_macos_display_load_seat (GdkMacosDisplay *self) g_object_unref (seat); } -static gboolean -gdk_macos_display_frame_cb (gpointer data) -{ - GdkMacosDisplay *self = data; - GdkDisplayLinkSource *source; - gint64 presentation_time; - gint64 now; - GList *iter; - - g_assert (GDK_IS_MACOS_DISPLAY (self)); - - source = (GdkDisplayLinkSource *)self->frame_source; - - presentation_time = source->presentation_time; - now = g_source_get_time ((GSource *)source); - - iter = self->awaiting_frames.head; - - while (iter != NULL) - { - GdkMacosSurface *surface = iter->data; - - g_assert (GDK_IS_MACOS_SURFACE (surface)); - - iter = iter->next; - - _gdk_macos_surface_publish_timings (surface, - source->presentation_time, - source->refresh_interval); - - _gdk_macos_display_remove_frame_callback (self, surface); - - if (GDK_SURFACE_IS_MAPPED (GDK_SURFACE (surface))) - gdk_surface_thaw_updates (GDK_SURFACE (surface)); - } - - return G_SOURCE_CONTINUE; -} - -static void -gdk_macos_display_load_display_link (GdkMacosDisplay *self) -{ - self->frame_source = gdk_display_link_source_new (); - g_source_set_callback (self->frame_source, - gdk_macos_display_frame_cb, - self, - NULL); - g_source_attach (self->frame_source, NULL); -} - static const char * gdk_macos_display_get_name (GdkDisplay *display) { @@ -398,11 +310,15 @@ gdk_macos_display_queue_events (GdkDisplay *display) g_return_if_fail (GDK_IS_MACOS_DISPLAY (self)); - if ((nsevent = _gdk_macos_event_source_get_pending ())) + while ((nsevent = _gdk_macos_event_source_get_pending ())) { GdkEvent *event = _gdk_macos_display_translate (self, nsevent); - if (event != NULL) + if (event == GDK_MACOS_EVENT_DROP) + { + [nsevent release]; + } + else if (event != NULL) { push_nsevent (event, nsevent); _gdk_windowing_got_event (GDK_DISPLAY (self), @@ -426,7 +342,6 @@ _gdk_macos_display_surface_added (GdkMacosDisplay *self, g_assert (GDK_IS_MACOS_SURFACE (surface)); g_assert (!queue_contains (&self->sorted_surfaces, &surface->sorted)); g_assert (!queue_contains (&self->main_surfaces, &surface->main)); - g_assert (!queue_contains (&self->awaiting_frames, &surface->frame)); g_assert (surface->sorted.data == surface); g_assert (surface->main.data == surface); g_assert (surface->frame.data == surface); @@ -453,9 +368,6 @@ _gdk_macos_display_surface_removed (GdkMacosDisplay *self, if (queue_contains (&self->main_surfaces, &surface->main)) _gdk_macos_display_surface_resigned_main (self, surface); - if (queue_contains (&self->awaiting_frames, &surface->frame)) - g_queue_unlink (&self->awaiting_frames, &surface->frame); - g_return_if_fail (self->keyboard_surface != surface); } @@ -501,6 +413,38 @@ _gdk_macos_display_surface_became_key (GdkMacosDisplay *self, gdk_surface_request_motion (GDK_SURFACE (surface)); } +static gboolean +select_key_in_idle_cb (gpointer data) +{ + GdkMacosDisplay *self = data; + + g_assert (GDK_IS_MACOS_DISPLAY (self)); + + self->select_key_in_idle = 0; + + /* Don't steal focus from NSPanel, etc */ + if (self->key_window_is_foregin) + return G_SOURCE_REMOVE; + + if (self->keyboard_surface == NULL) + { + const GList *surfaces = _gdk_macos_display_get_surfaces (self); + + for (const GList *iter = surfaces; iter; iter = iter->next) + { + GdkMacosSurface *surface = iter->data; + + if (GDK_SURFACE_IS_MAPPED (GDK_SURFACE (surface))) + { + [surface->window showAndMakeKey:YES]; + break; + } + } + } + + return G_SOURCE_REMOVE; +} + void _gdk_macos_display_surface_resigned_key (GdkMacosDisplay *self, GdkMacosSurface *surface) @@ -545,6 +489,9 @@ _gdk_macos_display_surface_resigned_key (GdkMacosDisplay *self, } _gdk_macos_display_clear_sorting (self); + + if (self->select_key_in_idle == 0) + self->select_key_in_idle = g_idle_add (select_key_in_idle_cb, self); } /* Raises a transient window. @@ -583,8 +530,6 @@ void _gdk_macos_display_surface_resigned_main (GdkMacosDisplay *self, GdkMacosSurface *surface) { - GdkMacosSurface *new_surface = NULL; - g_return_if_fail (GDK_IS_MACOS_DISPLAY (self)); g_return_if_fail (GDK_IS_MACOS_SURFACE (surface)); @@ -592,40 +537,6 @@ _gdk_macos_display_surface_resigned_main (GdkMacosDisplay *self, g_queue_unlink (&self->main_surfaces, &surface->main); _gdk_macos_display_clear_sorting (self); - - if (GDK_SURFACE (surface)->transient_for && - gdk_surface_get_mapped (GDK_SURFACE (surface)->transient_for)) - { - new_surface = GDK_MACOS_SURFACE (GDK_SURFACE (surface)->transient_for); - } - else - { - const GList *surfaces = _gdk_macos_display_get_surfaces (self); - - for (const GList *iter = surfaces; iter; iter = iter->next) - { - GdkMacosSurface *item = iter->data; - - g_assert (GDK_IS_MACOS_SURFACE (item)); - - if (item == surface) - continue; - - if (GDK_SURFACE_IS_MAPPED (GDK_SURFACE (item))) - { - new_surface = item; - break; - } - } - } - - if (new_surface != NULL) - { - NSWindow *nswindow = _gdk_macos_surface_get_native (new_surface); - [nswindow makeKeyAndOrderFront:nswindow]; - } - - _gdk_macos_display_clear_sorting (self); } static GdkSurface * @@ -686,20 +597,12 @@ gdk_macos_display_finalize (GObject *object) { GdkMacosDisplay *self = (GdkMacosDisplay *)object; - CFNotificationCenterRemoveObserver (CFNotificationCenterGetDistributedCenter (), - self, - CFSTR ("NSApplicationDidChangeScreenParametersNotification"), - NULL); - - CFNotificationCenterRemoveObserver (CFNotificationCenterGetDistributedCenter (), - self, - CFSTR ("NSUserDefaultsDidChangeNotification"), - NULL); + _gdk_macos_display_feedback_destroy (self); + g_clear_handle_id (&self->select_key_in_idle, g_source_remove); g_clear_pointer (&self->active_drags, g_hash_table_unref); g_clear_pointer (&self->active_drops, g_hash_table_unref); g_clear_object (&GDK_DISPLAY (self)->clipboard); - g_clear_pointer (&self->frame_source, g_source_unref); g_clear_object (&self->monitors); g_clear_pointer (&self->name, g_free); @@ -774,24 +677,10 @@ _gdk_macos_display_open (const char *display_name) gdk_macos_display_load_seat (self); gdk_macos_display_load_clipboard (self); - - /* Load CVDisplayLink before monitors to access refresh rates */ - gdk_macos_display_load_display_link (self); _gdk_macos_display_reload_monitors (self); - CFNotificationCenterAddObserver (CFNotificationCenterGetLocalCenter (), - self, - gdk_macos_display_monitors_changed_cb, - CFSTR ("NSApplicationDidChangeScreenParametersNotification"), - NULL, - CFNotificationSuspensionBehaviorDeliverImmediately); - - CFNotificationCenterAddObserver (CFNotificationCenterGetDistributedCenter (), - self, - gdk_macos_display_user_defaults_changed_cb, - CFSTR ("NSUserDefaultsDidChangeNotification"), - NULL, - CFNotificationSuspensionBehaviorDeliverImmediately); + /* Initialize feedback from display server */ + _gdk_macos_display_feedback_init (self); if (event_source == NULL) { @@ -803,6 +692,8 @@ _gdk_macos_display_open (const char *display_name) gdk_display_emit_opened (GDK_DISPLAY (self)); + [NSApp activateIgnoringOtherApps:YES]; + return GDK_DISPLAY (self); } @@ -1033,42 +924,6 @@ _gdk_macos_display_get_surface_at_display_coords (GdkMacosDisplay *self, return _gdk_macos_display_get_surface_at_coords (self, x_gdk, y_gdk, surface_x, surface_y); } -void -_gdk_macos_display_add_frame_callback (GdkMacosDisplay *self, - GdkMacosSurface *surface) -{ - g_return_if_fail (GDK_IS_MACOS_DISPLAY (self)); - g_return_if_fail (GDK_IS_MACOS_SURFACE (surface)); - - if (!queue_contains (&self->awaiting_frames, &surface->frame)) - { - /* Processing frames is always head to tail, so push to the - * head so that we don't possibly re-enter this right after - * adding to the queue. - */ - g_queue_push_head_link (&self->awaiting_frames, &surface->frame); - - if (self->awaiting_frames.length == 1) - gdk_display_link_source_unpause ((GdkDisplayLinkSource *)self->frame_source); - } -} - -void -_gdk_macos_display_remove_frame_callback (GdkMacosDisplay *self, - GdkMacosSurface *surface) -{ - g_return_if_fail (GDK_IS_MACOS_DISPLAY (self)); - g_return_if_fail (GDK_IS_MACOS_SURFACE (surface)); - - if (queue_contains (&self->awaiting_frames, &surface->frame)) - { - g_queue_unlink (&self->awaiting_frames, &surface->frame); - - if (self->awaiting_frames.length == 0) - gdk_display_link_source_pause ((GdkDisplayLinkSource *)self->frame_source); - } -} - NSWindow * _gdk_macos_display_find_native_under_pointer (GdkMacosDisplay *self, int *x, @@ -1088,17 +943,6 @@ _gdk_macos_display_find_native_under_pointer (GdkMacosDisplay *self, return NULL; } -int -_gdk_macos_display_get_nominal_refresh_rate (GdkMacosDisplay *self) -{ - g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (self), 60 * 1000); - - if (self->frame_source == NULL) - return 60 * 1000; - - return ((GdkDisplayLinkSource *)self->frame_source)->refresh_rate; -} - void _gdk_macos_display_clear_sorting (GdkMacosDisplay *self) { @@ -1120,11 +964,16 @@ _gdk_macos_display_get_surfaces (GdkMacosDisplay *self) NSArray *array = [NSApp orderedWindows]; GQueue sorted = G_QUEUE_INIT; + self->key_window_is_foregin = FALSE; + for (id obj in array) { NSWindow *nswindow = (NSWindow *)obj; GdkMacosSurface *surface; + if ([nswindow isKeyWindow]) + self->key_window_is_foregin = !GDK_IS_MACOS_WINDOW (nswindow); + if (!GDK_IS_MACOS_WINDOW (nswindow)) continue; 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); } diff --git a/gdk/macos/gdkmacosmonitor-private.h b/gdk/macos/gdkmacosmonitor-private.h index e15f17352d..1a4e197f76 100644 --- a/gdk/macos/gdkmacosmonitor-private.h +++ b/gdk/macos/gdkmacosmonitor-private.h @@ -24,16 +24,25 @@ #include "gdkmacosdisplay.h" #include "gdkmacosmonitor.h" +#include "gdkmacossurface.h" #include "gdkmonitorprivate.h" G_BEGIN_DECLS -GdkMacosMonitor *_gdk_macos_monitor_new (GdkMacosDisplay *display, - CGDirectDisplayID screen_id); -CGDirectDisplayID _gdk_macos_monitor_get_screen_id (GdkMacosMonitor *self); -gboolean _gdk_macos_monitor_reconfigure (GdkMacosMonitor *self); -CGColorSpaceRef _gdk_macos_monitor_copy_colorspace (GdkMacosMonitor *self); +char *_gdk_macos_monitor_get_localized_name (NSScreen *screen); +char *_gdk_macos_monitor_get_connector_name (CGDirectDisplayID screen_id); +GdkMacosMonitor *_gdk_macos_monitor_new (GdkMacosDisplay *display, + CGDirectDisplayID screen_id); +CGDirectDisplayID _gdk_macos_monitor_get_screen_id (GdkMacosMonitor *self); +gboolean _gdk_macos_monitor_reconfigure (GdkMacosMonitor *self); +CGColorSpaceRef _gdk_macos_monitor_copy_colorspace (GdkMacosMonitor *self); +void _gdk_macos_monitor_add_frame_callback (GdkMacosMonitor *self, + 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 6df1da0edc..a9aba3634b 100644 --- a/gdk/macos/gdkmacosmonitor.c +++ b/gdk/macos/gdkmacosmonitor.c @@ -22,16 +22,21 @@ #include <gdk/gdk.h> #include <math.h> +#include "gdkdisplaylinksource.h" #include "gdkmacosdisplay-private.h" #include "gdkmacosmonitor-private.h" +#include "gdkmacossurface-private.h" #include "gdkmacosutils-private.h" struct _GdkMacosMonitor { - GdkMonitor parent_instance; - CGDirectDisplayID screen_id; - NSRect workarea; - guint has_opengl : 1; + GdkMonitor parent_instance; + CGDirectDisplayID screen_id; + GdkDisplayLinkSource *display_link; + NSRect workarea; + GQueue awaiting_frames; + guint has_opengl : 1; + guint in_frame : 1; }; struct _GdkMacosMonitorClass @@ -76,8 +81,25 @@ gdk_macos_monitor_get_workarea (GdkMonitor *monitor, } static void +gdk_macos_monitor_dispose (GObject *object) +{ + GdkMacosMonitor *self = (GdkMacosMonitor *)object; + + if (self->display_link) + { + g_source_destroy ((GSource *)self->display_link); + g_clear_pointer ((GSource **)&self->display_link, g_source_unref); + } + + G_OBJECT_CLASS (gdk_macos_monitor_parent_class)->dispose (object); +} + +static void gdk_macos_monitor_class_init (GdkMacosMonitorClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gdk_macos_monitor_dispose; } static void @@ -138,8 +160,8 @@ GetSubpixelLayout (CGDirectDisplayID screen_id) return GDK_SUBPIXEL_LAYOUT_UNKNOWN; } -static char * -GetLocalizedName (NSScreen *screen) +char * +_gdk_macos_monitor_get_localized_name (NSScreen *screen) { #ifdef AVAILABLE_MAC_OS_X_VERSION_10_15_AND_LATER GDK_BEGIN_MACOS_ALLOC_POOL; @@ -160,8 +182,8 @@ GetLocalizedName (NSScreen *screen) #endif } -static char * -GetConnectorName (CGDirectDisplayID screen_id) +char * +_gdk_macos_monitor_get_connector_name (CGDirectDisplayID screen_id) { guint unit = CGDisplayUnitNumber (screen_id); return g_strdup_printf ("unit-%u", unit); @@ -188,6 +210,68 @@ find_screen (CGDirectDisplayID screen_id) return screen; } +static gboolean +gdk_macos_monitor_display_link_cb (GdkMacosMonitor *self) +{ + gint64 presentation_time; + gint64 refresh_interval; + gint64 now; + GList *iter; + + g_assert (GDK_IS_MACOS_MONITOR (self)); + g_assert (!self->display_link->paused); + + self->in_frame = TRUE; + + presentation_time = self->display_link->presentation_time; + refresh_interval = self->display_link->refresh_interval; + now = g_source_get_time ((GSource *)self->display_link); + + iter = self->awaiting_frames.head; + + while (iter != NULL) + { + GdkMacosSurface *surface = iter->data; + + g_assert (GDK_IS_MACOS_SURFACE (surface)); + + iter = iter->next; + + g_queue_unlink (&self->awaiting_frames, &surface->frame); + _gdk_macos_surface_frame_presented (surface, presentation_time, refresh_interval); + } + + if (self->awaiting_frames.length == 0 && !self->display_link->paused) + gdk_display_link_source_pause (self->display_link); + + self->in_frame = FALSE; + + return G_SOURCE_CONTINUE; +} + +static void +_gdk_macos_monitor_reset_display_link (GdkMacosMonitor *self, + CGDisplayModeRef mode) +{ + GSource *source; + + g_assert (GDK_IS_MACOS_MONITOR (self)); + + if (self->display_link) + { + g_source_destroy ((GSource *)self->display_link); + g_clear_pointer ((GSource **)&self->display_link, g_source_unref); + } + + source = gdk_display_link_source_new (self->screen_id, mode); + self->display_link = (GdkDisplayLinkSource *)source; + g_source_set_callback (source, + (GSourceFunc) gdk_macos_monitor_display_link_cb, + self, + NULL); + g_source_attach (source, NULL); +} + gboolean _gdk_macos_monitor_reconfigure (GdkMacosMonitor *self) { @@ -222,8 +306,8 @@ _gdk_macos_monitor_reconfigure (GdkMacosMonitor *self) pixel_width = CGDisplayModeGetPixelWidth (mode); has_opengl = CGDisplayUsesOpenGLAcceleration (self->screen_id); subpixel_layout = GetSubpixelLayout (self->screen_id); - name = GetLocalizedName (screen); - connector = GetConnectorName (self->screen_id); + name = _gdk_macos_monitor_get_localized_name (screen); + connector = _gdk_macos_monitor_get_connector_name (self->screen_id); if (width != 0 && pixel_width != 0) scale_factor = MAX (1, pixel_width / width); @@ -241,7 +325,7 @@ _gdk_macos_monitor_reconfigure (GdkMacosMonitor *self) * setting (which is also used by the frame clock). */ if (!(refresh_rate = CGDisplayModeGetRefreshRate (mode))) - refresh_rate = _gdk_macos_display_get_nominal_refresh_rate (display); + refresh_rate = 60 * 1000; gdk_monitor_set_connector (GDK_MONITOR (self), connector); gdk_monitor_set_model (GDK_MONITOR (self), name); @@ -261,6 +345,9 @@ _gdk_macos_monitor_reconfigure (GdkMacosMonitor *self) */ self->has_opengl = !!has_opengl; + /* Create a new display link to receive feedback about when to render */ + _gdk_macos_monitor_reset_display_link (self, mode); + CGDisplayModeRelease (mode); g_free (name); g_free (connector); @@ -302,3 +389,71 @@ _gdk_macos_monitor_copy_colorspace (GdkMacosMonitor *self) return CGDisplayCopyColorSpace (self->screen_id); } + +void +_gdk_macos_monitor_add_frame_callback (GdkMacosMonitor *self, + GdkMacosSurface *surface) +{ + g_return_if_fail (GDK_IS_MACOS_MONITOR (self)); + g_return_if_fail (GDK_IS_MACOS_SURFACE (surface)); + g_return_if_fail (surface->frame.data == (gpointer)surface); + g_return_if_fail (surface->frame.prev == NULL); + g_return_if_fail (surface->frame.next == NULL); + g_return_if_fail (self->awaiting_frames.head != &surface->frame); + g_return_if_fail (self->awaiting_frames.tail != &surface->frame); + + /* Processing frames is always head to tail, so push to the + * head so that we don't possibly re-enter this right after + * adding to the queue. + */ + if (!queue_contains (&self->awaiting_frames, &surface->frame)) + { + g_queue_push_head_link (&self->awaiting_frames, &surface->frame); + + if (!self->in_frame && self->awaiting_frames.length == 1) + gdk_display_link_source_unpause (self->display_link); + } +} + +void +_gdk_macos_monitor_remove_frame_callback (GdkMacosMonitor *self, + GdkMacosSurface *surface) +{ + g_return_if_fail (GDK_IS_MACOS_MONITOR (self)); + g_return_if_fail (GDK_IS_MACOS_SURFACE (surface)); + g_return_if_fail (surface->frame.data == (gpointer)surface); + + if (queue_contains (&self->awaiting_frames, &surface->frame)) + { + g_queue_unlink (&self->awaiting_frames, &surface->frame); + + if (!self->in_frame && self->awaiting_frames.length == 0) + 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; +} diff --git a/gdk/macos/gdkmacospopupsurface.c b/gdk/macos/gdkmacospopupsurface.c index 477961503f..061af9d85d 100644 --- a/gdk/macos/gdkmacospopupsurface.c +++ b/gdk/macos/gdkmacospopupsurface.c @@ -34,6 +34,7 @@ struct _GdkMacosPopupSurface { GdkMacosSurface parent_instance; GdkPopupLayout *layout; + guint attached : 1; }; struct _GdkMacosPopupSurfaceClass @@ -87,6 +88,9 @@ gdk_macos_popup_surface_layout (GdkMacosPopupSurface *self, gdk_surface_get_origin (GDK_SURFACE (self)->parent, &x, &y); + GDK_SURFACE (self)->x = final_rect.x; + GDK_SURFACE (self)->y = final_rect.y; + x += final_rect.x; y += final_rect.y; @@ -135,6 +139,9 @@ gdk_macos_popup_surface_present (GdkPopup *popup, if (GDK_SURFACE_IS_MAPPED (GDK_SURFACE (self))) return TRUE; + if (!self->attached && GDK_SURFACE (self)->parent != NULL) + _gdk_macos_popup_surface_attach_to_parent (self); + if (GDK_SURFACE (self)->autohide) { GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (popup)); @@ -201,6 +208,19 @@ enum { }; static void +_gdk_macos_popup_surface_hide (GdkSurface *surface) +{ + GdkMacosPopupSurface *self = (GdkMacosPopupSurface *)surface; + + g_assert (GDK_IS_MACOS_POPUP_SURFACE (self)); + + if (self->attached) + _gdk_macos_popup_surface_detach_from_parent (self); + + GDK_SURFACE_CLASS (_gdk_macos_popup_surface_parent_class)->hide (surface); +} + +static void _gdk_macos_popup_surface_finalize (GObject *object) { GdkMacosPopupSurface *self = (GdkMacosPopupSurface *)object; @@ -267,12 +287,15 @@ static void _gdk_macos_popup_surface_class_init (GdkMacosPopupSurfaceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + GdkSurfaceClass *surface_class = GDK_SURFACE_CLASS (klass); object_class->finalize = _gdk_macos_popup_surface_finalize; 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); + surface_class->hide = _gdk_macos_popup_surface_hide; + + gdk_popup_install_properties (object_class, LAST_PROP); } static void @@ -323,14 +346,8 @@ _gdk_macos_popup_surface_new (GdkMacosDisplay *display, [window setOpaque:NO]; [window setBackgroundColor:[NSColor clearColor]]; [window setDecorated:NO]; - -#if 0 - /* NOTE: We could set these to be popup level, but then - * [NSApp orderedWindows] would not give us the windows - * back with the stacking order applied. - */ + [window setExcludedFromWindowsMenu:YES]; [window setLevel:NSPopUpMenuWindowLevel]; -#endif self = g_object_new (GDK_TYPE_MACOS_POPUP_SURFACE, "display", display, @@ -361,6 +378,8 @@ _gdk_macos_popup_surface_attach_to_parent (GdkMacosPopupSurface *self) [parent addChildWindow:window ordered:NSWindowAbove]; + self->attached = TRUE; + _gdk_macos_display_clear_sorting (GDK_MACOS_DISPLAY (surface->display)); } } @@ -382,6 +401,8 @@ _gdk_macos_popup_surface_detach_from_parent (GdkMacosPopupSurface *self) [parent removeChildWindow:window]; + self->attached = FALSE; + _gdk_macos_display_clear_sorting (GDK_MACOS_DISPLAY (surface->display)); } } @@ -391,9 +412,7 @@ _gdk_macos_popup_surface_reposition (GdkMacosPopupSurface *self) { g_return_if_fail (GDK_IS_MACOS_POPUP_SURFACE (self)); - if (self->layout == NULL || - !gdk_surface_get_mapped (GDK_SURFACE (self)) || - GDK_SURFACE (self)->parent == NULL) + if (self->layout == NULL || GDK_SURFACE (self)->parent == NULL) return; gdk_macos_popup_surface_layout (self, diff --git a/gdk/macos/gdkmacossurface-private.h b/gdk/macos/gdkmacossurface-private.h index 2abc199698..5f1f551f93 100644 --- a/gdk/macos/gdkmacossurface-private.h +++ b/gdk/macos/gdkmacossurface-private.h @@ -49,7 +49,7 @@ struct _GdkMacosSurface GdkMacosBuffer *buffer; GdkMacosBuffer *front; GPtrArray *monitors; - cairo_region_t *opaque_region; + GdkMonitor *best_monitor; char *title; int root_x; @@ -75,6 +75,9 @@ 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; }; struct _GdkMacosSurfaceClass @@ -116,11 +119,11 @@ void _gdk_macos_surface_resize (GdkMacosSurface int width, int height); void _gdk_macos_surface_update_fullscreen_state (GdkMacosSurface *self); -void _gdk_macos_surface_update_position (GdkMacosSurface *self); -void _gdk_macos_surface_show (GdkMacosSurface *self); -void _gdk_macos_surface_publish_timings (GdkMacosSurface *self, +void _gdk_macos_surface_request_frame (GdkMacosSurface *self); +void _gdk_macos_surface_frame_presented (GdkMacosSurface *self, gint64 predicted_presentation_time, gint64 refresh_interval); +void _gdk_macos_surface_show (GdkMacosSurface *self); void _gdk_macos_surface_synthesize_null_key (GdkMacosSurface *self); void _gdk_macos_surface_move (GdkMacosSurface *self, int x, diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c index 5998c4162f..449a3b35a5 100644 --- a/gdk/macos/gdkmacossurface.c +++ b/gdk/macos/gdkmacossurface.c @@ -27,6 +27,7 @@ #include "gdkmacossurface-private.h" +#include "gdkdebug.h" #include "gdkdeviceprivate.h" #include "gdkdisplay.h" #include "gdkeventsprivate.h" @@ -63,6 +64,79 @@ window_is_fullscreen (GdkMacosSurface *self) return ([self->window styleMask] & NSWindowStyleMaskFullScreen) != 0; } +void +_gdk_macos_surface_request_frame (GdkMacosSurface *self) +{ + g_assert (GDK_IS_MACOS_SURFACE (self)); + + if (self->awaiting_frame) + return; + + 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 +_gdk_macos_surface_cancel_frame (GdkMacosSurface *self) +{ + g_assert (GDK_IS_MACOS_SURFACE (self)); + + if (!self->awaiting_frame) + return; + + 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 +_gdk_macos_surface_frame_presented (GdkMacosSurface *self, + gint64 presentation_time, + gint64 refresh_interval) +{ + GdkFrameTimings *timings; + GdkFrameClock *frame_clock; + + g_return_if_fail (GDK_IS_MACOS_SURFACE (self)); + + self->awaiting_frame = FALSE; + + if (GDK_SURFACE_DESTROYED (self)) + return; + + frame_clock = gdk_surface_get_frame_clock (GDK_SURFACE (self)); + + if (self->pending_frame_counter) + { + timings = gdk_frame_clock_get_timings (frame_clock, self->pending_frame_counter); + + if (timings != NULL) + { + timings->presentation_time = presentation_time - refresh_interval; + timings->complete = TRUE; + } + + self->pending_frame_counter = 0; + } + + timings = gdk_frame_clock_get_current_timings (frame_clock); + + if (timings != NULL) + { + timings->refresh_interval = refresh_interval; + timings->predicted_presentation_time = presentation_time; + } + + if (GDK_SURFACE_IS_MAPPED (GDK_SURFACE (self))) + gdk_surface_thaw_updates (GDK_SURFACE (self)); +} void _gdk_macos_surface_reposition_children (GdkMacosSurface *self) @@ -84,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 @@ -115,12 +186,6 @@ gdk_macos_surface_set_opaque_region (GdkSurface *surface, g_assert (GDK_IS_MACOS_SURFACE (self)); - if (region != self->opaque_region) - { - g_clear_pointer (&self->opaque_region, cairo_region_destroy); - self->opaque_region = cairo_region_copy (region); - } - if ((nsview = _gdk_macos_surface_get_view (GDK_MACOS_SURFACE (surface)))) [(GdkMacosView *)nsview setOpaqueRegion:region]; } @@ -137,9 +202,9 @@ gdk_macos_surface_hide (GdkSurface *surface) self->show_on_next_swap = FALSE; - _gdk_macos_display_remove_frame_callback (GDK_MACOS_DISPLAY (surface->display), self); + _gdk_macos_surface_cancel_frame (self); - was_mapped = GDK_SURFACE_IS_MAPPED (GDK_SURFACE (self)); + was_mapped = GDK_SURFACE_IS_MAPPED (surface); was_key = [self->window isKeyWindow]; seat = gdk_display_get_default_seat (surface->display); @@ -154,17 +219,21 @@ gdk_macos_surface_hide (GdkSurface *surface) if (was_key) { + GdkSurface *parent; + + if (GDK_IS_TOPLEVEL (surface)) + parent = surface->transient_for; + else + parent = surface->parent; + /* Return key input to the parent window if necessary */ - if (surface->parent != NULL && GDK_SURFACE_IS_MAPPED (surface->parent)) + if (parent != NULL && GDK_SURFACE_IS_MAPPED (parent)) { - GdkMacosWindow *parentWindow = GDK_MACOS_SURFACE (surface->parent)->window; + GdkMacosWindow *parentWindow = GDK_MACOS_SURFACE (parent)->window; [parentWindow showAndMakeKey:YES]; } } - - if (was_mapped) - gdk_surface_freeze_updates (GDK_SURFACE (self)); } static int @@ -208,6 +277,7 @@ gdk_macos_surface_begin_frame (GdkMacosSurface *self) { g_assert (GDK_IS_MACOS_SURFACE (self)); + self->in_frame = TRUE; } static void @@ -228,11 +298,9 @@ gdk_macos_surface_end_frame (GdkMacosSurface *self) if ((timings = gdk_frame_clock_get_current_timings (frame_clock))) self->pending_frame_counter = timings->frame_counter; - if (GDK_SURFACE_IS_MAPPED (GDK_SURFACE (self))) - { - _gdk_macos_display_add_frame_callback (GDK_MACOS_DISPLAY (display), self); - gdk_surface_freeze_updates (GDK_SURFACE (self)); - } + self->in_frame = FALSE; + + _gdk_macos_surface_request_frame (self); } static void @@ -406,6 +474,9 @@ gdk_macos_surface_destroy (GdkSurface *surface, GdkMacosWindow *window = g_steal_pointer (&self->window); GdkFrameClock *frame_clock; + _gdk_macos_surface_cancel_frame (self); + g_clear_object (&self->best_monitor); + if ((frame_clock = gdk_surface_get_frame_clock (GDK_SURFACE (self)))) { g_signal_handlers_disconnect_by_func (frame_clock, @@ -417,7 +488,6 @@ gdk_macos_surface_destroy (GdkSurface *surface, } g_clear_pointer (&self->title, g_free); - g_clear_pointer (&self->opaque_region, cairo_region_destroy); if (window != NULL) [window close]; @@ -559,7 +629,7 @@ _gdk_macos_surface_new (GdkMacosDisplay *display, g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (display), NULL); if (parent != NULL) - frame_clock = g_object_ref (gdk_surface_get_frame_clock (parent)); + frame_clock = g_object_ref (parent->frame_clock); else frame_clock = _gdk_frame_clock_idle_new (); @@ -619,14 +689,16 @@ _gdk_macos_surface_get_shadow (GdkMacosSurface *self, gboolean _gdk_macos_surface_is_opaque (GdkMacosSurface *self) { + GdkSurface *surface = (GdkSurface *)self; + g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), FALSE); - if (self->opaque_region != NULL && - cairo_region_num_rectangles (self->opaque_region) == 1) + if (surface->opaque_region != NULL && + cairo_region_num_rectangles (surface->opaque_region) == 1) { cairo_rectangle_int_t extents; - cairo_region_get_extents (self->opaque_region, &extents); + cairo_region_get_extents (surface->opaque_region, &extents); return (extents.x == 0 && extents.y == 0 && @@ -746,8 +818,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; @@ -756,6 +829,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]; @@ -765,10 +845,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 { @@ -786,7 +866,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); } @@ -794,41 +873,6 @@ _gdk_macos_surface_configure (GdkMacosSurface *self) } void -_gdk_macos_surface_publish_timings (GdkMacosSurface *self, - gint64 presentation_time, - gint64 refresh_interval) -{ - GdkFrameTimings *timings; - GdkFrameClock *frame_clock; - - g_return_if_fail (GDK_IS_MACOS_SURFACE (self)); - - if (!(frame_clock = gdk_surface_get_frame_clock (GDK_SURFACE (self)))) - return; - - if (self->pending_frame_counter) - { - timings = gdk_frame_clock_get_timings (frame_clock, self->pending_frame_counter); - - if (timings != NULL) - { - timings->presentation_time = presentation_time - refresh_interval; - timings->complete = TRUE; - } - - self->pending_frame_counter = 0; - } - - timings = gdk_frame_clock_get_current_timings (frame_clock); - - if (timings != NULL) - { - timings->refresh_interval = refresh_interval; - timings->predicted_presentation_time = presentation_time; - } -} - -void _gdk_macos_surface_show (GdkMacosSurface *self) { gboolean was_mapped; @@ -838,22 +882,17 @@ _gdk_macos_surface_show (GdkMacosSurface *self) if (GDK_SURFACE_DESTROYED (self)) return; - was_mapped = GDK_SURFACE_IS_MAPPED (GDK_SURFACE (self)); - - if (!was_mapped) - gdk_surface_set_is_mapped (GDK_SURFACE (self), TRUE); - _gdk_macos_display_clear_sorting (GDK_MACOS_DISPLAY (GDK_SURFACE (self)->display)); - self->show_on_next_swap = TRUE; + was_mapped = GDK_SURFACE_IS_MAPPED (GDK_SURFACE (self)); + if (!was_mapped) { - if (gdk_surface_get_mapped (GDK_SURFACE (self))) - { - _gdk_macos_surface_configure (self); - gdk_surface_thaw_updates (GDK_SURFACE (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)); } } @@ -906,36 +945,59 @@ _gdk_macos_surface_move_resize (GdkMacosSurface *self, GdkDisplay *display; NSRect content_rect; NSRect frame_rect; + gboolean ignore_move; + gboolean ignore_size; + GdkRectangle current; g_return_if_fail (GDK_IS_MACOS_SURFACE (self)); - if ((x == -1 || (x == self->root_x)) && - (y == -1 || (y == self->root_y)) && - (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; 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 = NSMakeRect (x, y, width, height); + if (!ignore_move) + content_rect.origin = NSMakePoint (x, y); + + if (!ignore_size) + 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 @@ -990,14 +1052,24 @@ void _gdk_macos_surface_monitor_changed (GdkMacosSurface *self) { GListModel *monitors; + GdkMonitor *best = NULL; GdkRectangle rect; GdkRectangle intersect; GdkDisplay *display; GdkMonitor *monitor; guint n_monitors; + int best_area = 0; 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); + _gdk_macos_surface_configure (self); + rect.x = self->root_x; rect.y = self->root_y; rect.width = GDK_SURFACE (self)->width; @@ -1037,29 +1109,10 @@ _gdk_macos_surface_monitor_changed (GdkMacosSurface *self) g_clear_object (&self->buffer); g_clear_object (&self->front); - _gdk_macos_surface_configure (self); - - gdk_surface_invalidate_rect (GDK_SURFACE (self), NULL); -} - -GdkMonitor * -_gdk_macos_surface_get_best_monitor (GdkMacosSurface *self) -{ - GdkMonitor *best = NULL; - GdkRectangle rect; - int best_area = 0; - - g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), NULL); - - rect.x = self->root_x; - rect.y = self->root_y; - rect.width = GDK_SURFACE (self)->width; - rect.height = GDK_SURFACE (self)->height; - + /* Determine the best-fit monitor */ for (guint i = 0; i < self->monitors->len; i++) { - GdkMonitor *monitor = g_ptr_array_index (self->monitors, i); - GdkRectangle intersect; + monitor = g_ptr_array_index (self->monitors, i); if (gdk_rectangle_intersect (&monitor->geometry, &rect, &intersect)) { @@ -1067,13 +1120,62 @@ _gdk_macos_surface_get_best_monitor (GdkMacosSurface *self) if (area > best_area) { - best = monitor; best_area = area; + best = monitor; } } } - return best; + if (g_set_object (&self->best_monitor, best)) + { + GDK_NOTE (MISC, + 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_surface_invalidate_rect (GDK_SURFACE (self), NULL); + + self->in_change_monitor = FALSE; +} + +GdkMonitor * +_gdk_macos_surface_get_best_monitor (GdkMacosSurface *self) +{ + g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), NULL); + + return self->best_monitor; } NSView * @@ -1149,11 +1251,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 diff --git a/gdk/macos/gdkmacostoplevelsurface.c b/gdk/macos/gdkmacostoplevelsurface.c index 27289787ca..1759077563 100644 --- a/gdk/macos/gdkmacostoplevelsurface.c +++ b/gdk/macos/gdkmacostoplevelsurface.c @@ -174,6 +174,14 @@ _gdk_macos_toplevel_surface_present (GdkToplevel *toplevel, _gdk_macos_surface_set_geometry_hints (GDK_MACOS_SURFACE (self), &geometry, mask); gdk_surface_constrain_size (&geometry, mask, width, height, &width, &height); + + GDK_NOTE (MISC, + g_message ("Resizing \"%s\" to %dx%d", + GDK_MACOS_SURFACE (self)->title ? + GDK_MACOS_SURFACE (self)->title : + "untitled", + width, height)); + _gdk_macos_surface_resize (GDK_MACOS_SURFACE (self), width, height); /* Maximized state */ @@ -202,6 +210,13 @@ _gdk_macos_toplevel_surface_present (GdkToplevel *toplevel, GDK_MACOS_SURFACE (self), &x, &y); + GDK_NOTE (MISC, + g_message ("Placing new toplevel \"%s\" at %d,%d", + GDK_MACOS_SURFACE (self)->title ? + GDK_MACOS_SURFACE (self)->title : + "untitled", + x, y)); + _gdk_macos_surface_move (GDK_MACOS_SURFACE (self), x, y); } @@ -421,7 +436,8 @@ _gdk_macos_toplevel_surface_compute_size (GdkSurface *surface) GDK_TOPLEVEL_STATE_RIGHT_TILED | GDK_TOPLEVEL_STATE_BOTTOM_TILED | GDK_TOPLEVEL_STATE_LEFT_TILED | - GDK_TOPLEVEL_STATE_MINIMIZED)) + GDK_TOPLEVEL_STATE_MINIMIZED) || + [macos_surface->window inLiveResize]) return FALSE; /* If we delayed a user resize until the beginning of the frame, @@ -632,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; @@ -648,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; + } - 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); + _gdk_macos_display_to_display_coords (display, x, y + height, &nx, &ny); + screen = _gdk_macos_display_get_screen_at_display_coords (display, nx, ny); + 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 @@ -691,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); } } diff --git a/gdk/macos/meson.build b/gdk/macos/meson.build index d17a60ac09..bd7bbb5324 100644 --- a/gdk/macos/meson.build +++ b/gdk/macos/meson.build @@ -8,6 +8,7 @@ gdk_macos_sources = files([ 'gdkmacoscursor.c', 'gdkmacosdevice.c', 'gdkmacosdisplay.c', + 'gdkmacosdisplay-feedback.c', 'gdkmacosdisplay-settings.c', 'gdkmacosdisplay-translate.c', 'gdkmacosdisplay-wm.c', diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c index ee8c2b0ba4..4879fee68d 100644 --- a/gtk/gtkscrolledwindow.c +++ b/gtk/gtkscrolledwindow.c @@ -1226,16 +1226,15 @@ check_update_scrollbar_proximity (GtkScrolledWindow *sw, } static double -get_scroll_unit (GtkScrolledWindow *sw, - GtkOrientation orientation) +get_scroll_unit (GtkScrolledWindow *sw, + GtkOrientation orientation, + GtkEventControllerScroll *scroll) { - double scroll_unit; - -#ifndef GDK_WINDOWING_MACOS GtkScrolledWindowPrivate *priv = gtk_scrolled_window_get_instance_private (sw); GtkScrollbar *scrollbar; GtkAdjustment *adj; double page_size; + double scroll_unit; if (orientation == GTK_ORIENTATION_HORIZONTAL) scrollbar = GTK_SCROLLBAR (priv->hscrollbar); @@ -1248,8 +1247,16 @@ get_scroll_unit (GtkScrolledWindow *sw, adj = gtk_scrollbar_get_adjustment (scrollbar); page_size = gtk_adjustment_get_page_size (adj); scroll_unit = pow (page_size, 2.0 / 3.0); -#else - scroll_unit = 1; + +#ifdef GDK_WINDOWING_MACOS + { + GdkEvent *event = gtk_event_controller_get_current_event (GTK_EVENT_CONTROLLER (scroll)); + + if (event != NULL && + gdk_event_get_event_type (event) == GDK_SCROLL && + gdk_scroll_event_get_direction (event) == GDK_SCROLL_SMOOTH) + scroll_unit = 1; + } #endif return scroll_unit; @@ -1397,7 +1404,7 @@ scrolled_window_scroll (GtkScrolledWindow *scrolled_window, double scroll_unit; adj = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->hscrollbar)); - scroll_unit = get_scroll_unit (scrolled_window, GTK_ORIENTATION_HORIZONTAL); + scroll_unit = get_scroll_unit (scrolled_window, GTK_ORIENTATION_HORIZONTAL, scroll); new_value = priv->unclamped_hadj_value + delta_x * scroll_unit; _gtk_scrolled_window_set_adjustment_value (scrolled_window, adj, @@ -1412,7 +1419,7 @@ scrolled_window_scroll (GtkScrolledWindow *scrolled_window, double scroll_unit; adj = gtk_scrollbar_get_adjustment (GTK_SCROLLBAR (priv->vscrollbar)); - scroll_unit = get_scroll_unit (scrolled_window, GTK_ORIENTATION_VERTICAL); + scroll_unit = get_scroll_unit (scrolled_window, GTK_ORIENTATION_VERTICAL, scroll); new_value = priv->unclamped_vadj_value + delta_y * scroll_unit; _gtk_scrolled_window_set_adjustment_value (scrolled_window, adj, @@ -1470,8 +1477,8 @@ scroll_controller_decelerate (GtkEventControllerScroll *scroll, shifted = (state & GDK_SHIFT_MASK) != 0; - unit_x = get_scroll_unit (scrolled_window, GTK_ORIENTATION_HORIZONTAL); - unit_y = get_scroll_unit (scrolled_window, GTK_ORIENTATION_VERTICAL); + unit_x = get_scroll_unit (scrolled_window, GTK_ORIENTATION_HORIZONTAL, scroll); + unit_y = get_scroll_unit (scrolled_window, GTK_ORIENTATION_VERTICAL, scroll); if (shifted) { |