Skip to content

Instantly share code, notes, and snippets.

@CyberShadow
Last active December 21, 2018 01:01
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CyberShadow/ae30a8d9f86c170c2451c3dd7edb649c to your computer and use it in GitHub Desktop.
Save CyberShadow/ae30a8d9f86c170c2451c3dd7edb649c to your computer and use it in GitHub Desktop.
uinput-filter-mouse - program to filter and rewrite uinput events
/uinput-filter-mouse
/config.h
#define UPSTREAM_UINPUT_DEVICE "/dev/input/by-id/usb-Logitech_G700_Laser_Mouse_37ADAF750037-event-mouse"
uinput-filter-mouse: Makefile uinput-filter-mouse.c config.h
gcc -o $@ $@.c
config.h:
@echo "Copy and edit \`config.h.sample' to \`config.h' first."
@exit 1
install:
install uinput-filter-mouse /usr/local/bin/
install -m 644 uinput-filter-mouse.service /etc/systemd/system
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include "config.h"
#define die(str, args...) do { \
perror(str); \
exit(EXIT_FAILURE); \
} while(0)
int
main(int argc, char* argv[])
{
int fdo, fdi;
struct uinput_user_dev uidev;
struct input_event ev;
int i;
//if(argc != 2) die("error: specify input device");
fdo = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
if(fdo < 0) die("error: open");
//fdi = open(argv[1], O_RDONLY);
fdi = open(UPSTREAM_UINPUT_DEVICE, O_RDONLY);
if(fdi < 0) die("error: open");
if(ioctl(fdi, EVIOCGRAB, 1) < 0) die("error: ioctl");
if(ioctl(fdo, UI_SET_EVBIT, EV_SYN) < 0) die("error: ioctl");
if(ioctl(fdo, UI_SET_EVBIT, EV_KEY) < 0) die("error: ioctl");
if(ioctl(fdo, UI_SET_EVBIT, EV_REL) < 0) die("error: ioctl");
if(ioctl(fdo, UI_SET_EVBIT, EV_MSC) < 0) die("error: ioctl");
for(i = 0; i < KEY_MAX; ++i)
if(ioctl(fdo, UI_SET_KEYBIT, i) < 0) die("error: ioctl");
for(i = 0; i < REL_MAX; ++i)
if(ioctl(fdo, UI_SET_RELBIT, i) < 0) die("error: ioctl");
memset(&uidev, 0, sizeof(uidev));
snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "my-uinput-filter");
uidev.id.bustype = BUS_USB;
uidev.id.vendor = 0x1;
uidev.id.product = 0x1;
uidev.id.version = 1;
if(write(fdo, &uidev, sizeof(uidev)) < 0) die("error: write");
if(ioctl(fdo, UI_DEV_CREATE) < 0) die("error: ioctl");
while(1)
{
if(read(fdi, &ev, sizeof(struct input_event)) < 0)
die("error: read");
//printf("Got event! type=%d code=%d value=%d\n", ev.type, ev.code, ev.value);
//ev.time.tv_sec = 0;
//ev.time.tv_usec = 0;
if (ev.type == EV_KEY)
{
int key = ev.code;
if (key == BTN_EXTRA || key == BTN_SIDE)
{
ev.code = KEY_LEFTMETA;
if(write(fdo, &ev, sizeof(struct input_event)) < 0)
die("error: write");
if (key == BTN_EXTRA)
ev.code = BTN_LEFT;
if (key == BTN_SIDE)
ev.code = BTN_RIGHT;
if(write(fdo, &ev, sizeof(struct input_event)) < 0)
die("error: write");
}
}
if(write(fdo, &ev, sizeof(struct input_event)) < 0)
die("error: write");
}
if(ioctl(fdo, UI_DEV_DESTROY) < 0) die("error: ioctl");
close(fdi);
close(fdo);
return 0;
}
[Unit]
Description=uinput mouse filter
[Service]
ExecStart=/usr/local/bin/uinput-filter-mouse
Restart=always
[Install]
WantedBy=default.target
@aksel
Copy link

aksel commented Sep 15, 2017

I'm don't have a lot of experience in C, so I was wondering, can you help me out a little bit?

The button code I'm trying to map is 0x118. I have gotten it to work with binding it to KEY_LEFTMETA + BTN_LEFT.

I would also like to bind it to BTN_RIGHT when leftmeta is already pressed. However, it doesn't seem to work. Any pointers what I'm doing wrong?

Here's the modified code.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/input.h>
#include <linux/uinput.h>

