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 * SECTION:gdkmonitor
30 * @Title: GdkMonitor
31 * @Short_description: Object representing an output
32 *
33 * GdkMonitor objects represent the individual outputs that are
34 * associated with a #GdkDisplay. GdkDisplay has APIs to enumerate
35 * monitors with gdk_display_get_monitors() and to find particular
36 * monitors with gdk_display_get_primary_monitor() or
37 * gdk_display_get_monitor_at_window().
38 *
39 * GdkMonitor was introduced in GTK+ 3.22 and supersedes earlier
40 * APIs in GdkScreen to obtain monitor-related information.
41 */
42
43enum {
44 PROP_0,
45 PROP_DISPLAY,
46 PROP_MANUFACTURER,
47 PROP_MODEL,
48 PROP_SCALE_FACTOR,
49 PROP_GEOMETRY,
50 PROP_WORKAREA,
51 PROP_WIDTH_MM,
52 PROP_HEIGHT_MM,
53 PROP_REFRESH_RATE,
54 PROP_SUBPIXEL_LAYOUT,
55 LAST_PROP
56};
57
58static GParamSpec *props[LAST_PROP] = { NULL, };
59
60enum {
61 INVALIDATE,
62 LAST_SIGNAL
63};
64
65static guint signals[LAST_SIGNAL] = { 0 };
66
67G_DEFINE_TYPE (GdkMonitor, gdk_monitor, G_TYPE_OBJECT)
68
69static void
70gdk_monitor_init (GdkMonitor *monitor)
71{
72 monitor->scale_factor = 1;
73}
74
75static void
76gdk_monitor_get_property (GObject *object,
77 guint prop_id,
78 GValue *value,
79 GParamSpec *pspec)
80{
81 GdkMonitor *monitor = GDK_MONITOR (object);
82
83 switch (prop_id)
84 {
85 case PROP_DISPLAY:
86 g_value_set_object (value, monitor->display);
87 break;
88
89 case PROP_MANUFACTURER:
90 g_value_set_string (value, monitor->manufacturer);
91 break;
92
93 case PROP_MODEL:
94 g_value_set_string (value, monitor->model);
95 break;
96
97 case PROP_SCALE_FACTOR:
98 g_value_set_int (value, monitor->scale_factor);
99 break;
100
101 case PROP_GEOMETRY:
102 g_value_set_boxed (value, &monitor->geometry);
103 break;
104
105 case PROP_WORKAREA:
106 {
107 GdkRectangle workarea;
108 gdk_monitor_get_workarea (monitor, &workarea);
109 g_value_set_boxed (value, &workarea);
110 }
111 break;
112
113 case PROP_WIDTH_MM:
114 g_value_set_int (value, monitor->width_mm);
115 break;
116
117 case PROP_HEIGHT_MM:
118 g_value_set_int (value, monitor->height_mm);
119 break;
120
121 case PROP_REFRESH_RATE:
122 g_value_set_int (value, monitor->refresh_rate);
123 break;
124
125 case PROP_SUBPIXEL_LAYOUT:
126 g_value_set_enum (value, monitor->subpixel_layout);
127 break;
128
129 default:
130 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
131 }
132}
133
134static void
135gdk_monitor_set_property (GObject *object,
136 guint prop_id,
137 const GValue *value,
138 GParamSpec *pspec)
139{
140 GdkMonitor *monitor = GDK_MONITOR (object);
141
142 switch (prop_id)
143 {
144 case PROP_DISPLAY:
145 monitor->display = g_value_get_object (value);
146 break;
147
148 default:
149 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
150 }
151}
152
153static void
154gdk_monitor_finalize (GObject *object)
155{
156 GdkMonitor *monitor = GDK_MONITOR (object);
157
158 g_free (monitor->manufacturer);
159 g_free (monitor->model);
160
161 G_OBJECT_CLASS (gdk_monitor_parent_class)->finalize (object);
162}
163
164static void
165gdk_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 props[PROP_DISPLAY] =
174 g_param_spec_object ("display",
175 "Display",
176 "The display of the monitor",
177 GDK_TYPE_DISPLAY,
178 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY);
179 props[PROP_MANUFACTURER] =
180 g_param_spec_string ("manufacturer",
181 "Manufacturer",
182 "The manufacturer name",
183 NULL,
184 G_PARAM_READABLE);
185 props[PROP_MODEL] =
186 g_param_spec_string ("model",
187 "Model",
188 "The model name",
189 NULL,
190 G_PARAM_READABLE);
191 props[PROP_SCALE_FACTOR] =
192 g_param_spec_int ("scale-factor",
193 "Scale factor",
194 "The scale factor",
195 0, G_MAXINT,
196 1,
197 G_PARAM_READABLE);
198 props[PROP_GEOMETRY] =
199 g_param_spec_boxed ("geometry",
200 "Geometry",
201 "The geometry of the monitor",
202 GDK_TYPE_RECTANGLE,
203 G_PARAM_READABLE);
204 props[PROP_WORKAREA] =
205 g_param_spec_boxed ("workarea",
206 "Workarea",
207 "The workarea of the monitor",
208 GDK_TYPE_RECTANGLE,
209 G_PARAM_READABLE);
210 props[PROP_WIDTH_MM] =
211 g_param_spec_int ("width-mm",
212 "Physical width",
213 "The width of the monitor, in millimeters",
214 0, G_MAXINT,
215 0,
216 G_PARAM_READABLE);
217 props[PROP_HEIGHT_MM] =
218 g_param_spec_int ("height-mm",
219 "Physical height",
220 "The height of the monitor, in millimeters",
221 0, G_MAXINT,
222 0,
223 G_PARAM_READABLE);
224 props[PROP_REFRESH_RATE] =
225 g_param_spec_int ("refresh-rate",
226 "Refresh rate",
227 "The refresh rate, in millihertz",
228 0, G_MAXINT,
229 0,
230 G_PARAM_READABLE);
231 props[PROP_SUBPIXEL_LAYOUT] =
232 g_param_spec_enum ("subpixel-layout",
233 "Subpixel layout",
234 "The subpixel layout",
235 GDK_TYPE_SUBPIXEL_LAYOUT,
236 GDK_SUBPIXEL_LAYOUT_UNKNOWN,
237 G_PARAM_READABLE);
238
239 g_object_class_install_properties (object_class, LAST_PROP, props);
240
241 signals[INVALIDATE] = g_signal_new ("invalidate",
242 G_TYPE_FROM_CLASS (object_class),
243 G_SIGNAL_RUN_FIRST,
244 0,
245 NULL, NULL,
246 g_cclosure_marshal_VOID__VOID,
247 G_TYPE_NONE, 0);
248}
249
250/**
251 * gdk_monitor_get_display:
252 * @monitor: a #GdkMonitor
253 *
254 * Gets the display that this monitor belongs to.
255 *
256 * Returns: (transfer none): the display
257 * Since: 3.22
258 */
259GdkDisplay *
260gdk_monitor_get_display (GdkMonitor *monitor)
261{
262 g_return_val_if_fail (GDK_IS_MONITOR (monitor), NULL);
263
264 return monitor->display;
265}
266
267/**
268 * gdk_monitor_get_geometry:
269 * @monitor: a #GdkMonitor
270 * @geometry: (out): a #GdkRectangle to be filled wiht the monitor geometry
271 *
272 * Retrieves the size and position of an individual monitor within the
273 * display coordinate space. The returned geometry is in ”application pixels”,
274 * not in ”device pixels” (see gdk_monitor_get_scale_factor()).
275 *
276 * Since: 3.22
277 */
278void
279gdk_monitor_get_geometry (GdkMonitor *monitor,
280 GdkRectangle *geometry)
281{
282 g_return_if_fail (GDK_IS_MONITOR (monitor));
283 g_return_if_fail (geometry != NULL);
284
285 *geometry = monitor->geometry;
286}
287
288/**
289 * gdk_monitor_get_workarea:
290 * @monitor: a #GdkMonitor
291 * @workarea: (out): a #GdkRectangle to be filled with
292 * the monitor workarea
293 *
294 * Retrieves the size and position of the “work area” on a monitor
295 * within the display coordinate space. The returned geometry is in
296 * ”application pixels”, not in ”device pixels” (see
297 * gdk_monitor_get_scale_factor()).
298 *
299 * The work area should be considered when positioning menus and
300 * similar popups, to avoid placing them below panels, docks or other
301 * desktop components.
302 *
303 * Note that not all backends may have a concept of workarea. This
304 * function will return the monitor geometry if a workarea is not
305 * available, or does not apply.
306 *
307 * Since: 3.22
308 */
309void
310gdk_monitor_get_workarea (GdkMonitor *monitor,
311 GdkRectangle *workarea)
312{
313 g_return_if_fail (GDK_IS_MONITOR (monitor));
314 g_return_if_fail (workarea != NULL);
315
316 if (GDK_MONITOR_GET_CLASS (monitor)->get_workarea)
317 GDK_MONITOR_GET_CLASS (monitor)->get_workarea (monitor, workarea);
318 else
319 *workarea = monitor->geometry;
320}
321
322/**
323 * gdk_monitor_get_width_mm:
324 * @monitor: a #GdkMonitor
325 *
326 * Gets the width in millimeters of the monitor.
327 *
328 * Returns: the physical width of the monitor
329 *
330 * Since: 3.22
331 */
332int
333gdk_monitor_get_width_mm (GdkMonitor *monitor)
334{
335 g_return_val_if_fail (GDK_IS_MONITOR (monitor), 0);
336
337 return monitor->width_mm;
338}
339
340/**
341 * gdk_monitor_get_height_mm:
342 * @monitor: a #GdkMonitor
343 *
344 * Gets the height in millimeters of the monitor.
345 *
346 * Returns: the physical height of the monitor
347 * Since: 3.22
348 */
349int
350gdk_monitor_get_height_mm (GdkMonitor *monitor)
351{
352 g_return_val_if_fail (GDK_IS_MONITOR (monitor), 0);
353
354 return monitor->height_mm;
355}
356
357/**
358 * gdk_monitor_get_manufacturer:
359 * @monitor: a #GdkMonitor
360 *
361 * Gets the name of the monitor's manufacturer, if available.
362 *
363 * Returns: (transfer none) (nullable): the name of the manufacturer, or %NULL
364 */
365const char *
366gdk_monitor_get_manufacturer (GdkMonitor *monitor)
367{
368 g_return_val_if_fail (GDK_IS_MONITOR (monitor), NULL);
369
370 return monitor->manufacturer;
371}
372
373/**
374 * gdk_monitor_get_model:
375 * @monitor: a #GdkMonitor
376 *
377 * Gets the a string identifying the monitor model, if available.
378 *
379 * Returns: (transfer none) (nullable): the monitor model, or %NULL
380 */
381const char *
382gdk_monitor_get_model (GdkMonitor *monitor)
383{
384 g_return_val_if_fail (GDK_IS_MONITOR (monitor), NULL);
385
386 return monitor->model;
387}
388
389/**
390 * gdk_monitor_get_scale_factor:
391 * @monitor: a #GdkMonitor
392 *
393 * Gets the internal scale factor that maps from monitor coordinates
394 * to the actual device pixels. On traditional systems this is 1, but
395 * on very high density outputs this can be a higher value (often 2).
396 *
397 * This can be used if you want to create pixel based data for a
398 * particular monitor, but most of the time you’re drawing to a window
399 * where it is better to use gdk_window_get_scale_factor() instead.
400 *
401 * Returns: the scale factor
402 * Since: 3.22
403 */
404int
405gdk_monitor_get_scale_factor (GdkMonitor *monitor)
406{
407 g_return_val_if_fail (GDK_IS_MONITOR (monitor), 0);
408
409 return monitor->scale_factor;
410}
411
412/**
413 * gdk_monitor_get_refresh_rate:
414 * @monitor: a #GdkMonitor
415 *
416 * Gets the refresh rate of the monitor, if available.
417 *
418 * The value is in milli-Hertz, so a refresh rate of 60Hz
419 * is returned as 60000.
420 *
421 * Returns: the refresh rate in milli-Hertz, or 0
422 * Since: 3.22
423 */
424int
425gdk_monitor_get_refresh_rate (GdkMonitor *monitor)
426{
427 g_return_val_if_fail (GDK_IS_MONITOR (monitor), 0);
428
429 return monitor->refresh_rate;
430}
431
432/**
433 * gdk_monitor_get_subpixel_layout:
434 * @monitor: a #GdkMonitor
435 *
436 * Gets information about the layout of red, green and blue
437 * primaries for each pixel in this monitor, if available.
438 *
439 * Returns: the subpixel layout
440 * Since: 3.22
441 */
442GdkSubpixelLayout
443gdk_monitor_get_subpixel_layout (GdkMonitor *monitor)
444{
445 g_return_val_if_fail (GDK_IS_MONITOR (monitor), GDK_SUBPIXEL_LAYOUT_UNKNOWN);
446
447 return monitor->subpixel_layout;
448}
449
450/**
451 * gdk_monitor_is_primary:
452 * @monitor: a #GdkMonitor
453 *
454 * Gets whether this monitor should be considered primary
455 * (see gdk_display_get_primary_monitor()).
456 *
457 * Returns: %TRUE if @monitor is primary
458 * Since: 3.22
459 */
460gboolean
461gdk_monitor_is_primary (GdkMonitor *monitor)
462{
463 g_return_val_if_fail (GDK_IS_MONITOR (monitor), FALSE);
464
465 return monitor == gdk_display_get_primary_monitor (monitor->display);
466}
467
468GdkMonitor *
469gdk_monitor_new (GdkDisplay *display)
470{
471 return GDK_MONITOR (g_object_new (GDK_TYPE_MONITOR,
472 "display", display,
473 NULL));
474}
475
476void
477gdk_monitor_set_manufacturer (GdkMonitor *monitor,
478 const char *manufacturer)
479{
480 g_free (monitor->manufacturer);
481 monitor->manufacturer = g_strdup (manufacturer);
482
483 g_object_notify (G_OBJECT (monitor), "manufacturer");
484}
485
486void
487gdk_monitor_set_model (GdkMonitor *monitor,
488 const char *model)
489{
490 g_free (monitor->model);
491 monitor->model = g_strdup (model);
492
493 g_object_notify (G_OBJECT (monitor), "model");
494}
495
496void
497gdk_monitor_set_position (GdkMonitor *monitor,
498 int x,
499 int y)
500{
501 g_object_freeze_notify (G_OBJECT (monitor));
502
503 if (monitor->geometry.x != x)
504 {
505 monitor->geometry.x = x;
506 g_object_notify (G_OBJECT (monitor), "geometry");
507 }
508
509 if (monitor->geometry.y != y)
510 {
511 monitor->geometry.y = y;
512 g_object_notify (G_OBJECT (monitor), "geometry");
513 }
514
515 g_object_thaw_notify (G_OBJECT (monitor));
516}
517
518void
519gdk_monitor_set_size (GdkMonitor *monitor,
520 int width,
521 int height)
522{
523 g_object_freeze_notify (G_OBJECT (monitor));
524
525 if (monitor->geometry.width != width)
526 {
527 monitor->geometry.width = width;
528 g_object_notify (G_OBJECT (monitor), "geometry");
529 }
530
531 if (monitor->geometry.height != height)
532 {
533 monitor->geometry.height = height;
534 g_object_notify (G_OBJECT (monitor), "geometry");
535 }
536
537 g_object_thaw_notify (G_OBJECT (monitor));
538}
539
540void
541gdk_monitor_set_physical_size (GdkMonitor *monitor,
542 int width_mm,
543 int height_mm)
544{
545 g_object_freeze_notify (G_OBJECT (monitor));
546
547 if (monitor->width_mm != width_mm)
548 {
549 monitor->width_mm = width_mm;
550 g_object_notify (G_OBJECT (monitor), "width-mm");
551 }
552
553 if (monitor->height_mm != height_mm)
554 {
555 monitor->height_mm = height_mm;
556 g_object_notify (G_OBJECT (monitor), "height-mm");
557 }
558
559 g_object_thaw_notify (G_OBJECT (monitor));
560}
561
562void
563gdk_monitor_set_scale_factor (GdkMonitor *monitor,
564 int scale_factor)
565{
566 if (monitor->scale_factor == scale_factor)
567 return;
568
569 monitor->scale_factor = scale_factor;
570
571 g_object_notify (G_OBJECT (monitor), "scale-factor");
572}
573
574void
575gdk_monitor_set_refresh_rate (GdkMonitor *monitor,
576 int refresh_rate)
577{
578 if (monitor->refresh_rate == refresh_rate)
579 return;
580
581 monitor->refresh_rate = refresh_rate;
582
583 g_object_notify (G_OBJECT (monitor), "refresh-rate");
584}
585
586void
587gdk_monitor_set_subpixel_layout (GdkMonitor *monitor,
588 GdkSubpixelLayout subpixel_layout)
589{
590 if (monitor->subpixel_layout == subpixel_layout)
591 return;
592
593 monitor->subpixel_layout = subpixel_layout;
594
595 g_object_notify (G_OBJECT (monitor), "subpixel-layout");
596}
597
598void
599gdk_monitor_invalidate (GdkMonitor *monitor)
600{
601 g_signal_emit (monitor, signals[INVALIDATE], 0);
602}
603