From 0903416a020ef8fe5ada9799a2c532433df3b520 Mon Sep 17 00:00:00 2001
From: khonkhortisan <khonkhorti...@gmail.com>
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

_______________________________________________
xorg-devel@lists.x.org: X.Org development
Archives: http://lists.x.org/archives/xorg-devel
Info: http://lists.x.org/mailman/listinfo/xorg-devel

Reply via email to