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 #include <float.h>
24 #include <gdk/gdk.h>
25 
26 #import "GdkMacosCairoView.h"
27 
28 #include "gdkdeviceprivate.h"
29 #include "gdkdisplay.h"
30 #include "gdkframeclockidleprivate.h"
31 #include "gdkinternals.h"
32 #include "gdksurfaceprivate.h"
33 
34 #include "gdkmacosdevice.h"
35 #include "gdkmacosdevice-private.h"
36 #include "gdkmacosdisplay-private.h"
37 #include "gdkmacosdrag-private.h"
38 #include "gdkmacosdragsurface-private.h"
39 #include "gdkmacosglcontext-private.h"
40 #include "gdkmacosmonitor-private.h"
41 #include "gdkmacospopupsurface-private.h"
42 #include "gdkmacossurface-private.h"
43 #include "gdkmacostoplevelsurface-private.h"
44 #include "gdkmacosutils-private.h"
45 
46 G_DEFINE_ABSTRACT_TYPE (GdkMacosSurface, gdk_macos_surface, GDK_TYPE_SURFACE)
47 
48 enum {
49   PROP_0,
50   PROP_NATIVE,
51   LAST_PROP
52 };
53 
54 static GParamSpec *properties [LAST_PROP];
55 
56 static gboolean
window_is_fullscreen(GdkMacosSurface * self)57 window_is_fullscreen (GdkMacosSurface *self)
58 {
59   g_assert (GDK_IS_MACOS_SURFACE (self));
60 
61   return ([self->window styleMask] & NSWindowStyleMaskFullScreen) != 0;
62 }
63 
64 void
_gdk_macos_surface_reposition_children(GdkMacosSurface * self)65 _gdk_macos_surface_reposition_children (GdkMacosSurface *self)
66 {
67   g_assert (GDK_IS_MACOS_SURFACE (self));
68 
69   if (GDK_SURFACE_DESTROYED (self))
70     return;
71 
72   if (!gdk_surface_get_mapped (GDK_SURFACE (self)))
73     return;
74 
75   for (const GList *iter = GDK_SURFACE (self)->children;
76        iter != NULL;
77        iter = iter->next)
78     {
79       GdkMacosSurface *child = iter->data;
80 
81       g_assert (GDK_IS_MACOS_SURFACE (child));
82 
83       if (GDK_IS_MACOS_POPUP_SURFACE (child))
84         _gdk_macos_popup_surface_reposition (GDK_MACOS_POPUP_SURFACE (child));
85     }
86 
87   if (GDK_IS_POPUP (self) && self->did_initial_present)
88     gdk_surface_request_layout (GDK_SURFACE (self));
89 }
90 
91 static void
gdk_macos_surface_set_input_region(GdkSurface * surface,cairo_region_t * region)92 gdk_macos_surface_set_input_region (GdkSurface     *surface,
93                                     cairo_region_t *region)
94 {
95 }
96 
97 static void
gdk_macos_surface_set_opaque_region(GdkSurface * surface,cairo_region_t * region)98 gdk_macos_surface_set_opaque_region (GdkSurface     *surface,
99                                      cairo_region_t *region)
100 {
101   GdkMacosSurface *self = (GdkMacosSurface *)surface;
102   NSView *nsview;
103 
104   g_assert (GDK_IS_MACOS_SURFACE (self));
105 
106   if (region != self->opaque_region)
107     {
108       g_clear_pointer (&self->opaque_region, cairo_region_destroy);
109       self->opaque_region = cairo_region_copy (region);
110     }
111 
112   if ((nsview = _gdk_macos_surface_get_view (GDK_MACOS_SURFACE (surface))) &&
113       GDK_IS_MACOS_CAIRO_VIEW (nsview))
114     [(GdkMacosCairoView *)nsview setOpaqueRegion:region];
115 }
116 
117 static void
gdk_macos_surface_hide(GdkSurface * surface)118 gdk_macos_surface_hide (GdkSurface *surface)
119 {
120   GdkMacosSurface *self = (GdkMacosSurface *)surface;
121   GdkSeat *seat;
122   gboolean was_mapped;
123 
124   g_assert (GDK_IS_MACOS_SURFACE (self));
125 
126   was_mapped = GDK_SURFACE_IS_MAPPED (GDK_SURFACE (self));
127 
128   seat = gdk_display_get_default_seat (surface->display);
129   gdk_seat_ungrab (seat);
130 
131   [self->window hide];
132 
133   _gdk_surface_clear_update_area (surface);
134 
135   if (was_mapped)
136     gdk_surface_freeze_updates (GDK_SURFACE (self));
137 }
138 
139 static int
gdk_macos_surface_get_scale_factor(GdkSurface * surface)140 gdk_macos_surface_get_scale_factor (GdkSurface *surface)
141 {
142   GdkMacosSurface *self = (GdkMacosSurface *)surface;
143 
144   g_assert (GDK_IS_MACOS_SURFACE (self));
145 
146   return [self->window backingScaleFactor];
147 }
148 
149 void
_gdk_macos_surface_set_shadow(GdkMacosSurface * surface,int top,int right,int bottom,int left)150 _gdk_macos_surface_set_shadow (GdkMacosSurface *surface,
151                                int              top,
152                                int              right,
153                                int              bottom,
154                                int              left)
155 {
156   GdkMacosSurface *self = (GdkMacosSurface *)surface;
157 
158   g_assert (GDK_IS_MACOS_SURFACE (self));
159 
160   if (self->shadow_top == top &&
161       self->shadow_right == right &&
162       self->shadow_bottom == bottom &&
163       self->shadow_left == left)
164     return;
165 
166   self->shadow_top = top;
167   self->shadow_right = right;
168   self->shadow_bottom = bottom;
169   self->shadow_left = left;
170 
171   if (top || right || bottom || left)
172     [self->window setHasShadow:NO];
173 }
174 
175 static void
gdk_macos_surface_begin_frame(GdkMacosSurface * self)176 gdk_macos_surface_begin_frame (GdkMacosSurface *self)
177 {
178   g_assert (GDK_IS_MACOS_SURFACE (self));
179 
180 }
181 
182 static void
gdk_macos_surface_end_frame(GdkMacosSurface * self)183 gdk_macos_surface_end_frame (GdkMacosSurface *self)
184 {
185   GdkFrameTimings *timings;
186   GdkFrameClock *frame_clock;
187   GdkDisplay *display;
188 
189   g_assert (GDK_IS_MACOS_SURFACE (self));
190 
191   display = gdk_surface_get_display (GDK_SURFACE (self));
192   frame_clock = gdk_surface_get_frame_clock (GDK_SURFACE (self));
193 
194   if ((timings = gdk_frame_clock_get_current_timings (frame_clock)))
195     self->pending_frame_counter = timings->frame_counter;
196 
197   _gdk_macos_display_add_frame_callback (GDK_MACOS_DISPLAY (display), self);
198 
199   gdk_surface_freeze_updates (GDK_SURFACE (self));
200 }
201 
202 static void
gdk_macos_surface_before_paint(GdkMacosSurface * self,GdkFrameClock * frame_clock)203 gdk_macos_surface_before_paint (GdkMacosSurface *self,
204                                 GdkFrameClock   *frame_clock)
205 {
206   GdkSurface *surface = (GdkSurface *)self;
207 
208   g_assert (GDK_IS_MACOS_SURFACE (self));
209   g_assert (GDK_IS_FRAME_CLOCK (frame_clock));
210 
211   if (surface->update_freeze_count > 0)
212     return;
213 
214   gdk_macos_surface_begin_frame (self);
215 }
216 
217 static void
gdk_macos_surface_after_paint(GdkMacosSurface * self,GdkFrameClock * frame_clock)218 gdk_macos_surface_after_paint (GdkMacosSurface *self,
219                                GdkFrameClock   *frame_clock)
220 {
221   GdkSurface *surface = (GdkSurface *)self;
222 
223   g_assert (GDK_IS_MACOS_SURFACE (self));
224   g_assert (GDK_IS_FRAME_CLOCK (frame_clock));
225 
226   if (surface->update_freeze_count > 0)
227     return;
228 
229   gdk_macos_surface_end_frame (self);
230 }
231 
232 static void
gdk_macos_surface_get_root_coords(GdkSurface * surface,int x,int y,int * root_x,int * root_y)233 gdk_macos_surface_get_root_coords (GdkSurface *surface,
234                                    int         x,
235                                    int         y,
236                                    int        *root_x,
237                                    int        *root_y)
238 {
239   GdkMacosSurface *self = (GdkMacosSurface *)surface;
240 
241   g_assert (GDK_IS_MACOS_SURFACE (self));
242 
243   if (root_x)
244     *root_x = self->root_x + x;
245 
246   if (root_y)
247     *root_y = self->root_y + y;
248 }
249 
250 static gboolean
gdk_macos_surface_get_device_state(GdkSurface * surface,GdkDevice * device,double * x,double * y,GdkModifierType * mask)251 gdk_macos_surface_get_device_state (GdkSurface      *surface,
252                                     GdkDevice       *device,
253                                     double          *x,
254                                     double          *y,
255                                     GdkModifierType *mask)
256 {
257   GdkDisplay *display;
258   NSWindow *nswindow;
259   NSPoint point;
260 
261   g_assert (GDK_IS_MACOS_SURFACE (surface));
262   g_assert (GDK_IS_MACOS_DEVICE (device));
263   g_assert (x != NULL);
264   g_assert (y != NULL);
265   g_assert (mask != NULL);
266 
267   if (GDK_SURFACE_DESTROYED (surface))
268     return FALSE;
269 
270   display = gdk_surface_get_display (surface);
271   nswindow = _gdk_macos_surface_get_native (GDK_MACOS_SURFACE (surface));
272   point = [nswindow mouseLocationOutsideOfEventStream];
273 
274   *mask = _gdk_macos_display_get_current_keyboard_modifiers (GDK_MACOS_DISPLAY (display))
275         | _gdk_macos_display_get_current_mouse_modifiers (GDK_MACOS_DISPLAY (display));
276 
277   *x = point.x;
278   *y = surface->height - point.y;
279 
280   return *x >= 0 && *y >= 0 && *x < surface->width && *y < surface->height;
281 }
282 
283 static void
gdk_macos_surface_get_geometry(GdkSurface * surface,int * x,int * y,int * width,int * height)284 gdk_macos_surface_get_geometry (GdkSurface *surface,
285                                 int        *x,
286                                 int        *y,
287                                 int        *width,
288                                 int        *height)
289 {
290   g_assert (GDK_IS_MACOS_SURFACE (surface));
291 
292   if (x != NULL)
293     *x = surface->x;
294 
295   if (y != NULL)
296     *y = surface->y;
297 
298   if (width != NULL)
299     *width = surface->width;
300 
301   if (height != NULL)
302     *height = surface->height;
303 }
304 
305 static GdkDrag *
gdk_macos_surface_drag_begin(GdkSurface * surface,GdkDevice * device,GdkContentProvider * content,GdkDragAction actions,double dx,double dy)306 gdk_macos_surface_drag_begin (GdkSurface         *surface,
307                               GdkDevice          *device,
308                               GdkContentProvider *content,
309                               GdkDragAction       actions,
310                               double              dx,
311                               double              dy)
312 {
313   GdkMacosSurface *self = (GdkMacosSurface *)surface;
314   GdkMacosSurface *drag_surface;
315   GdkMacosDrag *drag;
316   GdkCursor *cursor;
317   GdkSeat *seat;
318   double px;
319   double py;
320   int sx;
321   int sy;
322 
323   g_assert (GDK_IS_MACOS_SURFACE (self));
324   g_assert (GDK_IS_MACOS_TOPLEVEL_SURFACE (self) ||
325             GDK_IS_MACOS_POPUP_SURFACE (self));
326   g_assert (GDK_IS_MACOS_DEVICE (device));
327   g_assert (GDK_IS_CONTENT_PROVIDER (content));
328 
329   seat = gdk_device_get_seat (device);
330   gdk_macos_device_query_state (device, surface, NULL, &px, &py, NULL);
331   _gdk_macos_surface_get_root_coords (GDK_MACOS_SURFACE (surface), &sx, &sy);
332   drag_surface = _gdk_macos_surface_new (GDK_MACOS_DISPLAY (surface->display),
333                                          GDK_SURFACE_TEMP,
334                                          surface,
335                                          sx, sy, 1, 1);
336   drag = g_object_new (GDK_TYPE_MACOS_DRAG,
337                        "drag-surface", drag_surface,
338                        "surface", surface,
339                        "device", device,
340                        "content", content,
341                        "actions", actions,
342                        NULL);
343   g_clear_object (&drag_surface);
344 
345   cursor = gdk_drag_get_cursor (GDK_DRAG (drag),
346                                 gdk_drag_get_selected_action (GDK_DRAG (drag)));
347   gdk_drag_set_cursor (GDK_DRAG (drag), cursor);
348 
349   if (!_gdk_macos_drag_begin (drag))
350     {
351       g_object_unref (drag);
352       return NULL;
353     }
354 
355   /* Hold a reference until drop_done is called */
356   g_object_ref (drag);
357 
358   return GDK_DRAG (g_steal_pointer (&drag));
359 }
360 
361 static void
gdk_macos_surface_destroy(GdkSurface * surface,gboolean foreign_destroy)362 gdk_macos_surface_destroy (GdkSurface *surface,
363                            gboolean    foreign_destroy)
364 {
365   GDK_BEGIN_MACOS_ALLOC_POOL;
366 
367   GdkMacosSurface *self = (GdkMacosSurface *)surface;
368   GdkMacosWindow *window = g_steal_pointer (&self->window);
369   GdkFrameClock *frame_clock;
370 
371   if ((frame_clock = gdk_surface_get_frame_clock (GDK_SURFACE (self))))
372     {
373       g_signal_handlers_disconnect_by_func (frame_clock,
374                                             G_CALLBACK (gdk_macos_surface_before_paint),
375                                             self);
376       g_signal_handlers_disconnect_by_func (frame_clock,
377                                             G_CALLBACK (gdk_macos_surface_before_paint),
378                                             self);
379     }
380 
381   g_clear_pointer (&self->title, g_free);
382   g_clear_pointer (&self->opaque_region, cairo_region_destroy);
383 
384   if (window != NULL)
385     [window close];
386 
387   _gdk_macos_display_surface_removed (GDK_MACOS_DISPLAY (surface->display), self);
388 
389   g_clear_pointer (&self->monitors, g_ptr_array_unref);
390 
391   g_assert (self->sorted.prev == NULL);
392   g_assert (self->sorted.next == NULL);
393   g_assert (self->frame.prev == NULL);
394   g_assert (self->frame.next == NULL);
395   g_assert (self->main.prev == NULL);
396   g_assert (self->main.next == NULL);
397 
398   GDK_END_MACOS_ALLOC_POOL;
399 }
400 
401 static void
gdk_macos_surface_constructed(GObject * object)402 gdk_macos_surface_constructed (GObject *object)
403 {
404   GdkMacosSurface *self = (GdkMacosSurface *)object;
405   GdkFrameClock *frame_clock;
406 
407   g_assert (GDK_IS_MACOS_SURFACE (self));
408 
409   G_OBJECT_CLASS (gdk_macos_surface_parent_class)->constructed (object);
410 
411   if (self->window != NULL)
412     {
413       NSRect bounds = [[self->window contentView] bounds];
414 
415       GDK_SURFACE (self)->width = bounds.size.width;
416       GDK_SURFACE (self)->height = bounds.size.height;
417       _gdk_macos_surface_update_position (self);
418     }
419 
420   if ((frame_clock = gdk_surface_get_frame_clock (GDK_SURFACE (self))))
421     {
422       g_signal_connect_object (frame_clock,
423                                "before-paint",
424                                G_CALLBACK (gdk_macos_surface_before_paint),
425                                self,
426                                G_CONNECT_SWAPPED);
427       g_signal_connect_object (frame_clock,
428                                "after-paint",
429                                G_CALLBACK (gdk_macos_surface_after_paint),
430                                self,
431                                G_CONNECT_SWAPPED);
432     }
433 }
434 
435 static void
gdk_macos_surface_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)436 gdk_macos_surface_get_property (GObject    *object,
437                                 guint       prop_id,
438                                 GValue     *value,
439                                 GParamSpec *pspec)
440 {
441   GdkMacosSurface *self = GDK_MACOS_SURFACE (object);
442 
443   switch (prop_id)
444     {
445     case PROP_NATIVE:
446       g_value_set_pointer (value, self->window);
447       break;
448 
449     default:
450       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
451     }
452 }
453 
454 static void
gdk_macos_surface_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)455 gdk_macos_surface_set_property (GObject      *object,
456                                 guint         prop_id,
457                                 const GValue *value,
458                                 GParamSpec   *pspec)
459 {
460   GdkMacosSurface *self = GDK_MACOS_SURFACE (object);
461 
462   switch (prop_id)
463     {
464     case PROP_NATIVE:
465       self->window = g_value_get_pointer (value);
466       [self->window setGdkSurface:self];
467       break;
468 
469     default:
470       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
471     }
472 }
473 
474 static void
gdk_macos_surface_class_init(GdkMacosSurfaceClass * klass)475 gdk_macos_surface_class_init (GdkMacosSurfaceClass *klass)
476 {
477   GObjectClass *object_class = G_OBJECT_CLASS (klass);
478   GdkSurfaceClass *surface_class = GDK_SURFACE_CLASS (klass);
479 
480   object_class->constructed = gdk_macos_surface_constructed;
481   object_class->get_property = gdk_macos_surface_get_property;
482   object_class->set_property = gdk_macos_surface_set_property;
483 
484   surface_class->destroy = gdk_macos_surface_destroy;
485   surface_class->drag_begin = gdk_macos_surface_drag_begin;
486   surface_class->get_device_state = gdk_macos_surface_get_device_state;
487   surface_class->get_geometry = gdk_macos_surface_get_geometry;
488   surface_class->get_root_coords = gdk_macos_surface_get_root_coords;
489   surface_class->get_scale_factor = gdk_macos_surface_get_scale_factor;
490   surface_class->hide = gdk_macos_surface_hide;
491   surface_class->set_input_region = gdk_macos_surface_set_input_region;
492   surface_class->set_opaque_region = gdk_macos_surface_set_opaque_region;
493 
494   properties [PROP_NATIVE] =
495     g_param_spec_pointer ("native",
496                           "Native",
497                           "The native NSWindow",
498                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
499 
500   g_object_class_install_properties (object_class, LAST_PROP, properties);
501 }
502 
503 static void
gdk_macos_surface_init(GdkMacosSurface * self)504 gdk_macos_surface_init (GdkMacosSurface *self)
505 {
506   self->frame.data = self;
507   self->main.data = self;
508   self->sorted.data = self;
509   self->monitors = g_ptr_array_new_with_free_func (g_object_unref);
510 }
511 
512 GdkMacosSurface *
_gdk_macos_surface_new(GdkMacosDisplay * display,GdkSurfaceType surface_type,GdkSurface * parent,int x,int y,int width,int height)513 _gdk_macos_surface_new (GdkMacosDisplay   *display,
514                         GdkSurfaceType     surface_type,
515                         GdkSurface        *parent,
516                         int                x,
517                         int                y,
518                         int                width,
519                         int                height)
520 {
521   GdkFrameClock *frame_clock;
522   GdkMacosSurface *ret;
523 
524   g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (display), NULL);
525 
526   if (parent != NULL)
527     frame_clock = g_object_ref (gdk_surface_get_frame_clock (parent));
528   else
529     frame_clock = _gdk_frame_clock_idle_new ();
530 
531   switch (surface_type)
532     {
533     case GDK_SURFACE_TOPLEVEL:
534       ret = _gdk_macos_toplevel_surface_new (display, parent, frame_clock, x, y, width, height);
535       break;
536 
537     case GDK_SURFACE_POPUP:
538       ret = _gdk_macos_popup_surface_new (display, parent, frame_clock, x, y, width, height);
539       break;
540 
541     case GDK_SURFACE_TEMP:
542       ret = _gdk_macos_drag_surface_new (display, frame_clock, x, y, width, height);
543       break;
544 
545     default:
546       g_warn_if_reached ();
547       ret = NULL;
548     }
549 
550   if (ret != NULL)
551     {
552       gdk_surface_freeze_updates (GDK_SURFACE (ret));
553       _gdk_macos_surface_monitor_changed (ret);
554     }
555 
556   g_object_unref (frame_clock);
557 
558   return g_steal_pointer (&ret);
559 }
560 
561 void
_gdk_macos_surface_get_shadow(GdkMacosSurface * self,int * top,int * right,int * bottom,int * left)562 _gdk_macos_surface_get_shadow (GdkMacosSurface *self,
563                                int             *top,
564                                int             *right,
565                                int             *bottom,
566                                int             *left)
567 {
568 
569   g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
570 
571   if (top)
572     *top = self->shadow_top;
573 
574   if (left)
575     *left = self->shadow_left;
576 
577   if (bottom)
578     *bottom = self->shadow_bottom;
579 
580   if (right)
581     *right = self->shadow_right;
582 }
583 
584 const char *
_gdk_macos_surface_get_title(GdkMacosSurface * self)585 _gdk_macos_surface_get_title (GdkMacosSurface *self)
586 {
587 
588   return self->title;
589 }
590 
591 void
_gdk_macos_surface_set_title(GdkMacosSurface * self,const char * title)592 _gdk_macos_surface_set_title (GdkMacosSurface *self,
593                               const char      *title)
594 {
595   g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
596 
597   if (title == NULL)
598     title = "";
599 
600   if (g_strcmp0 (self->title, title) != 0)
601     {
602       g_free (self->title);
603       self->title = g_strdup (title);
604 
605       GDK_BEGIN_MACOS_ALLOC_POOL;
606       [self->window setTitle:[NSString stringWithUTF8String:title]];
607       GDK_END_MACOS_ALLOC_POOL;
608     }
609 }
610 
611 CGDirectDisplayID
_gdk_macos_surface_get_screen_id(GdkMacosSurface * self)612 _gdk_macos_surface_get_screen_id (GdkMacosSurface *self)
613 {
614   g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), (CGDirectDisplayID)-1);
615 
616   if (self->window != NULL)
617     {
618       NSScreen *screen = [self->window screen];
619       return [[[screen deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue];
620     }
621 
622   return (CGDirectDisplayID)-1;
623 }
624 
625 NSWindow *
_gdk_macos_surface_get_native(GdkMacosSurface * self)626 _gdk_macos_surface_get_native (GdkMacosSurface *self)
627 {
628   g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), NULL);
629 
630   return (NSWindow *)self->window;
631 }
632 
633 void
_gdk_macos_surface_set_geometry_hints(GdkMacosSurface * self,const GdkGeometry * geometry,GdkSurfaceHints geom_mask)634 _gdk_macos_surface_set_geometry_hints (GdkMacosSurface   *self,
635                                        const GdkGeometry *geometry,
636                                        GdkSurfaceHints    geom_mask)
637 {
638   NSSize max_size;
639   NSSize min_size;
640 
641   g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
642   g_return_if_fail (geometry != NULL);
643   g_return_if_fail (self->window != NULL);
644 
645   if (geom_mask & GDK_HINT_MAX_SIZE)
646     max_size = NSMakeSize (geometry->max_width, geometry->max_height);
647   else
648     max_size = NSMakeSize (FLT_MAX, FLT_MAX);
649   [self->window setContentMaxSize:max_size];
650 
651   if (geom_mask & GDK_HINT_MIN_SIZE)
652     min_size = NSMakeSize (geometry->min_width, geometry->min_height);
653   else
654     min_size = NSMakeSize (1, 1);
655   [self->window setContentMinSize:min_size];
656 }
657 
658 void
_gdk_macos_surface_resize(GdkMacosSurface * self,int width,int height)659 _gdk_macos_surface_resize (GdkMacosSurface *self,
660                            int              width,
661                            int              height)
662 {
663   g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
664 
665   _gdk_macos_surface_move_resize (self, -1, -1, width, height);
666 }
667 
668 void
_gdk_macos_surface_update_fullscreen_state(GdkMacosSurface * self)669 _gdk_macos_surface_update_fullscreen_state (GdkMacosSurface *self)
670 {
671   GdkToplevelState state;
672   gboolean is_fullscreen;
673   gboolean was_fullscreen;
674 
675   g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
676 
677   state = GDK_SURFACE (self)->state;
678   is_fullscreen = window_is_fullscreen (self);
679   was_fullscreen = (state & GDK_TOPLEVEL_STATE_FULLSCREEN) != 0;
680 
681   if (is_fullscreen != was_fullscreen)
682     {
683       if (is_fullscreen)
684         gdk_synthesize_surface_state (GDK_SURFACE (self), 0, GDK_TOPLEVEL_STATE_FULLSCREEN);
685       else
686         gdk_synthesize_surface_state (GDK_SURFACE (self), GDK_TOPLEVEL_STATE_FULLSCREEN, 0);
687     }
688 }
689 
690 void
_gdk_macos_surface_update_position(GdkMacosSurface * self)691 _gdk_macos_surface_update_position (GdkMacosSurface *self)
692 {
693   GdkSurface *surface = GDK_SURFACE (self);
694   GdkDisplay *display = gdk_surface_get_display (surface);
695   NSRect frame_rect = [self->window frame];
696   NSRect content_rect = [self->window contentRectForFrameRect:frame_rect];
697 
698   _gdk_macos_display_from_display_coords (GDK_MACOS_DISPLAY (display),
699                                           content_rect.origin.x,
700                                           content_rect.origin.y + content_rect.size.height,
701                                           &self->root_x, &self->root_y);
702 
703   if (surface->parent != NULL)
704     {
705       surface->x = self->root_x - GDK_MACOS_SURFACE (surface->parent)->root_x;
706       surface->y = self->root_y - GDK_MACOS_SURFACE (surface->parent)->root_y;
707     }
708   else
709     {
710       surface->x = self->root_x;
711       surface->y = self->root_y;
712     }
713 }
714 
715 void
_gdk_macos_surface_thaw(GdkMacosSurface * self,gint64 presentation_time,gint64 refresh_interval)716 _gdk_macos_surface_thaw (GdkMacosSurface *self,
717                          gint64           presentation_time,
718                          gint64           refresh_interval)
719 {
720   GdkFrameTimings *timings;
721   GdkFrameClock *frame_clock;
722 
723   g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
724 
725   gdk_surface_thaw_updates (GDK_SURFACE (self));
726 
727   frame_clock = gdk_surface_get_frame_clock (GDK_SURFACE (self));
728 
729   if (self->pending_frame_counter)
730     {
731       timings = gdk_frame_clock_get_timings (frame_clock, self->pending_frame_counter);
732 
733       if (timings != NULL)
734         {
735           timings->presentation_time = presentation_time - refresh_interval;
736           timings->complete = TRUE;
737         }
738 
739       self->pending_frame_counter = 0;
740     }
741 
742   timings = gdk_frame_clock_get_current_timings (frame_clock);
743 
744   if (timings != NULL)
745     {
746       timings->refresh_interval = refresh_interval;
747       timings->predicted_presentation_time = presentation_time;
748     }
749 }
750 
751 void
_gdk_macos_surface_show(GdkMacosSurface * self)752 _gdk_macos_surface_show (GdkMacosSurface *self)
753 {
754   gboolean was_mapped;
755 
756   g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
757 
758   if (GDK_SURFACE_DESTROYED (self))
759     return;
760 
761   was_mapped = GDK_SURFACE_IS_MAPPED (GDK_SURFACE (self));
762 
763   if (!was_mapped)
764     gdk_surface_set_is_mapped (GDK_SURFACE (self), TRUE);
765 
766   _gdk_macos_display_clear_sorting (GDK_MACOS_DISPLAY (GDK_SURFACE (self)->display));
767 
768   [self->window showAndMakeKey:YES];
769 
770   if (!was_mapped)
771     {
772       if (gdk_surface_get_mapped (GDK_SURFACE (self)))
773         {
774           _gdk_macos_surface_update_position (self);
775           gdk_surface_invalidate_rect (GDK_SURFACE (self), NULL);
776           gdk_surface_thaw_updates (GDK_SURFACE (self));
777         }
778     }
779 
780   [[self->window contentView] setNeedsDisplay:YES];
781 }
782 
783 CGContextRef
_gdk_macos_surface_acquire_context(GdkMacosSurface * self,gboolean clear_scale,gboolean antialias)784 _gdk_macos_surface_acquire_context (GdkMacosSurface *self,
785                                     gboolean         clear_scale,
786                                     gboolean         antialias)
787 {
788   CGContextRef cg_context;
789 
790   g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), NULL);
791 
792   if (GDK_SURFACE_DESTROYED (self))
793     return NULL;
794 
795   if (!(cg_context = [[NSGraphicsContext currentContext] CGContext]))
796     return NULL;
797 
798   CGContextSaveGState (cg_context);
799 
800   if (!antialias)
801     CGContextSetAllowsAntialiasing (cg_context, antialias);
802 
803   if (clear_scale)
804     {
805       CGSize scale;
806 
807       scale = CGSizeMake (1.0, 1.0);
808       scale = CGContextConvertSizeToDeviceSpace (cg_context, scale);
809 
810       CGContextScaleCTM (cg_context, 1.0 / fabs (scale.width), 1.0 / fabs (scale.height));
811     }
812 
813   return cg_context;
814 }
815 
816 void
_gdk_macos_surface_release_context(GdkMacosSurface * self,CGContextRef cg_context)817 _gdk_macos_surface_release_context (GdkMacosSurface *self,
818                                     CGContextRef     cg_context)
819 {
820   g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
821 
822   CGContextRestoreGState (cg_context);
823   CGContextSetAllowsAntialiasing (cg_context, TRUE);
824 }
825 
826 void
_gdk_macos_surface_synthesize_null_key(GdkMacosSurface * self)827 _gdk_macos_surface_synthesize_null_key (GdkMacosSurface *self)
828 {
829   GdkTranslatedKey translated = {0};
830   GdkTranslatedKey no_lock = {0};
831   GdkDisplay *display;
832   GdkEvent *event;
833   GdkSeat *seat;
834 
835   g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
836 
837   translated.keyval = GDK_KEY_VoidSymbol;
838   no_lock.keyval = GDK_KEY_VoidSymbol;
839 
840   display = gdk_surface_get_display (GDK_SURFACE (self));
841   seat = gdk_display_get_default_seat (display);
842   event = gdk_key_event_new (GDK_KEY_PRESS,
843                              GDK_SURFACE (self),
844                              gdk_seat_get_keyboard (seat),
845                              GDK_CURRENT_TIME,
846                              0,
847                              0,
848                              FALSE,
849                              &translated,
850                              &no_lock);
851   _gdk_event_queue_append (display, event);
852 }
853 
854 void
_gdk_macos_surface_move(GdkMacosSurface * self,int x,int y)855 _gdk_macos_surface_move (GdkMacosSurface *self,
856                          int              x,
857                          int              y)
858 {
859   g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
860 
861   _gdk_macos_surface_move_resize (self, x, y, -1, -1);
862 }
863 
864 void
_gdk_macos_surface_move_resize(GdkMacosSurface * self,int x,int y,int width,int height)865 _gdk_macos_surface_move_resize (GdkMacosSurface *self,
866                                 int              x,
867                                 int              y,
868                                 int              width,
869                                 int              height)
870 {
871   GdkSurface *surface = (GdkSurface *)self;
872   GdkDisplay *display;
873   NSRect content_rect;
874   NSRect frame_rect;
875   gboolean size_changed;
876 
877   g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
878 
879   if ((x == -1 || (x == self->root_x)) &&
880       (y == -1 || (y == self->root_y)) &&
881       (width == -1 || (width == surface->width)) &&
882       (height == -1 || (height == surface->height)))
883     return;
884 
885   display = gdk_surface_get_display (surface);
886 
887   if (width == -1)
888     width = surface->width;
889 
890   if (height == -1)
891     height = surface->height;
892 
893   if (x == -1)
894     x = self->root_x;
895 
896   if (y == -1)
897     y = self->root_y;
898 
899   size_changed = height != surface->height || width != surface->width;
900 
901   if (GDK_IS_MACOS_SURFACE (surface->parent))
902     {
903       surface->x = x - GDK_MACOS_SURFACE (surface->parent)->root_x;
904       surface->y = y - GDK_MACOS_SURFACE (surface->parent)->root_y;
905     }
906   else
907     {
908       surface->x = x;
909       surface->y = y;
910     }
911 
912   _gdk_macos_display_to_display_coords (GDK_MACOS_DISPLAY (display),
913                                         x, y + height, &x, &y);
914 
915   content_rect = NSMakeRect (x, y, width, height);
916   frame_rect = [self->window frameRectForContentRect:content_rect];
917   [self->window setFrame:frame_rect display:YES];
918 
919   if (size_changed)
920     gdk_surface_invalidate_rect (surface, NULL);
921 }
922 
923 gboolean
_gdk_macos_surface_is_tracking(GdkMacosSurface * self,NSTrackingArea * area)924 _gdk_macos_surface_is_tracking (GdkMacosSurface *self,
925                                 NSTrackingArea  *area)
926 {
927   GdkMacosBaseView *view;
928 
929   g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), FALSE);
930 
931   if (self->window == NULL)
932     return FALSE;
933 
934   view = (GdkMacosBaseView *)[self->window contentView];
935   if (view == NULL)
936     return FALSE;
937 
938   return [view trackingArea] == area;
939 }
940 
941 void
_gdk_macos_surface_monitor_changed(GdkMacosSurface * self)942 _gdk_macos_surface_monitor_changed (GdkMacosSurface *self)
943 {
944   GListModel *monitors;
945   GdkRectangle rect;
946   GdkRectangle intersect;
947   GdkDisplay *display;
948   GdkMonitor *monitor;
949   guint n_monitors;
950 
951   g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
952 
953   rect.x = self->root_x;
954   rect.y = self->root_y;
955   rect.width = GDK_SURFACE (self)->width;
956   rect.height = GDK_SURFACE (self)->height;
957 
958   for (guint i = self->monitors->len; i > 0; i--)
959     {
960       monitor = g_ptr_array_index (self->monitors, i-1);
961 
962       if (!gdk_rectangle_intersect (&monitor->geometry, &rect, &intersect))
963         {
964           g_object_ref (monitor);
965           g_ptr_array_remove_index (self->monitors, i-1);
966           gdk_surface_leave_monitor (GDK_SURFACE (self), monitor);
967           g_object_unref (monitor);
968         }
969     }
970 
971   display = gdk_surface_get_display (GDK_SURFACE (self));
972   monitors = gdk_display_get_monitors (display);
973   n_monitors = g_list_model_get_n_items (monitors);
974 
975   for (guint i = 0; i < n_monitors; i++)
976     {
977       monitor = g_list_model_get_item (monitors, i);
978 
979       if (!g_ptr_array_find (self->monitors, monitor, NULL))
980         {
981           gdk_surface_enter_monitor (GDK_SURFACE (self), monitor);
982           g_ptr_array_add (self->monitors, g_object_ref (monitor));
983         }
984 
985       g_object_unref (monitor);
986     }
987 }
988 
989 GdkMonitor *
_gdk_macos_surface_get_best_monitor(GdkMacosSurface * self)990 _gdk_macos_surface_get_best_monitor (GdkMacosSurface *self)
991 {
992   GdkMonitor *best = NULL;
993   GdkRectangle rect;
994   int best_area = 0;
995 
996   g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), NULL);
997 
998   rect.x = self->root_x;
999   rect.y = self->root_y;
1000   rect.width = GDK_SURFACE (self)->width;
1001   rect.height = GDK_SURFACE (self)->height;
1002 
1003   for (guint i = 0; i < self->monitors->len; i++)
1004     {
1005       GdkMonitor *monitor = g_ptr_array_index (self->monitors, i);
1006       GdkRectangle intersect;
1007 
1008       if (gdk_rectangle_intersect (&monitor->geometry, &rect, &intersect))
1009         {
1010           int area = intersect.width * intersect.height;
1011 
1012           if (area > best_area)
1013             {
1014               best = monitor;
1015               best_area = area;
1016             }
1017         }
1018     }
1019 
1020   return best;
1021 }
1022 
1023 NSView *
_gdk_macos_surface_get_view(GdkMacosSurface * self)1024 _gdk_macos_surface_get_view (GdkMacosSurface *self)
1025 {
1026   g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), NULL);
1027 
1028   if (self->window == NULL)
1029     return NULL;
1030 
1031   return [self->window contentView];
1032 }
1033 
1034 void
_gdk_macos_surface_set_opacity(GdkMacosSurface * self,double opacity)1035 _gdk_macos_surface_set_opacity (GdkMacosSurface *self,
1036                                 double           opacity)
1037 {
1038   g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
1039 
1040   if (self->window != NULL)
1041     [self->window setAlphaValue:opacity];
1042 }
1043 
1044 void
_gdk_macos_surface_get_root_coords(GdkMacosSurface * self,int * x,int * y)1045 _gdk_macos_surface_get_root_coords (GdkMacosSurface *self,
1046                                     int             *x,
1047                                     int             *y)
1048 {
1049   GdkSurface *surface;
1050   int out_x = 0;
1051   int out_y = 0;
1052 
1053   g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
1054 
1055   for (surface = GDK_SURFACE (self); surface; surface = surface->parent)
1056     {
1057       out_x += surface->x;
1058       out_y += surface->y;
1059     }
1060 
1061   if (x)
1062     *x = out_x;
1063 
1064   if (y)
1065     *y = out_y;
1066 }
1067