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