Search code examples
clinuxmousex11evdev

How to capture legitimately and programmatically a second mouse or a trackball under linux, X?


I am designing and implementing an experimental GUI application. I wish to allow my users to ergonomically use both hands simultaneously to interact with the application. As an example, imagine GIMP and a second mouse (a trackball) in your left (other) hand: you can navigate, pan, rotate or zoom the picture with your left hand, while drawing/editing point-accurate dots, lines or figures with your right hand at the same time. Back in 2000 ~ 2002 I did some experiments with two mice: one on PS2 (USB) interface and another one on RS232 serial interface. Nowadays, many years later, I am revisiting that ancient idea, with modern software and contemporary (USB) input devices.

In an ideal case, my goal is to attach a second mouse (or a trackball) to a GNU/ Linux system with X and then be able to:

  • (1.) Keep up using the primary mouse as the X default master pointer.
  • (2.) Programmatically access / read events from the second mouse.
  • (3.) Get the events from the second mouse separately (in a discernible way) from the primary mouse.
  • (4.) Be able to "consume" the events from the second mouse, so that whenever my program is started, the second mouse shall not affect the master pointer.
  • (5.) Preferably, the second mouse shall be captured only when the GUI window of my program has the focus.
  • (6.) Preferably, the complete solution shall not depend on other packages or libraries, like gpm for example. However, it is not a problem if (any) udev rules are required, as the end user would need to install my program anyway.
  • (7.) Preferably the solution shall not need any extra privileges or permissions like adding the user to "input" group, for example.

I have spent 6+ hours searching Internet for hints, but regrettably I did not go too far beyond some "dirty" partial solution, with several disadvantages:

  • (A.) Add the user to "input" group.
  • (B.) Capture the events from the second mouse as in the example program below.
  • (C.) Open point: look further as how to "consume" the events from the second mouse?
  • (D.) Cope with global capturing (not only the window with focus) and other issues?

...

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>

int main(int argc, char **argv)
{
  int ret=1; /* Program's return value; 0=success. */
  int fd; /* File descriptor (handler) */
  struct input_event ev;

  if (argc != 2) {
    printf("Usage:\n rdinpdev <device>\n"
      "Print events from an input device.\n"
    );
  }
  else {
    fd = open(argv[1], O_RDONLY);
    printf("open returned %d\n",fd);
    if(fd >= 0) {
      ret=0;
      while(1) {
        read(fd, &ev, sizeof(struct input_event));
        printf("value %d, type %d, code %d\n",ev.value,ev.type,ev.code);
      }
    }
  }
  return ret;
}

Solution

  • Short answer: The X Input Device Extension

    Somewhat longer answer: X has a concept of core input devices which are usually the keyboard and mouse, and additional input devices which can be queried with the extension mentioned above. These alternative inputs can generate events if you ask them to.

    Here's a short piece of code that will list all input devices:

    #include <stdio.h>
    
    #include <X11/Xlib.h>
    #include <X11/extensions/XInput.h>
    
    int main (int argc, char *argv[])
    {
    
      Display *d = XOpenDisplay (0);
    
      if (!d)
      {
        printf ("Failed to open display.\n");
        return 1;
      }
    
      int i, num;
      XDeviceInfo *dev;
    
      dev = XListInputDevices (d, &num);
      printf ("Found %d input devices:\n", num);
    
      for (i = 0; i < num; i++)
      {
        printf ("  name: %s; classes: %d; use: %d\n", dev[i].name, dev[i].num_classes, dev[i].use);
      }
    
      XCloseDisplay (d);
      return 0;
    }
    

    Makefile:

    second-input: second-input.o
           $(CC) -o $@ $< -lX11 -lXi
    
    .o:.c
           $(CC) -g -o $@ $<
    

    I have a Wacom drawing tablet connected to my system but this code lists even more, some that I didn't expect:

    Found 10 input devices:
      name: Virtual core pointer; classes: 2; use: 0
      name: Virtual core keyboard; classes: 1; use: 1
      name: Virtual core XTEST pointer; classes: 2; use: 4
      name: Virtual core XTEST keyboard; classes: 1; use: 3
      name: Power Button; classes: 1; use: 3
      name: Power Button; classes: 1; use: 3
      name: USB Mouse              ; classes: 2; use: 4
      name: Wacom Volito Pen; classes: 0; use: 2
      name: UVC Camera (046d:081d); classes: 1; use: 3
      name: AT Translated Set 2 keyboard; classes: 1; use: 3