From 0903416a020ef8fe5ada9799a2c532433df3b520 Mon Sep 17 00:00:00 2001
From: khonkhortisan <[email protected]>
Date: Sun, 30 Nov 2014 20:57:47 -0800
Subject: [PATCH xkill] Use secondary mouse pointers
---
configure.ac | 2 +-
man/xkill.man | 8 +++-
xkill.c | 130 +++++++++++++++++++++++++++++++++++++++++++++-------------
3 files changed, 110 insertions(+), 30 deletions(-)
diff --git a/configure.ac b/configure.ac
index 6af4b04..32b68c0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -38,7 +38,7 @@ XORG_MACROS_VERSION(1.8)
XORG_DEFAULT_OPTIONS
# Checks for pkg-config packages
-PKG_CHECK_MODULES(XKILL, [x11 xmuu xproto >= 7.0.22])
+PKG_CHECK_MODULES(XKILL, [xi x11 xmuu xproto >= 7.0.22])
AC_CONFIG_FILES([
Makefile
diff --git a/man/xkill.man b/man/xkill.man
index 6cddc0a..23a2c4c 100644
--- a/man/xkill.man
+++ b/man/xkill.man
@@ -27,7 +27,7 @@
xkill - kill a client by its X resource
.SH SYNOPSIS
.B "xkill"
-[\-display \fIdisplayname\fP] [\-id \fIresource\fP] [\-button number] [\-frame] [\-all] [\-version]
+[\-display \fIdisplayname\fP] [\-id \fIresource\fP] [\-pointer id] [\-button number] [\-frame] [\-all] [\-version]
.SH DESCRIPTION
.PP
.I Xkill
@@ -48,6 +48,12 @@ 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 \-pointer \fIid\fP
+This option specifies the id of a mouse pointer that may be used in
+selecting a window to kill. This id must match the number given by xinput(1).
+If the pointer is specified, any other pointers are ignored.
+By default, every pointer is used.
+.TP 8
.B \-button \fInumber\fP
This option specifies the number of pointer button
that should be used in selecting a window to kill.
diff --git a/xkill.c b/xkill.c
index 32cb0cc..1e6a26c 100644
--- a/xkill.c
+++ b/xkill.c
@@ -45,6 +45,7 @@ from The Open Group.
#include <X11/Xos.h>
#include <X11/Xlib.h>
+#include <X11/extensions/XInput2.h>
#include <X11/cursorfont.h>
#include <X11/Xproto.h>
@@ -52,11 +53,12 @@ from The Open Group.
static char *ProgramName;
+#define SelectPointerAny (-1)
#define SelectButtonAny (-1)
#define SelectButtonFirst (-2)
static int parse_button ( char *s, int *buttonp );
-static XID get_window_id ( Display *dpy, int screen, int button, const char *msg );
+static XID get_window_id ( Display *dpy, int screen, int pointer_id, int button, const 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 );
@@ -80,6 +82,7 @@ usage(const char *errmsg)
" -display displayname X server to contact\n"
" -id resource resource whose client is to be killed\n"
" -frame don't ignore window manager frames\n"
+" -pointer id specific mouse pointer to use for killing a window\n"
" -button number specific button to be pressed to select window\n"
" -all kill all clients with top level windows\n"
" -version print version and exit\n"
@@ -101,12 +104,14 @@ main(int argc, char *argv[])
char *displayname = NULL; /* name of server to contact */
int screenno; /* screen number of dpy */
XID id = None; /* resource to kill */
+ int pointer_id; /* number of mouse pointer to grab */
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];
+ pointer_id = SelectPointerAny;
button = SelectButtonFirst;
for (i = 1; i < argc; i++) {
@@ -127,6 +132,10 @@ main(int argc, char *argv[])
Exit (1, dpy);
}
continue;
+ case 'p':
+ if (++i >= argc) usage ("-pointer requires an argument");
+ pointer_id = strtoul (argv[i], NULL, 0);
+ continue;
case 'b': /* -button number */
if (++i >= argc) usage ("-button requires an argument");
button_name = argv[i];
@@ -208,7 +217,7 @@ main(int argc, char *argv[])
button = (int) ((unsigned int) pointer_map[0]);
}
}
- if ((id = get_window_id (dpy, screenno, button,
+ if ((id = get_window_id (dpy, screenno, pointer_id, button,
"the window whose client you wish to kill"))) {
if (id == RootWindow(dpy,screenno)) id = None;
else if (!top) {
@@ -270,15 +279,18 @@ parse_button(char *s, int *buttonp)
}
static XID
-get_window_id(Display *dpy, int screen, int button, const char *msg)
+get_window_id(Display *dpy, int screen, int pointer_id, int button, const 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)
+ int xi_opcode, xi_event, xi_error, major, minor, rc;
+ XIEventMask eventmask; /* event filter */
+ unsigned char mask[1]; /* the actual mask */
+ XIDeviceInfo *devices, *device;
+ int i, ndevices;
root = RootWindow (dpy, screen);
cursor = XCreateFontCursor (dpy, XC_pirate);
@@ -288,6 +300,64 @@ get_window_id(Display *dpy, int screen, int button, const char *msg)
Exit (1, dpy);
}
+ /* initialize XI2 */
+ if (!XQueryExtension(dpy, "XInputExtension", &xi_opcode, &xi_event, &xi_error)) {
+ fprintf(stderr, "X Input extension not available.\n");
+ Exit (1, dpy);
+ }
+
+ /* We support XI 2.0 */
+ major = 2;
+ minor = 0;
+
+ rc = XIQueryVersion(dpy, &major, &minor);
+ if (rc == BadRequest) {
+ fprintf(stderr, "No XI2 support. Server supports version %d.%d only.\n", major, minor);
+ Exit (1, dpy);
+ }
+ else if (rc != Success) {
+ fprintf(stderr, "Internal Error! This is a bug in Xlib.\n");
+ }
+
+ printf("XI2 supported. Server provides version %d.%d.\n", major, minor);
+
+ /* Filter events */
+ mask[0] = 0;
+
+ eventmask.deviceid = 2;
+ eventmask.mask_len = sizeof(mask); /* always in bytes */
+ eventmask.mask = mask;
+ /* now set the mask */
+ XISetMask(mask, XI_ButtonPress);
+ XISetMask(mask, XI_ButtonRelease);
+
+ /* select on the window */
+ XISelectEvents(dpy, root, &eventmask, 1);
+
+ /* grab mouse pointer(s) */
+ devices = XIQueryDevice(dpy,
+ (pointer_id == SelectPointerAny) ? XIAllMasterDevices : pointer_id,
+ &ndevices);
+
+ for (i = 0; i < ndevices; i++) {
+ device = &devices[i];
+ if (device->use == XIMasterPointer) {
+ if ((rc = XIGrabDevice (dpy, device->deviceid, root, CurrentTime, cursor, GrabModeAsync, GrabModeAsync, False, &eventmask)) != GrabSuccess) {
+ fprintf(stderr, "%s: Grabbing pointer (id=%i) failed with %d\n", ProgramName, device->deviceid, rc);
+ Exit (1, dpy);
+ }
+ else {
+ printf("Grabbed pointer (id=%i)\n", device->deviceid);
+ }
+ XFlush(dpy);
+ }
+ else if (pointer_id != SelectPointerAny) {
+ fprintf(stderr, "%s: Not a master pointer device (id=%i)\n", ProgramName, pointer_id);
+ Exit (1, dpy);
+ }
+ }
+ XIFreeDeviceInfo(devices);
+
printf ("Select %s with ", msg);
if (button == -1)
printf ("any button");
@@ -296,34 +366,38 @@ get_window_id(Display *dpy, int screen, int button, const char *msg)
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, dpy);
- }
-
/* from dsimple.c in xwininfo */
while (retwin == None || pressed != 0) {
XEvent event;
+ XGenericEventCookie *cookie = &event.xcookie;
- 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--;
+ XNextEvent(dpy, &event);
+
+ if (cookie->type != GenericEvent ||
+ cookie->extension != xi_opcode)
continue;
- } /* end switch */
- } /* end for */
- XUngrabPointer (dpy, CurrentTime);
+ if (XGetEventData(dpy, cookie))
+ {
+ XIDeviceEvent *devev = cookie->data;
+ switch (devev->evtype)
+ {
+ case XI_ButtonPress:
+ if (retwin == None) {
+ retbutton = devev->detail;
+ retwin = ((devev->child != None) ?
+ devev->child : root);
+ }
+ pressed++;
+ continue;
+ case XI_ButtonRelease:
+ if (pressed > 0) pressed--;
+ continue;
+ } /* end switch */
+ XFreeEventData(dpy, &event.xcookie);
+ }
+ } /* end while */
+
XFreeCursor (dpy, cursor);
XSync (dpy, 0);
@@ -390,7 +464,7 @@ verify_okay_to_kill(Display *dpy, int screenno)
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) {
+ if (get_window_id (dpy, screenno, SelectPointerAny, button, msg) != root) {
okay = 0;
break;
}
--
2.1.3
_______________________________________________
[email protected]: X.Org development
Archives: http://lists.x.org/archives/xorg-devel
Info: http://lists.x.org/mailman/listinfo/xorg-devel