#include "config.h"

#define die(str, args...) do { \
        perror(str); \
        exit(EXIT_FAILURE); \
    } while(0)

int
main(int argc, char* argv[])
{
    int                    fdo, fdi;
    struct uinput_user_dev uidev;
    struct input_event     ev;
    int                    i;
    int                    mod_pressed;

    //if(argc != 2) die("error: specify input device");

    fdo = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
    if(fdo < 0) die("error: open");

    //fdi = open(argv[1], O_RDONLY);
    fdi = open(UPSTREAM_UINPUT_DEVICE, O_RDONLY);
    if(fdi < 0) die("error: open");

    if(ioctl(fdi, EVIOCGRAB, 1) < 0) die("error: ioctl");

    if(ioctl(fdo, UI_SET_EVBIT, EV_SYN) < 0) die("error: ioctl");
    if(ioctl(fdo, UI_SET_EVBIT, EV_KEY) < 0) die("error: ioctl");
    if(ioctl(fdo, UI_SET_EVBIT, EV_REL) < 0) die("error: ioctl");
    if(ioctl(fdo, UI_SET_EVBIT, EV_MSC) < 0) die("error: ioctl");

    for(i = 0; i < KEY_MAX; ++i)
        if(ioctl(fdo, UI_SET_KEYBIT, i) < 0) die("error: ioctl");
    for(i = 0; i < REL_MAX; ++i)
        if(ioctl(fdo, UI_SET_RELBIT, i) < 0) die("error: ioctl");

    memset(&uidev, 0, sizeof(uidev));
    snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "my-uinput-filter");
    uidev.id.bustype = BUS_USB;
    uidev.id.vendor  = 0x1;
    uidev.id.product = 0x1;
    uidev.id.version = 1;

    if(write(fdo, &uidev, sizeof(uidev)) < 0) die("error: write");
    if(ioctl(fdo, UI_DEV_CREATE) < 0) die("error: ioctl");

    mod_pressed = 0;
    while(1)
    {
        if(read(fdi, &ev, sizeof(struct input_event)) < 0)
            die("error: read");

        //printf("Got event! type=%d code=%d value=%d\n", ev.type, ev.code, ev.value);
        //ev.time.tv_sec = 0;
        //ev.time.tv_usec = 0;

        if (ev.type == EV_KEY)
        {
            if (ev.code == KEY_LEFTMETA)
            {
                mod_pressed = ev.value; // 0 if released, 1 if pressed.
            }
            
            else if (ev.code == 0x118)
            {
                if (mod_pressed)
                {
                    ev.code = BTN_RIGHT;
                }
                
                else
                {
                    ev.code = KEY_LEFTMETA;
                    if(write(fdo, &ev, sizeof(struct input_event)) < 0)
                        die("error: write");
        
                    ev.code = BTN_LEFT;
                }
            }
        }

        if(write(fdo, &ev, sizeof(struct input_event)) < 0)
            die("error: write");
    }

    if(ioctl(fdo, UI_DEV_DESTROY) < 0) die("error: ioctl");

    close(fdi);
    close(fdo);

    return 0;
}

@CyberShadow
Copy link
Author

@aksel Sorry for the late reply, I don't think I saw your message until now. I'm not quite clear on what you're trying to achieve - you want to translate key 0x118 to KEY_LEFTMETA + BTN_LEFT, but also translate KEY_LEFTMETA + 0x118 to BTN_RIGHT?

@aksel
Copy link

aksel commented Jul 16, 2018

I haven't seen your reply until now

Gist stuff doesn't trigger a notification, apparently

But yeah, that was what I wanted to do.

0x118 -> KEY_LEFTMETA + BTN_LEFT (for moving floating)
KEY_LEFTMETA + 0x118 -> KEY_LEFTMETA + BTN_RIGHT (for resizing)

I have since gotten a new mouse, that has an extra button that I have bound to resizing. But for future reference, how
would I have achieved the other thing?

@aksel
Copy link

aksel commented Jul 17, 2018

Hahaha, how to contact you... Maybe a ping @CyberShadow will work?

@CyberShadow
Copy link
Author

Gist stuff doesn't trigger a notification, apparently

It seems to be a long-standing omission: isaacs/github#21

These's apparently a third-party service: https://giscus.co/

But for future reference, how
would I have achieved the other thing?

Your code looks correct... I don't know why it wouldn't work.

I would try debugging it with more logging in the program, and looking at what ends up in xev.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment