]> Pileus Git - ~andy/gtk/blob - gdk/quartz/gdkscreen-quartz.c
Create a proper subclass of GdkScreen: GdkScreenQuartz
[~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
51   gdk_screen_set_default_colormap (screen,
52                                    gdk_screen_get_system_colormap (screen));
53
54   gdk_screen_quartz_calculate_layout (screen_quartz);
55
56   CGDisplayRegisterReconfigurationCallback (display_reconfiguration_callback,
57                                             screen);
58 }
59
60 static void
61 gdk_screen_quartz_dispose (GObject *object)
62 {
63   GdkScreenQuartz *screen = GDK_SCREEN_QUARTZ (object);
64
65   if (screen->default_colormap)
66     {
67       g_object_unref (screen->default_colormap);
68       screen->default_colormap = NULL;
69     }
70
71   if (screen->screen_changed_id)
72     {
73       g_source_remove (screen->screen_changed_id);
74       screen->screen_changed_id = 0;
75     }
76
77   CGDisplayRemoveReconfigurationCallback (display_reconfiguration_callback,
78                                           screen);
79
80   G_OBJECT_CLASS (_gdk_screen_quartz_parent_class)->dispose (object);
81 }
82
83 static void
84 gdk_screen_quartz_screen_rects_free (GdkScreenQuartz *screen)
85 {
86   screen->n_screens = 0;
87
88   if (screen->screen_rects)
89     {
90       g_free (screen->screen_rects);
91       screen->screen_rects = NULL;
92     }
93 }
94
95 static void
96 gdk_screen_quartz_finalize (GObject *object)
97 {
98   GdkScreenQuartz *screen = GDK_SCREEN_QUARTZ (object);
99
100   gdk_screen_quartz_screen_rects_free (screen);
101 }
102
103
104 static void
105 gdk_screen_quartz_calculate_layout (GdkScreenQuartz *screen)
106 {
107   NSArray *array;
108   NSRect largest_rect;
109   int i;
110
111   GDK_QUARTZ_ALLOC_POOL;
112
113   gdk_screen_quartz_screen_rects_free (screen);
114
115   array = [NSScreen screens];
116
117   /* FIXME: For now we only support screen layouts where the screens are laid
118    * out horizontally.  Mac OS X also supports laying out the screens vertically
119    * and the screens having "non-standard" offsets from eachother.  In the
120    * future we need a much more sophiscated algorithm to translate these
121    * layouts to GDK coordinate space and GDK screen layout.
122    */
123   screen->width = 0;
124   screen->height = 0;
125
126   for (i = 0; i < [array count]; i++)
127     {
128       NSRect rect = [[array objectAtIndex:i] frame];
129
130       screen->width += rect.size.width;
131       screen->height = MAX (screen->height, rect.size.height);
132     }
133
134   screen->n_screens = [array count];
135   screen->screen_rects = g_new0 (GdkRectangle, screen->n_screens);
136
137   /* Find the monitor with the largest height.  All monitors should be
138    * offset to this one in the GDK screen space instead of offset to
139    * the screen with the menu bar.
140    */
141   largest_rect = [[array objectAtIndex:0] frame];
142   for (i = 1; i < [array count]; i++)
143     {
144       NSRect rect = [[array objectAtIndex:i] frame];
145
146       if (rect.size.height > largest_rect.size.height)
147         largest_rect = [[array objectAtIndex:i] frame];
148     }
149
150   for (i = 0; i < screen->n_screens; i++)
151     {
152       NSScreen *nsscreen;
153       NSRect rect;
154
155       nsscreen = [array objectAtIndex:i];
156       rect = [nsscreen frame];
157
158       screen->screen_rects[i].x = rect.origin.x;
159       screen->screen_rects[i].width = rect.size.width;
160       screen->screen_rects[i].height = rect.size.height;
161
162       if (largest_rect.size.height - rect.size.height == 0)
163         screen->screen_rects[i].y = 0;
164       else
165         screen->screen_rects[i].y = largest_rect.size.height - rect.size.height + largest_rect.origin.y;
166     }
167
168   GDK_QUARTZ_RELEASE_POOL;
169 }
170
171
172 static void
173 process_display_reconfiguration (GdkScreenQuartz *screen)
174 {
175   int width, height;
176
177   width = gdk_screen_get_width (GDK_SCREEN (screen));
178   height = gdk_screen_get_height (GDK_SCREEN (screen));
179
180   gdk_screen_quartz_calculate_layout (GDK_SCREEN_QUARTZ (screen));
181
182   if (width != gdk_screen_get_width (GDK_SCREEN (screen))
183       || height != gdk_screen_get_height (GDK_SCREEN (screen)))
184     g_signal_emit_by_name (_gdk_screen, "size-changed");
185 }
186
187 static gboolean
188 screen_changed_idle (gpointer data)
189 {
190   GdkScreenQuartz *screen = data;
191
192   process_display_reconfiguration (data);
193
194   screen->screen_changed_id = 0;
195
196   return FALSE;
197 }
198
199 static void
200 display_reconfiguration_callback (CGDirectDisplayID            display,
201                                   CGDisplayChangeSummaryFlags  flags,
202                                   void                        *userInfo)
203 {
204   GdkScreenQuartz *screen = userInfo;
205
206   if (flags & kCGDisplayBeginConfigurationFlag)
207     {
208       /* Ignore the begin configuration signal. */
209
210       /* FIXME: We can most probably use this flag to properly
211        * emit monitors-changed.
212        */
213       return;
214     }
215   else
216     {
217       /* At this point Cocoa does not know about the new screen data
218        * yet, so we delay our refresh into an idle handler.
219        */
220
221       if (!screen->screen_changed_id)
222         screen->screen_changed_id = gdk_threads_add_idle (screen_changed_idle,
223                                                           screen);
224     }
225 }
226
227 GdkScreen *
228 _gdk_screen_quartz_new (void)
229 {
230   return g_object_new (GDK_TYPE_SCREEN_QUARTZ, NULL);
231 }
232
233 GdkDisplay *
234 gdk_screen_get_display (GdkScreen *screen)
235 {
236   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
237
238   return _gdk_display;
239 }
240
241
242 GdkWindow *
243 gdk_screen_get_root_window (GdkScreen *screen)
244 {
245   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
246
247   return _gdk_root;
248 }
249
250 gint
251 gdk_screen_get_number (GdkScreen *screen)
252 {
253   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
254
255   return 0;
256 }
257
258 gchar * 
259 _gdk_windowing_substitute_screen_number (const gchar *display_name,
260                                          int          screen_number)
261 {
262   if (screen_number != 0)
263     return NULL;
264
265   return g_strdup (display_name);
266 }
267
268 GdkColormap*
269 gdk_screen_get_default_colormap (GdkScreen *screen)
270 {
271   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
272
273   return GDK_SCREEN_QUARTZ (screen)->default_colormap;
274 }
275
276 void
277 gdk_screen_set_default_colormap (GdkScreen   *screen,
278                                  GdkColormap *colormap)
279 {
280   GdkColormap *old_colormap;
281   
282   g_return_if_fail (GDK_IS_SCREEN (screen));
283   g_return_if_fail (GDK_IS_COLORMAP (colormap));
284
285   old_colormap = GDK_SCREEN_QUARTZ (screen)->default_colormap;
286
287   GDK_SCREEN_QUARTZ (screen)->default_colormap = g_object_ref (colormap);
288   
289   if (old_colormap)
290     g_object_unref (old_colormap);
291 }
292
293 gint
294 gdk_screen_get_width (GdkScreen *screen)
295 {
296   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
297
298   return GDK_SCREEN_QUARTZ (screen)->width;
299 }
300
301 gint
302 gdk_screen_get_height (GdkScreen *screen)
303 {
304   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
305
306   return GDK_SCREEN_QUARTZ (screen)->height;
307 }
308
309 static gint
310 get_mm_from_pixels (NSScreen *screen, int pixels)
311 {
312   /* userSpaceScaleFactor is in "pixels per point", 
313    * 72 is the number of points per inch, 
314    * and 25.4 is the number of millimeters per inch.
315    */
316 #if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_3
317   float dpi = [screen userSpaceScaleFactor] * 72.0;
318 #else
319   float dpi = 96.0 / 72.0;
320 #endif
321
322   return (pixels / dpi) * 25.4;
323 }
324
325 static NSScreen *
326 get_nsscreen_for_monitor (gint monitor_num)
327 {
328   NSArray *array;
329   NSScreen *screen;
330
331   GDK_QUARTZ_ALLOC_POOL;
332
333   array = [NSScreen screens];
334   screen = [array objectAtIndex:monitor_num];
335
336   GDK_QUARTZ_RELEASE_POOL;
337
338   return screen;
339 }
340
341 gint
342 gdk_screen_get_width_mm (GdkScreen *screen)
343 {
344   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
345
346   return get_mm_from_pixels (get_nsscreen_for_monitor (0),
347                              GDK_SCREEN_QUARTZ (screen)->width);
348 }
349
350 gint
351 gdk_screen_get_height_mm (GdkScreen *screen)
352 {
353   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
354
355   return get_mm_from_pixels (get_nsscreen_for_monitor (0),
356                              GDK_SCREEN_QUARTZ (screen)->height);
357 }
358
359 int
360 gdk_screen_get_n_monitors (GdkScreen *screen)
361 {
362   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
363
364   return GDK_SCREEN_QUARTZ (screen)->n_screens;
365 }
366
367 gint
368 gdk_screen_get_monitor_width_mm (GdkScreen *screen,
369                                  gint       monitor_num)
370 {
371   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
372   g_return_val_if_fail (monitor_num < gdk_screen_get_n_monitors (screen), 0);
373   g_return_val_if_fail (monitor_num >= 0, 0);
374
375   return get_mm_from_pixels (get_nsscreen_for_monitor (monitor_num),
376                              GDK_SCREEN_QUARTZ (screen)->screen_rects[monitor_num].width);
377 }
378
379 gint
380 gdk_screen_get_monitor_height_mm (GdkScreen *screen,
381                                   gint       monitor_num)
382 {
383   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
384   g_return_val_if_fail (monitor_num < gdk_screen_get_n_monitors (screen), 0);
385   g_return_val_if_fail (monitor_num >= 0, 0);
386
387   return get_mm_from_pixels (get_nsscreen_for_monitor (monitor_num),
388                              GDK_SCREEN_QUARTZ (screen)->screen_rects[monitor_num].height);
389 }
390
391 gchar *
392 gdk_screen_get_monitor_plug_name (GdkScreen *screen,
393                                   gint       monitor_num)
394 {
395   /* FIXME: Is there some useful name we could use here? */
396   return NULL;
397 }
398
399 void
400 gdk_screen_get_monitor_geometry (GdkScreen    *screen, 
401                                  gint          monitor_num,
402                                  GdkRectangle *dest)
403 {
404   g_return_if_fail (GDK_IS_SCREEN (screen));
405   g_return_if_fail (monitor_num < gdk_screen_get_n_monitors (screen));
406   g_return_if_fail (monitor_num >= 0);
407
408   *dest = GDK_SCREEN_QUARTZ (screen)->screen_rects[monitor_num];
409 }
410
411 gchar *
412 gdk_screen_make_display_name (GdkScreen *screen)
413 {
414   return g_strdup (gdk_display_get_name (_gdk_display));
415 }
416
417 GdkWindow *
418 gdk_screen_get_active_window (GdkScreen *screen)
419 {
420   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
421
422   return NULL;
423 }
424
425 GList *
426 gdk_screen_get_window_stack (GdkScreen *screen)
427 {
428   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
429
430   return NULL;
431 }
432
433 gboolean
434 gdk_screen_is_composited (GdkScreen *screen)
435 {
436   g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
437
438   return TRUE;
439 }