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 #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; + if (!display) + display = GDK_QUARTZ_DISPLAY (gdk_screen_get_display (GDK_SCREEN (screen))); - /* 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; +/* 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); - 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); - - screen->min_y = MIN (screen->min_y, rect.y); - max_y = MAX (max_y, rect.y + rect.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