3 * Copyright (C) 2005 Imendio AB
4 * Copyright (C) 2009,2010 Kristian Rietveld <kris@gtk.org>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
24 #include "gdkprivate-quartz.h"
27 /* A couple of notes about this file are in order. In GDK, a
28 * GdkScreen can contain multiple monitors. A GdkScreen has an
29 * associated root window, in which the monitors are placed. The
30 * root window "spans" all monitors. The origin is at the top-left
31 * corner of the root window.
33 * Cocoa works differently. The system has a "screen" (NSScreen) for
34 * each monitor that is connected (note the conflicting definitions
35 * of screen). The screen containing the menu bar is screen 0 and the
36 * bottom-left corner of this screen is the origin of the "monitor
37 * coordinate space". All other screens are positioned according to this
38 * origin. If the menu bar is on a secondary screen (for example on
39 * a monitor hooked up to a laptop), then this screen is screen 0 and
40 * other monitors will be positioned according to the "secondary screen".
41 * The main screen is the monitor that shows the window that is currently
42 * active (has focus), the position of the menu bar does not have influence
45 * Upon start up and changes in the layout of screens, we calculate the
46 * size of the GdkScreen root window that is needed to be able to place
47 * all monitors in the root window. Once that size is known, we iterate
48 * over the monitors and translate their Cocoa position to a position
49 * in the root window of the GdkScreen. This happens below in the
50 * function gdk_quartz_screen_calculate_layout().
52 * A Cocoa coordinate is always relative to the origin of the monitor
53 * coordinate space. Such coordinates are mapped to their respective
54 * position in the GdkScreen root window (_gdk_quartz_window_xy_to_gdk_xy)
55 * and vice versa (_gdk_quartz_window_gdk_xy_to_xy). Both functions can
56 * be found in gdkwindow-quartz.c. Note that Cocoa coordinates can have
57 * negative values (in case a monitor is located left or below of screen 0),
58 * but GDK coordinates can *not*!
61 static void gdk_quartz_screen_dispose (GObject *object);
62 static void gdk_quartz_screen_finalize (GObject *object);
63 static void gdk_quartz_screen_calculate_layout (GdkQuartzScreen *screen);
65 static void display_reconfiguration_callback (CGDirectDisplayID display,
66 CGDisplayChangeSummaryFlags flags,
69 G_DEFINE_TYPE (GdkQuartzScreen, gdk_quartz_screen, GDK_TYPE_SCREEN);
72 gdk_quartz_screen_init (GdkQuartzScreen *quartz_screen)
74 GdkScreen *screen = GDK_SCREEN (quartz_screen);
77 nsscreen = [[NSScreen screens] objectAtIndex:0];
78 gdk_screen_set_resolution (screen,
79 72.0 * [nsscreen userSpaceScaleFactor]);
81 gdk_quartz_screen_calculate_layout (quartz_screen);
83 CGDisplayRegisterReconfigurationCallback (display_reconfiguration_callback,
86 quartz_screen->emit_monitors_changed = FALSE;
90 gdk_quartz_screen_dispose (GObject *object)
92 GdkQuartzScreen *screen = GDK_QUARTZ_SCREEN (object);
94 if (screen->screen_changed_id)
96 g_source_remove (screen->screen_changed_id);
97 screen->screen_changed_id = 0;
100 CGDisplayRemoveReconfigurationCallback (display_reconfiguration_callback,
103 G_OBJECT_CLASS (gdk_quartz_screen_parent_class)->dispose (object);
107 gdk_quartz_screen_screen_rects_free (GdkQuartzScreen *screen)
109 screen->n_screens = 0;
111 if (screen->screen_rects)
113 g_free (screen->screen_rects);
114 screen->screen_rects = NULL;
119 gdk_quartz_screen_finalize (GObject *object)
121 GdkQuartzScreen *screen = GDK_QUARTZ_SCREEN (object);
123 gdk_quartz_screen_screen_rects_free (screen);
128 gdk_quartz_screen_calculate_layout (GdkQuartzScreen *screen)
134 GDK_QUARTZ_ALLOC_POOL;
136 gdk_quartz_screen_screen_rects_free (screen);
138 array = [NSScreen screens];
146 /* We determine the minimum and maximum x and y coordinates
147 * covered by the monitors. From this we can deduce the width
148 * and height of the root screen.
150 for (i = 0; i < [array count]; i++)
152 NSRect rect = [[array objectAtIndex:i] frame];
154 screen->min_x = MIN (screen->min_x, rect.origin.x);
155 max_x = MAX (max_x, rect.origin.x + rect.size.width);
157 screen->min_y = MIN (screen->min_y, rect.origin.y);
158 max_y = MAX (max_y, rect.origin.y + rect.size.height);
161 screen->width = max_x - screen->min_x;
162 screen->height = max_y - screen->min_y;
164 screen->n_screens = [array count];
165 screen->screen_rects = g_new0 (GdkRectangle, screen->n_screens);
167 for (i = 0; i < screen->n_screens; i++)
172 nsscreen = [array objectAtIndex:i];
173 rect = [nsscreen frame];
175 screen->screen_rects[i].x = rect.origin.x - screen->min_x;
176 screen->screen_rects[i].y
177 = screen->height - (rect.origin.y + rect.size.height) + screen->min_y;
178 screen->screen_rects[i].width = rect.size.width;
179 screen->screen_rects[i].height = rect.size.height;
182 GDK_QUARTZ_RELEASE_POOL;
186 _gdk_quartz_screen_update_window_sizes (GdkScreen *screen)
188 GList *windows, *list;
190 /* The size of the root window is so that it can contain all
191 * monitors attached to this machine. The monitors are laid out
192 * within this root window. We calculate the size of the root window
193 * and the positions of the different monitors in gdkscreen-quartz.c.
195 * This data is updated when the monitor configuration is changed.
198 /* FIXME: At some point, fetch the root window from GdkScreen. But
199 * on OS X will we only have a single root window anyway.
203 _gdk_root->abs_x = 0;
204 _gdk_root->abs_y = 0;
205 _gdk_root->width = gdk_screen_get_width (screen);
206 _gdk_root->height = gdk_screen_get_height (screen);
208 windows = gdk_screen_get_toplevel_windows (screen);
210 for (list = windows; list; list = list->next)
211 _gdk_quartz_window_update_position (list->data);
213 g_list_free (windows);
217 process_display_reconfiguration (GdkQuartzScreen *screen)
221 width = gdk_screen_get_width (GDK_SCREEN (screen));
222 height = gdk_screen_get_height (GDK_SCREEN (screen));
224 gdk_quartz_screen_calculate_layout (GDK_QUARTZ_SCREEN (screen));
226 _gdk_quartz_screen_update_window_sizes (GDK_SCREEN (screen));
228 if (screen->emit_monitors_changed)
230 g_signal_emit_by_name (screen, "monitors-changed");
231 screen->emit_monitors_changed = FALSE;
234 if (width != gdk_screen_get_width (GDK_SCREEN (screen))
235 || height != gdk_screen_get_height (GDK_SCREEN (screen)))
236 g_signal_emit_by_name (screen, "size-changed");
240 screen_changed_idle (gpointer data)
242 GdkQuartzScreen *screen = data;
244 process_display_reconfiguration (data);
246 screen->screen_changed_id = 0;
252 display_reconfiguration_callback (CGDirectDisplayID display,
253 CGDisplayChangeSummaryFlags flags,
256 GdkQuartzScreen *screen = userInfo;
258 if (flags & kCGDisplayBeginConfigurationFlag)
260 /* Ignore the begin configuration signal. */
265 /* We save information about the changes, so we can emit
266 * ::monitors-changed when appropriate. This signal must be
267 * emitted when the number, size of position of one of the
270 if (flags & kCGDisplayMovedFlag
271 || flags & kCGDisplayAddFlag
272 || flags & kCGDisplayRemoveFlag
273 || flags & kCGDisplayEnabledFlag
274 || flags & kCGDisplayDisabledFlag)
275 screen->emit_monitors_changed = TRUE;
277 /* At this point Cocoa does not know about the new screen data
278 * yet, so we delay our refresh into an idle handler.
280 if (!screen->screen_changed_id)
281 screen->screen_changed_id = gdk_threads_add_idle (screen_changed_idle,
287 gdk_quartz_screen_get_display (GdkScreen *screen)
293 gdk_quartz_screen_get_root_window (GdkScreen *screen)
299 gdk_quartz_screen_get_number (GdkScreen *screen)
305 _gdk_windowing_substitute_screen_number (const gchar *display_name,
308 if (screen_number != 0)
311 return g_strdup (display_name);
315 gdk_quartz_screen_get_width (GdkScreen *screen)
317 return GDK_QUARTZ_SCREEN (screen)->width;
321 gdk_quartz_screen_get_height (GdkScreen *screen)
323 return GDK_QUARTZ_SCREEN (screen)->height;
327 get_mm_from_pixels (NSScreen *screen, int pixels)
329 /* userSpaceScaleFactor is in "pixels per point",
330 * 72 is the number of points per inch,
331 * and 25.4 is the number of millimeters per inch.
333 #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_3
334 float dpi = [screen userSpaceScaleFactor] * 72.0;
336 float dpi = 96.0 / 72.0;
339 return (pixels / dpi) * 25.4;
343 get_nsscreen_for_monitor (gint monitor_num)
348 GDK_QUARTZ_ALLOC_POOL;
350 array = [NSScreen screens];
351 screen = [array objectAtIndex:monitor_num];
353 GDK_QUARTZ_RELEASE_POOL;
359 gdk_quartz_screen_get_width_mm (GdkScreen *screen)
361 return get_mm_from_pixels (get_nsscreen_for_monitor (0),
362 GDK_QUARTZ_SCREEN (screen)->width);
366 gdk_quartz_screen_get_height_mm (GdkScreen *screen)
368 return get_mm_from_pixels (get_nsscreen_for_monitor (0),
369 GDK_QUARTZ_SCREEN (screen)->height);
373 gdk_quartz_screen_get_n_monitors (GdkScreen *screen)
375 return GDK_QUARTZ_SCREEN (screen)->n_screens;
379 gdk_quartz_screen_get_primary_monitor (GdkScreen *screen)
385 gdk_quartz_screen_get_monitor_width_mm (GdkScreen *screen,
388 return get_mm_from_pixels (get_nsscreen_for_monitor (monitor_num),
389 GDK_QUARTZ_SCREEN (screen)->screen_rects[monitor_num].width);
393 gdk_quartz_screen_get_monitor_height_mm (GdkScreen *screen,
396 return get_mm_from_pixels (get_nsscreen_for_monitor (monitor_num),
397 GDK_QUARTZ_SCREEN (screen)->screen_rects[monitor_num].height);
401 gdk_quartz_screen_get_monitor_plug_name (GdkScreen *screen,
404 /* FIXME: Is there some useful name we could use here? */
409 gdk_quartz_screen_get_monitor_geometry (GdkScreen *screen,
413 *dest = GDK_QUARTZ_SCREEN (screen)->screen_rects[monitor_num];
417 gdk_quartz_screen_get_monitor_workarea (GdkScreen *screen,
421 GdkQuartzScreen *quartz_screen = GDK_QUARTZ_SCREEN (screen);
426 GDK_QUARTZ_ALLOC_POOL;
428 array = [NSScreen screens];
429 nsscreen = [array objectAtIndex:monitor_num];
430 rect = [nsscreen visibleFrame];
432 dest->x = rect.origin.x - quartz_screen->min_x;
433 dest->y = quartz_screen->height - (rect.origin.y + rect.size.height) + quartz_screen->min_y;
434 dest->width = rect.size.width;
435 dest->height = rect.size.height;
437 GDK_QUARTZ_RELEASE_POOL;
441 gdk_quartz_screen_make_display_name (GdkScreen *screen)
443 return g_strdup (gdk_display_get_name (_gdk_display));
447 gdk_quartz_screen_get_active_window (GdkScreen *screen)
453 gdk_quartz_screen_get_window_stack (GdkScreen *screen)
459 gdk_quartz_screen_is_composited (GdkScreen *screen)
465 gdk_quartz_screen_class_init (GdkQuartzScreenClass *klass)
467 GObjectClass *object_class = G_OBJECT_CLASS (klass);
468 GdkScreenClass *screen_class = GDK_SCREEN_CLASS (klass);
470 object_class->dispose = gdk_quartz_screen_dispose;
471 object_class->finalize = gdk_quartz_screen_finalize;
473 screen_class->get_display = gdk_quartz_screen_get_display;
474 screen_class->get_width = gdk_quartz_screen_get_width;
475 screen_class->get_height = gdk_quartz_screen_get_height;
476 screen_class->get_width_mm = gdk_quartz_screen_get_width_mm;
477 screen_class->get_height_mm = gdk_quartz_screen_get_height_mm;
478 screen_class->get_number = gdk_quartz_screen_get_number;
479 screen_class->get_root_window = gdk_quartz_screen_get_root_window;
480 screen_class->get_n_monitors = gdk_quartz_screen_get_n_monitors;
481 screen_class->get_primary_monitor = gdk_quartz_screen_get_primary_monitor;
482 screen_class->get_monitor_width_mm = gdk_quartz_screen_get_monitor_width_mm;
483 screen_class->get_monitor_height_mm = gdk_quartz_screen_get_monitor_height_mm;
484 screen_class->get_monitor_plug_name = gdk_quartz_screen_get_monitor_plug_name;
485 screen_class->get_monitor_geometry = gdk_quartz_screen_get_monitor_geometry;
486 screen_class->get_monitor_workarea = gdk_quartz_screen_get_monitor_workarea;
487 screen_class->is_composited = gdk_quartz_screen_is_composited;
488 screen_class->make_display_name = gdk_quartz_screen_make_display_name;
489 screen_class->get_active_window = gdk_quartz_screen_get_active_window;
490 screen_class->get_window_stack = gdk_quartz_screen_get_window_stack;
491 screen_class->broadcast_client_message = _gdk_quartz_screen_broadcast_client_message;
492 screen_class->get_setting = _gdk_quartz_screen_get_setting;
493 screen_class->get_rgba_visual = _gdk_quartz_screen_get_rgba_visual;
494 screen_class->get_system_visual = _gdk_quartz_screen_get_system_visual;
495 screen_class->visual_get_best_depth = _gdk_quartz_screen_visual_get_best_depth;
496 screen_class->visual_get_best_type = _gdk_quartz_screen_visual_get_best_type;
497 screen_class->visual_get_best = _gdk_quartz_screen_visual_get_best;
498 screen_class->visual_get_best_with_depth = _gdk_quartz_screen_visual_get_best_with_depth;
499 screen_class->visual_get_best_with_type = _gdk_quartz_screen_visual_get_best_with_type;
500 screen_class->visual_get_best_with_both = _gdk_quartz_screen_visual_get_best_with_both;
501 screen_class->query_depths = _gdk_quartz_screen_query_depths;
502 screen_class->query_visual_types = _gdk_quartz_screen_query_visual_types;
503 screen_class->list_visuals = _gdk_quartz_screen_list_visuals;