xkill.c revision 4929c8f8
14fb28925Smrg/*
24fb28925Smrg
34fb28925SmrgCopyright 1988, 1998  The Open Group
44fb28925Smrg
54fb28925SmrgPermission to use, copy, modify, distribute, and sell this software and its
64fb28925Smrgdocumentation for any purpose is hereby granted without fee, provided that
74fb28925Smrgthe above copyright notice appear in all copies and that both that
84fb28925Smrgcopyright notice and this permission notice appear in supporting
94fb28925Smrgdocumentation.
104fb28925Smrg
114fb28925SmrgThe above copyright notice and this permission notice shall be included
124fb28925Smrgin all copies or substantial portions of the Software.
134fb28925Smrg
144fb28925SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
154fb28925SmrgOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
164fb28925SmrgMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
174fb28925SmrgIN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
184fb28925SmrgOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
194fb28925SmrgARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
204fb28925SmrgOTHER DEALINGS IN THE SOFTWARE.
214fb28925Smrg
224fb28925SmrgExcept as contained in this notice, the name of The Open Group shall
234fb28925Smrgnot be used in advertising or otherwise to promote the sale, use or
244fb28925Smrgother dealings in this Software without prior written authorization
254fb28925Smrgfrom The Open Group.
264fb28925Smrg
274fb28925Smrg*/
284fb28925Smrg
294fb28925Smrg/*
304fb28925Smrg * xkill - simple program for destroying unwanted clients
314fb28925Smrg * Author:  Jim Fulton, MIT X Consortium; Dana Chee, Bellcore
324fb28925Smrg */
334fb28925Smrg
344fb28925Smrg/*
354fb28925Smrg * Warning, this is a very dangerous client....
364fb28925Smrg */
374fb28925Smrg
38df58931aSmrg#ifdef HAVE_CONFIG_H
39df58931aSmrg# include "config.h"
40df58931aSmrg#endif
41df58931aSmrg
424fb28925Smrg#include <stdio.h>
434fb28925Smrg#include <stdlib.h>
444fb28925Smrg#include <ctype.h>
454fb28925Smrg
464fb28925Smrg#include <X11/Xos.h>
474fb28925Smrg#include <X11/Xlib.h>
484fb28925Smrg#include <X11/cursorfont.h>
494fb28925Smrg#include <X11/Xproto.h>
504fb28925Smrg
514fb28925Smrg#include <X11/Xmu/WinUtil.h>
524fb28925Smrg
53b66d1acbSmrgstatic char *ProgramName;
544fb28925Smrg
554fb28925Smrg#define SelectButtonAny (-1)
564fb28925Smrg#define SelectButtonFirst (-2)
574fb28925Smrg
584929c8f8Smrgstatic int parse_button ( const char *s, int *buttonp );
59df58931aSmrgstatic XID get_window_id ( Display *dpy, int screen, int button, const char *msg );
604fb28925Smrgstatic int catch_window_errors ( Display *dpy, XErrorEvent *ev );
614fb28925Smrgstatic int kill_all_windows ( Display *dpy, int screenno, Bool top );
624fb28925Smrgstatic int verify_okay_to_kill ( Display *dpy, int screenno );
634fb28925Smrgstatic Bool wm_state_set ( Display *dpy, Window win );
644fb28925Smrgstatic Bool wm_running ( Display *dpy, int screenno );
654fb28925Smrg
66df58931aSmrgstatic void _X_NORETURN
67df58931aSmrgExit(int code, Display *dpy)
684fb28925Smrg{
694fb28925Smrg    if (dpy) {
704fb28925Smrg	XCloseDisplay (dpy);
714fb28925Smrg    }
724fb28925Smrg    exit (code);
734fb28925Smrg}
744fb28925Smrg
75df58931aSmrgstatic void _X_NORETURN
764929c8f8Smrgusage(const char *errmsg)
774fb28925Smrg{
78df58931aSmrg    const char *options =
79df58931aSmrg"where options include:\n"
80df58931aSmrg"    -display displayname    X server to contact\n"
81df58931aSmrg"    -id resource            resource whose client is to be killed\n"
82df58931aSmrg"    -frame                  don't ignore window manager frames\n"
83df58931aSmrg"    -button number          specific button to be pressed to select window\n"
84df58931aSmrg"    -all                    kill all clients with top level windows\n"
85df58931aSmrg"    -version                print version and exit\n"
86df58931aSmrg"\n";
87df58931aSmrg
884929c8f8Smrg    if (errmsg != NULL)
894929c8f8Smrg        fprintf (stderr, "%s: %s\n\n", ProgramName, errmsg);
904929c8f8Smrg
91df58931aSmrg    fprintf (stderr, "usage:  %s [-option ...]\n%s",
92df58931aSmrg	     ProgramName, options);
93df58931aSmrg    Exit (1, NULL);
944fb28925Smrg}
954fb28925Smrg
964fb28925Smrgint
974fb28925Smrgmain(int argc, char *argv[])
984fb28925Smrg{
994fb28925Smrg    int i;				/* iterator, temp variable */
100df58931aSmrg    Display *dpy = NULL;
1014fb28925Smrg    char *displayname = NULL;		/* name of server to contact */
1024fb28925Smrg    int screenno;			/* screen number of dpy */
1034fb28925Smrg    XID id = None;			/* resource to kill */
1044fb28925Smrg    char *button_name = NULL;		/* name of button for window select */
1054fb28925Smrg    int button;				/* button number or negative for all */
1064fb28925Smrg    Bool kill_all = False;
1074fb28925Smrg    Bool top = False;
1084fb28925Smrg
1094fb28925Smrg    ProgramName = argv[0];
110b66d1acbSmrg    button = SelectButtonFirst;
1114fb28925Smrg
1124fb28925Smrg    for (i = 1; i < argc; i++) {
1134fb28925Smrg	char *arg = argv[i];
1144fb28925Smrg
1154fb28925Smrg	if (arg[0] == '-') {
1164fb28925Smrg	    switch (arg[1]) {
1174fb28925Smrg	      case 'd':			/* -display displayname */
1184929c8f8Smrg		if (++i >= argc) usage ("-display requires an argument");
1194fb28925Smrg		displayname = argv[i];
1204fb28925Smrg		continue;
1214fb28925Smrg	      case 'i':			/* -id resourceid */
1224929c8f8Smrg		if (++i >= argc) usage ("-id requires an argument");
123df58931aSmrg		id = strtoul (argv[i], NULL, 0);
124df58931aSmrg		if (id == 0 || id >= 0xFFFFFFFFU) {
125df58931aSmrg		    fprintf (stderr, "%s:  invalid id \"%s\"\n",
126df58931aSmrg			     ProgramName, argv[i]);
127df58931aSmrg		    Exit (1, dpy);
128df58931aSmrg		}
1294fb28925Smrg		continue;
1304fb28925Smrg	      case 'b':			/* -button number */
1314929c8f8Smrg		if (++i >= argc) usage ("-button requires an argument");
1324fb28925Smrg		button_name = argv[i];
1334fb28925Smrg		continue;
1344fb28925Smrg	      case 'f':			/* -frame */
1354fb28925Smrg		top = True;
1364fb28925Smrg		continue;
1374fb28925Smrg	      case 'a':			/* -all */
1384fb28925Smrg		kill_all = True;
1394fb28925Smrg		continue;
140df58931aSmrg              case 'v':
141df58931aSmrg                puts(PACKAGE_STRING);
142df58931aSmrg                exit(0);
1434fb28925Smrg	      default:
1444929c8f8Smrg                fprintf(stderr, "%s: unrecognized argument %s\n\n",
1454929c8f8Smrg                        ProgramName, arg);
1464929c8f8Smrg		usage (NULL);
1474fb28925Smrg	    }
1484fb28925Smrg	} else {
1494929c8f8Smrg            fprintf(stderr, "%s: unrecognized argument %s\n\n",
1504929c8f8Smrg                    ProgramName, arg);
1514929c8f8Smrg            usage (NULL);
1524fb28925Smrg	}
1534fb28925Smrg    }					/* end for */
1544fb28925Smrg
1554fb28925Smrg    dpy = XOpenDisplay (displayname);
1564fb28925Smrg    if (!dpy) {
1574fb28925Smrg	fprintf (stderr, "%s:  unable to open display \"%s\"\n",
1584fb28925Smrg		 ProgramName, XDisplayName (displayname));
159df58931aSmrg	Exit (1, dpy);
1604fb28925Smrg    }
1614fb28925Smrg    screenno = DefaultScreen (dpy);
1624fb28925Smrg
1634fb28925Smrg    if (kill_all) {
1644fb28925Smrg	if (verify_okay_to_kill (dpy, screenno))
1654fb28925Smrg	  kill_all_windows (dpy, screenno, top);
166df58931aSmrg	Exit (0, dpy);
1674fb28925Smrg    }
1684fb28925Smrg
1694fb28925Smrg    /*
1704fb28925Smrg     * if no id was given, we need to choose a window
1714fb28925Smrg     */
1724fb28925Smrg
1734fb28925Smrg    if (id == None) {
1744fb28925Smrg	if (!button_name)
1754fb28925Smrg	    button_name = XGetDefault (dpy, ProgramName, "Button");
1764fb28925Smrg
177b66d1acbSmrg	if (button_name && !parse_button (button_name, &button)) {
1784fb28925Smrg	    fprintf (stderr, "%s:  invalid button specification \"%s\"\n",
1794fb28925Smrg		     ProgramName, button_name);
180df58931aSmrg	    Exit (1, dpy);
1814fb28925Smrg	}
1824fb28925Smrg
1834fb28925Smrg	if (button >= 0 || button == SelectButtonFirst) {
1844fb28925Smrg	    unsigned char pointer_map[256];	 /* 8 bits of pointer num */
1854fb28925Smrg	    int count, j;
1864fb28925Smrg	    unsigned int ub = (unsigned int) button;
1874fb28925Smrg
1884fb28925Smrg
1894fb28925Smrg	    count = XGetPointerMapping (dpy, pointer_map, 256);
1904fb28925Smrg	    if (count <= 0) {
1914fb28925Smrg		fprintf (stderr,
1924fb28925Smrg			 "%s:  no pointer mapping, can't select window\n",
1934fb28925Smrg			 ProgramName);
194df58931aSmrg		Exit (1, dpy);
1954fb28925Smrg	    }
1964fb28925Smrg
1974fb28925Smrg	    if (button >= 0) {			/* check button */
1984fb28925Smrg		for (j = 0; j < count; j++) {
1994fb28925Smrg		    if (ub == (unsigned int) pointer_map[j]) break;
2004fb28925Smrg		}
2014fb28925Smrg		if (j == count) {
2024fb28925Smrg		    fprintf (stderr,
2034fb28925Smrg	 "%s:  no button number %u in pointer map, can't select window\n",
2044fb28925Smrg			     ProgramName, ub);
205df58931aSmrg		    Exit (1, dpy);
2064fb28925Smrg	        }
2074fb28925Smrg	    } else {				/* get first entry */
2084fb28925Smrg		button = (int) ((unsigned int) pointer_map[0]);
2094fb28925Smrg	    }
2104fb28925Smrg	}
2114fb28925Smrg	if ((id = get_window_id (dpy, screenno, button,
2124fb28925Smrg				"the window whose client you wish to kill"))) {
2134fb28925Smrg	    if (id == RootWindow(dpy,screenno)) id = None;
2144fb28925Smrg	    else if (!top) {
2154fb28925Smrg		XID indicated = id;
2164fb28925Smrg		if ((id = XmuClientWindow(dpy, indicated)) == indicated) {
2174fb28925Smrg
2184fb28925Smrg		    /* Try not to kill the window manager when the user
2194fb28925Smrg		     * indicates an icon to xkill.
2204fb28925Smrg		     */
2214fb28925Smrg
2224fb28925Smrg		    if (! wm_state_set(dpy, id) && wm_running(dpy, screenno))
2234fb28925Smrg			id = None;
2244fb28925Smrg
2254fb28925Smrg		}
2264fb28925Smrg	    }
2274fb28925Smrg	}
2284fb28925Smrg    }
2294fb28925Smrg
2304fb28925Smrg    if (id != None) {
2314fb28925Smrg	printf ("%s:  killing creator of resource 0x%lx\n", ProgramName, id);
2324fb28925Smrg	XSync (dpy, 0);			/* give xterm a chance */
2334fb28925Smrg	XKillClient (dpy, id);
2344fb28925Smrg	XSync (dpy, 0);
2354fb28925Smrg    }
2364fb28925Smrg
237df58931aSmrg    Exit (0, dpy);
2384fb28925Smrg    /*NOTREACHED*/
2394fb28925Smrg    return 0;
2404fb28925Smrg}
2414fb28925Smrg
2424fb28925Smrgstatic int
2434929c8f8Smrgparse_button(const char *s, int *buttonp)
2444fb28925Smrg{
2454929c8f8Smrg    const char *cp;
2464fb28925Smrg
2474929c8f8Smrg    if (strcasecmp (s, "any") == 0) {
2484fb28925Smrg	*buttonp = SelectButtonAny;
2494fb28925Smrg	return (1);
2504fb28925Smrg    }
2514fb28925Smrg
2524fb28925Smrg    /* check for non-numeric input */
2534fb28925Smrg    for (cp = s; *cp; cp++) {
2544fb28925Smrg	if (!(isascii (*cp) && isdigit (*cp))) return (0);  /* bogus name */
2554fb28925Smrg    }
2564fb28925Smrg
2574fb28925Smrg    *buttonp = atoi (s);
2584fb28925Smrg    return (1);
2594fb28925Smrg}
2604fb28925Smrg
2614fb28925Smrgstatic XID
262df58931aSmrgget_window_id(Display *dpy, int screen, int button, const char *msg)
2634fb28925Smrg{
2644fb28925Smrg    Cursor cursor;		/* cursor to use when selecting */
2654fb28925Smrg    Window root;		/* the current root */
2664fb28925Smrg    Window retwin = None;	/* the window that got selected */
2674fb28925Smrg    int retbutton = -1;		/* button used to select window */
2684fb28925Smrg    int pressed = 0;		/* count of number of buttons pressed */
2694fb28925Smrg
2704fb28925Smrg#define MASK (ButtonPressMask | ButtonReleaseMask)
2714fb28925Smrg
2724fb28925Smrg    root = RootWindow (dpy, screen);
2734fb28925Smrg    cursor = XCreateFontCursor (dpy, XC_pirate);
2744fb28925Smrg    if (cursor == None) {
2754fb28925Smrg	fprintf (stderr, "%s:  unable to create selection cursor\n",
2764fb28925Smrg		 ProgramName);
277df58931aSmrg	Exit (1, dpy);
2784fb28925Smrg    }
2794fb28925Smrg
2804fb28925Smrg    printf ("Select %s with ", msg);
2814fb28925Smrg    if (button == -1)
2824fb28925Smrg      printf ("any button");
2834fb28925Smrg    else
2844fb28925Smrg      printf ("button %d", button);
2854fb28925Smrg    printf ("....\n");
2864fb28925Smrg    XSync (dpy, 0);			/* give xterm a chance */
2874fb28925Smrg
2884fb28925Smrg    if (XGrabPointer (dpy, root, False, MASK, GrabModeSync, GrabModeAsync,
2894fb28925Smrg    		      None, cursor, CurrentTime) != GrabSuccess) {
2904fb28925Smrg	fprintf (stderr, "%s:  unable to grab cursor\n", ProgramName);
291df58931aSmrg	Exit (1, dpy);
2924fb28925Smrg    }
2934fb28925Smrg
2944fb28925Smrg    /* from dsimple.c in xwininfo */
2954fb28925Smrg    while (retwin == None || pressed != 0) {
2964fb28925Smrg	XEvent event;
2974fb28925Smrg
2984fb28925Smrg	XAllowEvents (dpy, SyncPointer, CurrentTime);
2994fb28925Smrg	XWindowEvent (dpy, root, MASK, &event);
3004fb28925Smrg	switch (event.type) {
3014fb28925Smrg	  case ButtonPress:
3024fb28925Smrg	    if (retwin == None) {
3034fb28925Smrg		retbutton = event.xbutton.button;
3044fb28925Smrg		retwin = ((event.xbutton.subwindow != None) ?
3054fb28925Smrg			  event.xbutton.subwindow : root);
3064fb28925Smrg	    }
3074fb28925Smrg	    pressed++;
3084fb28925Smrg	    continue;
3094fb28925Smrg	  case ButtonRelease:
3104fb28925Smrg	    if (pressed > 0) pressed--;
3114fb28925Smrg	    continue;
3124fb28925Smrg	}					/* end switch */
3134fb28925Smrg    }						/* end for */
3144fb28925Smrg
3154fb28925Smrg    XUngrabPointer (dpy, CurrentTime);
3164fb28925Smrg    XFreeCursor (dpy, cursor);
3174fb28925Smrg    XSync (dpy, 0);
3184fb28925Smrg
3194fb28925Smrg    return ((button == -1 || retbutton == button) ? retwin : None);
3204fb28925Smrg}
3214fb28925Smrg
3224fb28925Smrg
3234fb28925Smrgstatic int
324df58931aSmrgcatch_window_errors(_X_UNUSED Display *dpy, _X_UNUSED XErrorEvent *ev)
3254fb28925Smrg{
3264fb28925Smrg    return 0;
3274fb28925Smrg}
3284fb28925Smrg
3294fb28925Smrgstatic int
3304fb28925Smrgkill_all_windows(Display *dpy, int screenno, Bool top)
3314fb28925Smrg{
3324fb28925Smrg    Window root = RootWindow (dpy, screenno);
3334fb28925Smrg    Window dummywindow;
3344fb28925Smrg    Window *children;
3354fb28925Smrg    unsigned int nchildren;
3364fb28925Smrg    unsigned int i;
3374fb28925Smrg    XWindowAttributes attr;
3384fb28925Smrg
3394fb28925Smrg    XSync (dpy, 0);
3404fb28925Smrg    XSetErrorHandler (catch_window_errors);
3414fb28925Smrg
3424fb28925Smrg    nchildren = 0;
3434fb28925Smrg    XQueryTree (dpy, root, &dummywindow, &dummywindow, &children, &nchildren);
3444fb28925Smrg    if (!top) {
3454fb28925Smrg	for (i = 0; i < nchildren; i++) {
3464fb28925Smrg	    if (XGetWindowAttributes(dpy, children[i], &attr) &&
3474fb28925Smrg		(attr.map_state == IsViewable))
3484fb28925Smrg		children[i] = XmuClientWindow(dpy, children[i]);
3494fb28925Smrg	    else
3504fb28925Smrg		children[i] = 0;
3514fb28925Smrg	}
3524fb28925Smrg    }
3534fb28925Smrg    for (i = 0; i < nchildren; i++) {
3544fb28925Smrg	if (children[i])
3554fb28925Smrg	    XKillClient (dpy, children[i]);
3564fb28925Smrg    }
3574fb28925Smrg    XFree ((char *)children);
3584fb28925Smrg
3594fb28925Smrg    XSync (dpy, 0);
3604fb28925Smrg    XSetErrorHandler (NULL);		/* pretty stupid way to do things... */
3614fb28925Smrg
3624fb28925Smrg    return 0;
3634fb28925Smrg}
3644fb28925Smrg
3654fb28925Smrg/*
3664fb28925Smrg * ask the user to press in the root with each button in succession
3674fb28925Smrg */
3684fb28925Smrgstatic int
3694fb28925Smrgverify_okay_to_kill(Display *dpy, int screenno)
3704fb28925Smrg{
3714fb28925Smrg    unsigned char pointer_map[256];
3724fb28925Smrg    int count = XGetPointerMapping (dpy, pointer_map, 256);
3734fb28925Smrg    int i;
3744fb28925Smrg    int button;
375df58931aSmrg    const char *msg = "the root window";
3764fb28925Smrg    Window root = RootWindow (dpy, screenno);
3774fb28925Smrg    int okay = 0;
3784fb28925Smrg
3794fb28925Smrg    for (i = 0; i < count; i++) {
3804fb28925Smrg	button = (int) pointer_map[i];
3814fb28925Smrg	if (button == 0) continue;	/* disabled */
3824fb28925Smrg	if (get_window_id (dpy, screenno, button, msg) != root) {
3834fb28925Smrg	    okay = 0;
3844fb28925Smrg	    break;
3854fb28925Smrg	}
3864fb28925Smrg	okay++;				/* must have at least one button */
3874fb28925Smrg    }
3884fb28925Smrg    if (okay) {
3894fb28925Smrg	return 1;
3904fb28925Smrg    } else {
3914fb28925Smrg	printf ("Aborting.\n");
3924fb28925Smrg	return 0;
3934fb28925Smrg    }
3944fb28925Smrg}
3954fb28925Smrg
3964fb28925Smrg/* Return True if the property WM_STATE is set on the window, otherwise
3974fb28925Smrg * return False.
3984fb28925Smrg */
3994fb28925Smrgstatic Bool
4004fb28925Smrgwm_state_set(Display *dpy, Window win)
4014fb28925Smrg{
4024fb28925Smrg    Atom wm_state;
4034fb28925Smrg    Atom actual_type;
4044fb28925Smrg    int success;
4054fb28925Smrg    int actual_format;
4064fb28925Smrg    unsigned long nitems, remaining;
4074fb28925Smrg    unsigned char* prop = NULL;
4084fb28925Smrg
4094fb28925Smrg    wm_state = XInternAtom(dpy, "WM_STATE", True);
4104fb28925Smrg    if (wm_state == None) return False;
4114fb28925Smrg    success = XGetWindowProperty(dpy, win, wm_state, 0L, 0L, False,
4124fb28925Smrg				 AnyPropertyType, &actual_type, &actual_format,
4134fb28925Smrg				 &nitems, &remaining, &prop);
4144fb28925Smrg    if (prop) XFree((char *) prop);
4154fb28925Smrg    return (success == Success && actual_type != None && actual_format);
4164fb28925Smrg}
4174fb28925Smrg
4184fb28925Smrg/* Using a heuristic method, return True if a window manager is running,
4194fb28925Smrg * otherwise, return False.
4204fb28925Smrg */
4214fb28925Smrg
4224fb28925Smrgstatic Bool
4234fb28925Smrgwm_running(Display *dpy, int screenno)
4244fb28925Smrg{
4254fb28925Smrg    XWindowAttributes	xwa;
4264fb28925Smrg    Status		status;
4274fb28925Smrg
4284fb28925Smrg    status = XGetWindowAttributes(dpy, RootWindow(dpy, screenno), &xwa);
4294fb28925Smrg    return (status &&
4304fb28925Smrg	    ((xwa.all_event_masks & SubstructureRedirectMask) ||
4314fb28925Smrg	     (xwa.all_event_masks & SubstructureNotifyMask)));
4324fb28925Smrg}
433