]> Pileus Git - ~andy/gtk/blob - gdk/gdkscreen.c
Fix get_nearest_monitor
[~andy/gtk] / gdk / gdkscreen.c
1 /*
2  * gdkscreen.c
3  * 
4  * Copyright 2001 Sun Microsystems Inc. 
5  *
6  * Erwann Chenede <erwann.chenede@sun.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #include "config.h"
25 #include "gdk.h"                /* For gdk_rectangle_intersect() */
26 #include "gdkcolor.h"
27 #include "gdkwindow.h"
28 #include "gdkscreen.h"
29 #include "gdkintl.h"
30 #include "gdkalias.h"
31
32 static void gdk_screen_dispose      (GObject        *object);
33 static void gdk_screen_finalize     (GObject        *object);
34 static void gdk_screen_set_property (GObject        *object,
35                                      guint           prop_id,
36                                      const GValue   *value,
37                                      GParamSpec     *pspec);
38 static void gdk_screen_get_property (GObject        *object,
39                                      guint           prop_id,
40                                      GValue         *value,
41                                      GParamSpec     *pspec);
42
43 enum
44 {
45   PROP_0,
46   PROP_FONT_OPTIONS,
47   PROP_RESOLUTION
48 };
49
50 enum
51 {
52   SIZE_CHANGED,
53   COMPOSITED_CHANGED,
54   MONITORS_CHANGED,
55   LAST_SIGNAL
56 };
57
58 static guint signals[LAST_SIGNAL] = { 0 };
59
60 G_DEFINE_TYPE (GdkScreen, gdk_screen, G_TYPE_OBJECT)
61
62 static void
63 gdk_screen_class_init (GdkScreenClass *klass)
64 {
65   GObjectClass *object_class = G_OBJECT_CLASS (klass);
66
67   object_class->dispose = gdk_screen_dispose;
68   object_class->finalize = gdk_screen_finalize;
69   object_class->set_property = gdk_screen_set_property;
70   object_class->get_property = gdk_screen_get_property;
71   
72   g_object_class_install_property (object_class,
73                                    PROP_FONT_OPTIONS,
74                                    g_param_spec_pointer ("font-options",
75                                                          P_("Font options"),
76                                                          P_("The default font options for the screen"),
77                                                          G_PARAM_READWRITE|G_PARAM_STATIC_NAME|
78                                                         G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
79
80   g_object_class_install_property (object_class,
81                                    PROP_RESOLUTION,
82                                    g_param_spec_double ("resolution",
83                                                         P_("Font resolution"),
84                                                         P_("The resolution for fonts on the screen"),
85                                                         -G_MAXDOUBLE,
86                                                         G_MAXDOUBLE,
87                                                         -1.0,
88                                                         G_PARAM_READWRITE|G_PARAM_STATIC_NAME|
89                                                         G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
90
91   /**
92    * GdkScreen::size-changed:
93    * @screen: the object on which the signal is emitted
94    * 
95    * The ::size-changed signal is emitted when the pixel width or 
96    * height of a screen changes.
97    *
98    * Since: 2.2
99    */
100   signals[SIZE_CHANGED] =
101     g_signal_new (g_intern_static_string ("size-changed"),
102                   G_OBJECT_CLASS_TYPE (klass),
103                   G_SIGNAL_RUN_LAST,
104                   G_STRUCT_OFFSET (GdkScreenClass, size_changed),
105                   NULL, NULL,
106                   g_cclosure_marshal_VOID__VOID,
107                   G_TYPE_NONE,
108                   0);
109
110   /**
111    * GdkScreen::composited-changed:
112    * @screen: the object on which the signal is emitted
113    *
114    * The ::composited-changed signal is emitted when the composited
115    * status of the screen changes
116    *
117    * Since: 2.10
118    */
119   signals[COMPOSITED_CHANGED] =
120     g_signal_new (g_intern_static_string ("composited-changed"),
121                   G_OBJECT_CLASS_TYPE (klass),
122                   G_SIGNAL_RUN_LAST,
123                   G_STRUCT_OFFSET (GdkScreenClass, composited_changed),
124                   NULL, NULL,
125                   g_cclosure_marshal_VOID__VOID,
126                   G_TYPE_NONE,
127                   0);
128         
129   /**
130    * GdkScreen::monitors-changed:
131    * @screen: the object on which the signal is emitted
132    *
133    * The ::monitors-changed signal is emitted when the number, size
134    * or position of the monitors attached to the screen change. 
135    *
136    * Only for X for now. Future implementations for Win32 and
137    * OS X may be a possibility.
138    *
139    * Since: 2.14
140    */
141   signals[MONITORS_CHANGED] =
142     g_signal_new (g_intern_static_string ("monitors-changed"),
143                   G_OBJECT_CLASS_TYPE (klass),
144                   G_SIGNAL_RUN_LAST,
145                   G_STRUCT_OFFSET (GdkScreenClass, monitors_changed),
146                   NULL, NULL,
147                   g_cclosure_marshal_VOID__VOID,
148                   G_TYPE_NONE,
149                   0);
150 }
151
152 static void
153 gdk_screen_init (GdkScreen *screen)
154 {
155   screen->resolution = -1.;
156 }
157
158 static void
159 gdk_screen_dispose (GObject *object)
160 {
161   GdkScreen *screen = GDK_SCREEN (object);
162   gint i;
163
164   for (i = 0; i < 32; ++i)
165     {
166       if (screen->exposure_gcs[i])
167         {
168           g_object_unref (screen->exposure_gcs[i]);
169           screen->exposure_gcs[i] = NULL;
170         }
171
172       if (screen->normal_gcs[i])
173         {
174           g_object_unref (screen->normal_gcs[i]);
175           screen->normal_gcs[i] = NULL;
176         }
177     }
178
179   G_OBJECT_CLASS (gdk_screen_parent_class)->dispose (object);
180 }
181
182 static void
183 gdk_screen_finalize (GObject *object)
184 {
185   GdkScreen *screen = GDK_SCREEN (object);
186
187   if (screen->font_options)
188       cairo_font_options_destroy (screen->font_options);
189
190   G_OBJECT_CLASS (gdk_screen_parent_class)->finalize (object);
191 }
192
193 void 
194 _gdk_screen_close (GdkScreen *screen)
195 {
196   g_return_if_fail (GDK_IS_SCREEN (screen));
197
198   if (!screen->closed)
199     {
200       screen->closed = TRUE;
201       g_object_run_dispose (G_OBJECT (screen));
202     }
203 }
204
205 /* Fallback used when the monitor "at" a point or window
206  * doesn't exist.
207  */
208 static gint
209 get_nearest_monitor (GdkScreen *screen,
210                      gint       x,
211                      gint       y)
212 {
213   gint num_monitors, i;
214   gint nearest_dist = G_MAXINT;
215   gint nearest_monitor = 0;
216
217   g_return_val_if_fail (GDK_IS_SCREEN (screen), -1);
218
219   num_monitors = gdk_screen_get_n_monitors (screen);
220   
221   for (i = 0; i < num_monitors; i++)
222     {
223       GdkRectangle monitor;
224       gint dist_x, dist_y, dist;
225       
226       gdk_screen_get_monitor_geometry (screen, i, &monitor);
227
228       if (x < monitor.x)
229         dist_x = monitor.x - x;
230       else if (x >= monitor.x + monitor.width)
231         dist_x = x - (monitor.x + monitor.width) + 1;
232       else
233         dist_x = 0;
234
235       if (y < monitor.y)
236         dist_y = monitor.y - y;
237       else if (y >= monitor.y + monitor.height)
238         dist_y = y - (monitor.y + monitor.height) + 1;
239       else
240         dist_y = 0;
241
242       dist = dist_x + dist_y;
243       if (dist < nearest_dist)
244         {
245           nearest_dist = dist;
246           nearest_monitor = i;
247         }
248     }
249
250   return nearest_monitor;
251 }
252
253 /**
254  * gdk_screen_get_monitor_at_point:
255  * @screen: a #GdkScreen.
256  * @x: the x coordinate in the virtual screen.
257  * @y: the y coordinate in the virtual screen.
258  *
259  * Returns the monitor number in which the point (@x,@y) is located.
260  *
261  * Returns: the monitor number in which the point (@x,@y) lies, or
262  *   a monitor close to (@x,@y) if the point is not in any monitor.
263  *
264  * Since: 2.2
265  **/
266 gint 
267 gdk_screen_get_monitor_at_point (GdkScreen *screen,
268                                  gint       x,
269                                  gint       y)
270 {
271   gint num_monitors, i;
272   
273   g_return_val_if_fail (GDK_IS_SCREEN (screen), -1);
274
275   num_monitors = gdk_screen_get_n_monitors (screen);
276   
277   for (i=0;i<num_monitors;i++)
278     {
279       GdkRectangle monitor;
280       
281       gdk_screen_get_monitor_geometry (screen, i, &monitor);
282
283       if (x >= monitor.x &&
284           x < monitor.x + monitor.width &&
285           y >= monitor.y &&
286           y < (monitor.y + monitor.height))
287         return i;
288     }
289
290   return get_nearest_monitor (screen, x, y);
291 }
292
293 /**
294  * gdk_screen_get_monitor_at_window:
295  * @screen: a #GdkScreen.
296  * @window: a #GdkWindow
297  * @returns: the monitor number in which most of @window is located,
298  *           or if @window does not intersect any monitors, a monitor,
299  *           close to @window.
300  *
301  * Returns the number of the monitor in which the largest area of the 
302  * bounding rectangle of @window resides.
303  *
304  * Since: 2.2
305  **/
306 gint 
307 gdk_screen_get_monitor_at_window (GdkScreen      *screen,
308                                   GdkWindow      *window)
309 {
310   gint num_monitors, i, area = 0, screen_num = -1;
311   GdkRectangle win_rect;
312
313   g_return_val_if_fail (GDK_IS_SCREEN (screen), -1);
314
315   gdk_window_get_geometry (window, &win_rect.x, &win_rect.y, &win_rect.width,
316                            &win_rect.height, NULL);
317   gdk_window_get_origin (window, &win_rect.x, &win_rect.y);
318   num_monitors = gdk_screen_get_n_monitors (screen);
319   
320   for (i=0;i<num_monitors;i++)
321     {
322       GdkRectangle tmp_monitor, intersect;
323       
324       gdk_screen_get_monitor_geometry (screen, i, &tmp_monitor);
325       gdk_rectangle_intersect (&win_rect, &tmp_monitor, &intersect);
326       
327       if (intersect.width * intersect.height > area)
328         { 
329           area = intersect.width * intersect.height;
330           screen_num = i;
331         }
332     }
333   if (screen_num >= 0)
334     return screen_num;
335   else
336     return get_nearest_monitor (screen,
337                                 win_rect.x + win_rect.width / 2,
338                                 win_rect.y + win_rect.height / 2);
339 }
340
341 /**
342  * gdk_screen_width:
343  * 
344  * Returns the width of the default screen in pixels.
345  * 
346  * Return value: the width of the default screen in pixels.
347  **/
348 gint
349 gdk_screen_width (void)
350 {
351   return gdk_screen_get_width (gdk_screen_get_default ());
352 }
353
354 /**
355  * gdk_screen_height:
356  * 
357  * Returns the height of the default screen in pixels.
358  * 
359  * Return value: the height of the default screen in pixels.
360  **/
361 gint
362 gdk_screen_height (void)
363 {
364   return gdk_screen_get_height (gdk_screen_get_default ());
365 }
366
367 /**
368  * gdk_screen_width_mm:
369  * 
370  * Returns the width of the default screen in millimeters.
371  * Note that on many X servers this value will not be correct.
372  * 
373  * Return value: the width of the default screen in millimeters,
374  * though it is not always correct.
375  **/
376 gint
377 gdk_screen_width_mm (void)
378 {
379   return gdk_screen_get_width_mm (gdk_screen_get_default ());
380 }
381
382 /**
383  * gdk_screen_height_mm:
384  * 
385  * Returns the height of the default screen in millimeters.
386  * Note that on many X servers this value will not be correct.
387  * 
388  * Return value: the height of the default screen in millimeters,
389  * though it is not always correct.
390  **/
391 gint
392 gdk_screen_height_mm (void)
393 {
394   return gdk_screen_get_height_mm (gdk_screen_get_default ());
395 }
396
397 /**
398  * gdk_screen_set_font_options:
399  * @screen: a #GdkScreen
400  * @options: a #cairo_font_options_t, or %NULL to unset any
401  *   previously set default font options.
402  *
403  * Sets the default font options for the screen. These
404  * options will be set on any #PangoContext's newly created
405  * with gdk_pango_context_get_for_screen(). Changing the
406  * default set of font options does not affect contexts that
407  * have already been created.
408  *
409  * Since: 2.10
410  **/
411 void
412 gdk_screen_set_font_options (GdkScreen                  *screen,
413                              const cairo_font_options_t *options)
414 {
415   g_return_if_fail (GDK_IS_SCREEN (screen));
416
417   if (screen->font_options != options)
418     {
419       if (screen->font_options)
420         cairo_font_options_destroy (screen->font_options);
421
422       if (options)
423         screen->font_options = cairo_font_options_copy (options);
424       else
425         screen->font_options = NULL;
426
427       g_object_notify (G_OBJECT (screen), "font-options");
428     }
429 }
430
431 /**
432  * gdk_screen_get_font_options:
433  * @screen: a #GdkScreen
434  * 
435  * Gets any options previously set with gdk_screen_set_font_options().
436  * 
437  * Return value: the current font options, or %NULL if no default
438  *  font options have been set.
439  *
440  * Since: 2.10
441  **/
442 const cairo_font_options_t *
443 gdk_screen_get_font_options (GdkScreen *screen)
444 {
445   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
446
447   return screen->font_options;
448 }
449
450 /**
451  * gdk_screen_set_resolution:
452  * @screen: a #GdkScreen
453  * @dpi: the resolution in "dots per inch". (Physical inches aren't actually
454  *   involved; the terminology is conventional.)
455  
456  * Sets the resolution for font handling on the screen. This is a
457  * scale factor between points specified in a #PangoFontDescription
458  * and cairo units. The default value is 96, meaning that a 10 point
459  * font will be 13 units high. (10 * 96. / 72. = 13.3).
460  *
461  * Since: 2.10
462  **/
463 void
464 gdk_screen_set_resolution (GdkScreen *screen,
465                            gdouble    dpi)
466 {
467   g_return_if_fail (GDK_IS_SCREEN (screen));
468
469   if (dpi < 0)
470     dpi = -1.0;
471
472   if (screen->resolution != dpi)
473     {
474       screen->resolution = dpi;
475
476       g_object_notify (G_OBJECT (screen), "resolution");
477     }
478 }
479
480 /**
481  * gdk_screen_get_resolution:
482  * @screen: a #GdkScreen
483  * 
484  * Gets the resolution for font handling on the screen; see
485  * gdk_screen_set_resolution() for full details.
486  * 
487  * Return value: the current resolution, or -1 if no resolution
488  * has been set.
489  *
490  * Since: 2.10
491  **/
492 gdouble
493 gdk_screen_get_resolution (GdkScreen *screen)
494 {
495   g_return_val_if_fail (GDK_IS_SCREEN (screen), -1.0);
496
497   return screen->resolution;
498 }
499
500 static void
501 gdk_screen_get_property (GObject      *object,
502                          guint         prop_id,
503                          GValue       *value,
504                          GParamSpec   *pspec)
505 {
506   GdkScreen *screen = GDK_SCREEN (object);
507
508   switch (prop_id)
509     {
510     case PROP_FONT_OPTIONS:
511       g_value_set_pointer (value, (gpointer) gdk_screen_get_font_options (screen));
512       break;
513     case PROP_RESOLUTION:
514       g_value_set_double (value, gdk_screen_get_resolution (screen));
515       break;
516     default:
517       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
518       break;
519     }
520 }
521
522 static void
523 gdk_screen_set_property (GObject      *object,
524                          guint         prop_id,
525                          const GValue *value,
526                          GParamSpec   *pspec)
527 {
528   GdkScreen *screen = GDK_SCREEN (object);
529
530   switch (prop_id)
531     {
532     case PROP_FONT_OPTIONS:
533       gdk_screen_set_font_options (screen, g_value_get_pointer (value));
534       break;
535     case PROP_RESOLUTION:
536       gdk_screen_set_resolution (screen, g_value_get_double (value));
537       break;
538     default:
539       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
540       break;
541     }
542 }
543
544 #define __GDK_SCREEN_C__
545 #include "gdkaliasdef.c"