Commit 3bd9a345 authored by John Ralls's avatar John Ralls

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 #1593
parent 28848176
......@@ -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);
}
......
......@@ -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;
};
......
......@@ -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;
}
......
......@@ -29,7 +29,6 @@
struct _GdkQuartzMonitor
{
GdkMonitor parent;
gint monitor_num;
CGDirectDisplayID id;
};
......
......@@ -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));
......
......@@ -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;
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment