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 ( const 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(const char *errmsg) 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 if (errmsg != NULL) 89 fprintf (stderr, "%s: %s\n\n", ProgramName, errmsg); 90 91 fprintf (stderr, "usage: %s [-option ...]\n%s", 92 ProgramName, options); 93 Exit (1, NULL); 94} 95 96int 97main(int argc, char *argv[]) 98{ 99 Display *dpy = NULL; 100 char *displayname = NULL; /* name of server to contact */ 101 int screenno; /* screen number of dpy */ 102 XID id = None; /* resource to kill */ 103 char *button_name = NULL; /* name of button for window select */ 104 int button; /* button number or negative for all */ 105 Bool kill_all = False; 106 Bool top = False; 107 108 ProgramName = argv[0]; 109 button = SelectButtonFirst; 110 111 for (int i = 1; i < argc; i++) { 112 char *arg = argv[i]; 113 114 if (arg[0] == '-') { 115 switch (arg[1]) { 116 case 'd': /* -display displayname */ 117 if (++i >= argc) usage ("-display requires an argument"); 118 displayname = argv[i]; 119 continue; 120 case 'i': /* -id resourceid */ 121 if (++i >= argc) usage ("-id requires an argument"); 122 id = strtoul (argv[i], NULL, 0); 123 if (id == 0 || id >= 0xFFFFFFFFU) { 124 fprintf (stderr, "%s: invalid id \"%s\"\n", 125 ProgramName, argv[i]); 126 Exit (1, dpy); 127 } 128 continue; 129 case 'b': /* -button number */ 130 if (++i >= argc) usage ("-button requires an argument"); 131 button_name = argv[i]; 132 continue; 133 case 'f': /* -frame */ 134 top = True; 135 continue; 136 case 'a': /* -all */ 137 kill_all = True; 138 continue; 139 case 'v': 140 puts(PACKAGE_STRING); 141 exit(0); 142 default: 143 fprintf(stderr, "%s: unrecognized argument %s\n\n", 144 ProgramName, arg); 145 usage (NULL); 146 } 147 } else { 148 fprintf(stderr, "%s: unrecognized argument %s\n\n", 149 ProgramName, arg); 150 usage (NULL); 151 } 152 } /* end for */ 153 154 dpy = XOpenDisplay (displayname); 155 if (!dpy) { 156 fprintf (stderr, "%s: unable to open display \"%s\"\n", 157 ProgramName, XDisplayName (displayname)); 158 Exit (1, dpy); 159 } 160 screenno = DefaultScreen (dpy); 161 162 if (kill_all) { 163 if (verify_okay_to_kill (dpy, screenno)) 164 kill_all_windows (dpy, screenno, top); 165 Exit (0, dpy); 166 } 167 168 /* 169 * if no id was given, we need to choose a window 170 */ 171 172 if (id == None) { 173 if (!button_name) 174 button_name = XGetDefault (dpy, ProgramName, "Button"); 175 176 if (button_name && !parse_button (button_name, &button)) { 177 fprintf (stderr, "%s: invalid button specification \"%s\"\n", 178 ProgramName, button_name); 179 Exit (1, dpy); 180 } 181 182 if (button >= 0 || button == SelectButtonFirst) { 183 unsigned char pointer_map[256]; /* 8 bits of pointer num */ 184 int count; 185 unsigned int ub = (unsigned int) button; 186 187 188 count = XGetPointerMapping (dpy, pointer_map, 256); 189 if (count <= 0) { 190 fprintf (stderr, 191 "%s: no pointer mapping, can't select window\n", 192 ProgramName); 193 Exit (1, dpy); 194 } 195 196 if (button >= 0) { /* check button */ 197 int j; 198 199 for (j = 0; j < count; j++) { 200 if (ub == (unsigned int) pointer_map[j]) break; 201 } 202 if (j == count) { 203 fprintf (stderr, 204 "%s: no button number %u in pointer map, can't select window\n", 205 ProgramName, ub); 206 Exit (1, dpy); 207 } 208 } else { /* get first entry */ 209 button = (int) ((unsigned int) pointer_map[0]); 210 } 211 } 212 if ((id = get_window_id (dpy, screenno, button, 213 "the window whose client you wish to kill"))) { 214 if (id == RootWindow(dpy,screenno)) id = None; 215 else if (!top) { 216 XID indicated = id; 217 if ((id = XmuClientWindow(dpy, indicated)) == indicated) { 218 219 /* Try not to kill the window manager when the user 220 * indicates an icon to xkill. 221 */ 222 223 if (! wm_state_set(dpy, id) && wm_running(dpy, screenno)) 224 id = None; 225 226 } 227 } 228 } 229 } 230 231 if (id != None) { 232 printf ("%s: killing creator of resource 0x%lx\n", ProgramName, id); 233 XSync (dpy, 0); /* give xterm a chance */ 234 XKillClient (dpy, id); 235 XSync (dpy, 0); 236 } 237 238 Exit (0, dpy); 239 /*NOTREACHED*/ 240 return 0; 241} 242 243static int 244parse_button(const char *s, int *buttonp) 245{ 246 if (strcasecmp (s, "any") == 0) { 247 *buttonp = SelectButtonAny; 248 return (1); 249 } 250 251 /* check for non-numeric input */ 252 for (const char *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 260static XID 261get_window_id(Display *dpy, int screen, int button, const char *msg) 262{ 263 Cursor cursor; /* cursor to use when selecting */ 264 Window root; /* the current root */ 265 Window retwin = None; /* the window that got selected */ 266 int retbutton = -1; /* button used to select window */ 267 int pressed = 0; /* count of number of buttons pressed */ 268 269#define MASK (ButtonPressMask | ButtonReleaseMask) 270 271 root = RootWindow (dpy, screen); 272 cursor = XCreateFontCursor (dpy, XC_pirate); 273 if (cursor == None) { 274 fprintf (stderr, "%s: unable to create selection cursor\n", 275 ProgramName); 276 Exit (1, dpy); 277 } 278 279 printf ("Select %s with ", msg); 280 if (button == -1) 281 printf ("any button"); 282 else 283 printf ("button %d", button); 284 printf ("....\n"); 285 XSync (dpy, 0); /* give xterm a chance */ 286 287 if (XGrabPointer (dpy, root, False, MASK, GrabModeSync, GrabModeAsync, 288 None, cursor, CurrentTime) != GrabSuccess) { 289 fprintf (stderr, "%s: unable to grab cursor\n", ProgramName); 290 Exit (1, dpy); 291 } 292 293 /* from dsimple.c in xwininfo */ 294 while (retwin == None || pressed != 0) { 295 XEvent event; 296 297 XAllowEvents (dpy, SyncPointer, CurrentTime); 298 XWindowEvent (dpy, root, MASK, &event); 299 switch (event.type) { 300 case ButtonPress: 301 if (retwin == None) { 302 retbutton = event.xbutton.button; 303 retwin = ((event.xbutton.subwindow != None) ? 304 event.xbutton.subwindow : root); 305 } 306 pressed++; 307 continue; 308 case ButtonRelease: 309 if (pressed > 0) pressed--; 310 continue; 311 } /* end switch */ 312 } /* end for */ 313 314 XUngrabPointer (dpy, CurrentTime); 315 XFreeCursor (dpy, cursor); 316 XSync (dpy, 0); 317 318 return ((button == -1 || retbutton == button) ? retwin : None); 319} 320 321 322static int 323catch_window_errors(_X_UNUSED Display *dpy, _X_UNUSED XErrorEvent *ev) 324{ 325 return 0; 326} 327 328static int 329kill_all_windows(Display *dpy, int screenno, Bool top) 330{ 331 Window root = RootWindow (dpy, screenno); 332 Window dummywindow; 333 Window *children; 334 unsigned int nchildren; 335 unsigned int i; 336 XWindowAttributes attr; 337 338 XSync (dpy, 0); 339 XSetErrorHandler (catch_window_errors); 340 341 nchildren = 0; 342 XQueryTree (dpy, root, &dummywindow, &dummywindow, &children, &nchildren); 343 if (!top) { 344 for (i = 0; i < nchildren; i++) { 345 if (XGetWindowAttributes(dpy, children[i], &attr) && 346 (attr.map_state == IsViewable)) 347 children[i] = XmuClientWindow(dpy, children[i]); 348 else 349 children[i] = 0; 350 } 351 } 352 for (i = 0; i < nchildren; i++) { 353 if (children[i]) 354 XKillClient (dpy, children[i]); 355 } 356 XFree ((char *)children); 357 358 XSync (dpy, 0); 359 XSetErrorHandler (NULL); /* pretty stupid way to do things... */ 360 361 return 0; 362} 363 364/* 365 * ask the user to press in the root with each button in succession 366 */ 367static int 368verify_okay_to_kill(Display *dpy, int screenno) 369{ 370 unsigned char pointer_map[256]; 371 int count = XGetPointerMapping (dpy, pointer_map, 256); 372 const char *msg = "the root window"; 373 Window root = RootWindow (dpy, screenno); 374 int okay = 0; 375 376 for (int i = 0; i < count; i++) { 377 int button = (int) pointer_map[i]; 378 if (button == 0) continue; /* disabled */ 379 if (get_window_id (dpy, screenno, button, msg) != root) { 380 okay = 0; 381 break; 382 } 383 okay++; /* must have at least one button */ 384 } 385 if (okay) { 386 return 1; 387 } else { 388 printf ("Aborting.\n"); 389 return 0; 390 } 391} 392 393/* Return True if the property WM_STATE is set on the window, otherwise 394 * return False. 395 */ 396static Bool 397wm_state_set(Display *dpy, Window win) 398{ 399 Atom wm_state; 400 Atom actual_type; 401 int success; 402 int actual_format; 403 unsigned long nitems, remaining; 404 unsigned char* prop = NULL; 405 406 wm_state = XInternAtom(dpy, "WM_STATE", True); 407 if (wm_state == None) return False; 408 success = XGetWindowProperty(dpy, win, wm_state, 0L, 0L, False, 409 AnyPropertyType, &actual_type, &actual_format, 410 &nitems, &remaining, &prop); 411 if (prop) XFree((char *) prop); 412 return (success == Success && actual_type != None && actual_format); 413} 414 415/* Using a heuristic method, return True if a window manager is running, 416 * otherwise, return False. 417 */ 418 419static Bool 420wm_running(Display *dpy, int screenno) 421{ 422 XWindowAttributes xwa; 423 Status status; 424 425 status = XGetWindowAttributes(dpy, RootWindow(dpy, screenno), &xwa); 426 return (status && 427 ((xwa.all_event_masks & SubstructureRedirectMask) || 428 (xwa.all_event_masks & SubstructureNotifyMask))); 429} 430