xkill.c revision b66d1acb
1/* $Xorg: xkill.c,v 1.5 2001/02/09 02:05:54 xorgcvs Exp $ */
2/*
3
4Copyright 1988, 1998  The Open Group
5
6Permission to use, copy, modify, distribute, and sell this software and its
7documentation for any purpose is hereby granted without fee, provided that
8the above copyright notice appear in all copies and that both that
9copyright notice and this permission notice appear in supporting
10documentation.
11
12The above copyright notice and this permission notice shall be included
13in all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
19OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21OTHER DEALINGS IN THE SOFTWARE.
22
23Except as contained in this notice, the name of The Open Group shall
24not be used in advertising or otherwise to promote the sale, use or
25other dealings in this Software without prior written authorization
26from The Open Group.
27
28*/
29/* $XFree86: xc/programs/xkill/xkill.c,v 1.5 2001/04/01 14:00:22 tsi Exp $ */
30
31/*
32 * xkill - simple program for destroying unwanted clients
33 * Author:  Jim Fulton, MIT X Consortium; Dana Chee, Bellcore
34 */
35
36/*
37 * Warning, this is a very dangerous client....
38 */
39
40#include <stdio.h>
41#include <stdlib.h>
42#include <ctype.h>
43
44#include <X11/Xos.h>
45#include <X11/Xlib.h>
46#include <X11/cursorfont.h>
47#include <X11/Xproto.h>
48
49#include <X11/Xmu/WinUtil.h>
50
51static Display *dpy = NULL;
52static char *ProgramName;
53
54#define SelectButtonAny (-1)
55#define SelectButtonFirst (-2)
56
57static int parse_button ( char *s, int *buttonp );
58static XID parse_id ( char *s );
59static XID get_window_id ( Display *dpy, int screen, int button, char *msg );
60static int catch_window_errors ( Display *dpy, XErrorEvent *ev );
61static int kill_all_windows ( Display *dpy, int screenno, Bool top );
62static int verify_okay_to_kill ( Display *dpy, int screenno );
63static Bool wm_state_set ( Display *dpy, Window win );
64static Bool wm_running ( Display *dpy, int screenno );
65
66static void
67Exit(int code)
68{
69    if (dpy) {
70	XCloseDisplay (dpy);
71    }
72    exit (code);
73}
74
75static void
76usage(void)
77{
78    static char *options[] = {
79"where options include:",
80"    -display displayname    X server to contact",
81"    -id resource            resource whose client is to be killed",
82"    -frame                  don't ignore window manager frames",
83"    -button number          specific button to be pressed to select window",
84"    -all                    kill all clients with top level windows",
85"",
86NULL};
87    char **cpp;
88
89    fprintf (stderr, "usage:  %s [-option ...]\n",
90	     ProgramName);
91    for (cpp = options; *cpp; cpp++) {
92	fprintf (stderr, "%s\n", *cpp);
93    }
94    Exit (1);
95}
96
97int
98main(int argc, char *argv[])
99{
100    int i;				/* iterator, temp variable */
101    char *displayname = NULL;		/* name of server to contact */
102    int screenno;			/* screen number of dpy */
103    XID id = None;			/* resource to kill */
104    char *button_name = NULL;		/* name of button for window select */
105    int button;				/* button number or negative for all */
106    Bool kill_all = False;
107    Bool top = False;
108
109    ProgramName = argv[0];
110    button = SelectButtonFirst;
111
112    for (i = 1; i < argc; i++) {
113	char *arg = argv[i];
114
115	if (arg[0] == '-') {
116	    switch (arg[1]) {
117	      case 'd':			/* -display displayname */
118		if (++i >= argc) usage ();
119		displayname = argv[i];
120		continue;
121	      case 'i':			/* -id resourceid */
122		if (++i >= argc) usage ();
123		id = parse_id (argv[i]);
124		continue;
125	      case 'b':			/* -button number */
126		if (++i >= argc) usage ();
127		button_name = argv[i];
128		continue;
129	      case 'f':			/* -frame */
130		top = True;
131		continue;
132	      case 'a':			/* -all */
133		kill_all = True;
134		continue;
135	      default:
136		usage ();
137	    }
138	} else {
139	    usage ();
140	}
141    }					/* end for */
142
143    dpy = XOpenDisplay (displayname);
144    if (!dpy) {
145	fprintf (stderr, "%s:  unable to open display \"%s\"\n",
146		 ProgramName, XDisplayName (displayname));
147	Exit (1);
148    }
149    screenno = DefaultScreen (dpy);
150
151    if (kill_all) {
152	if (verify_okay_to_kill (dpy, screenno))
153	  kill_all_windows (dpy, screenno, top);
154	Exit (0);
155    }
156
157    /*
158     * if no id was given, we need to choose a window
159     */
160
161    if (id == None) {
162	if (!button_name)
163	    button_name = XGetDefault (dpy, ProgramName, "Button");
164
165	if (button_name && !parse_button (button_name, &button)) {
166	    fprintf (stderr, "%s:  invalid button specification \"%s\"\n",
167		     ProgramName, button_name);
168	    Exit (1);
169	}
170
171	if (button >= 0 || button == SelectButtonFirst) {
172	    unsigned char pointer_map[256];	 /* 8 bits of pointer num */
173	    int count, j;
174	    unsigned int ub = (unsigned int) button;
175
176
177	    count = XGetPointerMapping (dpy, pointer_map, 256);
178	    if (count <= 0) {
179		fprintf (stderr,
180			 "%s:  no pointer mapping, can't select window\n",
181			 ProgramName);
182		Exit (1);
183	    }
184
185	    if (button >= 0) {			/* check button */
186		for (j = 0; j < count; j++) {
187		    if (ub == (unsigned int) pointer_map[j]) break;
188		}
189		if (j == count) {
190		    fprintf (stderr,
191	 "%s:  no button number %u in pointer map, can't select window\n",
192			     ProgramName, ub);
193		    Exit (1);
194	        }
195	    } else {				/* get first entry */
196		button = (int) ((unsigned int) pointer_map[0]);
197	    }
198	}
199	if ((id = get_window_id (dpy, screenno, button,
200				"the window whose client you wish to kill"))) {
201	    if (id == RootWindow(dpy,screenno)) id = None;
202	    else if (!top) {
203		XID indicated = id;
204		if ((id = XmuClientWindow(dpy, indicated)) == indicated) {
205
206		    /* Try not to kill the window manager when the user
207		     * indicates an icon to xkill.
208		     */
209
210		    if (! wm_state_set(dpy, id) && wm_running(dpy, screenno))
211			id = None;
212
213		}
214	    }
215	}
216    }
217
218    if (id != None) {
219	printf ("%s:  killing creator of resource 0x%lx\n", ProgramName, id);
220	XSync (dpy, 0);			/* give xterm a chance */
221	XKillClient (dpy, id);
222	XSync (dpy, 0);
223    }
224
225    Exit (0);
226    /*NOTREACHED*/
227    return 0;
228}
229
230static int
231parse_button(char *s, int *buttonp)
232{
233    register char *cp;
234
235    /* lower case name */
236    for (cp = s; *cp; cp++) {
237	if (isascii (*cp) && isupper (*cp)) {
238#ifdef _tolower
239	    *cp = _tolower (*cp);
240#else
241	    *cp = tolower (*cp);
242#endif /* _tolower */
243	}
244    }
245
246    if (strcmp (s, "any") == 0) {
247	*buttonp = SelectButtonAny;
248	return (1);
249    }
250
251    /* check for non-numeric input */
252    for (cp = s; *cp; cp++) {
253	if (!(isascii (*cp) && isdigit (*cp))) return (0);  /* bogus name */
254    }
255
256    *buttonp = atoi (s);
257    return (1);
258}
259
260
261static XID
262parse_id(char *s)
263{
264    XID retval = None;
265    char *fmt = "%ld";			/* since XID is long */
266
267    if (s) {
268	if (*s == '0') s++, fmt = "%lo";
269	if (*s == 'x' || *s == 'X') s++, fmt = "%lx";
270	sscanf (s, fmt, &retval);
271    }
272    return (retval);
273}
274
275static XID
276get_window_id(Display *dpy, int screen, int button, char *msg)
277{
278    Cursor cursor;		/* cursor to use when selecting */
279    Window root;		/* the current root */
280    Window retwin = None;	/* the window that got selected */
281    int retbutton = -1;		/* button used to select window */
282    int pressed = 0;		/* count of number of buttons pressed */
283
284#define MASK (ButtonPressMask | ButtonReleaseMask)
285
286    root = RootWindow (dpy, screen);
287    cursor = XCreateFontCursor (dpy, XC_pirate);
288    if (cursor == None) {
289	fprintf (stderr, "%s:  unable to create selection cursor\n",
290		 ProgramName);
291	Exit (1);
292    }
293
294    printf ("Select %s with ", msg);
295    if (button == -1)
296      printf ("any button");
297    else
298      printf ("button %d", button);
299    printf ("....\n");
300    XSync (dpy, 0);			/* give xterm a chance */
301
302    if (XGrabPointer (dpy, root, False, MASK, GrabModeSync, GrabModeAsync,
303    		      None, cursor, CurrentTime) != GrabSuccess) {
304	fprintf (stderr, "%s:  unable to grab cursor\n", ProgramName);
305	Exit (1);
306    }
307
308    /* from dsimple.c in xwininfo */
309    while (retwin == None || pressed != 0) {
310	XEvent event;
311
312	XAllowEvents (dpy, SyncPointer, CurrentTime);
313	XWindowEvent (dpy, root, MASK, &event);
314	switch (event.type) {
315	  case ButtonPress:
316	    if (retwin == None) {
317		retbutton = event.xbutton.button;
318		retwin = ((event.xbutton.subwindow != None) ?
319			  event.xbutton.subwindow : root);
320	    }
321	    pressed++;
322	    continue;
323	  case ButtonRelease:
324	    if (pressed > 0) pressed--;
325	    continue;
326	}					/* end switch */
327    }						/* end for */
328
329    XUngrabPointer (dpy, CurrentTime);
330    XFreeCursor (dpy, cursor);
331    XSync (dpy, 0);
332
333    return ((button == -1 || retbutton == button) ? retwin : None);
334}
335
336
337static int
338catch_window_errors(Display *dpy, XErrorEvent *ev)
339{
340    return 0;
341}
342
343static int
344kill_all_windows(Display *dpy, int screenno, Bool top)
345{
346    Window root = RootWindow (dpy, screenno);
347    Window dummywindow;
348    Window *children;
349    unsigned int nchildren;
350    unsigned int i;
351    XWindowAttributes attr;
352
353    XSync (dpy, 0);
354    XSetErrorHandler (catch_window_errors);
355
356    nchildren = 0;
357    XQueryTree (dpy, root, &dummywindow, &dummywindow, &children, &nchildren);
358    if (!top) {
359	for (i = 0; i < nchildren; i++) {
360	    if (XGetWindowAttributes(dpy, children[i], &attr) &&
361		(attr.map_state == IsViewable))
362		children[i] = XmuClientWindow(dpy, children[i]);
363	    else
364		children[i] = 0;
365	}
366    }
367    for (i = 0; i < nchildren; i++) {
368	if (children[i])
369	    XKillClient (dpy, children[i]);
370    }
371    XFree ((char *)children);
372
373    XSync (dpy, 0);
374    XSetErrorHandler (NULL);		/* pretty stupid way to do things... */
375
376    return 0;
377}
378
379/*
380 * ask the user to press in the root with each button in succession
381 */
382static int
383verify_okay_to_kill(Display *dpy, int screenno)
384{
385    unsigned char pointer_map[256];
386    int count = XGetPointerMapping (dpy, pointer_map, 256);
387    int i;
388    int button;
389    static char *msg = "the root window";
390    Window root = RootWindow (dpy, screenno);
391    int okay = 0;
392
393    for (i = 0; i < count; i++) {
394	button = (int) pointer_map[i];
395	if (button == 0) continue;	/* disabled */
396	if (get_window_id (dpy, screenno, button, msg) != root) {
397	    okay = 0;
398	    break;
399	}
400	okay++;				/* must have at least one button */
401    }
402    if (okay) {
403	return 1;
404    } else {
405	printf ("Aborting.\n");
406	return 0;
407    }
408}
409
410/* Return True if the property WM_STATE is set on the window, otherwise
411 * return False.
412 */
413static Bool
414wm_state_set(Display *dpy, Window win)
415{
416    Atom wm_state;
417    Atom actual_type;
418    int success;
419    int actual_format;
420    unsigned long nitems, remaining;
421    unsigned char* prop = NULL;
422
423    wm_state = XInternAtom(dpy, "WM_STATE", True);
424    if (wm_state == None) return False;
425    success = XGetWindowProperty(dpy, win, wm_state, 0L, 0L, False,
426				 AnyPropertyType, &actual_type, &actual_format,
427				 &nitems, &remaining, &prop);
428    if (prop) XFree((char *) prop);
429    return (success == Success && actual_type != None && actual_format);
430}
431
432/* Using a heuristic method, return True if a window manager is running,
433 * otherwise, return False.
434 */
435
436static Bool
437wm_running(Display *dpy, int screenno)
438{
439    XWindowAttributes	xwa;
440    Status		status;
441
442    status = XGetWindowAttributes(dpy, RootWindow(dpy, screenno), &xwa);
443    return (status &&
444	    ((xwa.all_event_masks & SubstructureRedirectMask) ||
445	     (xwa.all_event_masks & SubstructureNotifyMask)));
446}
447