[gtk/wip/chergert/quartz4u] macos: improve coordinate positioning and edge snapping



commit 76bcf5eddd853f726be14837acd854036bc9cb78
Author: Christian Hergert <chergert redhat com>
Date:   Mon May 18 10:48:39 2020 -0700

    macos: improve coordinate positioning and edge snapping
    
    This adds a little helper so that we can try to snap edges for CSD
    windows more similar to how quartz does for opaque/titled
    windows.

 gdk/macos/GdkMacosWindow.c          | 169 ++++++++++++++-------------
 gdk/macos/GdkMacosWindow.h          |   5 +-
 gdk/macos/edgesnapping.c            | 223 ++++++++++++++++++++++++++++++++++++
 gdk/macos/edgesnapping.h            |  50 ++++++++
 gdk/macos/gdkmacosdisplay-private.h |   2 +
 gdk/macos/gdkmacosdisplay.c         |  21 ++--
 gdk/macos/gdkmacosmonitor.c         |  99 ++++++++--------
 gdk/macos/gdkmacossurface.c         |   4 +-
 gdk/macos/meson.build               |   2 +
 9 files changed, 426 insertions(+), 149 deletions(-)
---
diff --git a/gdk/macos/GdkMacosWindow.c b/gdk/macos/GdkMacosWindow.c
index 6a5f4abddf..36e8611996 100644
--- a/gdk/macos/GdkMacosWindow.c
+++ b/gdk/macos/GdkMacosWindow.c
@@ -310,92 +310,79 @@
 
 -(BOOL)trackManualMove
 {
-  NSRect workarea;
-  NSRect geometry;
   NSRect windowFrame;
-  NSRect withoutShadow;
-  NSRect newFrame;
   NSPoint currentLocation;
+  GdkMonitor *monitor;
+  GdkRectangle geometry;
+  GdkRectangle workarea;
   int shadow_top = 0;
   int shadow_left = 0;
   int shadow_right = 0;
   int shadow_bottom = 0;
-  int thresh_top;
-  int thresh_left;
-  int thresh_right;
-  int thresh_bottom;
+  GdkRectangle window_gdk;
+  GdkPoint pointer_position;
+  GdkPoint new_origin;
 
   if (!inManualMove)
     return NO;
 
-  workarea = [[self screen] visibleFrame];
-  geometry = [[self screen] frame];
-  windowFrame = [self frame];
-
-  _gdk_macos_surface_get_shadow (gdk_surface, &shadow_top, &shadow_right, &shadow_bottom, &shadow_left);
-
-  currentLocation = [self convertPointToScreen:[self mouseLocationOutsideOfEventStream]];
-  windowFrame.origin.x = currentLocation.x - initialMoveLocation.x;
-  windowFrame.origin.y = currentLocation.y - initialMoveLocation.y;
-
-  if (currentLocation.y > lastMoveLocation.y)
-    {
-      thresh_bottom = 5;
-      thresh_top = 10;
-    }
-  else
-    {
-      thresh_bottom = 10;
-      thresh_top = 10;
-    }
-
-  if (currentLocation.x > lastMoveLocation.x)
-    {
-      thresh_right = 20;
-      thresh_left = 10;
-    }
-  else
-    {
-      thresh_right = 10;
-      thresh_left = 20;
-    }
-
-  withoutShadow = NSMakeRect (windowFrame.origin.x + shadow_left,
-                              windowFrame.origin.y + shadow_bottom,
-                              windowFrame.size.width - shadow_left - shadow_right,
-                              windowFrame.size.height - shadow_top - shadow_bottom);
-
-  newFrame = [self constrainFrameRect:withoutShadow
-                             toScreen:[self screen]];
-
-  if (newFrame.origin.x < workarea.origin.x &&
-      newFrame.origin.x > (workarea.origin.x - thresh_left))
-    newFrame.origin.x = workarea.origin.x;
-
-  if (newFrame.origin.x + newFrame.size.width > workarea.origin.x + workarea.size.width &&
-      newFrame.origin.x + newFrame.size.width < workarea.origin.x + workarea.size.width + thresh_right)
-    newFrame.origin.x = workarea.origin.x + workarea.size.width - newFrame.size.width;
-
-  if (newFrame.origin.y < workarea.origin.y &&
-      newFrame.origin.y > workarea.origin.y - thresh_bottom)
-    newFrame.origin.y = workarea.origin.y;
-
-  if (newFrame.origin.y < geometry.origin.y &&
-      newFrame.origin.y > geometry.origin.y - thresh_bottom)
-    newFrame.origin.y = geometry.origin.y;
+  /* Get our shadow so we can adjust the window position sans-shadow */
+  _gdk_macos_surface_get_shadow (gdk_surface,
+                                 &shadow_top,
+                                 &shadow_right,
+                                 &shadow_bottom,
+                                 &shadow_left);
 
-  if (newFrame.origin.y + newFrame.size.height > workarea.origin.y + workarea.size.height &&
-      newFrame.origin.y + newFrame.size.height < workarea.origin.y + workarea.size.height + thresh_top)
-    newFrame.origin.y = workarea.origin.y + workarea.size.height - newFrame.size.height;
-
-  newFrame.origin.x -= shadow_left;
-  newFrame.origin.y -= shadow_bottom;
-  newFrame.size.width += shadow_left + shadow_right;
-  newFrame.size.height += shadow_bottom + shadow_top;
-
-  lastMoveLocation = currentLocation;
-
-  [self setFrameOrigin:newFrame.origin];
+  windowFrame = [self frame];
+  currentLocation = [NSEvent mouseLocation];
+
+  /* Update the snapping geometry to match the current monitor */
+  monitor = _gdk_macos_display_get_monitor_at_display_coords ([self gdkDisplay],
+                                                              currentLocation.x,
+                                                              currentLocation.y);
+  gdk_monitor_get_geometry (monitor, &geometry);
+  gdk_monitor_get_workarea (monitor, &workarea);
+  _edge_snapping_set_monitor (&self->snapping, &geometry, &workarea);
+
+  /* Convert origins to GDK coordinates */
+  _gdk_macos_display_from_display_coords ([self gdkDisplay],
+                                          currentLocation.x,
+                                          currentLocation.y,
+                                          &pointer_position.x,
+                                          &pointer_position.y);
+  _gdk_macos_display_from_display_coords ([self gdkDisplay],
+                                          windowFrame.origin.x,
+                                          windowFrame.origin.y + windowFrame.size.height,
+                                          &window_gdk.x,
+                                          &window_gdk.y);
+  window_gdk.width = windowFrame.size.width;
+  window_gdk.height = windowFrame.size.height;
+
+  /* Subtract our shadowin from the window */
+  window_gdk.x += shadow_left;
+  window_gdk.y += shadow_top;
+  window_gdk.width = window_gdk.width - shadow_left - shadow_right;
+  window_gdk.height = window_gdk.height - shadow_top - shadow_bottom;
+
+  /* Now place things on the monitor */
+  _edge_snapping_motion (&self->snapping, &pointer_position, &window_gdk);
+
+  /* And add our shadow back to the frame */
+  window_gdk.x -= shadow_left;
+  window_gdk.y -= shadow_top;
+  window_gdk.width += shadow_left + shadow_right;
+  window_gdk.height += shadow_top + shadow_bottom;
+
+  /* Convert to quartz coordiantes */
+  _gdk_macos_display_to_display_coords ([self gdkDisplay],
+                                        window_gdk.x,
+                                        window_gdk.y + window_gdk.height,
+                                        &new_origin.x, &new_origin.y);
+  windowFrame.origin.x = new_origin.x;
+  windowFrame.origin.y = new_origin.y;
+
+  /* And now apply the frame to the window */
+  [self setFrameOrigin:NSMakePoint(new_origin.x, new_origin.y)];
 
   return YES;
 }
