• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Add more stress to X server by moving, resizing and activating windows
3  * Author: Darrick Wong <djwong@us.ibm.com>
4  */
5 
6 /*
7  * Copyright (C) 2003-2006 IBM
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of the
12  * License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
22  * 02111-1307, USA.
23  */
24 
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <X11/Xlib.h>
32 #include <X11/Xatom.h>
33 #include <string.h>
34 #include <stdint.h>
35 #include <limits.h>
36 
37 static int enable_fullscreen = 0;
38 
39 #define MAX_PROPERTY_VALUE_LEN	4096
40 #define _NET_WM_STATE_TOGGLE	2
41 #define SLIDE_THRESHOLD 	32
42 
43 /* We assume that the workspace number will either be -1 or some
44  * huge number for "On All Workspaces" windows.  Presumably there
45  * aren't 1,000,000 workspaces, so that should be a safe number.
46  */
47 #define DESKTOP_MAX		1000000
48 
49 #define ACTION_MOVE_WINDOW		0
50 #define ACTION_ACTIVATE_WINDOW		1
51 #define ACTION_MAXIMIZE_WINDOW		2
52 #define ACTION_FULLSCREEN_WINDOW	3
53 #define ACTION_HIDDEN_WINDOW		4
54 #define ACTION_SLIDE_WINDOW_0		5
55 #define ACTION_SLIDE_WINDOW_1		6
56 #define ACTION_SLIDE_WINDOW_2		7
57 #define ACTION_SLIDE_WINDOW_3		8
58 #define ACTION_SLIDE_WINDOW_4		9
59 #define ACTION_SLIDE_WINDOW_5		10
60 #define ACTION_MIN		ACTION_MOVE_WINDOW
61 #define ACTION_MAX		ACTION_SLIDE_WINDOW_5
62 
63 /* The goal of this program:
64  * 0. Seed random number generator
65  * 1. Grab the list of windows and the desktop size.
66  * 2. Filter out the panel/desktop/whatever.  We're going to make
67  *    a cheesy assumption that a window on desktop -1 should be left
68  *    alone.  (Actually, the -1 denotes "all desktops")
69  * 3. For each window:
70  *    a. Figure out what we're going to do--activate, move/resize,
71  *       or maximize it.
72  *    b. If we're going to move/resize, grab 4 random numbers.
73  *    c. Actually perform the action.
74  * 4. Every so often, jump back to (2) in case there are new windows.
75  *    Maybe every 10,000 moves or so.
76  *
77  * Note that you do NOT want to run this on any X session you care about.
78  * It shouldn't take down X, but YMMV and in any case mad window resizing
79  * makes it hard to get work done.
80  */
81 static int seed_random(void);
82 static int get_desktop_size(Display * disp, unsigned long *w, unsigned long *h);
83 static char *get_property(Display * disp, Window win, Atom xa_prop_type,
84 			  char *prop_name, unsigned long *size,
85 			  unsigned long *items);
86 static void go_bonkers(Display * disp, unsigned long iterations,
87 		       unsigned long sleep);
88 static Window *get_interesting_windows(Display * disp,
89 				       unsigned long *num_windows);
90 static Window *get_client_list(Display * disp, unsigned long *size,
91 			       unsigned long *items);
92 static long get_randnum(long min, long max);
93 static int send_client_msg(Display * disp, Window win, char *msg,
94 			   unsigned long data0, unsigned long data1,
95 			   unsigned long data2, unsigned long data3,
96 			   unsigned long data4);
97 static int activate_window(Display * disp, Window * win);
98 static int wm_supports(Display * disp, const char *prop);
99 static void move_window(Display * disp, Window * win, unsigned long desk_w,
100 			unsigned long desk_h);
101 static int toggle_property(Display * disp, Window * win, const char *property);
102 static inline unsigned long clamp_value(unsigned long value,
103 					unsigned long min, unsigned long max);
104 static int ignore_xlib_error(Display * disp, XErrorEvent * xee);
105 
106 /* Actual functions begin here. */
107 
seed_random(void)108 static int seed_random(void)
109 {
110 	int fp;
111 	long seed;
112 
113 	fp = open("/dev/urandom", O_RDONLY);
114 	if (fp < 0) {
115 		perror("/dev/urandom");
116 		return 0;
117 	}
118 
119 	if (read(fp, &seed, sizeof(seed)) != sizeof(seed)) {
120 		perror("read random seed");
121 		return 0;
122 	}
123 
124 	close(fp);
125 	srand(seed);
126 
127 	return 1;
128 }
129 
get_desktop_size(Display * disp,unsigned long * w,unsigned long * h)130 static int get_desktop_size(Display * disp, unsigned long *w, unsigned long *h)
131 {
132 	*w = DisplayWidth(disp, 0);
133 	*h = DisplayHeight(disp, 0);
134 
135 	return 1;
136 }
137 
get_property(Display * disp,Window win,Atom xa_prop_type,char * prop_name,unsigned long * size,unsigned long * items)138 static char *get_property(Display * disp, Window win, Atom xa_prop_type,
139 			  char *prop_name, unsigned long *size,
140 			  unsigned long *items)
141 {
142 	Atom xa_prop_name;
143 	Atom xa_ret_type;
144 	int ret_format;
145 	unsigned long ret_nitems;
146 	unsigned long ret_bytes_after;
147 	unsigned long tmp_size;
148 	unsigned char *ret_prop;
149 	char *ret;
150 
151 	xa_prop_name = XInternAtom(disp, prop_name, False);
152 
153 	if (XGetWindowProperty
154 	    (disp, win, xa_prop_name, 0, MAX_PROPERTY_VALUE_LEN / 4, False,
155 	     xa_prop_type, &xa_ret_type, &ret_format, &ret_nitems,
156 	     &ret_bytes_after, &ret_prop) != Success) {
157 		fprintf(stderr, "Cannot get %s property.\n", prop_name);
158 		return NULL;
159 	}
160 
161 	if (xa_ret_type != xa_prop_type) {
162 		fprintf(stderr, "Invalid type of %s property.\n", prop_name);
163 		XFree(ret_prop);
164 		return NULL;
165 	}
166 
167 	/* XXX: EVIL HACK to get around a bug when sizeof(Window) is 8 yet ret_format
168 	 * is listed as 32bits and we're trying to get the client list.  Just double
169 	 * ret_format and proceed. */
170 	if (ret_format == 32 && strcmp(prop_name, "_NET_CLIENT_LIST") == 0 &&
171 	    sizeof(Window) == 8) {
172 		ret_format *= 2;
173 	}
174 
175 	/* null terminate the result to make string handling easier */
176 	tmp_size = (ret_format / 8) * ret_nitems;
177 	ret = calloc(tmp_size + 1, 1);
178 	if (!ret) {
179 		perror("get_property malloc failed");
180 		return NULL;
181 	}
182 	memcpy(ret, ret_prop, tmp_size);
183 	ret[tmp_size] = '\0';
184 
185 	if (size) {
186 		*size = ret_format / 8;
187 	}
188 	if (items) {
189 		*items = ret_nitems;
190 	}
191 
192 	XFree(ret_prop);
193 	return ret;
194 }
195 
get_randnum(long min,long max)196 static long get_randnum(long min, long max)
197 {
198 	return min + (long)((float)max * (rand() / (RAND_MAX + 1.0)));
199 }
200 
wm_supports(Display * disp,const char * prop)201 static int wm_supports(Display * disp, const char *prop)
202 {
203 	Atom xa_prop = XInternAtom(disp, prop, False);
204 	Atom *list;
205 	unsigned long size, items;
206 	int i;
207 
208 	if (!(list = (Atom *) get_property(disp, DefaultRootWindow(disp),
209 					   XA_ATOM, "_NET_SUPPORTED", &size,
210 					   &items))) {
211 		fprintf(stderr, "Cannot get _NET_SUPPORTED property.\n");
212 		return 0;
213 	}
214 
215 	size *= items;
216 
217 	for (i = 0; i < size / sizeof(Atom); i++) {
218 		if (list[i] == xa_prop) {
219 			free(list);
220 			return 1;
221 		}
222 	}
223 
224 	free(list);
225 	return 0;
226 }
227 
clamp_value(unsigned long value,unsigned long min,unsigned long max)228 static inline unsigned long clamp_value(unsigned long value,
229 					unsigned long min, unsigned long max)
230 {
231 	return (value < min ? min : (value > max ? max : value));
232 }
233 
ignore_xlib_error(Display * disp,XErrorEvent * xee)234 static int ignore_xlib_error(Display * disp, XErrorEvent * xee)
235 {
236 	char errbuf[256];
237 
238 	XGetErrorText(disp, xee->error_code, errbuf, 256);
239 	fprintf(stderr,
240 		"IGNORING Xlib error %d (%s) on request (%d.%d), sernum = %lu.\n",
241 		xee->error_code, errbuf, xee->request_code, xee->minor_code,
242 		xee->serial);
243 	return 1;
244 }
245 
slide_window(Display * disp,Window * win,unsigned long desk_w,unsigned long desk_h)246 static void slide_window(Display * disp, Window * win, unsigned long desk_w,
247 			 unsigned long desk_h)
248 {
249 	unsigned long x, y;
250 	unsigned long w, h;
251 	XWindowAttributes moo;
252 	Window junk;
253 
254 	if (XGetWindowAttributes(disp, *win, &moo) != 1) {
255 		fprintf(stderr, "Cannot get attributes of window 0x%lx.\n",
256 			*win);
257 		return;
258 	}
259 
260 	if (XTranslateCoordinates(disp, *win, moo.root,
261 				  -moo.border_width, -moo.border_width, &moo.x,
262 				  &moo.y, &junk) != 1) {
263 		fprintf(stderr,
264 			"Cannot translate coordinates of window 0x%lx.\n",
265 			*win);
266 		return;
267 	}
268 
269 	x = moo.x + get_randnum(-SLIDE_THRESHOLD, SLIDE_THRESHOLD);
270 	y = moo.y + get_randnum(-SLIDE_THRESHOLD, SLIDE_THRESHOLD);
271 	w = moo.width + get_randnum(-SLIDE_THRESHOLD, SLIDE_THRESHOLD);
272 	h = moo.height + get_randnum(-SLIDE_THRESHOLD, SLIDE_THRESHOLD);
273 
274 	x = clamp_value(x, 0, desk_w);
275 	y = clamp_value(y, 0, desk_h);
276 	w = clamp_value(w, 0, desk_w);
277 	h = clamp_value(h, 0, desk_h);
278 
279 	if (wm_supports(disp, "_NET_MOVERESIZE_WINDOW")) {
280 		send_client_msg(disp, *win, "_NET_MOVERESIZE_WINDOW",
281 				0, x, y, w, h);
282 	} else {
283 		XMoveResizeWindow(disp, *win, x, y, w, h);
284 	}
285 }
286 
move_window(Display * disp,Window * win,unsigned long desk_w,unsigned long desk_h)287 static void move_window(Display * disp, Window * win, unsigned long desk_w,
288 			unsigned long desk_h)
289 {
290 	unsigned long x, y, w, h;
291 
292 	x = get_randnum(0, desk_w);
293 	y = get_randnum(0, desk_h);
294 	w = get_randnum(150, desk_w);
295 	h = get_randnum(150, desk_h);
296 
297 	if (wm_supports(disp, "_NET_MOVERESIZE_WINDOW")) {
298 		send_client_msg(disp, *win, "_NET_MOVERESIZE_WINDOW",
299 				0, x, y, w, h);
300 	} else {
301 		XMoveResizeWindow(disp, *win, x, y, w, h);
302 	}
303 }
304 
toggle_property(Display * disp,Window * win,const char * property)305 static int toggle_property(Display * disp, Window * win, const char *property)
306 {
307 	Atom prop;
308 
309 	prop = XInternAtom(disp, property, False);
310 	return send_client_msg(disp, *win, "_NET_WM_STATE",
311 			       _NET_WM_STATE_TOGGLE, prop, 0, 0, 0);
312 }
313 
go_bonkers(Display * disp,unsigned long iterations,unsigned long sleep)314 static void go_bonkers(Display * disp, unsigned long iterations,
315 		       unsigned long sleep)
316 {
317 	unsigned long desk_w, desk_h;
318 	Window *windows, *window;
319 	unsigned long windows_length = 0, i;
320 
321 	if (!get_desktop_size(disp, &desk_w, &desk_h)) {
322 		fprintf(stderr, "WARNING: Assuming desktop to be 1024x768!\n");
323 		desk_w = 1024;
324 		desk_h = 768;
325 	}
326 	printf("Desktop is %lu by %lu.\n", desk_w, desk_h);
327 
328 	windows = get_interesting_windows(disp, &windows_length);
329 	if (!windows) {
330 		usleep(1000000);
331 		return;
332 	}
333 	printf("There are %lu interesting windows.\n", windows_length);
334 
335 	/* Bump up the iteration count so that all windows get
336 	 * some exercise. */
337 	iterations += iterations % windows_length;
338 
339 	for (i = 0; i < iterations; i++) {
340 		window = &windows[i % windows_length];
341 		switch (get_randnum(ACTION_MIN, ACTION_MAX)) {
342 		case ACTION_MOVE_WINDOW:
343 			move_window(disp, window, desk_w, desk_h);
344 			break;
345 		case ACTION_ACTIVATE_WINDOW:
346 			activate_window(disp, window);
347 			break;
348 		case ACTION_MAXIMIZE_WINDOW:
349 			toggle_property(disp, window,
350 					"_NET_WM_STATE_MAXIMIZED_VERT");
351 			toggle_property(disp, window,
352 					"_NET_WM_STATE_MAXIMIZED_HORZ");
353 			break;
354 		case ACTION_FULLSCREEN_WINDOW:
355 			if (!enable_fullscreen)
356 				break;
357 			toggle_property(disp, window,
358 					"_NET_WM_STATE_FULLSCREEN");
359 			break;
360 		case ACTION_HIDDEN_WINDOW:
361 			toggle_property(disp, window, "_NET_WM_STATE_HIDDEN");
362 			break;
363 		case ACTION_SLIDE_WINDOW_0:
364 		case ACTION_SLIDE_WINDOW_1:
365 		case ACTION_SLIDE_WINDOW_2:
366 		case ACTION_SLIDE_WINDOW_3:
367 		case ACTION_SLIDE_WINDOW_4:
368 		case ACTION_SLIDE_WINDOW_5:
369 			slide_window(disp, window, desk_w, desk_h);
370 			break;
371 		}
372 		usleep(sleep);
373 	}
374 
375 	free(windows);
376 }
377 
send_client_msg(Display * disp,Window win,char * msg,unsigned long data0,unsigned long data1,unsigned long data2,unsigned long data3,unsigned long data4)378 static int send_client_msg(Display * disp, Window win, char *msg,
379 			   unsigned long data0, unsigned long data1,
380 			   unsigned long data2, unsigned long data3,
381 			   unsigned long data4)
382 {
383 	XEvent event;
384 	long mask = SubstructureRedirectMask | SubstructureNotifyMask;
385 
386 	event.xclient.type = ClientMessage;
387 	event.xclient.serial = 0;
388 	event.xclient.send_event = True;
389 	event.xclient.message_type = XInternAtom(disp, msg, False);
390 	event.xclient.window = win;
391 	event.xclient.format = 32;
392 	event.xclient.data.l[0] = data0;
393 	event.xclient.data.l[1] = data1;
394 	event.xclient.data.l[2] = data2;
395 	event.xclient.data.l[3] = data3;
396 	event.xclient.data.l[4] = data4;
397 
398 	if (XSendEvent(disp, DefaultRootWindow(disp), False, mask, &event)) {
399 		return 1;
400 	} else {
401 		fprintf(stderr, "Cannot send %s event.\n", msg);
402 		return 0;
403 	}
404 }
405 
activate_window(Display * disp,Window * win)406 static int activate_window(Display * disp, Window * win)
407 {
408 	int ret;
409 
410 	ret = send_client_msg(disp, *win, "_NET_ACTIVE_WINDOW", 0, 0, 0, 0, 0);
411 	XMapRaised(disp, *win);
412 
413 	return ret;
414 }
415 
get_client_list(Display * disp,unsigned long * size,unsigned long * items)416 static Window *get_client_list(Display * disp, unsigned long *size,
417 			       unsigned long *items)
418 {
419 	void *res;
420 
421 	if ((res = (Window *) get_property(disp, DefaultRootWindow(disp),
422 					   XA_WINDOW, "_NET_CLIENT_LIST", size,
423 					   items)) == NULL) {
424 		if ((res =
425 		     (Window *) get_property(disp, DefaultRootWindow(disp),
426 					     XA_CARDINAL, "_WIN_CLIENT_LIST",
427 					     size, items)) == NULL) {
428 			fprintf(stderr,
429 				"Cannot get client list properties. \n"
430 				"(_NET_CLIENT_LIST or _WIN_CLIENT_LIST)" "\n");
431 			return NULL;
432 		}
433 	}
434 
435 	return (Window *) res;
436 }
437 
get_interesting_windows(Display * disp,unsigned long * num_windows)438 static Window *get_interesting_windows(Display * disp,
439 				       unsigned long *num_windows)
440 {
441 	Window *client_list, *ret, *tmp;
442 	unsigned long client_list_size, client_list_items, i;
443 	long *desktop;
444 	unsigned long num_needed = 0;
445 
446 	if ((client_list = get_client_list(disp, &client_list_size,
447 					   &client_list_items)) == NULL) {
448 		return NULL;
449 	}
450 
451 	/* Figure out how many Window structs we'll ultimately need. */
452 	for (i = 0; i < client_list_items; i++) {
453 		/* desktop ID */
454 		if ((desktop = (long *)get_property(disp, client_list[i],
455 						    XA_CARDINAL,
456 						    "_NET_WM_DESKTOP", NULL,
457 						    NULL)) == NULL) {
458 			desktop =
459 			    (long *)get_property(disp, client_list[i],
460 						 XA_CARDINAL, "_WIN_WORKSPACE",
461 						 NULL, NULL);
462 		}
463 
464 		/* Ignore windows on unknown desktops */
465 		if (desktop && *desktop >= 0 && *desktop < DESKTOP_MAX) {
466 			num_needed++;
467 			free(desktop);
468 		}
469 	}
470 
471 	ret = calloc(num_needed, sizeof(Window));
472 	if (!ret) {
473 		perror("get_interesting_window allocations");
474 		free(client_list);
475 		return NULL;
476 	}
477 	tmp = ret;
478 
479 	/* Now copy all that crud. */
480 	for (i = 0; i < client_list_items; i++) {
481 		/* desktop ID */
482 		if ((desktop = (long *)get_property(disp, client_list[i],
483 						    XA_CARDINAL,
484 						    "_NET_WM_DESKTOP", NULL,
485 						    NULL)) == NULL) {
486 			desktop =
487 			    (long *)get_property(disp, client_list[i],
488 						 XA_CARDINAL, "_WIN_WORKSPACE",
489 						 NULL, NULL);
490 		}
491 
492 		if (desktop && *desktop >= 0 && *desktop < DESKTOP_MAX) {
493 			memcpy(tmp, &client_list[i], sizeof(Window));
494 			tmp++;
495 			free(desktop);
496 		}
497 	}
498 	free(client_list);
499 
500 	*num_windows = num_needed;
501 	return ret;
502 }
503 
main(int argc,char * argv[])504 int main(int argc, char *argv[])
505 {
506 	char *disp_string = NULL;
507 	unsigned long iterations = 10000, rounds = -1, i;
508 	unsigned long sleep = 100000;
509 	int opt;
510 	Display *disp;
511 
512 	while ((opt = getopt(argc, argv, "d:i:r:s:f")) != -1) {
513 		switch (opt) {
514 		case 'd':
515 			disp_string = optarg;
516 			break;
517 		case 'i':
518 			iterations = atoi(optarg);
519 			break;
520 		case 'r':
521 			rounds = atoi(optarg);
522 			break;
523 		case 's':
524 			sleep = atoi(optarg);
525 			break;
526 		case 'f':
527 			enable_fullscreen = 1;
528 			break;
529 		default:
530 			fprintf(stderr,
531 				"Usage: %s [-d DISPLAY] [-i ITERATIONS] [-r ROUNDS] [-s SLEEP] [-f]\n\
532 	DISPLAY is an X11 display string.\n\
533 	ITERATIONS is the approximate number of windows to play with before generating a new window list.\n\
534 	SLEEP is the amount of time (in usec) to sleep between window tweaks.\n\
535 	-f enables fullscreen toggling.\n\
536 	ROUNDS is the number of iterations to run, or -1 to run forever.\n",
537 				argv[0]);
538 			return 0;
539 		}
540 	}
541 
542 	if (!(disp = XOpenDisplay(disp_string))) {
543 		fprintf(stderr, "Unable to connect to display '%s'.\n",
544 			(disp_string !=
545 			 NULL ? disp_string : getenv("DISPLAY")));
546 		return 1;
547 	}
548 
549 	seed_random();
550 
551 	XSetErrorHandler(&ignore_xlib_error);
552 
553 	for (i = 0; i < rounds || rounds == -1; i++) {
554 		go_bonkers(disp, iterations, sleep);
555 	}
556 
557 	printf("Enough of that; I'm done.\n");
558 
559 	XCloseDisplay(disp);
560 
561 	return 0;
562 }
563