1 /*
2 Copyright (C) 2003 Tomas Styblo
3 From wmctrl:
4 A command line tool to interact with an EWMH/NetWM compatible X Window
5 Manager.
6
7 Copyright (C) 2004, 2005 Stephen Bach
8 This file is part of the Viewglob package.
9
10 Viewglob is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
14
15 Viewglob is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with Viewglob; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25 /* Modified from main.c in the wmctrl package. */
26
27
28 #include "common.h"
29 #include "x11-stuff.h"
30 #define MAX_PROPERTY_VALUE_LEN 4096
31
32 #if TIME_WITH_SYS_TIME
33 # include <sys/time.h>
34 # include <time.h>
35 #else
36 # if HAVE_SYS_TIME_H
37 # include <sys/time.h>
38 # else
39 # include <time.h>
40 # endif
41 #endif
42
43 #include <string.h>
44 #include <X11/Xlib.h>
45 #include <X11/Xatom.h>
46
47 #include <glib.h>
48
49 static Window *get_client_list(Display *disp, gulong *size);
50 static gchar* get_property(Display* disp, Window win, Atom xa_prop_type,
51 gchar* prop_name, gulong* size);
52 static gchar* get_window_title(Display* disp, Window win);
53 static gboolean client_msg(Display* disp, Window win, gchar* msg,
54 gulong data0, gulong data1, gulong data2, gulong data3, gulong data4);
55
refocus(Display * disp,Window w1,Window w2)56 void refocus(Display* disp, Window w1, Window w2) {
57 g_return_if_fail(disp != NULL);
58
59 if (w1 == 0 || w2 == 0)
60 return;
61
62 Window active_window;
63 gint dummy;
64 gint w1_desktop;
65 gint w2_desktop;
66
67 w1_desktop = get_desktop(disp, w1);
68 w2_desktop = get_desktop(disp, w2);
69
70 XGetInputFocus(disp, &active_window, &dummy);
71
72 /* Refocus the window which isn't focused. Or, if neither
73 are focused (?), focus both. */
74 if (active_window == w1)
75 focus_window(disp, w2, w1_desktop);
76 else if (active_window == w2)
77 focus_window(disp, w1, w2_desktop);
78 else {
79 focus_window(disp, w1, w2_desktop);
80 focus_window(disp, w2, w2_desktop);
81 }
82 }
83
84
get_active_window(Display * disp)85 Window get_active_window(Display* disp) {
86 g_return_val_if_fail(disp != NULL, 0);
87
88 /*
89 Window active_window;
90 gint dummy;
91
92 XGetInputFocus(disp, &active_window, &dummy);
93
94 return active_window;
95 */
96
97 gchar *prop;
98 gulong size;
99 Window ret = (Window)0;
100
101 prop = get_property(disp, DefaultRootWindow(disp), XA_WINDOW,
102 "_NET_ACTIVE_WINDOW", &size);
103 if (prop) {
104 ret = *((Window*)prop);
105 g_free(prop);
106 }
107 return(ret);
108 }
109
110
focus_window(Display * disp,Window win,gint desktop)111 void focus_window(Display* disp, Window win, gint desktop) {
112
113 if (window_to_desktop(disp, win, desktop)) {
114 /* 100 ms - make sure the WM has enough time to move the window,
115 before we activate it */
116 usleep(100000);
117 }
118
119 client_msg(disp, win, "_NET_ACTIVE_WINDOW", 0, 0, 0, 0, 0);
120 //XMapRaised(disp, win);
121 XRaiseWindow(disp, win);
122 }
123
124
125 /* Check to see if the given window is visible. */
is_visible(Display * disp,Window win)126 gboolean is_visible(Display* disp, Window win) {
127
128 if (win == 0)
129 return FALSE;
130
131 XWindowAttributes xwa;
132 if (!XGetWindowAttributes(disp, win, &xwa)) {
133 g_warning("Could not get X window attributes");
134 return FALSE;
135 }
136 else if (xwa.map_state != IsViewable)
137 return FALSE;
138
139 return TRUE;
140 }
141
142
get_desktop(Display * disp,Window win)143 gint get_desktop(Display* disp, Window win) {
144 gint desktop;
145 gulong* desktop_p;
146
147 /* desktop ID */
148 if (!(desktop_p = (gulong*)get_property(disp, win,
149 XA_CARDINAL, "_NET_WM_DESKTOP", NULL))) {
150 if (!(desktop_p = (gulong*)get_property(disp, win,
151 XA_CARDINAL, "_WIN_WORKSPACE", NULL))) {
152 g_warning("Cannot discover desktop. "
153 "(_NET_WM_DESKTOP or _WIN_WORKSPACE property)");
154 }
155 }
156
157 if (desktop_p)
158 desktop = *desktop_p;
159 else
160 desktop = -1;
161
162 g_free(desktop_p);
163 return desktop;
164 }
165
166
current_desktop(Display * disp)167 gint current_desktop(Display* disp) {
168 gint cur_desktop;
169 gulong* cur_desktop_p;
170
171 Window root = DefaultRootWindow(disp);
172
173 if (!(cur_desktop_p = (gulong*)get_property(disp, root,
174 XA_CARDINAL, "_NET_CURRENT_DESKTOP", NULL))) {
175 if (!(cur_desktop_p = (gulong*)get_property(disp, root,
176 XA_CARDINAL, "_WIN_WORKSPACE", NULL))) {
177 g_warning("Cannot get current desktop properties. "
178 "(_NET_CURRENT_DESKTOP or _WIN_WORKSPACE property)");
179 }
180 }
181
182 if (cur_desktop_p)
183 cur_desktop = *cur_desktop_p;
184 else
185 cur_desktop = -1;
186
187 g_free(cur_desktop_p);
188 return cur_desktop;
189 }
190
191
window_to_desktop(Display * disp,Window win,gint desktop)192 gboolean window_to_desktop(Display* disp, Window win, gint desktop) {
193
194 if (desktop == -1) {
195 if ( (desktop = current_desktop(disp)) == -1)
196 return FALSE;
197 }
198
199 return client_msg(disp, win, "_NET_WM_DESKTOP", (gulong)desktop,
200 0, 0, 0, 0);
201 }
202
203
client_msg(Display * disp,Window win,gchar * msg,gulong data0,gulong data1,gulong data2,gulong data3,gulong data4)204 static gboolean client_msg(Display* disp, Window win, gchar* msg,
205 gulong data0, gulong data1,
206 gulong data2, gulong data3,
207 gulong data4) {
208 XEvent event;
209 long mask = SubstructureRedirectMask | SubstructureNotifyMask;
210
211 event.xclient.type = ClientMessage;
212 event.xclient.serial = 0;
213 event.xclient.send_event = True;
214 event.xclient.message_type = XInternAtom(disp, msg, False);
215 event.xclient.window = win;
216 event.xclient.format = 32;
217 event.xclient.data.l[0] = data0;
218 event.xclient.data.l[1] = data1;
219 event.xclient.data.l[2] = data2;
220 event.xclient.data.l[3] = data3;
221 event.xclient.data.l[4] = data4;
222
223 if (XSendEvent(disp, DefaultRootWindow(disp), False, mask, &event))
224 return TRUE;
225 else {
226 g_warning("Cannot send %s event", msg);
227 return FALSE;
228 }
229 }
230
231
get_property(Display * disp,Window win,Atom xa_prop_type,gchar * prop_name,gulong * size)232 static gchar* get_property (Display* disp, Window win,
233 Atom xa_prop_type, gchar* prop_name, gulong* size) {
234 Atom xa_prop_name;
235 Atom xa_ret_type;
236 gint ret_format;
237 gulong ret_nitems;
238 gulong ret_bytes_after;
239 gulong tmp_size;
240 guchar* ret_prop;
241 gchar* ret;
242
243 xa_prop_name = XInternAtom(disp, prop_name, False);
244
245 /* MAX_PROPERTY_VALUE_LEN / 4 explanation (XGetWindowProperty manpage):
246
247 long_length = Specifies the length in 32-bit multiples of the
248 data to be retrieved.
249 */
250 if (XGetWindowProperty(disp, win, xa_prop_name, 0,
251 MAX_PROPERTY_VALUE_LEN / 4, False,
252 xa_prop_type, &xa_ret_type, &ret_format,
253 &ret_nitems, &ret_bytes_after, &ret_prop) != Success) {
254 g_warning("Cannot get %s property", prop_name);
255 return NULL;
256 }
257
258 if (xa_ret_type != xa_prop_type) {
259 g_warning("Invalid type of %s property", prop_name);
260 XFree(ret_prop);
261 return NULL;
262 }
263
264 /* null terminate the result to make string handling easier */
265 tmp_size = (ret_format / 8) * ret_nitems;
266 ret = g_malloc(tmp_size + 1);
267 memcpy(ret, ret_prop, tmp_size);
268 ret[tmp_size] = '\0';
269
270 if (size)
271 *size = tmp_size;
272
273 XFree(ret_prop);
274 return ret;
275 }
276
277
get_xid_from_title(Display * disp,gchar * title)278 Window get_xid_from_title(Display* disp, gchar* title) {
279 Window* client_list;
280 gulong client_list_size;
281 gint i, j;
282
283 if (!disp || !title)
284 return 0;
285
286 /* Wait for at most 3 seconds. */
287 for (i = 0; i < 30; i++) {
288
289 if ( (client_list = get_client_list(disp, &client_list_size)) == NULL)
290 return 0;
291
292 for (j = 0; j < client_list_size / sizeof(Window); j++) {
293 gchar* window_title = get_window_title(disp, client_list[j]);
294 /* title can appear anywhere in the window's title. */
295 if (window_title && strstr(window_title, title))
296 return client_list[j];
297 }
298
299 /* Check again in .1 seconds. */
300 usleep(100000);
301 }
302
303 return 0;
304 }
305
306
get_client_list(Display * disp,gulong * size)307 static Window* get_client_list (Display* disp, gulong* size) {
308 Window* client_list;
309
310 if ((client_list = (Window*)get_property(disp, DefaultRootWindow(disp),
311 XA_WINDOW, "_NET_CLIENT_LIST", size)) == NULL) {
312 if ((client_list = (Window*)get_property(disp, DefaultRootWindow(disp),
313 XA_CARDINAL, "_WIN_CLIENT_LIST", size)) == NULL) {
314 g_warning("Cannot get client list properties. \n"
315 "(_NET_CLIENT_LIST or _WIN_CLIENT_LIST)"
316 "\n");
317 return NULL;
318 }
319 }
320
321 return client_list;
322 }
323
324
get_window_title(Display * disp,Window win)325 static gchar* get_window_title(Display *disp, Window win) {
326 gchar* title_utf8;
327 gchar* wm_name;
328 gchar* net_wm_name;
329
330 wm_name = get_property(disp, win, XA_STRING, "WM_NAME", NULL);
331 net_wm_name = get_property(disp, win,
332 XInternAtom(disp, "UTF8_STRING", False), "_NET_WM_NAME", NULL);
333
334 if (net_wm_name)
335 title_utf8 = g_strdup(net_wm_name);
336 else if (wm_name)
337 title_utf8 = g_strdup(wm_name);
338 else
339 title_utf8 = NULL;
340
341 g_free(wm_name);
342 g_free(net_wm_name);
343
344 return title_utf8;
345 }
346
347
348 /* Converts win to a string (statically allocated memory) */
win_to_str(Window win)349 gchar* win_to_str(Window win) {
350
351 static GString* win_str = NULL;
352
353 if (!win_str)
354 win_str = g_string_new(NULL);
355
356 g_string_printf(win_str, "%lu", win);
357 return win_str->str;
358 }
359
360
str_to_win(gchar * string)361 Window str_to_win(gchar* string) {
362 g_return_val_if_fail(string != NULL, 0);
363
364 Window win;
365
366 win = strtoul(string, NULL, 10);
367
368 if (win == ULONG_MAX) {
369 g_warning("str_to_win(): conversion out of bounds: %s", string);
370 win = 0;
371 }
372
373 return win;
374 }
375
376