Computing Eureka Logo

evdevPatch

I spent a little while finding the right place to put two lines of code to enforce natural scrolling on my computer. Read on for the story, or scroll to the end for my fix.

Explanation

Natural scrolling is a feature introduced by Apple in OS X Lion. Basically, it is reversed scrolling. The user who wants this considers their finger to be moving the content, not the scroll bar. Apple separates the settings between trackpads and regular mice. Personally, I like both set to natural scrolling.

The Old Fix

xmodmap -e "pointer = 1 2 3 5 4"
This little bit of code is a wonderful workaround to get natural scrolling in X11.

The Problem

I use Gnome 3. Gnome 3's default apps use new smooth scroll UI elements from their gtk. Unfortunately, these new smooth scroll regions don't respect X11 buttons. They do their own thing with relative events so as to detect how fast the user has scrolled.
Now, Gnome 3's settings panel has a natural scrolling option, but it only affects trackpads. My workaround has been to use the old fix above and leave the natural scrolling off. But this makes everything backwards in any of the new smooth scroll regions. I had been living with it since switching to Gnome 3.

An Idea For A Better Fix

Changing the buttons at the X11 level was too high up. I needed a lower level fix. A hardware fix would work and wouldn't even be computer dependent, but I decided not to break my mouse. I decided to try to fix it at the driver level. I use Gentoo Linux and manually compile the Liunx kernel (with the Gentoo patches). So, I started exploring the source files in /usr/src/linux/drivers/input/. After a while, I found the right place in /usr/src/linux/drivers/input/evdev.c.

The Actual Fix

I was on kernel 3.12.10-r1 at the time I created this. I'd like to keep an up to date patch up, but I know that will never happen. So, if things change, you'll have to figure out what to do.
Thanks should go to the author of this page.
static void evdev_pass_values(struct evdev_client *client,
                        const struct input_value *vals, unsigned int count,
                        ktime_t mono, ktime_t real)
{
        struct evdev *evdev = client->evdev;
        const struct input_value *v;
        struct input_event event;
        bool wakeup = false;

        if (client->revoked)
                return;

        event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
                                      mono : real);

        /* Interrupts are disabled, just acquire the lock. */
        spin_lock(&client->buffer_lock);

        for (v = vals; v != vals + count; v++) {
                event.type = v->type;
                event.code = v->code;
                event.value = v->value;

                /* ----------I added this section.---------- */
                if(event.type == EV_REL && (event.code == REL_WHEEL || event.code == REL_HWHEEL))
                        event.value = -event.value;
                /* ----------  But only to here.  ---------- */

                __pass_event(client, &event);
                if (v->type == EV_SYN && v->code == SYN_REPORT)
                        wakeup = true;
        }

        spin_unlock(&client->buffer_lock);

        if (wakeup)
                wake_up_interruptible(&evdev->wait);
}
Last modified 23:34, Jul 9, 2014 (UTC)