1 /*
2 * propwatch.c -- (c) 1997-2003 Gerd Knorr <kraxel@bytesex.org>
3 *
4 * A tool to monitor window properties of root and application windows.
5 * Nice for debugging property-based IPC of X11 programs.
6 *
7 * see also:
8 * xhost(1), xauth(1), xprop(1), xwd(1)
9 *
10 */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <string.h>
16 #include <sys/time.h>
17 #include <sys/types.h>
18
19 #include <X11/Xlib.h>
20 #include <X11/Xatom.h>
21 #include <X11/Intrinsic.h>
22 #include <X11/StringDefs.h>
23 #include <X11/Shell.h>
24 #include <X11/cursorfont.h>
25 #include <X11/Xaw/XawInit.h>
26 #include <X11/Xaw/Label.h>
27 #include <X11/Xaw/List.h>
28 #include <X11/Xaw/Viewport.h>
29
30 #ifndef TRUE
31 #define TRUE 1
32 #define FALSE 0
33 #endif
34
35 /*-------------------------------------------------------------------------*/
36
37 struct WATCHLIST {
38 Window win;
39 int watch;
40 struct WATCHLIST *next;
41 char *text;
42 };
43
44 /* WM */
45 static Atom wm_del_win;
46 static Atom wm_class;
47
48 /*-------------------------------------------------------------------------*/
49
50 static Widget bl,vp;
51 static struct WATCHLIST *watchlist = NULL;
52 static char **watch_name;
53 static Atom *watch_atom;
54 static int watch_count;
55
56 static char *watch_default[] = {
57 "WM_COMMAND",
58 };
59
60 static String *str_list;
61 static int str_count;
62
63 static void AddWatch(Display *dpy, Window win, int i);
64 static void DeleteWatch(Window win);
65 static void CheckWindow(Display *dpy, Window win);
66 static void Update(Display *dpy, Window win, Atom prop);
67
68 /*-------------------------------------------------------------------------*/
69
70 struct ARGS {
71 char *watch;
72 int verbose;
73 int proplog;
74 int kbdlog;
75 } args;
76
77 XtResource args_desc[] = {
78 /* name, class, type, size, offset, default_type, default_addr */
79 {
80 /* ----- Strings ----- */
81 "watch",
82 XtCString, XtRString, sizeof(char*),
83 XtOffset(struct ARGS*,watch),
84 XtRString, NULL,
85 },{
86 /* ----- Integer ----- */
87 "verbose",
88 XtCValue, XtRInt, sizeof(int),
89 XtOffset(struct ARGS*,verbose),
90 XtRString, ""
91 },{
92 "proplog",
93 XtCValue, XtRInt, sizeof(int),
94 XtOffset(struct ARGS*,proplog),
95 XtRString, ""
96 },{
97 "kbdlog",
98 XtCValue, XtRInt, sizeof(int),
99 XtOffset(struct ARGS*,kbdlog),
100 XtRString, ""
101 }
102 };
103 const int args_count = XtNumber(args_desc);
104
105 XrmOptionDescRec opt_desc[] = {
106 { "-watch", "watch", XrmoptionSepArg, NULL },
107 { "-verbose", "verbose", XrmoptionNoArg, "1" },
108 { "-proplog", "proplog", XrmoptionNoArg, "1" },
109 { "-kbdlog", "kbdlog", XrmoptionNoArg, "1" },
110 };
111 const int opt_count = (sizeof(opt_desc)/sizeof(XrmOptionDescRec));
112
113 /*-------------------------------------------------------------------------*/
114
115 XtAppContext app_context;
116 Widget app_shell;
117 Cursor left_ptr;
118 Cursor menu_ptr;
119
120 static void QuitAction(Widget, XEvent*, String*, Cardinal*);
121 static void HookAction(Widget, XEvent*, String*, Cardinal*);
122
123 static void ProcessPropertyChange(Display*,XEvent*);
124 static void ProcessKeyPress(Display*,XEvent*);
125 static void ProcessClientMessage(Display*,XEvent*);
126 static void ProcessCreateWindow(Display*,XEvent*);
127 static void ProcessEvent(Display *dpy, XEvent *event);
128
129 /* Actions */
130 static XtActionsRec actionTable[] = {
131 { "Quit", QuitAction },
132 { "Hook", HookAction },
133 };
134
135 /*-------------------------------------------------------------------------*/
136
137 static int
138 x11_error_dev_null(Display * dpy, XErrorEvent * event)
139 {
140 if (args.verbose)
141 printf("x11 error -- ignored (likely just a race as X11 is async)\n");
142 return 0;
143 }
144
145 static void
146 spy_input(XtPointer client_data, int *src, XtInputId *id)
147 {
148 Display *spy_dpy = client_data;
149 XEvent event;
150
151 while (True == XCheckMaskEvent(spy_dpy, 0xffffffff, &event))
152 ProcessEvent(spy_dpy,&event);
153 }
154
155 static void
156 add_window(Display *dpy, Window win)
157 {
158 Window rroot,parent,*children = NULL;
159 int i, n;
160
161 if (NULL == args.watch && XtWindow(app_shell) == win)
162 /* don't f*ck up ourself */
163 return;
164
165 XSelectInput(dpy, win,
166 (args.kbdlog ? KeyPressMask | KeyReleaseMask : 0) |
167 PropertyChangeMask |
168 SubstructureNotifyMask);
169
170 if (0 != XQueryTree(dpy, win, &rroot, &parent, &children, &n)) {
171 for (i = 0; i < n; i++)
172 add_window(dpy,children[i]);
173 if (children)
174 XFree(children);
175 }
176
177 /* look for properties to show */
178 CheckWindow(dpy, win);
179 }
180
181 int
182 main(int argc, char *argv[])
183 {
184 Screen *scr;
185 XColor white,red,dummy;
186 int i;
187 Window root;
188 Display *dpy, *spy_dpy;
189 char title[1024];
190 XEvent event;
191
192 /* init X11 */
193 app_shell = XtAppInitialize(&app_context, "Propwatch",
194 opt_desc, opt_count,
195 &argc, argv,
196 NULL,
197 NULL, 0);
198 XtGetApplicationResources(app_shell,&args,
199 args_desc,args_count,
200 NULL,0);
201
202 XtAppAddActions(app_context,actionTable,
203 sizeof(actionTable)/sizeof(XtActionsRec));
204 XtOverrideTranslations
205 (app_shell,XtParseTranslationTable("<Message>WM_PROTOCOLS: Quit()\n"));
206 dpy = XtDisplay(app_shell);
207 if (NULL != args.watch) {
208 if (NULL == (spy_dpy = XOpenDisplay(args.watch))) {
209 fprintf(stderr,"can't open display: %s\n",args.watch);
210 exit(1);
211 }
212 sprintf(title,"watch on %s - ",args.watch);
213 } else {
214 spy_dpy = dpy;
215 sprintf(title,"watch - ");
216 }
217 root = DefaultRootWindow(spy_dpy);
218
219 XSetErrorHandler(x11_error_dev_null);
220
221 /* args */
222 if (argc > 1) {
223 watch_count = argc-1;
224 watch_name = argv+1;
225 } else {
226 watch_count = sizeof(watch_default)/sizeof(char*);
227 watch_name = watch_default;
228 }
229 watch_atom = malloc(sizeof(Atom)*watch_count);
230
231 /* Atoms */
232 wm_del_win = XInternAtom(dpy,"WM_DELETE_WINDOW", FALSE);
233 wm_class = XInternAtom(dpy,"WM_CLASS", FALSE);
234 for (i = 0; i < watch_count; i++) {
235 watch_atom[i] = XInternAtom(spy_dpy,watch_name[i],FALSE);
236 strcat(title,watch_name[i]);
237 if (i < watch_count-1)
238 strcat(title,", ");
239 }
240 XtVaSetValues(app_shell,XtNtitle,title,NULL);
241
242 /* nice Cursors */
243 left_ptr = XCreateFontCursor(dpy,XC_left_ptr);
244 menu_ptr = XCreateFontCursor(dpy,XC_right_ptr);
245 scr = DefaultScreenOfDisplay(dpy);
246 if (DefaultDepthOfScreen(scr) > 1) {
247 if (XAllocNamedColor(dpy,DefaultColormapOfScreen(scr),
248 "white",&white,&dummy) &&
249 XAllocNamedColor(dpy,DefaultColormapOfScreen(scr),
250 "red",&red,&dummy)) {
251 XRecolorCursor(dpy,left_ptr,&red,&white);
252 XRecolorCursor(dpy,menu_ptr,&red,&white);
253 }
254 }
255
256 /* widgets*/
257 vp = XtVaCreateManagedWidget("vp",viewportWidgetClass,app_shell,
258 XtNallowHoriz, False,
259 XtNallowVert, True,
260 XtNwidth, 600,
261 XtNheight, 400,
262 NULL);
263 bl = XtVaCreateManagedWidget("box",listWidgetClass,vp,
264 XtNdefaultColumns,1,
265 XtNforceColumns,True,
266 NULL);
267 XtOverrideTranslations(bl,XtParseTranslationTable
268 ("<Key>Q: Quit()\n"
269 "<Key>P: Hook(xprop)\n"));
270
271 /* display main window */
272 XtRealizeWidget(app_shell);
273 XDefineCursor(dpy,XtWindow(app_shell),left_ptr);
274 XSetWMProtocols(dpy,XtWindow(app_shell),&wm_del_win,1);
275
276 add_window(spy_dpy,root);
277
278 /* enter main loop */
279 if (spy_dpy != dpy) {
280 XtAppAddInput(app_context,ConnectionNumber(spy_dpy),
281 (XtPointer)XtInputReadMask,
282 spy_input,spy_dpy);
283 }
284 while (TRUE) {
285 XtAppNextEvent(app_context,&event);
286 if (XtDispatchEvent(&event))
287 continue;
288 ProcessEvent(spy_dpy,&event);
289 }
290
291 /* keep compiler happy */
292 return 0;
293 }
294
295 /*-------------------------------------------------------------------------*/
296
297 static int
298 cmp(const void *a, const void *b)
299 {
300 char **aa = (char**)a;
301 char **bb = (char**)b;
302 return strcmp(*aa,*bb);
303 }
304
305 static void
306 RebuildList(void)
307 {
308 static char *empty = "empty";
309 int i;
310 struct WATCHLIST *this;
311
312 if (str_list)
313 free(str_list);
314 str_list = malloc(str_count*sizeof(String));
315 for (i=0, this=watchlist; this!=NULL; i++, this=this->next)
316 str_list[i] = this->text;
317 qsort(str_list,str_count,sizeof(char*),cmp);
318 XawListChange(bl,str_count ? str_list : &empty,
319 str_count ? str_count : 1,1000,1);
320 }
321
322 void
323 AddWatch(Display *dpy, Window win, int i)
324 {
325 struct WATCHLIST *this;
326
327 this = malloc(sizeof(struct WATCHLIST));
328 memset(this,0,sizeof(struct WATCHLIST));
329 if (watchlist)
330 this->next = watchlist;
331 watchlist = this;
332
333 this->win = win;
334 this->watch = i;
335 str_count++;
336 Update(dpy,win,watch_atom[i]);
337 RebuildList();
338 }
339
340 void
341 DeleteWatch(Window win)
342 {
343 struct WATCHLIST *this,*prev = NULL;
344
345 for (this = watchlist; this != NULL;) {
346 if (this->win == win) {
347 if (prev)
348 prev->next = this->next;
349 else
350 watchlist = this->next;
351 this = this->next;
352 str_count--;
353 } else {
354 prev = this;
355 this = this->next;
356 }
357 }
358 RebuildList();
359 }
360
361 void
362 CheckWindow(Display *dpy, Window win)
363 {
364 Atom type;
365 int format,i;
366 unsigned long nitems,rest;
367 unsigned char *data;
368
369 for (i = 0; i < watch_count; i++) {
370 if (Success != XGetWindowProperty
371 (dpy,win,watch_atom[i],
372 0,64,False,AnyPropertyType,
373 &type,&format,&nitems,&rest,&data))
374 continue;
375 if (None != type) {
376 AddWatch(dpy,win,i);
377 XFree(data);
378 }
379 }
380 }
381
382 /*-------------------------------------------------------------------------*/
383
384 static char* str_append(char *dest, char *sep, char *quot, char *str)
385 {
386 int size, pos;
387
388 pos = dest ? strlen(dest) : 0;
389 size = str ? strlen(str) : 0;
390 size += sep ? strlen(sep) : 0;
391 size += quot ? strlen(quot)*2 : 0;
392 dest = realloc(dest,pos+size+1);
393 sprintf(dest+pos,"%s%s%s%s",
394 sep ? sep : "",
395 quot ? quot : "",
396 str ? str : "",
397 quot ? quot : "");
398 return dest;
399 }
400
401 static char*
402 PropertyToString(Display *dpy, Window win, Atom prop)
403 {
404 Atom type;
405 int format;
406 unsigned int i;
407 unsigned long nitems,rest;
408 unsigned char *cdata;
409 unsigned long *ldata;
410 char window[12],*name;
411 char *buf = NULL;
412 char *sep = NULL;
413
414 if (Success != XGetWindowProperty
415 (dpy,win,prop,0,64,False,AnyPropertyType,
416 &type,&format,&nitems,&rest,&cdata))
417 return NULL;
418 ldata = (unsigned long*)cdata;
419 switch (type) {
420 case XA_STRING:
421 for (i = 0; i < nitems; i += strlen(cdata+i)+1) {
422 buf = str_append(buf,sep,"\"",cdata+i);
423 sep = ", ";
424 }
425 break;
426 case XA_ATOM:
427 for (i = 0; i < nitems; i++) {
428 name = XGetAtomName(dpy,ldata[i]);
429 buf = str_append(buf,sep,NULL,name);
430 sep = ", ";
431 if (name)
432 XFree(name);
433 }
434 break;
435 default:
436 if (32 == format) {
437 for (i = 0; i < nitems; i++) {
438 sprintf(window,"0x%x",(unsigned int)ldata[i]);
439 buf = str_append(buf,sep,NULL,window);
440 sep = ", ";
441 }
442 } else {
443 name = XGetAtomName(dpy,type);
444 buf = malloc(40 + (name ? strlen(name) : 4));
445 sprintf(buf,"can't handle: format=%d type=%s",
446 format, name ? name : "NULL");
447 if (name)
448 XFree(name);
449 }
450 break;
451 }
452 XFree(cdata);
453 return buf;
454 }
455
456 void
457 Update(Display *dpy, Window win, Atom prop)
458 {
459 struct WATCHLIST *this;
460 char *str;
461
462 for (this = watchlist; this != NULL; this = this->next)
463 if (this->win == win && watch_atom[this->watch] == prop)
464 break;
465 if (this) {
466 if (this->text)
467 free(this->text);
468 str = PropertyToString(dpy,win,prop);
469 this->text = malloc((str ? strlen(str) : 4) +
470 strlen(watch_name[this->watch]) + 20);
471 sprintf(this->text,"0x%08lx: %s: %s",
472 this->win, watch_name[this->watch], str ? str : "NULL");
473 if (str)
474 free(str);
475 }
476 }
477
478 void
479 ProcessPropertyChange(Display *dpy, XEvent* event)
480 {
481 int i;
482 struct WATCHLIST *this;
483 char *name = NULL;
484 char *prop = NULL;
485
486 name = XGetAtomName(dpy,event->xproperty.atom);
487 for (i = 0; i < watch_count; i++) {
488 if (watch_atom[i] == event->xproperty.atom) {
489 for (this = watchlist; this != NULL; this = this->next)
490 if (this->win == event->xproperty.window &&
491 watch_atom[this->watch] == event->xproperty.atom)
492 break;
493 if (!this)
494 AddWatch(dpy,event->xproperty.window, i);
495 else {
496 Update(dpy,event->xproperty.window,event->xproperty.atom);
497 XawListChange(bl,str_list,str_count,1000,1);
498 }
499 }
500 }
501
502 if (args.proplog) {
503 prop = PropertyToString(dpy, event->xproperty.window,
504 event->xproperty.atom);
505 printf("0x%-8lx: PropertyChange: %s: %s\n",
506 event->xproperty.window,
507 name ? name : "NULL",
508 prop ? prop : "NULL");
509 if (prop)
510 free(prop);
511 }
512 if (name)
513 XFree(name);
514 }
515
516 void
517 ProcessClientMessage(Display *dpy, XEvent* event)
518 {
519 fprintf(stderr,"0x%-8lx: ClientMessage\n",
520 event->xclient.window);
521 }
522
523 void
524 ProcessKeyPress(Display *dpy, XEvent* event)
525 {
526 fprintf(stderr,"0x%-8lx: %s: code=%d sym=%s\n",
527 event->xkey.window,
528 event->type == KeyPress ? "KeyPress" : "KeyRelease",
529 event->xkey.keycode,
530 XKeysymToString(XKeycodeToKeysym(dpy,event->xkey.keycode,0)));
531 }
532
533 void
534 ProcessCreateWindow(Display *dpy, XEvent* event)
535 {
536 add_window(dpy,event->xcreatewindow.window);
537 }
538
539 void
540 ProcessEvent(Display *dpy, XEvent *event)
541 {
542 if (event->type == PropertyNotify) {
543 ProcessPropertyChange(dpy,event);
544
545 } else if (event->type == CreateNotify) {
546 ProcessCreateWindow(dpy,event);
547
548 } else if (event->type == KeyPress ||
549 event->type == KeyRelease) {
550 ProcessKeyPress(dpy,event);
551
552 } else if (event->type == ClientMessage) {
553 ProcessClientMessage(dpy,event);
554
555 } else if (event->type == DestroyNotify) {
556 DeleteWatch(event->xdestroywindow.window);
557 }
558 }
559
560 /*-------------------------------------------------------------------------*/
561
562 void
563 QuitAction(Widget widget, XEvent* event, String* arg, Cardinal* arg_count)
564 {
565 exit(0);
566 }
567
568 void
569 HookAction(Widget widget, XEvent* event, String* arg, Cardinal* arg_count)
570 {
571 XawListReturnStruct *ret;
572 struct WATCHLIST *this;
573 char *dname;
574 char cmd[256];
575 int i;
576
577 /* find window */
578 if (0 != strcmp(XtName(widget),"box"))
579 return;
580 ret = XawListShowCurrent(widget);
581 for (i = 0, this = watchlist; this != NULL; i++, this=this->next)
582 if (0 == strcmp(ret->string,this->text))
583 break;
584 if (NULL == this)
585 return;
586 if (0 == arg_count)
587 return;
588 dname = DisplayString(XtDisplay(widget));
589
590 if (0 == strcmp(arg[0],"xprop")) {
591 /* dump window properties */
592 sprintf(cmd,"xprop -display %s -id 0x%lx",
593 args.watch ? args.watch : dname,
594 this->win);
595 printf("### %s\n",cmd);
596 system(cmd);
597 printf("### done\n");
598 }
599 }
600
|
This page was automatically generated by the
LXR engine.
|