Skip to content

Instantly share code, notes, and snippets.

@rvaiya
Created February 9, 2019 03:20
Show Gist options
  • Save rvaiya/8dea74a99e75902ec54d22e3d5403c9a to your computer and use it in GitHub Desktop.
Save rvaiya/8dea74a99e75902ec54d22e3d5403c9a to your computer and use it in GitHub Desktop.
Kensington Orbit scrollwheel as scroll toggle (x11).
/*
* 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