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