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