]> Pileus Git - ~andy/gtk/blob - gdk/quartz/gdkscreen-quartz.c
quartz: _gdk_quartz_screen -> gdk_quartz_screen
[~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, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include "config.h"
23
24 #include <gdk/gdk.h>
25
26 #include "gdkprivate-quartz.h"
27  
28
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.
34  *
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
45  * on this!
46  *
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().
53  *
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*!
61  */
62
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);
66
67 static void display_reconfiguration_callback (CGDirectDisplayID            display,
68                                               CGDisplayChangeSummaryFlags  flags,
69                                               void                        *userInfo);
70
71 G_DEFINE_TYPE (GdkQuartzScreen, gdk_quartz_screen, GDK_TYPE_SCREEN);
72
73 static void
74 gdk_quartz_screen_init (GdkQuartzScreen *quartz_screen)
75 {
76   GdkScreen *screen = GDK_SCREEN (quartz_screen);
77   NSScreen *nsscreen;
78
79   nsscreen = [[NSScreen screens] objectAtIndex:0];
80   gdk_screen_set_resolution (screen,
81                              72.0 * [nsscreen userSpaceScaleFactor]);
82
83   gdk_quartz_screen_calculate_layout (quartz_screen);
84
85   CGDisplayRegisterReconfigurationCallback (display_reconfiguration_callback,
86                                             screen);
87
88   quartz_screen->emit_monitors_changed = FALSE;
89 }
90
91 static void
92 gdk_quartz_screen_dispose (GObject *object)
93 {
94   GdkQuartzScreen *screen = GDK_QUARTZ_SCREEN (object);
95
96   if (screen->screen_changed_id)
97     {
98       g_source_remove (screen->screen_changed_id);
99       screen->screen_changed_id = 0;
100     }
101
102   CGDisplayRemoveReconfigurationCallback (display_reconfiguration_callback,
103                                           screen);
104
105   G_OBJECT_CLASS (gdk_quartz_screen_parent_class)->dispose (object);
106 }
107
108 static void
109 gdk_quartz_screen_screen_rects_free (GdkQuartzScreen *screen)
110 {
111   screen->n_screens = 0;
112
113   if (screen->screen_rects)
114     {
115       g_free (screen->screen_rects);
116       screen->screen_rects = NULL;
117     }
118 }
119
120 static void
121 gdk_quartz_screen_finalize (GObject *object)
122 {
123   GdkQuartzScreen *screen = GDK_QUARTZ_SCREEN (object);
124
125   gdk_quartz_screen_screen_rects_free (screen);
126 }
127
128
129 static void
130 gdk_quartz_screen_calculate_layout (GdkQuartzScreen *screen)
131 {
132   NSArray *array;
133   int i;
134   int max_x, max_y;
135
136   GDK_QUARTZ_ALLOC_POOL;
137
138   gdk_quartz_screen_screen_rects_free (screen);
139
140   array = [NSScreen screens];
141
142   screen->width = 0;
143   screen->height = 0;
144   screen->min_x = 0;
145   screen->min_y = 0;
146   max_x = max_y = 0;
147
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.
151    */
152   for (i = 0; i < [array count]; i++)
153     {
154       NSRect rect = [[array objectAtIndex:i] frame];
155
156       screen->min_x = MIN (screen->min_x, rect.origin.x);
157       max_x = MAX (max_x, rect.origin.x + rect.size.width);
158
159       screen->min_y = MIN (screen->min_y, rect.origin.y);
160       max_y = MAX (max_y, rect.origin.y + rect.size.height);
161     }
162
163   screen->width = max_x - screen->min_x;
164   screen->height = max_y - screen->min_y;
165
166   screen->n_screens = [array count];
167   screen->screen_rects = g_new0 (GdkRectangle, screen->n_screens);
168
169   for (i = 0; i < screen->n_screens; i++)
170     {
171       NSScreen *nsscreen;
172       NSRect rect;
173
174       nsscreen = [array objectAtIndex:i];
175       rect = [nsscreen frame];
176
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;
182     }
183
184   GDK_QUARTZ_RELEASE_POOL;
185 }
186
187
188 static void
189 process_display_reconfiguration (GdkQuartzScreen *screen)
190 {
191   int width, height;
192
193   width = gdk_screen_get_width (GDK_SCREEN (screen));
194   height = gdk_screen_get_height (GDK_SCREEN (screen));
195
196   gdk_quartz_screen_calculate_layout (GDK_QUARTZ_SCREEN (screen));
197
198   _gdk_windowing_update_window_sizes (GDK_SCREEN (screen));
199
200   if (screen->emit_monitors_changed)
201     {
202       g_signal_emit_by_name (screen, "monitors-changed");
203       screen->emit_monitors_changed = FALSE;
204     }
205
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");
209 }
210
211 static gboolean
212 screen_changed_idle (gpointer data)
213 {
214   GdkQuartzScreen *screen = data;
215
216   process_display_reconfiguration (data);
217
218   screen->screen_changed_id = 0;
219
220   return FALSE;
221 }
222
223 static void
224 display_reconfiguration_callback (CGDirectDisplayID            display,
225                                   CGDisplayChangeSummaryFlags  flags,
226                                   void                        *userInfo)
227 {
228   GdkQuartzScreen *screen = userInfo;
229
230   if (flags & kCGDisplayBeginConfigurationFlag)
231     {
232       /* Ignore the begin configuration signal. */
233       return;
234     }
235   else
236     {
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
240        * monitors changes.
241        */
242       if (flags & kCGDisplayMovedFlag
243           || flags & kCGDisplayAddFlag
244           || flags & kCGDisplayRemoveFlag
245           || flags & kCGDisplayEnabledFlag
246           || flags & kCGDisplayDisabledFlag)
247         screen->emit_monitors_changed = TRUE;
248
249       /* At this point Cocoa does not know about the new screen data
250        * yet, so we delay our refresh into an idle handler.
251        */
252       if (!screen->screen_changed_id)
253         screen->screen_changed_id = gdk_threads_add_idle (screen_changed_idle,
254                                                           screen);
255     }
256 }
257
258 static GdkDisplay *
259 gdk_quartz_screen_get_display (GdkScreen *screen)
260 {
261   return _gdk_display;
262 }
263
264
265 static GdkWindow *
266 gdk_quartz_screen_get_root_window (GdkScreen *screen)
267 {
268   return _gdk_root;
269 }
270
271 static gint
272 gdk_quartz_screen_get_number (GdkScreen *screen)
273 {
274   return 0;
275 }
276
277 gchar * 
278 _gdk_windowing_substitute_screen_number (const gchar *display_name,
279                                          int          screen_number)
280 {
281   if (screen_number != 0)
282     return NULL;
283
284   return g_strdup (display_name);
285 }
286
287 static gint
288 gdk_quartz_screen_get_width (GdkScreen *screen)
289 {
290   return GDK_QUARTZ_SCREEN (screen)->width;
291 }
292
293 static gint
294 gdk_quartz_screen_get_height (GdkScreen *screen)
295 {
296   return GDK_QUARTZ_SCREEN (screen)->height;
297 }
298
299 static gint
300 get_mm_from_pixels (NSScreen *screen, int pixels)
301 {
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.
305    */
306 #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_3
307   float dpi = [screen userSpaceScaleFactor] * 72.0;
308 #else
309   float dpi = 96.0 / 72.0;
310 #endif
311
312   return (pixels / dpi) * 25.4;
313 }
314
315 static NSScreen *
316 get_nsscreen_for_monitor (gint monitor_num)
317 {
318   NSArray *array;
319   NSScreen *screen;
320
321   GDK_QUARTZ_ALLOC_POOL;
322
323   array = [NSScreen screens];
324   screen = [array objectAtIndex:monitor_num];
325
326   GDK_QUARTZ_RELEASE_POOL;
327
328   return screen;
329 }
330
331 static gint
332 gdk_quartz_screen_get_width_mm (GdkScreen *screen)
333 {
334   return get_mm_from_pixels (get_nsscreen_for_monitor (0),
335                              GDK_QUARTZ_SCREEN (screen)->width);
336 }
337
338 static gint
339 gdk_quartz_screen_get_height_mm (GdkScreen *screen)
340 {
341   return get_mm_from_pixels (get_nsscreen_for_monitor (0),
342                              GDK_QUARTZ_SCREEN (screen)->height);
343 }
344
345 static gint
346 gdk_quartz_screen_get_n_monitors (GdkScreen *screen)
347 {
348   return GDK_QUARTZ_SCREEN (screen)->n_screens;
349 }
350
351 static gint
352 gdk_quartz_screen_get_primary_monitor (GdkScreen *screen)
353 {
354   return 0;
355 }
356
357 static gint
358 gdk_quartz_screen_get_monitor_width_mm (GdkScreen *screen,
359                                         gint       monitor_num)
360 {
361   return get_mm_from_pixels (get_nsscreen_for_monitor (monitor_num),
362                              GDK_QUARTZ_SCREEN (screen)->screen_rects[monitor_num].width);
363 }
364
365 static gint
366 gdk_quartz_screen_get_monitor_height_mm (GdkScreen *screen,
367                                          gint       monitor_num)
368 {
369   return get_mm_from_pixels (get_nsscreen_for_monitor (monitor_num),
370                              GDK_QUARTZ_SCREEN (screen)->screen_rects[monitor_num].height);
371 }
372
373 static gchar *
374 gdk_quartz_screen_get_monitor_plug_name (GdkScreen *screen,
375                                          gint       monitor_num)
376 {
377   /* FIXME: Is there some useful name we could use here? */
378   return NULL;
379 }
380
381 static void
382 gdk_quartz_screen_get_monitor_geometry (GdkScreen    *screen,
383                                         gint          monitor_num,
384                                         GdkRectangle *dest)
385 {
386   *dest = GDK_QUARTZ_SCREEN (screen)->screen_rects[monitor_num];
387 }
388
389 static gchar *
390 gdk_quartz_screen_make_display_name (GdkScreen *screen)
391 {
392   return g_strdup (gdk_display_get_name (_gdk_display));
393 }
394
395 static GdkWindow *
396 gdk_quartz_screen_get_active_window (GdkScreen *screen)
397 {
398   return NULL;
399 }
400
401 static GList *
402 gdk_quartz_screen_get_window_stack (GdkScreen *screen)
403 {
404   return NULL;
405 }
406
407 static gboolean
408 gdk_quartz_screen_is_composited (GdkScreen *screen)
409 {
410   return TRUE;
411 }
412
413 static void
414 gdk_quartz_screen_class_init (GdkQuartzScreenClass *klass)
415 {
416   GObjectClass *object_class = G_OBJECT_CLASS (klass);
417   GdkScreenClass *screen_class = GDK_SCREEN_CLASS (klass);
418
419   object_class->dispose = gdk_quartz_screen_dispose;
420   object_class->finalize = gdk_quartz_screen_finalize;
421
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;
452 }