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 | |
43 | enum { |
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 | |
58 | static GParamSpec *props[LAST_PROP] = { NULL, }; |
59 | |
60 | enum { |
61 | INVALIDATE, |
62 | LAST_SIGNAL |
63 | }; |
64 | |
65 | static guint signals[LAST_SIGNAL] = { 0 }; |
66 | |
67 | G_DEFINE_TYPE (GdkMonitor, gdk_monitor, G_TYPE_OBJECT) |
68 | |
69 | static void |
70 | gdk_monitor_init (GdkMonitor *monitor) |
71 | { |
72 | monitor->scale_factor = 1; |
73 | } |
74 | |
75 | static void |
76 | gdk_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 | |
134 | static void |
135 | gdk_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 | |
153 | static void |
154 | gdk_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 | |
164 | static void |
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 | 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 | */ |
259 | GdkDisplay * |
260 | gdk_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 | */ |
278 | void |
279 | gdk_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 | */ |
309 | void |
310 | gdk_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 | */ |
332 | int |
333 | gdk_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 | */ |
349 | int |
350 | gdk_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 | */ |
365 | const char * |
366 | gdk_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 | */ |
381 | const char * |
382 | gdk_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 | */ |
404 | int |
405 | gdk_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 | */ |
424 | int |
425 | gdk_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 | */ |
442 | GdkSubpixelLayout |
443 | gdk_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 | */ |
460 | gboolean |
461 | gdk_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 | |
468 | GdkMonitor * |
469 | gdk_monitor_new (GdkDisplay *display) |
470 | { |
471 | return GDK_MONITOR (g_object_new (GDK_TYPE_MONITOR, |
472 | "display" , display, |
473 | NULL)); |
474 | } |
475 | |
476 | void |
477 | gdk_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 | |
486 | void |
487 | gdk_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 | |
496 | void |
497 | gdk_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 | |
518 | void |
519 | gdk_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 | |
540 | void |
541 | gdk_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 | |
562 | void |
563 | gdk_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 | |
574 | void |
575 | gdk_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 | |
586 | void |
587 | gdk_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 | |
598 | void |
599 | gdk_monitor_invalidate (GdkMonitor *monitor) |
600 | { |
601 | g_signal_emit (monitor, signals[INVALIDATE], 0); |
602 | } |
603 | |