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, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
26 #include "gdkprivate-quartz.h"
29 /* A couple of notes about this file are in order. In GDK, a
30 * GdkScreen can contain multiple monitors. A GdkScreen has an
31 * associated root window, in which the monitors are placed. The
32 * root window "spans" all monitors. The origin is at the top-left
33 * corner of the root window.
35 * Cocoa works differently. The system has a "screen" (NSScreen) for
36 * each monitor that is connected (note the conflicting definitions
37 * of screen). The screen containing the menu bar is screen 0 and the
38 * bottom-left corner of this screen is the origin of the "monitor
39 * coordinate space". All other screens are positioned according to this
40 * origin. If the menu bar is on a secondary screen (for example on
41 * a monitor hooked up to a laptop), then this screen is screen 0 and
42 * other monitors will be positioned according to the "secondary screen".
43 * The main screen is the monitor that shows the window that is currently
44 * active (has focus), the position of the menu bar does not have influence
47 * Upon start up and changes in the layout of screens, we calculate the
48 * size of the GdkScreen root window that is needed to be able to place
49 * all monitors in the root window. Once that size is known, we iterate
50 * over the monitors and translate their Cocoa position to a position
51 * in the root window of the GdkScreen. This happens below in the
52 * function gdk_quartz_screen_calculate_layout().
54 * A Cocoa coordinate is always relative to the origin of the monitor
55 * coordinate space. Such coordinates are mapped to their respective
56 * position in the GdkScreen root window (_gdk_quartz_window_xy_to_gdk_xy)
57 * and vice versa (_gdk_quartz_window_gdk_xy_to_xy). Both functions can
58 * be found in gdkwindow-quartz.c. Note that Cocoa coordinates can have
59 * negative values (in case a monitor is located left or below of screen 0),
60 * but GDK coordinates can *not*!
63 static void gdk_quartz_screen_dispose (GObject *object);
64 static void gdk_quartz_screen_finalize (GObject *object);
65 static void gdk_quartz_screen_calculate_layout (GdkQuartzScreen *screen);
67 static void display_reconfiguration_callback (CGDirectDisplayID display,
68 CGDisplayChangeSummaryFlags flags,
71 G_DEFINE_TYPE (GdkQuartzScreen, gdk_quartz_screen, GDK_TYPE_SCREEN);
74 gdk_quartz_screen_init (GdkQuartzScreen *quartz_screen)
76 GdkScreen *screen = GDK_SCREEN (quartz_screen);
79 nsscreen = [[NSScreen screens] objectAtIndex:0];
80 gdk_screen_set_resolution (screen,
81 72.0 * [nsscreen userSpaceScaleFactor]);
83 gdk_quartz_screen_calculate_layout (quartz_screen);
85 CGDisplayRegisterReconfigurationCallback (display_reconfiguration_callback,
88 quartz_screen->emit_monitors_changed = FALSE;
92 gdk_quartz_screen_dispose (GObject *object)
94 GdkQuartzScreen *screen = GDK_QUARTZ_SCREEN (object);
96 if (screen->screen_changed_id)
98 g_source_remove (screen->screen_changed_id);
99 screen->screen_changed_id = 0;
102 CGDisplayRemoveReconfigurationCallback (display_reconfiguration_callback,
105 G_OBJECT_CLASS (gdk_quartz_screen_parent_class)->dispose (object);
109 gdk_quartz_screen_screen_rects_free (GdkQuartzScreen *screen)
111 screen->n_screens = 0;
113 if (screen->screen_rects)
115 g_free (screen->screen_rects);
116 screen->screen_rects = NULL;
121 gdk_quartz_screen_finalize (GObject *object)
123 GdkQuartzScreen *screen = GDK_QUARTZ_SCREEN (object);
125 gdk_quartz_screen_screen_rects_free (screen);
130 gdk_quartz_screen_calculate_layout (GdkQuartzScreen *screen)
136 GDK_QUARTZ_ALLOC_POOL;
138 gdk_quartz_screen_screen_rects_free (screen);
140 array = [NSScreen screens];
148 /* We determine the minimum and maximum x and y coordinates
149 * covered by the monitors. From this we can deduce the width
150 * and height of the root screen.
152 for (i = 0; i < [array count]; i++)
154 NSRect rect = [[array objectAtIndex:i] frame];
156 screen->min_x = MIN (screen->min_x, rect.origin.x);
157 max_x = MAX (max_x, rect.origin.x + rect.size.width);
159 screen->min_y = MIN (screen->min_y, rect.origin.y);
160 max_y = MAX (max_y, rect.origin.y + rect.size.height);
163 screen->width = max_x - screen->min_x;
164 screen->height = max_y - screen->min_y;
166 screen->n_screens = [array count];
167 screen->screen_rects = g_new0 (GdkRectangle, screen->n_screens);
169 for (i = 0; i < screen->n_screens; i++)
174 nsscreen = [array objectAtIndex:i];
175 rect = [nsscreen frame];
177 screen->screen_rects[i].x = rect.origin.x - screen->min_x;
178 screen->screen_rects[i].y
179 = screen->height - (rect.origin.y + rect.size.height) + screen->min_y;
180 screen->screen_rects[i].width = rect.size.width;
181 screen->screen_rects[i].height = rect.size.height;
184 GDK_QUARTZ_RELEASE_POOL;
189 process_display_reconfiguration (GdkQuartzScreen *screen)
193 width = gdk_screen_get_width (GDK_SCREEN (screen));
194 height = gdk_screen_get_height (GDK_SCREEN (screen));
196 gdk_quartz_screen_calculate_layout (GDK_QUARTZ_SCREEN (screen));
198 _gdk_windowing_update_window_sizes (GDK_SCREEN (screen));
200 if (screen->emit_monitors_changed)
202 g_signal_emit_by_name (screen, "monitors-changed");
203 screen->emit_monitors_changed = FALSE;
206 if (width != gdk_screen_get_width (GDK_SCREEN (screen))
207 || height != gdk_screen_get_height (GDK_SCREEN (screen)))
208 g_signal_emit_by_name (screen, "size-changed");
212 screen_changed_idle (gpointer data)
214 GdkQuartzScreen *screen = data;
216 process_display_reconfiguration (data);
218 screen->screen_changed_id = 0;
224 display_reconfiguration_callback (CGDirectDisplayID display,
225 CGDisplayChangeSummaryFlags flags,
228 GdkQuartzScreen *screen = userInfo;
230 if (flags & kCGDisplayBeginConfigurationFlag)
232 /* Ignore the begin configuration signal. */
237 /* We save information about the changes, so we can emit
238 * ::monitors-changed when appropriate. This signal must be
239 * emitted when the number, size of position of one of the
242 if (flags & kCGDisplayMovedFlag
243 || flags & kCGDisplayAddFlag
244 || flags & kCGDisplayRemoveFlag
245 || flags & kCGDisplayEnabledFlag
246 || flags & kCGDisplayDisabledFlag)
247 screen->emit_monitors_changed = TRUE;
249 /* At this point Cocoa does not know about the new screen data
250 * yet, so we delay our refresh into an idle handler.
252 if (!screen->screen_changed_id)
253 screen->screen_changed_id = gdk_threads_add_idle (screen_changed_idle,
259 gdk_quartz_screen_get_display (GdkScreen *screen)
266 gdk_quartz_screen_get_root_window (GdkScreen *screen)
272 gdk_quartz_screen_get_number (GdkScreen *screen)
278 _gdk_windowing_substitute_screen_number (const gchar *display_name,
281 if (screen_number != 0)
284 return g_strdup (display_name);
288 gdk_quartz_screen_get_width (GdkScreen *screen)
290 return GDK_QUARTZ_SCREEN (screen)->width;
294 gdk_quartz_screen_get_height (GdkScreen *screen)
296 return GDK_QUARTZ_SCREEN (screen)->height;
300 get_mm_from_pixels (NSScreen *screen, int pixels)
302 /* userSpaceScaleFactor is in "pixels per point",
303 * 72 is the number of points per inch,
304 * and 25.4 is the number of millimeters per inch.
306 #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_3
307 float dpi = [screen userSpaceScaleFactor] * 72.0;
309 float dpi = 96.0 / 72.0;
312 return (pixels / dpi) * 25.4;
316 get_nsscreen_for_monitor (gint monitor_num)
321 GDK_QUARTZ_ALLOC_POOL;
323 array = [NSScreen screens];
324 screen = [array objectAtIndex:monitor_num];
326 GDK_QUARTZ_RELEASE_POOL;
332 gdk_quartz_screen_get_width_mm (GdkScreen *screen)
334 return get_mm_from_pixels (get_nsscreen_for_monitor (0),
335 GDK_QUARTZ_SCREEN (screen)->width);
339 gdk_quartz_screen_get_height_mm (GdkScreen *screen)
341 return get_mm_from_pixels (get_nsscreen_for_monitor (0),
342 GDK_QUARTZ_SCREEN (screen)->height);
346 gdk_quartz_screen_get_n_monitors (GdkScreen *screen)
348 return GDK_QUARTZ_SCREEN (screen)->n_screens;
352 gdk_quartz_screen_get_primary_monitor (GdkScreen *screen)
358 gdk_quartz_screen_get_monitor_width_mm (GdkScreen *screen,
361 return get_mm_from_pixels (get_nsscreen_for_monitor (monitor_num),
362 GDK_QUARTZ_SCREEN (screen)->screen_rects[monitor_num].width);
366 gdk_quartz_screen_get_monitor_height_mm (GdkScreen *screen,
369 return get_mm_from_pixels (get_nsscreen_for_monitor (monitor_num),
370 GDK_QUARTZ_SCREEN (screen)->screen_rects[monitor_num].height);
374 gdk_quartz_screen_get_monitor_plug_name (GdkScreen *screen,
377 /* FIXME: Is there some useful name we could use here? */
382 gdk_quartz_screen_get_monitor_geometry (GdkScreen *screen,
386 *dest = GDK_QUARTZ_SCREEN (screen)->screen_rects[monitor_num];
390 gdk_quartz_screen_make_display_name (GdkScreen *screen)
392 return g_strdup (gdk_display_get_name (_gdk_display));
396 gdk_quartz_screen_get_active_window (GdkScreen *screen)
402 gdk_quartz_screen_get_window_stack (GdkScreen *screen)
408 gdk_quartz_screen_is_composited (GdkScreen *screen)
414 gdk_quartz_screen_class_init (GdkQuartzScreenClass *klass)
416 GObjectClass *object_class = G_OBJECT_CLASS (klass);
417 GdkScreenClass *screen_class = GDK_SCREEN_CLASS (klass);
419 object_class->dispose = gdk_quartz_screen_dispose;
420 object_class->finalize = gdk_quartz_screen_finalize;
422 screen_class->get_display = gdk_quartz_screen_get_display;
423 screen_class->get_width = gdk_quartz_screen_get_width;
424 screen_class->get_height = gdk_quartz_screen_get_height;
425 screen_class->get_width_mm = gdk_quartz_screen_get_width_mm;
426 screen_class->get_height_mm = gdk_quartz_screen_get_height_mm;
427 screen_class->get_number = gdk_quartz_screen_get_number;
428 screen_class->get_root_window = gdk_quartz_screen_get_root_window;
429 screen_class->get_n_monitors = gdk_quartz_screen_get_n_monitors;
430 screen_class->get_primary_monitor = gdk_quartz_screen_get_primary_monitor;
431 screen_class->get_monitor_width_mm = gdk_quartz_screen_get_monitor_width_mm;
432 screen_class->get_monitor_height_mm = gdk_quartz_screen_get_monitor_height_mm;
433 screen_class->get_monitor_plug_name = gdk_quartz_screen_get_monitor_plug_name;
434 screen_class->get_monitor_geometry = gdk_quartz_screen_get_monitor_geometry;
435 screen_class->is_composited = gdk_quartz_screen_is_composited;
436 screen_class->make_display_name = gdk_quartz_screen_make_display_name;
437 screen_class->get_active_window = gdk_quartz_screen_get_active_window;
438 screen_class->get_window_stack = gdk_quartz_screen_get_window_stack;
439 screen_class->broadcast_client_message = _gdk_quartz_screen_broadcast_client_message;
440 screen_class->get_setting = _gdk_quartz_screen_get_setting;
441 screen_class->get_rgba_visual = _gdk_quartz_screen_get_rgba_visual;
442 screen_class->get_system_visual = _gdk_quartz_screen_get_system_visual;
443 screen_class->visual_get_best_depth = _gdk_quartz_screen_visual_get_best_depth;
444 screen_class->visual_get_best_type = _gdk_quartz_screen_visual_get_best_type;
445 screen_class->visual_get_best = _gdk_quartz_screen_visual_get_best;
446 screen_class->visual_get_best_with_depth = _gdk_quartz_screen_visual_get_best_with_depth;
447 screen_class->visual_get_best_with_type = _gdk_quartz_screen_visual_get_best_with_type;
448 screen_class->visual_get_best_with_both = _gdk_quartz_screen_visual_get_best_with_both;
449 screen_class->query_depths = _gdk_quartz_screen_query_depths;
450 screen_class->query_visual_types = _gdk_quartz_screen_query_visual_types;
451 screen_class->list_visuals = _gdk_quartz_screen_list_visuals;