1 | /* |
2 | * gdkscreen-x11.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, see <http://www.gnu.org/licenses/>. |
20 | */ |
21 | |
22 | #include "config.h" |
23 | |
24 | #include "gdkscreen-x11.h" |
25 | #include "gdkdisplay-x11.h" |
26 | #include "gdkprivate-x11.h" |
27 | #include "xsettings-client.h" |
28 | #include "gdkmonitor-x11.h" |
29 | |
30 | #include <glib.h> |
31 | |
32 | #include <stdlib.h> |
33 | #include <string.h> |
34 | |
35 | #include <X11/Xatom.h> |
36 | |
37 | #ifdef HAVE_XFREE_XINERAMA |
38 | #include <X11/extensions/Xinerama.h> |
39 | #endif |
40 | |
41 | #ifdef HAVE_RANDR |
42 | #include <X11/extensions/Xrandr.h> |
43 | #endif |
44 | |
45 | #ifdef HAVE_XFIXES |
46 | #include <X11/extensions/Xfixes.h> |
47 | #endif |
48 | |
49 | static void gdk_x11_screen_dispose (GObject *object); |
50 | static void gdk_x11_screen_finalize (GObject *object); |
51 | static void init_randr_support (GdkX11Screen *screen); |
52 | static void process_monitors_change (GdkX11Screen *screen); |
53 | |
54 | enum |
55 | { |
56 | WINDOW_MANAGER_CHANGED, |
57 | LAST_SIGNAL |
58 | }; |
59 | |
60 | static guint signals[LAST_SIGNAL] = { 0 }; |
61 | |
62 | G_DEFINE_TYPE (GdkX11Screen, gdk_x11_screen, G_TYPE_OBJECT) |
63 | |
64 | typedef struct _NetWmSupportedAtoms NetWmSupportedAtoms; |
65 | |
66 | struct _NetWmSupportedAtoms |
67 | { |
68 | Atom *atoms; |
69 | gulong n_atoms; |
70 | }; |
71 | |
72 | static void |
73 | gdk_x11_screen_init (GdkX11Screen *screen) |
74 | { |
75 | } |
76 | |
77 | static void |
78 | gdk_x11_screen_dispose (GObject *object) |
79 | { |
80 | GdkX11Screen *x11_screen = GDK_X11_SCREEN (object); |
81 | int i; |
82 | |
83 | for (i = 0; i < 32; ++i) |
84 | { |
85 | if (x11_screen->subwindow_gcs[i]) |
86 | { |
87 | XFreeGC (x11_screen->xdisplay, x11_screen->subwindow_gcs[i]); |
88 | x11_screen->subwindow_gcs[i] = 0; |
89 | } |
90 | } |
91 | |
92 | _gdk_x11_xsettings_finish (x11_screen); |
93 | |
94 | G_OBJECT_CLASS (gdk_x11_screen_parent_class)->dispose (object); |
95 | |
96 | x11_screen->xdisplay = NULL; |
97 | x11_screen->xscreen = NULL; |
98 | x11_screen->xroot_window = None; |
99 | x11_screen->wmspec_check_window = None; |
100 | } |
101 | |
102 | static void |
103 | gdk_x11_screen_finalize (GObject *object) |
104 | { |
105 | GdkX11Screen *x11_screen = GDK_X11_SCREEN (object); |
106 | |
107 | g_free (mem: x11_screen->window_manager_name); |
108 | |
109 | G_OBJECT_CLASS (gdk_x11_screen_parent_class)->finalize (object); |
110 | } |
111 | |
112 | /** |
113 | * gdk_x11_screen_get_monitor_output: |
114 | * @screen: a `GdkX11Screen` |
115 | * @monitor_num: number of the monitor, between 0 and gdk_screen_get_n_monitors (screen) |
116 | * |
117 | * Gets the XID of the specified output/monitor. |
118 | * If the X server does not support version 1.2 of the RANDR |
119 | * extension, 0 is returned. |
120 | * |
121 | * Returns: the XID of the monitor |
122 | */ |
123 | XID |
124 | gdk_x11_screen_get_monitor_output (GdkX11Screen *x11_screen, |
125 | int monitor_num) |
126 | { |
127 | GdkX11Display *x11_display = GDK_X11_DISPLAY (x11_screen->display); |
128 | GdkX11Monitor *monitor; |
129 | XID output; |
130 | |
131 | g_return_val_if_fail (monitor_num >= 0, None); |
132 | g_return_val_if_fail (monitor_num < g_list_model_get_n_items (G_LIST_MODEL (x11_display->monitors)), None); |
133 | |
134 | monitor = g_list_model_get_item (list: G_LIST_MODEL (ptr: x11_display->monitors), position: monitor_num); |
135 | output = monitor->output; |
136 | g_object_unref (object: monitor); |
137 | |
138 | return output; |
139 | } |
140 | |
141 | static int |
142 | get_current_desktop (GdkX11Screen *screen) |
143 | { |
144 | Display *display; |
145 | Window win; |
146 | Atom current_desktop, type; |
147 | int format; |
148 | unsigned long n_items, bytes_after; |
149 | unsigned char *data_return = NULL; |
150 | int workspace = 0; |
151 | |
152 | if (!gdk_x11_screen_supports_net_wm_hint (screen, |
153 | property_name: g_intern_static_string (string: "_NET_CURRENT_DESKTOP" ))) |
154 | return workspace; |
155 | |
156 | display = GDK_DISPLAY_XDISPLAY (GDK_SCREEN_DISPLAY (screen)); |
157 | win = XRootWindow (display, gdk_x11_screen_get_screen_number (screen)); |
158 | |
159 | current_desktop = XInternAtom (display, "_NET_CURRENT_DESKTOP" , True); |
160 | |
161 | XGetWindowProperty (display, |
162 | win, |
163 | current_desktop, |
164 | 0, G_MAXLONG, |
165 | False, XA_CARDINAL, |
166 | &type, &format, &n_items, &bytes_after, |
167 | &data_return); |
168 | |
169 | if (type == XA_CARDINAL && format == 32 && n_items > 0) |
170 | workspace = ((long *) data_return)[0]; |
171 | |
172 | if (data_return) |
173 | XFree (data_return); |
174 | |
175 | return workspace; |
176 | } |
177 | |
178 | gboolean |
179 | _gdk_x11_screen_get_monitor_work_area (GdkX11Screen *x11_screen, |
180 | GdkMonitor *monitor, |
181 | GdkRectangle *area) |
182 | { |
183 | Display *xdisplay; |
184 | Atom net_workareas; |
185 | int current_desktop; |
186 | char *workareas_dn_name; |
187 | Atom workareas_dn; |
188 | int screen_number; |
189 | Window xroot; |
190 | int result; |
191 | Atom type; |
192 | int format; |
193 | gulong num; |
194 | gulong leftovers; |
195 | guchar *ret_workarea; |
196 | long *workareas; |
197 | GdkRectangle geometry; |
198 | int i; |
199 | |
200 | if (!gdk_x11_screen_supports_net_wm_hint (screen: x11_screen, |
201 | property_name: g_intern_static_string (string: "_GTK_WORKAREAS" ))) |
202 | return FALSE; |
203 | |
204 | xdisplay = gdk_x11_display_get_xdisplay (display: x11_screen->display); |
205 | net_workareas = XInternAtom (xdisplay, "_GTK_WORKAREAS" , False); |
206 | |
207 | if (net_workareas == None) |
208 | return FALSE; |
209 | |
210 | current_desktop = get_current_desktop (screen: x11_screen); |
211 | workareas_dn_name = g_strdup_printf (format: "_GTK_WORKAREAS_D%d" , current_desktop); |
212 | workareas_dn = XInternAtom (xdisplay, workareas_dn_name, True); |
213 | g_free (mem: workareas_dn_name); |
214 | |
215 | if (workareas_dn == None) |
216 | return FALSE; |
217 | |
218 | screen_number = gdk_x11_screen_get_screen_number (screen: x11_screen); |
219 | xroot = XRootWindow (xdisplay, screen_number); |
220 | |
221 | gdk_x11_display_error_trap_push (display: x11_screen->display); |
222 | |
223 | ret_workarea = NULL; |
224 | result = XGetWindowProperty (xdisplay, |
225 | xroot, |
226 | workareas_dn, |
227 | 0, |
228 | G_MAXLONG, |
229 | False, |
230 | AnyPropertyType, |
231 | &type, |
232 | &format, |
233 | &num, |
234 | &leftovers, |
235 | &ret_workarea); |
236 | |
237 | gdk_x11_display_error_trap_pop_ignored (display: x11_screen->display); |
238 | |
239 | if (result != Success || |
240 | type == None || |
241 | format == 0 || |
242 | leftovers || |
243 | num % 4 != 0) |
244 | { |
245 | XFree (ret_workarea); |
246 | |
247 | return FALSE; |
248 | } |
249 | |
250 | workareas = (long *) ret_workarea; |
251 | |
252 | gdk_monitor_get_geometry (monitor, geometry: &geometry); |
253 | *area = geometry; |
254 | |
255 | for (i = 0; i < num / 4; i++) |
256 | { |
257 | GdkRectangle work_area; |
258 | |
259 | work_area = (GdkRectangle) { |
260 | .x = workareas[0] / x11_screen->surface_scale, |
261 | .y = workareas[1] / x11_screen->surface_scale, |
262 | .width = workareas[2] / x11_screen->surface_scale, |
263 | .height = workareas[3] / x11_screen->surface_scale, |
264 | }; |
265 | |
266 | if (gdk_rectangle_intersect (src1: area, src2: &work_area, dest: &work_area)) |
267 | *area = work_area; |
268 | |
269 | workareas += 4; |
270 | } |
271 | |
272 | XFree (ret_workarea); |
273 | |
274 | return TRUE; |
275 | } |
276 | |
277 | void |
278 | gdk_x11_screen_get_work_area (GdkX11Screen *x11_screen, |
279 | GdkRectangle *area) |
280 | { |
281 | Atom workarea; |
282 | Atom type; |
283 | Window win; |
284 | int format; |
285 | gulong num; |
286 | gulong leftovers; |
287 | gulong max_len = 4 * 32; |
288 | guchar *ret_workarea = NULL; |
289 | long *workareas; |
290 | int result; |
291 | int desktop; |
292 | Display *display; |
293 | |
294 | display = GDK_SCREEN_XDISPLAY (x11_screen); |
295 | workarea = XInternAtom (display, "_NET_WORKAREA" , True); |
296 | |
297 | /* Defaults in case of error */ |
298 | area->x = 0; |
299 | area->y = 0; |
300 | area->width = WidthOfScreen (x11_screen->xscreen); |
301 | area->height = HeightOfScreen (x11_screen->xscreen); |
302 | |
303 | if (!gdk_x11_screen_supports_net_wm_hint (screen: x11_screen, |
304 | property_name: g_intern_static_string (string: "_NET_WORKAREA" ))) |
305 | return; |
306 | |
307 | if (workarea == None) |
308 | return; |
309 | |
310 | win = XRootWindow (display, gdk_x11_screen_get_screen_number (screen: x11_screen)); |
311 | result = XGetWindowProperty (display, |
312 | win, |
313 | workarea, |
314 | 0, |
315 | max_len, |
316 | False, |
317 | AnyPropertyType, |
318 | &type, |
319 | &format, |
320 | &num, |
321 | &leftovers, |
322 | &ret_workarea); |
323 | if (result != Success || |
324 | type == None || |
325 | format == 0 || |
326 | leftovers || |
327 | num % 4 != 0) |
328 | goto out; |
329 | |
330 | desktop = get_current_desktop (screen: x11_screen); |
331 | if (desktop + 1 > num / 4) /* fvwm gets this wrong */ |
332 | goto out; |
333 | |
334 | workareas = (long *) ret_workarea; |
335 | area->x = workareas[desktop * 4]; |
336 | area->y = workareas[desktop * 4 + 1]; |
337 | area->width = workareas[desktop * 4 + 2]; |
338 | area->height = workareas[desktop * 4 + 3]; |
339 | |
340 | area->x /= x11_screen->surface_scale; |
341 | area->y /= x11_screen->surface_scale; |
342 | area->width /= x11_screen->surface_scale; |
343 | area->height /= x11_screen->surface_scale; |
344 | |
345 | out: |
346 | if (ret_workarea) |
347 | XFree (ret_workarea); |
348 | } |
349 | |
350 | /** |
351 | * gdk_x11_screen_get_xscreen: |
352 | * @screen: a `GdkX11Screen` |
353 | * |
354 | * Returns the screen of a `GdkX11Screen`. |
355 | * |
356 | * Returns: (transfer none): an Xlib Screen* |
357 | */ |
358 | Screen * |
359 | gdk_x11_screen_get_xscreen (GdkX11Screen *screen) |
360 | { |
361 | return screen->xscreen; |
362 | } |
363 | |
364 | /** |
365 | * gdk_x11_screen_get_screen_number: |
366 | * @screen: a `GdkX11Screen` |
367 | * |
368 | * Returns the index of a `GdkX11Screen`. |
369 | * |
370 | * Returns: the position of @screen among the screens |
371 | * of its display |
372 | */ |
373 | int |
374 | gdk_x11_screen_get_screen_number (GdkX11Screen *screen) |
375 | { |
376 | return screen->screen_num; |
377 | } |
378 | |
379 | static void |
380 | notify_surface_monitor_change (GdkX11Display *display, |
381 | GdkMonitor *monitor) |
382 | { |
383 | GHashTableIter iter; |
384 | GdkSurface *surface; |
385 | |
386 | /* We iterate the surfaces via the hash table here because it's the only |
387 | * thing that contains all the surfaces. |
388 | */ |
389 | if (display->xid_ht == NULL) |
390 | return; |
391 | |
392 | g_hash_table_iter_init (iter: &iter, hash_table: display->xid_ht); |
393 | while (g_hash_table_iter_next (iter: &iter, NULL, value: (gpointer *)&surface)) |
394 | { |
395 | gdk_x11_surface_check_monitor (surface, monitor); |
396 | } |
397 | } |
398 | |
399 | static GdkX11Monitor * |
400 | find_monitor_by_output (GdkX11Display *x11_display, XID output) |
401 | { |
402 | int i; |
403 | |
404 | for (i = 0; i < g_list_model_get_n_items (list: G_LIST_MODEL (ptr: x11_display->monitors)); i++) |
405 | { |
406 | GdkX11Monitor *monitor = g_list_model_get_item (list: G_LIST_MODEL (ptr: x11_display->monitors), position: i); |
407 | g_object_unref (object: monitor); |
408 | if (monitor->output == output) |
409 | return monitor; |
410 | } |
411 | |
412 | return NULL; |
413 | } |
414 | |
415 | static GdkSubpixelLayout |
416 | translate_subpixel_order (int subpixel) |
417 | { |
418 | switch (subpixel) |
419 | { |
420 | case 1: return GDK_SUBPIXEL_LAYOUT_HORIZONTAL_RGB; |
421 | case 2: return GDK_SUBPIXEL_LAYOUT_HORIZONTAL_BGR; |
422 | case 3: return GDK_SUBPIXEL_LAYOUT_VERTICAL_RGB; |
423 | case 4: return GDK_SUBPIXEL_LAYOUT_VERTICAL_BGR; |
424 | case 5: return GDK_SUBPIXEL_LAYOUT_NONE; |
425 | default: return GDK_SUBPIXEL_LAYOUT_UNKNOWN; |
426 | } |
427 | } |
428 | |
429 | static gboolean |
430 | init_randr15 (GdkX11Screen *x11_screen) |
431 | { |
432 | #ifdef HAVE_RANDR15 |
433 | GdkDisplay *display = GDK_SCREEN_DISPLAY (x11_screen); |
434 | GdkX11Display *x11_display = GDK_X11_DISPLAY (display); |
435 | XRRScreenResources *resources; |
436 | RROutput primary_output = None; |
437 | RROutput first_output = None; |
438 | int i; |
439 | XRRMonitorInfo *rr_monitors; |
440 | int num_rr_monitors; |
441 | |
442 | if (!x11_display->have_randr15) |
443 | return FALSE; |
444 | |
445 | resources = XRRGetScreenResourcesCurrent (dpy: x11_screen->xdisplay, |
446 | window: x11_screen->xroot_window); |
447 | if (!resources) |
448 | return FALSE; |
449 | |
450 | rr_monitors = XRRGetMonitors (dpy: x11_screen->xdisplay, |
451 | window: x11_screen->xroot_window, |
452 | True, |
453 | nmonitors: &num_rr_monitors); |
454 | if (!rr_monitors) |
455 | return FALSE; |
456 | |
457 | for (i = 0; i < g_list_model_get_n_items (list: G_LIST_MODEL (ptr: x11_display->monitors)); i++) |
458 | { |
459 | GdkX11Monitor *monitor = g_list_model_get_item (list: G_LIST_MODEL (ptr: x11_display->monitors), position: i); |
460 | monitor->add = FALSE; |
461 | monitor->remove = TRUE; |
462 | g_object_unref (object: monitor); |
463 | } |
464 | |
465 | for (i = 0; i < num_rr_monitors; i++) |
466 | { |
467 | RROutput output = rr_monitors[i].outputs[0]; |
468 | XRROutputInfo *output_info; |
469 | GdkX11Monitor *monitor; |
470 | GdkRectangle geometry; |
471 | GdkRectangle newgeo; |
472 | char *name; |
473 | char *manufacturer = NULL; |
474 | int refresh_rate = 0; |
475 | |
476 | gdk_x11_display_error_trap_push (display); |
477 | output_info = XRRGetOutputInfo (dpy: x11_screen->xdisplay, resources, output); |
478 | if (gdk_x11_display_error_trap_pop (display)) |
479 | continue; |
480 | |
481 | if (output_info == NULL) |
482 | continue; |
483 | |
484 | if (output_info->connection == RR_Disconnected) |
485 | { |
486 | XRRFreeOutputInfo (outputInfo: output_info); |
487 | continue; |
488 | } |
489 | |
490 | if (first_output == None) |
491 | first_output = output; |
492 | |
493 | if (output_info->crtc) |
494 | { |
495 | XRRCrtcInfo *crtc; |
496 | int j; |
497 | |
498 | gdk_x11_display_error_trap_push (display); |
499 | crtc = XRRGetCrtcInfo (dpy: x11_screen->xdisplay, resources, |
500 | crtc: output_info->crtc); |
501 | if (gdk_x11_display_error_trap_pop (display)) |
502 | { |
503 | XRRFreeOutputInfo (outputInfo: output_info); |
504 | continue; |
505 | } |
506 | |
507 | for (j = 0; j < resources->nmode; j++) |
508 | { |
509 | XRRModeInfo *xmode = &resources->modes[j]; |
510 | if (xmode->id == crtc->mode) |
511 | { |
512 | if (xmode->hTotal != 0 && xmode->vTotal != 0) |
513 | refresh_rate = (1000 * xmode->dotClock) / (xmode->hTotal * xmode->vTotal); |
514 | break; |
515 | } |
516 | } |
517 | |
518 | XRRFreeCrtcInfo (crtcInfo: crtc); |
519 | } |
520 | |
521 | monitor = find_monitor_by_output (x11_display, output); |
522 | if (monitor) |
523 | monitor->remove = FALSE; |
524 | else |
525 | { |
526 | monitor = g_object_new (GDK_TYPE_X11_MONITOR, |
527 | first_property_name: "display" , display, |
528 | NULL); |
529 | monitor->output = output; |
530 | monitor->add = TRUE; |
531 | g_list_store_append (store: x11_display->monitors, item: monitor); |
532 | } |
533 | |
534 | /* Fetch minimal manufacturer information (PNP ID) from EDID */ |
535 | { |
536 | #define EDID_LENGTH 128 |
537 | Atom actual_type, edid_atom; |
538 | char tmp[3]; |
539 | int actual_format; |
540 | unsigned char *prop; |
541 | unsigned long nbytes, bytes_left; |
542 | Display *disp = GDK_DISPLAY_XDISPLAY (x11_display); |
543 | |
544 | edid_atom = XInternAtom (disp, RR_PROPERTY_RANDR_EDID, FALSE); |
545 | |
546 | XRRGetOutputProperty (dpy: disp, output, |
547 | property: edid_atom, |
548 | offset: 0, |
549 | EDID_LENGTH, |
550 | FALSE, |
551 | FALSE, |
552 | AnyPropertyType, |
553 | actual_type: &actual_type, |
554 | actual_format: &actual_format, |
555 | nitems: &nbytes, |
556 | bytes_after: &bytes_left, |
557 | prop: &prop); |
558 | |
559 | // Check partial EDID header (whole header: 00 ff ff ff ff ff ff 00) |
560 | if (nbytes >= EDID_LENGTH && prop[0] == 0x00 && prop[1] == 0xff) |
561 | { |
562 | /* decode the Vendor ID from three 5 bit words packed into 2 bytes |
563 | * /--08--\/--09--\ |
564 | * 7654321076543210 |
565 | * |\---/\---/\---/ |
566 | * R C1 C2 C3 */ |
567 | tmp[0] = 'A' + ((prop[8] & 0x7c) / 4) - 1; |
568 | tmp[1] = 'A' + ((prop[8] & 0x3) * 8) + ((prop[9] & 0xe0) / 32) - 1; |
569 | tmp[2] = 'A' + (prop[9] & 0x1f) - 1; |
570 | |
571 | manufacturer = g_strndup (str: tmp, n: sizeof (tmp)); |
572 | } |
573 | |
574 | XFree(prop); |
575 | #undef EDID_LENGTH |
576 | } |
577 | |
578 | gdk_monitor_get_geometry (GDK_MONITOR (monitor), geometry: &geometry); |
579 | name = g_strndup (str: output_info->name, n: output_info->nameLen); |
580 | |
581 | newgeo.x = rr_monitors[i].x / x11_screen->surface_scale; |
582 | newgeo.y = rr_monitors[i].y / x11_screen->surface_scale; |
583 | newgeo.width = rr_monitors[i].width / x11_screen->surface_scale; |
584 | newgeo.height = rr_monitors[i].height / x11_screen->surface_scale; |
585 | |
586 | gdk_monitor_set_geometry (GDK_MONITOR (monitor), geometry: &newgeo); |
587 | gdk_monitor_set_physical_size (GDK_MONITOR (monitor), |
588 | width_mm: rr_monitors[i].mwidth, |
589 | height_mm: rr_monitors[i].mheight); |
590 | gdk_monitor_set_subpixel_layout (GDK_MONITOR (monitor), |
591 | subpixel: translate_subpixel_order (subpixel: output_info->subpixel_order)); |
592 | gdk_monitor_set_refresh_rate (GDK_MONITOR (monitor), refresh_rate); |
593 | gdk_monitor_set_scale_factor (GDK_MONITOR (monitor), scale: x11_screen->surface_scale); |
594 | gdk_monitor_set_model (GDK_MONITOR (monitor), model: name); |
595 | gdk_monitor_set_connector (GDK_MONITOR (monitor), connector: name); |
596 | gdk_monitor_set_manufacturer (GDK_MONITOR (monitor), manufacturer); |
597 | g_free (mem: manufacturer); |
598 | g_free (mem: name); |
599 | |
600 | if (rr_monitors[i].primary) |
601 | primary_output = monitor->output; |
602 | |
603 | XRRFreeOutputInfo (outputInfo: output_info); |
604 | } |
605 | |
606 | XRRFreeMonitors (monitors: rr_monitors); |
607 | XRRFreeScreenResources (resources); |
608 | |
609 | for (i = g_list_model_get_n_items (list: G_LIST_MODEL (ptr: x11_display->monitors)) - 1; i >= 0; i--) |
610 | { |
611 | GdkX11Monitor *monitor = g_list_model_get_item (list: G_LIST_MODEL (ptr: x11_display->monitors), position: i); |
612 | |
613 | notify_surface_monitor_change (display: x11_display, GDK_MONITOR (monitor)); |
614 | if (monitor->remove) |
615 | { |
616 | g_list_store_remove (store: x11_display->monitors, position: i); |
617 | gdk_monitor_invalidate (GDK_MONITOR (monitor)); |
618 | } |
619 | g_object_unref (object: monitor); |
620 | } |
621 | |
622 | x11_display->primary_monitor = 0; |
623 | for (i = 0; i < g_list_model_get_n_items (list: G_LIST_MODEL (ptr: x11_display->monitors)); i++) |
624 | { |
625 | GdkX11Monitor *monitor = g_list_model_get_item (list: G_LIST_MODEL (ptr: x11_display->monitors), position: i); |
626 | g_object_unref (object: monitor); |
627 | if (monitor->output == primary_output) |
628 | { |
629 | x11_display->primary_monitor = i; |
630 | break; |
631 | } |
632 | |
633 | /* No RandR1.3+ available or no primary set, fall back to prefer LVDS as primary if present */ |
634 | if (primary_output == None && |
635 | g_ascii_strncasecmp (s1: gdk_monitor_get_model (GDK_MONITOR (monitor)), s2: "LVDS" , n: 4) == 0) |
636 | { |
637 | x11_display->primary_monitor = i; |
638 | break; |
639 | } |
640 | |
641 | /* No primary specified and no LVDS found */ |
642 | if (monitor->output == first_output) |
643 | x11_display->primary_monitor = i; |
644 | } |
645 | |
646 | return g_list_model_get_n_items (list: G_LIST_MODEL (ptr: x11_display->monitors)) > 0; |
647 | #endif |
648 | |
649 | return FALSE; |
650 | } |
651 | |
652 | static gboolean |
653 | init_randr13 (GdkX11Screen *x11_screen) |
654 | { |
655 | #ifdef HAVE_RANDR |
656 | GdkDisplay *display = GDK_SCREEN_DISPLAY (x11_screen); |
657 | GdkX11Display *x11_display = GDK_X11_DISPLAY (display); |
658 | XRRScreenResources *resources; |
659 | RROutput primary_output = None; |
660 | RROutput first_output = None; |
661 | int i; |
662 | |
663 | if (!x11_display->have_randr13) |
664 | return FALSE; |
665 | |
666 | resources = XRRGetScreenResourcesCurrent (dpy: x11_screen->xdisplay, |
667 | window: x11_screen->xroot_window); |
668 | if (!resources) |
669 | return FALSE; |
670 | |
671 | for (i = 0; i < g_list_model_get_n_items (list: G_LIST_MODEL (ptr: x11_display->monitors)); i++) |
672 | { |
673 | GdkX11Monitor *monitor = g_list_model_get_item (list: G_LIST_MODEL (ptr: x11_display->monitors), position: i); |
674 | monitor->add = FALSE; |
675 | monitor->remove = TRUE; |
676 | g_object_unref (object: monitor); |
677 | } |
678 | |
679 | for (i = 0; i < resources->noutput; ++i) |
680 | { |
681 | RROutput output = resources->outputs[i]; |
682 | XRROutputInfo *output_info = |
683 | XRRGetOutputInfo (dpy: x11_screen->xdisplay, resources, output); |
684 | |
685 | if (output_info->connection == RR_Disconnected) |
686 | { |
687 | XRRFreeOutputInfo (outputInfo: output_info); |
688 | continue; |
689 | } |
690 | |
691 | if (output_info->crtc) |
692 | { |
693 | GdkX11Monitor *monitor; |
694 | XRRCrtcInfo *crtc = XRRGetCrtcInfo (dpy: x11_screen->xdisplay, resources, crtc: output_info->crtc); |
695 | char *name; |
696 | GdkRectangle geometry; |
697 | GdkRectangle newgeo; |
698 | int j; |
699 | int refresh_rate = 0; |
700 | |
701 | for (j = 0; j < resources->nmode; j++) |
702 | { |
703 | XRRModeInfo *xmode = &resources->modes[j]; |
704 | if (xmode->id == crtc->mode) |
705 | { |
706 | if (xmode->hTotal != 0 && xmode->vTotal != 0) |
707 | refresh_rate = (1000 * xmode->dotClock) / (xmode->hTotal * xmode->vTotal); |
708 | break; |
709 | } |
710 | } |
711 | |
712 | monitor = find_monitor_by_output (x11_display, output); |
713 | if (monitor) |
714 | monitor->remove = FALSE; |
715 | else |
716 | { |
717 | monitor = g_object_new (object_type: gdk_x11_monitor_get_type (), |
718 | first_property_name: "display" , display, |
719 | NULL); |
720 | monitor->output = output; |
721 | monitor->add = TRUE; |
722 | g_list_store_append (store: x11_display->monitors, item: monitor); |
723 | } |
724 | |
725 | gdk_monitor_get_geometry (GDK_MONITOR (monitor), geometry: &geometry); |
726 | name = g_strndup (str: output_info->name, n: output_info->nameLen); |
727 | |
728 | newgeo.x = crtc->x / x11_screen->surface_scale; |
729 | newgeo.y = crtc->y / x11_screen->surface_scale; |
730 | newgeo.width = crtc->width / x11_screen->surface_scale; |
731 | newgeo.height = crtc->height / x11_screen->surface_scale; |
732 | |
733 | gdk_monitor_set_geometry (GDK_MONITOR (monitor), geometry: &newgeo); |
734 | gdk_monitor_set_physical_size (GDK_MONITOR (monitor), |
735 | width_mm: output_info->mm_width, |
736 | height_mm: output_info->mm_height); |
737 | gdk_monitor_set_subpixel_layout (GDK_MONITOR (monitor), |
738 | subpixel: translate_subpixel_order (subpixel: output_info->subpixel_order)); |
739 | gdk_monitor_set_refresh_rate (GDK_MONITOR (monitor), refresh_rate); |
740 | gdk_monitor_set_scale_factor (GDK_MONITOR (monitor), scale: x11_screen->surface_scale); |
741 | gdk_monitor_set_model (GDK_MONITOR (monitor), model: name); |
742 | |
743 | g_free (mem: name); |
744 | |
745 | XRRFreeCrtcInfo (crtcInfo: crtc); |
746 | } |
747 | |
748 | XRRFreeOutputInfo (outputInfo: output_info); |
749 | } |
750 | |
751 | if (resources->noutput > 0) |
752 | first_output = resources->outputs[0]; |
753 | |
754 | XRRFreeScreenResources (resources); |
755 | |
756 | /* Which usable multihead data is not returned in non RandR 1.2+ X driver? */ |
757 | |
758 | for (i = g_list_model_get_n_items (list: G_LIST_MODEL (ptr: x11_display->monitors)) - 1; i >= 0; i--) |
759 | { |
760 | GdkX11Monitor *monitor = g_list_model_get_item (list: G_LIST_MODEL (ptr: x11_display->monitors), position: i); |
761 | |
762 | notify_surface_monitor_change (display: x11_display, GDK_MONITOR (monitor)); |
763 | if (monitor->remove) |
764 | { |
765 | g_list_store_remove (store: x11_display->monitors, position: i); |
766 | gdk_monitor_invalidate (GDK_MONITOR (monitor)); |
767 | } |
768 | g_object_unref (object: monitor); |
769 | } |
770 | |
771 | x11_display->primary_monitor = 0; |
772 | primary_output = XRRGetOutputPrimary (dpy: x11_screen->xdisplay, |
773 | window: x11_screen->xroot_window); |
774 | |
775 | for (i = 0; i < g_list_model_get_n_items (list: G_LIST_MODEL (ptr: x11_display->monitors)); i++) |
776 | { |
777 | GdkX11Monitor *monitor = g_list_model_get_item (list: G_LIST_MODEL (ptr: x11_display->monitors), position: i); |
778 | g_object_unref (object: monitor); |
779 | if (monitor->output == primary_output) |
780 | { |
781 | x11_display->primary_monitor = i; |
782 | break; |
783 | } |
784 | |
785 | /* No RandR1.3+ available or no primary set, fall back to prefer LVDS as primary if present */ |
786 | if (primary_output == None && |
787 | g_ascii_strncasecmp (s1: gdk_monitor_get_model (GDK_MONITOR (monitor)), s2: "LVDS" , n: 4) == 0) |
788 | { |
789 | x11_display->primary_monitor = i; |
790 | break; |
791 | } |
792 | |
793 | /* No primary specified and no LVDS found */ |
794 | if (monitor->output == first_output) |
795 | x11_display->primary_monitor = i; |
796 | } |
797 | |
798 | return g_list_model_get_n_items (list: G_LIST_MODEL (ptr: x11_display->monitors)) > 0; |
799 | #endif |
800 | |
801 | return FALSE; |
802 | } |
803 | |
804 | static void |
805 | init_no_multihead (GdkX11Screen *x11_screen) |
806 | { |
807 | GdkX11Display *x11_display = GDK_X11_DISPLAY (x11_screen->display); |
808 | GdkX11Monitor *monitor; |
809 | int width_mm, height_mm; |
810 | int width, height; |
811 | int i; |
812 | |
813 | for (i = 0; i < g_list_model_get_n_items (list: G_LIST_MODEL (ptr: x11_display->monitors)); i++) |
814 | { |
815 | monitor = g_list_model_get_item (list: G_LIST_MODEL (ptr: x11_display->monitors), position: i); |
816 | monitor->add = FALSE; |
817 | monitor->remove = TRUE; |
818 | g_object_unref (object: monitor); |
819 | } |
820 | |
821 | monitor = find_monitor_by_output (x11_display, output: 0); |
822 | if (monitor) |
823 | monitor->remove = FALSE; |
824 | else |
825 | { |
826 | monitor = g_object_new (object_type: gdk_x11_monitor_get_type (), |
827 | first_property_name: "display" , x11_display, |
828 | NULL); |
829 | monitor->output = 0; |
830 | monitor->add = TRUE; |
831 | g_list_store_append (store: x11_display->monitors, item: monitor); |
832 | } |
833 | |
834 | width_mm = WidthMMOfScreen (x11_screen->xscreen); |
835 | height_mm = HeightMMOfScreen (x11_screen->xscreen); |
836 | width = WidthOfScreen (x11_screen->xscreen); |
837 | height = HeightOfScreen (x11_screen->xscreen); |
838 | |
839 | gdk_monitor_set_geometry (GDK_MONITOR (monitor), geometry: &(GdkRectangle) { 0, 0, width, height }); |
840 | gdk_monitor_set_physical_size (GDK_MONITOR (monitor), width_mm, height_mm); |
841 | gdk_monitor_set_scale_factor (GDK_MONITOR (monitor), scale: x11_screen->surface_scale); |
842 | |
843 | x11_display->primary_monitor = 0; |
844 | |
845 | for (i = g_list_model_get_n_items (list: G_LIST_MODEL (ptr: x11_display->monitors)) - 1; i >= 0; i--) |
846 | { |
847 | monitor = g_list_model_get_item (list: G_LIST_MODEL (ptr: x11_display->monitors), position: i); |
848 | |
849 | notify_surface_monitor_change (display: x11_display, GDK_MONITOR (monitor)); |
850 | if (monitor->remove) |
851 | { |
852 | g_list_store_remove (store: x11_display->monitors, position: i); |
853 | gdk_monitor_invalidate (GDK_MONITOR (monitor)); |
854 | } |
855 | g_object_unref (object: monitor); |
856 | } |
857 | } |
858 | |
859 | static void |
860 | init_multihead (GdkX11Screen *screen) |
861 | { |
862 | if (!init_randr15 (x11_screen: screen) && |
863 | !init_randr13 (x11_screen: screen)) |
864 | init_no_multihead (x11_screen: screen); |
865 | } |
866 | |
867 | GdkX11Screen * |
868 | _gdk_x11_screen_new (GdkDisplay *display, |
869 | int screen_number) |
870 | { |
871 | GdkX11Screen *x11_screen; |
872 | GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); |
873 | const char *scale_str; |
874 | |
875 | x11_screen = g_object_new (GDK_TYPE_X11_SCREEN, NULL); |
876 | |
877 | x11_screen->display = display; |
878 | x11_screen->xdisplay = display_x11->xdisplay; |
879 | x11_screen->xscreen = ScreenOfDisplay (display_x11->xdisplay, screen_number); |
880 | x11_screen->screen_num = screen_number; |
881 | x11_screen->xroot_window = RootWindow (display_x11->xdisplay, screen_number); |
882 | x11_screen->wmspec_check_window = None; |
883 | /* we want this to be always non-null */ |
884 | x11_screen->window_manager_name = g_strdup (str: "unknown" ); |
885 | |
886 | scale_str = g_getenv (variable: "GDK_SCALE" ); |
887 | if (scale_str) |
888 | { |
889 | x11_screen->fixed_surface_scale = TRUE; |
890 | x11_screen->surface_scale = atol (nptr: scale_str); |
891 | if (x11_screen->surface_scale <= 0) |
892 | x11_screen->surface_scale = 1; |
893 | } |
894 | else |
895 | x11_screen->surface_scale = 1; |
896 | |
897 | init_randr_support (screen: x11_screen); |
898 | init_multihead (screen: x11_screen); |
899 | |
900 | return x11_screen; |
901 | } |
902 | |
903 | void |
904 | _gdk_x11_screen_set_surface_scale (GdkX11Screen *x11_screen, |
905 | int scale) |
906 | { |
907 | GdkX11Display *x11_display = GDK_X11_DISPLAY (x11_screen->display); |
908 | GList *toplevels, *l; |
909 | int i; |
910 | |
911 | if (x11_screen->surface_scale == scale) |
912 | return; |
913 | |
914 | x11_screen->surface_scale = scale; |
915 | |
916 | toplevels = gdk_x11_display_get_toplevel_windows (display: x11_screen->display); |
917 | |
918 | for (l = toplevels; l != NULL; l = l->next) |
919 | { |
920 | GdkSurface *surface = l->data; |
921 | |
922 | _gdk_x11_surface_set_surface_scale (window: surface, scale); |
923 | } |
924 | |
925 | for (i = 0; i < g_list_model_get_n_items (list: G_LIST_MODEL (ptr: x11_display->monitors)); i++) |
926 | { |
927 | GdkMonitor *monitor = g_list_model_get_item (list: G_LIST_MODEL (ptr: x11_display->monitors), position: i); |
928 | |
929 | gdk_monitor_set_scale_factor (monitor, scale); |
930 | |
931 | g_object_unref (object: monitor); |
932 | } |
933 | |
934 | /* We re-read the monitor sizes so we can apply the new scale */ |
935 | process_monitors_change (screen: x11_screen); |
936 | } |
937 | |
938 | static void |
939 | init_randr_support (GdkX11Screen *x11_screen) |
940 | { |
941 | /* NB: This is also needed for XSettings, so don't remove. */ |
942 | XSelectInput (GDK_SCREEN_XDISPLAY (x11_screen), |
943 | x11_screen->xroot_window, |
944 | StructureNotifyMask); |
945 | |
946 | #ifdef HAVE_RANDR |
947 | if (!GDK_X11_DISPLAY (GDK_SCREEN_DISPLAY (x11_screen))->have_randr12) |
948 | return; |
949 | |
950 | XRRSelectInput (GDK_SCREEN_XDISPLAY (x11_screen), |
951 | window: x11_screen->xroot_window, |
952 | RRScreenChangeNotifyMask |
953 | | RRCrtcChangeNotifyMask |
954 | | RROutputPropertyNotifyMask); |
955 | #endif |
956 | } |
957 | |
958 | static void |
959 | process_monitors_change (GdkX11Screen *screen) |
960 | { |
961 | init_multihead (screen); |
962 | } |
963 | |
964 | void |
965 | _gdk_x11_screen_size_changed (GdkX11Screen *screen, |
966 | const XEvent *event) |
967 | { |
968 | #ifdef HAVE_RANDR |
969 | GdkX11Display *display_x11; |
970 | |
971 | display_x11 = GDK_X11_DISPLAY (GDK_SCREEN_DISPLAY (screen)); |
972 | |
973 | if (display_x11->have_randr13 && event->type == ConfigureNotify) |
974 | return; |
975 | |
976 | XRRUpdateConfiguration (event: (XEvent *) event); |
977 | #else |
978 | if (event->type != ConfigureNotify) |
979 | return; |
980 | #endif |
981 | |
982 | process_monitors_change (screen); |
983 | } |
984 | |
985 | void |
986 | _gdk_x11_screen_get_edge_monitors (GdkX11Screen *x11_screen, |
987 | int *top, |
988 | int *bottom, |
989 | int *left, |
990 | int *right) |
991 | { |
992 | #ifdef HAVE_XFREE_XINERAMA |
993 | int top_most_pos = HeightOfScreen (x11_screen->xscreen); |
994 | int left_most_pos = WidthOfScreen (x11_screen->xscreen); |
995 | int bottom_most_pos = 0; |
996 | int right_most_pos = 0; |
997 | int i; |
998 | XineramaScreenInfo *x_monitors; |
999 | int x_n_monitors; |
1000 | #endif |
1001 | |
1002 | *top = *bottom = *left = *right = -1; |
1003 | |
1004 | #ifdef HAVE_XFREE_XINERAMA |
1005 | if (!XineramaIsActive (dpy: x11_screen->xdisplay)) |
1006 | return; |
1007 | |
1008 | x_monitors = XineramaQueryScreens (dpy: x11_screen->xdisplay, number: &x_n_monitors); |
1009 | if (x_n_monitors <= 0 || x_monitors == NULL) |
1010 | { |
1011 | if (x_monitors) |
1012 | XFree (x_monitors); |
1013 | |
1014 | return; |
1015 | } |
1016 | |
1017 | for (i = 0; i < x_n_monitors; i++) |
1018 | { |
1019 | if (left && left_most_pos > x_monitors[i].x_org) |
1020 | { |
1021 | left_most_pos = x_monitors[i].x_org; |
1022 | *left = i; |
1023 | } |
1024 | if (right && right_most_pos < x_monitors[i].x_org + x_monitors[i].width) |
1025 | { |
1026 | right_most_pos = x_monitors[i].x_org + x_monitors[i].width; |
1027 | *right = i; |
1028 | } |
1029 | if (top && top_most_pos > x_monitors[i].y_org) |
1030 | { |
1031 | top_most_pos = x_monitors[i].y_org; |
1032 | *top = i; |
1033 | } |
1034 | if (bottom && bottom_most_pos < x_monitors[i].y_org + x_monitors[i].height) |
1035 | { |
1036 | bottom_most_pos = x_monitors[i].y_org + x_monitors[i].height; |
1037 | *bottom = i; |
1038 | } |
1039 | } |
1040 | |
1041 | XFree (x_monitors); |
1042 | #endif |
1043 | } |
1044 | |
1045 | void |
1046 | _gdk_x11_screen_window_manager_changed (GdkX11Screen *screen) |
1047 | { |
1048 | g_signal_emit (instance: screen, signal_id: signals[WINDOW_MANAGER_CHANGED], detail: 0); |
1049 | } |
1050 | |
1051 | gboolean |
1052 | gdk_x11_screen_get_setting (GdkX11Screen *x11_screen, |
1053 | const char *name, |
1054 | GValue *value) |
1055 | { |
1056 | const GValue *setting; |
1057 | |
1058 | if (x11_screen->xsettings == NULL) |
1059 | goto out; |
1060 | setting = g_hash_table_lookup (hash_table: x11_screen->xsettings, key: name); |
1061 | if (setting == NULL) |
1062 | goto out; |
1063 | |
1064 | if (!g_value_transform (src_value: setting, dest_value: value)) |
1065 | { |
1066 | g_warning ("Cannot transform xsetting %s of type %s to type %s\n" , |
1067 | name, |
1068 | g_type_name (G_VALUE_TYPE (setting)), |
1069 | g_type_name (G_VALUE_TYPE (value))); |
1070 | goto out; |
1071 | } |
1072 | |
1073 | return TRUE; |
1074 | |
1075 | out: |
1076 | return _gdk_x11_screen_get_xft_setting (screen: x11_screen, name, value); |
1077 | } |
1078 | |
1079 | static void |
1080 | cleanup_atoms(gpointer data) |
1081 | { |
1082 | NetWmSupportedAtoms *supported_atoms = data; |
1083 | if (supported_atoms->atoms) |
1084 | XFree (supported_atoms->atoms); |
1085 | g_free (mem: supported_atoms); |
1086 | } |
1087 | |
1088 | static Window |
1089 | get_net_supporting_wm_check (GdkX11Screen *screen, |
1090 | Window window) |
1091 | { |
1092 | GdkDisplay *display; |
1093 | Atom type; |
1094 | int format; |
1095 | gulong n_items; |
1096 | gulong bytes_after; |
1097 | guchar *data; |
1098 | Window value; |
1099 | |
1100 | display = screen->display; |
1101 | type = None; |
1102 | data = NULL; |
1103 | value = None; |
1104 | |
1105 | gdk_x11_display_error_trap_push (display); |
1106 | XGetWindowProperty (screen->xdisplay, window, |
1107 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_SUPPORTING_WM_CHECK" ), |
1108 | 0, G_MAXLONG, False, XA_WINDOW, &type, &format, |
1109 | &n_items, &bytes_after, &data); |
1110 | gdk_x11_display_error_trap_pop_ignored (display); |
1111 | |
1112 | if (type == XA_WINDOW) |
1113 | value = *(Window *)data; |
1114 | |
1115 | if (data) |
1116 | XFree (data); |
1117 | |
1118 | return value; |
1119 | } |
1120 | |
1121 | static void |
1122 | fetch_net_wm_check_window (GdkX11Screen *x11_screen) |
1123 | { |
1124 | GdkDisplay *display; |
1125 | Window window; |
1126 | guint64 now; |
1127 | int error; |
1128 | |
1129 | display = x11_screen->display; |
1130 | |
1131 | g_return_if_fail (GDK_X11_DISPLAY (display)->trusted_client); |
1132 | |
1133 | if (x11_screen->wmspec_check_window != None) |
1134 | return; /* already have it */ |
1135 | |
1136 | now = g_get_monotonic_time (); |
1137 | |
1138 | if ((now - x11_screen->last_wmspec_check_time) / 1e6 < 15) |
1139 | return; /* we've checked recently */ |
1140 | |
1141 | window = get_net_supporting_wm_check (screen: x11_screen, window: x11_screen->xroot_window); |
1142 | if (window == None) |
1143 | return; |
1144 | |
1145 | if (window != get_net_supporting_wm_check (screen: x11_screen, window)) |
1146 | return; |
1147 | |
1148 | gdk_x11_display_error_trap_push (display); |
1149 | |
1150 | /* Find out if this WM goes away, so we can reset everything. */ |
1151 | XSelectInput (x11_screen->xdisplay, window, StructureNotifyMask); |
1152 | |
1153 | error = gdk_x11_display_error_trap_pop (display); |
1154 | if (!error) |
1155 | { |
1156 | /* We check the window property again because after XGetWindowProperty() |
1157 | * and before XSelectInput() the window may have been recycled in such a |
1158 | * way that XSelectInput() doesn't fail but the window is no longer what |
1159 | * we want. |
1160 | */ |
1161 | if (window != get_net_supporting_wm_check (screen: x11_screen, window)) |
1162 | return; |
1163 | |
1164 | x11_screen->wmspec_check_window = window; |
1165 | x11_screen->last_wmspec_check_time = now; |
1166 | x11_screen->need_refetch_net_supported = TRUE; |
1167 | x11_screen->need_refetch_wm_name = TRUE; |
1168 | |
1169 | /* Careful, reentrancy */ |
1170 | _gdk_x11_screen_window_manager_changed (screen: x11_screen); |
1171 | } |
1172 | } |
1173 | |
1174 | /** |
1175 | * gdk_x11_screen_supports_net_wm_hint: |
1176 | * @screen: the relevant `GdkX11Screen`. |
1177 | * @property_name: name of the WM property |
1178 | * |
1179 | * This function is specific to the X11 backend of GDK, and indicates |
1180 | * whether the window manager supports a certain hint from the |
1181 | * [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) specification. |
1182 | * |
1183 | * When using this function, keep in mind that the window manager |
1184 | * can change over time; so you shouldn’t use this function in |
1185 | * a way that impacts persistent application state. A common bug |
1186 | * is that your application can start up before the window manager |
1187 | * does when the user logs in, and before the window manager starts |
1188 | * gdk_x11_screen_supports_net_wm_hint() will return %FALSE for every property. |
1189 | * You can monitor the window_manager_changed signal on `GdkX11Screen` to detect |
1190 | * a window manager change. |
1191 | * |
1192 | * Returns: %TRUE if the window manager supports @property |
1193 | **/ |
1194 | gboolean |
1195 | gdk_x11_screen_supports_net_wm_hint (GdkX11Screen *x11_screen, |
1196 | const char *property_name) |
1197 | { |
1198 | gulong i; |
1199 | NetWmSupportedAtoms *supported_atoms; |
1200 | GdkDisplay *display; |
1201 | Atom atom; |
1202 | |
1203 | display = x11_screen->display; |
1204 | |
1205 | if (!G_LIKELY (GDK_X11_DISPLAY (display)->trusted_client)) |
1206 | return FALSE; |
1207 | |
1208 | supported_atoms = g_object_get_data (G_OBJECT (x11_screen), key: "gdk-net-wm-supported-atoms" ); |
1209 | if (!supported_atoms) |
1210 | { |
1211 | supported_atoms = g_new0 (NetWmSupportedAtoms, 1); |
1212 | g_object_set_data_full (G_OBJECT (x11_screen), key: "gdk-net-wm-supported-atoms" , data: supported_atoms, destroy: cleanup_atoms); |
1213 | } |
1214 | |
1215 | fetch_net_wm_check_window (x11_screen); |
1216 | |
1217 | if (x11_screen->wmspec_check_window == None) |
1218 | return FALSE; |
1219 | |
1220 | if (x11_screen->need_refetch_net_supported) |
1221 | { |
1222 | /* WM has changed since we last got the supported list, |
1223 | * refetch it. |
1224 | */ |
1225 | Atom type; |
1226 | int format; |
1227 | gulong bytes_after; |
1228 | |
1229 | x11_screen->need_refetch_net_supported = FALSE; |
1230 | |
1231 | if (supported_atoms->atoms) |
1232 | XFree (supported_atoms->atoms); |
1233 | |
1234 | supported_atoms->atoms = NULL; |
1235 | supported_atoms->n_atoms = 0; |
1236 | |
1237 | XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), x11_screen->xroot_window, |
1238 | gdk_x11_get_xatom_by_name_for_display (display, atom_name: "_NET_SUPPORTED" ), |
1239 | 0, G_MAXLONG, False, XA_ATOM, &type, &format, |
1240 | &supported_atoms->n_atoms, &bytes_after, |
1241 | (guchar **)&supported_atoms->atoms); |
1242 | |
1243 | if (type != XA_ATOM) |
1244 | return FALSE; |
1245 | } |
1246 | |
1247 | if (supported_atoms->atoms == NULL) |
1248 | return FALSE; |
1249 | |
1250 | atom = gdk_x11_get_xatom_by_name_for_display (display, atom_name: property_name); |
1251 | |
1252 | for (i = 0; i < supported_atoms->n_atoms; i++) |
1253 | { |
1254 | if (supported_atoms->atoms[i] == atom) |
1255 | return TRUE; |
1256 | } |
1257 | |
1258 | return FALSE; |
1259 | } |
1260 | |
1261 | /** |
1262 | * gdk_x11_screen_get_window_manager_name: |
1263 | * @screen: a `GdkX11Screen` |
1264 | * |
1265 | * Returns the name of the window manager for @screen. |
1266 | * |
1267 | * Returns: the name of the window manager screen @screen, or |
1268 | * "unknown" if the window manager is unknown. The string is owned by GDK |
1269 | * and should not be freed. |
1270 | **/ |
1271 | const char* |
1272 | gdk_x11_screen_get_window_manager_name (GdkX11Screen *x11_screen) |
1273 | { |
1274 | GdkDisplay *display; |
1275 | |
1276 | display = x11_screen->display; |
1277 | |
1278 | if (!G_LIKELY (GDK_X11_DISPLAY (display)->trusted_client)) |
1279 | return x11_screen->window_manager_name; |
1280 | |
1281 | fetch_net_wm_check_window (x11_screen); |
1282 | |
1283 | if (x11_screen->need_refetch_wm_name) |
1284 | { |
1285 | /* Get the name of the window manager */ |
1286 | x11_screen->need_refetch_wm_name = FALSE; |
1287 | |
1288 | g_free (mem: x11_screen->window_manager_name); |
1289 | x11_screen->window_manager_name = g_strdup (str: "unknown" ); |
1290 | |
1291 | if (x11_screen->wmspec_check_window != None) |
1292 | { |
1293 | Atom type; |
1294 | int format; |
1295 | gulong n_items; |
1296 | gulong bytes_after; |
1297 | char *name; |
1298 | |
1299 | name = NULL; |
1300 | |
1301 | gdk_x11_display_error_trap_push (display); |
1302 | |
1303 | XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), |
1304 | x11_screen->wmspec_check_window, |
1305 | gdk_x11_get_xatom_by_name_for_display (display, |
1306 | atom_name: "_NET_WM_NAME" ), |
1307 | 0, G_MAXLONG, False, |
1308 | gdk_x11_get_xatom_by_name_for_display (display, |
1309 | atom_name: "UTF8_STRING" ), |
1310 | &type, &format, |
1311 | &n_items, &bytes_after, |
1312 | (guchar **)&name); |
1313 | |
1314 | gdk_x11_display_error_trap_pop_ignored (display); |
1315 | |
1316 | if (name != NULL) |
1317 | { |
1318 | g_free (mem: x11_screen->window_manager_name); |
1319 | x11_screen->window_manager_name = g_strdup (str: name); |
1320 | XFree (name); |
1321 | } |
1322 | } |
1323 | } |
1324 | |
1325 | return x11_screen->window_manager_name; |
1326 | } |
1327 | |
1328 | static void |
1329 | gdk_x11_screen_class_init (GdkX11ScreenClass *klass) |
1330 | { |
1331 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
1332 | |
1333 | object_class->dispose = gdk_x11_screen_dispose; |
1334 | object_class->finalize = gdk_x11_screen_finalize; |
1335 | |
1336 | signals[WINDOW_MANAGER_CHANGED] = |
1337 | g_signal_new (signal_name: g_intern_static_string (string: "window-manager-changed" ), |
1338 | G_OBJECT_CLASS_TYPE (object_class), |
1339 | signal_flags: G_SIGNAL_RUN_LAST, |
1340 | G_STRUCT_OFFSET (GdkX11ScreenClass, window_manager_changed), |
1341 | NULL, NULL, |
1342 | NULL, |
1343 | G_TYPE_NONE, |
1344 | n_params: 0); |
1345 | } |
1346 | |
1347 | static guint32 |
1348 | get_netwm_cardinal_property (GdkX11Screen *x11_screen, |
1349 | const char *name) |
1350 | { |
1351 | guint32 prop = 0; |
1352 | Atom type; |
1353 | int format; |
1354 | gulong nitems; |
1355 | gulong bytes_after; |
1356 | guchar *data; |
1357 | |
1358 | if (!gdk_x11_screen_supports_net_wm_hint (x11_screen, property_name: name)) |
1359 | return 0; |
1360 | |
1361 | XGetWindowProperty (x11_screen->xdisplay, |
1362 | x11_screen->xroot_window, |
1363 | gdk_x11_get_xatom_by_name_for_display (GDK_SCREEN_DISPLAY (x11_screen), atom_name: name), |
1364 | 0, G_MAXLONG, |
1365 | False, XA_CARDINAL, &type, &format, &nitems, |
1366 | &bytes_after, &data); |
1367 | if (type == XA_CARDINAL) |
1368 | { |
1369 | prop = *(gulong *)data; |
1370 | XFree (data); |
1371 | } |
1372 | |
1373 | return prop; |
1374 | } |
1375 | |
1376 | /** |
1377 | * gdk_x11_screen_get_number_of_desktops: |
1378 | * @screen: a `GdkX11Screen` |
1379 | * |
1380 | * Returns the number of workspaces for @screen when running under a |
1381 | * window manager that supports multiple workspaces, as described |
1382 | * in the |
1383 | * [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) specification. |
1384 | * |
1385 | * Returns: the number of workspaces, or 0 if workspaces are not supported |
1386 | */ |
1387 | guint32 |
1388 | gdk_x11_screen_get_number_of_desktops (GdkX11Screen *screen) |
1389 | { |
1390 | return get_netwm_cardinal_property (x11_screen: screen, name: "_NET_NUMBER_OF_DESKTOPS" ); |
1391 | } |
1392 | |
1393 | /** |
1394 | * gdk_x11_screen_get_current_desktop: |
1395 | * @screen: a `GdkX11Screen` |
1396 | * |
1397 | * Returns the current workspace for @screen when running under a |
1398 | * window manager that supports multiple workspaces, as described |
1399 | * in the |
1400 | * [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) specification. |
1401 | * |
1402 | * Returns: the current workspace, or 0 if workspaces are not supported |
1403 | */ |
1404 | guint32 |
1405 | gdk_x11_screen_get_current_desktop (GdkX11Screen *screen) |
1406 | { |
1407 | return get_netwm_cardinal_property (x11_screen: screen, name: "_NET_CURRENT_DESKTOP" ); |
1408 | } |
1409 | |