xkill.c revision b66d1acb
1/* $Xorg: xkill.c,v 1.5 2001/02/09 02:05:54 xorgcvs Exp $ */ 2/* 3 4Copyright 1988, 1998 The Open Group 5 6Permission to use, copy, modify, distribute, and sell this software and its 7documentation for any purpose is hereby granted without fee, provided that 8the above copyright notice appear in all copies and that both that 9copyright notice and this permission notice appear in supporting 10documentation. 11 12The above copyright notice and this permission notice shall be included 13in all copies or substantial portions of the Software. 14 15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR 19OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21OTHER DEALINGS IN THE SOFTWARE. 22 23Except as contained in this notice, the name of The Open Group shall 24not be used in advertising or otherwise to promote the sale, use or 25other dealings in this Software without prior written authorization 26from The Open Group. 27 28*/ 29/* $XFree86: xc/programs/xkill/xkill.c,v 1.5 2001/04/01 14:00:22 tsi Exp $ */ 30 31/* 32 * xkill - simple program for destroying unwanted clients 33 * Author: Jim Fulton, MIT X Consortium; Dana Chee, Bellcore 34 */ 35 36/* 37 * Warning, this is a very dangerous client.... 38 */ 39 40#include <stdio.h> 41#include <stdlib.h> 42#include <ctype.h> 43 44#include <X11/Xos.h> 45#include <X11/Xlib.h> 46#include <X11/cursorfont.h> 47#include <X11/Xproto.h> 48 49#include <X11/Xmu/WinUtil.h> 50 51static Display *dpy = NULL; 52static char *ProgramName; 53 54#define SelectButtonAny (-1) 55#define SelectButtonFirst (-2) 56 57static int parse_button ( char *s, int *buttonp ); 58static XID parse_id ( char *s ); 59static XID get_window_id ( Display *dpy, int screen, int button, 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 67Exit(int code) 68{ 69 if (dpy) { 70 XCloseDisplay (dpy); 71 } 72 exit (code); 73} 74 75static void 76usage(void) 77{ 78 static char *options[] = { 79"where options include:", 80" -display displayname X server to contact", 81" -id resource resource whose client is to be killed", 82" -frame don't ignore window manager frames", 83" -button number specific button to be pressed to select window", 84" -all kill all clients with top level windows", 85"", 86NULL}; 87 char **cpp; 88 89 fprintf (stderr, "usage: %s [-option ...]\n", 90 ProgramName); 91 for (cpp = options; *cpp; cpp++) { 92 fprintf (stderr, "%s\n", *cpp); 93 } 94 Exit (1); 95} 96 97int 98main(int argc, char *argv[]) 99{ 100 int i; /* iterator, temp variable */ 101 char *displayname = NULL; /* name of server to contact */ 102 int screenno; /* screen number of dpy */ 103 XID id = None; /* resource to kill */ 104 char *button_name = NULL; /* name of button for window select */ 105 int button; /* button number or negative for all */ 106 Bool kill_all = False; 107 Bool top = False; 108 109 ProgramName = argv[0]; 110 button = SelectButtonFirst; 111 112 for (i = 1; i < argc; i++) { 113 char *arg = argv[i]; 114 115 if (arg[0] == '-') { 116 switch (arg[1]) { 117 case 'd': /* -display displayname */ 118 if (++i >= argc) usage (); 119 displayname = argv[i]; 120 continue; 121 case 'i': /* -id resourceid */ 122 if (++i >= argc) usage (); 123 id = parse_id (argv[i]); 124 continue; 125 case 'b': /* -button number */ 126 if (++i >= argc) usage (); 127 button_name = argv[i]; 128 continue; 129 case 'f': /* -frame */ 130 top = True; 131 continue; 132 case 'a': /* -all */ 133 kill_all = True; 134 continue; 135 default: 136 usage (); 137 } 138 } else { 139 usage (); 140 } 141 } /* end for */ 142 143 dpy = XOpenDisplay (displayname); 144 if (!dpy) { 145 fprintf (stderr, "%s: unable to open display \"%s\"\n", 146 ProgramName, XDisplayName (displayname)); 147 Exit (1); 148 } 149 screenno = DefaultScreen (dpy); 150 151 if (kill_all) { 152 if (verify_okay_to_kill (dpy, screenno)) 153 kill_all_windows (dpy, screenno, top); 154 Exit (0); 155 } 156 157 /* 158 * if no id was given, we need to choose a window 159 */ 160 161 if (id == None) { 162 if (!button_name) 163 button_name = XGetDefault (dpy, ProgramName, "Button"); 164 165 if (button_name && !parse_button (button_name, &button)) { 166 fprintf (stderr, "%s: invalid button specification \"%s\"\n", 167 ProgramName, button_name); 168 Exit (1); 169 } 170 171 if (button >= 0 || button == SelectButtonFirst) { 172 unsigned char pointer_map[256]; /* 8 bits of pointer num */ 173 int count, j; 174 unsigned int ub = (unsigned int) button; 175 176 177 count = XGetPointerMapping (dpy, pointer_map, 256); 178 if (count <= 0) { 179 fprintf (stderr, 180 "%s: no pointer mapping, can't select window\n", 181 ProgramName); 182 Exit (1); 183 } 184 185 if (button >= 0) { /* check button */ 186 for (j = 0; j < count; j++) { 187 if (ub == (unsigned int) pointer_map[j]) break; 188 } 189 if (j == count) { 190 fprintf (stderr, 191 "%s: no button number %u in pointer map, can't select window\n", 192 ProgramName, ub); 193 Exit (1); 194 } 195 } else { /* get first entry */ 196 button = (int) ((unsigned int) pointer_map[0]); 197 } 198 } 199 if ((id = get_window_id (dpy, screenno, button, 200 "the window whose client you wish to kill"))) { 201 if (id == RootWindow(dpy,screenno)) id = None; 202 else if (!top) { 203 XID indicated = id; 204 if ((id = XmuClientWindow(dpy, indicated)) == indicated) { 205 206 /* Try not to kill the window manager when the user 207 * indicates an icon to xkill. 208 */ 209 210 if (! wm_state_set(dpy, id) && wm_running(dpy, screenno)) 211 id = None; 212 213 } 214 } 215 } 216 } 217 218 if (id != None) { 219 printf ("%s: killing creator of resource 0x%lx\n", ProgramName, id); 220 XSync (dpy, 0); /* give xterm a chance */ 221 XKillClient (dpy, id); 222 XSync (dpy, 0); 223 } 224 225 Exit (0); 226 /*NOTREACHED*/ 227 return 0; 228} 229 230static int 231parse_button(char *s, int *buttonp) 232{ 233 register char *cp; 234 235 /* lower case name */ 236 for (cp = s; *cp; cp++) { 237 if (isascii (*cp) && isupper (*cp)) { 238#ifdef _tolower 239 *cp = _tolower (*cp); 240#else 241 *cp = tolower (*cp); 242#endif /* _tolower */ 243 } 244 } 245 246 if (strcmp (s, "any") == 0) { 247 *buttonp = SelectButtonAny; 248 return (1); 249 } 250 251 /* check for non-numeric input */ 252 for (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 260 261static XID 262parse_id(char *s) 263{ 264 XID retval = None; 265 char *fmt = "%ld"; /* since XID is long */ 266 267 if (s) { 268 if (*s == '0') s++, fmt = "%lo"; 269 if (*s == 'x' || *s == 'X') s++, fmt = "%lx"; 270 sscanf (s, fmt, &retval); 271 } 272 return (retval); 273} 274 275static XID 276get_window_id(Display *dpy, int screen, int button, char *msg) 277{ 278 Cursor cursor; /* cursor to use when selecting */ 279 Window root; /* the current root */ 280 Window retwin = None; /* the window that got selected */ 281 int retbutton = -1; /* button used to select window */ 282 int pressed = 0; /* count of number of buttons pressed */ 283 284#define MASK (ButtonPressMask | ButtonReleaseMask) 285 286 root = RootWindow (dpy, screen); 287 cursor = XCreateFontCursor (dpy, XC_pirate); 288 if (cursor == None) { 289 fprintf (stderr, "%s: unable to create selection cursor\n", 290 ProgramName); 291 Exit (1); 292 } 293 294 printf ("Select %s with ", msg); 295 if (button == -1) 296 printf ("any button"); 297 else 298 printf ("button %d", button); 299 printf ("....\n"); 300 XSync (dpy, 0); /* give xterm a chance */ 301 302 if (XGrabPointer (dpy, root, False, MASK, GrabModeSync, GrabModeAsync, 303 None, cursor, CurrentTime) != GrabSuccess) { 304 fprintf (stderr, "%s: unable to grab cursor\n", ProgramName); 305 Exit (1); 306 } 307 308 /* from dsimple.c in xwininfo */ 309 while (retwin == None || pressed != 0) { 310 XEvent event; 311 312 XAllowEvents (dpy, SyncPointer, CurrentTime); 313 XWindowEvent (dpy, root, MASK, &event); 314 switch (event.type) { 315 case ButtonPress: 316 if (retwin == None) { 317 retbutton = event.xbutton.button; 318 retwin = ((event.xbutton.subwindow != None) ? 319 event.xbutton.subwindow : root); 320 } 321 pressed++; 322 continue; 323 case ButtonRelease: 324 if (pressed > 0) pressed--; 325 continue; 326 } /* end switch */ 327 } /* end for */ 328 329 XUngrabPointer (dpy, CurrentTime); 330 XFreeCursor (dpy, cursor); 331 XSync (dpy, 0); 332 333 return ((button == -1 || retbutton == button) ? retwin : None); 334} 335 336 337static int 338catch_window_errors(Display *dpy, XErrorEvent *ev) 339{ 340 return 0; 341} 342 343static int 344kill_all_windows(Display *dpy, int screenno, Bool top) 345{ 346 Window root = RootWindow (dpy, screenno); 347 Window dummywindow; 348 Window *children; 349 unsigned int nchildren; 350 unsigned int i; 351 XWindowAttributes attr; 352 353 XSync (dpy, 0); 354 XSetErrorHandler (catch_window_errors); 355 356 nchildren = 0; 357 XQueryTree (dpy, root, &dummywindow, &dummywindow, &children, &nchildren); 358 if (!top) { 359 for (i = 0; i < nchildren; i++) { 360 if (XGetWindowAttributes(dpy, children[i], &attr) && 361 (attr.map_state == IsViewable)) 362 children[i] = XmuClientWindow(dpy, children[i]); 363 else 364 children[i] = 0; 365 } 366 } 367 for (i = 0; i < nchildren; i++) { 368 if (children[i]) 369 XKillClient (dpy, children[i]); 370 } 371 XFree ((char *)children); 372 373 XSync (dpy, 0); 374 XSetErrorHandler (NULL); /* pretty stupid way to do things... */ 375 376 return 0; 377} 378 379/* 380 * ask the user to press in the root with each button in succession 381 */ 382static int 383verify_okay_to_kill(Display *dpy, int screenno) 384{ 385 unsigned char pointer_map[256]; 386 int count = XGetPointerMapping (dpy, pointer_map, 256); 387 int i; 388 int button; 389 static char *msg = "the root window"; 390 Window root = RootWindow (dpy, screenno); 391 int okay = 0; 392 393 for (i = 0; i < count; i++) { 394 button = (int) pointer_map[i]; 395 if (button == 0) continue; /* disabled */ 396 if (get_window_id (dpy, screenno, button, msg) != root) { 397 okay = 0; 398 break; 399 } 400 okay++; /* must have at least one button */ 401 } 402 if (okay) { 403 return 1; 404 } else { 405 printf ("Aborting.\n"); 406 return 0; 407 } 408} 409 410/* Return True if the property WM_STATE is set on the window, otherwise 411 * return False. 412 */ 413static Bool 414wm_state_set(Display *dpy, Window win) 415{ 416 Atom wm_state; 417 Atom actual_type; 418 int success; 419 int actual_format; 420 unsigned long nitems, remaining; 421 unsigned char* prop = NULL; 422 423 wm_state = XInternAtom(dpy, "WM_STATE", True); 424 if (wm_state == None) return False; 425 success = XGetWindowProperty(dpy, win, wm_state, 0L, 0L, False, 426 AnyPropertyType, &actual_type, &actual_format, 427 &nitems, &remaining, &prop); 428 if (prop) XFree((char *) prop); 429 return (success == Success && actual_type != None && actual_format); 430} 431 432/* Using a heuristic method, return True if a window manager is running, 433 * otherwise, return False. 434 */ 435 436static Bool 437wm_running(Display *dpy, int screenno) 438{ 439 XWindowAttributes xwa; 440 Status status; 441 442 status = XGetWindowAttributes(dpy, RootWindow(dpy, screenno), &xwa); 443 return (status && 444 ((xwa.all_event_masks & SubstructureRedirectMask) || 445 (xwa.all_event_masks & SubstructureNotifyMask))); 446} 447