1 /* $Id: xwit.c,v 3.4 97/10/20 18:32:49 dd Exp $ */
2 /*
3  * Pop up or iconify the current xterm window (using its WINDOW_ID in the env)
4  * or a given window id or a list of window matching names. etc...
5  * A miscellany of trivial functions.
6  *
7  * Copyright 1991 CETIA
8  * Permission to use, copy, modify, distribute, and sell this software and its
9  * documentation for any purpose is hereby granted without fee, provided that
10  * the above copyright notice appear in all copies and that both that
11  * copyright notice and this permission notice appear in supporting
12  * documentation, and that the name of CETIA not be used in advertising or
13  * publicity pertaining to distribution of the software without specific,
14  * written prior permission.  CETIA makes no representations about the
15  * suitability of this software for any purpose.  It is provided "as is"
16  * without express or implied warranty.
17  *
18  * CETIA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL CETIA
20  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
22  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
23  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24  *
25  * Original by Mark M Martin. cetia 93/08/13 r1.6 mmm@cetia.fr
26  *
27  * This version by David DiGiacomo, david@slack.com.
28  */
29 #include <X11/Xatom.h>
30 #include <X11/Xos.h>
31 #include <X11/Xlib.h>
32 #include <X11/Xutil.h>
33 #include <X11/Xproto.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <limits.h>
37 #include <sys/time.h>
38 #include "dsimple.h"
39 #include "ClientWin.h"
40 
41 /* note: called by dsimple.c code, must be global */
42 void
usage()43 usage()
44 {
45 	static char Revision[] = "$Revision: 3.4 $";
46 	char *rbeg, *rend;
47 
48 	for (rbeg = Revision; *rbeg; rbeg++) {
49 		if (*rbeg == ' ') {
50 			break;
51 		}
52 	}
53 	if (*rbeg) {
54 		for (rend = ++rbeg; *rend; rend++) {
55 			if (*rend == ' ') {
56 				*rend = 0;
57 				break;
58 			}
59 		}
60 		fprintf(stderr, "%s version %s\n\n",
61 			program_name, rbeg);
62 	}
63 
64 	fprintf(stderr,
65 	"usage: %s -display <display> -sync\n\
66 	-pop -focus -iconify -unmap -print \n\
67 	-raise -lower -opposite -[un]circulate\n\
68 	-resize w h -rows r -columns c -[r]move x y\n\
69 	-[r]warp x y -colormap <colormapid> -[no]save\n\
70 	-name <name> -iconname <name> -property <lookfor>\n\
71 	-bitmap <file> -mask <file> -[r]iconmove x y\n\
72 	-[no]backingstore -[no]saveunder\n\
73 	-[no]keyrepeat keycode ... keycode - keycode\n\
74 	-id <windowid> -root -current -select -all\n\
75 	-names <initialsubstrings>... [must be last]\n",
76 		program_name);
77 	exit(2);
78 }
79 
80 enum functions {
81     pop, focus, icon, unmap, colormap,
82     print,
83     raise, lower, opposite, circulate, uncirculate,
84     move, rmove, warp, rwarp,
85     resize, save, nosave,
86     keyrepeat, nokeyrepeat,
87     name, iconname,
88     rows, columns,
89     iconbitmap, iconmove, riconmove,
90     F_winattr,
91     lastfunc
92 };
93 static long function;
94 #define	FBIT(func)	(1 << (func))
95 
96 /* options that don't need a window */
97 #define	NOWINDOW \
98 	(FBIT(save) | FBIT(nosave) | \
99 	FBIT(keyrepeat) | FBIT(nokeyrepeat) | \
100 	FBIT(rwarp))
101 
102 static enum winidmode {
103 	WID_none,
104 	WID_env,
105 	WID_num,
106 	WID_root,
107 	WID_select,
108 	WID_curr,
109 	WID_names,
110 } Winidmode;
111 
112 static Window root;
113 static int tox, toy;
114 static int Gright, Gbottom;
115 static int towidth, toheight, warpx, warpy;
116 static Colormap cmap;
117 static char **names;	/* list of names to avoid */
118 static int numnames;
119 static int keys[256];
120 static char *wmname;
121 static char *wmiconname;
122 static int Giconx, Gicony;
123 static int nrows;
124 static int ncolumns;
125 static int nbuffer;
126 static char *bitmapname;
127 static char *maskname;
128 static int Gbs, Gsu;
129 
130 /* set if we found a window to act on*/
131 static int Gwinfound;
132 
133 /* forward declarations */
134 static void doit(Window);
135 
136 /*
137  * sleep for given millisecs for those without usleep
138  */
139 static void
mssleep(int ms)140 mssleep(int ms)
141 {
142     struct timeval tv;
143     tv.tv_sec = ms/1000;
144     tv.tv_usec = (ms%1000)*1000;
145     select(0,NULL,NULL,NULL,&tv);
146 }
147 
148 static Atom property = XA_WM_NAME;
149 
MyFetchName(Display * display,Window w,unsigned char ** name)150 static Bool MyFetchName(Display *display, Window w, unsigned char **name)
151 {
152 	Atom returnedType;
153 	int returnedFormat;
154 	unsigned long number;
155 	unsigned long bytesAfterReturn;
156 	unsigned char *data;
157 
158 	if( Success != XGetWindowProperty(display, w, property,
159 				0, (long)BUFSIZ, False,
160 				XA_STRING,
161 				&returnedType, &returnedFormat,
162 				&number, &bytesAfterReturn, &data)) {
163 		*name = NULL;
164 		return False;
165 	} else if( returnedType != XA_STRING || returnedFormat != 8 ) {
166 		if(data)
167 			XFree(data);
168 		*name = NULL;
169 		return False;
170 	} else {
171 		*name = data;
172 		return (data!=NULL)?True:False;
173 	}
174 }
175 
176 
177 /* See if the given atom of the _NET_* family is supported on the
178    display.  */
179 
HasNetAtom(Display * dpy,Atom atom)180 static Bool HasNetAtom(Display *dpy, Atom atom)
181 {
182 	static Atom *atoms;
183 	static unsigned long n_atoms = -1;
184 	int i;
185 
186 	if (n_atoms == -1) {
187 		Atom type;
188 		int format;
189 		unsigned long bytes_after;
190 
191 		n_atoms = 0;
192 		XGetWindowProperty (dpy, RootWindow (dpy, screen),
193 				    XInternAtom(dpy, "_NET_SUPPORTED", True),
194 				    0, LONG_MAX, False, XA_ATOM, &type, &format,
195 				    &n_atoms, &bytes_after, (void *)&atoms);
196 
197 		if (type != XA_ATOM)
198 			return False;
199 	}
200 
201 	if (atoms == NULL)
202 		return False;
203 
204 	for (i = 0; i < n_atoms; i++)
205 		if (atoms[i] == atom)
206 			return True;
207 	return False;
208 }
209 
210 /*
211  * find all windows below this and if name matches call doit on it
212  */
213 static void
downtree(Window top)214 downtree(Window top)
215 {
216     Window *child, dummy;
217     unsigned int children, i;
218     char **cpp;
219     unsigned char *name;
220     if (XQueryTree(dpy, top, &dummy, &dummy, &child, &children)==0)
221 	Fatal_Error("XQueryTree failed");
222     for (i=0; i<children; i++)
223     if(MyFetchName (dpy, child[i], &name)){
224 	for(cpp = names;*cpp!=0;cpp++)
225 	    if(strncmp(*cpp, (char*)name, strlen(*cpp))==0){
226 		doit(child[i]);
227 		break;
228 	    }
229 	XFree(name);
230     } else
231 	downtree(child[i]);	/* dont go down if found a name */
232     if(child)XFree((char *)child);
233 }
234 
235 
236 /*
237  * [un]set autorepeat for individual keys
238  */
239 static void
setrepeat(void)240 setrepeat(void)
241 {
242     unsigned long value_mask;
243     XKeyboardControl values;
244     int i;
245 
246     value_mask = KBKey|KBAutoRepeatMode;
247     values.auto_repeat_mode = (function & FBIT(keyrepeat)) ?
248 	AutoRepeatModeOn : AutoRepeatModeOff;
249 
250     for(i=0;i<256;i++)
251     if(keys[i]){
252 	values.key = i;
253 	XChangeKeyboardControl(dpy, value_mask, &values);
254     }
255 }
256 
257 /*
258  * get window position, compensating for decorations
259  * (based on xwininfo.c)
260  */
261 static void
getpos(Window window,int * xp,int * yp)262 getpos(Window window, int *xp, int *yp)
263 {
264 	XWindowAttributes attributes;
265 	int rx, ry;
266 	Window junkwin;
267 
268 	if (XGetWindowAttributes(dpy, window, &attributes) == 0)
269 		Fatal_Error("XGetWindowAttributes(0x%x)", window);
270 
271 	(void) XTranslateCoordinates(dpy, window, attributes.root,
272 		-attributes.border_width, -attributes.border_width,
273 		&rx, &ry, &junkwin);
274 
275 	*xp = rx - attributes.x;
276 	*yp = ry - attributes.y;
277 }
278 
279 /*
280  * get window size
281  */
282 static void
getsize(Window window,int * wp,int * hp)283 getsize(Window window, int *wp, int *hp)
284 {
285 	XWindowAttributes attributes;
286 
287 	if (XGetWindowAttributes(dpy, window, &attributes) == 0)
288 		Fatal_Error("XGetWindowAttributes(0x%x)", window);
289 
290 	*wp = attributes.width;
291 	*hp = attributes.height;
292 }
293 
294 /*
295  * set window position
296  */
297 static void
domove(Window window,int x,int y,int right,int bottom)298 domove(Window window, int x, int y, int right, int bottom)
299 {
300 	XWindowChanges values;
301 	unsigned int value_mask;
302 
303 	if (right || bottom) {
304 		XWindowAttributes win_attr, frame_attr;
305 		Window wmframe;
306 
307 		if (XGetWindowAttributes(dpy, window, &win_attr) == 0)
308 			Fatal_Error("XGetWindowAttributes(0x%x)", window);
309 
310 		/* find our window manager frame, if any */
311 		for (wmframe = window; ; ) {
312 			Status status;
313 			Window wroot, parent;
314 			Window *childlist;
315 			unsigned int ujunk;
316 
317 			status = XQueryTree(dpy, wmframe,
318 				&wroot, &parent, &childlist, &ujunk);
319 			if (parent == wroot || !parent || !status)
320 				break;
321 			wmframe = parent;
322 			if (status && childlist)
323 				XFree((char *) childlist);
324 		}
325 
326 #ifndef FVWM2
327 		/*
328 		 * Norman R. McBride <norm@city.ac.uk> reports that
329 		 * this code doesn't work with fvwm2, and I don't have
330 		 * a fix yet, sorry. - DD
331 		 */
332 		if (wmframe != window) {
333 			if (!XGetWindowAttributes(dpy, wmframe, &frame_attr))
334 				Fatal_Error("XGetWindowAttributes(0x%x)",
335 					wmframe);
336 
337 			win_attr.width = frame_attr.width;
338 			win_attr.height = frame_attr.height;
339 			win_attr.border_width +=
340 				frame_attr.border_width;
341 		}
342 #endif /* !FVWM2 */
343 
344 		if (right)
345 			x += DisplayWidth(dpy, screen) -
346 				win_attr.width -
347 				win_attr.border_width;
348 
349 		if (bottom)
350 			y += DisplayHeight(dpy, screen) -
351 				win_attr.height -
352 				win_attr.border_width;
353 	}
354 
355 	values.x = x;
356 	values.y = y;
357 	value_mask = CWX | CWY;
358 
359 	if (XReconfigureWMWindow(dpy, window, screen,
360 		value_mask, &values) == 0)
361 		Fatal_Error("move failed");
362 }
363 
364 /*
365  * dump some intresting window data
366  */
367 static void
doprint(Window window)368 doprint(Window window)
369 {
370 	XWindowAttributes attributes;
371 	unsigned char *name;
372 
373 	if( MyFetchName(dpy,window,&name) ) {
374 		if (XGetWindowAttributes(dpy, window, &attributes) == 0)
375 			Fatal_Error("XGetWindowAttributes(0x%x)", window);
376 
377 		printf("0x%x: x=%d y=%d w=%d h=%d d=%d ",
378 			(int)window,
379 			attributes.x,attributes.y,
380 			attributes.width,attributes.height,
381 			attributes.depth);
382 		putchar('\'');
383 		while( *name != '\0' ) {
384 			if( *name >= ' ' && ((*name)&0x80)== 0 ) {
385 				putchar(*name);
386 			} else
387 				printf("\\%03hho",*name);
388 			name++;
389 		}
390 		putchar('\'');
391 		putchar('\n');
392 	}
393 }
394 
395 /*
396  * set window size
397  */
398 static void
doresize(Window window,int w,int h)399 doresize(Window window, int w, int h)
400 {
401     XWindowChanges values;
402     unsigned int value_mask;
403     int try;
404     int nw, nh;
405 
406     values.width = w;
407     values.height = h;
408     value_mask = CWWidth | CWHeight;
409 
410     for (try = 0; try < 2; try++) {
411 	if (XReconfigureWMWindow(dpy, window, screen, value_mask, &values) == 0)
412 	    Fatal_Error("resize: XReconfigureWMWindow");
413 
414 	getsize(window, &nw, &nh);
415 	if (values.width == nw && values.height == nh)
416 	    return;
417 
418 	/* give window manager a couple of chances to react */
419 	mssleep(100);
420 	getsize(window, &nw, &nh);
421 	if (values.width == nw && values.height == nh)
422 	    return;
423 
424 	mssleep(400);
425 	getsize(window, &nw, &nh);
426 	if (values.width == nw && values.height == nh)
427 	    return;
428     }
429 
430     /* last chance */
431     values.width += values.width - nw;
432     values.height += values.height - nh;
433     if (XReconfigureWMWindow(dpy, window, screen, value_mask, &values) == 0)
434 	Fatal_Error("resize: XReconfigureWMWindow 2");
435 }
436 
437 /*
438  * set row/column size
439  */
440 static void
rcresize(enum functions what,Window window)441 rcresize(enum functions what, Window window)
442 {
443     XSizeHints *hints;
444     long supplied;
445     int w, h;
446 
447     if (!(what & FBIT(rows)) || !(what & FBIT(columns)))
448 	getsize(window, &w, &h);
449 
450     if (!(hints = XAllocSizeHints()))
451 	Fatal_Error("XAllocSizeHints");
452 
453     if (XGetWMNormalHints(dpy, window, hints, &supplied) == 0)
454 	Fatal_Error("XGetWMNormalHints");
455 
456     if (!(supplied & PBaseSize) || !(supplied & PResizeInc))
457 	Fatal_Error("missing PBaseSize and/or PResizeInc hint");
458 
459     if (what & FBIT(columns))
460 	w = hints->base_width + hints->width_inc * ncolumns;
461 
462     if (what & FBIT(rows))
463 	h = hints->base_height + hints->height_inc * nrows;
464 
465     doresize(window, w, h);
466 
467     XFree(hints);
468 }
469 
470 static void
loadbitmap(Window window,const char * file,Pixmap * pmp)471 loadbitmap(Window window, const char *file, Pixmap *pmp)
472 {
473 	unsigned int w, h;
474 	int xhot, yhot;
475 
476 	if (XReadBitmapFile(dpy, window, file,
477 		&w, &h, pmp, &xhot, &yhot) != BitmapSuccess)
478 		Fatal_Error("XReadBitmapFile failed");
479 }
480 
481 static void
setbitmap(Window window)482 setbitmap(Window window)
483 {
484 	static XWMHints *hints;
485 	static Pixmap bitmap_pm;
486 	static Pixmap mask_pm;
487 	XWMHints *ohints;
488 
489 	if (!hints) {
490 		if (!(hints = XAllocWMHints()) ||
491 			!(ohints = XAllocWMHints()))
492 			Fatal_Error("XAllocWMHints");
493 
494 		if (bitmapname) {
495 			loadbitmap(window, bitmapname, &bitmap_pm);
496 			hints->flags |= IconPixmapHint;
497 			hints->icon_pixmap = bitmap_pm;
498 		}
499 
500 		if (maskname) {
501 			loadbitmap(window, maskname, &mask_pm);
502 			hints->flags |= IconMaskHint;
503 			hints->icon_mask = mask_pm;
504 		}
505 
506 		XSetCloseDownMode(dpy, RetainTemporary);
507 	}
508 
509 	if ((ohints = XGetWMHints(dpy, window)) != NULL ) {
510 		if (ohints->icon_pixmap && hints->icon_pixmap)
511 			XFreePixmap(dpy, ohints->icon_pixmap);
512 		if (ohints->icon_mask && hints->icon_mask)
513 			XFreePixmap(dpy, ohints->icon_mask);
514 		XFree(ohints);
515 	}
516 
517 	XSetWMHints(dpy, window, hints);
518 }
519 
520 static void
setwinattr(Window window)521 setwinattr(Window window)
522 {
523 	XSetWindowAttributes swa;
524 	unsigned long valuemask;
525 
526 	valuemask = 0;
527 
528 	if (Gbs) {
529 		valuemask |= CWBackingStore | CWBackingPlanes;
530 		swa.backing_store = Gbs > 0 ? Always : NotUseful;
531 		swa.backing_planes = ~0L;
532 	}
533 	if (Gsu) {
534 		valuemask |= CWSaveUnder;
535 		swa.save_under = Gsu > 0;
536 	}
537 
538 	XChangeWindowAttributes(dpy, window, valuemask, &swa);
539 }
540 
541 /*
542  * iconify the given window, or map and raise it, or whatever
543  */
544 static void
doit(Window window)545 doit(Window window)
546 {
547 	XWindowChanges values;
548 	unsigned int value_mask;
549 	XWMHints *wmhp;
550 	enum functions f;
551 	int i = 0;
552 
553 	Gwinfound = 1;
554 
555 	f = function;
556 	for (i = 0; i < lastfunc; i++) {
557 		if ((f & FBIT(i)) == 0)
558 			continue;
559 
560 		switch (i) {
561 		case warp:
562 			XWarpPointer(dpy, None, window, 0, 0, 0, 0,
563 				warpx, warpy);
564 			break;
565 		case rwarp:
566 			XWarpPointer(dpy, None, None, 0, 0, 0, 0,
567 				warpx, warpy);
568 			break;
569 		case move:
570 			domove(window, tox, toy, Gright, Gbottom);
571 			break;
572 		case rmove:
573 			getpos(window, &values.x, &values.y);
574 			values.x += tox;
575 			values.y += toy;
576 			value_mask = CWX | CWY;
577 			if (XReconfigureWMWindow(dpy, window, screen,
578 					value_mask, &values) == 0)
579 				Fatal_Error("rmove failed");
580 			break;
581 		case resize:
582 			doresize(window, towidth, toheight);
583 			break;
584 		case colormap:
585 			XSetWindowColormap(dpy, window, cmap);
586 			break;
587 		case print:
588 			doprint(window);
589 			break;
590 		case pop:
591 			XMapRaised(dpy, window);
592 			break;
593 		case focus: {
594 			static XClientMessageEvent event;
595 
596 			if (event.type == 0) {
597 				event.type = ClientMessage;
598 				event.message_type =
599 					XInternAtom(dpy, "_NET_ACTIVE_WINDOW", True);
600 				event.format = 32;
601 				event.data.l[0] = 1;
602 				event.data.l[1] = CurrentTime;
603 			}
604 
605 			/* if no _NET_ACTIVE_WINDOW, the wm is oldschool... */
606 			if (event.message_type == 0
607 			    || !HasNetAtom (dpy, event.message_type))
608 				XSetInputFocus(dpy, window, CurrentTime, RevertToNone);
609 			else {
610 				event.window = window;
611 				event.data.l[2] = root;
612 				if (XSendEvent(dpy, root, (Bool) False,
613 					       SubstructureRedirectMask,
614 					       (XEvent *) & event) == 0)
615 					Fatal_Error("send event failed");
616 			}
617 			break;
618 		}
619 		case raise:
620 			values.stack_mode = Above;
621 			value_mask = CWStackMode;
622 			XConfigureWindow(dpy, window, value_mask, &values);
623 			break;
624 		case lower:
625 			values.stack_mode = Below;
626 			value_mask = CWStackMode;
627 			XConfigureWindow(dpy, window, value_mask, &values);
628 			break;
629 		case opposite:
630 			values.stack_mode = Opposite;
631 			value_mask = CWStackMode;
632 			XConfigureWindow(dpy, window, value_mask, &values);
633 			break;
634 		case circulate:
635 			XCirculateSubwindowsUp(dpy, window);
636 			break;
637 		case uncirculate:
638 			XCirculateSubwindowsDown(dpy, window);
639 			break;
640 		case unmap:
641 			XUnmapWindow(dpy, window);
642 			break;
643 		case icon:
644 #if iconify_by_sending_client_message
645 			static XClientMessageEvent event;
646 
647 			if (event.type == 0) {
648 				event.type = ClientMessage;
649 #ifdef XA_WM_CHANGE_STATE
650 				event.message_type = XA_WM_CHANGE_STATE;
651 #else
652 				event.message_type =
653 					XInternAtom(dpy, "WM_CHANGE_STATE", True);
654 				if (event.message_type == 0)
655 					Fatal_Error("no WM_CHANGE_STATE atom");
656 #endif
657 				event.format = 32;
658 				event.data.l[0] = IconicState;
659 			}
660 
661 			event.window = window;
662 			if (XSendEvent(dpy, root, (Bool) False,
663 					SubstructureRedirectMask | SubstructureNotifyMask,
664 					(XEvent *) & event) == 0)
665 				Fatal_Error("send event failed");
666 #else /* iconify_by_sending_client_message */
667 			if (XIconifyWindow(dpy, window, screen) == 0)
668 				Fatal_Error("iconify failed");
669 #endif /* iconify_by_sending_client_message */
670 			break;
671 		case save:
672 			XForceScreenSaver(dpy, ScreenSaverActive);
673 			break;
674 		case nosave:
675 			XForceScreenSaver(dpy, ScreenSaverReset);
676 			break;
677 		case keyrepeat:
678 		case nokeyrepeat:
679 			setrepeat();
680 			break;
681 		case name:
682 			XStoreName(dpy, window, wmname);
683 			break;
684 		case iconname:
685 			XSetIconName(dpy, window, wmiconname);
686 			break;
687 		case rows:
688 			/* don't do it twice */
689 			if (f & FBIT(columns))
690 				break;
691 			/* fall through */
692 		case columns:
693 			rcresize(f, window);
694 			break;
695 		case iconbitmap:
696 			setbitmap(window);
697 			break;
698 		case iconmove:
699 			wmhp = XGetWMHints(dpy, window);
700 			if (wmhp == 0)
701 				Fatal_Error("no WM_HINTS");
702 			wmhp->flags |= IconPositionHint;
703 			wmhp->icon_x = Giconx;
704 			wmhp->icon_y = Gicony;
705 			XSetWMHints(dpy, window, wmhp);
706 			XFree(wmhp);
707 			break;
708 		case riconmove:
709 			wmhp = XGetWMHints(dpy, window);
710 			if (wmhp == 0)
711 				Fatal_Error("no WM_HINTS");
712 			if (wmhp->flags & IconPositionHint) {
713 				wmhp->icon_x += Giconx;
714 				wmhp->icon_y += Gicony;
715 				XSetWMHints(dpy, window, wmhp);
716 			}
717 			else
718 				Fatal_Error("no current icon position");
719 			XFree(wmhp);
720 			break;
721 		case F_winattr:
722 			setwinattr(window);
723 			break;
724 		}
725 	}
726 }
727 
728 /* based on xwininfo.c */
729 static Window
xwit_select_window(Display * dpy,int current)730 xwit_select_window(Display *dpy, int current)
731 {
732 	Window window = None;
733 	Window wroot;
734 	int dummyi;
735 	unsigned int dummy;
736 
737 	if (current) {
738 		XQueryPointer(dpy, RootWindow(dpy, screen),
739 			&wroot, &window,
740 			&dummyi, &dummyi, &dummyi, &dummyi, &dummy);
741 	}
742 	else {
743 		printf("\n");
744 		printf("%s: select window by clicking the mouse\n",
745 			program_name);
746 		(void) fflush(stdout);
747 		window = Select_Window(dpy);
748 	}
749 	if (window) {
750 		if (XGetGeometry(dpy, window, &wroot, &dummyi, &dummyi,
751 			&dummy, &dummy, &dummy, &dummy) &&
752 			window != wroot)
753 			window = XmuClientWindow(dpy, window);
754 	}
755 	return window;
756 }
757 
758 static Window
getxid(const char * s)759 getxid(const char *s)
760 {
761 	XID id;
762 
763 	if (sscanf(s, "0x%lx", &id) == 1)
764 		return id;
765 	if (sscanf(s, "%ld", &id) == 1)
766 		return id;
767 	Fatal_Error("Invalid ID format: %s", s);
768 	/* NOTREACHED */
769         return -1;
770 }
771 
772 static int
matchopt(const char * key,int nargs,int * argc,char ** argv)773 matchopt(const char *key, int nargs, int *argc, char **argv)
774 {
775 	int enough = 0;
776 	int match = 1;
777 	char *ap;
778 	const char *kp;
779 
780 	ap = *argv;
781 	if (*ap == '-')
782 		ap++;
783 
784 	for (kp = key; *kp; kp++, ap++) {
785 		if (*kp == '*') {
786 			enough = 1;
787 			ap--;
788 			continue;
789 		}
790 		if (*ap == 0) {
791 			match = enough;
792 			break;
793 		}
794 		if (*ap != *kp) {
795 			match = 0;
796 			break;
797 		}
798 	}
799 
800 	if (match) {
801 		if (argc[0] <= nargs) {
802 			char option[32];
803 			int dash, skip;
804 
805 			strncpy(option, *argv, sizeof option - 1);
806 			option[sizeof option - 1] = 0;
807 
808 			dash = *argv[0] == '-';
809 
810 			for (ap = option; *ap; ap++)
811 				/* nothing */ ;
812 			skip = ap - option - dash;
813 			enough = 0;
814 
815 			for (kp = key; *kp && skip--; kp++) {
816 				if (*kp == '*')
817 					skip++;
818 			}
819 			for (; *kp; kp++) {
820 				if (*kp == '*')
821 					continue;
822 				if (enough == 0) {
823 					*ap++ = '(';
824 					enough = 1;
825 				}
826 				*ap++ = *kp;
827 			}
828 			if (enough)
829 				*ap++ = ')';
830 			*ap = 0;
831 
832 			fprintf(stderr,
833 				"%s: option %s needs %d argument%s\n\n",
834 				program_name, option,
835 				nargs, nargs > 1 ? "s" : "");
836 			usage();
837 		}
838 		argc[0] -= nargs;
839 	}
840 	return match;
841 }
842 
843 static void
FetchBuffer(Display * dpy,int nbuf)844 FetchBuffer(Display *dpy, int nbuf)
845 {
846         char *buf;
847         int size;
848 
849         buf = XFetchBuffer(dpy, &size, nbuf);
850 
851         if( size == 0 )
852                 fprintf( stderr, "Could not fetch cutbuffer %d\n", nbuf );
853         else
854                 fwrite( buf, 1, size, stdout );
855 }
856 
857 static void
StoreBuffer(Display * dpy,int nbuf)858 StoreBuffer(Display *dpy, int nbuf)
859 {
860         char *buf = NULL;
861         int bufsize, nread, total=0;
862 
863         bufsize = 10;
864         buf = malloc( bufsize );
865         while( (nread=read(0,buf+total,bufsize-total)) > 0 )
866         {
867                 total+=nread;
868                 bufsize *= 2;
869                 buf = realloc( buf, bufsize );
870         }
871         XStoreBuffer(dpy, buf, total, nbuf);
872         free(buf);
873 }
874 
875 char *allwindows[] = {""};
876 
877 int
main(int argc,char * argv[])878 main(int argc, char *argv[])
879 {
880 	Window window = 0;
881 	int *pargc = &argc;
882 
883 	program_name = argv[0] + strlen(argv[0]);
884 	while (program_name != argv[0] && program_name[-1] != '/')
885 		program_name--;
886 
887 	Setup_Display_And_Screen(pargc, argv);
888 
889 	Winidmode = WID_env;
890 
891 	while (argv++, --argc > 0) {
892 		/* argv[0] = next argument */
893 		/* argc = # of arguments left */
894 		if (matchopt("a*ll", 0, pargc, argv)) {
895 			Winidmode = WID_names;
896 			names = allwindows;
897 			numnames = 1;
898 		}
899 		else if (matchopt("ba*ckingstore", 0, pargc, argv) ||
900 			matchopt("bs", 0, pargc, argv)) {
901 			function |= FBIT(F_winattr);
902 			Gbs = 1;
903 		}
904 		else if (matchopt("b*itmap", 1, pargc, argv)) {
905 			function |= FBIT(iconbitmap);
906 			bitmapname = *++argv;
907 		}
908 		else if (matchopt("colo*rmap", 1, pargc, argv) ||
909 			matchopt("cm*ap", 1, pargc, argv)) {
910 			function |= FBIT(colormap);
911 			cmap = (Colormap) getxid(*++argv);
912 		}
913 		else if (matchopt("co*lumns", 1, pargc, argv)) {
914 			function |= FBIT(columns);
915 			ncolumns = atoi(*++argv);
916 		}
917 		else if (matchopt("store*buffer", 1, pargc, argv)) {
918 			nbuffer = atoi(*++argv);
919                         StoreBuffer( dpy, nbuffer );
920 		}
921 		else if (matchopt("fetch*buffer", 1, pargc, argv)) {
922 			nbuffer = atoi(*++argv);
923                         FetchBuffer( dpy, nbuffer );
924 		}
925 		else if (matchopt("c*urrent", 0, pargc, argv)) {
926 			Winidmode = WID_curr;
927 		}
928 		else if (matchopt("iconm*ove", 2, pargc, argv)) {
929 			function |= FBIT(iconmove);
930 			Giconx = atoi(argv[1]);
931 			Gicony = atoi(argv[2]);
932 			argv += 2;
933 		}
934 		else if (matchopt("iconn*ame", 1, pargc, argv) ||
935 			matchopt("in*ame", 1, pargc, argv)) {
936 			function |= FBIT(iconname);
937 			wmiconname = *++argv;
938 		}
939 		else if (matchopt("id", 1, pargc, argv)) {
940 			Winidmode = WID_num;
941 			window = (Window) getxid(*++argv);
942 		}
943 		else if (matchopt("i*conify", 0, pargc, argv)) {
944 			function |= FBIT(icon);
945 		}
946 		else if (matchopt("k*eyrepeat", 1, pargc, argv) ||
947 			matchopt("nok*eyrepeat", 1, pargc, argv)) {
948 			int i;
949 
950 			function |= argv[0][1] == 'n' ?
951 				FBIT(nokeyrepeat) : FBIT(keyrepeat);
952 
953 			i = atoi(*++argv);
954 			if (i < 0)
955 				usage();
956 
957 			while (1) {
958 				keys[i & 0xff] = 1;
959 				if (argc <= 0)
960 					break;
961 				if (strcmp(argv[0], "-") == 0) {
962 					int from = i;
963 
964 					argc--, argv++;
965 					if (argc < 0 || (i = atoi(argv[0])) <= 0)
966 						usage();
967 					while (from <= i)
968 						keys[from++ & 0xff] = 1;
969 					argc--, argv++;
970 					if (argc <= 0)
971 						break;
972 				}
973 				if ((i = atoi(argv[0])) <= 0)
974 					break;
975 				argc--, argv++;
976 			}
977 		}
978 		else if (matchopt("li*st", 1, pargc, argv) ||
979 			matchopt("names", 1, pargc, argv)) {
980 			Winidmode = WID_names;
981 			/* take rest of arg list */
982 			names = ++argv;
983 			numnames = argc;
984 			argc = 0;
985 		}
986 		else if (matchopt("l*abel", 1, pargc, argv)) {
987 			function |= FBIT(name);
988 			wmname = *++argv;
989 		}
990 		else if (matchopt("ma*sk", 1, pargc, argv)) {
991 			function |= FBIT(iconbitmap);
992 			maskname = *++argv;
993 		}
994 		else if (matchopt("m*ove", 2, pargc, argv)) {
995 			function |= FBIT(move);
996 			Gright = (argv[1][0] == '-');
997 			Gbottom = (argv[2][0] == '-');
998 			tox = atoi(argv[1]);
999 			toy = atoi(argv[2]);
1000 			argv += 2;
1001 		}
1002 		else if (matchopt("noba*ckingstore", 0, pargc, argv) ||
1003 			matchopt("nobs", 0, pargc, argv)) {
1004 			function |= FBIT(F_winattr);
1005 			Gbs = -1;
1006 		}
1007 		else if (matchopt("nosaveu*nder", 0, pargc, argv) ||
1008 			matchopt("nosu", 0, pargc, argv)) {
1009 			function |= FBIT(F_winattr);
1010 			Gsu = -1;
1011 		}
1012 		else if (matchopt("nos*ave", 0, pargc, argv)) {
1013 			function |= FBIT(nosave);
1014 		}
1015 		else if (matchopt("n*ame", 1, pargc, argv)) {
1016 			function |= FBIT(name);
1017 			wmname = *++argv;
1018 		}
1019 		else if (matchopt("o*pen", 0, pargc, argv)) {
1020 			function |= FBIT(pop);
1021 		}
1022 		else if (matchopt("p*op", 0, pargc, argv)) {
1023 			function |= FBIT(pop);
1024 		}
1025 		else if (matchopt("pr*int", 0, pargc, argv)) {
1026 			function |= FBIT(print);
1027 		}
1028 		else if (matchopt("f*ocus", 0, pargc, argv)) {
1029 			function |= FBIT(focus);
1030 		}
1031 		else if (matchopt("ra*ise", 0, pargc, argv)) {
1032 			function |= FBIT(raise);
1033 		}
1034 		else if (matchopt("lo*wer", 0, pargc, argv)) {
1035 			function |= FBIT(lower);
1036 		}
1037 		else if (matchopt("op*posite", 0, pargc, argv)) {
1038 			function |= FBIT(opposite);
1039 		}
1040 		else if (matchopt("cir*culate", 0, pargc, argv)) {
1041 			function |= FBIT(circulate);
1042 		}
1043 		else if (matchopt("uncir*culate", 0, pargc, argv)) {
1044 			function |= FBIT(uncirculate);
1045 		}
1046 		else if (matchopt("ri*conmove", 2, pargc, argv)) {
1047 			function |= FBIT(riconmove);
1048 			Giconx = atoi(argv[1]);
1049 			Gicony = atoi(argv[2]);
1050 			argv += 2;
1051 		}
1052 		else if (matchopt("rm*ove", 2, pargc, argv)) {
1053 			function |= FBIT(rmove);
1054 			tox = atoi(argv[1]);
1055 			toy = atoi(argv[2]);
1056 			argv += 2;
1057 		}
1058 		else if (matchopt("roo*t", 0, pargc, argv)) {
1059 			Winidmode = WID_root;
1060 		}
1061 		else if (matchopt("ro*ws", 1, pargc, argv)) {
1062 			function |= FBIT(rows);
1063 			nrows = atoi(*++argv);
1064 		}
1065 		else if (matchopt("rw*arp", 2, pargc, argv)) {
1066 			function |= FBIT(rwarp);
1067 			warpx = atoi(argv[1]);
1068 			warpy = atoi(argv[2]);
1069 			argv += 2;
1070 		}
1071 		else if (matchopt("r*esize", 2, pargc, argv)) {
1072 			function |= FBIT(resize);
1073 			towidth = atoi(argv[1]);
1074 			toheight = atoi(argv[2]);
1075 			argv += 2;
1076 		}
1077 		else if (matchopt("saveu*nder", 0, pargc, argv) ||
1078 			matchopt("su", 0, pargc, argv)) {
1079 			function |= FBIT(F_winattr);
1080 			Gsu = argv[0][1] == 1;
1081 		}
1082 		else if (matchopt("se*lect", 0, pargc, argv)) {
1083 			Winidmode = WID_select;
1084 		}
1085 		else if (matchopt("sy*nc", 0, pargc, argv)) {
1086 			XSynchronize(dpy, True);
1087 		}
1088 		else if (matchopt("s*ave", 0, pargc, argv)) {
1089 			function |= FBIT(save);
1090 		}
1091 		else if (matchopt("un*map", 0, pargc, argv)) {
1092 			function |= FBIT(unmap);
1093 		}
1094 		else if (matchopt("w*arp", 2, pargc, argv)) {
1095 			function |= FBIT(warp);
1096 			warpx = atoi(argv[1]);
1097 			warpy = atoi(argv[2]);
1098 			argv += 2;
1099 		} else if(matchopt("prop*erty",1, pargc,argv)) {
1100 			property = XInternAtom(dpy,argv[1],False);
1101 			if( None == property ) {
1102 			    Fatal_Error("Unknown atom %s",argv[1]);
1103 			}
1104 			argv++;
1105 		}
1106 		else
1107 			usage();
1108 	}
1109 
1110 	/* default function: pop */
1111 	if (function == 0)
1112 		function = FBIT(pop);
1113 
1114 	if ((function & ~NOWINDOW) == 0)
1115 		Winidmode = WID_none;
1116 
1117 	root = DefaultRootWindow(dpy);
1118 
1119 	switch (Winidmode) {
1120 	case WID_env:
1121 		{
1122 			char *s;
1123 
1124 			s = getenv("WINDOWID");
1125 			if (s != 0)
1126 				window = (Window) getxid(s);
1127 			else {
1128 				/* no WINDOWID, use window under cursor */
1129 				window = xwit_select_window(dpy, 1);
1130 				if (window == None)
1131 					Fatal_Error("WINDOWID not set");
1132 			}
1133 		}
1134 		break;
1135 	case WID_root:
1136 		window = root;
1137 		break;
1138 	case WID_curr:
1139 		window = xwit_select_window(dpy, 1);
1140 		break;
1141 	case WID_select:
1142 		window = xwit_select_window(dpy, 0);
1143 		break;
1144 	default:
1145 	        break;
1146 	}
1147 
1148 	switch (Winidmode) {
1149 	case WID_none:
1150 		doit((Window) 0);
1151 		break;
1152 	case WID_names:
1153 		downtree(root);
1154 		break;
1155 	default:
1156 		if (!window)
1157 			Fatal_Error("no window selected");
1158 		doit(window);
1159 		break;
1160 	}
1161 
1162 	XSync(dpy, True);
1163 	(void) XCloseDisplay(dpy);
1164 	return(!Gwinfound);
1165 }
1166 
1167