xkill.c revision 1c145c9f
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{ 99df58931aSmrg Display *dpy = NULL; 1004fb28925Smrg char *displayname = NULL; /* name of server to contact */ 1014fb28925Smrg int screenno; /* screen number of dpy */ 1024fb28925Smrg XID id = None; /* resource to kill */ 1034fb28925Smrg char *button_name = NULL; /* name of button for window select */ 1044fb28925Smrg int button; /* button number or negative for all */ 1054fb28925Smrg Bool kill_all = False; 1064fb28925Smrg Bool top = False; 1074fb28925Smrg 1084fb28925Smrg ProgramName = argv[0]; 109b66d1acbSmrg button = SelectButtonFirst; 1104fb28925Smrg 1111c145c9fSmrg for (int i = 1; i < argc; i++) { 1124fb28925Smrg char *arg = argv[i]; 1134fb28925Smrg 1144fb28925Smrg if (arg[0] == '-') { 1154fb28925Smrg switch (arg[1]) { 1164fb28925Smrg case 'd': /* -display displayname */ 1174929c8f8Smrg if (++i >= argc) usage ("-display requires an argument"); 1184fb28925Smrg displayname = argv[i]; 1194fb28925Smrg continue; 1204fb28925Smrg case 'i': /* -id resourceid */ 1214929c8f8Smrg if (++i >= argc) usage ("-id requires an argument"); 122df58931aSmrg id = strtoul (argv[i], NULL, 0); 123df58931aSmrg if (id == 0 || id >= 0xFFFFFFFFU) { 124df58931aSmrg fprintf (stderr, "%s: invalid id \"%s\"\n", 125df58931aSmrg ProgramName, argv[i]); 126df58931aSmrg Exit (1, dpy); 127df58931aSmrg } 1284fb28925Smrg continue; 1294fb28925Smrg case 'b': /* -button number */ 1304929c8f8Smrg if (++i >= argc) usage ("-button requires an argument"); 1314fb28925Smrg button_name = argv[i]; 1324fb28925Smrg continue; 1334fb28925Smrg case 'f': /* -frame */ 1344fb28925Smrg top = True; 1354fb28925Smrg continue; 1364fb28925Smrg case 'a': /* -all */ 1374fb28925Smrg kill_all = True; 1384fb28925Smrg continue; 139df58931aSmrg case 'v': 140df58931aSmrg puts(PACKAGE_STRING); 141df58931aSmrg exit(0); 1424fb28925Smrg default: 1434929c8f8Smrg fprintf(stderr, "%s: unrecognized argument %s\n\n", 1444929c8f8Smrg ProgramName, arg); 1454929c8f8Smrg usage (NULL); 1464fb28925Smrg } 1474fb28925Smrg } else { 1484929c8f8Smrg fprintf(stderr, "%s: unrecognized argument %s\n\n", 1494929c8f8Smrg ProgramName, arg); 1504929c8f8Smrg usage (NULL); 1514fb28925Smrg } 1524fb28925Smrg } /* end for */ 1534fb28925Smrg 1544fb28925Smrg dpy = XOpenDisplay (displayname); 1554fb28925Smrg if (!dpy) { 1564fb28925Smrg fprintf (stderr, "%s: unable to open display \"%s\"\n", 1574fb28925Smrg ProgramName, XDisplayName (displayname)); 158df58931aSmrg Exit (1, dpy); 1594fb28925Smrg } 1604fb28925Smrg screenno = DefaultScreen (dpy); 1614fb28925Smrg 1624fb28925Smrg if (kill_all) { 1634fb28925Smrg if (verify_okay_to_kill (dpy, screenno)) 1644fb28925Smrg kill_all_windows (dpy, screenno, top); 165df58931aSmrg Exit (0, dpy); 1664fb28925Smrg } 1674fb28925Smrg 1684fb28925Smrg /* 1694fb28925Smrg * if no id was given, we need to choose a window 1704fb28925Smrg */ 1714fb28925Smrg 1724fb28925Smrg if (id == None) { 1734fb28925Smrg if (!button_name) 1744fb28925Smrg button_name = XGetDefault (dpy, ProgramName, "Button"); 1754fb28925Smrg 176b66d1acbSmrg if (button_name && !parse_button (button_name, &button)) { 1774fb28925Smrg fprintf (stderr, "%s: invalid button specification \"%s\"\n", 1784fb28925Smrg ProgramName, button_name); 179df58931aSmrg Exit (1, dpy); 1804fb28925Smrg } 1814fb28925Smrg 1824fb28925Smrg if (button >= 0 || button == SelectButtonFirst) { 1834fb28925Smrg unsigned char pointer_map[256]; /* 8 bits of pointer num */ 1841c145c9fSmrg int count; 1854fb28925Smrg unsigned int ub = (unsigned int) button; 1864fb28925Smrg 1874fb28925Smrg 1884fb28925Smrg count = XGetPointerMapping (dpy, pointer_map, 256); 1894fb28925Smrg if (count <= 0) { 1904fb28925Smrg fprintf (stderr, 1914fb28925Smrg "%s: no pointer mapping, can't select window\n", 1924fb28925Smrg ProgramName); 193df58931aSmrg Exit (1, dpy); 1944fb28925Smrg } 1954fb28925Smrg 1964fb28925Smrg if (button >= 0) { /* check button */ 1971c145c9fSmrg int j; 1981c145c9fSmrg 1994fb28925Smrg for (j = 0; j < count; j++) { 2004fb28925Smrg if (ub == (unsigned int) pointer_map[j]) break; 2014fb28925Smrg } 2024fb28925Smrg if (j == count) { 2034fb28925Smrg fprintf (stderr, 2044fb28925Smrg "%s: no button number %u in pointer map, can't select window\n", 2054fb28925Smrg ProgramName, ub); 206df58931aSmrg Exit (1, dpy); 2074fb28925Smrg } 2084fb28925Smrg } else { /* get first entry */ 2094fb28925Smrg button = (int) ((unsigned int) pointer_map[0]); 2104fb28925Smrg } 2114fb28925Smrg } 2124fb28925Smrg if ((id = get_window_id (dpy, screenno, button, 2134fb28925Smrg "the window whose client you wish to kill"))) { 2144fb28925Smrg if (id == RootWindow(dpy,screenno)) id = None; 2154fb28925Smrg else if (!top) { 2164fb28925Smrg XID indicated = id; 2174fb28925Smrg if ((id = XmuClientWindow(dpy, indicated)) == indicated) { 2184fb28925Smrg 2194fb28925Smrg /* Try not to kill the window manager when the user 2204fb28925Smrg * indicates an icon to xkill. 2214fb28925Smrg */ 2224fb28925Smrg 2234fb28925Smrg if (! wm_state_set(dpy, id) && wm_running(dpy, screenno)) 2244fb28925Smrg id = None; 2254fb28925Smrg 2264fb28925Smrg } 2274fb28925Smrg } 2284fb28925Smrg } 2294fb28925Smrg } 2304fb28925Smrg 2314fb28925Smrg if (id != None) { 2324fb28925Smrg printf ("%s: killing creator of resource 0x%lx\n", ProgramName, id); 2334fb28925Smrg XSync (dpy, 0); /* give xterm a chance */ 2344fb28925Smrg XKillClient (dpy, id); 2354fb28925Smrg XSync (dpy, 0); 2364fb28925Smrg } 2374fb28925Smrg 238df58931aSmrg Exit (0, dpy); 2394fb28925Smrg /*NOTREACHED*/ 2404fb28925Smrg return 0; 2414fb28925Smrg} 2424fb28925Smrg 2434fb28925Smrgstatic int 2444929c8f8Smrgparse_button(const char *s, int *buttonp) 2454fb28925Smrg{ 2464929c8f8Smrg if (strcasecmp (s, "any") == 0) { 2474fb28925Smrg *buttonp = SelectButtonAny; 2484fb28925Smrg return (1); 2494fb28925Smrg } 2504fb28925Smrg 2514fb28925Smrg /* check for non-numeric input */ 2521c145c9fSmrg for (const char *cp = s; *cp; cp++) { 2534fb28925Smrg if (!(isascii (*cp) && isdigit (*cp))) return (0); /* bogus name */ 2544fb28925Smrg } 2554fb28925Smrg 2564fb28925Smrg *buttonp = atoi (s); 2574fb28925Smrg return (1); 2584fb28925Smrg} 2594fb28925Smrg 2604fb28925Smrgstatic XID 261df58931aSmrgget_window_id(Display *dpy, int screen, int button, const char *msg) 2624fb28925Smrg{ 2634fb28925Smrg Cursor cursor; /* cursor to use when selecting */ 2644fb28925Smrg Window root; /* the current root */ 2654fb28925Smrg Window retwin = None; /* the window that got selected */ 2664fb28925Smrg int retbutton = -1; /* button used to select window */ 2674fb28925Smrg int pressed = 0; /* count of number of buttons pressed */ 2684fb28925Smrg 2694fb28925Smrg#define MASK (ButtonPressMask | ButtonReleaseMask) 2704fb28925Smrg 2714fb28925Smrg root = RootWindow (dpy, screen); 2724fb28925Smrg cursor = XCreateFontCursor (dpy, XC_pirate); 2734fb28925Smrg if (cursor == None) { 2744fb28925Smrg fprintf (stderr, "%s: unable to create selection cursor\n", 2754fb28925Smrg ProgramName); 276df58931aSmrg Exit (1, dpy); 2774fb28925Smrg } 2784fb28925Smrg 2794fb28925Smrg printf ("Select %s with ", msg); 2804fb28925Smrg if (button == -1) 2814fb28925Smrg printf ("any button"); 2824fb28925Smrg else 2834fb28925Smrg printf ("button %d", button); 2844fb28925Smrg printf ("....\n"); 2854fb28925Smrg XSync (dpy, 0); /* give xterm a chance */ 2864fb28925Smrg 2874fb28925Smrg if (XGrabPointer (dpy, root, False, MASK, GrabModeSync, GrabModeAsync, 2884fb28925Smrg None, cursor, CurrentTime) != GrabSuccess) { 2894fb28925Smrg fprintf (stderr, "%s: unable to grab cursor\n", ProgramName); 290df58931aSmrg Exit (1, dpy); 2914fb28925Smrg } 2924fb28925Smrg 2934fb28925Smrg /* from dsimple.c in xwininfo */ 2944fb28925Smrg while (retwin == None || pressed != 0) { 2954fb28925Smrg XEvent event; 2964fb28925Smrg 2974fb28925Smrg XAllowEvents (dpy, SyncPointer, CurrentTime); 2984fb28925Smrg XWindowEvent (dpy, root, MASK, &event); 2994fb28925Smrg switch (event.type) { 3004fb28925Smrg case ButtonPress: 3014fb28925Smrg if (retwin == None) { 3024fb28925Smrg retbutton = event.xbutton.button; 3034fb28925Smrg retwin = ((event.xbutton.subwindow != None) ? 3044fb28925Smrg event.xbutton.subwindow : root); 3054fb28925Smrg } 3064fb28925Smrg pressed++; 3074fb28925Smrg continue; 3084fb28925Smrg case ButtonRelease: 3094fb28925Smrg if (pressed > 0) pressed--; 3104fb28925Smrg continue; 3114fb28925Smrg } /* end switch */ 3124fb28925Smrg } /* end for */ 3134fb28925Smrg 3144fb28925Smrg XUngrabPointer (dpy, CurrentTime); 3154fb28925Smrg XFreeCursor (dpy, cursor); 3164fb28925Smrg XSync (dpy, 0); 3174fb28925Smrg 3184fb28925Smrg return ((button == -1 || retbutton == button) ? retwin : None); 3194fb28925Smrg} 3204fb28925Smrg 3214fb28925Smrg 3224fb28925Smrgstatic int 323df58931aSmrgcatch_window_errors(_X_UNUSED Display *dpy, _X_UNUSED XErrorEvent *ev) 3244fb28925Smrg{ 3254fb28925Smrg return 0; 3264fb28925Smrg} 3274fb28925Smrg 3284fb28925Smrgstatic int 3294fb28925Smrgkill_all_windows(Display *dpy, int screenno, Bool top) 3304fb28925Smrg{ 3314fb28925Smrg Window root = RootWindow (dpy, screenno); 3324fb28925Smrg Window dummywindow; 3334fb28925Smrg Window *children; 3344fb28925Smrg unsigned int nchildren; 3354fb28925Smrg unsigned int i; 3364fb28925Smrg XWindowAttributes attr; 3374fb28925Smrg 3384fb28925Smrg XSync (dpy, 0); 3394fb28925Smrg XSetErrorHandler (catch_window_errors); 3404fb28925Smrg 3414fb28925Smrg nchildren = 0; 3424fb28925Smrg XQueryTree (dpy, root, &dummywindow, &dummywindow, &children, &nchildren); 3434fb28925Smrg if (!top) { 3444fb28925Smrg for (i = 0; i < nchildren; i++) { 3454fb28925Smrg if (XGetWindowAttributes(dpy, children[i], &attr) && 3464fb28925Smrg (attr.map_state == IsViewable)) 3474fb28925Smrg children[i] = XmuClientWindow(dpy, children[i]); 3484fb28925Smrg else 3494fb28925Smrg children[i] = 0; 3504fb28925Smrg } 3514fb28925Smrg } 3524fb28925Smrg for (i = 0; i < nchildren; i++) { 3534fb28925Smrg if (children[i]) 3544fb28925Smrg XKillClient (dpy, children[i]); 3554fb28925Smrg } 3564fb28925Smrg XFree ((char *)children); 3574fb28925Smrg 3584fb28925Smrg XSync (dpy, 0); 3594fb28925Smrg XSetErrorHandler (NULL); /* pretty stupid way to do things... */ 3604fb28925Smrg 3614fb28925Smrg return 0; 3624fb28925Smrg} 3634fb28925Smrg 3644fb28925Smrg/* 3654fb28925Smrg * ask the user to press in the root with each button in succession 3664fb28925Smrg */ 3674fb28925Smrgstatic int 3684fb28925Smrgverify_okay_to_kill(Display *dpy, int screenno) 3694fb28925Smrg{ 3704fb28925Smrg unsigned char pointer_map[256]; 3714fb28925Smrg int count = XGetPointerMapping (dpy, pointer_map, 256); 372df58931aSmrg const char *msg = "the root window"; 3734fb28925Smrg Window root = RootWindow (dpy, screenno); 3744fb28925Smrg int okay = 0; 3754fb28925Smrg 3761c145c9fSmrg for (int i = 0; i < count; i++) { 3771c145c9fSmrg int button = (int) pointer_map[i]; 3784fb28925Smrg if (button == 0) continue; /* disabled */ 3794fb28925Smrg if (get_window_id (dpy, screenno, button, msg) != root) { 3804fb28925Smrg okay = 0; 3814fb28925Smrg break; 3824fb28925Smrg } 3834fb28925Smrg okay++; /* must have at least one button */ 3844fb28925Smrg } 3854fb28925Smrg if (okay) { 3864fb28925Smrg return 1; 3874fb28925Smrg } else { 3884fb28925Smrg printf ("Aborting.\n"); 3894fb28925Smrg return 0; 3904fb28925Smrg } 3914fb28925Smrg} 3924fb28925Smrg 3934fb28925Smrg/* Return True if the property WM_STATE is set on the window, otherwise 3944fb28925Smrg * return False. 3954fb28925Smrg */ 3964fb28925Smrgstatic Bool 3974fb28925Smrgwm_state_set(Display *dpy, Window win) 3984fb28925Smrg{ 3994fb28925Smrg Atom wm_state; 4004fb28925Smrg Atom actual_type; 4014fb28925Smrg int success; 4024fb28925Smrg int actual_format; 4034fb28925Smrg unsigned long nitems, remaining; 4044fb28925Smrg unsigned char* prop = NULL; 4054fb28925Smrg 4064fb28925Smrg wm_state = XInternAtom(dpy, "WM_STATE", True); 4074fb28925Smrg if (wm_state == None) return False; 4084fb28925Smrg success = XGetWindowProperty(dpy, win, wm_state, 0L, 0L, False, 4094fb28925Smrg AnyPropertyType, &actual_type, &actual_format, 4104fb28925Smrg &nitems, &remaining, &prop); 4114fb28925Smrg if (prop) XFree((char *) prop); 4124fb28925Smrg return (success == Success && actual_type != None && actual_format); 4134fb28925Smrg} 4144fb28925Smrg 4154fb28925Smrg/* Using a heuristic method, return True if a window manager is running, 4164fb28925Smrg * otherwise, return False. 4174fb28925Smrg */ 4184fb28925Smrg 4194fb28925Smrgstatic Bool 4204fb28925Smrgwm_running(Display *dpy, int screenno) 4214fb28925Smrg{ 4224fb28925Smrg XWindowAttributes xwa; 4234fb28925Smrg Status status; 4244fb28925Smrg 4254fb28925Smrg status = XGetWindowAttributes(dpy, RootWindow(dpy, screenno), &xwa); 4264fb28925Smrg return (status && 4274fb28925Smrg ((xwa.all_event_masks & SubstructureRedirectMask) || 4284fb28925Smrg (xwa.all_event_masks & SubstructureNotifyMask))); 4294fb28925Smrg} 430