1 /*
2  * gdkmonitor.c
3  *
4  * Copyright 2016 Red Hat, Inc.
5  *
6  * Matthias Clasen <mclasen@redhat.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, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include "config.h"
23 
24 #include "gdkmonitorprivate.h"
25 #include "gdkdisplay.h"
26 #include "gdkenumtypes.h"
27 
28 /**
29  * GdkMonitor:
30  *
31  * `GdkMonitor` objects represent the individual outputs that are
32  * associated with a `GdkDisplay`.
33  *
34  * `GdkDisplay` keeps a `GListModel` to enumerate and monitor
35  * monitors with [method@Gdk.Display.get_monitors]. You can use
36  * [method@Gdk.Display.get_monitor_at_surface] to find a particular
37  * monitor.
38  */
39 
40 enum {
41   PROP_0,
42   PROP_DISPLAY,
43   PROP_MANUFACTURER,
44   PROP_MODEL,
45   PROP_CONNECTOR,
46   PROP_SCALE_FACTOR,
47   PROP_GEOMETRY,
48   PROP_WIDTH_MM,
49   PROP_HEIGHT_MM,
50   PROP_REFRESH_RATE,
51   PROP_SUBPIXEL_LAYOUT,
52   PROP_VALID,
53   LAST_PROP
54 };
55 
56 static GParamSpec *props[LAST_PROP] = { NULL, };
57 
58 enum {
59   INVALIDATE,
60   LAST_SIGNAL
61 };
62 
63 static guint signals[LAST_SIGNAL] = { 0 };
64 
G_DEFINE_TYPE(GdkMonitor,gdk_monitor,G_TYPE_OBJECT)65 G_DEFINE_TYPE (GdkMonitor, gdk_monitor, G_TYPE_OBJECT)
66 
67 static void
68 gdk_monitor_init (GdkMonitor *monitor)
69 {
70   monitor->scale_factor = 1;
71   monitor->valid = TRUE;
72 }
73 
74 static void
gdk_monitor_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)75 gdk_monitor_get_property (GObject    *object,
76                           guint       prop_id,
77                           GValue     *value,
78                           GParamSpec *pspec)
79 {
80   GdkMonitor *monitor = GDK_MONITOR (object);
81 
82   switch (prop_id)
83     {
84     case PROP_DISPLAY:
85       g_value_set_object (value, monitor->display);
86       break;
87 
88     case PROP_MANUFACTURER:
89       g_value_set_string (value, monitor->manufacturer);
90       break;
91 
92     case PROP_MODEL:
93       g_value_set_string (value, monitor->model);
94       break;
95 
96     case PROP_CONNECTOR:
97       g_value_set_string (value, monitor->connector);
98       break;
99 
100     case PROP_SCALE_FACTOR:
101       g_value_set_int (value, monitor->scale_factor);
102       break;
103 
104     case PROP_GEOMETRY:
105       g_value_set_boxed (value, &monitor->geometry);
106       break;
107 
108     case PROP_WIDTH_MM:
109       g_value_set_int (value, monitor->width_mm);
110       break;
111 
112     case PROP_HEIGHT_MM:
113       g_value_set_int (value, monitor->height_mm);
114       break;
115 
116     case PROP_REFRESH_RATE:
117       g_value_set_int (value, monitor->refresh_rate);
118       break;
119 
120     case PROP_SUBPIXEL_LAYOUT:
121       g_value_set_enum (value, monitor->subpixel_layout);
122       break;
123 
124     case PROP_VALID:
125       g_value_set_boolean (value, monitor->valid);
126       break;
127 
128     default:
129       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
130     }
131 }
132 
133 static void
gdk_monitor_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)134 gdk_monitor_set_property (GObject      *object,
135                           guint         prop_id,
136                           const GValue *value,
137                           GParamSpec   *pspec)
138 {
139   GdkMonitor *monitor = GDK_MONITOR (object);
140 
141   switch (prop_id)
142     {
143     case PROP_DISPLAY:
144       monitor->display = g_value_get_object (value);
145       break;
146 
147     default:
148       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
149     }
150 }
151 
152 static void
gdk_monitor_finalize(GObject * object)153 gdk_monitor_finalize (GObject *object)
154 {
155   GdkMonitor *monitor = GDK_MONITOR (object);
156 
157   g_free (monitor->connector);
158   g_free (monitor->manufacturer);
159   g_free (monitor->model);
160 
161   G_OBJECT_CLASS (gdk_monitor_parent_class)->finalize (object);
162 }
163 
164 static void
gdk_monitor_class_init(GdkMonitorClass * class)165 gdk_monitor_class_init (GdkMonitorClass *class)
166 {
167   GObjectClass *object_class = G_OBJECT_CLASS (class);
168 
169   object_class->finalize = gdk_monitor_finalize;
170   object_class->get_property = gdk_monitor_get_property;
171   object_class->set_property = gdk_monitor_set_property;
172 
173   /**
174    * GdkMonitor:display: (attributes org.gtk.Property.get=gdk_monitor_get_display)
175    *
176    * The `GdkDisplay` of the monitor.
177    */
178   props[PROP_DISPLAY] =
179     g_param_spec_object ("display",
180                          "Display",
181                          "The display of the monitor",
182                          GDK_TYPE_DISPLAY,
183                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
184 
185   /**
186    * GdkMonitor:manufacturer: (attributes org.gtk.Property.get=gdk_monitor_get_manufacturer)
187    *
188    * The manufacturer name.
189    */
190   props[PROP_MANUFACTURER] =
191     g_param_spec_string ("manufacturer",
192                          "Manufacturer",
193                          "The manufacturer name",
194                          NULL,
195                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
196 
197   /**
198    * GdkMonitor:model: (attributes org.gtk.Property.get=gdk_monitor_get_model)
199    *
200    * The model name.
201    */
202   props[PROP_MODEL] =
203     g_param_spec_string ("model",
204                          "Model",
205                          "The model name",
206                          NULL,
207                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
208 
209   /**
210    * GdkMonitor:connector: (attributes org.gtk.Property.get=gdk_monitor_get_connector)
211    *
212    * The connector name.
213    */
214   props[PROP_CONNECTOR] =
215     g_param_spec_string ("connector",
216                          "Connector",
217                          "The connector name",
218                          NULL,
219                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
220 
221   /**
222    * GdkMonitor:scale-factor: (attributes org.gtk.Property.get=gdk_monitor_get_scale_factor)
223    *
224    * The scale factor.
225    */
226   props[PROP_SCALE_FACTOR] =
227     g_param_spec_int ("scale-factor",
228                       "Scale factor",
229                       "The scale factor",
230                       0, G_MAXINT,
231                       1,
232                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
233 
234   /**
235    * GdkMonitor:geometry: (attributes org.gtk.Property.get=gdk_monitor_get_geometry)
236    *
237    * The geometry of the monitor.
238    */
239   props[PROP_GEOMETRY] =
240     g_param_spec_boxed ("geometry",
241                         "Geometry",
242                         "The geometry of the monitor",
243                         GDK_TYPE_RECTANGLE,
244                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
245 
246   /**
247    * GdkMonitor:width-mm: (attributes org.gtk.Property.get=gdk_monitor_get_width_mm)
248    *
249    * The width of the monitor, in millimeters.
250    */
251   props[PROP_WIDTH_MM] =
252     g_param_spec_int ("width-mm",
253                       "Physical width",
254                       "The width of the monitor, in millimeters",
255                       0, G_MAXINT,
256                       0,
257                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
258 
259   /**
260    * GdkMonitor:height-mm: (attributes org.gtk.Property.get=gdk_monitor_get_height_mm)
261    *
262    * The height of the monitor, in millimeters.
263    */
264   props[PROP_HEIGHT_MM] =
265     g_param_spec_int ("height-mm",
266                       "Physical height",
267                       "The height of the monitor, in millimeters",
268                       0, G_MAXINT,
269                       0,
270                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
271 
272   /**
273    * GdkMonitor:refresh-rate: (attributes org.gtk.Property.get=gdk_monitor_get_refresh_rate)
274    *
275    * The refresh rate, in milli-Hertz.
276    */
277   props[PROP_REFRESH_RATE] =
278     g_param_spec_int ("refresh-rate",
279                       "Refresh rate",
280                       "The refresh rate, in millihertz",
281                       0, G_MAXINT,
282                       0,
283                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
284 
285   /**
286    * GdkMonitor:subpixel-layout: (attributes org.gtk.Property.get=gdk_monitor_get_subpixel_layout)
287    *
288    * The subpixel layout.
289    */
290   props[PROP_SUBPIXEL_LAYOUT] =
291     g_param_spec_enum ("subpixel-layout",
292                        "Subpixel layout",
293                        "The subpixel layout",
294                        GDK_TYPE_SUBPIXEL_LAYOUT,
295                        GDK_SUBPIXEL_LAYOUT_UNKNOWN,
296                        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
297 
298   /**
299    * GdkMonitor:valid: (attributes org.gtk.Property.get=gdk_monitor_is_valid)
300    *
301    * Whether the object is still valid.
302    */
303   props[PROP_VALID] =
304     g_param_spec_boolean ("valid",
305                           "Valid",
306                           "Whether the monitor is still valid",
307                           TRUE,
308                           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
309 
310   g_object_class_install_properties (object_class, LAST_PROP, props);
311 
312   /**
313    * GdkMonitor::invalidate:
314    * @monitor: the object on which this signal was emitted
315    *
316    * Emitted when the output represented by @monitor gets disconnected.
317    */
318   signals[INVALIDATE] = g_signal_new (g_intern_static_string ("invalidate"),
319                                       G_TYPE_FROM_CLASS (object_class),
320                                       G_SIGNAL_RUN_FIRST,
321                                       0,
322                                       NULL, NULL,
323                                       NULL,
324                                       G_TYPE_NONE, 0);
325 }
326 
327 /**
328  * gdk_monitor_get_display: (attributes org.gtk.Method.get_property=display)
329  * @monitor: a `GdkMonitor`
330  *
331  * Gets the display that this monitor belongs to.
332  *
333  * Returns: (transfer none): the display
334  */
335 GdkDisplay *
gdk_monitor_get_display(GdkMonitor * monitor)336 gdk_monitor_get_display (GdkMonitor *monitor)
337 {
338   g_return_val_if_fail (GDK_IS_MONITOR (monitor), NULL);
339 
340   return monitor->display;
341 }
342 
343 /**
344  * gdk_monitor_get_geometry: (attributes org.gtk.Method.get_property=geometry)
345  * @monitor: a `GdkMonitor`
346  * @geometry: (out): a `GdkRectangle` to be filled with the monitor geometry
347  *
348  * Retrieves the size and position of the monitor within the
349  * display coordinate space.
350  *
351  * The returned geometry is in  ”application pixels”, not in
352  * ”device pixels” (see [method@Gdk.Monitor.get_scale_factor]).
353  */
354 void
gdk_monitor_get_geometry(GdkMonitor * monitor,GdkRectangle * geometry)355 gdk_monitor_get_geometry (GdkMonitor   *monitor,
356                           GdkRectangle *geometry)
357 {
358   g_return_if_fail (GDK_IS_MONITOR (monitor));
359   g_return_if_fail (geometry != NULL);
360 
361   *geometry = monitor->geometry;
362 }
363 
364 /**
365  * gdk_monitor_get_width_mm: (attributes org.gtk.Method.get_property=width-mm)
366  * @monitor: a `GdkMonitor`
367  *
368  * Gets the width in millimeters of the monitor.
369  *
370  * Returns: the physical width of the monitor
371  */
372 int
gdk_monitor_get_width_mm(GdkMonitor * monitor)373 gdk_monitor_get_width_mm (GdkMonitor *monitor)
374 {
375   g_return_val_if_fail (GDK_IS_MONITOR (monitor), 0);
376 
377   return monitor->width_mm;
378 }
379 
380 /**
381  * gdk_monitor_get_height_mm: (attributes org.gtk.Method.get_property=height-mm)
382  * @monitor: a `GdkMonitor`
383  *
384  * Gets the height in millimeters of the monitor.
385  *
386  * Returns: the physical height of the monitor
387  */
388 int
gdk_monitor_get_height_mm(GdkMonitor * monitor)389 gdk_monitor_get_height_mm (GdkMonitor *monitor)
390 {
391   g_return_val_if_fail (GDK_IS_MONITOR (monitor), 0);
392 
393   return monitor->height_mm;
394 }
395 
396 /**
397  * gdk_monitor_get_connector: (attributes org.gtk.Method.get_property=connector)
398  * @monitor: a `GdkMonitor`
399  *
400  * Gets the name of the monitor's connector, if available.
401  *
402  * Returns: (transfer none) (nullable): the name of the connector
403  */
404 const char *
gdk_monitor_get_connector(GdkMonitor * monitor)405 gdk_monitor_get_connector (GdkMonitor *monitor)
406 {
407   g_return_val_if_fail (GDK_IS_MONITOR (monitor), NULL);
408 
409   return monitor->connector;
410 }
411 
412 /**
413  * gdk_monitor_get_manufacturer: (attributes org.gtk.Method.get_property=manufacturer)
414  * @monitor: a `GdkMonitor`
415  *
416  * Gets the name or PNP ID of the monitor's manufacturer.
417  *
418  * Note that this value might also vary depending on actual
419  * display backend.
420  *
421  * The PNP ID registry is located at
422  * [https://uefi.org/pnp_id_list](https://uefi.org/pnp_id_list).
423  *
424  * Returns: (transfer none) (nullable): the name of the manufacturer
425  */
426 const char *
gdk_monitor_get_manufacturer(GdkMonitor * monitor)427 gdk_monitor_get_manufacturer (GdkMonitor *monitor)
428 {
429   g_return_val_if_fail (GDK_IS_MONITOR (monitor), NULL);
430 
431   return monitor->manufacturer;
432 }
433 
434 /**
435  * gdk_monitor_get_model: (attributes org.gtk.Method.get_property=model)
436  * @monitor: a `GdkMonitor`
437  *
438  * Gets the string identifying the monitor model, if available.
439  *
440  * Returns: (transfer none) (nullable): the monitor model
441  */
442 const char *
gdk_monitor_get_model(GdkMonitor * monitor)443 gdk_monitor_get_model (GdkMonitor *monitor)
444 {
445   g_return_val_if_fail (GDK_IS_MONITOR (monitor), NULL);
446 
447   return monitor->model;
448 }
449 
450 /**
451  * gdk_monitor_get_scale_factor: (attributes org.gtk.Method.get_property=scale-factor)
452  * @monitor: a `GdkMonitor`
453  *
454  * Gets the internal scale factor that maps from monitor coordinates
455  * to device pixels.
456  *
457  * On traditional systems this is 1, but on very high density outputs
458  * it can be a higher value (often 2).
459  *
460  * This can be used if you want to create pixel based data for a
461  * particular monitor, but most of the time you’re drawing to a surface
462  * where it is better to use [method@Gdk.Surface.get_scale_factor] instead.
463  *
464  * Returns: the scale factor
465  */
466 int
gdk_monitor_get_scale_factor(GdkMonitor * monitor)467 gdk_monitor_get_scale_factor (GdkMonitor *monitor)
468 {
469   g_return_val_if_fail (GDK_IS_MONITOR (monitor), 1);
470 
471   return monitor->scale_factor;
472 }
473 
474 /**
475  * gdk_monitor_get_refresh_rate: (attributes org.gtk.Method.get_property=refresh-rate)
476  * @monitor: a `GdkMonitor`
477  *
478  * Gets the refresh rate of the monitor, if available.
479  *
480  * The value is in milli-Hertz, so a refresh rate of 60Hz
481  * is returned as 60000.
482  *
483  * Returns: the refresh rate in milli-Hertz, or 0
484  */
485 int
gdk_monitor_get_refresh_rate(GdkMonitor * monitor)486 gdk_monitor_get_refresh_rate (GdkMonitor *monitor)
487 {
488   g_return_val_if_fail (GDK_IS_MONITOR (monitor), 0);
489 
490   return monitor->refresh_rate;
491 }
492 
493 /**
494  * gdk_monitor_get_subpixel_layout: (attributes org.gtk.Method.get_property=subpixel-layout)
495  * @monitor: a `GdkMonitor`
496  *
497  * Gets information about the layout of red, green and blue
498  * primaries for pixels.
499  *
500  * Returns: the subpixel layout
501  */
502 GdkSubpixelLayout
gdk_monitor_get_subpixel_layout(GdkMonitor * monitor)503 gdk_monitor_get_subpixel_layout (GdkMonitor *monitor)
504 {
505   g_return_val_if_fail (GDK_IS_MONITOR (monitor), GDK_SUBPIXEL_LAYOUT_UNKNOWN);
506 
507   return monitor->subpixel_layout;
508 }
509 
510 GdkMonitor *
gdk_monitor_new(GdkDisplay * display)511 gdk_monitor_new (GdkDisplay *display)
512 {
513   return GDK_MONITOR (g_object_new (GDK_TYPE_MONITOR,
514                                     "display", display,
515                                     NULL));
516 }
517 
518 void
gdk_monitor_set_manufacturer(GdkMonitor * monitor,const char * manufacturer)519 gdk_monitor_set_manufacturer (GdkMonitor *monitor,
520                               const char *manufacturer)
521 {
522   g_free (monitor->manufacturer);
523   monitor->manufacturer = g_strdup (manufacturer);
524 
525   g_object_notify (G_OBJECT (monitor), "manufacturer");
526 }
527 
528 void
gdk_monitor_set_model(GdkMonitor * monitor,const char * model)529 gdk_monitor_set_model (GdkMonitor *monitor,
530                        const char *model)
531 {
532   g_free (monitor->model);
533   monitor->model = g_strdup (model);
534 
535   g_object_notify (G_OBJECT (monitor), "model");
536 }
537 
538 void
gdk_monitor_set_connector(GdkMonitor * monitor,const char * connector)539 gdk_monitor_set_connector (GdkMonitor *monitor,
540                            const char *connector)
541 {
542   g_free (monitor->connector);
543   monitor->connector = g_strdup (connector);
544 
545   g_object_notify (G_OBJECT (monitor), "connector");
546 }
547 
548 void
gdk_monitor_set_geometry(GdkMonitor * monitor,const GdkRectangle * geometry)549 gdk_monitor_set_geometry (GdkMonitor *monitor,
550                           const GdkRectangle *geometry)
551 {
552   if (gdk_rectangle_equal (&monitor->geometry, geometry))
553     return;
554 
555   monitor->geometry = *geometry;
556   g_object_notify (G_OBJECT (monitor), "geometry");
557 }
558 
559 void
gdk_monitor_set_physical_size(GdkMonitor * monitor,int width_mm,int height_mm)560 gdk_monitor_set_physical_size (GdkMonitor *monitor,
561                                int         width_mm,
562                                int         height_mm)
563 {
564   g_object_freeze_notify (G_OBJECT (monitor));
565 
566   if (monitor->width_mm != width_mm)
567     {
568       monitor->width_mm = width_mm;
569       g_object_notify (G_OBJECT (monitor), "width-mm");
570     }
571 
572   if (monitor->height_mm != height_mm)
573     {
574       monitor->height_mm = height_mm;
575       g_object_notify (G_OBJECT (monitor), "height-mm");
576     }
577 
578   g_object_thaw_notify (G_OBJECT (monitor));
579 }
580 
581 void
gdk_monitor_set_scale_factor(GdkMonitor * monitor,int scale_factor)582 gdk_monitor_set_scale_factor (GdkMonitor *monitor,
583                               int         scale_factor)
584 {
585   if (monitor->scale_factor == scale_factor)
586     return;
587 
588   monitor->scale_factor = scale_factor;
589 
590   g_object_notify (G_OBJECT (monitor), "scale-factor");
591 }
592 
593 void
gdk_monitor_set_refresh_rate(GdkMonitor * monitor,int refresh_rate)594 gdk_monitor_set_refresh_rate (GdkMonitor *monitor,
595                               int         refresh_rate)
596 {
597   if (monitor->refresh_rate == refresh_rate)
598     return;
599 
600   monitor->refresh_rate = refresh_rate;
601 
602   g_object_notify (G_OBJECT (monitor), "refresh-rate");
603 }
604 
605 void
gdk_monitor_set_subpixel_layout(GdkMonitor * monitor,GdkSubpixelLayout subpixel_layout)606 gdk_monitor_set_subpixel_layout (GdkMonitor        *monitor,
607                                  GdkSubpixelLayout  subpixel_layout)
608 {
609   if (monitor->subpixel_layout == subpixel_layout)
610     return;
611 
612   monitor->subpixel_layout = subpixel_layout;
613 
614   g_object_notify (G_OBJECT (monitor), "subpixel-layout");
615 }
616 
617 void
gdk_monitor_invalidate(GdkMonitor * monitor)618 gdk_monitor_invalidate (GdkMonitor *monitor)
619 {
620   monitor->valid = FALSE;
621   g_object_notify (G_OBJECT (monitor), "valid");
622   g_signal_emit (monitor, signals[INVALIDATE], 0);
623 }
624 
625 /**
626  * gdk_monitor_is_valid: (attributes org.gtk.Method.get_property=valid)
627  * @monitor: a `GdkMonitor`
628  *
629  * Returns %TRUE if the @monitor object corresponds to a
630  * physical monitor.
631  *
632  * The @monitor becomes invalid when the physical monitor
633  * is unplugged or removed.
634  *
635  * Returns: %TRUE if the object corresponds to a physical monitor
636  */
637 gboolean
gdk_monitor_is_valid(GdkMonitor * monitor)638 gdk_monitor_is_valid (GdkMonitor *monitor)
639 {
640   g_return_val_if_fail (GDK_IS_MONITOR (monitor), FALSE);
641 
642   return monitor->valid;
643 }
644