[gtk/gtk-3-24] Change computation of coordinates.



commit 3bd9a3453fb6149a3fbffc73a2c086994b2697f1
Author: John Ralls <jralls ceridwen us>
Date:   Sat Feb 23 14:35:42 2019 -0800

    Change computation of coordinates.
    
    Transform GdkQuartzMonitor geometry to Gdk coordinate system.
    Move computation of Display geometry from GdkQuartzScreen to
    GdkQuartzDisplay and use AppKit coordinates.
    
    Closes https://gitlab.gnome.org/GNOME/gtk/issues/1593

 gdk/quartz/gdkdisplay-quartz.c | 140 +++++++++++++++++++++++++++++++++--------
 gdk/quartz/gdkdisplay-quartz.h |   2 +
 gdk/quartz/gdkmonitor-quartz.c |  29 ++++++---
 gdk/quartz/gdkmonitor-quartz.h |   1 -
 gdk/quartz/gdkscreen-quartz.c  |  60 ++++++------------
 gdk/quartz/gdkscreen-quartz.h  |   6 +-
 gdk/quartz/gdkwindow-quartz.c  |   8 +--
 7 files changed, 163 insertions(+), 83 deletions(-)
---
diff --git a/gdk/quartz/gdkdisplay-quartz.c b/gdk/quartz/gdkdisplay-quartz.c
index 6d6ed0924e..fc81a6de86 100644
--- a/gdk/quartz/gdkdisplay-quartz.c
+++ b/gdk/quartz/gdkdisplay-quartz.c
@@ -32,6 +32,34 @@
 #include "gdkdisplay-quartz.h"
 #include "gdkmonitor-quartz.h"
 