@@ -411,18 +398,40 @@
 
 -(void)beginManualMove
 {
-  NSRect frame = [self frame];
+  NSPoint initialMoveLocation;
+  GdkPoint point;
+  GdkMonitor *monitor;
+  GdkRectangle geometry;
+  GdkRectangle area;
+  GdkRectangle workarea;
 
   if (inMove || inManualMove || inManualResize)
     return;
 
   inManualMove = YES;
 
-  initialMoveLocation = [self convertPointToScreen:[self mouseLocationOutsideOfEventStream]];
-  initialMoveLocation.x -= frame.origin.x;
-  initialMoveLocation.y -= frame.origin.y;
+  monitor = _gdk_macos_surface_get_best_monitor ([self gdkSurface]);
+  gdk_monitor_get_geometry (monitor, &geometry);
+  gdk_monitor_get_workarea (monitor, &workarea);
+
+  initialMoveLocation = [NSEvent mouseLocation];
+
+  _gdk_macos_display_from_display_coords ([self gdkDisplay],
+                                          initialMoveLocation.x,
+                                          initialMoveLocation.y,
+                                          &point.x,
+                                          &point.y);
+
+  area.x = GDK_SURFACE (gdk_surface)->x;
+  area.y = GDK_SURFACE (gdk_surface)->y;
+  area.width = GDK_SURFACE (gdk_surface)->width;
+  area.height = GDK_SURFACE (gdk_surface)->height;
 
-  lastMoveLocation = initialMoveLocation;
+  _edge_snapping_init (&self->snapping,
+                       &geometry,
+                       &workarea,
+                       &point,
+                       &area);
 }
 
 -(BOOL)trackManualResize
