]> Pileus Git - ~andy/gtk/blob - gdk/quartz/gdkscreen-quartz.c
Support arbitrary screen layouts
[~andy/gtk] / gdk / quartz / gdkscreen-quartz.c
1 /* gdkscreen-quartz.c
2  *
3  * Copyright (C) 2005 Imendio AB
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22 #include "gdk.h"
23 #include "gdkscreen-quartz.h"
24 #include "gdkprivate-quartz.h"
25  
26
27 static void  gdk_screen_quartz_dispose          (GObject         *object);
28 static void  gdk_screen_quartz_finalize         (GObject         *object);
29 static void  gdk_screen_quartz_calculate_layout (GdkScreenQuartz *screen);
30
31 static void display_reconfiguration_callback (CGDirectDisplayID            display,
32                                               CGDisplayChangeSummaryFlags  flags,
33                                               void                        *userInfo);
34
35 G_DEFINE_TYPE (GdkScreenQuartz, _gdk_screen_quartz, GDK_TYPE_SCREEN);
36
37 static void
38 _gdk_screen_quartz_class_init (GdkScreenQuartzClass *klass)
39 {
40   GObjectClass *object_class = G_OBJECT_CLASS (klass);
41
42   object_class->dispose = gdk_screen_quartz_dispose;
43   object_class->finalize = gdk_screen_quartz_finalize;
44 }
45
46 static void
47 _gdk_screen_quartz_init (GdkScreenQuartz *screen_quartz)
48 {
49   GdkScreen *screen = GDK_SCREEN (screen_quartz);
50   NSScreen *nsscreen;
51
52   gdk_screen_set_default_colormap (screen,
53                                    gdk_screen_get_system_colormap (screen));
54
55   nsscreen = [[NSScreen screens] objectAtIndex:0];
56   gdk_screen_set_resolution (screen,
57                              72.0 * [nsscreen userSpaceScaleFactor]);
58
59   gdk_screen_quartz_calculate_layout (screen_quartz);
60
61   CGDisplayRegisterReconfigurationCallback (display_reconfiguration_callback,
62                                             screen);
63
64   screen_quartz->emit_monitors_changed = FALSE;
65 }
66
67 static void
68 gdk_screen_quartz_dispose (GObject *object)
69 {
70   GdkScreenQuartz *screen = GDK_SCREEN_QUARTZ (object);
71
72   if (screen->default_colormap)
73     {
74       g_object_unref (screen->default_colormap);
75       screen->default_colormap = NULL;
76     }
77
78   if (screen->screen_changed_id)
79     {
80       g_source_remove (screen->screen_changed_id);
81       screen->screen_changed_id = 0;
82     }
83
84   CGDisplayRemoveReconfigurationCallback (display_reconfiguration_callback,
85                                           screen);
86
87   G_OBJECT_CLASS (_gdk_screen_quartz_parent_class)->dispose (object);
88 }
89
90 static void
91 gdk_screen_quartz_screen_rects_free (GdkScreenQuartz *screen)
92 {
93   screen->n_screens = 0;
94
95   if (screen->screen_rects)
96     {
97       g_free (screen->screen_rects);
98       screen->screen_rects = NULL;
99     }
100 }
101
102 static void
103 gdk_screen_quartz_finalize (GObject *object)
104 {
105   GdkScreenQuartz *screen = GDK_SCREEN_QUARTZ (object);
106
107   gdk_screen_quartz_screen_rects_free (screen);
108 }
109
110
111 static void
112 gdk_screen_quartz_calculate_layout (GdkScreenQuartz *screen)
113 {
114   NSArray *array;
115   int i;
116   int max_x, max_y;
117
118   GDK_QUARTZ_ALLOC_POOL;
119
120   gdk_screen_quartz_screen_rects_free (screen);
121
122   array = [NSScreen screens];
123
124   screen->width = 0;
125   screen->height = 0;
126   screen->min_x = 0;
127   screen->min_y = 0;
128   max_x = max_y = 0;
129
130   /* We determine the minimum and maximum x and y coordinates
131    * covered by the monitors.  From this we can deduce the width
132    * and height of the root screen.
133    */
134   for (i = 0; i < [array count]; i++)
135     {
136       NSRect rect = [[array objectAtIndex:i] frame];
137
138       screen->min_x = MIN (screen->min_x, rect.origin.x);
139       max_x = MAX (max_x, rect.origin.x + rect.size.width);
140
141       screen->min_y = MIN (screen->min_y, rect.origin.y);
142       max_y = MAX (max_y, rect.origin.y + rect.size.height);
143     }
144
145   screen->width = max_x - screen->min_x;
146   screen->height = max_y - screen->min_y;
147
148   screen->n_screens = [array count];
149   screen->screen_rects = g_new0 (GdkRectangle, screen->n_screens);
150
151   for (i = 0; i < screen->n_screens; i++)
152     {
153       NSScreen *nsscreen;
154       NSRect rect;
155
156       nsscreen = [array objectAtIndex:i];
157       rect = [nsscreen frame];
158
159       screen->screen_rects[i].x = rect.origin.x - screen->min_x;
160       screen->screen_rects[i].y
161           = screen->height - (rect.origin.y + rect.size.height) + screen->min_y;
162       screen->screen_rects[i].width = rect.size.width;
163       screen->screen_rects[i].height = rect.size.height;
164     }
165
166   GDK_QUARTZ_RELEASE_POOL;
167 }
168
169
170 static void
171 process_display_reconfiguration (GdkScreenQuartz *screen)
172 {
173   int width, height;
174
175   width = gdk_screen_get_width (GDK_SCREEN (screen));
176   height = gdk_screen_get_height (GDK_SCREEN (screen));
177
178   gdk_screen_quartz_calculate_layout (GDK_SCREEN_QUARTZ (screen));
179
180   if (screen->emit_monitors_changed)
181     {
182       g_signal_emit_by_name (screen, "monitors-changed");
183       screen->emit_monitors_changed = FALSE;
184     }
185
186   if (width != gdk_screen_get_width (GDK_SCREEN (screen))
187       || height != gdk_screen_get_height (GDK_SCREEN (screen)))
188     g_signal_emit_by_name (screen, "size-changed");
189 }
190
191 static gboolean
192 screen_changed_idle (gpointer data)
193 {
194   GdkScreenQuartz *screen = data;
195
196   process_display_reconfiguration (data);
197
198   screen->screen_changed_id = 0;
199
200   return FALSE;
201 }
202
203 static void
204 display_reconfiguration_callback (CGDirectDisplayID            display,
205                                   CGDisplayChangeSummaryFlags  flags,
206                                   void                        *userInfo)
207 {
208   GdkScreenQuartz *screen = userInfo;
209
210   if (flags & kCGDisplayBeginConfigurationFlag)
211     {
212       /* Ignore the begin configuration signal. */
213       return;
214     }
215   else
216     {
217       /* We save information about the changes, so we can emit
218        * ::monitors-changed when appropriate.  This signal must be
219        * emitted when the number, size of position of one of the
220        * monitors changes.
221        */
222       if (flags & kCGDisplayMovedFlag
223           || flags & kCGDisplayAddFlag
224           || flags & kCGDisplayRemoveFlag
225           || flags & kCGDisplayEnabledFlag
226           || flags & kCGDisplayDisabledFlag)
227         screen->emit_monitors_changed = TRUE;
228
229       /* At this point Cocoa does not know about the new screen data
230        * yet, so we delay our refresh into an idle handler.
231        */
232       if (!screen->screen_changed_id)
233         screen->screen_changed_id = gdk_threads_add_idle (screen_changed_idle,
234                                                           screen);
235     }
236 }
237
238 GdkScreen *
239 _gdk_screen_quartz_new (void)
240 {
241   return g_object_new (GDK_TYPE_SCREEN_QUARTZ, NULL);
242 }
243
244 GdkDisplay *
245 gdk_screen_get_display (GdkScreen *screen)
246 {
247   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
248
249   return _gdk_display;
250 }
251
252
253 GdkWindow *
254 gdk_screen_get_root_window (GdkScreen *screen)
255 {
256   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
257
258   return _gdk_root;
259 }
260
261 gint
262 gdk_screen_get_number (GdkScreen *screen)
263 {
264   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
265
266   return 0;
267 }
268
269 gchar * 
270 _gdk_windowing_substitute_screen_number (const gchar *display_name,
271                                          int          screen_number)
272 {
273   if (screen_number != 0)
274     return NULL;
275
276   return g_strdup (display_name);
277 }
278
279 GdkColormap*
280 gdk_screen_get_default_colormap (GdkScreen *screen)
281 {
282   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
283
284   return GDK_SCREEN_QUARTZ (screen)->default_colormap;
285 }
286
287 void
288 gdk_screen_set_default_colormap (GdkScreen   *screen,
289                                  GdkColormap *colormap)
290 {
291   GdkColormap *old_colormap;
292   
293   g_return_if_fail (GDK_IS_SCREEN (screen));
294   g_return_if_fail (GDK_IS_COLORMAP (colormap));
295
296   old_colormap = GDK_SCREEN_QUARTZ (screen)->default_colormap;
297
298   GDK_SCREEN_QUARTZ (screen)->default_colormap = g_object_ref (colormap);
299   
300   if (old_colormap)
301     g_object_unref (old_colormap);
302 }
303
304 gint
305 gdk_screen_get_width (GdkScreen *screen)
306 {
307   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
308
309   return GDK_SCREEN_QUARTZ (screen)->width;
310 }
311
312 gint
313 gdk_screen_get_height (GdkScreen *screen)
314 {
315   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
316
317   return GDK_SCREEN_QUARTZ (screen)->height;
318 }
319
320 static gint
321 get_mm_from_pixels (NSScreen *screen, int pixels)
322 {
323   /* userSpaceScaleFactor is in "pixels per point", 
324    * 72 is the number of points per inch, 
325    * and 25.4 is the number of millimeters per inch.
326    */
327 #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_3
328   float dpi = [screen userSpaceScaleFactor] * 72.0;
329 #else
330   float dpi = 96.0 / 72.0;
331 #endif
332
333   return (pixels / dpi) * 25.4;
334 }
335
336 static NSScreen *
337 get_nsscreen_for_monitor (gint monitor_num)
338 {
339   NSArray *array;
340   NSScreen *screen;
341
342   GDK_QUARTZ_ALLOC_POOL;
343
344   array = [NSScreen screens];
345   screen = [array objectAtIndex:monitor_num];
346
347   GDK_QUARTZ_RELEASE_POOL;
348
349   return screen;
350 }
351
352 gint
353 gdk_screen_get_width_mm (GdkScreen *screen)
354 {
355   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
356
357   return get_mm_from_pixels (get_nsscreen_for_monitor (0),
358                              GDK_SCREEN_QUARTZ (screen)->width);
359 }
360
361 gint
362 gdk_screen_get_height_mm (GdkScreen *screen)
363 {
364   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
365
366   return get_mm_from_pixels (get_nsscreen_for_monitor (0),
367                              GDK_SCREEN_QUARTZ (screen)->height);
368 }
369
370 int
371 gdk_screen_get_n_monitors (GdkScreen *screen)
372 {
373   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
374
375   return GDK_SCREEN_QUARTZ (screen)->n_screens;
376 }
377
378 gint
379 gdk_screen_get_monitor_width_mm (GdkScreen *screen,
380                                  gint       monitor_num)
381 {
382   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
383   g_return_val_if_fail (monitor_num < gdk_screen_get_n_monitors (screen), 0);
384   g_return_val_if_fail (monitor_num >= 0, 0);
385
386   return get_mm_from_pixels (get_nsscreen_for_monitor (monitor_num),
387                              GDK_SCREEN_QUARTZ (screen)->screen_rects[monitor_num].width);
388 }
389
390 gint
391 gdk_screen_get_monitor_height_mm (GdkScreen *screen,
392                                   gint       monitor_num)
393 {
394   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
395   g_return_val_if_fail (monitor_num < gdk_screen_get_n_monitors (screen), 0);
396   g_return_val_if_fail (monitor_num >= 0, 0);
397
398   return get_mm_from_pixels (get_nsscreen_for_monitor (monitor_num),
399                              GDK_SCREEN_QUARTZ (screen)->screen_rects[monitor_num].height);
400 }
401
402 gchar *
403 gdk_screen_get_monitor_plug_name (GdkScreen *screen,
404                                   gint       monitor_num)
405 {
406   /* FIXME: Is there some useful name we could use here? */
407   return NULL;
408 }
409
410 void
411 gdk_screen_get_monitor_geometry (GdkScreen    *screen, 
412                                  gint          monitor_num,
413                                  GdkRectangle *dest)
414 {
415   g_return_if_fail (GDK_IS_SCREEN (screen));
416   g_return_if_fail (monitor_num < gdk_screen_get_n_monitors (screen));
417   g_return_if_fail (monitor_num >= 0);
418
419   *dest = GDK_SCREEN_QUARTZ (screen)->screen_rects[monitor_num];
420 }
421
422 gchar *
423 gdk_screen_make_display_name (GdkScreen *screen)
424 {
425   return g_strdup (gdk_display_get_name (_gdk_display));
426 }
427
428 GdkWindow *
429 gdk_screen_get_active_window (GdkScreen *screen)
430 {
431   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
432
433   return NULL;
434 }
435
436 GList *
437 gdk_screen_get_window_stack (GdkScreen *screen)
438 {
439   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
440
441   return NULL;
442 }
443
444 gboolean
445 gdk_screen_is_composited (GdkScreen *screen)
446 {
447   g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
448
449   return TRUE;
450 }