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; + impl->refresh_rate = CGDisplayModeGetRefreshRate (mode) * 1000.0; - /* - * Wire up our callback to be executed within the high-priority thread. - */ + 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; @@ -158,6 +142,52 @@ failure: return cr; } +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, @@ -165,34 +195,68 @@ _gdk_macos_cairo_context_begin_frame (GdkDrawContext *draw_context, { 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_surface_swap_buffers (GDK_MACOS_SURFACE (surface), painted); - _gdk_macos_buffer_set_damage (buffer, NULL); + _gdk_macos_buffer_unlock (buffer); + + _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 . + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include + +#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..ef17618d61 100644 --- a/gdk/macos/gdkmacosdisplay-private.h +++ b/gdk/macos/gdkmacosdisplay-private.h @@ -68,16 +68,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; @@ -124,6 +114,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 +128,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 +143,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-translate.c b/gdk/macos/gdkmacosdisplay-translate.c index 646ba0cd9e..e3fb03fea5 100644 --- a/gdk/macos/gdkmacosdisplay-translate.c +++ b/gdk/macos/gdkmacosdisplay-translate.c @@ -635,6 +635,17 @@ fill_scroll_event (GdkMacosDisplay *self, 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]; @@ -678,31 +689,17 @@ 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; + g_assert (ret == NULL); - 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 - { - ret = gdk_scroll_event_new_discrete (GDK_SURFACE (surface), - pointer, - NULL, - get_time_from_ns_event (nsevent), - state, - direction, - FALSE); - } + 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) diff --git a/gdk/macos/gdkmacosdisplay.c b/gdk/macos/gdkmacosdisplay.c index 74095504a2..ddcc274372 100644 --- a/gdk/macos/gdkmacosdisplay.c +++ b/gdk/macos/gdkmacosdisplay.c @@ -159,48 +159,6 @@ gdk_macos_display_update_bounds (GdkMacosDisplay *self) 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); - } -} - -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); -} - void _gdk_macos_display_reload_monitors (GdkMacosDisplay *self) { @@ -273,56 +231,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) { @@ -426,7 +334,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 +360,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); } @@ -686,20 +590,11 @@ 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_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 +669,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 +684,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 +916,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 +935,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) { diff --git a/gdk/macos/gdkmacosmonitor-private.h b/gdk/macos/gdkmacosmonitor-private.h index e15f17352d..dfde4142c0 100644 --- a/gdk/macos/gdkmacosmonitor-private.h +++ b/gdk/macos/gdkmacosmonitor-private.h @@ -24,16 +24,23 @@ #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); G_END_DECLS diff --git a/gdk/macos/gdkmacosmonitor.c b/gdk/macos/gdkmacosmonitor.c index 6df1da0edc..bfec76b7ef 100644 --- a/gdk/macos/gdkmacosmonitor.c +++ b/gdk/macos/gdkmacosmonitor.c @@ -22,16 +22,21 @@ #include #include +#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 @@ -75,9 +80,26 @@ gdk_macos_monitor_get_workarea (GdkMonitor *monitor, geometry->height = self->workarea.size.height; } +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,45 @@ _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); + } +} diff --git a/gdk/macos/gdkmacospopupsurface.c b/gdk/macos/gdkmacospopupsurface.c index 477961503f..5a2d7ed481 100644 --- a/gdk/macos/gdkmacospopupsurface.c +++ b/gdk/macos/gdkmacospopupsurface.c @@ -87,6 +87,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; @@ -391,9 +394,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 08947f1ce3..15a7442c17 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,8 @@ struct _GdkMacosSurface guint geometry_dirty : 1; guint next_frame_set : 1; guint show_on_next_swap : 1; + guint in_frame : 1; + guint awaiting_frame : 1; }; struct _GdkMacosSurfaceClass @@ -116,10 +118,11 @@ void _gdk_macos_surface_resize (GdkMacosSurface int width, int height); void _gdk_macos_surface_update_fullscreen_state (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 f837e2c650..cb900a7328 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,73 @@ 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; + + 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; + + 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) @@ -115,12 +183,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,8 +199,6 @@ gdk_macos_surface_hide (GdkSurface *surface) self->show_on_next_swap = FALSE; - _gdk_macos_display_remove_frame_callback (GDK_MACOS_DISPLAY (surface->display), self); - was_mapped = GDK_SURFACE_IS_MAPPED (GDK_SURFACE (self)); was_key = [self->window isKeyWindow]; @@ -163,8 +223,12 @@ gdk_macos_surface_hide (GdkSurface *surface) } } - if (was_mapped) - gdk_surface_freeze_updates (GDK_SURFACE (self)); + 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 @@ -208,6 +272,7 @@ gdk_macos_surface_begin_frame (GdkMacosSurface *self) { g_assert (GDK_IS_MACOS_SURFACE (self)); + self->in_frame = TRUE; } static void @@ -228,11 +293,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 +469,17 @@ 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); + } + if ((frame_clock = gdk_surface_get_frame_clock (GDK_SURFACE (self)))) { g_signal_handlers_disconnect_by_func (frame_clock, @@ -417,7 +491,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]; @@ -563,10 +636,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)); - else - frame_clock = _gdk_frame_clock_idle_new (); + frame_clock = _gdk_frame_clock_idle_new (); switch (surface_type) { @@ -624,14 +694,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 && @@ -819,41 +891,6 @@ _gdk_macos_surface_configure (GdkMacosSurface *self) _gdk_macos_surface_reposition_children (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) { @@ -864,22 +901,16 @@ _gdk_macos_surface_show (GdkMacosSurface *self) if (GDK_SURFACE_DESTROYED (self)) return; + _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) - 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; - 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_thaw_updates (GDK_SURFACE (self)); } } @@ -932,13 +963,17 @@ _gdk_macos_surface_move_resize (GdkMacosSurface *self, GdkDisplay *display; NSRect content_rect; NSRect frame_rect; + gboolean ignore_move; + gboolean ignore_size; 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))) + 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)); + + if (ignore_move && ignore_size) return; display = gdk_surface_get_display (surface); @@ -959,7 +994,14 @@ _gdk_macos_surface_move_resize (GdkMacosSurface *self, x, y + height, &x, &y); - content_rect = NSMakeRect (x, y, width, height); + content_rect = [self->window contentRectForFrameRect:[self->window frame]]; + + 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]; } @@ -1016,14 +1058,18 @@ 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)); + _gdk_macos_surface_cancel_frame (self); + rect.x = self->root_x; rect.y = self->root_y; rect.width = GDK_SURFACE (self)->width; @@ -1063,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)) { @@ -1093,13 +1120,31 @@ _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); + gdk_surface_invalidate_rect (GDK_SURFACE (self), NULL); + _gdk_macos_surface_request_frame (self); +} + +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 * diff --git a/gdk/macos/gdkmacostoplevelsurface.c b/gdk/macos/gdkmacostoplevelsurface.c index c84ebb704b..d89434593b 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); } 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',