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	       (GdkScreen	  *screen);
52 static void         process_monitors_change    (GdkScreen         *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, GDK_TYPE_SCREEN)
63 
64 typedef struct _NetWmSupportedAtoms NetWmSupportedAtoms;
65 
66 struct _NetWmSupportedAtoms
67 {
68   Atom *atoms;
69   gulong n_atoms;
70 };
71 
72 static void
gdk_x11_screen_init(GdkX11Screen * screen)73 gdk_x11_screen_init (GdkX11Screen *screen)
74 {
75 }
76 
77 static GdkDisplay *
gdk_x11_screen_get_display(GdkScreen * screen)78 gdk_x11_screen_get_display (GdkScreen *screen)
79 {
80   return GDK_X11_SCREEN (screen)->display;
81 }
82 
83 gint
gdk_x11_screen_get_width(GdkScreen * screen)84 gdk_x11_screen_get_width (GdkScreen *screen)
85 {
86   return GDK_X11_SCREEN (screen)->width;
87 }
88 
89 gint
gdk_x11_screen_get_height(GdkScreen * screen)90 gdk_x11_screen_get_height (GdkScreen *screen)
91 {
92   return GDK_X11_SCREEN (screen)->height;
93 }
94 
95 static gint
gdk_x11_screen_get_width_mm(GdkScreen * screen)96 gdk_x11_screen_get_width_mm (GdkScreen *screen)
97 {
98   return WidthMMOfScreen (GDK_X11_SCREEN (screen)->xscreen);
99 }
100 
101 static gint
gdk_x11_screen_get_height_mm(GdkScreen * screen)102 gdk_x11_screen_get_height_mm (GdkScreen *screen)
103 {
104   return HeightMMOfScreen (GDK_X11_SCREEN (screen)->xscreen);
105 }
106 
107 gint
gdk_x11_screen_get_number(GdkScreen * screen)108 gdk_x11_screen_get_number (GdkScreen *screen)
109 {
110   return GDK_X11_SCREEN (screen)->screen_num;
111 }
112 
113 static GdkWindow *
gdk_x11_screen_get_root_window(GdkScreen * screen)114 gdk_x11_screen_get_root_window (GdkScreen *screen)
115 {
116   return GDK_X11_SCREEN (screen)->root_window;
117 }
118 
119 static void
gdk_x11_screen_dispose(GObject * object)120 gdk_x11_screen_dispose (GObject *object)
121 {
122   GdkX11Screen *x11_screen = GDK_X11_SCREEN (object);
123   int i;
124 
125   for (i = 0; i < 32; ++i)
126     {
127       if (x11_screen->subwindow_gcs[i])
128         {
129           XFreeGC (x11_screen->xdisplay, x11_screen->subwindow_gcs[i]);
130           x11_screen->subwindow_gcs[i] = 0;
131         }
132     }
133 
134   _gdk_x11_xsettings_finish (x11_screen);
135 
136   if (x11_screen->root_window)
137     _gdk_window_destroy (x11_screen->root_window, TRUE);
138 
139   for (i = 0; i < x11_screen->nvisuals; i++)
140     g_object_run_dispose (G_OBJECT (x11_screen->visuals[i]));
141 
142   G_OBJECT_CLASS (gdk_x11_screen_parent_class)->dispose (object);
143 
144   x11_screen->xdisplay = NULL;
145   x11_screen->xscreen = NULL;
146   x11_screen->screen_num = -1;
147   x11_screen->xroot_window = None;
148   x11_screen->wmspec_check_window = None;
149 }
150 
151 static void
gdk_x11_screen_finalize(GObject * object)152 gdk_x11_screen_finalize (GObject *object)
153 {
154   GdkX11Screen *x11_screen = GDK_X11_SCREEN (object);
155   gint          i;
156 
157   if (x11_screen->root_window)
158     g_object_unref (x11_screen->root_window);
159 
160   /* Visual Part */
161   for (i = 0; i < x11_screen->nvisuals; i++)
162     g_object_unref (x11_screen->visuals[i]);
163   g_free (x11_screen->visuals);
164   g_hash_table_destroy (x11_screen->visual_hash);
165 
166   g_free (x11_screen->window_manager_name);
167 
168   G_OBJECT_CLASS (gdk_x11_screen_parent_class)->finalize (object);
169 }
170 
171 /**
172  * gdk_x11_screen_get_monitor_output:
173  * @screen: (type GdkX11Screen): a #GdkScreen
174  * @monitor_num: number of the monitor, between 0 and gdk_screen_get_n_monitors (screen)
175  *
176  * Gets the XID of the specified output/monitor.
177  * If the X server does not support version 1.2 of the RANDR
178  * extension, 0 is returned.
179  *
180  * Returns: the XID of the monitor
181  *
182  * Since: 2.14
183  */
184 XID
gdk_x11_screen_get_monitor_output(GdkScreen * screen,gint monitor_num)185 gdk_x11_screen_get_monitor_output (GdkScreen *screen,
186                                    gint       monitor_num)
187 {
188   GdkX11Screen *x11_screen = GDK_X11_SCREEN (screen);
189   GdkX11Display *x11_display = GDK_X11_DISPLAY (x11_screen->display);
190   GdkX11Monitor *monitor;
191 
192   g_return_val_if_fail (GDK_IS_SCREEN (screen), None);
193   g_return_val_if_fail (monitor_num >= 0, None);
194   g_return_val_if_fail (monitor_num < x11_display->monitors->len, None);
195 
196   monitor = x11_display->monitors->pdata[monitor_num];
197   return monitor->output;
198 }
199 
200 static int
get_current_desktop(GdkScreen * screen)201 get_current_desktop (GdkScreen *screen)
202 {
203   Display *display;
204   Window win;
205   Atom current_desktop, type;
206   int format;
207   unsigned long n_items, bytes_after;
208   unsigned char *data_return = NULL;
209   int workspace = 0;
210 
211   if (!gdk_x11_screen_supports_net_wm_hint (screen,
212                                             gdk_atom_intern_static_string ("_NET_CURRENT_DESKTOP")))
213     return workspace;
214 
215   display = GDK_DISPLAY_XDISPLAY (gdk_screen_get_display (screen));
216   win = XRootWindow (display, GDK_SCREEN_XNUMBER (screen));
217 
218   current_desktop = XInternAtom (display, "_NET_CURRENT_DESKTOP", True);
219 
220   XGetWindowProperty (display,
221                       win,
222                       current_desktop,
223                       0, G_MAXLONG,
224                       False, XA_CARDINAL,
225                       &type, &format, &n_items, &bytes_after,
226                       &data_return);
227 
228   if (type == XA_CARDINAL && format == 32 && n_items > 0)
229     workspace = ((long *) data_return)[0];
230 
231   if (data_return)
232     XFree (data_return);
233 
234   return workspace;
235 }
236 
237 gboolean
_gdk_x11_screen_get_monitor_work_area(GdkScreen * screen,GdkMonitor * monitor,GdkRectangle * area)238 _gdk_x11_screen_get_monitor_work_area (GdkScreen    *screen,
239                                        GdkMonitor   *monitor,
240                                        GdkRectangle *area)
241 {
242   GdkX11Screen *x11_screen;
243   GdkAtom net_workareas;
244   GdkDisplay *display;
245   Display *xdisplay;
246   int current_desktop;
247   char *workareas_dn_name;
248   Atom workareas_dn;
249   int screen_number;
250   Window xroot;
251   int result;
252   Atom type;
253   int format;
254   gulong num;
255   gulong leftovers;
256   guchar *ret_workarea;
257   long *workareas;
258   GdkRectangle geometry;
259   int i;
260 
261   x11_screen = GDK_X11_SCREEN (screen);
262 
263   net_workareas = gdk_atom_intern_static_string ("_GTK_WORKAREAS");
264   if (!gdk_x11_screen_supports_net_wm_hint (screen, net_workareas))
265     return FALSE;
266 
267   display = gdk_screen_get_display (screen);
268   xdisplay = gdk_x11_display_get_xdisplay (display);
269 
270   current_desktop = get_current_desktop (screen);
271   workareas_dn_name = g_strdup_printf ("_GTK_WORKAREAS_D%d", current_desktop);
272   workareas_dn = XInternAtom (xdisplay, workareas_dn_name, True);
273   g_free (workareas_dn_name);
274 
275   if (workareas_dn == None)
276     return FALSE;
277 
278   screen_number = gdk_x11_screen_get_screen_number (screen);
279   xroot = XRootWindow (xdisplay, screen_number);
280 
281   gdk_x11_display_error_trap_push (display);
282 
283   ret_workarea = NULL;
284   result = XGetWindowProperty (xdisplay,
285                                xroot,
286                                workareas_dn,
287                                0,
288                                G_MAXLONG,
289                                False,
290                                AnyPropertyType,
291                                &type,
292                                &format,
293                                &num,
294                                &leftovers,
295                                &ret_workarea);
296 
297   gdk_x11_display_error_trap_pop_ignored (display);
298 
299   if (result != Success ||
300       type == None ||
301       format == 0 ||
302       leftovers ||
303       num % 4 != 0)
304     {
305       XFree (ret_workarea);
306 
307       return FALSE;
308     }
309 
310   workareas = (long *) ret_workarea;
311 
312   gdk_monitor_get_geometry (monitor, &geometry);
313   *area = geometry;
314 
315   for (i = 0; i < num / 4; i++)
316     {
317       GdkRectangle work_area;
318 
319       work_area = (GdkRectangle) {
320         .x = workareas[0] / x11_screen->window_scale,
321         .y = workareas[1] / x11_screen->window_scale,
322         .width = workareas[2] / x11_screen->window_scale,
323         .height = workareas[3] / x11_screen->window_scale,
324       };
325 
326       if (gdk_rectangle_intersect (area, &work_area, &work_area))
327         *area = work_area;
328 
329       workareas += 4;
330     }
331 
332   XFree (ret_workarea);
333 
334   return TRUE;
335 }
336 
337 void
gdk_x11_screen_get_work_area(GdkScreen * screen,GdkRectangle * area)338 gdk_x11_screen_get_work_area (GdkScreen    *screen,
339                               GdkRectangle *area)
340 {
341   GdkX11Screen   *x11_screen = GDK_X11_SCREEN (screen);
342   Atom            workarea;
343   Atom            type;
344   Window          win;
345   int             format;
346   gulong          num;
347   gulong          leftovers;
348   gulong          max_len = 4 * 32;
349   guchar         *ret_workarea = NULL;
350   long           *workareas;
351   int             result;
352   int             disp_screen;
353   int             desktop;
354   Display        *display;
355 
356   display = GDK_DISPLAY_XDISPLAY (gdk_screen_get_display (screen));
357   disp_screen = GDK_SCREEN_XNUMBER (screen);
358   workarea = XInternAtom (display, "_NET_WORKAREA", True);
359 
360   /* Defaults in case of error */
361   area->x = 0;
362   area->y = 0;
363   area->width = gdk_x11_screen_get_width (screen);
364   area->height = gdk_x11_screen_get_height (screen);
365 
366   if (!gdk_x11_screen_supports_net_wm_hint (screen,
367                                             gdk_atom_intern_static_string ("_NET_WORKAREA")))
368     return;
369 
370   if (workarea == None)
371     return;
372 
373   win = XRootWindow (display, disp_screen);
374   result = XGetWindowProperty (display,
375                                win,
376                                workarea,
377                                0,
378                                max_len,
379                                False,
380                                AnyPropertyType,
381                                &type,
382                                &format,
383                                &num,
384                                &leftovers,
385                                &ret_workarea);
386   if (result != Success ||
387       type == None ||
388       format == 0 ||
389       leftovers ||
390       num % 4 != 0)
391     goto out;
392 
393   desktop = get_current_desktop (screen);
394   if (desktop + 1 > num / 4) /* fvwm gets this wrong */
395     goto out;
396 
397   workareas = (long *) ret_workarea;
398   area->x = workareas[desktop * 4];
399   area->y = workareas[desktop * 4 + 1];
400   area->width = workareas[desktop * 4 + 2];
401   area->height = workareas[desktop * 4 + 3];
402 
403   area->x /= x11_screen->window_scale;
404   area->y /= x11_screen->window_scale;
405   area->width /= x11_screen->window_scale;
406   area->height /= x11_screen->window_scale;
407 
408 out:
409   if (ret_workarea)
410     XFree (ret_workarea);
411 }
412 
413 static GdkVisual *
gdk_x11_screen_get_rgba_visual(GdkScreen * screen)414 gdk_x11_screen_get_rgba_visual (GdkScreen *screen)
415 {
416   GdkX11Screen *x11_screen = GDK_X11_SCREEN (screen);
417 
418   return x11_screen->rgba_visual;
419 }
420 
421 /**
422  * gdk_x11_screen_get_xscreen:
423  * @screen: (type GdkX11Screen): a #GdkScreen
424  *
425  * Returns the screen of a #GdkScreen.
426  *
427  * Returns: (transfer none): an Xlib Screen*
428  *
429  * Since: 2.2
430  */
431 Screen *
gdk_x11_screen_get_xscreen(GdkScreen * screen)432 gdk_x11_screen_get_xscreen (GdkScreen *screen)
433 {
434   return GDK_X11_SCREEN (screen)->xscreen;
435 }
436 
437 /**
438  * gdk_x11_screen_get_screen_number:
439  * @screen: (type GdkX11Screen): a #GdkScreen
440  *
441  * Returns the index of a #GdkScreen.
442  *
443  * Returns: the position of @screen among the screens
444  *     of its display
445  *
446  * Since: 2.2
447  */
448 int
gdk_x11_screen_get_screen_number(GdkScreen * screen)449 gdk_x11_screen_get_screen_number (GdkScreen *screen)
450 {
451   return GDK_X11_SCREEN (screen)->screen_num;
452 }
453 
454 static Atom
get_cm_atom(GdkX11Screen * x11_screen)455 get_cm_atom (GdkX11Screen *x11_screen)
456 {
457   return _gdk_x11_get_xatom_for_display_printf (x11_screen->display, "_NET_WM_CM_S%d", x11_screen->screen_num);
458 }
459 
460 static gboolean
check_is_composited(GdkDisplay * display,GdkX11Screen * x11_screen)461 check_is_composited (GdkDisplay *display,
462 		     GdkX11Screen *x11_screen)
463 {
464   Window xwindow;
465 
466   xwindow = XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display), get_cm_atom (x11_screen));
467 
468   return xwindow != None;
469 }
470 
471 static GdkX11Monitor *
find_monitor_by_output(GdkX11Display * x11_display,XID output)472 find_monitor_by_output (GdkX11Display *x11_display, XID output)
473 {
474   int i;
475 
476   for (i = 0; i < x11_display->monitors->len; i++)
477     {
478       GdkX11Monitor *monitor = x11_display->monitors->pdata[i];
479       if (monitor->output == output)
480         return monitor;
481     }
482 
483   return NULL;
484 }
485 
486 static GdkSubpixelLayout
translate_subpixel_order(int subpixel)487 translate_subpixel_order (int subpixel)
488 {
489   switch (subpixel)
490     {
491     case 1: return GDK_SUBPIXEL_LAYOUT_HORIZONTAL_RGB;
492     case 2: return GDK_SUBPIXEL_LAYOUT_HORIZONTAL_BGR;
493     case 3: return GDK_SUBPIXEL_LAYOUT_VERTICAL_RGB;
494     case 4: return GDK_SUBPIXEL_LAYOUT_VERTICAL_BGR;
495     case 5: return GDK_SUBPIXEL_LAYOUT_NONE;
496     default: return GDK_SUBPIXEL_LAYOUT_UNKNOWN;
497     }
498 }
499 
500 static gboolean
init_randr15(GdkScreen * screen,gboolean * changed)501 init_randr15 (GdkScreen *screen, gboolean *changed)
502 {
503 #ifdef HAVE_RANDR15
504   GdkDisplay *display = gdk_screen_get_display (screen);
505   GdkX11Display *x11_display = GDK_X11_DISPLAY (display);
506   GdkX11Screen *x11_screen = GDK_X11_SCREEN (screen);
507   XRRScreenResources *resources;
508   RROutput primary_output = None;
509   RROutput first_output = None;
510   int i;
511   gboolean randr12_compat = FALSE;
512   XRRMonitorInfo *rr_monitors;
513   int num_rr_monitors;
514   int old_primary;
515 
516   if (!x11_display->have_randr15)
517     return FALSE;
518 
519   resources = XRRGetScreenResourcesCurrent (x11_screen->xdisplay,
520                                             x11_screen->xroot_window);
521   if (!resources)
522     return FALSE;
523 
524   rr_monitors = XRRGetMonitors (x11_screen->xdisplay,
525                                 x11_screen->xroot_window,
526                                 True,
527                                 &num_rr_monitors);
528   if (!rr_monitors)
529     return FALSE;
530 
531   for (i = 0; i < x11_display->monitors->len; i++)
532     {
533       GdkX11Monitor *monitor = x11_display->monitors->pdata[i];
534       monitor->add = FALSE;
535       monitor->remove = TRUE;
536     }
537 
538   for (i = 0; i < num_rr_monitors; i++)
539     {
540       RROutput output = rr_monitors[i].outputs[0];
541       XRROutputInfo *output_info;
542       GdkX11Monitor *monitor;
543       GdkRectangle geometry;
544       GdkRectangle newgeo;
545       char *name;
546       char *manufacturer = NULL;
547       int refresh_rate = 0;
548 
549       gdk_x11_display_error_trap_push (display);
550       output_info = XRRGetOutputInfo (x11_screen->xdisplay, resources, output);
551       if (gdk_x11_display_error_trap_pop (display))
552         continue;
553 
554       if (output_info == NULL)
555         continue;
556 
557       /* Non RandR1.2+ X driver have output name "default" */
558       randr12_compat |= !g_strcmp0 (output_info->name, "default");
559 
560       if (output_info->connection == RR_Disconnected)
561         {
562           XRRFreeOutputInfo (output_info);
563           continue;
564         }
565 
566       if (first_output == None)
567         first_output = output;
568 
569       if (output_info->crtc)
570         {
571           XRRCrtcInfo *crtc;
572           int j;
573 
574           gdk_x11_display_error_trap_push (display);
575           crtc = XRRGetCrtcInfo (x11_screen->xdisplay, resources,
576                                  output_info->crtc);
577           if (gdk_x11_display_error_trap_pop (display))
578             {
579               XRRFreeOutputInfo (output_info);
580               continue;
581             }
582 
583           for (j = 0; j < resources->nmode; j++)
584             {
585               XRRModeInfo *xmode = &resources->modes[j];
586               if (xmode->id == crtc->mode)
587                 {
588                   if (xmode->hTotal != 0 && xmode->vTotal != 0)
589                     refresh_rate = (1000 * xmode->dotClock) / (xmode->hTotal * xmode->vTotal);
590                   break;
591                 }
592             }
593 
594           XRRFreeCrtcInfo (crtc);
595         }
596 
597       monitor = find_monitor_by_output (x11_display, output);
598       if (monitor)
599         monitor->remove = FALSE;
600       else
601         {
602           monitor = g_object_new (GDK_TYPE_X11_MONITOR,
603                                   "display", display,
604                                   NULL);
605           monitor->output = output;
606           monitor->add = TRUE;
607           g_ptr_array_add (x11_display->monitors, monitor);
608         }
609 
610       /* Fetch minimal manufacturer information (PNP ID) from EDID */
611       {
612         #define EDID_LENGTH 128
613         Atom actual_type, edid_atom;
614         char tmp[3];
615         int actual_format;
616         unsigned char *prop;
617         unsigned long nbytes, bytes_left;
618         Display *disp = GDK_DISPLAY_XDISPLAY (x11_display);
619 
620         edid_atom = XInternAtom (disp, RR_PROPERTY_RANDR_EDID, FALSE);
621 
622         XRRGetOutputProperty (disp, output,
623                               edid_atom,
624                               0,
625                               EDID_LENGTH,
626                               FALSE,
627                               FALSE,
628                               AnyPropertyType,
629                               &actual_type,
630                               &actual_format,
631                               &nbytes,
632                               &bytes_left,
633                               &prop);
634 
635         // Check partial EDID header (whole header: 00 ff ff ff ff ff ff 00)
636         if (nbytes >= EDID_LENGTH && prop[0] == 0x00 && prop[1] == 0xff)
637           {
638             /* decode the Vendor ID from three 5 bit words packed into 2 bytes
639              * /--08--\/--09--\
640              * 7654321076543210
641              * |\---/\---/\---/
642              * R  C1   C2   C3 */
643             tmp[0] = 'A' + ((prop[8] & 0x7c) / 4) - 1;
644             tmp[1] = 'A' + ((prop[8] & 0x3) * 8) + ((prop[9] & 0xe0) / 32) - 1;
645             tmp[2] = 'A' + (prop[9] & 0x1f) - 1;
646 
647             manufacturer = g_strndup (tmp, sizeof (tmp));
648           }
649 
650         XFree(prop);
651         #undef EDID_LENGTH
652       }
653 
654       gdk_monitor_get_geometry (GDK_MONITOR (monitor), &geometry);
655       name = g_strndup (output_info->name, output_info->nameLen);
656 
657       newgeo.x = rr_monitors[i].x / x11_screen->window_scale;
658       newgeo.y = rr_monitors[i].y / x11_screen->window_scale;
659       newgeo.width = rr_monitors[i].width / x11_screen->window_scale;
660       newgeo.height = rr_monitors[i].height / x11_screen->window_scale;
661       if (newgeo.x != geometry.x ||
662           newgeo.y != geometry.y ||
663           newgeo.width != geometry.width ||
664           newgeo.height != geometry.height ||
665           rr_monitors[i].mwidth != gdk_monitor_get_width_mm (GDK_MONITOR (monitor)) ||
666           rr_monitors[i].mheight != gdk_monitor_get_height_mm (GDK_MONITOR (monitor)) ||
667           g_strcmp0 (name, gdk_monitor_get_model (GDK_MONITOR (monitor))))
668         *changed = TRUE;
669 
670       gdk_monitor_set_position (GDK_MONITOR (monitor), newgeo.x, newgeo.y);
671       gdk_monitor_set_size (GDK_MONITOR (monitor), newgeo.width, newgeo.height);
672       g_object_notify (G_OBJECT (monitor), "workarea");
673       gdk_monitor_set_physical_size (GDK_MONITOR (monitor),
674                                      rr_monitors[i].mwidth,
675                                      rr_monitors[i].mheight);
676       gdk_monitor_set_subpixel_layout (GDK_MONITOR (monitor),
677                                        translate_subpixel_order (output_info->subpixel_order));
678       gdk_monitor_set_refresh_rate (GDK_MONITOR (monitor), refresh_rate);
679       gdk_monitor_set_scale_factor (GDK_MONITOR (monitor), x11_screen->window_scale);
680       gdk_monitor_set_model (GDK_MONITOR (monitor), name);
681       gdk_monitor_set_connector (GDK_MONITOR (monitor), name);
682       gdk_monitor_set_manufacturer (GDK_MONITOR (monitor), manufacturer);
683       g_free (manufacturer);
684       g_free (name);
685 
686       if (rr_monitors[i].primary)
687         primary_output = monitor->output;
688 
689       XRRFreeOutputInfo (output_info);
690     }
691 
692   XRRFreeMonitors (rr_monitors);
693   XRRFreeScreenResources (resources);
694 
695   /* non RandR 1.2+ X driver doesn't return any usable multihead data */
696   if (randr12_compat)
697     {
698       for (i = 0; i < x11_display->monitors->len; i++)
699         {
700           GdkX11Monitor *monitor = x11_display->monitors->pdata[i];
701           if (monitor->remove)
702             gdk_display_monitor_removed (display, GDK_MONITOR (monitor));
703         }
704       g_ptr_array_remove_range (x11_display->monitors, 0, x11_display->monitors->len);
705       return FALSE;
706     }
707 
708   for (i = x11_display->monitors->len - 1; i >= 0; i--)
709     {
710       GdkX11Monitor *monitor = x11_display->monitors->pdata[i];
711       if (monitor->add)
712         {
713           gdk_display_monitor_added (display, GDK_MONITOR (monitor));
714           *changed = TRUE;
715         }
716       else if (monitor->remove)
717         {
718           g_object_ref (monitor);
719           g_ptr_array_remove (x11_display->monitors, monitor);
720           gdk_display_monitor_removed (display, GDK_MONITOR (monitor));
721           g_object_unref (monitor);
722           *changed = TRUE;
723         }
724     }
725 
726   old_primary = x11_display->primary_monitor;
727   x11_display->primary_monitor = 0;
728   for (i = 0; i < x11_display->monitors->len; ++i)
729     {
730       GdkX11Monitor *monitor = x11_display->monitors->pdata[i];
731       if (monitor->output == primary_output)
732         {
733           x11_display->primary_monitor = i;
734           break;
735         }
736 
737       /* No RandR1.3+ available or no primary set, fall back to prefer LVDS as primary if present */
738       if (primary_output == None &&
739           g_ascii_strncasecmp (gdk_monitor_get_model (GDK_MONITOR (monitor)), "LVDS", 4) == 0)
740         {
741           x11_display->primary_monitor = i;
742           break;
743         }
744 
745       /* No primary specified and no LVDS found */
746       if (monitor->output == first_output)
747         x11_display->primary_monitor = i;
748     }
749 
750   if (x11_display->primary_monitor != old_primary)
751     *changed = TRUE;
752 
753   return x11_display->monitors->len > 0;
754 #endif
755 
756   return FALSE;
757 }
758 
759 static gboolean
init_randr13(GdkScreen * screen,gboolean * changed)760 init_randr13 (GdkScreen *screen, gboolean *changed)
761 {
762 #ifdef HAVE_RANDR
763   GdkDisplay *display = gdk_screen_get_display (screen);
764   GdkX11Display *x11_display = GDK_X11_DISPLAY (display);
765   GdkX11Screen *x11_screen = GDK_X11_SCREEN (screen);
766   XRRScreenResources *resources;
767   RROutput primary_output = None;
768   RROutput first_output = None;
769   int i;
770   gboolean randr12_compat = FALSE;
771   int old_primary;
772 
773   if (!x11_display->have_randr13)
774       return FALSE;
775 
776   resources = XRRGetScreenResourcesCurrent (x11_screen->xdisplay,
777                                             x11_screen->xroot_window);
778   if (!resources)
779     return FALSE;
780 
781   for (i = 0; i < x11_display->monitors->len; i++)
782     {
783       GdkX11Monitor *monitor = x11_display->monitors->pdata[i];
784       monitor->add = FALSE;
785       monitor->remove = TRUE;
786     }
787 
788   for (i = 0; i < resources->noutput; ++i)
789     {
790       RROutput output = resources->outputs[i];
791       XRROutputInfo *output_info =
792         XRRGetOutputInfo (x11_screen->xdisplay, resources, output);
793 
794       /* Non RandR1.2+ X driver have output name "default" */
795       randr12_compat |= !g_strcmp0 (output_info->name, "default");
796 
797       if (output_info->connection == RR_Disconnected)
798         {
799           XRRFreeOutputInfo (output_info);
800           continue;
801         }
802 
803       if (output_info->crtc)
804 	{
805 	  GdkX11Monitor *monitor;
806 	  XRRCrtcInfo *crtc = XRRGetCrtcInfo (x11_screen->xdisplay, resources, output_info->crtc);
807           char *name;
808           GdkRectangle geometry;
809           GdkRectangle newgeo;
810           int j;
811           int refresh_rate = 0;
812 
813           for (j = 0; j < resources->nmode; j++)
814             {
815               XRRModeInfo *xmode = &resources->modes[j];
816               if (xmode->id == crtc->mode)
817                 {
818                   if (xmode->hTotal != 0 && xmode->vTotal != 0)
819                     refresh_rate = (1000 * xmode->dotClock) / (xmode->hTotal * xmode->vTotal);
820                   break;
821                 }
822             }
823 
824           monitor = find_monitor_by_output (x11_display, output);
825           if (monitor)
826             monitor->remove = FALSE;
827           else
828             {
829               monitor = g_object_new (gdk_x11_monitor_get_type (),
830                                       "display", display,
831                                       NULL);
832               monitor->output = output;
833               monitor->add = TRUE;
834               g_ptr_array_add (x11_display->monitors, monitor);
835             }
836 
837           gdk_monitor_get_geometry (GDK_MONITOR (monitor), &geometry);
838           name = g_strndup (output_info->name, output_info->nameLen);
839 
840           newgeo.x = crtc->x / x11_screen->window_scale;
841           newgeo.y = crtc->y / x11_screen->window_scale;
842           newgeo.width = crtc->width / x11_screen->window_scale;
843           newgeo.height = crtc->height / x11_screen->window_scale;
844           if (newgeo.x != geometry.x ||
845               newgeo.y != geometry.y ||
846               newgeo.width != geometry.width ||
847               newgeo.height != geometry.height ||
848               output_info->mm_width != gdk_monitor_get_width_mm (GDK_MONITOR (monitor)) ||
849               output_info->mm_height != gdk_monitor_get_height_mm (GDK_MONITOR (monitor)) ||
850               g_strcmp0 (name, gdk_monitor_get_model (GDK_MONITOR (monitor))) != 0)
851             *changed = TRUE;
852 
853           gdk_monitor_set_position (GDK_MONITOR (monitor), newgeo.x, newgeo.y);
854           gdk_monitor_set_size (GDK_MONITOR (monitor), newgeo.width, newgeo.height);
855           g_object_notify (G_OBJECT (monitor), "workarea");
856           gdk_monitor_set_physical_size (GDK_MONITOR (monitor),
857                                          output_info->mm_width,
858                                          output_info->mm_height);
859           gdk_monitor_set_subpixel_layout (GDK_MONITOR (monitor),
860                                            translate_subpixel_order (output_info->subpixel_order));
861           gdk_monitor_set_refresh_rate (GDK_MONITOR (monitor), refresh_rate);
862           gdk_monitor_set_scale_factor (GDK_MONITOR (monitor), x11_screen->window_scale);
863           gdk_monitor_set_model (GDK_MONITOR (monitor), name);
864 
865           g_free (name);
866 
867           XRRFreeCrtcInfo (crtc);
868 	}
869 
870       XRRFreeOutputInfo (output_info);
871     }
872 
873   if (resources->noutput > 0)
874     first_output = resources->outputs[0];
875 
876   XRRFreeScreenResources (resources);
877 
878   if (randr12_compat)
879     {
880       for (i = 0; i < x11_display->monitors->len; i++)
881         {
882           GdkX11Monitor *monitor = x11_display->monitors->pdata[i];
883           if (monitor->remove)
884             gdk_display_monitor_removed (display, GDK_MONITOR (monitor));
885         }
886       g_ptr_array_remove_range (x11_display->monitors, 0, x11_display->monitors->len);
887       return FALSE;
888     }
889 
890   for (i = x11_display->monitors->len - 1; i >= 0; i--)
891     {
892       GdkX11Monitor *monitor = x11_display->monitors->pdata[i];
893       if (monitor->add)
894         {
895           gdk_display_monitor_added (display, GDK_MONITOR (monitor));
896           *changed = TRUE;
897         }
898       else if (monitor->remove)
899         {
900           g_object_ref (monitor);
901           g_ptr_array_remove (x11_display->monitors, monitor);
902           gdk_display_monitor_removed (display, GDK_MONITOR (monitor));
903           g_object_unref (monitor);
904           *changed = TRUE;
905         }
906     }
907 
908   old_primary = x11_display->primary_monitor;
909   x11_display->primary_monitor = 0;
910   primary_output = XRRGetOutputPrimary (x11_screen->xdisplay,
911                                         x11_screen->xroot_window);
912 
913   for (i = 0; i < x11_display->monitors->len; ++i)
914     {
915       GdkX11Monitor *monitor = x11_display->monitors->pdata[i];
916       if (monitor->output == primary_output)
917         {
918           x11_display->primary_monitor = i;
919           break;
920         }
921 
922       /* No RandR1.3+ available or no primary set, fall back to prefer LVDS as primary if present */
923       if (primary_output == None &&
924           g_ascii_strncasecmp (gdk_monitor_get_model (GDK_MONITOR (monitor)), "LVDS", 4) == 0)
925         {
926           x11_display->primary_monitor = i;
927           break;
928         }
929 
930       /* No primary specified and no LVDS found */
931       if (monitor->output == first_output)
932         x11_display->primary_monitor = i;
933     }
934 
935   if (x11_display->primary_monitor != old_primary)
936     *changed = TRUE;
937 
938   return x11_display->monitors->len > 0;
939 #endif
940 
941   return FALSE;
942 }
943 
944 static void
init_no_multihead(GdkScreen * screen,gboolean * changed)945 init_no_multihead (GdkScreen *screen, gboolean *changed)
946 {
947   GdkDisplay *display = gdk_screen_get_display (screen);
948   GdkX11Display *x11_display = GDK_X11_DISPLAY (display);
949   GdkX11Screen *x11_screen = GDK_X11_SCREEN (screen);
950   GdkX11Monitor *monitor;
951   GdkRectangle geometry;
952   GdkRectangle newgeo;
953   int i;
954 
955   for (i = 0; i < x11_display->monitors->len; i++)
956     {
957       GdkX11Monitor *monitor = x11_display->monitors->pdata[i];
958       monitor->add = FALSE;
959       monitor->remove = TRUE;
960     }
961 
962   monitor = find_monitor_by_output (x11_display, 0);
963   if (monitor)
964     monitor->remove = FALSE;
965   else
966     {
967       monitor = g_object_new (gdk_x11_monitor_get_type (),
968                               "display", x11_display,
969                               NULL);
970       monitor->output = 0;
971       monitor->add = TRUE;
972       g_ptr_array_add (x11_display->monitors, monitor);
973     }
974 
975   gdk_monitor_get_geometry (GDK_MONITOR (monitor), &geometry);
976 
977   newgeo.x = 0;
978   newgeo.y = 0;
979   newgeo.width = DisplayWidth (x11_display->xdisplay, x11_screen->screen_num) /
980                                x11_screen->window_scale;
981   newgeo.height = DisplayHeight (x11_display->xdisplay, x11_screen->screen_num) /
982                                  x11_screen->window_scale;
983 
984   if (newgeo.x != geometry.x ||
985       newgeo.y != geometry.y ||
986       newgeo.width != geometry.width ||
987       newgeo.height != geometry.height ||
988       gdk_x11_screen_get_width_mm (screen) != gdk_monitor_get_width_mm (GDK_MONITOR (monitor)) ||
989       gdk_x11_screen_get_height_mm (screen) != gdk_monitor_get_height_mm (GDK_MONITOR (monitor)))
990     *changed = TRUE;
991 
992   gdk_monitor_set_position (GDK_MONITOR (monitor), newgeo.x, newgeo.y);
993   gdk_monitor_set_size (GDK_MONITOR (monitor), newgeo.width, newgeo.height);
994 
995   g_object_notify (G_OBJECT (monitor), "workarea");
996   gdk_monitor_set_physical_size (GDK_MONITOR (monitor),
997                                  gdk_x11_screen_get_width_mm (screen),
998                                  gdk_x11_screen_get_height_mm (screen));
999   gdk_monitor_set_scale_factor (GDK_MONITOR (monitor), x11_screen->window_scale);
1000 
1001   if (x11_display->primary_monitor != 0)
1002     *changed = TRUE;
1003   x11_display->primary_monitor = 0;
1004 
1005   for (i = x11_display->monitors->len - 1; i >= 0; i--)
1006     {
1007       GdkX11Monitor *monitor = x11_display->monitors->pdata[i];
1008       if (monitor->add)
1009         {
1010           gdk_display_monitor_added (GDK_DISPLAY (x11_display), GDK_MONITOR (monitor));
1011           *changed = TRUE;
1012         }
1013       else if (monitor->remove)
1014         {
1015           g_object_ref (monitor);
1016           g_ptr_array_remove (x11_display->monitors, monitor);
1017           gdk_display_monitor_removed (GDK_DISPLAY (x11_display), GDK_MONITOR (monitor));
1018           g_object_unref (monitor);
1019           *changed = TRUE;
1020         }
1021     }
1022 }
1023 
1024 static gboolean
init_multihead(GdkScreen * screen)1025 init_multihead (GdkScreen *screen)
1026 {
1027   gboolean any_changed = FALSE;
1028 
1029   if (!init_randr15 (screen, &any_changed) &&
1030       !init_randr13 (screen, &any_changed))
1031     init_no_multihead (screen, &any_changed);
1032 
1033   return any_changed;
1034 }
1035 
1036 static void
update_bounding_box(GdkScreen * screen)1037 update_bounding_box (GdkScreen *screen)
1038 {
1039   GdkX11Screen *x11_screen = GDK_X11_SCREEN (screen);
1040   GdkX11Display *x11_display = GDK_X11_DISPLAY (x11_screen->display);
1041   gint i, x1, y1, x2, y2;
1042 
1043   x1 = y1 = G_MAXINT;
1044   x2 = y2 = G_MININT;
1045 
1046   for (i = 0; i < x11_display->monitors->len; i++)
1047     {
1048       GdkX11Monitor *monitor = x11_display->monitors->pdata[i];
1049       GdkRectangle geometry;
1050 
1051       gdk_monitor_get_geometry (GDK_MONITOR (monitor), &geometry);
1052       x1 = MIN (x1, geometry.x);
1053       y1 = MIN (y1, geometry.y);
1054       x2 = MAX (x2, geometry.x + geometry.width);
1055       y2 = MAX (y2, geometry.y + geometry.height);
1056     }
1057 
1058   x11_screen->width = x2 - x1;
1059   x11_screen->height = y2 - y1;
1060 }
1061 
1062 GdkScreen *
_gdk_x11_screen_new(GdkDisplay * display,gint screen_number)1063 _gdk_x11_screen_new (GdkDisplay *display,
1064 		     gint	 screen_number)
1065 {
1066   GdkScreen *screen;
1067   GdkX11Screen *x11_screen;
1068   GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
1069   const char *scale_str;
1070 
1071   screen = g_object_new (GDK_TYPE_X11_SCREEN, NULL);
1072 
1073   x11_screen = GDK_X11_SCREEN (screen);
1074   x11_screen->display = display;
1075   x11_screen->xdisplay = display_x11->xdisplay;
1076   x11_screen->xscreen = ScreenOfDisplay (display_x11->xdisplay, screen_number);
1077   x11_screen->screen_num = screen_number;
1078   x11_screen->xroot_window = RootWindow (display_x11->xdisplay,screen_number);
1079   x11_screen->wmspec_check_window = None;
1080   /* we want this to be always non-null */
1081   x11_screen->window_manager_name = g_strdup ("unknown");
1082 
1083   scale_str = g_getenv ("GDK_SCALE");
1084   if (scale_str)
1085     {
1086       x11_screen->fixed_window_scale = TRUE;
1087       x11_screen->window_scale = atol (scale_str);
1088       if (x11_screen->window_scale == 0)
1089         x11_screen->window_scale = 1;
1090     }
1091   else
1092     x11_screen->window_scale = 1;
1093 
1094   init_randr_support (screen);
1095   init_multihead (screen);
1096 
1097   _gdk_x11_screen_init_visuals (screen);
1098   _gdk_x11_screen_init_root_window (screen);
1099   update_bounding_box (screen);
1100 
1101   return screen;
1102 }
1103 
1104 void
_gdk_x11_screen_set_window_scale(GdkX11Screen * x11_screen,gint scale)1105 _gdk_x11_screen_set_window_scale (GdkX11Screen *x11_screen,
1106 				  gint          scale)
1107 {
1108   GList *toplevels, *l;
1109   GdkWindow *root;
1110 
1111   if (x11_screen->window_scale == scale)
1112     return;
1113 
1114   x11_screen->window_scale = scale;
1115 
1116   root = x11_screen->root_window;
1117   GDK_WINDOW_IMPL_X11 (root->impl)->window_scale = scale;
1118 
1119   if (GDK_WINDOW_IMPL_X11 (root->impl)->cairo_surface)
1120     cairo_surface_set_device_scale (GDK_WINDOW_IMPL_X11 (root->impl)->cairo_surface,
1121                                     scale, scale);
1122 
1123   toplevels = gdk_screen_get_toplevel_windows (GDK_SCREEN (x11_screen));
1124 
1125   for (l = toplevels; l != NULL; l = l->next)
1126     {
1127       GdkWindow *window = l->data;
1128 
1129       _gdk_x11_window_set_window_scale (window, scale);
1130     }
1131 
1132   process_monitors_change (GDK_SCREEN (x11_screen));
1133 }
1134 
1135 /*
1136  * It is important that we first request the selection
1137  * notification, and then setup the initial state of
1138  * is_composited to avoid a race condition here.
1139  */
1140 void
_gdk_x11_screen_setup(GdkScreen * screen)1141 _gdk_x11_screen_setup (GdkScreen *screen)
1142 {
1143   GdkX11Screen *x11_screen = GDK_X11_SCREEN (screen);
1144 
1145   gdk_display_request_selection_notification (x11_screen->display,
1146 					      gdk_x11_xatom_to_atom_for_display (x11_screen->display, get_cm_atom (x11_screen)));
1147   x11_screen->is_composited = check_is_composited (x11_screen->display, x11_screen);
1148 }
1149 
1150 static gboolean
gdk_x11_screen_is_composited(GdkScreen * screen)1151 gdk_x11_screen_is_composited (GdkScreen *screen)
1152 {
1153   GdkX11Screen *x11_screen = GDK_X11_SCREEN (screen);
1154 
1155   return x11_screen->is_composited;
1156 }
1157 
1158 static void
init_randr_support(GdkScreen * screen)1159 init_randr_support (GdkScreen *screen)
1160 {
1161   GdkX11Screen *x11_screen = GDK_X11_SCREEN (screen);
1162 
1163   /* NB: This is also needed for XSettings, so don't remove. */
1164   XSelectInput (GDK_SCREEN_XDISPLAY (screen),
1165                 x11_screen->xroot_window,
1166                 StructureNotifyMask);
1167 
1168 #ifdef HAVE_RANDR
1169   if (!GDK_X11_DISPLAY (gdk_screen_get_display (screen))->have_randr12)
1170     return;
1171 
1172   XRRSelectInput (GDK_SCREEN_XDISPLAY (screen),
1173                   x11_screen->xroot_window,
1174                   RRScreenChangeNotifyMask
1175                   | RRCrtcChangeNotifyMask
1176                   | RROutputPropertyNotifyMask);
1177 #endif
1178 }
1179 
1180 static void
process_monitors_change(GdkScreen * screen)1181 process_monitors_change (GdkScreen *screen)
1182 {
1183   if (init_multihead (screen))
1184     {
1185       update_bounding_box (screen);
1186       g_signal_emit_by_name (screen, "monitors-changed");
1187     }
1188 }
1189 
1190 void
_gdk_x11_screen_size_changed(GdkScreen * screen,XEvent * event)1191 _gdk_x11_screen_size_changed (GdkScreen *screen,
1192 			      XEvent    *event)
1193 {
1194   gint width, height;
1195 #ifdef HAVE_RANDR
1196   GdkX11Display *display_x11;
1197 #endif
1198 
1199   width = gdk_x11_screen_get_width (screen);
1200   height = gdk_x11_screen_get_height (screen);
1201 
1202 #ifdef HAVE_RANDR
1203   display_x11 = GDK_X11_DISPLAY (gdk_screen_get_display (screen));
1204 
1205   if (display_x11->have_randr13 && event->type == ConfigureNotify)
1206     return;
1207 
1208   XRRUpdateConfiguration (event);
1209 #else
1210   if (event->type == ConfigureNotify)
1211     {
1212       XConfigureEvent *rcevent = (XConfigureEvent *) event;
1213       Screen	    *xscreen = gdk_x11_screen_get_xscreen (screen);
1214 
1215       xscreen->width   = rcevent->width;
1216       xscreen->height  = rcevent->height;
1217     }
1218   else
1219     return;
1220 #endif
1221 
1222   process_monitors_change (screen);
1223 
1224   if (width != gdk_x11_screen_get_width (screen) ||
1225       height != gdk_x11_screen_get_height (screen))
1226     g_signal_emit_by_name (screen, "size-changed");
1227 }
1228 
1229 void
_gdk_x11_screen_get_edge_monitors(GdkScreen * screen,gint * top,gint * bottom,gint * left,gint * right)1230 _gdk_x11_screen_get_edge_monitors (GdkScreen *screen,
1231                                    gint      *top,
1232                                    gint      *bottom,
1233                                    gint      *left,
1234                                    gint      *right)
1235 {
1236 #ifdef HAVE_XFREE_XINERAMA
1237   GdkX11Screen *x11_screen = GDK_X11_SCREEN (screen);
1238   gint          top_most_pos = x11_screen->height;
1239   gint          left_most_pos = x11_screen->width;
1240   gint          bottom_most_pos = 0;
1241   gint          right_most_pos = 0;
1242   gint          i;
1243   XineramaScreenInfo *x_monitors;
1244   int x_n_monitors;
1245 #endif
1246 
1247   *top = *bottom = *left = *right = -1;
1248 
1249 #ifdef HAVE_XFREE_XINERAMA
1250   if (!XineramaIsActive (x11_screen->xdisplay))
1251     return;
1252 
1253   x_monitors = XineramaQueryScreens (x11_screen->xdisplay, &x_n_monitors);
1254   if (x_n_monitors <= 0 || x_monitors == NULL)
1255     {
1256       if (x_monitors)
1257         XFree (x_monitors);
1258 
1259       return;
1260     }
1261 
1262   for (i = 0; i < x_n_monitors; i++)
1263     {
1264       if (left && left_most_pos > x_monitors[i].x_org)
1265 	{
1266 	  left_most_pos = x_monitors[i].x_org;
1267 	  *left = i;
1268 	}
1269       if (right && right_most_pos < x_monitors[i].x_org + x_monitors[i].width)
1270 	{
1271 	  right_most_pos = x_monitors[i].x_org + x_monitors[i].width;
1272 	  *right = i;
1273 	}
1274       if (top && top_most_pos > x_monitors[i].y_org)
1275 	{
1276 	  top_most_pos = x_monitors[i].y_org;
1277 	  *top = i;
1278 	}
1279       if (bottom && bottom_most_pos < x_monitors[i].y_org + x_monitors[i].height)
1280 	{
1281 	  bottom_most_pos = x_monitors[i].y_org + x_monitors[i].height;
1282 	  *bottom = i;
1283 	}
1284     }
1285 
1286   XFree (x_monitors);
1287 #endif
1288 }
1289 
1290 void
_gdk_x11_screen_window_manager_changed(GdkScreen * screen)1291 _gdk_x11_screen_window_manager_changed (GdkScreen *screen)
1292 {
1293   g_signal_emit (screen, signals[WINDOW_MANAGER_CHANGED], 0);
1294 }
1295 
1296 void
_gdk_x11_screen_process_owner_change(GdkScreen * screen,XEvent * event)1297 _gdk_x11_screen_process_owner_change (GdkScreen *screen,
1298 				      XEvent *event)
1299 {
1300 #ifdef HAVE_XFIXES
1301   XFixesSelectionNotifyEvent *selection_event = (XFixesSelectionNotifyEvent *)event;
1302   GdkX11Screen *x11_screen = GDK_X11_SCREEN (screen);
1303 
1304   if (selection_event->selection == get_cm_atom (x11_screen))
1305     {
1306       gboolean composited = selection_event->owner != None;
1307 
1308       if (composited != x11_screen->is_composited)
1309 	{
1310 	  x11_screen->is_composited = composited;
1311 
1312 	  g_signal_emit_by_name (screen, "composited-changed");
1313 	}
1314     }
1315 #endif
1316 }
1317 
1318 static gchar *
substitute_screen_number(const gchar * display_name,gint screen_number)1319 substitute_screen_number (const gchar *display_name,
1320                           gint         screen_number)
1321 {
1322   GString *str;
1323   gchar   *p;
1324 
1325   str = g_string_new (display_name);
1326 
1327   p = strrchr (str->str, '.');
1328   if (p && p >	strchr (str->str, ':'))
1329     g_string_truncate (str, p - str->str);
1330 
1331   g_string_append_printf (str, ".%d", screen_number);
1332 
1333   return g_string_free (str, FALSE);
1334 }
1335 
1336 static gchar *
gdk_x11_screen_make_display_name(GdkScreen * screen)1337 gdk_x11_screen_make_display_name (GdkScreen *screen)
1338 {
1339   const gchar *old_display;
1340 
1341   old_display = gdk_display_get_name (gdk_screen_get_display (screen));
1342 
1343   return substitute_screen_number (old_display,
1344                                    gdk_x11_screen_get_number (screen));
1345 }
1346 
1347 static GdkWindow *
gdk_x11_screen_get_active_window(GdkScreen * screen)1348 gdk_x11_screen_get_active_window (GdkScreen *screen)
1349 {
1350   GdkX11Screen *x11_screen = GDK_X11_SCREEN (screen);
1351   GdkWindow *ret = NULL;
1352   Atom type_return;
1353   gint format_return;
1354   gulong nitems_return;
1355   gulong bytes_after_return;
1356   guchar *data = NULL;
1357 
1358   if (!gdk_x11_screen_supports_net_wm_hint (screen,
1359                                             gdk_atom_intern_static_string ("_NET_ACTIVE_WINDOW")))
1360     return NULL;
1361 
1362   if (XGetWindowProperty (x11_screen->xdisplay, x11_screen->xroot_window,
1363 	                  gdk_x11_get_xatom_by_name_for_display (x11_screen->display,
1364 			                                         "_NET_ACTIVE_WINDOW"),
1365 		          0, 1, False, XA_WINDOW, &type_return,
1366 		          &format_return, &nitems_return,
1367                           &bytes_after_return, &data)
1368       == Success)
1369     {
1370       if ((type_return == XA_WINDOW) && (format_return == 32) && (data))
1371         {
1372           Window window = *(Window *) data;
1373 
1374           if (window != None)
1375             {
1376               ret = gdk_x11_window_foreign_new_for_display (x11_screen->display,
1377                                                             window);
1378             }
1379         }
1380     }
1381 
1382   if (data)
1383     XFree (data);
1384 
1385   return ret;
1386 }
1387 
1388 static GList *
gdk_x11_screen_get_window_stack(GdkScreen * screen)1389 gdk_x11_screen_get_window_stack (GdkScreen *screen)
1390 {
1391   GdkX11Screen *x11_screen = GDK_X11_SCREEN (screen);
1392   GList *ret = NULL;
1393   Atom type_return;
1394   gint format_return;
1395   gulong nitems_return;
1396   gulong bytes_after_return;
1397   guchar *data = NULL;
1398 
1399   if (!gdk_x11_screen_supports_net_wm_hint (screen,
1400                                             gdk_atom_intern_static_string ("_NET_CLIENT_LIST_STACKING")))
1401     return NULL;
1402 
1403   if (XGetWindowProperty (x11_screen->xdisplay, x11_screen->xroot_window,
1404 	                  gdk_x11_get_xatom_by_name_for_display (x11_screen->display,
1405 			                                         "_NET_CLIENT_LIST_STACKING"),
1406 		          0, G_MAXLONG, False, XA_WINDOW, &type_return,
1407 		          &format_return, &nitems_return,
1408                           &bytes_after_return, &data)
1409       == Success)
1410     {
1411       if ((type_return == XA_WINDOW) && (format_return == 32) &&
1412           (data) && (nitems_return > 0))
1413         {
1414           gulong *stack = (gulong *) data;
1415           GdkWindow *win;
1416           int i;
1417 
1418           for (i = 0; i < nitems_return; i++)
1419             {
1420               win = gdk_x11_window_foreign_new_for_display (x11_screen->display,
1421                                                             (Window)stack[i]);
1422 
1423               if (win != NULL)
1424                 ret = g_list_append (ret, win);
1425             }
1426         }
1427     }
1428 
1429   if (data)
1430     XFree (data);
1431 
1432   return ret;
1433 }
1434 
1435 static gboolean
gdk_x11_screen_get_setting(GdkScreen * screen,const gchar * name,GValue * value)1436 gdk_x11_screen_get_setting (GdkScreen   *screen,
1437 			    const gchar *name,
1438 			    GValue      *value)
1439 {
1440   GdkX11Screen *x11_screen = GDK_X11_SCREEN (screen);
1441   const GValue *setting;
1442 
1443   if (x11_screen->xsettings == NULL)
1444     goto out;
1445   setting = g_hash_table_lookup (x11_screen->xsettings, name);
1446   if (setting == NULL)
1447     goto out;
1448 
1449   if (!g_value_transform (setting, value))
1450     {
1451       g_warning ("Cannot transform xsetting %s of type %s to type %s\n",
1452 		 name,
1453 		 g_type_name (G_VALUE_TYPE (setting)),
1454 		 g_type_name (G_VALUE_TYPE (value)));
1455       goto out;
1456     }
1457 
1458   return TRUE;
1459 
1460  out:
1461   return _gdk_x11_get_xft_setting (screen, name, value);
1462 }
1463 
1464 static void
cleanup_atoms(gpointer data)1465 cleanup_atoms(gpointer data)
1466 {
1467   NetWmSupportedAtoms *supported_atoms = data;
1468   if (supported_atoms->atoms)
1469       XFree (supported_atoms->atoms);
1470   g_free (supported_atoms);
1471 }
1472 
1473 static Window
get_net_supporting_wm_check(GdkX11Screen * screen,Window window)1474 get_net_supporting_wm_check (GdkX11Screen *screen,
1475                              Window        window)
1476 {
1477   GdkDisplay *display;
1478   Atom type;
1479   gint format;
1480   gulong n_items;
1481   gulong bytes_after;
1482   guchar *data;
1483   Window value;
1484 
1485   display = screen->display;
1486   type = None;
1487   data = NULL;
1488   value = None;
1489 
1490   gdk_x11_display_error_trap_push (display);
1491   XGetWindowProperty (screen->xdisplay, window,
1492 		      gdk_x11_get_xatom_by_name_for_display (display, "_NET_SUPPORTING_WM_CHECK"),
1493 		      0, G_MAXLONG, False, XA_WINDOW, &type, &format,
1494 		      &n_items, &bytes_after, &data);
1495   gdk_x11_display_error_trap_pop_ignored (display);
1496 
1497   if (type == XA_WINDOW)
1498     value = *(Window *)data;
1499 
1500   if (data)
1501     XFree (data);
1502 
1503   return value;
1504 }
1505 
1506 static void
fetch_net_wm_check_window(GdkScreen * screen)1507 fetch_net_wm_check_window (GdkScreen *screen)
1508 {
1509   GdkX11Screen *x11_screen;
1510   GdkDisplay *display;
1511   Window window;
1512   GTimeVal tv;
1513   gint error;
1514 
1515   x11_screen = GDK_X11_SCREEN (screen);
1516   display = x11_screen->display;
1517 
1518   g_return_if_fail (GDK_X11_DISPLAY (display)->trusted_client);
1519 
1520   if (x11_screen->wmspec_check_window != None)
1521     return; /* already have it */
1522 
1523   g_get_current_time (&tv);
1524 
1525   if (ABS  (tv.tv_sec - x11_screen->last_wmspec_check_time) < 15)
1526     return; /* we've checked recently */
1527 
1528   window = get_net_supporting_wm_check (x11_screen, x11_screen->xroot_window);
1529   if (window == None)
1530     return;
1531 
1532   if (window != get_net_supporting_wm_check (x11_screen, window))
1533     return;
1534 
1535   gdk_x11_display_error_trap_push (display);
1536 
1537   /* Find out if this WM goes away, so we can reset everything. */
1538   XSelectInput (x11_screen->xdisplay, window, StructureNotifyMask);
1539 
1540   error = gdk_x11_display_error_trap_pop (display);
1541   if (!error)
1542     {
1543       /* We check the window property again because after XGetWindowProperty()
1544        * and before XSelectInput() the window may have been recycled in such a
1545        * way that XSelectInput() doesn't fail but the window is no longer what
1546        * we want.
1547        */
1548       if (window != get_net_supporting_wm_check (x11_screen, window))
1549         return;
1550 
1551       x11_screen->wmspec_check_window = window;
1552       x11_screen->last_wmspec_check_time = tv.tv_sec;
1553       x11_screen->need_refetch_net_supported = TRUE;
1554       x11_screen->need_refetch_wm_name = TRUE;
1555 
1556       /* Careful, reentrancy */
1557       _gdk_x11_screen_window_manager_changed (screen);
1558     }
1559 }
1560 
1561 /**
1562  * gdk_x11_screen_supports_net_wm_hint:
1563  * @screen: (type GdkX11Screen): the relevant #GdkScreen.
1564  * @property: a property atom.
1565  *
1566  * This function is specific to the X11 backend of GDK, and indicates
1567  * whether the window manager supports a certain hint from the
1568  * [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) specification.
1569  *
1570  * When using this function, keep in mind that the window manager
1571  * can change over time; so you shouldn’t use this function in
1572  * a way that impacts persistent application state. A common bug
1573  * is that your application can start up before the window manager
1574  * does when the user logs in, and before the window manager starts
1575  * gdk_x11_screen_supports_net_wm_hint() will return %FALSE for every property.
1576  * You can monitor the window_manager_changed signal on #GdkScreen to detect
1577  * a window manager change.
1578  *
1579  * Returns: %TRUE if the window manager supports @property
1580  *
1581  * Since: 2.2
1582  **/
1583 gboolean
gdk_x11_screen_supports_net_wm_hint(GdkScreen * screen,GdkAtom property)1584 gdk_x11_screen_supports_net_wm_hint (GdkScreen *screen,
1585 				     GdkAtom    property)
1586 {
1587   gulong i;
1588   GdkX11Screen *x11_screen;
1589   NetWmSupportedAtoms *supported_atoms;
1590   GdkDisplay *display;
1591   Atom atom;
1592 
1593   g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
1594 
1595   x11_screen = GDK_X11_SCREEN (screen);
1596   display = x11_screen->display;
1597 
1598   if (!G_LIKELY (GDK_X11_DISPLAY (display)->trusted_client))
1599     return FALSE;
1600 
1601   supported_atoms = g_object_get_data (G_OBJECT (screen), "gdk-net-wm-supported-atoms");
1602   if (!supported_atoms)
1603     {
1604       supported_atoms = g_new0 (NetWmSupportedAtoms, 1);
1605       g_object_set_data_full (G_OBJECT (screen), "gdk-net-wm-supported-atoms", supported_atoms, cleanup_atoms);
1606     }
1607 
1608   fetch_net_wm_check_window (screen);
1609 
1610   if (x11_screen->wmspec_check_window == None)
1611     return FALSE;
1612 
1613   if (x11_screen->need_refetch_net_supported)
1614     {
1615       /* WM has changed since we last got the supported list,
1616        * refetch it.
1617        */
1618       Atom type;
1619       gint format;
1620       gulong bytes_after;
1621 
1622       x11_screen->need_refetch_net_supported = FALSE;
1623 
1624       if (supported_atoms->atoms)
1625         XFree (supported_atoms->atoms);
1626 
1627       supported_atoms->atoms = NULL;
1628       supported_atoms->n_atoms = 0;
1629 
1630       XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), x11_screen->xroot_window,
1631                           gdk_x11_get_xatom_by_name_for_display (display, "_NET_SUPPORTED"),
1632                           0, G_MAXLONG, False, XA_ATOM, &type, &format,
1633                           &supported_atoms->n_atoms, &bytes_after,
1634                           (guchar **)&supported_atoms->atoms);
1635 
1636       if (type != XA_ATOM)
1637         return FALSE;
1638     }
1639 
1640   if (supported_atoms->atoms == NULL)
1641     return FALSE;
1642 
1643   atom = gdk_x11_atom_to_xatom_for_display (display, property);
1644 
1645   for (i = 0; i < supported_atoms->n_atoms; i++)
1646     {
1647       if (supported_atoms->atoms[i] == atom)
1648         return TRUE;
1649     }
1650 
1651   return FALSE;
1652 }
1653 
1654 /**
1655  * gdk_x11_screen_get_window_manager_name:
1656  * @screen: (type GdkX11Screen): a #GdkScreen
1657  *
1658  * Returns the name of the window manager for @screen.
1659  *
1660  * Returns: the name of the window manager screen @screen, or
1661  * "unknown" if the window manager is unknown. The string is owned by GDK
1662  * and should not be freed.
1663  *
1664  * Since: 2.2
1665  **/
1666 const char*
gdk_x11_screen_get_window_manager_name(GdkScreen * screen)1667 gdk_x11_screen_get_window_manager_name (GdkScreen *screen)
1668 {
1669   GdkX11Screen *x11_screen;
1670   GdkDisplay *display;
1671 
1672   x11_screen = GDK_X11_SCREEN (screen);
1673   display = x11_screen->display;
1674 
1675   if (!G_LIKELY (GDK_X11_DISPLAY (display)->trusted_client))
1676     return x11_screen->window_manager_name;
1677 
1678   fetch_net_wm_check_window (screen);
1679 
1680   if (x11_screen->need_refetch_wm_name)
1681     {
1682       /* Get the name of the window manager */
1683       x11_screen->need_refetch_wm_name = FALSE;
1684 
1685       g_free (x11_screen->window_manager_name);
1686       x11_screen->window_manager_name = g_strdup ("unknown");
1687 
1688       if (x11_screen->wmspec_check_window != None)
1689         {
1690           Atom type;
1691           gint format;
1692           gulong n_items;
1693           gulong bytes_after;
1694           gchar *name;
1695 
1696           name = NULL;
1697 
1698           gdk_x11_display_error_trap_push (display);
1699 
1700           XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
1701                               x11_screen->wmspec_check_window,
1702                               gdk_x11_get_xatom_by_name_for_display (display,
1703                                                                      "_NET_WM_NAME"),
1704                               0, G_MAXLONG, False,
1705                               gdk_x11_get_xatom_by_name_for_display (display,
1706                                                                      "UTF8_STRING"),
1707                               &type, &format,
1708                               &n_items, &bytes_after,
1709                               (guchar **)&name);
1710 
1711           gdk_x11_display_error_trap_pop_ignored (display);
1712 
1713           if (name != NULL)
1714             {
1715               g_free (x11_screen->window_manager_name);
1716               x11_screen->window_manager_name = g_strdup (name);
1717               XFree (name);
1718             }
1719         }
1720     }
1721 
1722   return GDK_X11_SCREEN (screen)->window_manager_name;
1723 }
1724 
1725 static void
gdk_x11_screen_class_init(GdkX11ScreenClass * klass)1726 gdk_x11_screen_class_init (GdkX11ScreenClass *klass)
1727 {
1728   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1729   GdkScreenClass *screen_class = GDK_SCREEN_CLASS (klass);
1730 
1731   object_class->dispose = gdk_x11_screen_dispose;
1732   object_class->finalize = gdk_x11_screen_finalize;
1733 
1734   screen_class->get_display = gdk_x11_screen_get_display;
1735   screen_class->get_width = gdk_x11_screen_get_width;
1736   screen_class->get_height = gdk_x11_screen_get_height;
1737   screen_class->get_width_mm = gdk_x11_screen_get_width_mm;
1738   screen_class->get_height_mm = gdk_x11_screen_get_height_mm;
1739   screen_class->get_number = gdk_x11_screen_get_number;
1740   screen_class->get_root_window = gdk_x11_screen_get_root_window;
1741   screen_class->get_system_visual = _gdk_x11_screen_get_system_visual;
1742   screen_class->get_rgba_visual = gdk_x11_screen_get_rgba_visual;
1743   screen_class->is_composited = gdk_x11_screen_is_composited;
1744   screen_class->make_display_name = gdk_x11_screen_make_display_name;
1745   screen_class->get_active_window = gdk_x11_screen_get_active_window;
1746   screen_class->get_window_stack = gdk_x11_screen_get_window_stack;
1747   screen_class->get_setting = gdk_x11_screen_get_setting;
1748   screen_class->visual_get_best_depth = _gdk_x11_screen_visual_get_best_depth;
1749   screen_class->visual_get_best_type = _gdk_x11_screen_visual_get_best_type;
1750   screen_class->visual_get_best = _gdk_x11_screen_visual_get_best;
1751   screen_class->visual_get_best_with_depth = _gdk_x11_screen_visual_get_best_with_depth;
1752   screen_class->visual_get_best_with_type = _gdk_x11_screen_visual_get_best_with_type;
1753   screen_class->visual_get_best_with_both = _gdk_x11_screen_visual_get_best_with_both;
1754   screen_class->query_depths = _gdk_x11_screen_query_depths;
1755   screen_class->query_visual_types = _gdk_x11_screen_query_visual_types;
1756   screen_class->list_visuals = _gdk_x11_screen_list_visuals;
1757 
1758   signals[WINDOW_MANAGER_CHANGED] =
1759     g_signal_new (g_intern_static_string ("window-manager-changed"),
1760                   G_OBJECT_CLASS_TYPE (object_class),
1761                   G_SIGNAL_RUN_LAST,
1762                   G_STRUCT_OFFSET (GdkX11ScreenClass, window_manager_changed),
1763                   NULL, NULL,
1764                   NULL,
1765                   G_TYPE_NONE,
1766                   0);
1767 }
1768 
1769 static guint32
get_netwm_cardinal_property(GdkScreen * screen,const gchar * name)1770 get_netwm_cardinal_property (GdkScreen   *screen,
1771                              const gchar *name)
1772 {
1773   GdkX11Screen *x11_screen = GDK_X11_SCREEN (screen);
1774   GdkAtom atom;
1775   guint32 prop = 0;
1776   Atom type;
1777   gint format;
1778   gulong nitems;
1779   gulong bytes_after;
1780   guchar *data;
1781 
1782   atom = gdk_atom_intern_static_string (name);
1783 
1784   if (!gdk_x11_screen_supports_net_wm_hint (screen, atom))
1785     return 0;
1786 
1787   XGetWindowProperty (x11_screen->xdisplay,
1788                       x11_screen->xroot_window,
1789                       gdk_x11_get_xatom_by_name_for_display (GDK_SCREEN_DISPLAY (screen), name),
1790                       0, G_MAXLONG,
1791                       False, XA_CARDINAL, &type, &format, &nitems,
1792                       &bytes_after, &data);
1793   if (type == XA_CARDINAL)
1794     {
1795       prop = *(gulong *)data;
1796       XFree (data);
1797     }
1798 
1799   return prop;
1800 }
1801 
1802 /**
1803  * gdk_x11_screen_get_number_of_desktops:
1804  * @screen: (type GdkX11Screen): a #GdkScreen
1805  *
1806  * Returns the number of workspaces for @screen when running under a
1807  * window manager that supports multiple workspaces, as described
1808  * in the
1809  * [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) specification.
1810  *
1811  * Returns: the number of workspaces, or 0 if workspaces are not supported
1812  *
1813  * Since: 3.10
1814  */
1815 guint32
gdk_x11_screen_get_number_of_desktops(GdkScreen * screen)1816 gdk_x11_screen_get_number_of_desktops (GdkScreen *screen)
1817 {
1818   return get_netwm_cardinal_property (screen, "_NET_NUMBER_OF_DESKTOPS");
1819 }
1820 
1821 /**
1822  * gdk_x11_screen_get_current_desktop:
1823  * @screen: (type GdkX11Screen): a #GdkScreen
1824  *
1825  * Returns the current workspace for @screen when running under a
1826  * window manager that supports multiple workspaces, as described
1827  * in the
1828  * [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) specification.
1829  *
1830  * Returns: the current workspace, or 0 if workspaces are not supported
1831  *
1832  * Since: 3.10
1833  */
1834 guint32
gdk_x11_screen_get_current_desktop(GdkScreen * screen)1835 gdk_x11_screen_get_current_desktop (GdkScreen *screen)
1836 {
1837   return get_netwm_cardinal_property (screen, "_NET_CURRENT_DESKTOP");
1838 }
1839