/* * Copyright © 2020 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 #import "GdkMacosWindow.h" #include "gdkdisplayprivate.h" #include "gdkeventsprivate.h" #include "gdkdisplaylinksource.h" #include "gdkmacosclipboard-private.h" #include "gdkmacoscairocontext-private.h" #include "gdkmacoseventsource-private.h" #include "gdkmacosdisplay-private.h" #include "gdkmacosdrag-private.h" #include "gdkmacosdrop-private.h" #include "gdkmacosglcontext-private.h" #include "gdkmacoskeymap-private.h" #include "gdkmacosmonitor-private.h" #include "gdkmacosseat-private.h" #include "gdkmacossurface-private.h" #include "gdkmacosutils-private.h" G_DEFINE_TYPE (GdkMacosDisplay, gdk_macos_display, GDK_TYPE_DISPLAY) #define EVENT_MAP_MAX_SIZE 10 typedef struct { GList link; GdkEvent *gdk_event; NSEvent *nsevent; } GdkToNSEventMap; static GSource *event_source; static GQueue event_map = G_QUEUE_INIT; static GdkMacosMonitor * get_monitor (GdkMacosDisplay *self, guint position) { GdkMacosMonitor *monitor; g_assert (GDK_IS_MACOS_DISPLAY (self)); /* Get the monitor but return a borrowed reference */ monitor = g_list_model_get_item (G_LIST_MODEL (self->monitors), position); if (monitor != NULL) g_object_unref (monitor); return monitor; } static gboolean gdk_macos_display_get_setting (GdkDisplay *display, const char *setting, GValue *value) { return _gdk_macos_display_get_setting (GDK_MACOS_DISPLAY (display), setting, value); } static GListModel * gdk_macos_display_get_monitors (GdkDisplay *display) { return G_LIST_MODEL (GDK_MACOS_DISPLAY (display)->monitors); } static GdkMonitor * gdk_macos_display_get_monitor_at_surface (GdkDisplay *display, GdkSurface *surface) { GdkMacosDisplay *self = (GdkMacosDisplay *)display; CGDirectDisplayID screen_id; guint n_monitors; g_assert (GDK_IS_MACOS_DISPLAY (self)); g_assert (GDK_IS_MACOS_SURFACE (surface)); screen_id = _gdk_macos_surface_get_screen_id (GDK_MACOS_SURFACE (surface)); n_monitors = g_list_model_get_n_items (G_LIST_MODEL (self->monitors)); for (guint i = 0; i < n_monitors; i++) { GdkMacosMonitor *monitor = get_monitor (self, i); if (screen_id == _gdk_macos_monitor_get_screen_id (monitor)) return GDK_MONITOR (monitor); } return GDK_MONITOR (get_monitor (self, 0)); } static GdkMacosMonitor * gdk_macos_display_find_monitor (GdkMacosDisplay *self, CGDirectDisplayID screen_id) { guint n_monitors; g_assert (GDK_IS_MACOS_DISPLAY (self)); n_monitors = g_list_model_get_n_items (G_LIST_MODEL (self->monitors)); for (guint i = 0; i < n_monitors; i++) { GdkMacosMonitor *monitor = get_monitor (self, i); if (screen_id == _gdk_macos_monitor_get_screen_id (monitor)) return monitor; } return NULL; } static void gdk_macos_display_update_bounds (GdkMacosDisplay *self) { GDK_BEGIN_MACOS_ALLOC_POOL; g_assert (GDK_IS_MACOS_DISPLAY (self)); self->min_x = G_MAXINT; self->min_y = G_MAXINT; self->max_x = G_MININT; self->max_y = G_MININT; for (id obj in [NSScreen screens]) { NSRect geom = [(NSScreen *)obj frame]; self->min_x = MIN (self->min_x, geom.origin.x); self->min_y = MIN (self->min_y, geom.origin.y); self->max_x = MAX (self->max_x, geom.origin.x + geom.size.width); self->max_y = MAX (self->max_y, geom.origin.y + geom.size.height); } 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); } } 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) { GDK_BEGIN_MACOS_ALLOC_POOL; GArray *seen; guint n_monitors; g_assert (GDK_IS_MACOS_DISPLAY (self)); gdk_macos_display_update_bounds (self); seen = g_array_new (FALSE, FALSE, sizeof (CGDirectDisplayID)); for (id obj in [NSScreen screens]) { CGDirectDisplayID screen_id; GdkMacosMonitor *monitor; screen_id = [[[obj deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue]; g_array_append_val (seen, screen_id); if ((monitor = gdk_macos_display_find_monitor (self, screen_id))) { _gdk_macos_monitor_reconfigure (monitor); } else { monitor = _gdk_macos_monitor_new (self, screen_id); g_list_store_append (self->monitors, monitor); g_object_unref (monitor); } } n_monitors = g_list_model_get_n_items (G_LIST_MODEL (self->monitors)); for (guint i = n_monitors; i > 0; i--) { GdkMacosMonitor *monitor = get_monitor (self, i - 1); CGDirectDisplayID screen_id = _gdk_macos_monitor_get_screen_id (monitor); gboolean found = FALSE; for (guint j = 0; j < seen->len; j++) { if (screen_id == g_array_index (seen, CGDirectDisplayID, j)) { found = TRUE; break; } } if (!found) g_list_store_remove (self->monitors, i - 1); } g_array_unref (seen); GDK_END_MACOS_ALLOC_POOL; } static void gdk_macos_display_load_seat (GdkMacosDisplay *self) { GdkSeat *seat; g_assert (GDK_IS_MACOS_DISPLAY (self)); seat = _gdk_macos_seat_new (self); gdk_display_add_seat (GDK_DISPLAY (self), seat); 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) { return GDK_MACOS_DISPLAY (display)->name; } static void gdk_macos_display_beep (GdkDisplay *display) { NSBeep (); } static void gdk_macos_display_flush (GdkDisplay *display) { /* Not Supported */ } static void gdk_macos_display_sync (GdkDisplay *display) { /* Not Supported */ } static gulong gdk_macos_display_get_next_serial (GdkDisplay *display) { static gulong serial = 0; return ++serial; } static gboolean gdk_macos_display_has_pending (GdkDisplay *display) { return _gdk_event_queue_find_first (display) || _gdk_macos_event_source_check_pending (); } static void gdk_macos_display_notify_startup_complete (GdkDisplay *display, const char *startup_notification_id) { /* Not Supported */ } static void push_nsevent (GdkEvent *gdk_event, NSEvent *nsevent) { GdkToNSEventMap *map = g_slice_new0 (GdkToNSEventMap); map->link.data = map; map->gdk_event = gdk_event_ref (gdk_event); map->nsevent = g_steal_pointer (&nsevent); g_queue_push_tail_link (&event_map, &map->link); if (event_map.length > EVENT_MAP_MAX_SIZE) { map = g_queue_pop_head_link (&event_map)->data; gdk_event_unref (map->gdk_event); [map->nsevent release]; g_slice_free (GdkToNSEventMap, map); } } static void gdk_macos_display_queue_events (GdkDisplay *display) { GdkMacosDisplay *self = (GdkMacosDisplay *)display; NSEvent *nsevent; g_return_if_fail (GDK_IS_MACOS_DISPLAY (self)); if ((nsevent = _gdk_macos_event_source_get_pending ())) { GdkEvent *event = _gdk_macos_display_translate (self, nsevent); if (event != NULL) { push_nsevent (event, nsevent); _gdk_windowing_got_event (GDK_DISPLAY (self), _gdk_event_queue_append (GDK_DISPLAY (self), event), event, _gdk_display_get_next_serial (GDK_DISPLAY (self))); } else { [NSApp sendEvent:nsevent]; [nsevent release]; } } } static void _gdk_macos_display_surface_added (GdkMacosDisplay *self, GdkMacosSurface *surface) { g_assert (GDK_IS_MACOS_DISPLAY (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); if (GDK_IS_TOPLEVEL (surface)) g_queue_push_tail_link (&self->main_surfaces, &surface->main); _gdk_macos_display_clear_sorting (self); } void _gdk_macos_display_surface_removed (GdkMacosDisplay *self, GdkMacosSurface *surface) { g_return_if_fail (GDK_IS_MACOS_DISPLAY (self)); g_return_if_fail (GDK_IS_MACOS_SURFACE (surface)); if (self->keyboard_surface == surface) _gdk_macos_display_surface_resigned_key (self, surface); if (queue_contains (&self->sorted_surfaces, &surface->sorted)) g_queue_unlink (&self->sorted_surfaces, &surface->sorted); 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); } void _gdk_macos_display_surface_became_key (GdkMacosDisplay *self, GdkMacosSurface *surface) { GdkDevice *keyboard; GdkEvent *event; GdkSeat *seat; g_return_if_fail (GDK_IS_MACOS_DISPLAY (self)); g_return_if_fail (GDK_IS_MACOS_SURFACE (surface)); g_return_if_fail (self->keyboard_surface == NULL); self->keyboard_surface = surface; seat = gdk_display_get_default_seat (GDK_DISPLAY (self)); keyboard = gdk_seat_get_keyboard (seat); event = gdk_focus_event_new (GDK_SURFACE (surface), keyboard, TRUE); _gdk_event_queue_append (GDK_DISPLAY (self), event); /* For each parent surface, we want them to look like they * are also still focused, so ensure they have that same * state associated with them. */ if (GDK_IS_POPUP (surface)) { for (GdkSurface *parent = GDK_SURFACE (surface)->parent; parent != NULL; parent = parent->parent) { if (GDK_IS_TOPLEVEL (parent)) gdk_synthesize_surface_state (parent, 0, GDK_TOPLEVEL_STATE_FOCUSED); } } /* We just became the active window. Unlike X11, Mac OS X does * not send us motion events while the window does not have focus * ("is not key"). We send a dummy motion notify event now, so that * everything in the window is set to correct state. */ gdk_surface_request_motion (GDK_SURFACE (surface)); } void _gdk_macos_display_surface_resigned_key (GdkMacosDisplay *self, GdkMacosSurface *surface) { gboolean was_keyboard_surface; g_return_if_fail (GDK_IS_MACOS_DISPLAY (self)); g_return_if_fail (GDK_IS_MACOS_SURFACE (surface)); was_keyboard_surface = self->keyboard_surface == surface; self->keyboard_surface = NULL; if (was_keyboard_surface) { GdkDevice *keyboard; GdkEvent *event; GdkSeat *seat; GList *node; seat = gdk_display_get_default_seat (GDK_DISPLAY (self)); keyboard = gdk_seat_get_keyboard (seat); event = gdk_focus_event_new (GDK_SURFACE (surface), keyboard, FALSE); node = _gdk_event_queue_append (GDK_DISPLAY (self), event); _gdk_windowing_got_event (GDK_DISPLAY (self), node, event, _gdk_display_get_next_serial (GDK_DISPLAY (self))); } /* For each parent surface, we want them to look like they * are also still focused, so ensure they have that same * state associated with them. */ if (GDK_IS_POPUP (surface)) { for (GdkSurface *parent = GDK_SURFACE (surface)->parent; parent != NULL; parent = parent->parent) { if (GDK_IS_TOPLEVEL (parent)) gdk_synthesize_surface_state (parent, GDK_TOPLEVEL_STATE_FOCUSED, 0); } } _gdk_macos_display_clear_sorting (self); } /* Raises a transient window. */ static void raise_transient (GdkMacosSurface *surface) { GdkMacosSurface *parent_surface = GDK_MACOS_SURFACE (GDK_SURFACE (surface)->transient_for); NSWindow *parent = _gdk_macos_surface_get_native (parent_surface); NSWindow *window = _gdk_macos_surface_get_native (surface); [parent removeChildWindow:window]; [parent addChildWindow:window ordered:NSWindowAbove]; } void _gdk_macos_display_surface_became_main (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->main_surfaces, &surface->main)) g_queue_unlink (&self->main_surfaces, &surface->main); g_queue_push_head_link (&self->main_surfaces, &surface->main); if (GDK_SURFACE (surface)->transient_for) raise_transient (surface); _gdk_macos_display_clear_sorting (self); } 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)); if (queue_contains (&self->main_surfaces, &surface->main)) 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 * gdk_macos_display_create_surface (GdkDisplay *display, GdkSurfaceType surface_type, GdkSurface *parent, int x, int y, int width, int height) { GdkMacosDisplay *self = (GdkMacosDisplay *)display; GdkMacosSurface *surface; g_assert (GDK_IS_MACOS_DISPLAY (self)); g_assert (!parent || GDK_IS_MACOS_SURFACE (parent)); surface = _gdk_macos_surface_new (self, surface_type, parent, x, y, width, height); if (surface != NULL) _gdk_macos_display_surface_added (self, surface); return GDK_SURFACE (surface); } static GdkKeymap * gdk_macos_display_get_keymap (GdkDisplay *display) { GdkMacosDisplay *self = (GdkMacosDisplay *)display; g_assert (GDK_IS_MACOS_DISPLAY (self)); return GDK_KEYMAP (self->keymap); } static void gdk_macos_display_load_clipboard (GdkMacosDisplay *self) { g_assert (GDK_IS_MACOS_DISPLAY (self)); GDK_DISPLAY (self)->clipboard = _gdk_macos_clipboard_new (self); } static GdkGLContext * gdk_macos_display_init_gl (GdkDisplay *display, GError **error) { if (!gdk_gl_backend_can_be_used (GDK_GL_CGL, error)) return FALSE; return g_object_new (GDK_TYPE_MACOS_GL_CONTEXT, "display", display, NULL); } static void gdk_macos_display_finalize (GObject *object) { GdkMacosDisplay *self = (GdkMacosDisplay *)object; CFNotificationCenterRemoveObserver (CFNotificationCenterGetDistributedCenter (), self, CFSTR ("NSApplicationDidChangeScreenParametersNotification"), NULL); CFNotificationCenterRemoveObserver (CFNotificationCenterGetDistributedCenter (), self, CFSTR ("NSUserDefaultsDidChangeNotification"), NULL); 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); G_OBJECT_CLASS (gdk_macos_display_parent_class)->finalize (object); } static void gdk_macos_display_class_init (GdkMacosDisplayClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GdkDisplayClass *display_class = GDK_DISPLAY_CLASS (klass); object_class->finalize = gdk_macos_display_finalize; display_class->cairo_context_type = GDK_TYPE_MACOS_CAIRO_CONTEXT; display_class->beep = gdk_macos_display_beep; display_class->create_surface = gdk_macos_display_create_surface; display_class->flush = gdk_macos_display_flush; display_class->get_keymap = gdk_macos_display_get_keymap; display_class->get_monitors = gdk_macos_display_get_monitors; display_class->get_monitor_at_surface = gdk_macos_display_get_monitor_at_surface; display_class->get_next_serial = gdk_macos_display_get_next_serial; display_class->get_name = gdk_macos_display_get_name; display_class->get_setting = gdk_macos_display_get_setting; display_class->has_pending = gdk_macos_display_has_pending; display_class->init_gl = gdk_macos_display_init_gl; display_class->notify_startup_complete = gdk_macos_display_notify_startup_complete; display_class->queue_events = gdk_macos_display_queue_events; display_class->sync = gdk_macos_display_sync; } static void gdk_macos_display_init (GdkMacosDisplay *self) { self->monitors = g_list_store_new (GDK_TYPE_MONITOR); self->active_drags = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref); self->active_drops = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref); gdk_display_set_composited (GDK_DISPLAY (self), TRUE); gdk_display_set_input_shapes (GDK_DISPLAY (self), FALSE); gdk_display_set_rgba (GDK_DISPLAY (self), TRUE); } GdkDisplay * _gdk_macos_display_open (const char *display_name) { static GdkMacosDisplay *self; ProcessSerialNumber psn = { 0, kCurrentProcess }; /* Until we can have multiple GdkMacosEventSource instances * running concurrently, we can't exactly support multiple * display connections. So just short-circuit if we already * have one active. */ if (self != NULL) return NULL; display_name = display_name ? display_name : ""; GDK_NOTE (MISC, g_message ("opening display %s", display_name)); /* Make the current process a foreground application, i.e. an app * with a user interface, in case we're not running from a .app bundle */ TransformProcessType (&psn, kProcessTransformToForegroundApplication); [NSApplication sharedApplication]; self = g_object_new (GDK_TYPE_MACOS_DISPLAY, NULL); self->name = g_strdup (display_name); self->keymap = _gdk_macos_keymap_new (self); 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); if (event_source == NULL) { event_source = _gdk_macos_event_source_new (self); g_source_attach (event_source, NULL); } g_object_add_weak_pointer (G_OBJECT (self), (gpointer *)&self); gdk_display_emit_opened (GDK_DISPLAY (self)); return GDK_DISPLAY (self); } void _gdk_macos_display_to_display_coords (GdkMacosDisplay *self, int x, int y, int *out_x, int *out_y) { g_return_if_fail (GDK_IS_MACOS_DISPLAY (self)); if (out_y) *out_y = self->height - y + self->min_y; if (out_x) *out_x = x + self->min_x; } void _gdk_macos_display_from_display_coords (GdkMacosDisplay *self, int x, int y, int *out_x, int *out_y) { g_return_if_fail (GDK_IS_MACOS_DISPLAY (self)); if (out_y != NULL) *out_y = self->height - y + self->min_y; if (out_x != NULL) *out_x = x - self->min_x; } GdkMonitor * _gdk_macos_display_get_monitor_at_coords (GdkMacosDisplay *self, int x, int y) { GdkMacosMonitor *best_match = NULL; guint n_monitors; g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (self), NULL); n_monitors = g_list_model_get_n_items (G_LIST_MODEL (self->monitors)); for (guint i = 0; i < n_monitors; i++) { GdkMacosMonitor *monitor = get_monitor (self, i); const GdkRectangle *geom = &GDK_MONITOR (monitor)->geometry; if (x >= geom->x && y >= geom->y && x <= (geom->x + geom->width) && y <= (geom->y + geom->height)) { if (x <= geom->x + geom->width && y < geom->y + geom->height) return GDK_MONITOR (monitor); /* Not an exact match as we're on a boundary, but there is * a good chance another monitor doesn't exist there so we * would want to still treat this as the best monitor. */ best_match = monitor; } } return GDK_MONITOR (best_match); } GdkMonitor * _gdk_macos_display_get_monitor_at_display_coords (GdkMacosDisplay *self, int x, int y) { g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (self), NULL); _gdk_macos_display_from_display_coords (self, x, y, &x, &y); return _gdk_macos_display_get_monitor_at_coords (self, x, y); } NSScreen * _gdk_macos_display_get_screen_at_display_coords (GdkMacosDisplay *self, int x, int y) { GDK_BEGIN_MACOS_ALLOC_POOL; NSArray *screens; NSScreen *screen = NULL; g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (self), NULL); screens = [NSScreen screens]; for (id obj in screens) { NSRect geom = [obj frame]; if (x >= geom.origin.x && x <= geom.origin.x + geom.size.width && y >= geom.origin.y && y <= geom.origin.y + geom.size.height) { screen = obj; break; } } GDK_END_MACOS_ALLOC_POOL; return screen; } void _gdk_macos_display_break_all_grabs (GdkMacosDisplay *self, guint32 time) { GdkDevice *devices[2]; GdkSeat *seat; g_return_if_fail (GDK_IS_MACOS_DISPLAY (self)); seat = gdk_display_get_default_seat (GDK_DISPLAY (self)); devices[0] = gdk_seat_get_keyboard (seat); devices[1] = gdk_seat_get_pointer (seat); for (guint i = 0; i < G_N_ELEMENTS (devices); i++) { GdkDevice *device = devices[i]; GdkDeviceGrabInfo *grab; grab = _gdk_display_get_last_device_grab (GDK_DISPLAY (self), device); if (grab != NULL) { GdkEvent *event; GList *node; event = gdk_grab_broken_event_new (grab->surface, device, grab->surface, TRUE); node = _gdk_event_queue_append (GDK_DISPLAY (self), event); _gdk_windowing_got_event (GDK_DISPLAY (self), node, event, _gdk_display_get_next_serial (GDK_DISPLAY (self))); } } } void _gdk_macos_display_queue_events (GdkMacosDisplay *self) { g_return_if_fail (GDK_IS_MACOS_DISPLAY (self)); gdk_macos_display_queue_events (GDK_DISPLAY (self)); } static GdkMacosSurface * _gdk_macos_display_get_surface_at_coords (GdkMacosDisplay *self, int x, int y, int *surface_x, int *surface_y) { const GList *surfaces; g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (self), NULL); g_return_val_if_fail (surface_x != NULL, NULL); g_return_val_if_fail (surface_y != NULL, NULL); surfaces = _gdk_macos_display_get_surfaces (self); for (const GList *iter = surfaces; iter; iter = iter->next) { GdkSurface *surface = iter->data; NSWindow *nswindow; g_assert (GDK_IS_MACOS_SURFACE (surface)); if (!gdk_surface_get_mapped (surface)) continue; nswindow = _gdk_macos_surface_get_native (GDK_MACOS_SURFACE (surface)); if (x >= GDK_MACOS_SURFACE (surface)->root_x && y >= GDK_MACOS_SURFACE (surface)->root_y && x <= (GDK_MACOS_SURFACE (surface)->root_x + surface->width) && y <= (GDK_MACOS_SURFACE (surface)->root_y + surface->height)) { *surface_x = x - GDK_MACOS_SURFACE (surface)->root_x; *surface_y = y - GDK_MACOS_SURFACE (surface)->root_y; /* One last check to make sure that the x,y is within the input * region of the window. Otherwise we might send the event to the * wrong window because of window shadow. */ if (surface->input_region != NULL && !cairo_region_contains_point (surface->input_region, *surface_x, *surface_y)) continue; return GDK_MACOS_SURFACE (surface); } } *surface_x = 0; *surface_y = 0; return NULL; } GdkMacosSurface * _gdk_macos_display_get_surface_at_display_coords (GdkMacosDisplay *self, double x, double y, int *surface_x, int *surface_y) { int x_gdk; int y_gdk; g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (self), NULL); g_return_val_if_fail (surface_x != NULL, NULL); g_return_val_if_fail (surface_y != NULL, NULL); _gdk_macos_display_from_display_coords (self, x, y, &x_gdk, &y_gdk); 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, int *y) { GdkMacosSurface *surface; NSPoint point; g_assert (GDK_IS_MACOS_DISPLAY (self)); point = [NSEvent mouseLocation]; surface = _gdk_macos_display_get_surface_at_display_coords (self, point.x, point.y, x, y); if (surface != NULL) return _gdk_macos_surface_get_native (surface); 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) { g_return_if_fail (GDK_IS_MACOS_DISPLAY (self)); while (self->sorted_surfaces.head != NULL) g_queue_unlink (&self->sorted_surfaces, self->sorted_surfaces.head); } const GList * _gdk_macos_display_get_surfaces (GdkMacosDisplay *self) { g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (self), NULL); if (self->sorted_surfaces.length == 0) { GDK_BEGIN_MACOS_ALLOC_POOL; NSArray *array = [NSApp orderedWindows]; GQueue sorted = G_QUEUE_INIT; for (id obj in array) { NSWindow *nswindow = (NSWindow *)obj; GdkMacosSurface *surface; if (!GDK_IS_MACOS_WINDOW (nswindow)) continue; surface = [(GdkMacosWindow *)nswindow gdkSurface]; surface->sorted.prev = NULL; surface->sorted.next = NULL; g_queue_push_tail_link (&sorted, &surface->sorted); } self->sorted_surfaces = sorted; /* We don't get notification of clipboard changes from the system so we * instead update it every time the foreground changes (and thusly * rebuild the sorted list). Things could change other ways, such as * with scripts, but that is currently out of scope for us. */ _gdk_macos_clipboard_check_externally_modified ( GDK_MACOS_CLIPBOARD (GDK_DISPLAY (self)->clipboard)); GDK_END_MACOS_ALLOC_POOL; } return self->sorted_surfaces.head; } void _gdk_macos_display_warp_pointer (GdkMacosDisplay *self, int x, int y) { g_return_if_fail (GDK_IS_MACOS_DISPLAY (self)); _gdk_macos_display_to_display_coords (self, x, y, &x, &y); CGWarpMouseCursorPosition ((CGPoint) { x, y }); } NSEvent * _gdk_macos_display_get_nsevent (GdkEvent *event) { for (const GList *iter = event_map.head; iter; iter = iter->next) { const GdkToNSEventMap *map = iter->data; if (map->gdk_event == event) return map->nsevent; } return NULL; } GdkDrag * _gdk_macos_display_find_drag (GdkMacosDisplay *self, NSInteger sequence_number) { g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (self), NULL); return g_hash_table_lookup (self->active_drags, GSIZE_TO_POINTER (sequence_number)); } void _gdk_macos_display_set_drag (GdkMacosDisplay *self, NSInteger sequence_number, GdkDrag *drag) { g_return_if_fail (GDK_IS_MACOS_DISPLAY (self)); g_return_if_fail (!drag || GDK_IS_MACOS_DRAG (drag)); if (drag) g_hash_table_insert (self->active_drags, GSIZE_TO_POINTER (sequence_number), g_object_ref (drag)); else g_hash_table_remove (self->active_drags, GSIZE_TO_POINTER (sequence_number)); } GdkDrop * _gdk_macos_display_find_drop (GdkMacosDisplay *self, NSInteger sequence_number) { g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (self), NULL); return g_hash_table_lookup (self->active_drops, GSIZE_TO_POINTER (sequence_number)); } void _gdk_macos_display_set_drop (GdkMacosDisplay *self, NSInteger sequence_number, GdkDrop *drop) { g_return_if_fail (GDK_IS_MACOS_DISPLAY (self)); g_return_if_fail (!drop || GDK_IS_MACOS_DROP (drop)); if (drop) g_hash_table_insert (self->active_drops, GSIZE_TO_POINTER (sequence_number), g_object_ref (drop)); else g_hash_table_remove (self->active_drops, GSIZE_TO_POINTER (sequence_number)); }