+/* Note about coordinates: There are three coordinate systems at play:
+ *
+ * 1. Core Graphics starts at the origin at the upper right of the
+ * main window (the one with the menu bar when you look at arrangement
+ * in System Preferences>Displays) and increases down and to the
+ * right; up and to the left are negative values of y and x
+ * respectively.
+ *
+ * 2. AppKit (functions beginning with "NS" for NextStep) coordinates
+ * also have their origin at the main window, but it's the *lower*
+ * left corner and coordinates increase up and to the
+ * right. Coordinates below or left of the origin are negative.
+ *
+ * 3. Gdk coordinates origin is at the upper left corner of the
+ * imaginary rectangle enclosing all monitors and like Core Graphics
+ * increase down and to the right. There are no negative coordinates.
+ *
+ * We need to deal with all three because AppKit's NSScreen array is
+ * recomputed with new pointers whenever the monitor arrangement
+ * changes so we can't cache the references it provides. CoreGraphics
+ * screen IDs are constant between reboots so those are what we use to
+ * map GdkMonitors and screens, but the sizes and origins must be
+ * converted to Gdk coordinates to make sense to Gdk and we must
+ * frequently convert between Gdk and AppKit coordinates when
+ * determining the drawable area of a monitor and placing windows and
+ * views (the latter containing our cairo surfaces for drawing on).
+ */
+
 static gint MONITORS_CHANGED = 0;
 
 static void display_reconfiguration_callback (CGDirectDisplayID            display,
@@ -220,32 +248,45 @@ gdk_quartz_display_pop_error_trap (GdkDisplay *display, gboolean ignore)
  */
 
 int
-get_active_displays (CGDirectDisplayID **screens)
+get_active_displays (CGDirectDisplayID **displays)
 {
-  unsigned int displays = 0;
+  unsigned int n_displays = 0;
 
-  CGGetActiveDisplayList (0, NULL, &displays);
-  if (screens)
+  CGGetActiveDisplayList (0, NULL, &n_displays);
+  if (displays)
     {
-      *screens = g_new0 (CGDirectDisplayID, displays);
-      CGGetActiveDisplayList (displays, *screens, &displays);
+      *displays = g_new0 (CGDirectDisplayID, n_displays);
+      CGGetActiveDisplayList (n_displays, *displays, &n_displays);
     }
 
-  return displays;
+  return n_displays;
+}
+
+static inline GdkRectangle
+cgrect_to_gdkrect (CGRect cgrect)
+{
+  GdkRectangle gdkrect = {(int)trunc (cgrect.origin.x),
+                          (int)trunc (cgrect.origin.y),
+                          (int)trunc (cgrect.size.width),
+                          (int)trunc (cgrect.size.height)};
+  return gdkrect;
 }
 
 static void
-configure_monitor (GdkMonitor *monitor)
+configure_monitor (GdkMonitor       *monitor,
+                   GdkQuartzDisplay *display)
 {
   GdkQuartzMonitor *quartz_monitor = GDK_QUARTZ_MONITOR (monitor);
   CGSize disp_size = CGDisplayScreenSize (quartz_monitor->id);
   gint width = (int)trunc (disp_size.width);
   gint height = (int)trunc (disp_size.height);
   CGRect disp_bounds = CGDisplayBounds (quartz_monitor->id);
-  GdkRectangle disp_geometry = {(int)trunc (disp_bounds.origin.x),
-                                (int)trunc (disp_bounds.origin.y),
-                                (int)trunc (disp_bounds.size.width),
-                                (int)trunc (disp_bounds.size.height)};
+  CGRect main_bounds = CGDisplayBounds (CGMainDisplayID());
+  /* Change origin to Gdk coordinates. */
+  disp_bounds.origin.x = disp_bounds.origin.x + display->geometry.origin.x;
+  disp_bounds.origin.y =
+    display->geometry.origin.y - main_bounds.size.height + disp_bounds.origin.y;
+  GdkRectangle disp_geometry = cgrect_to_gdkrect (disp_bounds);
   CGDisplayModeRef mode = CGDisplayCopyDisplayMode (quartz_monitor->id);
   gint refresh_rate = (int)trunc (CGDisplayModeGetRefreshRate (mode));
 
@@ -265,6 +306,42 @@ configure_monitor (GdkMonitor *monitor)
   monitor->subpixel_layout = GDK_SUBPIXEL_LAYOUT_UNKNOWN;
 }
 
+static void
+display_rect (GdkQuartzDisplay *display)
+{
+  uint32_t disp, count = 0;
+  float min_x = 0.0, max_x = 0.0, min_y = 0.0, max_y = 0.0;
+  float min_x_mm = 0.0, max_x_mm = 0.0, min_y_mm = 0.0, max_y_mm = 0.0;
+  float main_height;
+  CGDirectDisplayID *displays;
+
+  count = get_active_displays (&displays);
+  for (disp = 0; disp < count; ++disp)
+    {
+      CGRect bounds = CGDisplayBounds (displays[disp]);
+      CGSize disp_size = CGDisplayScreenSize (displays[disp]);
+      float x_scale = disp_size.width / bounds.size.width;
+      float y_scale = disp_size.height / bounds.size.height;
+      if (disp == 0)
+        main_height = bounds.size.height;
+      min_x = MIN (min_x, bounds.origin.x);
+      min_y = MIN (min_y, bounds.origin.y);
+
+      max_x = MAX (max_x, bounds.origin.x + bounds.size.width);
+      max_y = MAX (max_y, bounds.origin.y + bounds.size.height);
+      min_x_mm = MIN (min_x_mm, bounds.origin.x / x_scale);
+      min_y_mm = MIN (min_y_mm, main_height - (bounds.size.height + bounds.origin.y) / y_scale);
+      max_x_mm = MAX (max_x_mm, (bounds.origin.x + bounds.size.width) / x_scale);
+      max_y_mm = MAX (max_y_mm, (bounds.origin.y + bounds.size.height) / y_scale);
+
+    }
+  g_free (displays);
+  /* Adjusts the origin to AppKit coordinates. */
+  display->geometry = NSMakeRect (-min_x, main_height - min_y,
+                                  max_x - min_x, max_y - min_y);
+  display->size = NSMakeSize (max_x_mm - min_x_mm, max_y_mm - min_y_mm);
+}
+
 static void
 display_reconfiguration_callback (CGDirectDisplayID            cg_display,
                                   CGDisplayChangeSummaryFlags  flags,
@@ -293,7 +370,8 @@ display_reconfiguration_callback (CGDirectDisplayID            cg_display,
           gdk_display_monitor_added (GDK_DISPLAY (display),
                                      GDK_MONITOR (monitor));
         }
-      configure_monitor (GDK_MONITOR (monitor));
+      display_rect (display);
+      configure_monitor (GDK_MONITOR (monitor), display);
     }
   else if (flags & (kCGDisplayRemoveFlag |  kCGDisplayDisabledFlag))
     {
@@ -322,12 +400,13 @@ gdk_quartz_display_get_monitor (GdkDisplay *display,
   CGDirectDisplayID *screens = NULL;
 
   int count = get_active_displays (&screens);
+  GdkMonitor *monitor = NULL;
 
   if (monitor_num >= 0 && monitor_num < count)
-    return g_hash_table_lookup (quartz_display->monitors,
+    monitor = g_hash_table_lookup (quartz_display->monitors,
                                 GINT_TO_POINTER (screens[monitor_num]));
-
-  return NULL;
+  g_free (screens);
+  return monitor;
 }
 
 static GdkMonitor *
@@ -347,10 +426,18 @@ gdk_quartz_display_get_monitor_at_window (GdkDisplay *display,
   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
   NSWindow *nswindow = impl->toplevel;
   NSScreen *screen = [nswindow screen];
-  CGDirectDisplayID id = [[[screen deviceDescription]
-                           objectForKey: @"NSScreenNumber"] unsignedIntValue];
-  return g_hash_table_lookup (GDK_QUARTZ_DISPLAY (display)->monitors,
-                              GINT_TO_POINTER (id));
+  if (screen)
+  {
+       CGDirectDisplayID disp_id =
+            [[[screen deviceDescription]
+              objectForKey: @"NSScreenNumber"] unsignedIntValue];
+       return g_hash_table_lookup (GDK_QUARTZ_DISPLAY (display)->monitors,
+                                   GINT_TO_POINTER (disp_id));
+  }
+  GdkRectangle rect = cgrect_to_gdkrect ([nswindow frame]);
+  return gdk_display_get_monitor_at_point (display,
+                                           rect.x + rect.width/2,
+                                           rect.y + rect.height /2);
 }
 
 G_DEFINE_TYPE (GdkQuartzDisplay, gdk_quartz_display, GDK_TYPE_DISPLAY)
@@ -358,25 +445,26 @@ G_DEFINE_TYPE (GdkQuartzDisplay, gdk_quartz_display, GDK_TYPE_DISPLAY)
 static void
 gdk_quartz_display_init (GdkQuartzDisplay *display)
 {
-  uint32_t max_displays = 0, disp;
+  uint32_t count = 0, disp;
   CGDirectDisplayID *displays;
 
-  CGGetActiveDisplayList (0, NULL, &max_displays);
+  display_rect(display); /* Initialize the overall display coordinates. */
+  count = get_active_displays (&displays);
   display->monitors = g_hash_table_new_full (g_direct_hash, NULL,
                                              NULL, g_object_unref);
-  displays = g_new0 (CGDirectDisplayID, max_displays);
-  CGGetActiveDisplayList (max_displays, displays, &max_displays);
-  for (disp = 0; disp < max_displays; ++disp)
+  for (disp = 0; disp < count; ++disp)
     {
       GdkQuartzMonitor *monitor = g_object_new (GDK_TYPE_QUARTZ_MONITOR,
                                                        "display", display, NULL);
       monitor->id = displays[disp];
       g_hash_table_insert (display->monitors, GINT_TO_POINTER (monitor->id),
                            monitor);
-      configure_monitor (GDK_MONITOR (monitor));
+      configure_monitor (GDK_MONITOR (monitor), display);
     }
+  g_free (displays);
   CGDisplayRegisterReconfigurationCallback (display_reconfiguration_callback,
                                             display);
+  /* So that monitors changed will keep display->geometry syncronized. */
   g_signal_emit (display, MONITORS_CHANGED, 0);
 }
 
diff --git a/gdk/quartz/gdkdisplay-quartz.h b/gdk/quartz/gdkdisplay-quartz.h
index c256d7bca5..d539642f1e 100644
--- a/gdk/quartz/gdkdisplay-quartz.h
+++ b/gdk/quartz/gdkdisplay-quartz.h
@@ -32,6 +32,8 @@ G_BEGIN_DECLS
 struct _GdkQuartzDisplay
 {
   GdkDisplay parent_instance;
+  NSRect geometry; /* In AppKit coordinates. */
+  NSSize size; /* Aggregate size of displays in millimeters. */
   GHashTable *monitors;
 };
 
diff --git a/gdk/quartz/gdkmonitor-quartz.c b/gdk/quartz/gdkmonitor-quartz.c
index b3e0343740..1182816a40 100644
--- a/gdk/quartz/gdkmonitor-quartz.c
+++ b/gdk/quartz/gdkmonitor-quartz.c
@@ -21,7 +21,7 @@
 #include <gio/gio.h>
 
 #include "gdkmonitor-quartz.h"
-#include "gdkscreen-quartz.h"
+#include "gdkdisplay-quartz.h"
 
 
 G_DEFINE_TYPE (GdkQuartzMonitor, gdk_quartz_monitor, GDK_TYPE_MONITOR)
@@ -30,19 +30,30 @@ static void
 gdk_quartz_monitor_get_workarea (GdkMonitor   *monitor,
                                  GdkRectangle *dest)
 {
-  GdkQuartzScreen *quartz_screen = GDK_QUARTZ_SCREEN(gdk_display_get_default_screen (monitor->display));
-  GdkQuartzMonitor *quartz_monitor = GDK_QUARTZ_MONITOR(monitor);
-
   GDK_QUARTZ_ALLOC_POOL;
 
   NSArray *array = [NSScreen screens];
-  if (quartz_monitor->monitor_num < [array count])
+  NSScreen* screen;
+  for (id obj in array)
     {
-      NSScreen *screen = [array objectAtIndex:quartz_monitor->monitor_num];
-      NSRect rect = [screen visibleFrame];
+      CGDirectDisplayID screen_id =
+        [[[obj deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue];
+      GdkQuartzMonitor *q_mon = GDK_QUARTZ_MONITOR (monitor);
+      if (screen_id == q_mon->id)
+        {
+          screen = obj;
+          break;
+        }
+    }
 
-      dest->x = rect.origin.x - quartz_screen->min_x;
-      dest->y = quartz_screen->height - (rect.origin.y + rect.size.height) + quartz_screen->min_y;
+  if (screen)
+    {
+      GdkQuartzDisplay *display =
+        GDK_QUARTZ_DISPLAY (gdk_monitor_get_display (monitor));
+      NSRect rect = [screen visibleFrame];
+      dest->x = (int)trunc (display->geometry.origin.x + rect.origin.x);
+      dest->y = (int)trunc (display->geometry.origin.y -
+                            rect.origin.y - rect.size.height);
       dest->width = rect.size.width;
       dest->height = rect.size.height;
     }
diff --git a/gdk/quartz/gdkmonitor-quartz.h b/gdk/quartz/gdkmonitor-quartz.h
index 75dc9c8b12..86811b2380 100644
--- a/gdk/quartz/gdkmonitor-quartz.h
+++ b/gdk/quartz/gdkmonitor-quartz.h
@@ -29,7 +29,6 @@
 struct _GdkQuartzMonitor
 {
   GdkMonitor parent;
-  gint monitor_num;
   CGDirectDisplayID id;
 };
 
diff --git a/gdk/quartz/gdkscreen-quartz.c b/gdk/quartz/gdkscreen-quartz.c
index cd9d6649cb..ed2a30491a 100644
--- a/gdk/quartz/gdkscreen-quartz.c
+++ b/gdk/quartz/gdkscreen-quartz.c
@@ -60,11 +60,12 @@
  * but GDK coordinates can *not*!
  */
 
-static void  gdk_quartz_screen_dispose          (GObject         *object);
-static void  gdk_quartz_screen_finalize         (GObject         *object);
-static void  gdk_quartz_screen_calculate_layout (GdkQuartzScreen *screen);
-static void  gdk_quartz_screen_reconfigure      (GdkQuartzDisplay *dispplay,
-                                                 GdkQuartzScreen *screen);
+static void  gdk_quartz_screen_dispose          (GObject          *object);
+static void  gdk_quartz_screen_finalize         (GObject          *object);
+static void  gdk_quartz_screen_calculate_layout (GdkQuartzScreen  *screen,
+                                                 GdkQuartzDisplay *display);
+static void  gdk_quartz_screen_reconfigure      (GdkQuartzDisplay *display,
+                                                 GdkQuartzScreen  *screen);
 
 static const double dpi = 72.0;
 
@@ -88,7 +89,7 @@ gdk_quartz_screen_init (GdkQuartzScreen *quartz_screen)
                     G_CALLBACK (gdk_quartz_screen_reconfigure), quartz_screen);
   /* The first monitors-changed should have fired already. */
   _gdk_screen_set_resolution (screen, dpi);
-  gdk_quartz_screen_calculate_layout (quartz_screen);
+  gdk_quartz_screen_calculate_layout (quartz_screen, NULL);
   quartz_screen->emit_monitors_changed = FALSE;
 }
 
@@ -118,45 +119,24 @@ gdk_quartz_screen_finalize (GObject *object)
 @end
 
 static void
-gdk_quartz_screen_calculate_layout (GdkQuartzScreen *screen)
+gdk_quartz_screen_calculate_layout (GdkQuartzScreen *screen,
+                                    GdkQuartzDisplay *display)
 {
   int i, monitors;
   int max_x, max_y;
-  GdkDisplay *display = gdk_screen_get_display (GDK_SCREEN (screen));
-
-  screen->width = 0;
-  screen->height = 0;
-  screen->min_x = 0;
-  screen->min_y = 0;
-  max_x = max_y = 0;
-  screen->mm_width = 0;
-  screen->mm_height = 0;
-
-  /* We determine the minimum and maximum x and y coordinates
-   * covered by the monitors.  From this we can deduce the width
-   * and height of the root screen.
-   */
-  monitors = gdk_display_get_n_monitors (display);
-  for (i = 0; i < monitors; ++i)
-    {
-      GdkQuartzMonitor *monitor =
-           GDK_QUARTZ_MONITOR (gdk_display_get_monitor (display, i));
-      GdkRectangle rect;
 
-      gdk_monitor_get_geometry (GDK_MONITOR (monitor), &rect);
-      screen->min_x = MIN (screen->min_x, rect.x);
-      max_x = MAX (max_x, rect.x + rect.width);
+  if (!display)
+    display = GDK_QUARTZ_DISPLAY (gdk_screen_get_display (GDK_SCREEN (screen)));
 
-      screen->min_y = MIN (screen->min_y, rect.y);
-      max_y = MAX (max_y, rect.y + rect.height);
+/* Display geometry is the origin and size in AppKit coordinates. AppKit computes */
+  screen->width = (int)trunc (display->geometry.size.width);
+  screen->height = (int)trunc (display->geometry.size.height);
+  screen->orig_x = -(int)trunc (display->geometry.origin.x);
+  screen->orig_y = (int)trunc (display->geometry.origin.y);
+  screen->mm_width = (int)trunc (display->size.width);
+  screen->mm_height = (int)trunc (display->size.height);
 
-      screen->mm_height += GDK_MONITOR (monitor)->height_mm;
-      screen->mm_width += GDK_MONITOR (monitor)->width_mm;
-    }
-
-  screen->width = max_x - screen->min_x;
-  screen->height = max_y - screen->min_y;
-}
+ }
 
 void
 _gdk_quartz_screen_update_window_sizes (GdkScreen *screen)
@@ -201,7 +181,7 @@ gdk_quartz_screen_reconfigure (GdkQuartzDisplay *display, GdkQuartzScreen *scree
   width = gdk_screen_get_width (GDK_SCREEN (screen));
   height = gdk_screen_get_height (GDK_SCREEN (screen));
 
-  gdk_quartz_screen_calculate_layout (GDK_QUARTZ_SCREEN (screen));
+  gdk_quartz_screen_calculate_layout (screen, display);
 
   _gdk_quartz_screen_update_window_sizes (GDK_SCREEN (screen));
 
diff --git a/gdk/quartz/gdkscreen-quartz.h b/gdk/quartz/gdkscreen-quartz.h
index 99c2c4fe88..6b8230f65f 100644
--- a/gdk/quartz/gdkscreen-quartz.h
+++ b/gdk/quartz/gdkscreen-quartz.h
@@ -29,9 +29,9 @@ struct _GdkQuartzScreen
 
   GdkDisplay *display;
 
-  /* Origin of "root window" in Cocoa coordinates */
-  gint min_x;
-  gint min_y;
+  /* Origin of "root window" in AppKit coordinates */
+  gint orig_x;
+  gint orig_y;
 
   gint width;
   gint height;
diff --git a/gdk/quartz/gdkwindow-quartz.c b/gdk/quartz/gdkwindow-quartz.c
index 302598a090..1011c9fb51 100644
--- a/gdk/quartz/gdkwindow-quartz.c
+++ b/gdk/quartz/gdkwindow-quartz.c
@@ -609,10 +609,10 @@ _gdk_quartz_window_gdk_xy_to_xy (gint  gdk_x,
   GdkQuartzScreen *screen_quartz = GDK_QUARTZ_SCREEN (_gdk_screen);
 
   if (ns_y)
-    *ns_y = screen_quartz->height - gdk_y + screen_quartz->min_y;
+    *ns_y = screen_quartz->orig_y - gdk_y;
 
   if (ns_x)
-    *ns_x = gdk_x + screen_quartz->min_x;
+    *ns_x = gdk_x - screen_quartz->orig_x;
 }
 
 void
@@ -624,10 +624,10 @@ _gdk_quartz_window_xy_to_gdk_xy (gint  ns_x,
   GdkQuartzScreen *screen_quartz = GDK_QUARTZ_SCREEN (_gdk_screen);
 
   if (gdk_y)
-    *gdk_y = screen_quartz->height - ns_y + screen_quartz->min_y;
+    *gdk_y = screen_quartz->orig_y - ns_y;
 
   if (gdk_x)
-    *gdk_x = ns_x - screen_quartz->min_x;
+    *gdk_x = ns_x + screen_quartz->orig_x;
 }
 
 void


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