Created
February 9, 2019 03:20
-
-
Save rvaiya/8dea74a99e75902ec54d22e3d5403c9a to your computer and use it in GitHub Desktop.
Kensington Orbit scrollwheel as scroll toggle (x11).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Author: Raheman Vaiya | |
* License: WTFPL (do you really need one?) | |
* */ | |
#include <stdio.h> | |
#include <sys/time.h> | |
#include <sys/types.h> | |
#include <dirent.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <X11/Xlib.h> | |
#include <linux/input.h> | |
#include <X11/extensions/XInput.h> | |
#include <X11/Xatom.h> | |
#include <stdlib.h> | |
#include <fcntl.h> | |
/* | |
* Ugly kludge which uses the scrollwheel on a kensington orbit as a toggle for | |
* using the ball to scroll (i.e treats scroll down as a sticky wheel emulation | |
* toggle). This sits between the kernel evdev interface and the xorg evdev | |
* driver since the latter does not discriminate between the scroll wheel and | |
* ball when wheel emulation is enabled. This probably could have been written | |
* as an evdev patch instead of a userspace program but aint nobody got time for | |
* that. I doubt anyone else will have any interest in this but given the number | |
* of man pages and old code I had to scour in order to do this I am publishing it | |
* as a gist anyway. | |
* | |
* Use | |
* | |
* gcc -lXi -lX11 orbit.c | |
* | |
* to compile */ | |
XDevice *get_device(Display *dpy, const char *devname) { | |
int n; | |
XDeviceInfo *devices, *target; | |
devices = XListInputDevices(dpy, &n); | |
target = NULL; | |
for (int i = 0; i < n; i++) { | |
if(!strcmp(devname, devices[i].name)) { | |
target = XOpenDevice(dpy, devices[i].id); | |
break; | |
} | |
} | |
XFreeDeviceList(devices); | |
return target; | |
} | |
void set_int_prop(Display *dpy, XDevice *dev, char *prop_name, int val) { | |
Atom type; | |
int fmt; | |
long nitems, bytes; | |
char *ret; | |
Atom prop = XInternAtom(dpy, prop_name, True); | |
if(prop == None) { | |
fprintf(stderr, "Attempted to intern invalid prop: %s\n", prop_name); | |
exit(1); | |
} | |
XGetDeviceProperty(dpy, dev, prop, 0, 1, False, AnyPropertyType, &type, &fmt, &nitems, &bytes, &ret); | |
if(type != XA_INTEGER) { | |
fprintf(stderr, "%s is not a valid integer property\n", prop_name); | |
exit(1); | |
} | |
if(nitems != 1) { | |
fprintf(stderr, "%s requires more than one int\n", prop_name); | |
exit(1); | |
} | |
XChangeDeviceProperty(dpy, dev, prop, XA_INTEGER, fmt, PropModeReplace, (unsigned char*)&val, 1); | |
XFlush(dpy); | |
} | |
int get_event_fd(const char *devname) { | |
char path[256]; | |
struct dirent *ent; | |
DIR *dir = opendir("/dev/input"); | |
while((ent = readdir(dir))) { | |
if(strstr(ent->d_name, "event") == ent->d_name) { | |
char name[256]; | |
int fd; | |
sprintf(path, "/dev/input/%s", ent->d_name); | |
fd = open(path, O_RDONLY); | |
if(fd == -1) { | |
perror("open"); | |
exit(1); | |
} | |
if(ioctl(fd, EVIOCGNAME(sizeof(name)), name) == -1) { | |
perror("ioctl"); | |
exit(1); | |
} | |
if(!strcmp(devname, name)) return fd; | |
close(fd); | |
} | |
} | |
fprintf(stderr, "Failed to find device with name: %s\n", devname); | |
exit(1); | |
} | |
void main() { | |
int set = 0; | |
int fd; | |
struct input_event ev; | |
Display *dpy; | |
XDevice *xtrackball; | |
unsigned char scrollmap[] = { 1, 2, 3, 4, 5, 6, 7 }; | |
unsigned char noscrollmap[] = { 1, 2, 3, 0, 0, 6, 7 }; | |
const char *device_name = "Primax Kensington Eagle Trackball"; | |
dpy = XOpenDisplay(NULL); | |
xtrackball = get_device(dpy, device_name); | |
if(!xtrackball) { | |
fprintf(stderr, "Trackball does not appear to be connected\n"); | |
exit(1); | |
} | |
fd = get_event_fd(device_name); | |
if(fd == -1) { | |
perror("open"); | |
exit(1); | |
} | |
while(1) { | |
read(fd, &ev, sizeof(ev)); | |
if(ev.type == EV_REL && ev.code == REL_WHEEL) { | |
if((ev.value == 1) && set) { //Scroll Up | |
set = 0; | |
set_int_prop(dpy, xtrackball, "Evdev Wheel Emulation", 0); | |
set_int_prop(dpy, xtrackball, "Evdev Wheel Emulation Button", 0); | |
XSetDeviceButtonMapping(dpy, | |
xtrackball, | |
noscrollmap, | |
sizeof(scrollmap)/sizeof(scrollmap[0])); | |
} else if((ev.value != 1) && !set) { //Scroll Down | |
set = 1; | |
set_int_prop(dpy, xtrackball, "Evdev Wheel Emulation", 1); | |
set_int_prop(dpy, xtrackball, "Evdev Wheel Emulation Button", 0); | |
XSetDeviceButtonMapping(dpy, | |
xtrackball, | |
scrollmap, | |
sizeof(scrollmap)/sizeof(scrollmap[0])); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment