|
|
- /*
- * xbanish
- * Copyright (c) 2013-2021 joshua stein <jcs@jcs.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
- #include <err.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <unistd.h>
-
- #include <X11/X.h>
- #include <X11/Xlib.h>
- #include <X11/Intrinsic.h>
- #include <X11/extensions/Xfixes.h>
- #include <X11/extensions/XInput.h>
- #include <X11/extensions/XInput2.h>
-
- void hide_cursor(void);
- void show_cursor(void);
- void snoop_root(void);
- int snoop_xinput(Window);
- void snoop_legacy(Window);
- void usage(char *);
- int swallow_error(Display *, XErrorEvent *);
-
- /* xinput event type ids to be filled in later */
- static int button_press_type = -1;
- static int button_release_type = -1;
- static int key_press_type = -1;
- static int key_release_type = -1;
- static int motion_type = -1;
- static int device_change_type = -1;
- static long last_device_change = -1;
-
- static Display *dpy;
- static int hiding = 0, legacy = 0, always_hide = 0;
- static unsigned char ignored;
-
- static int debug = 0;
- #define DPRINTF(x) { if (debug) { printf x; } };
-
- static int move = 0, move_x, move_y;
- enum move_types {
- MOVE_NW = 1,
- MOVE_NE,
- MOVE_SW,
- MOVE_SE,
- };
-
- int
- main(int argc, char *argv[])
- {
- int ch, i;
- XEvent e;
- XGenericEventCookie *cookie;
-
- struct mod_lookup {
- char *name;
- int mask;
- } mods[] = {
- {"shift", ShiftMask}, {"lock", LockMask},
- {"control", ControlMask}, {"mod1", Mod1Mask},
- {"mod2", Mod2Mask}, {"mod3", Mod3Mask},
- {"mod4", Mod4Mask}, {"mod5", Mod5Mask},
- {"all", -1},
- };
-
- while ((ch = getopt(argc, argv, "adi:m:")) != -1)
- switch (ch) {
- case 'a':
- always_hide = 1;
- break;
- case 'd':
- debug = 1;
- break;
- case 'i':
- for (i = 0;
- i < sizeof(mods) / sizeof(struct mod_lookup); i++)
- if (strcasecmp(optarg, mods[i].name) == 0)
- ignored |= mods[i].mask;
-
- break;
- case 'm':
- if (strcmp(optarg, "nw") == 0)
- move = MOVE_NW;
- else if (strcmp(optarg, "ne") == 0)
- move = MOVE_NE;
- else if (strcmp(optarg, "sw") == 0)
- move = MOVE_SW;
- else if (strcmp(optarg, "se") == 0)
- move = MOVE_SE;
- else {
- warnx("invalid '-m' argument");
- usage(argv[0]);
- }
- break;
- default:
- usage(argv[0]);
- }
-
- argc -= optind;
- argv += optind;
-
- if (!(dpy = XOpenDisplay(NULL)))
- errx(1, "can't open display %s", XDisplayName(NULL));
-
- #ifdef __OpenBSD__
- if (pledge("stdio", NULL) == -1)
- err(1, "pledge");
- #endif
-
- XSetErrorHandler(swallow_error);
-
- snoop_root();
-
- if (always_hide)
- hide_cursor();
-
- for (;;) {
- cookie = &e.xcookie;
- XNextEvent(dpy, &e);
-
- int etype = e.type;
- if (e.type == motion_type)
- etype = MotionNotify;
- else if (e.type == key_press_type ||
- e.type == key_release_type)
- etype = KeyRelease;
- else if (e.type == button_press_type ||
- e.type == button_release_type)
- etype = ButtonRelease;
- else if (e.type == device_change_type) {
- XDevicePresenceNotifyEvent *xdpe =
- (XDevicePresenceNotifyEvent *)&e;
- if (last_device_change == xdpe->serial)
- continue;
- snoop_root();
- last_device_change = xdpe->serial;
- continue;
- }
-
- switch (etype) {
- case KeyRelease:
- if (ignored) {
- unsigned int state = 0;
-
- /* masks are only set on key release, if
- * ignore is set we must throw out non-release
- * events here */
- if (e.type == key_press_type) {
- break;
- }
-
- /* extract modifier state */
- if (e.type == key_release_type) {
- /* xinput device event */
- XDeviceKeyEvent *key =
- (XDeviceKeyEvent *) &e;
- state = key->state;
- } else if (e.type == KeyRelease) {
- /* legacy event */
- state = e.xkey.state;
- }
-
- if (state & ignored) {
- DPRINTF(("ignoring key %d\n", state));
- break;
- }
- }
-
- hide_cursor();
- break;
-
- case ButtonRelease:
- case MotionNotify:
- if (!always_hide)
- show_cursor();
- break;
-
- case CreateNotify:
- if (legacy) {
- DPRINTF(("new window, snooping on it\n"));
-
- /* not sure why snooping directly on the window
- * doesn't work, so snoop on all windows from
- * its parent (probably root) */
- snoop_legacy(e.xcreatewindow.parent);
- }
- break;
-
- case GenericEvent:
- /* xi2 raw event */
- XGetEventData(dpy, cookie);
- XIDeviceEvent *xie = (XIDeviceEvent *)cookie->data;
-
- switch (xie->evtype) {
- case XI_RawMotion:
- case XI_RawButtonPress:
- if (!always_hide)
- show_cursor();
- break;
-
- case XI_RawButtonRelease:
- break;
-
- default:
- DPRINTF(("unknown XI event type %d\n",
- xie->evtype));
- }
-
- XFreeEventData(dpy, cookie);
- break;
-
- default:
- DPRINTF(("unknown event type %d\n", e.type));
- }
- }
- }
-
- void
- hide_cursor(void)
- {
- Window win;
- int x, y, h, w, junk;
- unsigned int ujunk;
-
- DPRINTF(("keystroke, %shiding cursor\n", (hiding ? "already " : "")));
-
- if (hiding)
- return;
-
- if (move) {
- if (XQueryPointer(dpy, DefaultRootWindow(dpy),
- &win, &win, &x, &y, &junk, &junk, &ujunk)) {
- move_x = x;
- move_y = y;
-
- h = XHeightOfScreen(DefaultScreenOfDisplay(dpy));
- w = XWidthOfScreen(DefaultScreenOfDisplay(dpy));
-
- switch (move) {
- case MOVE_NW:
- x = 0;
- y = 0;
- break;
- case MOVE_NE:
- x = w;
- y = 0;
- break;
- case MOVE_SW:
- x = 0;
- y = h;
- break;
- case MOVE_SE:
- x = w;
- y = h;
- break;
- }
-
- XWarpPointer(dpy, None, DefaultRootWindow(dpy),
- 0, 0, 0, 0, x, y);
- } else {
- move_x = -1;
- move_y = -1;
- warn("failed finding cursor coordinates");
- }
- }
-
- XFixesHideCursor(dpy, DefaultRootWindow(dpy));
- hiding = 1;
- }
-
- void
- show_cursor(void)
- {
- DPRINTF(("mouse moved, %sunhiding cursor\n",
- (hiding ? "" : "already ")));
-
- if (!hiding)
- return;
-
- if (move && move_x != -1 && move_y != -1)
- XWarpPointer(dpy, None, DefaultRootWindow(dpy), 0, 0, 0, 0,
- move_x, move_y);
-
- XFixesShowCursor(dpy, DefaultRootWindow(dpy));
- hiding = 0;
- }
-
- void
- snoop_root(void)
- {
- if (snoop_xinput(DefaultRootWindow(dpy)) == 0) {
- DPRINTF(("no XInput devices found, using legacy snooping"));
- legacy = 1;
- snoop_legacy(DefaultRootWindow(dpy));
- }
- }
-
- int
- snoop_xinput(Window win)
- {
- int opcode, event, error, numdevs, i, j;
- int major, minor, rc, rawmotion = 0;
- int ev = 0;
- unsigned char mask[(XI_LASTEVENT + 7)/8];
- XDeviceInfo *devinfo = NULL;
- XInputClassInfo *ici;
- XDevice *device;
- XIEventMask evmasks[1];
- XEventClass class_presence;
-
- if (!XQueryExtension(dpy, "XInputExtension", &opcode, &event, &error)) {
- DPRINTF(("XInput extension not available"));
- return 0;
- }
-
- /*
- * If we support xinput 2, use that for raw motion and button events to
- * get pointer data when the cursor is over a Chromium window. We
- * could also use this to get raw key input and avoid the other XInput
- * stuff, but we may need to be able to examine the key value later to
- * filter out ignored keys.
- */
- major = minor = 2;
- rc = XIQueryVersion(dpy, &major, &minor);
- if (rc != BadRequest) {
- memset(mask, 0, sizeof(mask));
-
- XISetMask(mask, XI_RawMotion);
- XISetMask(mask, XI_RawButtonPress);
- evmasks[0].deviceid = XIAllMasterDevices;
- evmasks[0].mask_len = sizeof(mask);
- evmasks[0].mask = mask;
-
- XISelectEvents(dpy, win, evmasks, 1);
- XFlush(dpy);
-
- rawmotion = 1;
-
- DPRINTF(("using xinput2 raw motion events\n"));
- }
-
- devinfo = XListInputDevices(dpy, &numdevs);
- XEventClass event_list[numdevs * 2];
- for (i = 0; i < numdevs; i++) {
- if (devinfo[i].use != IsXExtensionKeyboard &&
- devinfo[i].use != IsXExtensionPointer)
- continue;
-
- if (!(device = XOpenDevice(dpy, devinfo[i].id)))
- break;
-
- for (ici = device->classes, j = 0; j < devinfo[i].num_classes;
- ici++, j++) {
- switch (ici->input_class) {
- case KeyClass:
- DPRINTF(("attaching to keyboard device %s "
- "(use %d)\n", devinfo[i].name,
- devinfo[i].use));
-
- DeviceKeyPress(device, key_press_type,
- event_list[ev]); ev++;
- DeviceKeyRelease(device, key_release_type,
- event_list[ev]); ev++;
- break;
-
- case ButtonClass:
- if (rawmotion)
- continue;
-
- DPRINTF(("attaching to buttoned device %s "
- "(use %d)\n", devinfo[i].name,
- devinfo[i].use));
-
- DeviceButtonPress(device, button_press_type,
- event_list[ev]); ev++;
- DeviceButtonRelease(device,
- button_release_type, event_list[ev]); ev++;
- break;
-
- case ValuatorClass:
- if (rawmotion)
- continue;
-
- DPRINTF(("attaching to pointing device %s "
- "(use %d)\n", devinfo[i].name,
- devinfo[i].use));
-
- DeviceMotionNotify(device, motion_type,
- event_list[ev]); ev++;
- break;
- }
- }
-
- XCloseDevice(dpy, device);
-
- if (XSelectExtensionEvent(dpy, win, event_list, ev)) {
- warn("error selecting extension events");
- ev = 0;
- goto done;
- }
- }
-
- DevicePresence(dpy, device_change_type, class_presence);
- if (XSelectExtensionEvent(dpy, win, &class_presence, 1)) {
- warn("error selecting extension events");
- ev = 0;
- goto done;
- }
-
- done:
- if (devinfo != NULL)
- XFreeDeviceList(devinfo);
-
- return ev;
- }
-
- void
- snoop_legacy(Window win)
- {
- Window parent, root, *kids = NULL;
- XSetWindowAttributes sattrs;
- unsigned int nkids = 0, i;
-
- /*
- * Firefox stops responding to keys when KeyPressMask is used, so
- * settle for KeyReleaseMask
- */
- int type = PointerMotionMask | KeyReleaseMask | Button1MotionMask |
- Button2MotionMask | Button3MotionMask | Button4MotionMask |
- Button5MotionMask | ButtonMotionMask;
-
- if (XQueryTree(dpy, win, &root, &parent, &kids, &nkids) == FALSE) {
- warn("can't query window tree\n");
- goto done;
- }
-
- XSelectInput(dpy, root, type);
-
- /* listen for newly mapped windows */
- sattrs.event_mask = SubstructureNotifyMask;
- XChangeWindowAttributes(dpy, root, CWEventMask, &sattrs);
-
- for (i = 0; i < nkids; i++) {
- XSelectInput(dpy, kids[i], type);
- snoop_legacy(kids[i]);
- }
-
- done:
- if (kids != NULL)
- XFree(kids); /* hide yo kids */
- }
-
- void
- usage(char *progname)
- {
- fprintf(stderr, "usage: %s [-a] [-d] [-i mod] [-m nw|ne|sw|se]\n",
- progname);
- exit(1);
- }
-
- int
- swallow_error(Display *d, XErrorEvent *e)
- {
- if (e->error_code == BadWindow)
- /* no biggie */
- return 0;
- else if (e->error_code & FirstExtensionError)
- /* error requesting input on a particular xinput device */
- return 0;
- else
- errx(1, "got X error %d", e->error_code);
- }
|