diff --git a/gdk/macos/GdkMacosWindow.h b/gdk/macos/GdkMacosWindow.h
index 664cc22b9b..61f546a78b 100644
--- a/gdk/macos/GdkMacosWindow.h
+++ b/gdk/macos/GdkMacosWindow.h
@@ -26,6 +26,7 @@
 
 #include "gdkmacosdisplay.h"
 #include "gdkmacossurface.h"
+#include "edgesnapping.h"
 
 #define GDK_IS_MACOS_WINDOW(obj) ([obj isKindOfClass:[GdkMacosWindow class]])
 
@@ -40,12 +41,12 @@
   BOOL             inManualMove;
   BOOL             inManualResize;
   BOOL             inTrackManualResize;
-  NSPoint          initialMoveLocation;
-  NSPoint          lastMoveLocation;
   NSPoint          initialResizeLocation;
   NSRect           initialResizeFrame;
   GdkSurfaceEdge   resizeEdge;
 
+  EdgeSnapping     snapping;
+
   NSRect           lastUnmaximizedFrame;
   NSRect           lastMaximizedFrame;
   NSRect           lastUnfullscreenFrame;
diff --git a/gdk/macos/edgesnapping.c b/gdk/macos/edgesnapping.c
new file mode 100644
index 0000000000..e81b29626c
--- /dev/null
+++ b/gdk/macos/edgesnapping.c
@@ -0,0 +1,223 @@
+/* edgesnapping.c
+ *
+ * Copyright © 2020 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include "gdkrectangle.h"
+#include "edgesnapping.h"
+
+#define LEAVE_THRESHOLD 3.0
+#define ENTER_THRESHOLD 2.0
+
+#define X1(r) ((r)->x)
+#define X2(r) ((r)->x + (r)->width)
+#define Y1(r) ((r)->y)
+#define Y2(r) ((r)->y + (r)->height)
+
+void
+_edge_snapping_init (EdgeSnapping       *self,
+                     const GdkRectangle *geometry,
+                     const GdkRectangle *workarea,
+                     const GdkPoint     *pointer_position,
+                     const GdkRectangle *window)
+{
+  g_assert (self != NULL);
+  g_assert (geometry != NULL);
+  g_assert (workarea != NULL);
+  g_assert (pointer_position != NULL);
+
+  self->geometry = *geometry;
+  self->workarea = *workarea;
+  self->last_pointer_position = *pointer_position;
+  self->pointer_offset_in_window.x = pointer_position->x - window->x;
+  self->pointer_offset_in_window.y = pointer_position->y - window->y;
+}
+
+static void
+edge_snapping_constrain_left (EdgeSnapping       *self,
+                              int                 change,
+                              const GdkRectangle *geometry,
+                              GdkRectangle       *window)
+{
+  if (change < 0)
+    {
+      if (X1 (window) < X1 (geometry) &&
+          X1 (window) > X1 (geometry) - LEAVE_THRESHOLD &&
+          ABS (change) < LEAVE_THRESHOLD)
+        window->x = geometry->x;
+    }
+
+  /* We don't constrain when returning from left edge */
+}
+
+static void
+edge_snapping_constrain_right (EdgeSnapping       *self,
+                               int                 change,
+                               const GdkRectangle *geometry,
+                               GdkRectangle       *window)
+{
+  if (change > 0)
+    {
+      if (X2 (window) > X2 (geometry) &&
+          X2 (window) < X2 (geometry) + LEAVE_THRESHOLD &&
+          ABS (change) < LEAVE_THRESHOLD)
+        window->x = X2 (geometry) - window->width;
+    }
+
+  /* We don't constrain when returning from right edge */
+}
+
+static void
+edge_snapping_constrain_top (EdgeSnapping       *self,
+                             int                 change,
+                             const GdkRectangle *geometry,
+                             GdkRectangle       *window)
+{
+  if (change < 0)
+    {
+      if (Y1 (window) < Y1 (geometry))
+        window->y = geometry->y;
+    }
+
+  /* We don't constrain when returning from top edge */
+}
+
+static void
+edge_snapping_constrain_bottom (EdgeSnapping       *self,
+                                int                 change,
+                                const GdkRectangle *geometry,
+                                GdkRectangle       *window)
+{
+  if (change > 0)
+    {
+      if (Y2 (window) > Y2 (geometry) &&
+          Y2 (window) < Y2 (geometry) + LEAVE_THRESHOLD &&
+          ABS (change) < LEAVE_THRESHOLD)
+        window->y = Y2 (geometry) - window->height;
+    }
+  else if (change < 0)
+    {
+      if (Y2 (window) < Y2 (geometry) &&
+          Y2 (window) > Y2 (geometry) - ENTER_THRESHOLD &&
+          ABS (change) < ENTER_THRESHOLD)
+        window->y = Y2 (geometry) - window->height;
+    }
+
+}
+
+static void
+edge_snapping_constrain_horizontal (EdgeSnapping       *self,
+                                    int                 change,
+                                    const GdkRectangle *geometry,
+                                    GdkRectangle       *window)
+{
+  g_assert (self != NULL);
+  g_assert (geometry != NULL);
+  g_assert (window != NULL);
+  g_assert (change != 0);
+
+  if (ABS (X1 (geometry) - X1 (window)) < ABS (X2 (geometry)) - ABS (X2 (window)))
+    edge_snapping_constrain_left (self, change, geometry, window);
+  else
+    edge_snapping_constrain_right (self, change, geometry, window);
+}
+
+static void
+edge_snapping_constrain_vertical (EdgeSnapping       *self,
+                                  int                 change,
+                                  const GdkRectangle *geometry,
+                                  GdkRectangle       *window,
+                                  gboolean            bottom_only)
+{
+  g_assert (self != NULL);
+  g_assert (geometry != NULL);
+  g_assert (window != NULL);
+  g_assert (change != 0);
+
+  if (!bottom_only &&
+      ABS (Y1 (geometry) - Y1 (window)) < ABS (Y2 (geometry)) - ABS (Y2 (window)))
+    edge_snapping_constrain_top (self, change, geometry, window);
+  else
+    edge_snapping_constrain_bottom (self, change, geometry, window);
+}
+
+void
+_edge_snapping_motion (EdgeSnapping   *self,
+                       const GdkPoint *pointer_position,
+                       GdkRectangle   *window)
+{
+  GdkRectangle new_window;
+  GdkRectangle overlap;
+  GdkPoint change;
+
+  g_assert (self != NULL);
+  g_assert (pointer_position != NULL);
+
+  change.x = pointer_position->x - self->last_pointer_position.x;
+  change.y = pointer_position->y - self->last_pointer_position.y;
+
+  self->last_pointer_position = *pointer_position;
+
+  window->x += change.x;
+  window->y += change.y;
+
+  new_window = *window;
+
+  /* First constrain horizontal */
+  if (change.x)
+    {
+      edge_snapping_constrain_horizontal (self, change.x, &self->workarea, &new_window);
+      if (gdk_rectangle_equal (&new_window, window))
+        edge_snapping_constrain_horizontal (self, change.x, &self->geometry, &new_window);
+    }
+
+  /* Now constrain veritcally */
+  if (change.y)
+    {
+      edge_snapping_constrain_vertical (self, change.y, &self->workarea, &new_window, FALSE);
+      if (new_window.y == window->y)
+        edge_snapping_constrain_vertical (self, change.y, &self->geometry, &new_window, TRUE);
+    }
+
+  /* If the window is not placed in the monitor at all, then we need to
+   * just move the window onto the new screen using the original offset
+   * of the pointer within the window.
+   */
+  if (!gdk_rectangle_intersect (&self->geometry, &new_window, &overlap))
+    {
+      new_window.x = pointer_position->x - self->pointer_offset_in_window.x;
+      new_window.y = pointer_position->y - self->pointer_offset_in_window.y;
+    }
+
+  *window = new_window;
+}
+
+void
+_edge_snapping_set_monitor (EdgeSnapping       *self,
+                            const GdkRectangle *geometry,
+                            const GdkRectangle *workarea)
+{
+  g_assert (self != NULL);
+  g_assert (geometry != NULL);
+  g_assert (workarea != NULL);
+
+  self->geometry = *geometry;
+  self->workarea = *workarea;
+}
diff --git a/gdk/macos/edgesnapping.h b/gdk/macos/edgesnapping.h
new file mode 100644
index 0000000000..8769ea7e47
--- /dev/null
+++ b/gdk/macos/edgesnapping.h
@@ -0,0 +1,50 @@
+/* edgesnapping.h
+ *
+ * Copyright © 2020 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __EDGE_SNAPPING_H__
+#define __EDGE_SNAPPING_H__
+
+#include "gdktypes.h"
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+  GdkRectangle geometry;
+  GdkRectangle workarea;
+  GdkPoint     last_pointer_position;
+  GdkPoint     pointer_offset_in_window;
+} EdgeSnapping;
+
+void _edge_snapping_init        (EdgeSnapping       *self,
+                                 const GdkRectangle *geometry,
+                                 const GdkRectangle *workarea,
+                                 const GdkPoint     *pointer_position,
+                                 const GdkRectangle *window);
+void _edge_snapping_motion      (EdgeSnapping       *self,
+                                 const GdkPoint     *pointer_position,
+                                 GdkRectangle       *window);
+void _edge_snapping_set_monitor (EdgeSnapping       *self,
+                                 const GdkRectangle *geometry,
+                                 const GdkRectangle *workarea);
+
+G_END_DECLS
+
+#endif /* __EDGE_SNAPPING_H__ */
diff --git a/gdk/macos/gdkmacosdisplay-private.h b/gdk/macos/gdkmacosdisplay-private.h
index c9089f0f52..d731f8dce8 100644
--- a/gdk/macos/gdkmacosdisplay-private.h
+++ b/gdk/macos/gdkmacosdisplay-private.h
@@ -72,6 +72,8 @@ struct _GdkMacosDisplay
   int height;
   int min_x;
   int min_y;
