forked from AuroraMiddleware/gtk
59806eee38
Ported from gtk-3-24 e278f38905
Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/2436
1150 lines
33 KiB
C
1150 lines
33 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <AppKit/AppKit.h>
|
|
|
|
#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 "gdkmacosglcontext-private.h"
|
|
#include "gdkmacoskeymap-private.h"
|
|
#include "gdkmacosmonitor-private.h"
|
|
#include "gdkmacosseat-private.h"
|
|
#include "gdkmacossurface-private.h"
|
|
#include "gdkmacosutils-private.h"
|
|
|
|
/**
|
|
* SECTION:macos_interaction
|
|
* @Short_description: macOS backend-specific functions
|
|
* @Title: macOS Interaction
|
|
* @Include: gdk/macos/gdkmacos.h
|
|
*
|
|
* The functions in this section are specific to the GDK macOS backend.
|
|
* To use them, you need to include the `<gdk/macos/gdkmacos.h>` header and
|
|
* use the macOS-specific pkg-config `gtk4-macos` file to build your
|
|
* application.
|
|
*
|
|
* To make your code compile with other GDK backends, guard backend-specific
|
|
* calls by an ifdef as follows. Since GDK may be built with multiple
|
|
* backends, you should also check for the backend that is in use (e.g. by
|
|
* using the GDK_IS_MACOS_DISPLAY() macro).
|
|
* |[<!-- language="C" -->
|
|
* #ifdef GDK_WINDOWING_MACOS
|
|
* if (GDK_IS_MACOS_DISPLAY (display))
|
|
* {
|
|
* // make macOS-specific calls here
|
|
* }
|
|
* else
|
|
* #endif
|
|
* #ifdef GDK_WINDOWING_X11
|
|
* if (GDK_IS_X11_DISPLAY (display))
|
|
* {
|
|
* // make X11-specific calls here
|
|
* }
|
|
* else
|
|
* #endif
|
|
* g_error ("Unsupported GDK backend");
|
|
* ]|
|
|
*/
|
|
|
|
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. We ignore the popup surfaces
|
|
* since we can rely on the toplevel surfaces to handle that.
|
|
*/
|
|
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));
|
|
|
|
if (GDK_IS_TOPLEVEL (surface))
|
|
_gdk_macos_surface_update_position (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_display_remove_frame_callback (self, surface);
|
|
_gdk_macos_surface_thaw (surface,
|
|
source->presentation_time,
|
|
source->refresh_interval);
|
|
}
|
|
|
|
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);
|
|
|
|
/* 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)));
|
|
}
|
|
|
|
_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 gboolean
|
|
gdk_macos_display_make_gl_context_current (GdkDisplay *display,
|
|
GdkGLContext *gl_context)
|
|
{
|
|
g_assert (GDK_IS_MACOS_DISPLAY (display));
|
|
g_assert (!gl_context || GDK_IS_MACOS_GL_CONTEXT (gl_context));
|
|
|
|
if (gl_context == NULL)
|
|
return FALSE;
|
|
|
|
return _gdk_macos_gl_context_make_current (GDK_MACOS_GL_CONTEXT (gl_context));
|
|
}
|
|
|
|
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_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->make_gl_context_current = gdk_macos_display_make_gl_context_current;
|
|
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);
|
|
|
|
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;
|
|
|
|
GDK_NOTE (MISC, g_message ("opening display %s", display_name ? 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)
|
|
{
|
|
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);
|
|
|
|
if (gdk_rectangle_contains_point (&GDK_MONITOR (monitor)->geometry, x, y))
|
|
return GDK_MONITOR (monitor);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
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;
|
|
|
|
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))
|
|
{
|
|
g_queue_push_tail_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;
|
|
}
|