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