vulgar gestures
I like to go on the internet and click on links, but some of the links are bad, so then I swipe right to make it go away. The problem is that when I’m running chrome on OpenBSD, the swipe gesture doesn’t seem to work like it does on other platforms. We’re not going to fix it, but we are going to make it work. (Although, I hear the younglings say they swipe right when they like something. Explains a lot, actually.)
We are of course not the first to journey down this road. The first suggested fix is obviously useless, so we’re going to ignore that. The second suggestion is some --enable-xinput2 command option. That doesn’t work. The third suggestion is to dig around in chrome flags and enable some overscroll option which doesn’t exist.
At this point, we’re in danger of learning too much about how chrome works, so it’s time for one of my favorite problem solving techniques. When something doesn’t work, find something that does, and then find a way to do the thing that works. In this case, I know that pressing alt-left on the keyboard goes back. So we don’t need to find a way to make swipe go back, we need to make swipe right press alt-left.
Quick side note on the fact that some (ThinkPad) keyboards of a certain vintage had forward and back keys, right there on the keyboard, but it was always a bit fussy getting applications to respond to their exotic scan codes, so we’ll just stick with alt-left.
Direction set, all we need to do is write the code. On OpenBSD, input is handled by wscons, which is why I run wsconsctl to reverse scrolling direction. So I figure src/sys/dev/wscons is a good place to start looking. A grep for “finger” later, and we’re looking through wstpad.c. The code calls the relevant variable contacts, and some more searching brings us to wstpad_f2scroll, the function that controls two finger scrolling.
This function, as the name implies, only wants to look at two fingers, but we can hijack it. There’s a loop elsewhere in the file that calls all the event handlers, but we’re not interested in appropriate placement at this time. We just want to distinguish two finger scrolls from three finger gestures. The code currently ignores any three contact scroll, but we can relax that check. Two fingers scroll, three fingers gesture.
if (tp->ignore == 0) {
- if (tp->contacts != 2)
+ if (tp->contacts == 3)
+ gesture = 1;
+ else if (tp->contacts != 2)
return;
And with that in place, we start with a simple printf since I have no idea what magnitude deltas we’ll be working with. Reboot, make a few test swipes, inspect the log, and it seems like maybe a sum of 400 constitutes a gesture. We can embellish the code a bit more.
@@ -525,9 +529,13 @@ wstpad_f2scroll(struct wsmouseinput *inp
struct wstpad *tp = input->tp;
struct tpad_touch *t2;
int dir, dx, dy, centered;
+ int gesture = 0;
+ static int gesturex, gesturey, gdone;
if (tp->ignore == 0) {
- if (tp->contacts != 2)
+ if (tp->contacts == 3)
+ gesture = 1;
+ else if (tp->contacts != 2)
return;
} else if (tp->contacts != 3 || (tp->ignore == input->mt.ptr)) {
return;
@@ -541,6 +549,46 @@ wstpad_f2scroll(struct wsmouseinput *inp
dy = 0;
if (!(EAST(dir) || WEST(dir)))
dx = 0;
+
+ if (gesture) {
+ if (gdone)
+ return;
+ gesturex += dx;
+ gesturey += dy;
+ printf("gesture %d %d %d\n", dy, dx, gesturex);
+ if (gesturex < -400) {
+ printf("go right\n");
+ } else if (gesturex > 400) {
+ printf("go left\n");
+ }
+ return;
+ } else {
+ gdone = 0;
+ gesturex = 0;
+ gesturey = 0;
+ }
Another reboot to verify this is about what I want, and now it’s a matter of turning the printf into here’s a key press. Unfortunately, we are in the mouse driver. Actually, we’re in a helper function in a helper module which is in some way interfacing with the mouse driver, and I’m not certain how it all fits together, but the keyboard is over there and we are over here.
Ah, well, we switch over to wskbd.c and look for something useful. Yes, yes, wskbd_console_device, this will do nicely. And in a rare twist for global variables in the OpenBSD kernel, it’s even marked static. That’s how we know it’s a good one. But no need to stand on ceremony, we’re all friends here, so no more static.
After some futile attempts to make wskbd_input work, and after fully decorating the function with printfs, I remembered that X is almost certainly doing its own scan code conversion. We need to be using wskbd_rawinput, but I’m not sure what to input. Okay, printf again to the rescue. I print out the bytes passing through rawinput whenever I press a key on the (real) keyboard, and (after some hilarity discovering the difference between "%20x"
and "%02x"
format strings) now know exactly what to pass down. 0xe0 to say here comes something special, then the key, and 0x80 for release.
const int alt = 0x38, left = 0x4b, right = 0x4d, tab = 0x0f;
int key = 0;
if (gesturex < -400) {
key = right;
} else if (gesturex > 400) {
key = left;
} else if (gesturey > 300) {
key = tab;
}
if (key) {
struct device *cons = (struct device *)wskbd_console_device;
char cbuf[32];
int j = 0;
cbuf[j++] = 0xe0;
cbuf[j++] = alt;
if (key != tab)
cbuf[j++] = 0xe0;
cbuf[j++] = key;
if (key != tab)
cbuf[j++] = 0xe0;
cbuf[j++] = key | 0x80;
cbuf[j++] = 0xe0;
cbuf[j++] = alt | 0x80;
wskbd_rawinput(cons, cbuf, j);
gdone = 1;
}
I also added an up gesture to switch desktops, one of the few actual gestures I occasionally use on other systems. It takes three fingers to swipe right instead of two fingers, but that’s not difficult to adapt to, and importantly, I don’t have to constantly shuffle my hands around from touchpad to keyboard just browsing around.
This may not be the proper way to make gestures, but the purpose of communication is to convey intent, and by that measure I’d say they’re just fine.