1 #include <X11/Xatom.h>
2 #include <gdk/gdkx.h>
3 #include <gdk/gdk.h>
4 #include <string.h>
5 #include <glib.h>
6 #include <glib-object.h>
7 #include "wm-common.h"
8
9 typedef struct _WMCallbackData
10 {
11 GFunc func;
12 gpointer data;
13 } WMCallbackData;
14
15 /* Our WM Window */
16 static Window wm_window = None;
17
18 /*
19 * These push/pop implementations are based on the GDK versions, except that they
20 * use only non-deprecated API.
21 */
22
23 static GPtrArray*
push_error_traps(void)24 push_error_traps (void)
25 {
26 GdkDisplayManager *manager;
27 g_autoptr(GPtrArray) trapped_displays = NULL;
28 g_autoptr(GSList) displays = NULL;
29 GSList *l;
30
31 manager = gdk_display_manager_get ();
32 displays = gdk_display_manager_list_displays (manager);
33 trapped_displays = g_ptr_array_new ();
34
35 for (l = displays; l != NULL; l = l->next)
36 {
37 GdkDisplay *display = l->data;
38
39 #ifdef GDK_WINDOWING_X11
40 if (GDK_IS_X11_DISPLAY (display))
41 {
42 gdk_x11_display_error_trap_push (display);
43 g_ptr_array_add (trapped_displays, display);
44 }
45 #endif
46 }
47
48 return g_steal_pointer (&trapped_displays);
49 }
50
51 static gint
pop_error_traps(GPtrArray * displays)52 pop_error_traps (GPtrArray *displays)
53 {
54 guint i;
55 gint result;
56
57 result = 0;
58
59 for (i = 0; displays && i < displays->len; i++)
60 {
61 GdkDisplay *display;
62 gint code = 0;
63
64 display = g_ptr_array_index (displays, i);
65
66 #ifdef GDK_WINDOWING_X11
67 code = gdk_x11_display_error_trap_pop (display);
68 #endif
69
70 if (code != 0)
71 result = code;
72 }
73
74 return result;
75 }
76
77 static char *
wm_common_get_window_manager_property(Atom atom)78 wm_common_get_window_manager_property (Atom atom)
79 {
80 g_autoptr(GPtrArray) trapped_displays = NULL;
81 Atom utf8_string, type;
82 int result;
83 char *retval;
84 int format;
85 gulong nitems;
86 gulong bytes_after;
87 gchar *val;
88
89 if (wm_window == None)
90 return NULL;
91
92 utf8_string = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "UTF8_STRING", False);
93
94 trapped_displays = push_error_traps ();
95
96 val = NULL;
97 result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
98 wm_window,
99 atom,
100 0, G_MAXLONG,
101 False, utf8_string,
102 &type, &format, &nitems,
103 &bytes_after, (guchar **) &val);
104
105 if (pop_error_traps (trapped_displays) ||
106 result != Success ||
107 type != utf8_string ||
108 format != 8 ||
109 nitems == 0 ||
110 !g_utf8_validate (val, nitems, NULL))
111 {
112 retval = NULL;
113 }
114 else
115 {
116 retval = g_strndup (val, nitems);
117 }
118
119 g_clear_pointer (&val, XFree);
120
121 return retval;
122 }
123 static gchar*
wm_common_get_current_window_manager(void)124 wm_common_get_current_window_manager (void)
125 {
126 Atom atom = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "_NET_WM_NAME", False);
127 char *result;
128
129 result = wm_common_get_window_manager_property (atom);
130 if (result)
131 return result;
132 else
133 return g_strdup (WM_COMMON_UNKNOWN);
134 }
135
136 GStrv
wm_common_get_current_keybindings(void)137 wm_common_get_current_keybindings (void)
138 {
139 g_autofree gchar* keybindings = NULL;
140 g_auto(GStrv) results = NULL;
141 Atom keybindings_atom;
142
143 keybindings_atom = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "_GNOME_WM_KEYBINDINGS", False);
144 keybindings = wm_common_get_window_manager_property (keybindings_atom);
145
146 if (keybindings)
147 {
148 GStrv p;
149
150 results = g_strsplit (keybindings, ",", -1);
151
152 for (p = results; p && *p; p++)
153 g_strstrip (*p);
154 }
155 else
156 {
157 g_autofree gchar *wm_name = NULL;
158 Atom wm_atom;
159 gchar *to_copy[2] = { NULL, NULL };
160
161 wm_atom = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "_NET_WM_NAME", False);
162 wm_name = wm_common_get_window_manager_property (wm_atom);
163
164 to_copy[0] = wm_name ? wm_name : WM_COMMON_UNKNOWN;
165
166 results = g_strdupv (to_copy);
167 }
168
169 return g_steal_pointer (&results);
170 }
171
172 static void
update_wm_window(void)173 update_wm_window (void)
174 {
175 g_autoptr(GPtrArray) trapped_displays = NULL;
176 Window *xwindow;
177 Atom type;
178 gint format;
179 gulong nitems;
180 gulong bytes_after;
181
182 XGetWindowProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), GDK_ROOT_WINDOW (),
183 XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "_NET_SUPPORTING_WM_CHECK", False),
184 0, G_MAXLONG, False, XA_WINDOW, &type, &format,
185 &nitems, &bytes_after, (guchar **) &xwindow);
186
187 if (type != XA_WINDOW)
188 {
189 wm_window = None;
190 return;
191 }
192
193 trapped_displays = push_error_traps ();
194
195 XSelectInput (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), *xwindow, StructureNotifyMask | PropertyChangeMask);
196 XSync (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), False);
197
198 if (pop_error_traps (trapped_displays))
199 {
200 XFree (xwindow);
201 wm_window = None;
202 return;
203 }
204
205 wm_window = *xwindow;
206 XFree (xwindow);
207 }
208
209 static GdkFilterReturn
wm_window_event_filter(GdkXEvent * xev,GdkEvent * event,gpointer data)210 wm_window_event_filter (GdkXEvent *xev,
211 GdkEvent *event,
212 gpointer data)
213 {
214 WMCallbackData *ncb_data = (WMCallbackData*) data;
215 XEvent *xevent = (XEvent *)xev;
216
217 if ((xevent->type == DestroyNotify &&
218 wm_window != None && xevent->xany.window == wm_window) ||
219 (xevent->type == PropertyNotify &&
220 xevent->xany.window == GDK_ROOT_WINDOW () &&
221 xevent->xproperty.atom == (XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "_NET_SUPPORTING_WM_CHECK", False))) ||
222 (xevent->type == PropertyNotify &&
223 wm_window != None && xevent->xany.window == wm_window &&
224 xevent->xproperty.atom == (XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "_NET_WM_NAME", False))))
225 {
226 update_wm_window ();
227 (* ncb_data->func) ((gpointer) wm_common_get_current_window_manager (), ncb_data->data);
228 }
229
230 return GDK_FILTER_CONTINUE;
231 }
232
233 gpointer
wm_common_register_window_manager_change(GFunc func,gpointer data)234 wm_common_register_window_manager_change (GFunc func,
235 gpointer data)
236 {
237 WMCallbackData *ncb_data;
238
239 ncb_data = g_new0 (WMCallbackData, 1);
240
241 ncb_data->func = func;
242 ncb_data->data = data;
243
244 gdk_window_add_filter (NULL, wm_window_event_filter, ncb_data);
245
246 update_wm_window ();
247
248 XSelectInput (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), GDK_ROOT_WINDOW (), PropertyChangeMask);
249 XSync (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), False);
250
251 return ncb_data;
252 }
253
254 void
wm_common_unregister_window_manager_change(gpointer id)255 wm_common_unregister_window_manager_change (gpointer id)
256 {
257 g_return_if_fail (id != NULL);
258
259 gdk_window_remove_filter (NULL, wm_window_event_filter, id);
260 g_free (id);
261 }
262