xkill.c revision 4fb28925
14fb28925Smrg/* $Xorg: xkill.c,v 1.5 2001/02/09 02:05:54 xorgcvs Exp $ */
24fb28925Smrg/*
34fb28925Smrg
44fb28925SmrgCopyright 1988, 1998  The Open Group
54fb28925Smrg
64fb28925SmrgPermission to use, copy, modify, distribute, and sell this software and its
74fb28925Smrgdocumentation for any purpose is hereby granted without fee, provided that
84fb28925Smrgthe above copyright notice appear in all copies and that both that
94fb28925Smrgcopyright notice and this permission notice appear in supporting
104fb28925Smrgdocumentation.
114fb28925Smrg
124fb28925SmrgThe above copyright notice and this permission notice shall be included
134fb28925Smrgin all copies or substantial portions of the Software.
144fb28925Smrg
154fb28925SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
164fb28925SmrgOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
174fb28925SmrgMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
184fb28925SmrgIN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
194fb28925SmrgOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
204fb28925SmrgARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
214fb28925SmrgOTHER DEALINGS IN THE SOFTWARE.
224fb28925Smrg
234fb28925SmrgExcept as contained in this notice, the name of The Open Group shall
244fb28925Smrgnot be used in advertising or otherwise to promote the sale, use or
254fb28925Smrgother dealings in this Software without prior written authorization
264fb28925Smrgfrom The Open Group.
274fb28925Smrg
284fb28925Smrg*/
294fb28925Smrg/* $XFree86: xc/programs/xkill/xkill.c,v 1.5 2001/04/01 14:00:22 tsi Exp $ */
304fb28925Smrg
314fb28925Smrg/*
324fb28925Smrg * xkill - simple program for destroying unwanted clients
334fb28925Smrg * Author:  Jim Fulton, MIT X Consortium; Dana Chee, Bellcore
344fb28925Smrg */
354fb28925Smrg
364fb28925Smrg/*
374fb28925Smrg * Warning, this is a very dangerous client....
384fb28925Smrg */
394fb28925Smrg
404fb28925Smrg#include <stdio.h>
414fb28925Smrg#include <stdlib.h>
424fb28925Smrg#include <ctype.h>
434fb28925Smrg
444fb28925Smrg#include <X11/Xos.h>
454fb28925Smrg#include <X11/Xlib.h>
464fb28925Smrg#include <X11/cursorfont.h>
474fb28925Smrg#include <X11/Xproto.h>
484fb28925Smrg
494fb28925Smrg#include <X11/Xmu/WinUtil.h>
504fb28925Smrg
514fb28925SmrgDisplay *dpy = NULL;
524fb28925Smrgchar *ProgramName;
534fb28925Smrg
544fb28925Smrg#define SelectButtonAny (-1)
554fb28925Smrg#define SelectButtonFirst (-2)
564fb28925Smrg
574fb28925Smrgstatic int parse_button ( char *s, int *buttonp );
584fb28925Smrgstatic XID parse_id ( char *s );
594fb28925Smrgstatic XID get_window_id ( Display *dpy, int screen, int button, 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
664fb28925Smrgstatic void
674fb28925SmrgExit(int code)
684fb28925Smrg{
694fb28925Smrg    if (dpy) {
704fb28925Smrg	XCloseDisplay (dpy);
714fb28925Smrg    }
724fb28925Smrg    exit (code);
734fb28925Smrg}
744fb28925Smrg
754fb28925Smrgstatic void
764fb28925Smrgusage(void)
774fb28925Smrg{
784fb28925Smrg    static char *options[] = {
794fb28925Smrg"where options include:",
804fb28925Smrg"    -display displayname    X server to contact",
814fb28925Smrg"    -id resource            resource whose client is to be killed",
824fb28925Smrg"    -frame                  don't ignore window manager frames",
834fb28925Smrg"    -button number          specific button to be pressed to select window",
844fb28925Smrg"    -all                    kill all clients with top level windows",
854fb28925Smrg"",
864fb28925SmrgNULL};
874fb28925Smrg    char **cpp;
884fb28925Smrg
894fb28925Smrg    fprintf (stderr, "usage:  %s [-option ...]\n",
904fb28925Smrg	     ProgramName);
914fb28925Smrg    for (cpp = options; *cpp; cpp++) {
924fb28925Smrg	fprintf (stderr, "%s\n", *cpp);
934fb28925Smrg    }
944fb28925Smrg    Exit (1);
954fb28925Smrg}
964fb28925Smrg
974fb28925Smrgint
984fb28925Smrgmain(int argc, char *argv[])
994fb28925Smrg{
1004fb28925Smrg    int i;				/* iterator, temp variable */
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];
1104fb28925Smrg
1114fb28925Smrg    for (i = 1; i < argc; i++) {
1124fb28925Smrg	char *arg = argv[i];
1134fb28925Smrg
1144fb28925Smrg	if (arg[0] == '-') {
1154fb28925Smrg	    switch (arg[1]) {
1164fb28925Smrg	      case 'd':			/* -display displayname */
1174fb28925Smrg		if (++i >= argc) usage ();
1184fb28925Smrg		displayname = argv[i];
1194fb28925Smrg		continue;
1204fb28925Smrg	      case 'i':			/* -id resourceid */
1214fb28925Smrg		if (++i >= argc) usage ();
1224fb28925Smrg		id = parse_id (argv[i]);
1234fb28925Smrg		continue;
1244fb28925Smrg	      case 'b':			/* -button number */
1254fb28925Smrg		if (++i >= argc) usage ();
1264fb28925Smrg		button_name = argv[i];
1274fb28925Smrg		continue;
1284fb28925Smrg	      case 'f':			/* -frame */
1294fb28925Smrg		top = True;
1304fb28925Smrg		continue;
1314fb28925Smrg	      case 'a':			/* -all */
1324fb28925Smrg		kill_all = True;
1334fb28925Smrg		continue;
1344fb28925Smrg	      default:
1354fb28925Smrg		usage ();
1364fb28925Smrg	    }
1374fb28925Smrg	} else {
1384fb28925Smrg	    usage ();
1394fb28925Smrg	}
1404fb28925Smrg    }					/* end for */
1414fb28925Smrg
1424fb28925Smrg    dpy = XOpenDisplay (displayname);
1434fb28925Smrg    if (!dpy) {
1444fb28925Smrg	fprintf (stderr, "%s:  unable to open display \"%s\"\n",
1454fb28925Smrg		 ProgramName, XDisplayName (displayname));
1464fb28925Smrg	Exit (1);
1474fb28925Smrg    }
1484fb28925Smrg    screenno = DefaultScreen (dpy);
1494fb28925Smrg
1504fb28925Smrg    if (kill_all) {
1514fb28925Smrg	if (verify_okay_to_kill (dpy, screenno))
1524fb28925Smrg	  kill_all_windows (dpy, screenno, top);
1534fb28925Smrg	Exit (0);
1544fb28925Smrg    }
1554fb28925Smrg
1564fb28925Smrg    /*
1574fb28925Smrg     * if no id was given, we need to choose a window
1584fb28925Smrg     */
1594fb28925Smrg
1604fb28925Smrg    if (id == None) {
1614fb28925Smrg	if (!button_name)
1624fb28925Smrg	    button_name = XGetDefault (dpy, ProgramName, "Button");
1634fb28925Smrg
1644fb28925Smrg	if (!button_name)
1654fb28925Smrg	    button = SelectButtonFirst;
1664fb28925Smrg	else if (!parse_button (button_name, &button)) {
1674fb28925Smrg	    fprintf (stderr, "%s:  invalid button specification \"%s\"\n",
1684fb28925Smrg		     ProgramName, button_name);
1694fb28925Smrg	    Exit (1);
1704fb28925Smrg	}
1714fb28925Smrg
1724fb28925Smrg	if (button >= 0 || button == SelectButtonFirst) {
1734fb28925Smrg	    unsigned char pointer_map[256];	 /* 8 bits of pointer num */
1744fb28925Smrg	    int count, j;
1754fb28925Smrg	    unsigned int ub = (unsigned int) button;
1764fb28925Smrg
1774fb28925Smrg
1784fb28925Smrg	    count = XGetPointerMapping (dpy, pointer_map, 256);
1794fb28925Smrg	    if (count <= 0) {
1804fb28925Smrg		fprintf (stderr,
1814fb28925Smrg			 "%s:  no pointer mapping, can't select window\n",
1824fb28925Smrg			 ProgramName);
1834fb28925Smrg		Exit (1);
1844fb28925Smrg	    }
1854fb28925Smrg
1864fb28925Smrg	    if (button >= 0) {			/* check button */
1874fb28925Smrg		for (j = 0; j < count; j++) {
1884fb28925Smrg		    if (ub == (unsigned int) pointer_map[j]) break;
1894fb28925Smrg		}
1904fb28925Smrg		if (j == count) {
1914fb28925Smrg		    fprintf (stderr,
1924fb28925Smrg	 "%s:  no button number %u in pointer map, can't select window\n",
1934fb28925Smrg			     ProgramName, ub);
1944fb28925Smrg		    Exit (1);
1954fb28925Smrg	        }
1964fb28925Smrg	    } else {				/* get first entry */
1974fb28925Smrg		button = (int) ((unsigned int) pointer_map[0]);
1984fb28925Smrg	    }
1994fb28925Smrg	}
2004fb28925Smrg	if ((id = get_window_id (dpy, screenno, button,
2014fb28925Smrg				"the window whose client you wish to kill"))) {
2024fb28925Smrg	    if (id == RootWindow(dpy,screenno)) id = None;
2034fb28925Smrg	    else if (!top) {
2044fb28925Smrg		XID indicated = id;
2054fb28925Smrg		if ((id = XmuClientWindow(dpy, indicated)) == indicated) {
2064fb28925Smrg
2074fb28925Smrg		    /* Try not to kill the window manager when the user
2084fb28925Smrg		     * indicates an icon to xkill.
2094fb28925Smrg		     */
2104fb28925Smrg
2114fb28925Smrg		    if (! wm_state_set(dpy, id) && wm_running(dpy, screenno))
2124fb28925Smrg			id = None;
2134fb28925Smrg
2144fb28925Smrg		}
2154fb28925Smrg	    }
2164fb28925Smrg	}
2174fb28925Smrg    }
2184fb28925Smrg
2194fb28925Smrg    if (id != None) {
2204fb28925Smrg	printf ("%s:  killing creator of resource 0x%lx\n", ProgramName, id);
2214fb28925Smrg	XSync (dpy, 0);			/* give xterm a chance */
2224fb28925Smrg	XKillClient (dpy, id);
2234fb28925Smrg	XSync (dpy, 0);
2244fb28925Smrg    }
2254fb28925Smrg
2264fb28925Smrg    Exit (0);
2274fb28925Smrg    /*NOTREACHED*/
2284fb28925Smrg    return 0;
2294fb28925Smrg}
2304fb28925Smrg
2314fb28925Smrgstatic int
2324fb28925Smrgparse_button(char *s, int *buttonp)
2334fb28925Smrg{
2344fb28925Smrg    register char *cp;
2354fb28925Smrg
2364fb28925Smrg    /* lower case name */
2374fb28925Smrg    for (cp = s; *cp; cp++) {
2384fb28925Smrg	if (isascii (*cp) && isupper (*cp)) {
2394fb28925Smrg#ifdef _tolower
2404fb28925Smrg	    *cp = _tolower (*cp);
2414fb28925Smrg#else
2424fb28925Smrg	    *cp = tolower (*cp);
2434fb28925Smrg#endif /* _tolower */
2444fb28925Smrg	}
2454fb28925Smrg    }
2464fb28925Smrg
2474fb28925Smrg    if (strcmp (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
2614fb28925Smrg
2624fb28925Smrgstatic XID
2634fb28925Smrgparse_id(char *s)
2644fb28925Smrg{
2654fb28925Smrg    XID retval = None;
2664fb28925Smrg    char *fmt = "%ld";			/* since XID is long */
2674fb28925Smrg
2684fb28925Smrg    if (s) {
2694fb28925Smrg	if (*s == '0') s++, fmt = "%lo";
2704fb28925Smrg	if (*s == 'x' || *s == 'X') s++, fmt = "%lx";
2714fb28925Smrg	sscanf (s, fmt, &retval);
2724fb28925Smrg    }
2734fb28925Smrg    return (retval);
2744fb28925Smrg}
2754fb28925Smrg
2764fb28925Smrgstatic XID
2774fb28925Smrgget_window_id(Display *dpy, int screen, int button, char *msg)
2784fb28925Smrg{
2794fb28925Smrg    Cursor cursor;		/* cursor to use when selecting */
2804fb28925Smrg    Window root;		/* the current root */
2814fb28925Smrg    Window retwin = None;	/* the window that got selected */
2824fb28925Smrg    int retbutton = -1;		/* button used to select window */
2834fb28925Smrg    int pressed = 0;		/* count of number of buttons pressed */
2844fb28925Smrg
2854fb28925Smrg#define MASK (ButtonPressMask | ButtonReleaseMask)
2864fb28925Smrg
2874fb28925Smrg    root = RootWindow (dpy, screen);
2884fb28925Smrg    cursor = XCreateFontCursor (dpy, XC_pirate);
2894fb28925Smrg    if (cursor == None) {
2904fb28925Smrg	fprintf (stderr, "%s:  unable to create selection cursor\n",
2914fb28925Smrg		 ProgramName);
2924fb28925Smrg	Exit (1);
2934fb28925Smrg    }
2944fb28925Smrg
2954fb28925Smrg    printf ("Select %s with ", msg);
2964fb28925Smrg    if (button == -1)
2974fb28925Smrg      printf ("any button");
2984fb28925Smrg    else
2994fb28925Smrg      printf ("button %d", button);
3004fb28925Smrg    printf ("....\n");
3014fb28925Smrg    XSync (dpy, 0);			/* give xterm a chance */
3024fb28925Smrg
3034fb28925Smrg    if (XGrabPointer (dpy, root, False, MASK, GrabModeSync, GrabModeAsync,
3044fb28925Smrg    		      None, cursor, CurrentTime) != GrabSuccess) {
3054fb28925Smrg	fprintf (stderr, "%s:  unable to grab cursor\n", ProgramName);
3064fb28925Smrg	Exit (1);
3074fb28925Smrg    }
3084fb28925Smrg
3094fb28925Smrg    /* from dsimple.c in xwininfo */
3104fb28925Smrg    while (retwin == None || pressed != 0) {
3114fb28925Smrg	XEvent event;
3124fb28925Smrg
3134fb28925Smrg	XAllowEvents (dpy, SyncPointer, CurrentTime);
3144fb28925Smrg	XWindowEvent (dpy, root, MASK, &event);
3154fb28925Smrg	switch (event.type) {
3164fb28925Smrg	  case ButtonPress:
3174fb28925Smrg	    if (retwin == None) {
3184fb28925Smrg		retbutton = event.xbutton.button;
3194fb28925Smrg		retwin = ((event.xbutton.subwindow != None) ?
3204fb28925Smrg			  event.xbutton.subwindow : root);
3214fb28925Smrg	    }
3224fb28925Smrg	    pressed++;
3234fb28925Smrg	    continue;
3244fb28925Smrg	  case ButtonRelease:
3254fb28925Smrg	    if (pressed > 0) pressed--;
3264fb28925Smrg	    continue;
3274fb28925Smrg	}					/* end switch */
3284fb28925Smrg    }						/* end for */
3294fb28925Smrg
3304fb28925Smrg    XUngrabPointer (dpy, CurrentTime);
3314fb28925Smrg    XFreeCursor (dpy, cursor);
3324fb28925Smrg    XSync (dpy, 0);
3334fb28925Smrg
3344fb28925Smrg    return ((button == -1 || retbutton == button) ? retwin : None);
3354fb28925Smrg}
3364fb28925Smrg
3374fb28925Smrg
3384fb28925Smrgstatic int
3394fb28925Smrgcatch_window_errors(Display *dpy, XErrorEvent *ev)
3404fb28925Smrg{
3414fb28925Smrg    return 0;
3424fb28925Smrg}
3434fb28925Smrg
3444fb28925Smrgstatic int
3454fb28925Smrgkill_all_windows(Display *dpy, int screenno, Bool top)
3464fb28925Smrg{
3474fb28925Smrg    Window root = RootWindow (dpy, screenno);
3484fb28925Smrg    Window dummywindow;
3494fb28925Smrg    Window *children;
3504fb28925Smrg    unsigned int nchildren;
3514fb28925Smrg    unsigned int i;
3524fb28925Smrg    XWindowAttributes attr;
3534fb28925Smrg
3544fb28925Smrg    XSync (dpy, 0);
3554fb28925Smrg    XSetErrorHandler (catch_window_errors);
3564fb28925Smrg
3574fb28925Smrg    nchildren = 0;
3584fb28925Smrg    XQueryTree (dpy, root, &dummywindow, &dummywindow, &children, &nchildren);
3594fb28925Smrg    if (!top) {
3604fb28925Smrg	for (i = 0; i < nchildren; i++) {
3614fb28925Smrg	    if (XGetWindowAttributes(dpy, children[i], &attr) &&
3624fb28925Smrg		(attr.map_state == IsViewable))
3634fb28925Smrg		children[i] = XmuClientWindow(dpy, children[i]);
3644fb28925Smrg	    else
3654fb28925Smrg		children[i] = 0;
3664fb28925Smrg	}
3674fb28925Smrg    }
3684fb28925Smrg    for (i = 0; i < nchildren; i++) {
3694fb28925Smrg	if (children[i])
3704fb28925Smrg	    XKillClient (dpy, children[i]);
3714fb28925Smrg    }
3724fb28925Smrg    XFree ((char *)children);
3734fb28925Smrg
3744fb28925Smrg    XSync (dpy, 0);
3754fb28925Smrg    XSetErrorHandler (NULL);		/* pretty stupid way to do things... */
3764fb28925Smrg
3774fb28925Smrg    return 0;
3784fb28925Smrg}
3794fb28925Smrg
3804fb28925Smrg/*
3814fb28925Smrg * ask the user to press in the root with each button in succession
3824fb28925Smrg */
3834fb28925Smrgstatic int
3844fb28925Smrgverify_okay_to_kill(Display *dpy, int screenno)
3854fb28925Smrg{
3864fb28925Smrg    unsigned char pointer_map[256];
3874fb28925Smrg    int count = XGetPointerMapping (dpy, pointer_map, 256);
3884fb28925Smrg    int i;
3894fb28925Smrg    int button;
3904fb28925Smrg    static char *msg = "the root window";
3914fb28925Smrg    Window root = RootWindow (dpy, screenno);
3924fb28925Smrg    int okay = 0;
3934fb28925Smrg
3944fb28925Smrg    for (i = 0; i < count; i++) {
3954fb28925Smrg	button = (int) pointer_map[i];
3964fb28925Smrg	if (button == 0) continue;	/* disabled */
3974fb28925Smrg	if (get_window_id (dpy, screenno, button, msg) != root) {
3984fb28925Smrg	    okay = 0;
3994fb28925Smrg	    break;
4004fb28925Smrg	}
4014fb28925Smrg	okay++;				/* must have at least one button */
4024fb28925Smrg    }
4034fb28925Smrg    if (okay) {
4044fb28925Smrg	return 1;
4054fb28925Smrg    } else {
4064fb28925Smrg	printf ("Aborting.\n");
4074fb28925Smrg	return 0;
4084fb28925Smrg    }
4094fb28925Smrg}
4104fb28925Smrg
4114fb28925Smrg/* Return True if the property WM_STATE is set on the window, otherwise
4124fb28925Smrg * return False.
4134fb28925Smrg */
4144fb28925Smrgstatic Bool
4154fb28925Smrgwm_state_set(Display *dpy, Window win)
4164fb28925Smrg{
4174fb28925Smrg    Atom wm_state;
4184fb28925Smrg    Atom actual_type;
4194fb28925Smrg    int success;
4204fb28925Smrg    int actual_format;
4214fb28925Smrg    unsigned long nitems, remaining;
4224fb28925Smrg    unsigned char* prop = NULL;
4234fb28925Smrg
4244fb28925Smrg    wm_state = XInternAtom(dpy, "WM_STATE", True);
4254fb28925Smrg    if (wm_state == None) return False;
4264fb28925Smrg    success = XGetWindowProperty(dpy, win, wm_state, 0L, 0L, False,
4274fb28925Smrg				 AnyPropertyType, &actual_type, &actual_format,
4284fb28925Smrg				 &nitems, &remaining, &prop);
4294fb28925Smrg    if (prop) XFree((char *) prop);
4304fb28925Smrg    return (success == Success && actual_type != None && actual_format);
4314fb28925Smrg}
4324fb28925Smrg
4334fb28925Smrg/* Using a heuristic method, return True if a window manager is running,
4344fb28925Smrg * otherwise, return False.
4354fb28925Smrg */
4364fb28925Smrg
4374fb28925Smrgstatic Bool
4384fb28925Smrgwm_running(Display *dpy, int screenno)
4394fb28925Smrg{
4404fb28925Smrg    XWindowAttributes	xwa;
4414fb28925Smrg    Status		status;
4424fb28925Smrg
4434fb28925Smrg    status = XGetWindowAttributes(dpy, RootWindow(dpy, screenno), &xwa);
4444fb28925Smrg    return (status &&
4454fb28925Smrg	    ((xwa.all_event_masks & SubstructureRedirectMask) ||
4464fb28925Smrg	     (xwa.all_event_masks & SubstructureNotifyMask)));
4474fb28925Smrg}
448