1 /*
2 * Copyright © 2020 Red Hat, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * SPDX-License-Identifier: LGPL-2.1-or-later
18 */
19
20 #include "config.h"
21
22 #include <AppKit/AppKit.h>
23
24 #import "GdkMacosWindow.h"
25
26 #include "gdkdisplayprivate.h"
27 #include "gdkeventsprivate.h"
28
29 #include "gdkdisplaylinksource.h"
30 #include "gdkmacosclipboard-private.h"
31 #include "gdkmacoscairocontext-private.h"
32 #include "gdkmacoseventsource-private.h"
33 #include "gdkmacosdisplay-private.h"
34 #include "gdkmacosdrag-private.h"
35 #include "gdkmacosdrop-private.h"
36 #include "gdkmacosglcontext-private.h"
37 #include "gdkmacoskeymap-private.h"
38 #include "gdkmacosmonitor-private.h"
39 #include "gdkmacosseat-private.h"
40 #include "gdkmacossurface-private.h"
41 #include "gdkmacosutils-private.h"
42
43 G_DEFINE_TYPE (GdkMacosDisplay, gdk_macos_display, GDK_TYPE_DISPLAY)
44
45 #define EVENT_MAP_MAX_SIZE 10
46
47 typedef struct
48 {
49 GList link;
50 GdkEvent *gdk_event;
51 NSEvent *nsevent;
52 } GdkToNSEventMap;
53
54 static GSource *event_source;
55 static GQueue event_map = G_QUEUE_INIT;
56
57 static GdkMacosMonitor *
get_monitor(GdkMacosDisplay * self,guint position)58 get_monitor (GdkMacosDisplay *self,
59 guint position)
60 {
61 GdkMacosMonitor *monitor;
62
63 g_assert (GDK_IS_MACOS_DISPLAY (self));
64
65 /* Get the monitor but return a borrowed reference */
66 monitor = g_list_model_get_item (G_LIST_MODEL (self->monitors), position);
67 if (monitor != NULL)
68 g_object_unref (monitor);
69
70 return monitor;
71 }
72
73 static gboolean
gdk_macos_display_get_setting(GdkDisplay * display,const char * setting,GValue * value)74 gdk_macos_display_get_setting (GdkDisplay *display,
75 const char *setting,
76 GValue *value)
77 {
78 return _gdk_macos_display_get_setting (GDK_MACOS_DISPLAY (display), setting, value);
79 }
80
81 static GListModel *
gdk_macos_display_get_monitors(GdkDisplay * display)82 gdk_macos_display_get_monitors (GdkDisplay *display)
83 {
84 return G_LIST_MODEL (GDK_MACOS_DISPLAY (display)->monitors);
85 }
86
87 static GdkMonitor *
gdk_macos_display_get_monitor_at_surface(GdkDisplay * display,GdkSurface * surface)88 gdk_macos_display_get_monitor_at_surface (GdkDisplay *display,
89 GdkSurface *surface)
90 {
91 GdkMacosDisplay *self = (GdkMacosDisplay *)display;
92 CGDirectDisplayID screen_id;
93 guint n_monitors;
94
95 g_assert (GDK_IS_MACOS_DISPLAY (self));
96 g_assert (GDK_IS_MACOS_SURFACE (surface));
97
98 screen_id = _gdk_macos_surface_get_screen_id (GDK_MACOS_SURFACE (surface));
99 n_monitors = g_list_model_get_n_items (G_LIST_MODEL (self->monitors));
100
101 for (guint i = 0; i < n_monitors; i++)
102 {
103 GdkMacosMonitor *monitor = get_monitor (self, i);
104
105 if (screen_id == _gdk_macos_monitor_get_screen_id (monitor))
106 return GDK_MONITOR (monitor);
107 }
108
109 return GDK_MONITOR (get_monitor (self, 0));
110 }
111
112 static GdkMacosMonitor *
gdk_macos_display_find_monitor(GdkMacosDisplay * self,CGDirectDisplayID screen_id)113 gdk_macos_display_find_monitor (GdkMacosDisplay *self,
114 CGDirectDisplayID screen_id)
115 {
116 guint n_monitors;
117
118 g_assert (GDK_IS_MACOS_DISPLAY (self));
119
120 n_monitors = g_list_model_get_n_items (G_LIST_MODEL (self->monitors));
121
122 for (guint i = 0; i < n_monitors; i++)
123 {
124 GdkMacosMonitor *monitor = get_monitor (self, i);
125
126 if (screen_id == _gdk_macos_monitor_get_screen_id (monitor))
127 return monitor;
128 }
129
130 return NULL;
131 }
132
133 static void
gdk_macos_display_update_bounds(GdkMacosDisplay * self)134 gdk_macos_display_update_bounds (GdkMacosDisplay *self)
135 {
136 GDK_BEGIN_MACOS_ALLOC_POOL;
137
138 g_assert (GDK_IS_MACOS_DISPLAY (self));
139
140 self->min_x = G_MAXINT;
141 self->min_y = G_MAXINT;
142
143 self->max_x = G_MININT;
144 self->max_y = G_MININT;
145
146 for (id obj in [NSScreen screens])
147 {
148 NSRect geom = [(NSScreen *)obj frame];
149
150 self->min_x = MIN (self->min_x, geom.origin.x);
151 self->min_y = MIN (self->min_y, geom.origin.y);
152 self->max_x = MAX (self->max_x, geom.origin.x + geom.size.width);
153 self->max_y = MAX (self->max_y, geom.origin.y + geom.size.height);
154 }
155
156 self->width = self->max_x - self->min_x;
157 self->height = self->max_y - self->min_y;
158
159 GDK_END_MACOS_ALLOC_POOL;
160 }
161
162 static void
gdk_macos_display_monitors_changed_cb(CFNotificationCenterRef center,void * observer,CFStringRef name,const void * object,CFDictionaryRef userInfo)163 gdk_macos_display_monitors_changed_cb (CFNotificationCenterRef center,
164 void *observer,
165 CFStringRef name,
166 const void *object,
167 CFDictionaryRef userInfo)
168 {
169 GdkMacosDisplay *self = observer;
170
171 g_assert (GDK_IS_MACOS_DISPLAY (self));
172
173 _gdk_macos_display_reload_monitors (self);
174
175 /* Now we need to update all our surface positions since they
176 * probably just changed origins. We ignore the popup surfaces
177 * since we can rely on the toplevel surfaces to handle that.
178 */
179 for (const GList *iter = _gdk_macos_display_get_surfaces (self);
180 iter != NULL;
181 iter = iter->next)
182 {
183 GdkMacosSurface *surface = iter->data;
184
185 g_assert (GDK_IS_MACOS_SURFACE (surface));
186
187 if (GDK_IS_TOPLEVEL (surface))
188 _gdk_macos_surface_update_position (surface);
189 }
190 }
191
192 static void
gdk_macos_display_user_defaults_changed_cb(CFNotificationCenterRef center,void * observer,CFStringRef name,const void * object,CFDictionaryRef userInfo)193 gdk_macos_display_user_defaults_changed_cb (CFNotificationCenterRef center,
194 void *observer,
195 CFStringRef name,
196 const void *object,
197 CFDictionaryRef userInfo)
198 {
199 GdkMacosDisplay *self = observer;
200
201 g_assert (GDK_IS_MACOS_DISPLAY (self));
202
203 _gdk_macos_display_reload_settings (self);
204 }
205
206 void
_gdk_macos_display_reload_monitors(GdkMacosDisplay * self)207 _gdk_macos_display_reload_monitors (GdkMacosDisplay *self)
208 {
209 GDK_BEGIN_MACOS_ALLOC_POOL;
210
211 GArray *seen;
212 guint n_monitors;
213
214 g_assert (GDK_IS_MACOS_DISPLAY (self));
215
216 gdk_macos_display_update_bounds (self);
217
218 seen = g_array_new (FALSE, FALSE, sizeof (CGDirectDisplayID));
219
220 for (id obj in [NSScreen screens])
221 {
222 CGDirectDisplayID screen_id;
223 GdkMacosMonitor *monitor;
224
225 screen_id = [[[obj deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue];
226 g_array_append_val (seen, screen_id);
227
228 if ((monitor = gdk_macos_display_find_monitor (self, screen_id)))
229 {
230 _gdk_macos_monitor_reconfigure (monitor);
231 }
232 else
233 {
234 monitor = _gdk_macos_monitor_new (self, screen_id);
235 g_list_store_append (self->monitors, monitor);
236 g_object_unref (monitor);
237 }
238 }
239
240 n_monitors = g_list_model_get_n_items (G_LIST_MODEL (self->monitors));
241
242 for (guint i = n_monitors; i > 0; i--)
243 {
244 GdkMacosMonitor *monitor = get_monitor (self, i - 1);
245 CGDirectDisplayID screen_id = _gdk_macos_monitor_get_screen_id (monitor);
246 gboolean found = FALSE;
247
248 for (guint j = 0; j < seen->len; j++)
249 {
250 if (screen_id == g_array_index (seen, CGDirectDisplayID, j))
251 {
252 found = TRUE;
253 break;
254 }
255 }
256
257 if (!found)
258 g_list_store_remove (self->monitors, i - 1);
259 }
260
261 g_array_unref (seen);
262
263 GDK_END_MACOS_ALLOC_POOL;
264 }
265
266 static void
gdk_macos_display_load_seat(GdkMacosDisplay * self)267 gdk_macos_display_load_seat (GdkMacosDisplay *self)
268 {
269 GdkSeat *seat;
270
271 g_assert (GDK_IS_MACOS_DISPLAY (self));
272
273 seat = _gdk_macos_seat_new (self);
274 gdk_display_add_seat (GDK_DISPLAY (self), seat);
275 g_object_unref (seat);
276 }
277
278 static gboolean
gdk_macos_display_frame_cb(gpointer data)279 gdk_macos_display_frame_cb (gpointer data)
280 {
281 GdkMacosDisplay *self = data;
282 GdkDisplayLinkSource *source;
283 gint64 presentation_time;
284 gint64 now;
285 GList *iter;
286
287 g_assert (GDK_IS_MACOS_DISPLAY (self));
288
289 source = (GdkDisplayLinkSource *)self->frame_source;
290
291 presentation_time = source->presentation_time;
292 now = g_source_get_time ((GSource *)source);
293
294 iter = self->awaiting_frames.head;
295
296 while (iter != NULL)
297 {
298 GdkMacosSurface *surface = iter->data;
299
300 g_assert (GDK_IS_MACOS_SURFACE (surface));
301
302 iter = iter->next;
303
304 _gdk_macos_display_remove_frame_callback (self, surface);
305 _gdk_macos_surface_thaw (surface,
306 source->presentation_time,
307 source->refresh_interval);
308 }
309
310 return G_SOURCE_CONTINUE;
311 }
312
313 static void
gdk_macos_display_load_display_link(GdkMacosDisplay * self)314 gdk_macos_display_load_display_link (GdkMacosDisplay *self)
315 {
316 self->frame_source = gdk_display_link_source_new ();
317 g_source_set_callback (self->frame_source,
318 gdk_macos_display_frame_cb,
319 self,
320 NULL);
321 g_source_attach (self->frame_source, NULL);
322 }
323
324 static const char *
gdk_macos_display_get_name(GdkDisplay * display)325 gdk_macos_display_get_name (GdkDisplay *display)
326 {
327 return GDK_MACOS_DISPLAY (display)->name;
328 }
329
330 static void
gdk_macos_display_beep(GdkDisplay * display)331 gdk_macos_display_beep (GdkDisplay *display)
332 {
333 NSBeep ();
334 }
335
336 static void
gdk_macos_display_flush(GdkDisplay * display)337 gdk_macos_display_flush (GdkDisplay *display)
338 {
339 /* Not Supported */
340 }
341
342 static void
gdk_macos_display_sync(GdkDisplay * display)343 gdk_macos_display_sync (GdkDisplay *display)
344 {
345 /* Not Supported */
346 }
347
348 static gulong
gdk_macos_display_get_next_serial(GdkDisplay * display)349 gdk_macos_display_get_next_serial (GdkDisplay *display)
350 {
351 static gulong serial = 0;
352 return ++serial;
353 }
354
355 static gboolean
gdk_macos_display_has_pending(GdkDisplay * display)356 gdk_macos_display_has_pending (GdkDisplay *display)
357 {
358 return _gdk_event_queue_find_first (display) ||
359 _gdk_macos_event_source_check_pending ();
360 }
361
362 static void
gdk_macos_display_notify_startup_complete(GdkDisplay * display,const char * startup_notification_id)363 gdk_macos_display_notify_startup_complete (GdkDisplay *display,
364 const char *startup_notification_id)
365 {
366 /* Not Supported */
367 }
368
369 static void
push_nsevent(GdkEvent * gdk_event,NSEvent * nsevent)370 push_nsevent (GdkEvent *gdk_event,
371 NSEvent *nsevent)
372 {
373 GdkToNSEventMap *map = g_slice_new0 (GdkToNSEventMap);
374
375 map->link.data = map;
376 map->gdk_event = gdk_event_ref (gdk_event);
377 map->nsevent = g_steal_pointer (&nsevent);
378
379 g_queue_push_tail_link (&event_map, &map->link);
380
381 if (event_map.length > EVENT_MAP_MAX_SIZE)
382 {
383 map = g_queue_pop_head_link (&event_map)->data;
384
385 gdk_event_unref (map->gdk_event);
386 [map->nsevent release];
387 g_slice_free (GdkToNSEventMap, map);
388 }
389 }
390
391 static void
gdk_macos_display_queue_events(GdkDisplay * display)392 gdk_macos_display_queue_events (GdkDisplay *display)
393 {
394 GdkMacosDisplay *self = (GdkMacosDisplay *)display;
395 NSEvent *nsevent;
396
397 g_return_if_fail (GDK_IS_MACOS_DISPLAY (self));
398
399 if ((nsevent = _gdk_macos_event_source_get_pending ()))
400 {
401 GdkEvent *event = _gdk_macos_display_translate (self, nsevent);
402
403 if (event != NULL)
404 {
405 push_nsevent (event, nsevent);
406 _gdk_windowing_got_event (GDK_DISPLAY (self),
407 _gdk_event_queue_append (GDK_DISPLAY (self), event),
408 event,
409 _gdk_display_get_next_serial (GDK_DISPLAY (self)));
410 }
411 else
412 {
413 [NSApp sendEvent:nsevent];
414 [nsevent release];
415 }
416 }
417 }
418
419 static void
_gdk_macos_display_surface_added(GdkMacosDisplay * self,GdkMacosSurface * surface)420 _gdk_macos_display_surface_added (GdkMacosDisplay *self,
421 GdkMacosSurface *surface)
422 {
423 g_assert (GDK_IS_MACOS_DISPLAY (self));
424 g_assert (GDK_IS_MACOS_SURFACE (surface));
425 g_assert (!queue_contains (&self->sorted_surfaces, &surface->sorted));
426 g_assert (!queue_contains (&self->main_surfaces, &surface->main));
427 g_assert (!queue_contains (&self->awaiting_frames, &surface->frame));
428 g_assert (surface->sorted.data == surface);
429 g_assert (surface->main.data == surface);
430 g_assert (surface->frame.data == surface);
431
432 if (GDK_IS_TOPLEVEL (surface))
433 g_queue_push_tail_link (&self->main_surfaces, &surface->main);
434
435 _gdk_macos_display_clear_sorting (self);
436 }
437
438 void
_gdk_macos_display_surface_removed(GdkMacosDisplay * self,GdkMacosSurface * surface)439 _gdk_macos_display_surface_removed (GdkMacosDisplay *self,
440 GdkMacosSurface *surface)
441 {
442 g_return_if_fail (GDK_IS_MACOS_DISPLAY (self));
443 g_return_if_fail (GDK_IS_MACOS_SURFACE (surface));
444
445 if (self->keyboard_surface == surface)
446 _gdk_macos_display_surface_resigned_key (self, surface);
447
448 if (queue_contains (&self->sorted_surfaces, &surface->sorted))
449 g_queue_unlink (&self->sorted_surfaces, &surface->sorted);
450
451 if (queue_contains (&self->main_surfaces, &surface->main))
452 _gdk_macos_display_surface_resigned_main (self, surface);
453
454 if (queue_contains (&self->awaiting_frames, &surface->frame))
455 g_queue_unlink (&self->awaiting_frames, &surface->frame);
456
457 g_return_if_fail (self->keyboard_surface != surface);
458 }
459
460 void
_gdk_macos_display_surface_became_key(GdkMacosDisplay * self,GdkMacosSurface * surface)461 _gdk_macos_display_surface_became_key (GdkMacosDisplay *self,
462 GdkMacosSurface *surface)
463 {
464 GdkDevice *keyboard;
465 GdkEvent *event;
466 GdkSeat *seat;
467
468 g_return_if_fail (GDK_IS_MACOS_DISPLAY (self));
469 g_return_if_fail (GDK_IS_MACOS_SURFACE (surface));
470 g_return_if_fail (self->keyboard_surface == NULL);
471
472 self->keyboard_surface = surface;
473
474 seat = gdk_display_get_default_seat (GDK_DISPLAY (self));
475 keyboard = gdk_seat_get_keyboard (seat);
476 event = gdk_focus_event_new (GDK_SURFACE (surface), keyboard, TRUE);
477 _gdk_event_queue_append (GDK_DISPLAY (self), event);
478
479 /* We just became the active window. Unlike X11, Mac OS X does
480 * not send us motion events while the window does not have focus
481 * ("is not key"). We send a dummy motion notify event now, so that
482 * everything in the window is set to correct state.
483 */
484 gdk_surface_request_motion (GDK_SURFACE (surface));
485 }
486
487 void
_gdk_macos_display_surface_resigned_key(GdkMacosDisplay * self,GdkMacosSurface * surface)488 _gdk_macos_display_surface_resigned_key (GdkMacosDisplay *self,
489 GdkMacosSurface *surface)
490 {
491 gboolean was_keyboard_surface;
492
493 g_return_if_fail (GDK_IS_MACOS_DISPLAY (self));
494 g_return_if_fail (GDK_IS_MACOS_SURFACE (surface));
495
496 was_keyboard_surface = self->keyboard_surface == surface;
497
498 self->keyboard_surface = NULL;
499
500 if (was_keyboard_surface)
501 {
502 GdkDevice *keyboard;
503 GdkEvent *event;
504 GdkSeat *seat;
505 GList *node;
506
507 seat = gdk_display_get_default_seat (GDK_DISPLAY (self));
508 keyboard = gdk_seat_get_keyboard (seat);
509 event = gdk_focus_event_new (GDK_SURFACE (surface), keyboard, FALSE);
510 node = _gdk_event_queue_append (GDK_DISPLAY (self), event);
511 _gdk_windowing_got_event (GDK_DISPLAY (self), node, event,
512 _gdk_display_get_next_serial (GDK_DISPLAY (self)));
513 }
514
515 _gdk_macos_display_clear_sorting (self);
516 }
517
518 /* Raises a transient window.
519 */
520 static void
raise_transient(GdkMacosSurface * surface)521 raise_transient (GdkMacosSurface *surface)
522 {
523 GdkMacosSurface *parent_surface = GDK_MACOS_SURFACE (GDK_SURFACE (surface)->transient_for);
524
525 NSWindow *parent = _gdk_macos_surface_get_native (parent_surface);
526 NSWindow *window = _gdk_macos_surface_get_native (surface);
527
528 [parent removeChildWindow:window];
529 [parent addChildWindow:window ordered:NSWindowAbove];
530 }
531
532 void
_gdk_macos_display_surface_became_main(GdkMacosDisplay * self,GdkMacosSurface * surface)533 _gdk_macos_display_surface_became_main (GdkMacosDisplay *self,
534 GdkMacosSurface *surface)
535 {
536 g_return_if_fail (GDK_IS_MACOS_DISPLAY (self));
537 g_return_if_fail (GDK_IS_MACOS_SURFACE (surface));
538
539 if (queue_contains (&self->main_surfaces, &surface->main))
540 g_queue_unlink (&self->main_surfaces, &surface->main);
541
542 g_queue_push_head_link (&self->main_surfaces, &surface->main);
543
544 if (GDK_SURFACE (surface)->transient_for)
545 raise_transient (surface);
546
547 _gdk_macos_display_clear_sorting (self);
548 }
549
550 void
_gdk_macos_display_surface_resigned_main(GdkMacosDisplay * self,GdkMacosSurface * surface)551 _gdk_macos_display_surface_resigned_main (GdkMacosDisplay *self,
552 GdkMacosSurface *surface)
553 {
554 GdkMacosSurface *new_surface = NULL;
555
556 g_return_if_fail (GDK_IS_MACOS_DISPLAY (self));
557 g_return_if_fail (GDK_IS_MACOS_SURFACE (surface));
558
559 if (queue_contains (&self->main_surfaces, &surface->main))
560 g_queue_unlink (&self->main_surfaces, &surface->main);
561
562 _gdk_macos_display_clear_sorting (self);
563
564 if (GDK_SURFACE (surface)->transient_for &&
565 gdk_surface_get_mapped (GDK_SURFACE (surface)->transient_for))
566 {
567 new_surface = GDK_MACOS_SURFACE (GDK_SURFACE (surface)->transient_for);
568 }
569 else
570 {
571 const GList *surfaces = _gdk_macos_display_get_surfaces (self);
572
573 for (const GList *iter = surfaces; iter; iter = iter->next)
574 {
575 GdkMacosSurface *item = iter->data;
576
577 g_assert (GDK_IS_MACOS_SURFACE (item));
578
579 if (item == surface)
580 continue;
581
582 if (GDK_SURFACE_IS_MAPPED (GDK_SURFACE (item)))
583 {
584 new_surface = item;
585 break;
586 }
587 }
588 }
589
590 if (new_surface != NULL)
591 {
592 NSWindow *nswindow = _gdk_macos_surface_get_native (new_surface);
593 [nswindow makeKeyAndOrderFront:nswindow];
594 }
595
596 _gdk_macos_display_clear_sorting (self);
597 }
598
599 static GdkSurface *
gdk_macos_display_create_surface(GdkDisplay * display,GdkSurfaceType surface_type,GdkSurface * parent,int x,int y,int width,int height)600 gdk_macos_display_create_surface (GdkDisplay *display,
601 GdkSurfaceType surface_type,
602 GdkSurface *parent,
603 int x,
604 int y,
605 int width,
606 int height)
607 {
608 GdkMacosDisplay *self = (GdkMacosDisplay *)display;
609 GdkMacosSurface *surface;
610
611 g_assert (GDK_IS_MACOS_DISPLAY (self));
612 g_assert (!parent || GDK_IS_MACOS_SURFACE (parent));
613
614 surface = _gdk_macos_surface_new (self, surface_type, parent, x, y, width, height);
615
616 if (surface != NULL)
617 _gdk_macos_display_surface_added (self, surface);
618
619 return GDK_SURFACE (surface);
620 }
621
622 static GdkKeymap *
gdk_macos_display_get_keymap(GdkDisplay * display)623 gdk_macos_display_get_keymap (GdkDisplay *display)
624 {
625 GdkMacosDisplay *self = (GdkMacosDisplay *)display;
626
627 g_assert (GDK_IS_MACOS_DISPLAY (self));
628
629 return GDK_KEYMAP (self->keymap);
630 }
631
632 static void
gdk_macos_display_load_clipboard(GdkMacosDisplay * self)633 gdk_macos_display_load_clipboard (GdkMacosDisplay *self)
634 {
635 g_assert (GDK_IS_MACOS_DISPLAY (self));
636
637 GDK_DISPLAY (self)->clipboard = _gdk_macos_clipboard_new (self);
638 }
639
640 static GdkGLContext *
gdk_macos_display_init_gl(GdkDisplay * display,GError ** error)641 gdk_macos_display_init_gl (GdkDisplay *display,
642 GError **error)
643 {
644 if (!gdk_gl_backend_can_be_used (GDK_GL_CGL, error))
645 return FALSE;
646
647 return g_object_new (GDK_TYPE_MACOS_GL_CONTEXT,
648 "display", display,
649 NULL);
650 }
651
652 static void
gdk_macos_display_finalize(GObject * object)653 gdk_macos_display_finalize (GObject *object)
654 {
655 GdkMacosDisplay *self = (GdkMacosDisplay *)object;
656
657 CFNotificationCenterRemoveObserver (CFNotificationCenterGetDistributedCenter (),
658 self,
659 CFSTR ("NSApplicationDidChangeScreenParametersNotification"),
660 NULL);
661
662 CFNotificationCenterRemoveObserver (CFNotificationCenterGetDistributedCenter (),
663 self,
664 CFSTR ("NSUserDefaultsDidChangeNotification"),
665 NULL);
666
667 g_clear_pointer (&self->active_drags, g_hash_table_unref);
668 g_clear_pointer (&self->active_drops, g_hash_table_unref);
669 g_clear_object (&GDK_DISPLAY (self)->clipboard);
670 g_clear_pointer (&self->frame_source, g_source_unref);
671 g_clear_object (&self->monitors);
672 g_clear_pointer (&self->name, g_free);
673
674 G_OBJECT_CLASS (gdk_macos_display_parent_class)->finalize (object);
675 }
676
677 static void
gdk_macos_display_class_init(GdkMacosDisplayClass * klass)678 gdk_macos_display_class_init (GdkMacosDisplayClass *klass)
679 {
680 GObjectClass *object_class = G_OBJECT_CLASS (klass);
681 GdkDisplayClass *display_class = GDK_DISPLAY_CLASS (klass);
682
683 object_class->finalize = gdk_macos_display_finalize;
684
685 display_class->cairo_context_type = GDK_TYPE_MACOS_CAIRO_CONTEXT;
686
687 display_class->beep = gdk_macos_display_beep;
688 display_class->create_surface = gdk_macos_display_create_surface;
689 display_class->flush = gdk_macos_display_flush;
690 display_class->get_keymap = gdk_macos_display_get_keymap;
691 display_class->get_monitors = gdk_macos_display_get_monitors;
692 display_class->get_monitor_at_surface = gdk_macos_display_get_monitor_at_surface;
693 display_class->get_next_serial = gdk_macos_display_get_next_serial;
694 display_class->get_name = gdk_macos_display_get_name;
695 display_class->get_setting = gdk_macos_display_get_setting;
696 display_class->has_pending = gdk_macos_display_has_pending;
697 display_class->init_gl = gdk_macos_display_init_gl;
698 display_class->notify_startup_complete = gdk_macos_display_notify_startup_complete;
699 display_class->queue_events = gdk_macos_display_queue_events;
700 display_class->sync = gdk_macos_display_sync;
701 }
702
703 static void
gdk_macos_display_init(GdkMacosDisplay * self)704 gdk_macos_display_init (GdkMacosDisplay *self)
705 {
706 self->monitors = g_list_store_new (GDK_TYPE_MONITOR);
707 self->active_drags = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
708 self->active_drops = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
709
710 gdk_display_set_composited (GDK_DISPLAY (self), TRUE);
711 gdk_display_set_input_shapes (GDK_DISPLAY (self), FALSE);
712 gdk_display_set_rgba (GDK_DISPLAY (self), TRUE);
713 }
714
715 GdkDisplay *
_gdk_macos_display_open(const char * display_name)716 _gdk_macos_display_open (const char *display_name)
717 {
718 static GdkMacosDisplay *self;
719 ProcessSerialNumber psn = { 0, kCurrentProcess };
720
721 /* Until we can have multiple GdkMacosEventSource instances
722 * running concurrently, we can't exactly support multiple
723 * display connections. So just short-circuit if we already
724 * have one active.
725 */
726 if (self != NULL)
727 return NULL;
728
729 GDK_NOTE (MISC, g_message ("opening display %s", display_name ? display_name : ""));
730
731 /* Make the current process a foreground application, i.e. an app
732 * with a user interface, in case we're not running from a .app bundle
733 */
734 TransformProcessType (&psn, kProcessTransformToForegroundApplication);
735
736 [NSApplication sharedApplication];
737
738 self = g_object_new (GDK_TYPE_MACOS_DISPLAY, NULL);
739 self->name = g_strdup (display_name);
740 self->keymap = _gdk_macos_keymap_new (self);
741
742 gdk_macos_display_load_seat (self);
743 gdk_macos_display_load_clipboard (self);
744
745 /* Load CVDisplayLink before monitors to access refresh rates */
746 gdk_macos_display_load_display_link (self);
747 _gdk_macos_display_reload_monitors (self);
748
749 CFNotificationCenterAddObserver (CFNotificationCenterGetLocalCenter (),
750 self,
751 gdk_macos_display_monitors_changed_cb,
752 CFSTR ("NSApplicationDidChangeScreenParametersNotification"),
753 NULL,
754 CFNotificationSuspensionBehaviorDeliverImmediately);
755
756 CFNotificationCenterAddObserver (CFNotificationCenterGetDistributedCenter (),
757 self,
758 gdk_macos_display_user_defaults_changed_cb,
759 CFSTR ("NSUserDefaultsDidChangeNotification"),
760 NULL,
761 CFNotificationSuspensionBehaviorDeliverImmediately);
762
763 if (event_source == NULL)
764 {
765 event_source = _gdk_macos_event_source_new (self);
766 g_source_attach (event_source, NULL);
767 }
768
769 g_object_add_weak_pointer (G_OBJECT (self), (gpointer *)&self);
770
771 gdk_display_emit_opened (GDK_DISPLAY (self));
772
773 return GDK_DISPLAY (self);
774 }
775
776 void
_gdk_macos_display_to_display_coords(GdkMacosDisplay * self,int x,int y,int * out_x,int * out_y)777 _gdk_macos_display_to_display_coords (GdkMacosDisplay *self,
778 int x,
779 int y,
780 int *out_x,
781 int *out_y)
782 {
783 g_return_if_fail (GDK_IS_MACOS_DISPLAY (self));
784
785 if (out_y)
786 *out_y = self->height - y + self->min_y;
787
788 if (out_x)
789 *out_x = x + self->min_x;
790 }
791
792 void
_gdk_macos_display_from_display_coords(GdkMacosDisplay * self,int x,int y,int * out_x,int * out_y)793 _gdk_macos_display_from_display_coords (GdkMacosDisplay *self,
794 int x,
795 int y,
796 int *out_x,
797 int *out_y)
798 {
799 g_return_if_fail (GDK_IS_MACOS_DISPLAY (self));
800
801 if (out_y != NULL)
802 *out_y = self->height - y + self->min_y;
803
804 if (out_x != NULL)
805 *out_x = x - self->min_x;
806 }
807
808 GdkMonitor *
_gdk_macos_display_get_monitor_at_coords(GdkMacosDisplay * self,int x,int y)809 _gdk_macos_display_get_monitor_at_coords (GdkMacosDisplay *self,
810 int x,
811 int y)
812 {
813 guint n_monitors;
814
815 g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (self), NULL);
816
817 n_monitors = g_list_model_get_n_items (G_LIST_MODEL (self->monitors));
818
819 for (guint i = 0; i < n_monitors; i++)
820 {
821 GdkMacosMonitor *monitor = get_monitor (self, i);
822
823 if (gdk_rectangle_contains_point (&GDK_MONITOR (monitor)->geometry, x, y))
824 return GDK_MONITOR (monitor);
825 }
826
827 return NULL;
828 }
829
830 GdkMonitor *
_gdk_macos_display_get_monitor_at_display_coords(GdkMacosDisplay * self,int x,int y)831 _gdk_macos_display_get_monitor_at_display_coords (GdkMacosDisplay *self,
832 int x,
833 int y)
834 {
835 g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (self), NULL);
836
837 _gdk_macos_display_from_display_coords (self, x, y, &x, &y);
838
839 return _gdk_macos_display_get_monitor_at_coords (self, x, y);
840 }
841
842 NSScreen *
_gdk_macos_display_get_screen_at_display_coords(GdkMacosDisplay * self,int x,int y)843 _gdk_macos_display_get_screen_at_display_coords (GdkMacosDisplay *self,
844 int x,
845 int y)
846 {
847 GDK_BEGIN_MACOS_ALLOC_POOL;
848
849 NSArray *screens;
850 NSScreen *screen = NULL;
851
852 g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (self), NULL);
853
854 screens = [NSScreen screens];
855
856 for (id obj in screens)
857 {
858 NSRect geom = [obj frame];
859
860 if (x >= geom.origin.x && x <= geom.origin.x + geom.size.width &&
861 y >= geom.origin.y && y <= geom.origin.y + geom.size.height)
862 {
863 screen = obj;
864 break;
865 }
866 }
867
868 GDK_END_MACOS_ALLOC_POOL;
869
870 return screen;
871 }
872
873 void
_gdk_macos_display_break_all_grabs(GdkMacosDisplay * self,guint32 time)874 _gdk_macos_display_break_all_grabs (GdkMacosDisplay *self,
875 guint32 time)
876 {
877 GdkDevice *devices[2];
878 GdkSeat *seat;
879
880 g_return_if_fail (GDK_IS_MACOS_DISPLAY (self));
881
882 seat = gdk_display_get_default_seat (GDK_DISPLAY (self));
883 devices[0] = gdk_seat_get_keyboard (seat);
884 devices[1] = gdk_seat_get_pointer (seat);
885
886 for (guint i = 0; i < G_N_ELEMENTS (devices); i++)
887 {
888 GdkDevice *device = devices[i];
889 GdkDeviceGrabInfo *grab;
890
891 grab = _gdk_display_get_last_device_grab (GDK_DISPLAY (self), device);
892
893 if (grab != NULL)
894 {
895 GdkEvent *event;
896 GList *node;
897
898 event = gdk_grab_broken_event_new (grab->surface,
899 device,
900 grab->surface,
901 TRUE);
902 node = _gdk_event_queue_append (GDK_DISPLAY (self), event);
903 _gdk_windowing_got_event (GDK_DISPLAY (self), node, event,
904 _gdk_display_get_next_serial (GDK_DISPLAY (self)));
905 }
906 }
907 }
908
909 void
_gdk_macos_display_queue_events(GdkMacosDisplay * self)910 _gdk_macos_display_queue_events (GdkMacosDisplay *self)
911 {
912 g_return_if_fail (GDK_IS_MACOS_DISPLAY (self));
913
914 gdk_macos_display_queue_events (GDK_DISPLAY (self));
915 }
916
917 static GdkMacosSurface *
_gdk_macos_display_get_surface_at_coords(GdkMacosDisplay * self,int x,int y,int * surface_x,int * surface_y)918 _gdk_macos_display_get_surface_at_coords (GdkMacosDisplay *self,
919 int x,
920 int y,
921 int *surface_x,
922 int *surface_y)
923 {
924 const GList *surfaces;
925
926 g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (self), NULL);
927 g_return_val_if_fail (surface_x != NULL, NULL);
928 g_return_val_if_fail (surface_y != NULL, NULL);
929
930 surfaces = _gdk_macos_display_get_surfaces (self);
931
932 for (const GList *iter = surfaces; iter; iter = iter->next)
933 {
934 GdkSurface *surface = iter->data;
935 NSWindow *nswindow;
936
937 g_assert (GDK_IS_MACOS_SURFACE (surface));
938
939 if (!gdk_surface_get_mapped (surface))
940 continue;
941
942 nswindow = _gdk_macos_surface_get_native (GDK_MACOS_SURFACE (surface));
943
944 if (x >= GDK_MACOS_SURFACE (surface)->root_x &&
945 y >= GDK_MACOS_SURFACE (surface)->root_y &&
946 x <= (GDK_MACOS_SURFACE (surface)->root_x + surface->width) &&
947 y <= (GDK_MACOS_SURFACE (surface)->root_y + surface->height))
948 {
949 *surface_x = x - GDK_MACOS_SURFACE (surface)->root_x;
950 *surface_y = y - GDK_MACOS_SURFACE (surface)->root_y;
951
952 return GDK_MACOS_SURFACE (surface);
953 }
954 }
955
956 *surface_x = 0;
957 *surface_y = 0;
958
959 return NULL;
960 }
961
962 GdkMacosSurface *
_gdk_macos_display_get_surface_at_display_coords(GdkMacosDisplay * self,double x,double y,int * surface_x,int * surface_y)963 _gdk_macos_display_get_surface_at_display_coords (GdkMacosDisplay *self,
964 double x,
965 double y,
966 int *surface_x,
967 int *surface_y)
968 {
969 int x_gdk;
970 int y_gdk;
971
972 g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (self), NULL);
973 g_return_val_if_fail (surface_x != NULL, NULL);
974 g_return_val_if_fail (surface_y != NULL, NULL);
975
976 _gdk_macos_display_from_display_coords (self, x, y, &x_gdk, &y_gdk);
977
978 return _gdk_macos_display_get_surface_at_coords (self, x_gdk, y_gdk, surface_x, surface_y);
979 }
980
981 void
_gdk_macos_display_add_frame_callback(GdkMacosDisplay * self,GdkMacosSurface * surface)982 _gdk_macos_display_add_frame_callback (GdkMacosDisplay *self,
983 GdkMacosSurface *surface)
984 {
985 g_return_if_fail (GDK_IS_MACOS_DISPLAY (self));
986 g_return_if_fail (GDK_IS_MACOS_SURFACE (surface));
987
988 if (!queue_contains (&self->awaiting_frames, &surface->frame))
989 {
990 g_queue_push_tail_link (&self->awaiting_frames, &surface->frame);
991
992 if (self->awaiting_frames.length == 1)
993 gdk_display_link_source_unpause ((GdkDisplayLinkSource *)self->frame_source);
994 }
995 }
996
997 void
_gdk_macos_display_remove_frame_callback(GdkMacosDisplay * self,GdkMacosSurface * surface)998 _gdk_macos_display_remove_frame_callback (GdkMacosDisplay *self,
999 GdkMacosSurface *surface)
1000 {
1001 g_return_if_fail (GDK_IS_MACOS_DISPLAY (self));
1002 g_return_if_fail (GDK_IS_MACOS_SURFACE (surface));
1003
1004 if (queue_contains (&self->awaiting_frames, &surface->frame))
1005 {
1006 g_queue_unlink (&self->awaiting_frames, &surface->frame);
1007
1008 if (self->awaiting_frames.length == 0)
1009 gdk_display_link_source_pause ((GdkDisplayLinkSource *)self->frame_source);
1010 }
1011 }
1012
1013 NSWindow *
_gdk_macos_display_find_native_under_pointer(GdkMacosDisplay * self,int * x,int * y)1014 _gdk_macos_display_find_native_under_pointer (GdkMacosDisplay *self,
1015 int *x,
1016 int *y)
1017 {
1018 GdkMacosSurface *surface;
1019 NSPoint point;
1020
1021 g_assert (GDK_IS_MACOS_DISPLAY (self));
1022
1023 point = [NSEvent mouseLocation];
1024
1025 surface = _gdk_macos_display_get_surface_at_display_coords (self, point.x, point.y, x, y);
1026 if (surface != NULL)
1027 return _gdk_macos_surface_get_native (surface);
1028
1029 return NULL;
1030 }
1031
1032 int
_gdk_macos_display_get_nominal_refresh_rate(GdkMacosDisplay * self)1033 _gdk_macos_display_get_nominal_refresh_rate (GdkMacosDisplay *self)
1034 {
1035 g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (self), 60 * 1000);
1036
1037 if (self->frame_source == NULL)
1038 return 60 * 1000;
1039
1040 return ((GdkDisplayLinkSource *)self->frame_source)->refresh_rate;
1041 }
1042
1043 void
_gdk_macos_display_clear_sorting(GdkMacosDisplay * self)1044 _gdk_macos_display_clear_sorting (GdkMacosDisplay *self)
1045 {
1046 g_return_if_fail (GDK_IS_MACOS_DISPLAY (self));
1047
1048 while (self->sorted_surfaces.head != NULL)
1049 g_queue_unlink (&self->sorted_surfaces, self->sorted_surfaces.head);
1050 }
1051
1052 const GList *
_gdk_macos_display_get_surfaces(GdkMacosDisplay * self)1053 _gdk_macos_display_get_surfaces (GdkMacosDisplay *self)
1054 {
1055 g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (self), NULL);
1056
1057 if (self->sorted_surfaces.length == 0)
1058 {
1059 GDK_BEGIN_MACOS_ALLOC_POOL;
1060
1061 NSArray *array = [NSApp orderedWindows];
1062 GQueue sorted = G_QUEUE_INIT;
1063
1064 for (id obj in array)
1065 {
1066 NSWindow *nswindow = (NSWindow *)obj;
1067 GdkMacosSurface *surface;
1068
1069 if (!GDK_IS_MACOS_WINDOW (nswindow))
1070 continue;
1071
1072 surface = [(GdkMacosWindow *)nswindow gdkSurface];
1073
1074 surface->sorted.prev = NULL;
1075 surface->sorted.next = NULL;
1076
1077 g_queue_push_tail_link (&sorted, &surface->sorted);
1078 }
1079
1080 self->sorted_surfaces = sorted;
1081
1082 /* We don't get notification of clipboard changes from the system so we
1083 * instead update it every time the foreground changes (and thusly
1084 * rebuild the sorted list). Things could change other ways, such as
1085 * with scripts, but that is currently out of scope for us.
1086 */
1087 _gdk_macos_clipboard_check_externally_modified (
1088 GDK_MACOS_CLIPBOARD (GDK_DISPLAY (self)->clipboard));
1089
1090 GDK_END_MACOS_ALLOC_POOL;
1091 }
1092
1093 return self->sorted_surfaces.head;
1094 }
1095
1096 void
_gdk_macos_display_warp_pointer(GdkMacosDisplay * self,int x,int y)1097 _gdk_macos_display_warp_pointer (GdkMacosDisplay *self,
1098 int x,
1099 int y)
1100 {
1101 g_return_if_fail (GDK_IS_MACOS_DISPLAY (self));
1102
1103 _gdk_macos_display_to_display_coords (self, x, y, &x, &y);
1104
1105 CGWarpMouseCursorPosition ((CGPoint) { x, y });
1106 }
1107
1108 NSEvent *
_gdk_macos_display_get_nsevent(GdkEvent * event)1109 _gdk_macos_display_get_nsevent (GdkEvent *event)
1110 {
1111 for (const GList *iter = event_map.head; iter; iter = iter->next)
1112 {
1113 const GdkToNSEventMap *map = iter->data;
1114
1115 if (map->gdk_event == event)
1116 return map->nsevent;
1117 }
1118
1119 return NULL;
1120 }
1121
1122 GdkDrag *
_gdk_macos_display_find_drag(GdkMacosDisplay * self,NSInteger sequence_number)1123 _gdk_macos_display_find_drag (GdkMacosDisplay *self,
1124 NSInteger sequence_number)
1125 {
1126 g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (self), NULL);
1127
1128 return g_hash_table_lookup (self->active_drags, GSIZE_TO_POINTER (sequence_number));
1129 }
1130
1131 void
_gdk_macos_display_set_drag(GdkMacosDisplay * self,NSInteger sequence_number,GdkDrag * drag)1132 _gdk_macos_display_set_drag (GdkMacosDisplay *self,
1133 NSInteger sequence_number,
1134 GdkDrag *drag)
1135 {
1136 g_return_if_fail (GDK_IS_MACOS_DISPLAY (self));
1137 g_return_if_fail (!drag || GDK_IS_MACOS_DRAG (drag));
1138
1139 if (drag)
1140 g_hash_table_insert (self->active_drags,
1141 GSIZE_TO_POINTER (sequence_number),
1142 g_object_ref (drag));
1143 else
1144 g_hash_table_remove (self->active_drags,
1145 GSIZE_TO_POINTER (sequence_number));
1146 }
1147
1148 GdkDrop *
_gdk_macos_display_find_drop(GdkMacosDisplay * self,NSInteger sequence_number)1149 _gdk_macos_display_find_drop (GdkMacosDisplay *self,
1150 NSInteger sequence_number)
1151 {
1152 g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (self), NULL);
1153
1154 return g_hash_table_lookup (self->active_drops, GSIZE_TO_POINTER (sequence_number));
1155 }
1156
1157 void
_gdk_macos_display_set_drop(GdkMacosDisplay * self,NSInteger sequence_number,GdkDrop * drop)1158 _gdk_macos_display_set_drop (GdkMacosDisplay *self,
1159 NSInteger sequence_number,
1160 GdkDrop *drop)
1161 {
1162 g_return_if_fail (GDK_IS_MACOS_DISPLAY (self));
1163 g_return_if_fail (!drop || GDK_IS_MACOS_DROP (drop));
1164
1165 if (drop)
1166 g_hash_table_insert (self->active_drops,
1167 GSIZE_TO_POINTER (sequence_number),
1168 g_object_ref (drop));
1169 else
1170 g_hash_table_remove (self->active_drops,
1171 GSIZE_TO_POINTER (sequence_number));
1172 }
1173