2
winspew - a EWMH testing tool.
4
Copyright 2003 Matthew Allum
6
This program is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2, or (at your option)
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
GNU General Public License for more details.
20
#include <X11/Xutil.h>
21
#include <X11/Xatom.h>
30
#define WIN_OPTS_NO_TITLE (1<<1)
31
#define WIN_OPTS_STATE_FULLSCREEN (1<<2)
32
#define WIN_OPTS_STATE_MODAL (1<<3)
33
#define WIN_OPTS_WM_PROTO_ACCEPT (1<<5)
34
#define WIN_OPTS_WM_PROTO_HELP (1<<6)
36
#define WIN_OPTS_UTF8_TITLE (1<<7)
37
#define WIN_OPTS_NO_DECOR (1<<8)
39
enum { /* Window Atoms */
41
WINDOW_TYPE_TOOLBAR = 1,
49
WINDOW_STATE_FULLSCREEN,
65
_NET_WM_CONTEXT_ACCEPT,
68
WINDOW_TYPE_MESSAGE_TIMEOUT,
77
static void paint(Window win, int width, int height);
80
static Window win_top_level[MAX_WINS], win_child[MAX_WINS],
81
win_current_top_level, win_root;
82
int win_top_level_idx = 0, win_child_idx = 0;
84
static Atom atoms[ATOM_COUNT];
85
static Bool WinIsFullscreen = False;
88
char *key; int flag; char *desc;
89
} Options_lookup[] = {
90
{ "no_title", WIN_OPTS_NO_TITLE,
91
"Request no titlebar / border only\n"},
92
{ "no_decor", WIN_OPTS_NO_DECOR,
93
"Request no window decorations\n"},
94
{ "fullscreen", WIN_OPTS_STATE_FULLSCREEN ,
95
"Request Fullscreen\n"},
96
{ "modal", WIN_OPTS_STATE_MODAL,
97
"Request dialog be modal\n"},
98
{ "help_button", WIN_OPTS_WM_PROTO_HELP,
99
"Request help button in window title bar\n"},
100
{ "accept_button", WIN_OPTS_WM_PROTO_ACCEPT,
101
"Request 'OK' button in window title bar\n"},
106
char *key; unsigned char *data; int len;
107
} UTF8_Names_lookup[] = {
108
{ "utf8-zn_CH", "\344\275\240\345\245\275\344\270\255\345\233\275!", 13 },
113
wm_check_supported_features(void)
116
XXX This functioniality here could be greatly enhances, though
117
Im not sure how useful it would be.
119
Atom type, *atoms_supported = NULL;
124
unsigned char *atom_str_name;
126
Bool have_help_support = False, have_accept_support = False;
128
result = XGetWindowProperty (dpy, win_root, atoms[_NET_SUPPORTED],
131
&type, &format, &n_items,
133
(unsigned char **)&atoms_supported);
135
printf("winspew log: Checking wm features..\n");
137
if (result != Success || atoms_supported == NULL)
139
if (atoms_supported) XFree (atoms_supported);
140
printf("winspew log: *WARNING* looks like wm is not EWMH compliant\n"
141
" Many features _will_not_ work!");
145
for (i=0; i<n_items; i++)
147
atom_str_name = XGetAtomName(dpy, atoms_supported[i]);
148
printf("\t %s supported\n", atom_str_name);
149
if (atoms_supported[i] == atoms[_NET_WM_CONTEXT_HELP])
150
have_help_support = True;
151
if (atoms_supported[i] == atoms[_NET_WM_CONTEXT_ACCEPT])
152
have_accept_support = True;
153
if (atom_str_name) XFree(atom_str_name);
156
if (!have_help_support)
157
printf("winspew log: *WARNING* This wm _does_not_ support help buttons\n");
159
if (!have_accept_support)
160
printf("winspew log: *WARNING* This wm _does_not_ support accpet buttons\n");
162
XFree (atoms_supported);
169
atoms[WINDOW_TYPE_TOOLBAR]
170
= XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR",False);
171
atoms[WINDOW_TYPE_DOCK]
172
= XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK",False);
173
atoms[WINDOW_TYPE_DIALOG]
174
= XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG",False);
175
atoms[WINDOW_TYPE_SPLASH]
176
= XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_SPLASH",False);
177
atoms[WINDOW_TYPE_DESKTOP]
178
= XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DESKTOP",False);
180
= XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
184
= XInternAtom(dpy, "_NET_WM_STATE",False);
185
atoms[WINDOW_STATE_FULLSCREEN]
186
= XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN",False);
187
atoms[WINDOW_STATE_MODAL]
188
= XInternAtom(dpy, "_NET_WM_STATE_MODAL",False);
192
= XInternAtom(dpy, "_NET_WM_NAME",False);
194
= XInternAtom(dpy, "UTF8_STRING",False);
196
atoms[_NET_CLOSE_WINDOW]
197
= XInternAtom(dpy, "_NET_CLOSE_WINDOW",False);
198
atoms[_NET_SHOW_DESKTOP]
199
= XInternAtom(dpy, "_NET_SHOWING_DESKTOP",False);
201
= XInternAtom(dpy, "_NET_WM_ICON", False);
204
atoms[_MOTIF_WM_HINTS]
205
= XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
208
= XInternAtom(dpy, "WM_PROTOCOLS", False);
209
atoms[WM_DELETE_WINDOW]
210
= XInternAtom(dpy, "WM_DELETE_WINDOW", False);
211
atoms[_NET_WM_CONTEXT_HELP]
212
= XInternAtom(dpy, "_NET_WM_CONTEXT_HELP", False);
213
atoms[_NET_WM_CONTEXT_ACCEPT]
214
= XInternAtom(dpy, "_NET_WM_CONTEXT_ACCEPT", False);
216
atoms[_NET_SUPPORTED]
217
= XInternAtom(dpy, "_NET_SUPPORTED", False);
219
atoms[WINDOW_TYPE_MESSAGE]
220
= XInternAtom(dpy, "_MB_WM_WINDOW_TYPE_MESSAGE", False);
222
atoms[WINDOW_TYPE_MESSAGE_TIMEOUT]
223
= XInternAtom(dpy, "_MB_WM_WINDOW_TYPE_MESSAGE_TIMEOUT", False);
229
usage(char *bin_name, char *error)
233
bin_name = "winspew";
235
printf("\n %s usage:\n"
236
"%s [ -display <X11 display> ] -top <options> [ -dialog <options> ] ...\n\n"
237
"-top, ( -t ) creates a top level window, <options> should consist of;\n\n"
238
" <title>,<window type>[,[geom],[extra opts], [message window timeout]]\n\n"
239
"-dialog, ( -c) creates a dialog for the previous top level, <options> are;\n\n"
240
" <title>[,[geom],[extra opts],]\n\n"
242
" 'window type' is one of normal, dock, toolbar, dialog, splash, desktop, message\n"
244
" 'geom' is a X11 window geometry definition, like 100x100+100+100\n"
245
" 'message window timeout' is the time ( seconds ) a message win is active\n"
246
" 'extra opts' is a ':' seperated list of the following flags;\n\n",
249
while( Options_lookup[i].key != NULL )
251
printf(" %s - %s", Options_lookup[i].key, Options_lookup[i].desc);
255
printf("\nNote, you may use as many instances of -top and -dialog as you like.\n");
258
printf("\nError: %s\n", error);
264
set_motif_no_title_decoration_hint(Window win, Bool border_only)
267
#define PROP_MOTIF_WM_HINTS_ELEMENTS 5
268
#define MWM_HINTS_DECORATIONS (1L << 1)
269
#define MWM_DECOR_BORDER (1L << 1)
274
unsigned long functions;
275
unsigned long decorations;
277
unsigned long status;
280
PropMotifWmHints *hints;
282
hints = malloc(sizeof(PropMotifWmHints));
283
memset(hints, 0, sizeof(PropMotifWmHints));
285
hints->flags = MWM_HINTS_DECORATIONS;
288
hints->decorations = MWM_DECOR_BORDER;
290
hints->decorations = 0;
292
XChangeProperty(dpy, win, atoms[_MOTIF_WM_HINTS],
293
XA_ATOM, 32, PropModeReplace,
294
(unsigned char *)hints, PROP_MOTIF_WM_HINTS_ELEMENTS);
304
parse_options(const char *options_spec)
307
int i = 0, result_flags = 0;
309
printf("parsing %s\n", options_spec);
311
while( Options_lookup[i].key != NULL )
313
if (strstr(options_spec, Options_lookup[i].key) != NULL)
315
printf("parsed %s\n", Options_lookup[i].key);
316
result_flags |= Options_lookup[i].flag;
325
win_set_utf8_name(Window win,
329
XChangeProperty(dpy, win, atoms[_NET_WM_NAME],
330
atoms[UTF8_STRING], 8, PropModeReplace,
335
win_set_standard_props(Window win, char *win_title, int x, int y, int h, int w)
337
XSizeHints size_hints;
339
size_hints.flags = PPosition | PSize | PMinSize;
342
size_hints.width = h;
343
size_hints.height = w;
344
size_hints.min_width = w;
345
size_hints.min_height = h;
347
XSetStandardProperties(dpy, win,
348
win_title, win_title, None,
349
NULL, 0, &size_hints);
353
win_set_ewmh_type(Window win, Atom atom_type)
355
printf("winspew log: Setting type\n");
356
XChangeProperty(dpy, win, atoms[WINDOW_TYPE], XA_ATOM, 32,
357
PropModeReplace, (unsigned char *) &atom_type, 1);
361
win_set_extened_options(Window win, int option_flags, int message_timeout)
363
Atom wm_protocols[3];
364
int wm_protocols_idx = 1;
366
wm_protocols[0] = atoms[WM_DELETE_WINDOW];
368
if (option_flags & WIN_OPTS_NO_TITLE)
370
printf("winspew log: Setting no decor hint\n");
371
set_motif_no_title_decoration_hint(win, True);
374
if (option_flags & WIN_OPTS_NO_DECOR)
376
printf("winspew log: Setting no decor hint\n");
377
set_motif_no_title_decoration_hint(win, False);
381
if (option_flags & WIN_OPTS_STATE_FULLSCREEN)
383
printf("winspew log: Setting fullscreen hint\n");
384
XChangeProperty(dpy, win,
385
atoms[WINDOW_STATE], XA_ATOM, 32,
387
(unsigned char *) &atoms[WINDOW_STATE_FULLSCREEN], 1);
388
WinIsFullscreen = True;
391
if (option_flags & WIN_OPTS_STATE_MODAL)
393
printf("winspew log: Setting modal hint\n");
394
XChangeProperty(dpy, win,
395
atoms[WINDOW_STATE], XA_ATOM, 32,
397
(unsigned char *) &atoms[WINDOW_STATE_MODAL], 1);
404
printf("winspew log: Setting message timeout\n");
406
data[0] = message_timeout;
407
XChangeProperty(dpy, win,
408
atoms[WINDOW_TYPE_MESSAGE_TIMEOUT], XA_CARDINAL, 32,
410
(unsigned char *) &data[0], 1);
413
if (option_flags & WIN_OPTS_WM_PROTO_ACCEPT)
415
printf("winspew log: Setting accept protocol\n");
416
wm_protocols[wm_protocols_idx] = atoms[_NET_WM_CONTEXT_ACCEPT];
420
if (option_flags & WIN_OPTS_WM_PROTO_HELP)
422
printf("winspew log: Setting help protocol\n");
423
wm_protocols[wm_protocols_idx] = atoms[_NET_WM_CONTEXT_HELP];
427
if (wm_protocols_idx)
428
XSetWMProtocols(dpy, win, wm_protocols, wm_protocols_idx );
433
win_realize(Window win)
435
XSelectInput(dpy, win,
443
|PointerMotionHintMask
452
|VisibilityChangeMask
454
|SubstructureNotifyMask
459
); /* XXX really need all this ? */
461
XMapWindow(dpy, win);
465
win_destroy(Window win)
470
for (i=0; i<win_child_idx; i++) /* destroy any transient dialogs */
472
if (win_child[i] != None
473
&& XGetTransientForHint(dpy, win_child[i], &win_tmp)
476
XDestroyWindow(dpy, win_child[i]);
481
XDestroyWindow(dpy,win);
483
/* Update window arrays */
485
for (i=0; i<win_top_level_idx; i++)
486
if (win_top_level[i] == win)
488
win_top_level[i] = None;
492
for (i=0; i<win_child_idx; i++)
493
if (win_child[i] == win)
499
create_top_level(int argc, char **argv, char *spec)
502
const char delim[] = ",";
507
int win_type = 0, win_x = 0, win_y = 0, win_w = 100, win_h = 50;
509
int option_flags = 0;
510
int message_timeout = 0;
511
int geom_bitmask = 0;
518
{"dock", WINDOW_TYPE_DOCK},
519
{"dialog", WINDOW_TYPE_DIALOG},
520
{"splash", WINDOW_TYPE_SPLASH},
521
{"toolbar", WINDOW_TYPE_TOOLBAR},
522
{"desktop", WINDOW_TYPE_DESKTOP},
523
{"message", WINDOW_TYPE_MESSAGE},
527
if ((strchr(spec, delim[0]) != NULL))
531
if ((win_title = strsep (&str, delim)) == NULL)
532
usage(NULL, "top title missing");
534
if ((tmp = strsep (&str, delim)) == NULL)
535
usage(NULL, "top type missing");
538
win_type = atoi(tmp);
543
while(win_types[i].name != NULL)
545
if (!strcmp(tmp, win_types[i].name))
547
win_type = win_types[i].id;
555
/* Rest are optional */
557
if ((tmp = strsep (&str, delim)) != NULL)
559
geom_bitmask = XParseGeometry(tmp, &win_x, &win_y, &win_w, &win_h);
561
if (!win_w) win_w = 100;
562
if (!win_h) win_h = 100;
564
/* XXX see code for dialogs
565
if (!(geom_bitmask & XNegative))
567
win_x = (-1 * DisplayWidth(dpy, screen)) + win_x;
570
if (!(geom_bitmask & YNegative))
572
win_y = (-1 * DisplayHeight(dpy, screen)) + win_y;
577
if ((tmp = strsep (&str, delim)) != NULL)
579
option_flags = parse_options(tmp);
582
if ((tmp = strsep (&str, delim)) != NULL)
584
message_timeout = atoi(tmp);
589
/* Now create actual window */
591
printf("winspew log: creating top level x:%i, y:%i, w:%i, h:%i\n",
592
win_x, win_y, win_w, win_h);
594
win_top_level[win_top_level_idx]
595
= XCreateSimpleWindow(dpy, win_root, win_x, win_y,
597
BlackPixel(dpy, screen),
598
WhitePixel(dpy, screen));
600
printf("winspew log: window id is %li\n",
601
win_top_level[win_top_level_idx]);
603
win_current_top_level = win_top_level[win_top_level_idx];
605
win_set_standard_props(win_top_level[win_top_level_idx], win_title,
606
win_x, win_y, win_h, win_w);
608
if (win_type) /* something other than a normal window */
609
win_set_ewmh_type(win_top_level[win_top_level_idx], atoms[win_type]);
611
win_set_extened_options(win_top_level[win_top_level_idx],
612
option_flags, message_timeout);
614
/* Set utf8 name from lookup */
616
while( UTF8_Names_lookup[i].key != NULL )
618
if (!strcmp(win_title, UTF8_Names_lookup[i].key))
620
win_set_utf8_name(win_top_level[win_top_level_idx],
621
UTF8_Names_lookup[i].data,
622
UTF8_Names_lookup[i].len);
627
win_realize(win_top_level[win_top_level_idx]);
634
usage("winspew", "top options missing");
638
void /* XXX this should be merged into above */
639
create_child_dialog(int argc, char **argv, char *spec)
642
const char delim[] = ",";
646
int win_x = 0, win_y = 0, win_w = 100, win_h = 50;
647
int option_flags = 0, geom_bitmask = 0;
649
if (spec == NULL) usage("winspew", "child options missing");
651
if ((strchr(spec, delim[0]) != NULL))
655
if ((win_title = strsep (&str, delim)) == NULL)
656
usage(NULL, "child title missing");
658
/* Rest are optional */
660
if ((tmp = strsep (&str, delim)) != NULL)
662
geom_bitmask = XParseGeometry(tmp, &win_x, &win_y, &win_w, &win_h);
664
if (!win_w) win_w = 100;
665
if (!win_h) win_h = 100;
667
/* XXX we need to do gravitys for the below to work correctly.
668
if (!(geom_bitmask & XNegative))
670
win_x = (-1 * DisplayWidth(dpy, screen)) - win_x - win_w;
673
if (!(geom_bitmask & YNegative))
675
win_y = (-1 * DisplayHeight(dpy, screen)) - win_y - win_h;
680
if ((tmp = strsep (&str, delim)) != NULL)
682
option_flags = parse_options(tmp);
687
else win_title = spec;
689
/* Now create actual window */
691
printf("winspew log: creating dialog x:%i, y:%i, w:%i, h:%i\n",
692
win_x, win_y, win_w, win_h);
694
win_child[win_child_idx]
695
= XCreateSimpleWindow(dpy, win_root, win_x, win_y,
697
BlackPixel(dpy, screen),
698
WhitePixel(dpy, screen));
700
printf("winspew log: window id is %li\n",
701
win_child[win_child_idx]);
703
XSetTransientForHint(dpy, win_child[win_child_idx],
704
win_current_top_level);
706
win_set_standard_props(win_child[win_child_idx], win_title,
707
win_x, win_y, win_h, win_w);
709
win_set_extened_options(win_child[win_child_idx], option_flags, 0);
711
win_realize(win_child[win_child_idx]);
717
static void /* Fill the window with a simple pattern */
718
paint(Window win, int width, int height)
720
XWindowAttributes win_attr;
726
XGetWindowAttributes(dpy, win, &win_attr);
728
gc_values.graphics_exposures = False;
729
gc_values.function = GXcopy;
730
gc_values.background = WhitePixel(dpy, DefaultScreen(dpy));
731
gc = XCreateGC(dpy, win, GCGraphicsExposures|GCFunction|GCForeground,
734
pxm_backing = XCreatePixmap(dpy, win, width, height,
737
XSetForeground(dpy, gc, WhitePixel(dpy, DefaultScreen(dpy)));
738
XFillRectangle(dpy, pxm_backing, gc, 0, 0, width, height);
740
XSetForeground(dpy, gc, BlackPixel(dpy, DefaultScreen(dpy)));
742
/* Quick hack for a window pattern, using window ID */
745
for (y = 0; y < height; y += win % 12)
746
XDrawLine(dpy, pxm_backing, gc, 0, y, width, y);
748
for (x = 0; x < width; x += win % 12)
749
XDrawLine(dpy, pxm_backing, gc, x, 0, x, height);
752
XSetWindowBackgroundPixmap(dpy, win, pxm_backing);
753
XClearWindow(dpy, win);
755
XFreePixmap(dpy, pxm_backing);
760
toggle_ewmh_fullscreen (Window win_to_toggle)
763
#define _NET_WM_STATE_TOGGLE 2 /* toggle property */
766
Atom atom_win_state, atom_win_state_fullscreen;
768
atom_win_state = XInternAtom(dpy, "_NET_WM_STATE",False);
769
atom_win_state_fullscreen = XInternAtom(dpy,
770
"_NET_WM_STATE_FULLSCREEN",False);
772
printf("winspew-log: attempting toggle on %li\n", win_to_toggle);
774
memset(&ev, 0, sizeof(ev));
775
ev.xclient.type = ClientMessage;
776
ev.xclient.window = win_to_toggle;
777
ev.xclient.message_type = atom_win_state;
778
ev.xclient.format = 32;
779
ev.xclient.data.l[1] = atom_win_state_fullscreen;
780
ev.xclient.data.l[0] = _NET_WM_STATE_TOGGLE;
782
XSendEvent(dpy, win_root, False, SubstructureRedirectMask , &ev);
789
main(int argc, char **argv)
792
char *dpy_name = NULL;
795
for (i = 1; i < argc; i++) {
796
if (!strcmp ("-display", argv[i]) || !strcmp ("-d", argv[i])) {
797
if (++i>=argc) usage (argv[0], "display missing");
798
dpy_name = argv[i++];
804
usage(argv[0], "no options");
806
/* no display option */
811
if ((dpy = XOpenDisplay(dpy_name)) == NULL) {
812
fprintf(stderr, "Cannot connect to X server on display %s.",
817
screen = DefaultScreen(dpy);
818
win_root = DefaultRootWindow(dpy);
822
wm_check_supported_features();
824
win_current_top_level = win_root;
826
for (; i < argc; i++) {
827
if (!strcmp ("-top", argv[i]) || !strcmp ("-t", argv[i])) {
828
if (++i>=argc) usage (argv[0], "no -top options");
829
create_top_level(argc, argv, argv[i]);
832
if (!strcmp ("-dialog", argv[i]) || !strcmp ("-c", argv[i])) {
833
if (++i>=argc) usage (argv[0], "no -child options");
834
create_child_dialog(argc, argv, argv[i]);
837
usage(argv[0], argv[i]);
844
XNextEvent(dpy, &xevent);
848
printf("winspew log: %li, Button press event\n",
849
xevent.xbutton.window);
851
toggle_ewmh_fullscreen (xevent.xbutton.window);
854
printf("winspew log: %li, Expose event\n",
855
xevent.xexpose.window);
857
paint(xevent.xexpose.window, xevent.xexpose.width,
858
xevent.xexpose.height);
862
printf("winspew log: %li, MapNotfiy event\n",
866
printf("winspew log: %li, UnmapNotfiy event\n",
867
xevent.xunmap.window);
870
printf("winspew log: %li, ReparentNotify\n",
871
xevent.xreparent.window);
874
case ConfigureNotify:
875
printf("winspew log: %li, ConfigureNotify event\n",
876
xevent.xconfigure.window);
877
/* XXX compress configure notifys ? */
878
paint(xevent.xconfigure.window, xevent.xconfigure.width,
879
xevent.xconfigure.height);
882
printf("winspew log: %li, ClientMessage event\n",
883
xevent.xclient.window);
884
if ((xevent.xclient.message_type == atoms[WM_PROTOCOLS])
885
&& (xevent.xclient.data.l[0] == atoms[WM_DELETE_WINDOW]))
887
printf("\tis WM_DELETE, deleteing...\n");
888
win_destroy(xevent.xclient.window);
894
printf("winspew log: %li, FocusIn event\n",
895
xevent.xfocus.window);
898
printf("winspew log: %li, FocusOut event\n",
899
xevent.xfocus.window);
902
tmp = XGetAtomName(dpy, xevent.xproperty.atom);
903
printf("winspew log: %li, PropertyNotify event. Atom '%s' ( ID: %li )\n",
904
xevent.xproperty.window, tmp, xevent.xproperty.atom);