diff options
-rw-r--r-- | xkill.c | 447 | ||||
-rw-r--r-- | xkill.man | 80 |
2 files changed, 527 insertions, 0 deletions
@@ -0,0 +1,447 @@ +/* $Xorg: xkill.c,v 1.5 2001/02/09 02:05:54 xorgcvs Exp $ */ +/* + +Copyright 1988, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + +*/ + +/* + * xkill - simple program for destroying unwanted clients + * Author: Jim Fulton, MIT X Consortium; Dana Chee, Bellcore + */ + +/* + * Warning, this is a very dangerous client.... + */ + +#include <stdio.h> +#include <ctype.h> + +#include <X11/Xos.h> +#include <X11/Xlib.h> +#include <X11/cursorfont.h> +#include <X11/Xproto.h> + +#include <X11/Xmu/WinUtil.h> + +Display *dpy = NULL; +char *ProgramName; + +#define SelectButtonAny (-1) +#define SelectButtonFirst (-2) + +XID parse_id(), get_window_id(); +int parse_button(), verify_okay_to_kill(); + +Exit (code) + int code; +{ + if (dpy) { + XCloseDisplay (dpy); + } + exit (code); +} + +usage () +{ + static char *options[] = { +"where options include:", +" -display displayname X server to contact", +" -id resource resource whose client is to be killed", +" -frame don't ignore window manager frames", +" -button number specific button to be pressed to select window", +" -all kill all clients with top level windows", +"", +NULL}; + char **cpp; + + fprintf (stderr, "usage: %s [-option ...]\n", + ProgramName); + for (cpp = options; *cpp; cpp++) { + fprintf (stderr, "%s\n", *cpp); + } + Exit (1); +} + +main (argc, argv) + int argc; + char *argv[]; +{ + int i; /* iterator, temp variable */ + char *displayname = NULL; /* name of server to contact */ + int screenno; /* screen number of dpy */ + XID id = None; /* resource to kill */ + char *button_name = NULL; /* name of button for window select */ + int button; /* button number or negative for all */ + Bool kill_all = False; + Bool top = False; + + ProgramName = argv[0]; + + for (i = 1; i < argc; i++) { + char *arg = argv[i]; + + if (arg[0] == '-') { + switch (arg[1]) { + case 'd': /* -display displayname */ + if (++i >= argc) usage (); + displayname = argv[i]; + continue; + case 'i': /* -id resourceid */ + if (++i >= argc) usage (); + id = parse_id (argv[i]); + continue; + case 'b': /* -button number */ + if (++i >= argc) usage (); + button_name = argv[i]; + continue; + case 'f': /* -frame */ + top = True; + continue; + case 'a': /* -all */ + kill_all = True; + continue; + default: + usage (); + } + } else { + usage (); + } + } /* end for */ + + dpy = XOpenDisplay (displayname); + if (!dpy) { + fprintf (stderr, "%s: unable to open display \"%s\"\n", + ProgramName, XDisplayName (displayname)); + Exit (1); + } + screenno = DefaultScreen (dpy); + + if (kill_all) { + if (verify_okay_to_kill (dpy, screenno)) + kill_all_windows (dpy, screenno, top); + Exit (0); + } + + /* + * if no id was given, we need to choose a window + */ + + if (id == None) { + if (!button_name) + button_name = XGetDefault (dpy, ProgramName, "Button"); + + if (!button_name) + button = SelectButtonFirst; + else if (!parse_button (button_name, &button)) { + fprintf (stderr, "%s: invalid button specification \"%s\"\n", + ProgramName, button_name); + Exit (1); + } + + if (button >= 0 || button == SelectButtonFirst) { + unsigned char pointer_map[256]; /* 8 bits of pointer num */ + int count, j; + unsigned int ub = (unsigned int) button; + + + count = XGetPointerMapping (dpy, pointer_map, 256); + if (count <= 0) { + fprintf (stderr, + "%s: no pointer mapping, can't select window\n", + ProgramName); + Exit (1); + } + + if (button >= 0) { /* check button */ + for (j = 0; j < count; j++) { + if (ub == (unsigned int) pointer_map[j]) break; + } + if (j == count) { + fprintf (stderr, + "%s: no button number %u in pointer map, can't select window\n", + ProgramName, ub); + Exit (1); + } + } else { /* get first entry */ + button = (int) ((unsigned int) pointer_map[0]); + } + } + if ((id = get_window_id (dpy, screenno, button, + "the window whose client you wish to kill"))) { + if (id == RootWindow(dpy,screenno)) id = None; + else if (!top) { + XID indicated = id; + if ((id = XmuClientWindow(dpy, indicated)) == indicated) { + + /* Try not to kill the window manager when the user + * indicates an icon to xkill. + */ + + if (! wm_state_set(dpy, id) && wm_running(dpy, screenno)) + id = None; + + } + } + } + } + + if (id != None) { + printf ("%s: killing creator of resource 0x%lx\n", ProgramName, id); + XSync (dpy, 0); /* give xterm a chance */ + XKillClient (dpy, id); + XSync (dpy, 0); + } + + Exit (0); +} + +int parse_button (s, buttonp) + register char *s; + int *buttonp; +{ + register char *cp; + + /* lower case name */ + for (cp = s; *cp; cp++) { + if (isascii (*cp) && isupper (*cp)) { +#ifdef _tolower + *cp = _tolower (*cp); +#else + *cp = tolower (*cp); +#endif /* _tolower */ + } + } + + if (strcmp (s, "any") == 0) { + *buttonp = SelectButtonAny; + return (1); + } + + /* check for non-numeric input */ + for (cp = s; *cp; cp++) { + if (!(isascii (*cp) && isdigit (*cp))) return (0); /* bogus name */ + } + + *buttonp = atoi (s); + return (1); +} + + +XID parse_id (s) + char *s; +{ + XID retval = None; + char *fmt = "%ld"; /* since XID is long */ + + if (s) { + if (*s == '0') s++, fmt = "%lo"; + if (*s == 'x' || *s == 'X') s++, fmt = "%lx"; + sscanf (s, fmt, &retval); + } + return (retval); +} + +XID get_window_id (dpy, screen, button, msg) + Display *dpy; + int screen; + int button; + char *msg; +{ + Cursor cursor; /* cursor to use when selecting */ + Window root; /* the current root */ + Window retwin = None; /* the window that got selected */ + int retbutton = -1; /* button used to select window */ + int pressed = 0; /* count of number of buttons pressed */ + +#define MASK (ButtonPressMask | ButtonReleaseMask) + + root = RootWindow (dpy, screen); + cursor = XCreateFontCursor (dpy, XC_draped_box); + if (cursor == None) { + fprintf (stderr, "%s: unable to create selection cursor\n", + ProgramName); + Exit (1); + } + + printf ("Select %s with ", msg); + if (button == -1) + printf ("any button"); + else + printf ("button %d", button); + printf ("....\n"); + XSync (dpy, 0); /* give xterm a chance */ + + if (XGrabPointer (dpy, root, False, MASK, GrabModeSync, GrabModeAsync, + None, cursor, CurrentTime) != GrabSuccess) { + fprintf (stderr, "%s: unable to grab cursor\n", ProgramName); + Exit (1); + } + + /* from dsimple.c in xwininfo */ + while (retwin == None || pressed != 0) { + XEvent event; + + XAllowEvents (dpy, SyncPointer, CurrentTime); + XWindowEvent (dpy, root, MASK, &event); + switch (event.type) { + case ButtonPress: + if (retwin == None) { + retbutton = event.xbutton.button; + retwin = ((event.xbutton.subwindow != None) ? + event.xbutton.subwindow : root); + } + pressed++; + continue; + case ButtonRelease: + if (pressed > 0) pressed--; + continue; + } /* end switch */ + } /* end for */ + + XUngrabPointer (dpy, CurrentTime); + XFreeCursor (dpy, cursor); + XSync (dpy, 0); + + return ((button == -1 || retbutton == button) ? retwin : None); +} + + +int catch_window_errors (dpy, ev) + Display *dpy; + XErrorEvent *ev; +{ + return 0; +} + +int kill_all_windows (dpy, screenno, top) + Display *dpy; + int screenno; + Bool top; +{ + Window root = RootWindow (dpy, screenno); + Window dummywindow; + Window *children; + unsigned int nchildren; + unsigned int i; + XWindowAttributes attr; + + XSync (dpy, 0); + XSetErrorHandler (catch_window_errors); + + nchildren = 0; + XQueryTree (dpy, root, &dummywindow, &dummywindow, &children, &nchildren); + if (!top) { + for (i = 0; i < nchildren; i++) { + if (XGetWindowAttributes(dpy, children[i], &attr) && + (attr.map_state == IsViewable)) + children[i] = XmuClientWindow(dpy, children[i]); + else + children[i] = 0; + } + } + for (i = 0; i < nchildren; i++) { + if (children[i]) + XKillClient (dpy, children[i]); + } + XFree ((char *)children); + + XSync (dpy, 0); + XSetErrorHandler (NULL); /* pretty stupid way to do things... */ + + return 0; +} + +/* + * ask the user to press in the root with each button in succession + */ +int verify_okay_to_kill (dpy, screenno) + Display *dpy; + int screenno; +{ + unsigned char pointer_map[256]; + int count = XGetPointerMapping (dpy, pointer_map, 256); + int i; + int button; + static char *msg = "the root window"; + Window root = RootWindow (dpy, screenno); + int okay = 0; + + for (i = 0; i < count; i++) { + button = (int) pointer_map[i]; + if (button == 0) continue; /* disabled */ + if (get_window_id (dpy, screenno, button, msg) != root) { + okay = 0; + break; + } + okay++; /* must have at least one button */ + } + if (okay) { + return 1; + } else { + printf ("Aborting.\n"); + return 0; + } +} + +/* Return True if the property WM_STATE is set on the window, otherwise + * return False. + */ +Bool wm_state_set(dpy, win) +Display *dpy; +Window win; +{ + Atom wm_state; + Atom actual_type; + int success; + int actual_format; + unsigned long nitems, remaining; + unsigned char* prop = NULL; + + wm_state = XInternAtom(dpy, "WM_STATE", True); + if (wm_state == None) return False; + success = XGetWindowProperty(dpy, win, wm_state, 0L, 0L, False, + AnyPropertyType, &actual_type, &actual_format, + &nitems, &remaining, &prop); + if (prop) XFree((char *) prop); + return (success == Success && actual_type != None && actual_format); +} + +/* Using a heuristic method, return True if a window manager is running, + * otherwise, return False. + */ + +Bool wm_running(dpy, screenno) +Display *dpy; +int screenno; +{ + XWindowAttributes xwa; + Status status; + + status = XGetWindowAttributes(dpy, RootWindow(dpy, screenno), &xwa); + return (status && + ((xwa.all_event_masks & SubstructureRedirectMask) || + (xwa.all_event_masks & SubstructureNotifyMask))); +} diff --git a/xkill.man b/xkill.man new file mode 100644 index 0000000..015eb7d --- /dev/null +++ b/xkill.man @@ -0,0 +1,80 @@ +.\" $Xorg: xkill.man,v 1.4 2001/02/09 02:05:54 xorgcvs Exp $ +.\" Copyright 1988, 1998 The Open Group +.\" +.\" Permission to use, copy, modify, distribute, and sell this software and its +.\" documentation for any purpose is hereby granted without fee, provided that +.\" the above copyright notice appear in all copies and that both that +.\" copyright notice and this permission notice appear in supporting +.\" documentation. +.\" +.\" The above copyright notice and this permission notice shall be included +.\" in all copies or substantial portions of the Software. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +.\" IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +.\" OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +.\" ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +.\" OTHER DEALINGS IN THE SOFTWARE. +.\" +.\" Except as contained in this notice, the name of The Open Group shall +.\" not be used in advertising or otherwise to promote the sale, use or +.\" other dealings in this Software without prior written authorization +.\" from The Open Group. +.TH XKILL 1 "Release 6.4" "X Version 11" +.SH NAME +xkill - kill a client by its X resource +.SH SYNOPSIS +.B "xkill" +[\-display \fIdisplayname\fP] [\-id \fIresource\fP] [\-button number] [\-frame] [\-all] +.SH DESCRIPTION +.PP +.I Xkill +is a utility for forcing the X server to close connections to clients. This +program is very dangerous, but is useful for aborting programs that have +displayed undesired windows on a user's screen. If no resource identifier +is given with \fI-id\fP, \fIxkill\fP will display a special cursor +as a prompt for the user to select a window to be killed. If a pointer button +is pressed over a non-root window, the server will close its connection to +the client that created the window. +.SH OPTIONS +.TP 8 +.B \-display \fIdisplayname\fP +This option specifies the name of the X server to contact. +.TP 8 +.B \-id \fIresource\fP +This option specifies the X identifier for the resource whose creator is +to be aborted. If no resource is specified, \fIxkill\fP will display a +special cursor with which you should select a window to be kill. +.TP 8 +.B \-button \fInumber\fP +This option specifies the number of pointer button +that should be used in selecting a window to kill. +If the word "any" is specified, any button on the pointer may be used. By +default, the first button in the pointer map (which is usually the leftmost +button) is used. +.TP 8 +.B \-all +This option indicates that all clients with top-level windows on the screen +should be killed. \fIXkill\fP will ask you to select the root window with +each of the currently defined buttons to give you several chances to abort. +Use of this option is highly discouraged. +.TP 8 +.B \-frame +This option indicates that xkill should ignore the standard conventions for +finding top-level client windows (which are typically nested inside a window +manager window), and simply believe that you want to kill direct children of +the root. +.SH XDEFAULTS +.TP 8 +.B Button +Specifies a specific pointer button number or the word "any" to use when +selecting windows. +.SH "SEE ALSO" +X(1), xwininfo(1), XKillClient and XGetPointerMapping in the Xlib Programmers +Manual, KillClient in the X Protocol Specification +.SH AUTHOR +Jim Fulton, MIT X Consortium +.br +Dana Chee, Bellcore |