+  int max_x;
+  int max_y;
 };
 
 struct _GdkMacosDisplayClass
diff --git a/gdk/macos/gdkmacosdisplay.c b/gdk/macos/gdkmacosdisplay.c
index 8aedcee9ed..58740aa2d9 100644
--- a/gdk/macos/gdkmacosdisplay.c
+++ b/gdk/macos/gdkmacosdisplay.c
@@ -184,30 +184,26 @@ gdk_macos_display_update_bounds (GdkMacosDisplay *self)
 {
   GDK_BEGIN_MACOS_ALLOC_POOL;
 
-  int max_x = G_MININT;
-  int max_y = G_MININT;
-
   g_assert (GDK_IS_MACOS_DISPLAY (self));
 
   self->min_x = G_MAXINT;
   self->min_y = G_MAXINT;
 
+  self->max_x = G_MININT;
+  self->max_y = G_MININT;
+
   for (id obj in [NSScreen screens])
     {
-      CGDirectDisplayID screen_id;
-      CGRect geom;
-
-      screen_id = [[[obj deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue];
-      geom = CGDisplayBounds (screen_id);
+      NSRect geom = [(NSScreen *)obj frame];
 
       self->min_x = MIN (self->min_x, geom.origin.x);
       self->min_y = MIN (self->min_y, geom.origin.y);
-      max_x = MAX (max_x, geom.origin.x + geom.size.width);
-      max_y = MAX (max_y, geom.origin.y + geom.size.height);
+      self->max_x = MAX (self->max_x, geom.origin.x + geom.size.width);
+      self->max_y = MAX (self->max_y, geom.origin.y + geom.size.height);
     }
 
-  self->width = max_x - self->min_x;
-  self->height = max_y - self->min_y;
+  self->width = self->max_x - self->min_x;
+  self->height = self->max_y - self->min_y;
 
   GDK_END_MACOS_ALLOC_POOL;
 }
@@ -819,6 +815,7 @@ _gdk_macos_display_get_monitor_at_display_coords (GdkMacosDisplay *self,
   g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (self), NULL);
 
   _gdk_macos_display_from_display_coords (self, x, y, &x, &y);
+
   return _gdk_macos_display_get_monitor_at_coords (self, x, y);
 }
 
diff --git a/gdk/macos/gdkmacosmonitor.c b/gdk/macos/gdkmacosmonitor.c
index 0333872e03..2c3d53b742 100644
--- a/gdk/macos/gdkmacosmonitor.c
+++ b/gdk/macos/gdkmacosmonitor.c
@@ -30,6 +30,7 @@ struct _GdkMacosMonitor
 {
   GdkMonitor        parent_instance;
   CGDirectDisplayID screen_id;
+  NSRect            workarea;
   guint             has_opengl : 1;
 };
 
@@ -47,36 +48,22 @@ gdk_macos_monitor_get_workarea (GdkMonitor   *monitor,
   GDK_BEGIN_MACOS_ALLOC_POOL;
 
   GdkMacosMonitor *self = (GdkMacosMonitor *)monitor;
+  int x,  y;
 
   g_assert (GDK_IS_MACOS_MONITOR (self));
   g_assert (geometry != NULL);
 
-  *geometry = monitor->geometry;
+  x = self->workarea.origin.x;
+  y = self->workarea.origin.y + self->workarea.size.height;
 
-  for (id obj in [NSScreen screens])
-    {
-      CGDirectDisplayID screen_id;
-
-      screen_id = [[[obj deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue];
-
-      if (screen_id == self->screen_id)
-        {
-          NSScreen *screen = (NSScreen *)obj;
-          NSRect visibleFrame = [screen visibleFrame];
-          int x = visibleFrame.origin.x;
-          int y = visibleFrame.origin.y + visibleFrame.size.height;
+  _gdk_macos_display_from_display_coords (GDK_MACOS_DISPLAY (monitor->display),
+                                          x, y,
+                                          &x, &y);
 
-          _gdk_macos_display_from_display_coords (GDK_MACOS_DISPLAY (monitor->display),
-                                                  x, y, &x, &y);
-
-          geometry->x = x;
-          geometry->y = y;
-          geometry->width = visibleFrame.size.width;
-          geometry->height = visibleFrame.size.height;
-
-          break;
-        }
-    }
+  geometry->x = x;
+  geometry->y = y;
+  geometry->width = self->workarea.size.width;
+  geometry->height = self->workarea.size.height;
 
   GDK_END_MACOS_ALLOC_POOL;
 }
@@ -148,23 +135,17 @@ GetSubpixelLayout (CGDirectDisplayID screen_id)
 }
 
 static char *
-GetLocalizedName (CGDirectDisplayID screen_id)
+GetLocalizedName (NSScreen *screen)
 {
   GDK_BEGIN_MACOS_ALLOC_POOL;
 
-  char *name = NULL;
+  NSString *str;
+  char *name;
 
-  for (id obj in [NSScreen screens])
-    {
-      CGDirectDisplayID this_id = [[[obj deviceDescription] objectForKey:@"NSScreenNumber"] 
unsignedIntValue];
+  g_assert (screen);
 
-      if (screen_id == this_id)
-        {
-          NSString *str = [(NSScreen *)obj localizedName];
-          name = g_strdup ([str UTF8String]);
-          break;
-        }
-    }
+  str = [screen localizedName];
+  name = g_strdup ([str UTF8String]);
 
   GDK_END_MACOS_ALLOC_POOL;
 
@@ -178,17 +159,38 @@ GetConnectorName (CGDirectDisplayID screen_id)
   return g_strdup_printf ("unit-%u", unit);
 }
 
+static NSScreen *
+find_screen (CGDirectDisplayID screen_id)
+{
+  GDK_BEGIN_MACOS_ALLOC_POOL;
+
+  NSScreen *screen = NULL;
+
+  for (id obj in [NSScreen screens])
+    {
+      if (screen_id == [[[obj deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue])
+        {
+          screen = (NSScreen *)obj;
+          break;
+        }
+    }
+
+  GDK_END_MACOS_ALLOC_POOL;
+
+  return screen;
+}
+
 gboolean
 _gdk_macos_monitor_reconfigure (GdkMacosMonitor *self)
 {
   GdkSubpixelLayout subpixel_layout;
   CGDisplayModeRef mode;
   GdkMacosDisplay *display;
+  NSScreen *screen;
   GdkRectangle geom;
   gboolean has_opengl;
   CGSize size;
   CGRect bounds;
-  CGRect main_bounds;
   size_t width;
   size_t pixel_width;
   gchar *connector;
@@ -202,17 +204,17 @@ _gdk_macos_monitor_reconfigure (GdkMacosMonitor *self)
 
   display = GDK_MACOS_DISPLAY (GDK_MONITOR (self)->display);
 
-  if (!(mode = CGDisplayCopyDisplayMode (self->screen_id)))
+  if (!(screen = find_screen (self->screen_id)) ||
+      !(mode = CGDisplayCopyDisplayMode (self->screen_id)))
     return FALSE;
 
   size = CGDisplayScreenSize (self->screen_id);
-  bounds = CGDisplayBounds (self->screen_id);
-  main_bounds = CGDisplayBounds (CGMainDisplayID ());
+  bounds = [screen frame];
   width = CGDisplayModeGetWidth (mode);
   pixel_width = CGDisplayModeGetPixelWidth (mode);
   has_opengl = CGDisplayUsesOpenGLAcceleration (self->screen_id);
   subpixel_layout = GetSubpixelLayout (self->screen_id);
-  name = GetLocalizedName (self->screen_id);
+  name = GetLocalizedName (screen);
   connector = GetConnectorName (self->screen_id);
 
   if (width != 0 && pixel_width != 0)
@@ -221,19 +223,8 @@ _gdk_macos_monitor_reconfigure (GdkMacosMonitor *self)
   width_mm = size.width;
   height_mm = size.height;
 
-  /* This requires that the display bounds have been updated before the monitor
-   * is reconfigured. Note that the result from CGDisplayBounds() is not exactly
-   * the same as display coordinates.
-   *
-   * As the docs say:
-   *
-   *  Returns the bounds of a display in the global display coordinate space.
-   *
-   *  The bounds of the display, expressed as a rectangle in the global display
-   *  coordinate space (relative to the upper-left corner of the main display).
-   */
   geom.x = bounds.origin.x - display->min_x;
-  geom.y = bounds.origin.y - display->min_y;
+  geom.y = display->height - bounds.origin.y - bounds.size.height + display->min_y;
   geom.width = bounds.size.width;
   geom.height = bounds.size.height;
 
@@ -253,6 +244,8 @@ _gdk_macos_monitor_reconfigure (GdkMacosMonitor *self)
   gdk_monitor_set_refresh_rate (GDK_MONITOR (self), refresh_rate);
   gdk_monitor_set_subpixel_layout (GDK_MONITOR (self), GDK_SUBPIXEL_LAYOUT_UNKNOWN);
 
+  self->workarea = [screen visibleFrame];
+
   /* We might be able to use this at some point to change which GSK renderer
    * we use for surfaces on this monitor.  For example, it might be better
    * to use cairo if we cannot use OpenGL (or it would be software) anyway.
diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c
index ce84d36d9f..019fa14de1 100644
--- a/gdk/macos/gdkmacossurface.c
+++ b/gdk/macos/gdkmacossurface.c
@@ -25,6 +25,7 @@
 
 #import "GdkMacosCairoView.h"
 
+#include "gdkdisplay.h"
 #include "gdkframeclockidleprivate.h"
 #include "gdkinternals.h"
 #include "gdksurfaceprivate.h"
@@ -895,7 +896,7 @@ _gdk_macos_surface_monitor_changed (GdkMacosSurface *self)
 GdkMonitor *
 _gdk_macos_surface_get_best_monitor (GdkMacosSurface *self)
 {
-  GdkMonitor *best;
+  GdkMonitor *best = NULL;
   GdkDisplay *display;
   GdkRectangle rect;
   guint n_monitors;
@@ -911,7 +912,6 @@ _gdk_macos_surface_get_best_monitor (GdkMacosSurface *self)
   rect.width = GDK_SURFACE (self)->width;
   rect.height = GDK_SURFACE (self)->height;
 
-  best = gdk_display_get_monitor (display, 0);
 
   for (guint i = 0; i < n_monitors; i++)
     {
diff --git a/gdk/macos/meson.build b/gdk/macos/meson.build
index c10a3bb5b8..cecbdbe97f 100644
--- a/gdk/macos/meson.build
+++ b/gdk/macos/meson.build
@@ -1,4 +1,6 @@
 gdk_macos_sources = files([
+  'edgesnapping.c',
+
   'gdkdisplaylinksource.c',
   'GdkMacosWindow.c',
   'gdkmacoscairocontext.c',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]