#include <stdio.h>
#include <stdlib.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)
static int parse_button ( char *s, int *buttonp );
static XID parse_id ( char *s );
static XID get_window_id ( Display *dpy, int screen, int button, char *msg );
static int catch_window_errors ( Display *dpy, XErrorEvent *ev );
static int kill_all_windows ( Display *dpy, int screenno, Bool top );
static int verify_okay_to_kill ( Display *dpy, int screenno );
static Bool wm_state_set ( Display *dpy, Window win );
static Bool wm_running ( Display *dpy, int screenno );
static void
Exit(int code)
{
if (dpy) {
XCloseDisplay (dpy);
}
exit (code);
}
static void
usage(void)
{
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);
}
int
main(int argc, char *argv[])
{
int i;
char *displayname = NULL;
int screenno;
XID id = None;
char *button_name = NULL;
int button;
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':
if (++i >= argc) usage ();
displayname = argv[i];
continue;
case 'i':
if (++i >= argc) usage ();
id = parse_id (argv[i]);
continue;
case 'b':
if (++i >= argc) usage ();
button_name = argv[i];
continue;
case 'f':
top = True;
continue;
case 'a':
kill_all = True;
continue;
default:
usage ();
}
} else {
usage ();
}
}
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 (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];
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) {
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 {
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) {
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);
XKillClient (dpy, id);
XSync (dpy, 0);
}
Exit (0);
return 0;
}
static int
parse_button(char *s, int *buttonp)
{
register char *cp;
for (cp = s; *cp; cp++) {
if (isascii (*cp) && isupper (*cp)) {
#ifdef _tolower
*cp = _tolower (*cp);
#else
*cp = tolower (*cp);
#endif
}
}
if (strcmp (s, "any") == 0) {
*buttonp = SelectButtonAny;
return (1);
}
for (cp = s; *cp; cp++) {
if (!(isascii (*cp) && isdigit (*cp))) return (0);
}
*buttonp = atoi (s);
return (1);
}
static XID
parse_id(char *s)
{
XID retval = None;
char *fmt = "%ld";
if (s) {
if (*s == '0') s++, fmt = "%lo";
if (*s == 'x' || *s == 'X') s++, fmt = "%lx";
sscanf (s, fmt, &retval);
}
return (retval);
}
static XID
get_window_id(Display *dpy, int screen, int button, char *msg)
{
Cursor cursor;
Window root;
Window retwin = None;
int retbutton = -1;
int pressed = 0;
#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);
if (XGrabPointer (dpy, root, False, MASK, GrabModeSync, GrabModeAsync,
None, cursor, CurrentTime) != GrabSuccess) {
fprintf (stderr, "%s: unable to grab cursor\n", ProgramName);
Exit (1);
}
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;
}
}
XUngrabPointer (dpy, CurrentTime);
XFreeCursor (dpy, cursor);
XSync (dpy, 0);
return ((button == -1 || retbutton == button) ? retwin : None);
}
static int
catch_window_errors(Display *dpy, XErrorEvent *ev)
{
return 0;
}
static int
kill_all_windows(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);
return 0;
}
static int
verify_okay_to_kill(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;
if (get_window_id (dpy, screenno, button, msg) != root) {
okay = 0;
break;
}
okay++;
}
if (okay) {
return 1;
} else {
printf ("Aborting.\n");
return 0;
}
}
static Bool
wm_state_set(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);
}
static Bool
wm_running(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)));
}