]> Pileus Git - ~andy/gtk/blob - gdk/quartz/gdkscreen-quartz.c
x11: Get rid of XSettingsSetting
[~andy/gtk] / gdk / quartz / gdkscreen-quartz.c
1 /* gdkscreen-quartz.c
2  *
3  * Copyright (C) 2005 Imendio AB
4  * Copyright (C) 2009,2010  Kristian Rietveld  <kris@gtk.org>
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 #include "config.h"
21
22 #include <gdk/gdk.h>
23
24 #include "gdkprivate-quartz.h"
25  
26
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.
32  *
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
43  * on this!
44  *
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().
51  *
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*!
59  */
60
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);
64
65 static void display_reconfiguration_callback (CGDirectDisplayID            display,
66                                               CGDisplayChangeSummaryFlags  flags,
67                                               void                        *userInfo);
68
69 G_DEFINE_TYPE (GdkQuartzScreen, gdk_quartz_screen, GDK_TYPE_SCREEN);
70
71 static void
72 gdk_quartz_screen_init (GdkQuartzScreen *quartz_screen)
73 {
74   GdkScreen *screen = GDK_SCREEN (quartz_screen);
75   NSScreen *nsscreen;
76
77   nsscreen = [[NSScreen screens] objectAtIndex:0];
78   gdk_screen_set_resolution (screen,
79                              72.0 * [nsscreen userSpaceScaleFactor]);
80
81   gdk_quartz_screen_calculate_layout (quartz_screen);
82
83   CGDisplayRegisterReconfigurationCallback (display_reconfiguration_callback,
84                                             screen);
85
86   quartz_screen->emit_monitors_changed = FALSE;
87 }
88
89 static void
90 gdk_quartz_screen_dispose (GObject *object)
91 {
92   GdkQuartzScreen *screen = GDK_QUARTZ_SCREEN (object);
93
94   if (screen->screen_changed_id)
95     {
96       g_source_remove (screen->screen_changed_id);
97       screen->screen_changed_id = 0;
98     }
99
100   CGDisplayRemoveReconfigurationCallback (display_reconfiguration_callback,
101                                           screen);
102
103   G_OBJECT_CLASS (gdk_quartz_screen_parent_class)->dispose (object);
104 }
105
106 static void
107 gdk_quartz_screen_screen_rects_free (GdkQuartzScreen *screen)
108 {
109   screen->n_screens = 0;
110
111   if (screen->screen_rects)
112     {
113       g_free (screen->screen_rects);
114       screen->screen_rects = NULL;
115     }
116 }
117
118 static void
119 gdk_quartz_screen_finalize (GObject *object)
120 {
121   GdkQuartzScreen *screen = GDK_QUARTZ_SCREEN (object);
122
123   gdk_quartz_screen_screen_rects_free (screen);
124 }
125
126
127 static void
128 gdk_quartz_screen_calculate_layout (GdkQuartzScreen *screen)
129 {
130   NSArray *array;
131   int i;
132   int max_x, max_y;
133
134   GDK_QUARTZ_ALLOC_POOL;
135
136   gdk_quartz_screen_screen_rects_free (screen);
137
138   array = [NSScreen screens];
139
140   screen->width = 0;
141   screen->height = 0;
142   screen->min_x = 0;
143   screen->min_y = 0;
144   max_x = max_y = 0;
145
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.
149    */
150   for (i = 0; i < [array count]; i++)
151     {
152       NSRect rect = [[array objectAtIndex:i] frame];
153
154       screen->min_x = MIN (screen->min_x, rect.origin.x);
155       max_x = MAX (max_x, rect.origin.x + rect.size.width);
156
157       screen->min_y = MIN (screen->min_y, rect.origin.y);
158       max_y = MAX (max_y, rect.origin.y + rect.size.height);
159     }
160
161   screen->width = max_x - screen->min_x;
162   screen->height = max_y - screen->min_y;
163
164   screen->n_screens = [array count];
165   screen->screen_rects = g_new0 (GdkRectangle, screen->n_screens);
166
167   for (i = 0; i < screen->n_screens; i++)
168     {
169       NSScreen *nsscreen;
170       NSRect rect;
171
172       nsscreen = [array objectAtIndex:i];
173       rect = [nsscreen frame];
174
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;
180     }
181
182   GDK_QUARTZ_RELEASE_POOL;
183 }
184
185 void
186 _gdk_quartz_screen_update_window_sizes (GdkScreen *screen)
187 {
188   GList *windows, *list;
189
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.
194    *
195    * This data is updated when the monitor configuration is changed.
196    */
197
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.
200    */
201   _gdk_root->x = 0;
202   _gdk_root->y = 0;
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);
207
208   windows = gdk_screen_get_toplevel_windows (screen);
209
210   for (list = windows; list; list = list->next)
211     _gdk_quartz_window_update_position (list->data);
212
213   g_list_free (windows);
214 }
215
216 static void
217 process_display_reconfiguration (GdkQuartzScreen *screen)
218 {
219   int width, height;
220
221   width = gdk_screen_get_width (GDK_SCREEN (screen));
222   height = gdk_screen_get_height (GDK_SCREEN (screen));
223
224   gdk_quartz_screen_calculate_layout (GDK_QUARTZ_SCREEN (screen));
225
226   _gdk_quartz_screen_update_window_sizes (GDK_SCREEN (screen));
227
228   if (screen->emit_monitors_changed)
229     {
230       g_signal_emit_by_name (screen, "monitors-changed");
231       screen->emit_monitors_changed = FALSE;
232     }
233
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");
237 }
238
239 static gboolean
240 screen_changed_idle (gpointer data)
241 {
242   GdkQuartzScreen *screen = data;
243
244   process_display_reconfiguration (data);
245
246   screen->screen_changed_id = 0;
247
248   return FALSE;
249 }
250
251 static void
252 display_reconfiguration_callback (CGDirectDisplayID            display,
253                                   CGDisplayChangeSummaryFlags  flags,
254                                   void                        *userInfo)
255 {
256   GdkQuartzScreen *screen = userInfo;
257
258   if (flags & kCGDisplayBeginConfigurationFlag)
259     {
260       /* Ignore the begin configuration signal. */
261       return;
262     }
263   else
264     {
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
268        * monitors changes.
269        */
270       if (flags & kCGDisplayMovedFlag
271           || flags & kCGDisplayAddFlag
272           || flags & kCGDisplayRemoveFlag
273           || flags & kCGDisplayEnabledFlag
274           || flags & kCGDisplayDisabledFlag)
275         screen->emit_monitors_changed = TRUE;
276
277       /* At this point Cocoa does not know about the new screen data
278        * yet, so we delay our refresh into an idle handler.
279        */
280       if (!screen->screen_changed_id)
281         screen->screen_changed_id = gdk_threads_add_idle (screen_changed_idle,
282                                                           screen);
283     }
284 }
285
286 static GdkDisplay *
287 gdk_quartz_screen_get_display (GdkScreen *screen)
288 {
289   return _gdk_display;
290 }
291
292 static GdkWindow *
293 gdk_quartz_screen_get_root_window (GdkScreen *screen)
294 {
295   return _gdk_root;
296 }
297
298 static gint
299 gdk_quartz_screen_get_number (GdkScreen *screen)
300 {
301   return 0;
302 }
303
304 gchar *
305 _gdk_windowing_substitute_screen_number (const gchar *display_name,
306                                          int          screen_number)
307 {
308   if (screen_number != 0)
309     return NULL;
310
311   return g_strdup (display_name);
312 }
313
314 static gint
315 gdk_quartz_screen_get_width (GdkScreen *screen)
316 {
317   return GDK_QUARTZ_SCREEN (screen)->width;
318 }
319
320 static gint
321 gdk_quartz_screen_get_height (GdkScreen *screen)
322 {
323   return GDK_QUARTZ_SCREEN (screen)->height;
324 }
325
326 static gint
327 get_mm_from_pixels (NSScreen *screen, int pixels)
328 {
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.
332    */
333 #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_3
334   float dpi = [screen userSpaceScaleFactor] * 72.0;
335 #else
336   float dpi = 96.0 / 72.0;
337 #endif
338
339   return (pixels / dpi) * 25.4;
340 }
341
342 static NSScreen *
343 get_nsscreen_for_monitor (gint monitor_num)
344 {
345   NSArray *array;
346   NSScreen *screen;
347
348   GDK_QUARTZ_ALLOC_POOL;
349
350   array = [NSScreen screens];
351   screen = [array objectAtIndex:monitor_num];
352
353   GDK_QUARTZ_RELEASE_POOL;
354
355   return screen;
356 }
357
358 static gint
359 gdk_quartz_screen_get_width_mm (GdkScreen *screen)
360 {
361   return get_mm_from_pixels (get_nsscreen_for_monitor (0),
362                              GDK_QUARTZ_SCREEN (screen)->width);
363 }
364
365 static gint
366 gdk_quartz_screen_get_height_mm (GdkScreen *screen)
367 {
368   return get_mm_from_pixels (get_nsscreen_for_monitor (0),
369                              GDK_QUARTZ_SCREEN (screen)->height);
370 }
371
372 static gint
373 gdk_quartz_screen_get_n_monitors (GdkScreen *screen)
374 {
375   return GDK_QUARTZ_SCREEN (screen)->n_screens;
376 }
377
378 static gint
379 gdk_quartz_screen_get_primary_monitor (GdkScreen *screen)
380 {
381   return 0;
382 }
383
384 static gint
385 gdk_quartz_screen_get_monitor_width_mm (GdkScreen *screen,
386                                         gint       monitor_num)
387 {
388   return get_mm_from_pixels (get_nsscreen_for_monitor (monitor_num),
389                              GDK_QUARTZ_SCREEN (screen)->screen_rects[monitor_num].width);
390 }
391
392 static gint
393 gdk_quartz_screen_get_monitor_height_mm (GdkScreen *screen,
394                                          gint       monitor_num)
395 {
396   return get_mm_from_pixels (get_nsscreen_for_monitor (monitor_num),
397                              GDK_QUARTZ_SCREEN (screen)->screen_rects[monitor_num].height);
398 }
399
400 static gchar *
401 gdk_quartz_screen_get_monitor_plug_name (GdkScreen *screen,
402                                          gint       monitor_num)
403 {
404   /* FIXME: Is there some useful name we could use here? */
405   return NULL;
406 }
407
408 static void
409 gdk_quartz_screen_get_monitor_geometry (GdkScreen    *screen,
410                                         gint          monitor_num,
411                                         GdkRectangle *dest)
412 {
413   *dest = GDK_QUARTZ_SCREEN (screen)->screen_rects[monitor_num];
414 }
415
416 static void
417 gdk_quartz_screen_get_monitor_workarea (GdkScreen    *screen,
418                                         gint          monitor_num,
419                                         GdkRectangle *dest)
420 {
421   GdkQuartzScreen *quartz_screen = GDK_QUARTZ_SCREEN (screen);
422   NSArray *array;
423   NSScreen *nsscreen;
424   NSRect rect;
425
426   GDK_QUARTZ_ALLOC_POOL;
427
428   array = [NSScreen screens];
429   nsscreen = [array objectAtIndex:monitor_num];
430   rect = [nsscreen visibleFrame];
431
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;
436
437   GDK_QUARTZ_RELEASE_POOL;
438 }
439
440 static gchar *
441 gdk_quartz_screen_make_display_name (GdkScreen *screen)
442 {
443   return g_strdup (gdk_display_get_name (_gdk_display));
444 }
445
446 static GdkWindow *
447 gdk_quartz_screen_get_active_window (GdkScreen *screen)
448 {
449   return NULL;
450 }
451
452 static GList *
453 gdk_quartz_screen_get_window_stack (GdkScreen *screen)
454 {
455   return NULL;
456 }
457
458 static gboolean
459 gdk_quartz_screen_is_composited (GdkScreen *screen)
460 {
461   return TRUE;
462 }
463
464 static void
465 gdk_quartz_screen_class_init (GdkQuartzScreenClass *klass)
466 {
467   GObjectClass *object_class = G_OBJECT_CLASS (klass);
468   GdkScreenClass *screen_class = GDK_SCREEN_CLASS (klass);
469
470   object_class->dispose = gdk_quartz_screen_dispose;
471   object_class->finalize = gdk_quartz_screen_finalize;
472
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;
504 }