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