Previous Thread
Next Thread
Print Thread
Page 1 of 2 1 2
Joined: Feb 2014
Posts: 1,016
Likes: 110
G
Very Senior Member
OP Offline
Very Senior Member
G
Joined: Feb 2014
Posts: 1,016
Likes: 110
Hi guys,

I've got a million usb keyboards around here and can't really use them as extra controllers in linux since SDL doesn't seem to handle multi keyboard.

Thinking about the problem, I've been looking at uinput, where you can generate virtual input events.

So if I can open a keyboard in exclusive mode with EVIOCGRAB, read the key events and then translate them into button presses, then I can use the keyboard as a bunch of buttons.

Looking in input-event-codes.h there's a bunch of buttons defined:

Code
locate input-event-codes.h
less /usr/src/linux-headers-5.4.0-67/include/uapi/linux/input-event-codes.h

#define BTN_MISC                0x100
#define BTN_0                   0x100
#define BTN_1                   0x101
#define BTN_2                   0x102
#define BTN_3                   0x103
#define BTN_4                   0x104
#define BTN_5                   0x105
#define BTN_6                   0x106
#define BTN_7                   0x107
#define BTN_8                   0x108
#define BTN_9                   0x109

#define BTN_DPAD_UP             0x220
#define BTN_DPAD_DOWN           0x221
#define BTN_DPAD_LEFT           0x222
#define BTN_DPAD_RIGHT          0x223

#define BTN_TRIGGER_HAPPY               0x2c0
#define BTN_TRIGGER_HAPPY1              0x2c0
...
#define BTN_TRIGGER_HAPPY40             0x2e7

So if I generate button events, they are handled as though they came from distinct controllers. This is in contrast to keyboard key events which all keyboards are treated as a single keyboard.
Also, one should stay away from mouse button events like BTN_RIGHT because multiple mice are treated as a single mouse. I generated a bunch of BTN_RIGHT events and it was hard to get control back of the system.


Another warning, if you are messing around with grabbing keyboards in exclusive mode, you definitely need more than 1 keyboard, because hitting CTRL+C to kill the program must be done on the non-exclusive keyboard.



I grabbed a lot of sample code from around the net, sample code from:
https://gist.github.com/matthewaveryusa/a721aad80ae89a5c69f7c964fa20fec1
https://github.com/GrantEdwards/uinput-joystick-demo
https://www.kernel.org/doc/html/v4.12/input/uinput.html
https://blog.marekkraus.sk/c/linuxs-uinput-usage-tutorial-virtual-gamepad/




Save this program as translate.c,
compile with
cc translate.c -o translate
and run with
sudo ./translate
CTRL+C to close it.

You'll need to find out the name of your keyboard to grab in exclusive mode by "ls /dev/input" and change keyboard_to_open.

If you have more than one "extra" keyboard you can change the keyboard_to_open to the other keyboard name and run these programs simultaneously for more than one virtual controller.

So this program will translate all the keys along the LEFT SHIFT, Z,X,C,B,N,M, COMMA, DOT, RIGHT SHIFT, LEFT, RIGHT, UP AND DOWN into button presses that you can assign with mame's UI Input menus.

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

#define msleep(ms) usleep((ms)*1000)

static void setup_abs(int fd, unsigned chan, int min, int max);  


// demo showing how to provide a joystick/gamepad instance from
// userspace.

int main(void)
{ 
  int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
  
  if (fd < 0)
    {
      perror("open /dev/uinput");
      return 1;
    }

  ioctl(fd, UI_SET_EVBIT, EV_KEY); // enable button/key handling

int translatetable[] = { 

KEY_LEFTSHIFT, BTN_0,
KEY_Z, BTN_1,
KEY_X, BTN_2,
KEY_C, BTN_3,
KEY_V, BTN_4,
KEY_B, BTN_5,
KEY_N, BTN_6,
KEY_M, BTN_7,
KEY_COMMA, BTN_8,
KEY_DOT, BTN_9,
KEY_SLASH, BTN_TRIGGER_HAPPY1,
KEY_RIGHTSHIFT, BTN_TRIGGER_HAPPY2,
KEY_UP, BTN_DPAD_UP,
KEY_LEFT, BTN_DPAD_LEFT,
KEY_DOWN, BTN_DPAD_DOWN,
KEY_RIGHT, BTN_DPAD_RIGHT };

int translatetablelen = sizeof(translatetable) / sizeof (int);

int rcode; 


// tell uinput which BTN events we will generate
for (int i=0;i<translatetablelen;i+=2)     ioctl(fd, UI_SET_KEYBIT, translatetable[i+1]);

 char keyboard_to_open[256] = "/dev/input/by-id/usb-0461_USB_Wired_Keyboard-event-kbd";
 char keyboard_name[256] = "Unknown";
 
 int keyboard_fd = open(keyboard_to_open, O_RDONLY | O_NONBLOCK);  


  if ( keyboard_fd == -1 ) {
    printf("Failed to open keyboard.\n");
    exit(1);
  }
  rcode = ioctl(keyboard_fd, EVIOCGNAME(sizeof(keyboard_name)), keyboard_name);
  printf("Reading From : %s \n", keyboard_name);

  printf("Getting exclusive access: ");
  rcode = ioctl(keyboard_fd, EVIOCGRAB, 1);
  printf("%s\n", (rcode == 0) ? "SUCCESS" : "FAILURE");
  struct input_event keyboard_event;


  ioctl(fd, UI_SET_EVBIT, EV_ABS); // enable analog absolute position handling
  
  setup_abs(fd, ABS_X,  -512, 512);
  setup_abs(fd, ABS_Y,  -512, 512);
  setup_abs(fd, ABS_Z,  -32767, 32767);
  
  struct uinput_setup setup =
    {
     .name = "Userspace Joystick",
     .id =
     {
      .bustype = BUS_USB,
      .vendor  = 0x3,
      .product = 0x3,
      .version = 2,
     }
    };

  if (ioctl(fd, UI_DEV_SETUP, &setup))
    {
      perror("UI_DEV_SETUP");
      return 1;
    }
  
  if (ioctl(fd, UI_DEV_CREATE))
    {
      perror("UI_DEV_CREATE");
      return 1;
    }


struct input_event button_event;
while (1) {      
      int readval;
      
      if ( readval = read(keyboard_fd, &keyboard_event, sizeof(keyboard_event)), readval != -1 ) {
      	if (keyboard_event.type == EV_KEY)
      		for (int i=0;i<translatetablelen;i+=2) if (keyboard_event.code == translatetable[i]) 
      					{
						button_event.type = EV_KEY;
						button_event.code = translatetable[i+1];
						button_event.value = keyboard_event.value;
						
						  printf("ev type = %d keycode=%d code = %d value = %d\n",
						  	button_event.type,
						  	keyboard_event.code,
							button_event.code,
							button_event.value); 
	
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
						// send a SYNC event
						button_event.type = EV_SYN;
						button_event.code = 0;
						button_event.value = 0;

	
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
	
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
						break;
					}    														
 	if (readval == -1) msleep(50);
    }
}


  if(ioctl(fd, UI_DEV_DESTROY))
    {
      printf("UI_DEV_DESTROY");
      return 1;
    }

  close(fd);
  return 0;
}


// enable and configure an absolute "position" analog channel

static void setup_abs(int fd, unsigned chan, int min, int max)
{
  if (ioctl(fd, UI_SET_ABSBIT, chan))
    perror("UI_SET_ABSBIT");
  
  struct uinput_abs_setup s =
    {
     .code = chan,
     .absinfo = { .minimum = min,  .maximum = max },
    };

  if (ioctl(fd, UI_ABS_SETUP, &s))
    perror("UI_ABS_SETUP");
}


My keyboard isn't n-key rollover but I can hit a few keys simultaneously. (shown in jstest-gtk)

[Linked Image from i.imgur.com]

and using the keyboard as joystick buttons in the UI:
[Linked Image from i.imgur.com]

One thing to note is that it seems that mame doesn't see a joystick unless it's been connected prior to launching mame, so you should start the virtual joystick program first.

Joined: Feb 2014
Posts: 1,016
Likes: 110
G
Very Senior Member
OP Offline
Very Senior Member
G
Joined: Feb 2014
Posts: 1,016
Likes: 110
Modified my translate program to take a parameter of the keyboard path to open and also to press ESC to quit translating and exit.

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

#define msleep(ms) usleep((ms)*1000)

// Translate a keyboard opened in exclusive mode into virtual button presses with uinput
// by Golden Child
//
// using examples from
//
// https://gist.github.com/matthewaveryusa/a721aad80ae89a5c69f7c964fa20fec1
// https://github.com/GrantEdwards/uinput-joystick-demo
// https://www.kernel.org/doc/html/v4.12/input/uinput.html
// https://blog.marekkraus.sk/c/linuxs-uinput-usage-tutorial-virtual-gamepad/

// hard code the keyboard to open in keyboard_to_open
// you can pass the path of the keyboard to open on the command line

 char keyboard_to_open[256] = "/dev/input/by-id/usb-0461_USB_Wired_Keyboard-event-kbd";


static void setup_abs(int fd, unsigned chan, int min, int max);  

// demo showing how to provide a joystick/gamepad instance from
// userspace.

int main(int argc, char* argv[])
{ sleep(1); // wait for a second
  int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
  
  if (fd < 0)
    {
      perror("open /dev/uinput");
      return 1;
    }

  ioctl(fd, UI_SET_EVBIT, EV_KEY); // enable button/key handling

int translatetable[] = { 

KEY_LEFTSHIFT, BTN_0,
KEY_Z, BTN_1,
KEY_X, BTN_2,
KEY_C, BTN_3,
KEY_V, BTN_4,
KEY_B, BTN_5,
KEY_N, BTN_6,
KEY_M, BTN_7,
KEY_COMMA, BTN_8,
KEY_DOT, BTN_9,
KEY_SLASH, BTN_TRIGGER_HAPPY1,
KEY_RIGHTSHIFT, BTN_TRIGGER_HAPPY2,
KEY_UP, BTN_DPAD_UP,
KEY_LEFT, BTN_DPAD_LEFT,
KEY_DOWN, BTN_DPAD_DOWN,
KEY_RIGHT, BTN_DPAD_RIGHT };

int translatetablelen = sizeof(translatetable) / sizeof (int);

int rcode; 


// tell uinput which BTN events we will generate
for (int i=0;i<translatetablelen;i+=2)     ioctl(fd, UI_SET_KEYBIT, translatetable[i+1]);

 char keyboard_name[256] = "Unknown";
 
 int keyboard_fd;

printf ("\nparameters %d\n",argc);
for (int i=0;i<argc;i++) printf("parameter %d = %s\n",i,argv[i]);
 if (argc > 1)
 	keyboard_fd = open(argv[1], O_RDONLY | O_NONBLOCK);  
else
    keyboard_fd = open(keyboard_to_open, O_RDONLY | O_NONBLOCK);  


  if ( keyboard_fd == -1 ) {
    printf("Failed to open keyboard.\n");
    exit(1);
  }
  rcode = ioctl(keyboard_fd, EVIOCGNAME(sizeof(keyboard_name)), keyboard_name);
  printf("Reading From : %s \n", keyboard_name);

  printf("Getting exclusive access: ");
  rcode = ioctl(keyboard_fd, EVIOCGRAB, 1);
  printf("%s\n", (rcode == 0) ? "SUCCESS" : "FAILURE");
  printf("Translating codes: \n\
KEY_LEFTSHIFT, BTN_0,\n\
KEY_Z, BTN_1,\n\
KEY_X, BTN_2,\n\
KEY_C, BTN_3,\n\
KEY_V, BTN_4,\n\
KEY_B, BTN_5,\n\
KEY_N, BTN_6,\n\
KEY_M, BTN_7,\n\
KEY_COMMA, BTN_8,\n\
KEY_DOT, BTN_9,\n\
KEY_SLASH, BTN_TRIGGER_HAPPY1,\n\
KEY_RIGHTSHIFT, BTN_TRIGGER_HAPPY2,\n\
KEY_UP, BTN_DPAD_UP,\n\
KEY_LEFT, BTN_DPAD_LEFT,\n\
KEY_DOWN, BTN_DPAD_DOWN,\n\
KEY_RIGHT, BTN_DPAD_RIGHT\n\n");

for (int i=0;i<translatetablelen;i+=2)  printf("%d => %d\n",translatetable[i],translatetable[i+1]);

  printf("Press ESC to exit\n");
  struct input_event keyboard_event;


  ioctl(fd, UI_SET_EVBIT, EV_ABS); // enable analog absolute position handling
  
  setup_abs(fd, ABS_X,  -512, 512);
  setup_abs(fd, ABS_Y,  -512, 512);
  setup_abs(fd, ABS_Z,  -32767, 32767);
  
  struct uinput_setup setup =
    {
     .name = "Userspace Joystick",
     .id =
     {
      .bustype = BUS_USB,
      .vendor  = 0x3,
      .product = 0x3,
      .version = 2,
     }
    };

  if (ioctl(fd, UI_DEV_SETUP, &setup))
    {
      perror("UI_DEV_SETUP");
      return 1;
    }
  
  if (ioctl(fd, UI_DEV_CREATE))
    {
      perror("UI_DEV_CREATE");
      return 1;
    }


int doquit=0; // must initialize variable or else it's nonzero
struct input_event button_event;
while (1) {      
      int readval;
      
      if ( readval = read(keyboard_fd, &keyboard_event, sizeof(keyboard_event)), readval != -1 ) 
        {
        if (keyboard_event.code == KEY_ESC && keyboard_event.type == EV_KEY) 
        {
        	doquit = 1;
        	printf("ESC pressed, exiting\n");
        }
      	if (keyboard_event.type == EV_KEY)
      		for (int i=0;i<translatetablelen;i+=2) if (keyboard_event.code == translatetable[i]) 
      					{
      					
      					if (keyboard_event.value != 2) {  // skip key repeat events
      					
						button_event.type = EV_KEY;
						button_event.code = translatetable[i+1];
						button_event.value = keyboard_event.value;
						
						  printf("ev type=%d  key in=%d  => button out=%d value=%d\n",
						  	button_event.type,
						  	keyboard_event.code,
							button_event.code,
							button_event.value);
		
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
						
						// send SYNC event
						
						button_event.type = EV_SYN;
						button_event.code = 0;
						button_event.value = 0;

	
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
	
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
						break;
					}    						
					}								
 	// if (readval <= 0) msleep(50); 
 	if (doquit) break;
    }
 	if (readval <= 0) msleep(50); // keep from taking a lot of cpu by going to sleep
}


  if(ioctl(fd, UI_DEV_DESTROY))
    {
      printf("UI_DEV_DESTROY");
      return 1;
    }

  close(fd);
  return 0;
}

// enable and configure an absolute "position" analog channel

static void setup_abs(int fd, unsigned chan, int min, int max)
{
  if (ioctl(fd, UI_SET_ABSBIT, chan))
    perror("UI_SET_ABSBIT");
  
  struct uinput_abs_setup s =
    {
     .code = chan,
     .absinfo = { .minimum = min,  .maximum = max },
    };

  if (ioctl(fd, UI_ABS_SETUP, &s))
    perror("UI_ABS_SETUP");
}


So here's a sample run:

Code
sudo ./translate /dev/input/by-id/usb-USB_USB_Keyboard-event-kbd 

parameters 2
parameter 0 = ./translateverify2
parameter 1 = /dev/input/by-id/usb-USB_USB_Keyboard-event-kbd
Reading From : USB USB Keyboard 
Getting exclusive access: SUCCESS
Translating codes: 
KEY_LEFTSHIFT, BTN_0,
KEY_Z, BTN_1,
KEY_X, BTN_2,
KEY_C, BTN_3,
KEY_V, BTN_4,
KEY_B, BTN_5,
KEY_N, BTN_6,
KEY_M, BTN_7,
KEY_COMMA, BTN_8,
KEY_DOT, BTN_9,
KEY_SLASH, BTN_TRIGGER_HAPPY1,
KEY_RIGHTSHIFT, BTN_TRIGGER_HAPPY2,
KEY_UP, BTN_DPAD_UP,
KEY_LEFT, BTN_DPAD_LEFT,
KEY_DOWN, BTN_DPAD_DOWN,
KEY_RIGHT, BTN_DPAD_RIGHT

42 => 256
44 => 257
45 => 258
46 => 259
47 => 260
48 => 261
49 => 262
50 => 263
51 => 264
52 => 265
53 => 704
54 => 705
103 => 544
105 => 546
108 => 545
106 => 547
Press ESC to exit
ev type=1  key in=42  => button out=256 value=1
ev type=1  key in=42  => button out=256 value=0
ev type=1  key in=46  => button out=259 value=1
ev type=1  key in=44  => button out=257 value=1
ev type=1  key in=46  => button out=259 value=0
ev type=1  key in=45  => button out=258 value=1
ev type=1  key in=46  => button out=259 value=1
ev type=1  key in=44  => button out=257 value=0
ev type=1  key in=45  => button out=258 value=0
ev type=1  key in=46  => button out=259 value=0
ev type=1  key in=44  => button out=257 value=1
ev type=1  key in=45  => button out=258 value=1
ev type=1  key in=46  => button out=259 value=1
ev type=1  key in=44  => button out=257 value=0
ev type=1  key in=47  => button out=260 value=1
ev type=1  key in=45  => button out=258 value=0
ev type=1  key in=46  => button out=259 value=0
ev type=1  key in=47  => button out=260 value=0
ev type=1  key in=42  => button out=256 value=1
ev type=1  key in=42  => button out=256 value=0
ev type=1  key in=42  => button out=256 value=1
ev type=1  key in=44  => button out=257 value=1
ev type=1  key in=44  => button out=257 value=0
ev type=1  key in=42  => button out=256 value=0
ESC pressed, exiting

Noticed it was consuming a lot of cpu, I put the msleep in the wrong place of the loop.


Once you launch this, you can now see a new entry in /proc/bus/input/devices

Code
cat /proc/bus/input/devices
...
I: Bus=0003 Vendor=0003 Product=0003 Version=0002
N: Name="Userspace Joystick"
P: Phys=
S: Sysfs=/devices/virtual/input/input143
U: Uniq=
H: Handlers=event22 js1 
B: PROP=0
B: EV=b
B: KEY=3 0 0 f00000000 0 0 0 3ff 0 0 0 0
B: ABS=7


Joined: Feb 2014
Posts: 1,016
Likes: 110
G
Very Senior Member
OP Offline
Very Senior Member
G
Joined: Feb 2014
Posts: 1,016
Likes: 110
I hooked up 3 extra usb keyboards, ran a separate translate for each one:

Code
sudo ./translate /dev/input/by-id/usb-0461_USB_Wired_Keyboard-event-kbd &
sudo ./translate /dev/input/by-id/usb-USB_USB_Keyboard-event-kbd &
sudo ./translate /dev/input/by-id/usb-Microsoft_Wired_Keyboard_600-event-kbd &

[Linked Image from i.imgur.com]

Pressing keys on each keyboard simultaneously in jstest-gtk.

Joined: Feb 2014
Posts: 1,016
Likes: 110
G
Very Senior Member
OP Offline
Very Senior Member
G
Joined: Feb 2014
Posts: 1,016
Likes: 110
One of the things I've wanted to try is to convert midi into a controller. This gave me an idea: if I can read raw midi bytes from the linux /dev/midi1 device, it shouldn't be difficult at all to convert that to button presses.

A quick and dirty test:

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

#define msleep(ms) usleep((ms)*1000)

// miditest3.c
// midi to uinput button test mapping notes to buttons, and mod/pitch/volume to axes
// by Golden Child
// just maps the low register of the keyboard into button pressess
// using examples from
//
// https://gist.github.com/matthewaveryusa/a721aad80ae89a5c69f7c964fa20fec1
// https://github.com/GrantEdwards/uinput-joystick-demo
// https://www.kernel.org/doc/html/v4.12/input/uinput.html
// https://blog.marekkraus.sk/c/linuxs-uinput-usage-tutorial-virtual-gamepad/


static void setup_abs(int fd, unsigned chan, int min, int max);  

int main(int argc, char* argv[])
{ 
  int midi_fd = open("/dev/midi1", O_RDONLY | O_NONBLOCK);
  
  if (midi_fd < 0)
    {
      perror("open /dev/midi1");
      return 1;
    }  
    
    
  //sleep(1); // wait for a second
  int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
  
  if (fd < 0)
    {
      perror("open /dev/uinput");
      return 1;
    }

  ioctl(fd, UI_SET_EVBIT, EV_KEY); // enable button/key handling

// event codes from linux/input-event-codes.h

int translatetable[] = { 
0xc, BTN_0,
0xd, BTN_1,
0xe, BTN_2,
0xf, BTN_3,
0x10, BTN_4,
0x11, BTN_5,
0x12, BTN_6,
0x13, BTN_7,
0x14, BTN_8,
0x15, BTN_9,
0x16, BTN_TRIGGER_HAPPY1,
0x17, BTN_TRIGGER_HAPPY2,
0x18, BTN_DPAD_UP,
0x19, BTN_DPAD_LEFT,
0x1a, BTN_DPAD_DOWN,
0x1b, BTN_DPAD_RIGHT,
0x1c, 0x2ff,
0x1d, 0x280, // 0x300 doesn't work
0x1e, 0x2F0,  // 2F0 seems to be undefined but works
0x1f, 0x2F1,
0x20, 0x224,  // 224 - 22f undefined
0x21, 0x22f,
0x22, 0x120,
0x23, 0x12f,
0x24, 0x130,
0x25, 0x13e };  // greater than 0x2ff key max doesn't show up as a button press

int translatetablelen = sizeof(translatetable) / sizeof (int);


// tell uinput which BTN events we will generate
for (int i=0;i<translatetablelen;i+=2)     ioctl(fd, UI_SET_KEYBIT, translatetable[i+1]);
for (int i=0;i<translatetablelen;i+=2)  printf("%d => %d\n",translatetable[i],translatetable[i+1]);


  ioctl(fd, UI_SET_EVBIT, EV_ABS); // enable analog absolute position handling
  
//  setup_abs(fd, ABS_X,  -512, 512);
//  setup_abs(fd, ABS_Y,  -512, 512);
//  setup_abs(fd, ABS_Z,  0, 32767);

  setup_abs(fd, ABS_X,  0, 127);
  setup_abs(fd, ABS_Y,  0, 127);
  setup_abs(fd, ABS_Z,  0, 127);
  
  struct uinput_setup setup =
    {
     .name = "Userspace Joystick",
     .id =
     {
      .bustype = BUS_USB,
      .vendor  = 0x3,
      .product = 0x3,
      .version = 2,
     }
    };

  if (ioctl(fd, UI_DEV_SETUP, &setup))
    {
      perror("UI_DEV_SETUP");
      return 1;
    }
  
  if (ioctl(fd, UI_DEV_CREATE))
    {
      perror("UI_DEV_CREATE");
      return 1;
    }

  printf("Press CTRL+C to exit or top key on keyboard to quit\n");

    
    struct input_event button_event;
    
unsigned char  buffer[256];
int doquit = 0;

while (1) {      
      int readval;
      
      if ( readval = read(midi_fd, &buffer, 3), readval != -1 ) {
      printf("read: %x %x %x\n",buffer[0],buffer[1],buffer[2]);
 
      if (buffer[0] == 0x90)  // note on/off convert to EV_KEY events
	      	{   if (buffer[1] == 0x3c) doquit = 1;
	      		for (int i=0;i<translatetablelen;i+=2) 
	       			if (buffer[1] == translatetable[i]) 
      					{
      										
						button_event.type = EV_KEY;
						button_event.code = translatetable[i+1];
						button_event.value = (buffer[2]!=0) ? 1 : 0;  // 1 for button down, 0 for button up
						
						  printf("readval=%d  ev type=%d  key in=%d  => button out=%d value=%d\n",
						  	readval,
						  	button_event.type,
						  	buffer[1],
							button_event.code,
							button_event.value);
		
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
						
						// send SYNC event
						
						button_event.type = EV_SYN;
						button_event.code = 0;
						button_event.value = 0;

	
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
					    }
			}
        else if (buffer[0]==0xb0) // convert midi controller message into EV_ABS events
      					{
      					button_event.type = EV_ABS;
						button_event.code = (buffer[1]==1) ? ABS_X : (buffer[1]==7) ? ABS_Y : 0;  // my midi keyboard has 1 for volume, 7 for modulation
						button_event.value = (buffer[2]);
						
						  printf("0xb0 readval=%d  ev type=%d  key in=%d  => button out=%d value=%d\n",
						  	readval,
						  	button_event.type,
						  	buffer[1],
							button_event.code,
							button_event.value);
		
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
						
						// send SYNC event
						
						button_event.type = EV_SYN;
						button_event.code = 0;
						button_event.value = 0;

					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
			    }
			 else if (buffer[0]==0xe0)  // pitch bend wheel message to EV_ABS event
      					{
      					button_event.type = EV_ABS;
						button_event.code = ABS_Z;
						button_event.value = (buffer[2]);
						
						  printf("0xb0 readval=%d  ev type=%d  key in=%d  => button out=%d value=%d\n",
						  	readval,
						  	button_event.type,
						  	buffer[1],
							button_event.code,
							button_event.value);
		
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
						
						// send SYNC event
						
						button_event.type = EV_SYN;
						button_event.code = 0;
						button_event.value = 0;

	
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
			    }
			}
     	if (readval <=0) msleep(50); // sleep for a bit so we don't use all the cpu
     	if (doquit) break;
    }
 
    close(midi_fd);
      
  if(ioctl(fd, UI_DEV_DESTROY))
    {
      printf("UI_DEV_DESTROY");
      return 1;
    }

  close(fd);
    
  return 0;
}


// enable and configure an absolute "position" analog channel

static void setup_abs(int fd, unsigned chan, int min, int max)
{
  if (ioctl(fd, UI_SET_ABSBIT, chan))
    perror("UI_SET_ABSBIT");
  
  struct uinput_abs_setup s =
    {
     .code = chan,
     .absinfo = { .minimum = min,  .maximum = max },
    };

  if (ioctl(fd, UI_ABS_SETUP, &s))
    perror("UI_ABS_SETUP");
}


[Linked Image from i.imgur.com]

The usb keyboard protocol limits the keypresses to 6, but I can hold down a bunch of midi keys at once.

Joined: Feb 2014
Posts: 1,016
Likes: 110
G
Very Senior Member
OP Offline
Very Senior Member
G
Joined: Feb 2014
Posts: 1,016
Likes: 110
One of the things I was fiddling with was a driver for the Apple Graphics Tablet for the Apple 2, but didn't have a good input device for it.

So why not try to process input events for my wacom tablet device?

Code

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

#define msleep(ms) usleep((ms)*1000)

// Test to translate a tablet opened in exclusive mode into virtual button presses with uinput
// by Golden Child
// 
//
// using examples from
//
// https://gist.github.com/matthewaveryusa/a721aad80ae89a5c69f7c964fa20fec1
// https://github.com/GrantEdwards/uinput-joystick-demo
// https://www.kernel.org/doc/html/v4.12/input/uinput.html
// https://blog.marekkraus.sk/c/linuxs-uinput-usage-tutorial-virtual-gamepad/

// hard code the keyboard to open in keyboard_to_open
// you can pass the path of the keyboard to open on the command line

 char keyboard_to_open[256] = "/dev/input/by-id/usb-0461_USB_Wired_Keyboard-event-kbd";


static void setup_abs(int fd, unsigned chan, int min, int max);  

// demo showing how to provide a joystick/gamepad instance from
// userspace.

int main(int argc, char* argv[])
{ sleep(1); // wait for a second
  int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
  
  if (fd < 0)
    {
      perror("open /dev/uinput");
      return 1;
    }

  ioctl(fd, UI_SET_EVBIT, EV_KEY); // enable button/key handling

int translatetable[] = { 
BTN_TOOL_PEN, BTN_0,
//BTN_TOOL_RUBBER, BTN_1,  // don't seem used
//BTN_TOOL_BRUSH, BTN_2,
BTN_TOUCH, BTN_3,
BTN_STYLUS, BTN_4,
BTN_STYLUS2, BTN_5,
};

int translatetablelen = sizeof(translatetable) / sizeof (int);

int rcode; 


// tell uinput which BTN events we will generate
for (int i=0;i<translatetablelen;i+=2)     ioctl(fd, UI_SET_KEYBIT, translatetable[i+1]);

 char keyboard_name[256] = "Unknown";
 
 int keyboard_fd;

printf ("\nparameters %d\n",argc);
for (int i=0;i<argc;i++) printf("parameter %d = %s\n",i,argv[i]);
 if (argc > 1)
 	keyboard_fd = open(argv[1], O_RDONLY | O_NONBLOCK);  
else
    keyboard_fd = open(keyboard_to_open, O_RDONLY | O_NONBLOCK);  


  if ( keyboard_fd == -1 ) {
    printf("Failed to open keyboard.\n");
    exit(1);
  }
  rcode = ioctl(keyboard_fd, EVIOCGNAME(sizeof(keyboard_name)), keyboard_name);
  printf("Reading From : %s \n", keyboard_name);

  printf("Getting exclusive access: ");
  rcode = ioctl(keyboard_fd, EVIOCGRAB, 1);
  printf("%s\n", (rcode == 0) ? "SUCCESS" : "FAILURE");
 
for (int i=0;i<translatetablelen;i+=2)  printf("%d => %d\n",translatetable[i],translatetable[i+1]);

  printf("Press CTRL+C to exit\n");
  struct input_event tablet_event;


  ioctl(fd, UI_SET_EVBIT, EV_ABS); // enable analog absolute position handling
  
  setup_abs(fd, ABS_X,  -32767, 32767);
  setup_abs(fd, ABS_Y,  -32767, 32767);
  setup_abs(fd, ABS_PRESSURE,  -32767, 32767);
  setup_abs(fd, ABS_DISTANCE,  -32767, 32767);
  
  struct uinput_setup setup =
    {
     .name = "Userspace Joystick",
     .id =
     {
      .bustype = BUS_USB,
      .vendor  = 0x3,
      .product = 0x3,
      .version = 2,
     }
    };

  if (ioctl(fd, UI_DEV_SETUP, &setup))
    {
      perror("UI_DEV_SETUP");
      return 1;
    }
  
  if (ioctl(fd, UI_DEV_CREATE))
    {
      perror("UI_DEV_CREATE");
      return 1;
    }


int doquit=0; // must initialize variable or else it's nonzero
struct input_event button_event;
while (1) {      
      int readval;
      
      if ( readval = read(keyboard_fd, &tablet_event, sizeof(tablet_event)), readval != -1 ) 
        {
        if (tablet_event.code == KEY_ESC && tablet_event.type == EV_KEY) 
        {
        	doquit = 1;
        	printf("ESC pressed, exiting\n");
        }
      	if (tablet_event.type == EV_KEY) {
      		int found = 0;
      		for (int i=0;i<translatetablelen;i+=2) if (tablet_event.code == translatetable[i]) 
      					{
      					found = 1;
      					if (tablet_event.value != 2) {  // skip key repeat events
      					
						button_event.type = EV_KEY;
						button_event.code = translatetable[i+1];
						button_event.value = tablet_event.value;
						
						  printf("readval=%d  ev type=%d  key in=%d  => button out=%d value=%d\n",
						  	readval,
						  	button_event.type,
						  	tablet_event.code,
							button_event.code,
							button_event.value);
		
						  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
						
						// send SYNC event
						
						button_event.type = EV_SYN;
						button_event.code = 0;
						button_event.value = 0;

	
						  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
	
						  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
						break;
						}    						
					}
					if (!found) printf ("TRANSLATE  UNKNOWN CODE %x\n",tablet_event.code);
				}
			else if  (tablet_event.type == EV_ABS) 		
					{
      					button_event.type = EV_ABS;
						button_event.code = tablet_event.code; 
						button_event.value = tablet_event.value;
						
						  printf("readval=%d  ev type=%d  key in=%d  => button out=%d value=%d\n",
						  	readval,
						  	button_event.type,
						  	tablet_event.code,
							button_event.code,
							button_event.value);
		
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
						
						// send SYNC event
						
						button_event.type = EV_SYN;
						button_event.code = 0;
						button_event.value = 0;

					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
			    }
				
 	// if (readval <= 0) msleep(50); 
 	if (doquit) break;
    }
 	if (readval <= 0) msleep(50); // keep from taking a lot of cpu by going to sleep
}


  if(ioctl(fd, UI_DEV_DESTROY))
    {
      printf("UI_DEV_DESTROY");
      return 1;
    }

  close(fd);
  return 0;
}

// enable and configure an absolute "position" analog channel

static void setup_abs(int fd, unsigned chan, int min, int max)
{
  if (ioctl(fd, UI_SET_ABSBIT, chan))
    perror("UI_SET_ABSBIT");
  
  struct uinput_abs_setup s =
    {
     .code = chan,
     .absinfo = { .minimum = min,  .maximum = max },
    };

  if (ioctl(fd, UI_ABS_SETUP, &s))
    perror("UI_ABS_SETUP");
}



With the pen away from the tablet, all the axes and buttons report 0.

[Linked Image from i.imgur.com]

putting the stylus on the tablet,
button 0 is "proximity", button 1 is "actually pressing on tablet",
button 2 is the first stylus button and button 3 is the second stylus button.

Axis 0/1 is x,y and Axis 2/3 are distance, pressure

[Linked Image from i.imgur.com]



Code
sudo ./translatetablet /dev/input/by-id/usb-Wacom_Co._Ltd._CTL-460-event-mouse 

parameters 2
parameter 0 = ./translatetablet
parameter 1 = /dev/input/by-id/usb-Wacom_Co._Ltd._CTL-460-event-mouse
Reading From : Wacom Bamboo Pen Pen 
Getting exclusive access: SUCCESS
320 => 256
330 => 259
331 => 260
332 => 261
Press CTRL+C to exit
0xb0 readval=24  ev type=3  key in=0  => button out=0 value=8931
0xb0 readval=24  ev type=3  key in=1  => button out=1 value=178
0xb0 readval=24  ev type=3  key in=25  => button out=25 value=31
...

Joined: Feb 2014
Posts: 1,016
Likes: 110
G
Very Senior Member
OP Offline
Very Senior Member
G
Joined: Feb 2014
Posts: 1,016
Likes: 110
Ok, if we can translate keyboards, midi, and tablets, why not translate a mouse.

So if I run this test program which will open a mouse device and basically pass through the EV_REL events, then I can have a mouse that's independent of the system mouse pointer.

It's VERY important to have it map the buttons BTN_DPAD_UP/DOWN/LEFT/RIGHT or the system will not treat it as a joystick and it will be invisible to mame.
If you map BTN_LEFT/RIGHT/MIDDLE the system will treat it as a regular mouse (not what we want).




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

#define msleep(ms) usleep((ms)*1000)

// Translate a mouse opened in exclusive mode into virtual joystick events
// by Golden Child
//
// using examples from
//
// https://gist.github.com/matthewaveryusa/a721aad80ae89a5c69f7c964fa20fec1
// https://github.com/GrantEdwards/uinput-joystick-demo
// https://www.kernel.org/doc/html/v4.12/input/uinput.html
// https://blog.marekkraus.sk/c/linuxs-uinput-usage-tutorial-virtual-gamepad/

// hard code the keyboard to open in keyboard_to_open
// you can pass the path of the keyboard to open on the command line

 char keyboard_to_open[256] = "/dev/input/by-id/usb-0461_USB_Wired_Keyboard-event-kbd";


static void setup_abs(int fd, unsigned chan, int min, int max);  

// demo showing how to provide a joystick/gamepad instance from
// userspace.

int main(int argc, char* argv[])
{ sleep(1); // wait for a second
  int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
  
  if (fd < 0)
    {
      perror("open /dev/uinput");
      return 1;
    }

  ioctl(fd, UI_SET_EVBIT, EV_KEY); // enable button/key handling

int translatetable[] = { 

// IF I ENABLE THESE BUTTONS, SYSTEM TREATS IT AS A SYSTEM MOUSE
/*
BTN_LEFT, BTN_LEFT,
BTN_RIGHT, BTN_RIGHT,
BTN_MIDDLE, BTN_MIDDLE 
*/

// IF YOU ENABLE BTN_DPAD_UP/LEFT/DOWN/RIGHT  THE SYSTEM WILL TREAT IT AS A JOYSTICK
0x18, BTN_DPAD_UP,
0x19, BTN_DPAD_LEFT,
0x1a, BTN_DPAD_DOWN,
0x1b, BTN_DPAD_RIGHT,
BTN_LEFT, BTN_0,
BTN_RIGHT, BTN_1,
BTN_MIDDLE, BTN_2 

};

int translatetablelen = sizeof(translatetable) / sizeof (int);

int rcode; 


// tell uinput which BTN events we will generate
for (int i=0;i<translatetablelen;i+=2)     ioctl(fd, UI_SET_KEYBIT, translatetable[i+1]);

 char keyboard_name[256] = "Unknown";
 
 int keyboard_fd;

printf ("\nparameters %d\n",argc);
for (int i=0;i<argc;i++) printf("parameter %d = %s\n",i,argv[i]);
 if (argc > 1)
 	keyboard_fd = open(argv[1], O_RDONLY | O_NONBLOCK);  
else
    keyboard_fd = open(keyboard_to_open, O_RDONLY | O_NONBLOCK);  


  if ( keyboard_fd == -1 ) {
    printf("Failed to open keyboard.\n");
    exit(1);
  }
  rcode = ioctl(keyboard_fd, EVIOCGNAME(sizeof(keyboard_name)), keyboard_name);
  printf("Reading From : %s \n", keyboard_name);

  printf("Getting exclusive access: ");
  rcode = ioctl(keyboard_fd, EVIOCGRAB, 1);
  printf("%s\n", (rcode == 0) ? "SUCCESS" : "FAILURE");


for (int i=0;i<translatetablelen;i+=2)  printf("%d => %d\n",translatetable[i],translatetable[i+1]);

  printf("Press ESC to exit\n");
  struct input_event keyboard_event;

// if you hide the ABS part, it won't register as a joystick

  ioctl(fd, UI_SET_EVBIT, EV_ABS); // enable analog absolute position handling
  
  setup_abs(fd, ABS_X,  -512, 512);
  setup_abs(fd, ABS_Y,  -512, 512);
  setup_abs(fd, ABS_Z,  -32767, 32767);
  
  
// SETUP THE RELATIVE UINPUT AXES

  ioctl(fd, UI_SET_EVBIT, EV_REL); // enable relative event handling

  ioctl(fd, UI_SET_RELBIT, REL_X);
  ioctl(fd, UI_SET_RELBIT, REL_Y);
  ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);


  
  struct uinput_setup setup =
    {
     .name = "Userspace Joystick",
     .id =
     {
      .bustype = BUS_USB,
      .vendor  = 0x3,
      .product = 0x3,
      .version = 2,
     }
    };

  if (ioctl(fd, UI_DEV_SETUP, &setup))
    {
      perror("UI_DEV_SETUP");
      return 1;
    }
  
  if (ioctl(fd, UI_DEV_CREATE))
    {
      perror("UI_DEV_CREATE");
      return 1;
    }


int doquit=0; // must initialize variable or else it's nonzero
struct input_event button_event;
while (1) {      
      int readval;
      
      if ( readval = read(keyboard_fd, &keyboard_event, sizeof(keyboard_event)), readval != -1 ) 
        {
        if (keyboard_event.code == KEY_ESC && keyboard_event.type == EV_KEY) 
        {
        	doquit = 1;
        	printf("ESC pressed, exiting\n");
        }
      	if (keyboard_event.type == EV_KEY){
      		for (int i=0;i<translatetablelen;i+=2) if (keyboard_event.code == translatetable[i]) 
      					{
      					
      					if (keyboard_event.value != 2) {  // skip key repeat events
      					
						button_event.type = EV_KEY;
						button_event.code = translatetable[i+1];
						button_event.value = keyboard_event.value;
						
						  printf("readval=%d  ev type=%d  key in=%d  => button out=%d value=%d\n",
						  	readval,
						  	button_event.type,
						  	keyboard_event.code,
							button_event.code,
							button_event.value);
		
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
						
						// send SYNC event
						
						button_event.type = EV_SYN;
						button_event.code = 0;
						button_event.value = 0;

	
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
	
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
						break;
					}    						
					}						}
		else if (keyboard_event.type == EV_REL){ 							
	  					if (keyboard_event.value != 2) {  // skip key repeat events
      					
      					// copy event and pass it through
      					
						button_event.type = EV_REL;
						button_event.code = keyboard_event.code;
						button_event.value = keyboard_event.value;
						
						  printf("readval=%d  ev type=%d  key in=%d  => button out=%d value=%d\n",
						  	readval,
						  	button_event.type,
						  	keyboard_event.code,
							button_event.code,
							button_event.value);
		
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
						
						// send SYNC event
						
						button_event.type = EV_SYN;
						button_event.code = 0;
						button_event.value = 0;

	
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
	
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
						//break;
		
		}
		
		}
 	// if (readval <= 0) msleep(50); 
 	if (doquit) break;
    }
 	if (readval <= 0) msleep(50); // keep from taking a lot of cpu by going to sleep
}


  if(ioctl(fd, UI_DEV_DESTROY))
    {
      printf("UI_DEV_DESTROY");
      return 1;
    }

  close(fd);
  return 0;
}

// enable and configure an absolute "position" analog channel

static void setup_abs(int fd, unsigned chan, int min, int max)
{
  if (ioctl(fd, UI_SET_ABSBIT, chan))
    perror("UI_SET_ABSBIT");
  
  struct uinput_abs_setup s =
    {
     .code = chan,
     .absinfo = { .minimum = min,  .maximum = max },
    };

  if (ioctl(fd, UI_ABS_SETUP, &s))
    perror("UI_ABS_SETUP");
}

Code
sudo ./mousetest /dev/input/by-id/usb-Logitech_Dell_Wireless_Mouse_WM413-event-mouse 

parameters 2
parameter 0 = ./mousetest
parameter 1 = /dev/input/by-id/usb-Logitech_Dell_Wireless_Mouse_WM413-event-mouse
Reading From : Logitech Dell Wireless Mouse WM413 
Getting exclusive access: SUCCESS
24 => 544
25 => 546
26 => 545
27 => 547
Press ESC to exit
readval=24  ev type=2  key in=0  => button out=0 value=5
readval=24  ev type=2  key in=0  => button out=0 value=7
readval=24  ev type=2  key in=1  => button out=1 value=1
readval=24  ev type=2  key in=0  => button out=0 value=9
readval=24  ev type=2  key in=0  => button out=0 value=9
readval=24  ev type=2  key in=0  => button out=0 value=7
readval=24  ev type=2  key in=1  => button out=1 value=-1
readval=24  ev type=2  key in=0  => button out=0 value=3
readval=24  ev type=2  key in=1  => button out=1 value=-1
...

And you can map the mouse in mame and it works:

[Linked Image from i.imgur.com]

You see it says "Joy1 R0 UserspaceJoystick" that I have mapped to Mouse X axis and "Joy1 R1 UserspaceJoystick" that I have mapped to the Mouse Y Axis.

Note that it is now totally independent of the regular system mouse pointer.

Mouse buttons come up as
Button 4/Left Button
Button 5/Right Button
Button 6/Middle Button

The only problem is that it doesn't "zero" the relative motion when you stop moving the mouse. It keeps going and won't stay still. I'll have to send a rel motion of 0 when it's idle.

Joined: Feb 2014
Posts: 1,016
Likes: 110
G
Very Senior Member
OP Offline
Very Senior Member
G
Joined: Feb 2014
Posts: 1,016
Likes: 110
I have this little Azio Levetron add-on keyboard and it generates normal EV_KEY events, KEY_F1 to KEY_F6.

If we can translate a full size keyboard, why not a little mini-keyboard or one of these add-on usb numeric keypads.

[Linked Image from i.imgur.com]

And we could also translate a single keypress into a macro. As an experimental test I made a string of keys that types "macro" as a sequence of KEY_M, KEY_A, KEY_C, KEY_R, KEY_O and it sends press and release events when I hit F6.



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

#define msleep(ms) usleep((ms)*1000)

// Test translating Levetron 6 key function keyboard into virtual joystick button events
// by Golden Child
//
// using examples from
//
// https://gist.github.com/matthewaveryusa/a721aad80ae89a5c69f7c964fa20fec1
// https://github.com/GrantEdwards/uinput-joystick-demo
// https://www.kernel.org/doc/html/v4.12/input/uinput.html
// https://blog.marekkraus.sk/c/linuxs-uinput-usage-tutorial-virtual-gamepad/

// hard code the keyboard to open in keyboard_to_open
// you can pass the path of the keyboard to open on the command line

 char keyboard_to_open[256] = "/dev/input/by-id/usb-0461_USB_Wired_Keyboard-event-kbd";


static void setup_abs(int fd, unsigned chan, int min, int max);  

// demo showing how to provide a joystick/gamepad instance from
// userspace.

int main(int argc, char* argv[])
{ sleep(1); // wait for a second
  int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
  
  if (fd < 0)
    {
      perror("open /dev/uinput");
      return 1;
    }

  ioctl(fd, UI_SET_EVBIT, EV_KEY); // enable button/key handling


int macroteststring[] = { KEY_M, KEY_A, KEY_C, KEY_R, KEY_O };


for (int i=0;i<(sizeof macroteststring / sizeof macroteststring[0]);i+=1)     
	ioctl(fd, UI_SET_KEYBIT, macroteststring[i]); // you must tell uinput which events you will be generating


int translatetable[] = { 


KEY_F1, BTN_0,
KEY_F2, BTN_1,
KEY_F3, BTN_2,
KEY_F4, BTN_3,
KEY_F5, BTN_4,
KEY_F6, BTN_5,


// IF I ENABLE THESE BUTTONS, SYSTEM TREATS IT AS A SYSTEM MOUSE
/*
BTN_LEFT, BTN_LEFT,
BTN_RIGHT, BTN_RIGHT,
BTN_MIDDLE, BTN_MIDDLE 
*/

// IF YOU ENABLE BTN_DPAD_UP/LEFT/DOWN/RIGHT  THE SYSTEM WILL TREAT IT AS A JOYSTICK
0x18, BTN_DPAD_UP,
0x19, BTN_DPAD_LEFT,
0x1a, BTN_DPAD_DOWN,
0x1b, BTN_DPAD_RIGHT,
BTN_LEFT, BTN_0,
BTN_RIGHT, BTN_1,
BTN_MIDDLE, BTN_2 

};

int translatetablelen = sizeof(translatetable) / sizeof (int);

int rcode; 


// tell uinput which BTN events we will generate
for (int i=0;i<translatetablelen;i+=2)     ioctl(fd, UI_SET_KEYBIT, translatetable[i+1]);

 char keyboard_name[256] = "Unknown";
 
 int keyboard_fd;

printf ("\nparameters %d\n",argc);
for (int i=0;i<argc;i++) printf("parameter %d = %s\n",i,argv[i]);
 if (argc > 1)
 	keyboard_fd = open(argv[1], O_RDONLY | O_NONBLOCK);  
else
    keyboard_fd = open(keyboard_to_open, O_RDONLY | O_NONBLOCK);  


  if ( keyboard_fd == -1 ) {
    printf("Failed to open keyboard.\n");
    exit(1);
  }
  rcode = ioctl(keyboard_fd, EVIOCGNAME(sizeof(keyboard_name)), keyboard_name);
  printf("Reading From : %s \n", keyboard_name);

  printf("Getting exclusive access: ");
  rcode = ioctl(keyboard_fd, EVIOCGRAB, 1);
  printf("%s\n", (rcode == 0) ? "SUCCESS" : "FAILURE");


for (int i=0;i<translatetablelen;i+=2)  printf("%d => %d\n",translatetable[i],translatetable[i+1]);

  printf("Press ESC to exit\n");
  struct input_event keyboard_event;

// if you hide the ABS part, it won't register as a joystick

  ioctl(fd, UI_SET_EVBIT, EV_ABS); // enable analog absolute position handling
  
  setup_abs(fd, ABS_X,  -512, 512);
  setup_abs(fd, ABS_Y,  -512, 512);
  setup_abs(fd, ABS_Z,  -32767, 32767);
  
  
// SETUP THE RELATIVE UINPUT AXES

  ioctl(fd, UI_SET_EVBIT, EV_REL); // enable relative event handling

  ioctl(fd, UI_SET_RELBIT, REL_X);
  ioctl(fd, UI_SET_RELBIT, REL_Y);
  ioctl(fd, UI_SET_RELBIT, REL_Z);
  ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);


  
  struct uinput_setup setup =
    {
     .name = "Userspace Joystick",
     .id =
     {
      .bustype = BUS_USB,
      .vendor  = 0x3,
      .product = 0x3,
      .version = 2,
     }
    };

  if (ioctl(fd, UI_DEV_SETUP, &setup))
    {
      perror("UI_DEV_SETUP");
      return 1;
    }
  
  if (ioctl(fd, UI_DEV_CREATE))
    {
      perror("UI_DEV_CREATE");
      return 1;
    }


int doquit=0; // must initialize variable or else it's nonzero
struct input_event button_event;
while (1) {      
      int readval;
      
      if ( readval = read(keyboard_fd, &keyboard_event, sizeof(keyboard_event)), readval != -1 ) 
        {
        if (keyboard_event.code == KEY_ESC && keyboard_event.type == EV_KEY) 
        {
        	doquit = 1;
        	printf("ESC pressed, exiting\n");
        }
      	if (keyboard_event.type == EV_KEY){
      		for (int i=0;i<translatetablelen;i+=2) if (keyboard_event.code == translatetable[i]) 
      					{
      					
      					if (keyboard_event.value != 2) {  // skip key repeat events

						if (keyboard_event.code == KEY_F6 && keyboard_event.value == 1) {  // macro generation send only on pressing KEY_F6
						  for (int j=0;j<(sizeof macroteststring / sizeof macroteststring[0]);j++){
								button_event.type = EV_KEY;
								button_event.code = macroteststring[j];
								button_event.value = 1;  // key down
							  if(write(fd, &button_event, sizeof button_event) < 0)
								{
								  perror("write");
								  return 1;
								}
								button_event.type = EV_KEY;
								button_event.code = macroteststring[j];
								button_event.value = 0; // key release
							  if(write(fd, &button_event, sizeof button_event) < 0)
								{
								  perror("write");
								  return 1;
								}
						button_event.type = EV_SYN;
						button_event.code = 0;
						button_event.value = 0;

	
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}

								}
						
						}
      					
						button_event.type = EV_KEY;
						button_event.code = translatetable[i+1];
						button_event.value = keyboard_event.value;
						
						  printf("readval=%d  ev type=%d  key in=%d  => button out=%d value=%d\n",
						  	readval,
						  	button_event.type,
						  	keyboard_event.code,
							button_event.code,
							button_event.value);
		
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
						
						// send SYNC event
						
						button_event.type = EV_SYN;
						button_event.code = 0;
						button_event.value = 0;

	
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
	
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
						break;
					}    						
					}						}
		else if (keyboard_event.type == EV_REL){ 							
      					
      					// copy event and pass it through
      					
						button_event.type = EV_REL;
						button_event.code = keyboard_event.code;
						button_event.value = keyboard_event.value;
					
						  printf("readval=%d  ev type=%d  key in=%d  => button out=%d value=%d\n",
						  	readval,
						  	button_event.type,
						  	keyboard_event.code,
							button_event.code,
							button_event.value);
		
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}

						
						// send SYNC event
						
						button_event.type = EV_SYN;
						button_event.code = 0;
						button_event.value = 0;

	
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
	
					  if(write(fd, &button_event, sizeof button_event) < 0)
							{
							  perror("write");
							  return 1;
							}
		
		}
 	if (doquit) break;
    }
 	if (readval <= 0) msleep(50); // keep from taking a lot of cpu by going to sleep
}


  if(ioctl(fd, UI_DEV_DESTROY))
    {
      printf("UI_DEV_DESTROY");
      return 1;
    }

  close(fd);
  return 0;
}

// enable and configure an absolute "position" analog channel

static void setup_abs(int fd, unsigned chan, int min, int max)
{
  if (ioctl(fd, UI_SET_ABSBIT, chan))
    perror("UI_SET_ABSBIT");
  
  struct uinput_abs_setup s =
    {
     .code = chan,
     .absinfo = { .minimum = min,  .maximum = max },
    };

  if (ioctl(fd, UI_ABS_SETUP, &s))
    perror("UI_ABS_SETUP");
}






Code
sudo ./test6keypost /dev/input/by-id/usb-SIGMACH1P_USB_Keyboard-event-kbd 

parameters 2
parameter 0 = ./test6keypost
parameter 1 = /dev/input/by-id/usb-SIGMACH1P_USB_Keyboard-event-kbd
Reading From : SIGMACH1P USB Keyboard 
Getting exclusive access: SUCCESS
59 => 256
60 => 257
61 => 258
62 => 259
63 => 260
64 => 261
24 => 544
25 => 546
26 => 545
27 => 547
272 => 256
273 => 257
274 => 258
Press ESC to exit
readval=24  ev type=1  key in=62  => button out=259 value=1
readval=24  ev type=1  key in=62  => button out=259 value=0
readval=24  ev type=1  key in=63  => button out=260 value=1
readval=24  ev type=1  key in=63  => button out=260 value=0
readval=24  ev type=1  key in=64  => button out=261 value=1
macroreadval=24  ev type=1  key in=64  => button out=261 value=0
readval=24  ev type=1  key in=59  => button out=256 value=1
readval=24  ev type=1  key in=59  => button out=256 value=0
readval=24  ev type=1  key in=60  => button out=257 value=1
readval=24  ev type=1  key in=60  => button out=257 value=0
readval=24  ev type=1  key in=61  => button out=258 value=1
readval=24  ev type=1  key in=61  => button out=258 value=0
readval=24  ev type=1  key in=59  => button out=256 value=1
readval=24  ev type=1  key in=59  => button out=256 value=0
readval=24  ev type=1  key in=60  => button out=257 value=1
readval=24  ev type=1  key in=60  => button out=257 value=0
readval=24  ev type=1  key in=61  => button out=258 value=1
readval=24  ev type=1  key in=61  => button out=258 value=0
readval=24  ev type=1  key in=62  => button out=259 value=1
readval=24  ev type=1  key in=62  => button out=259 value=0
readval=24  ev type=1  key in=63  => button out=260 value=1
readval=24  ev type=1  key in=63  => button out=260 value=0
readval=24  ev type=1  key in=64  => button out=261 value=1
macroreadval=24  ev type=1  key in=64  => button out=261 value=0




Code
sudo evtest
No device specified, trying to scan all of /dev/input/event*
Available devices:
/dev/input/event26:	Userspace Joystick
/dev/input/event27:	SIGMACH1P USB Keyboard
/dev/input/event29:	SIGMACH1P USB Keyboard Consumer Control
/dev/input/event30:	SIGMACH1P USB Keyboard System Control
Select the device event number [0-30]: 26
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x3 product 0x3 version 0x2
Input device name: "Userspace Joystick"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 19 (KEY_R)
    Event code 24 (KEY_O)
    Event code 30 (KEY_A)
    Event code 46 (KEY_C)
    Event code 50 (KEY_M)
    Event code 256 (BTN_0)
    Event code 257 (BTN_1)
    Event code 258 (BTN_2)
    Event code 259 (BTN_3)
    Event code 260 (BTN_4)
    Event code 261 (BTN_5)
    Event code 544 (BTN_DPAD_UP)
    Event code 545 (BTN_DPAD_DOWN)
    Event code 546 (BTN_DPAD_LEFT)
    Event code 547 (BTN_DPAD_RIGHT)
  Event type 2 (EV_REL)
    Event code 0 (REL_X)
    Event code 1 (REL_Y)
    Event code 2 (REL_Z)
    Event code 11 (REL_WHEEL_HI_RES)
  Event type 3 (EV_ABS)
    Event code 0 (ABS_X)
      Value      0
      Min     -512
      Max      512
    Event code 1 (ABS_Y)
      Value      0
      Min     -512
      Max      512
    Event code 2 (ABS_Z)
      Value      0
      Min   -32767
      Max    32767
Properties:
Testing ... (interrupt to exit)
Event: time 1616617802.637105, type 1 (EV_KEY), code 256 (BTN_0), value 1
Event: time 1616617802.637105, -------------- SYN_REPORT ------------
Event: time 1616617802.787355, type 1 (EV_KEY), code 256 (BTN_0), value 0
Event: time 1616617802.787355, -------------- SYN_REPORT ------------
Event: time 1616617803.187933, type 1 (EV_KEY), code 257 (BTN_1), value 1
Event: time 1616617803.187933, -------------- SYN_REPORT ------------
Event: time 1616617803.338172, type 1 (EV_KEY), code 257 (BTN_1), value 0
Event: time 1616617803.338172, -------------- SYN_REPORT ------------
Event: time 1616617803.738741, type 1 (EV_KEY), code 258 (BTN_2), value 1
Event: time 1616617803.738741, -------------- SYN_REPORT ------------
Event: time 1616617803.989130, type 1 (EV_KEY), code 258 (BTN_2), value 0
Event: time 1616617803.989130, -------------- SYN_REPORT ------------
Event: time 1616617804.339669, type 1 (EV_KEY), code 259 (BTN_3), value 1
Event: time 1616617804.339669, -------------- SYN_REPORT ------------
Event: time 1616617804.590057, type 1 (EV_KEY), code 259 (BTN_3), value 0
Event: time 1616617804.590057, -------------- SYN_REPORT ------------
Event: time 1616617804.890508, type 1 (EV_KEY), code 260 (BTN_4), value 1
Event: time 1616617804.890508, -------------- SYN_REPORT ------------
Event: time 1616617805.090819, type 1 (EV_KEY), code 260 (BTN_4), value 0
Event: time 1616617805.090819, -------------- SYN_REPORT ------------
Event: time 1616617805.591517, type 1 (EV_KEY), code 50 (KEY_M), value 1
Event: time 1616617805.591517, type 1 (EV_KEY), code 50 (KEY_M), value 0
Event: time 1616617805.591517, -------------- SYN_REPORT ------------
Event: time 1616617805.591553, type 1 (EV_KEY), code 30 (KEY_A), value 1
Event: time 1616617805.591553, type 1 (EV_KEY), code 30 (KEY_A), value 0
Event: time 1616617805.591553, -------------- SYN_REPORT ------------
Event: time 1616617805.591556, type 1 (EV_KEY), code 46 (KEY_C), value 1
Event: time 1616617805.591556, type 1 (EV_KEY), code 46 (KEY_C), value 0
Event: time 1616617805.591556, -------------- SYN_REPORT ------------
Event: time 1616617805.591558, type 1 (EV_KEY), code 19 (KEY_R), value 1
Event: time 1616617805.591558, type 1 (EV_KEY), code 19 (KEY_R), value 0
Event: time 1616617805.591558, -------------- SYN_REPORT ------------
Event: time 1616617805.591561, type 1 (EV_KEY), code 24 (KEY_O), value 1
Event: time 1616617805.591561, type 1 (EV_KEY), code 24 (KEY_O), value 0
Event: time 1616617805.591561, -------------- SYN_REPORT ------------
Event: time 1616617805.591578, type 1 (EV_KEY), code 261 (BTN_5), value 1
Event: time 1616617805.591578, -------------- SYN_REPORT ------------
macroEvent: time 1616617805.791881, type 1 (EV_KEY), code 261 (BTN_5), value 0
Event: time 1616617805.791881, -------------- SYN_REPORT ------------

So I can assign ctrl to one of the keys and reset to another so I can do CTRL+RESET from the little keyboard.

[Linked Image from i.imgur.com]

Joined: Feb 2014
Posts: 1,016
Likes: 110
G
Very Senior Member
OP Offline
Very Senior Member
G
Joined: Feb 2014
Posts: 1,016
Likes: 110
Just for fun I thought I'd try to make more than 2 relative axes, by adding REL_RX and REL_RY but SDL seems to only find 1 "ball".

/usr/src/linux-headers-5.4.0-67/include/uapi/linux/input-event-codes.h has a bunch of axes to choose from:

Code
#define REL_X                   0x00
#define REL_Y                   0x01
#define REL_Z                   0x02
#define REL_RX                  0x03
#define REL_RY                  0x04
#define REL_RZ                  0x05
#define REL_HWHEEL              0x06
#define REL_DIAL                0x07
#define REL_WHEEL               0x08
#define REL_MISC                0x09

so let's add some more axes:

Code
  ioctl(fd, UI_SET_EVBIT, EV_REL); // enable relative event handling

  ioctl(fd, UI_SET_RELBIT, REL_X);
  ioctl(fd, UI_SET_RELBIT, REL_Y);
  ioctl(fd, UI_SET_RELBIT, REL_Z);
  ioctl(fd, UI_SET_RELBIT, REL_RX);
  ioctl(fd, UI_SET_RELBIT, REL_RY);

Launching mame with -verbose:

says I have 3 axes, 18 buttons 0 hats 1 balls

Code
Joystick: Start initialization
Input: Adding joystick #0: MicrosoftSideWinderJoystick (device id: )
Joystick: Microsoft SideWinder Joystick [GUID ]
Joystick:   ...  3 axes, 8 buttons 0 hats 0 balls
Joystick:   ...  Physical id 0 mapped to logical id 1
Joystick:   ...  Does not have haptic capability
Input: Adding joystick #1: UserspaceJoystick (device id: 03000000030000000300000002000000)
Joystick: Userspace Joystick [GUID 03000000030000000300000002000000]
Joystick:   ...  3 axes, 18 buttons 0 hats 1 balls
Joystick:   ...  Physical id 1 mapped to logical id 2
Joystick:   ...  Does not have haptic capability
Joystick: End initialization



looking in 3rdparty/SDL2/src/joystick/linux/SDL_sysjoystick.c it looks like SDL only checks REL_X and REL_Y to set the number of balls, so even if I add other axes they'll be ignored.

Code
        if (test_bit(REL_X, relbit) || test_bit(REL_Y, relbit)) {
            ++joystick->nballs;
        }

One workaround could be to make another virtual joystick with uinput and translate events to the second virtual device if I only get 1 "ball" per device.

Joined: Feb 2014
Posts: 1,016
Likes: 110
G
Very Senior Member
OP Offline
Very Senior Member
G
Joined: Feb 2014
Posts: 1,016
Likes: 110
Now that I can translate keyboard EV_KEY events, why not see if I can do something with my Kensington Presenter remote.


[Linked Image from i.imgur.com]


It will generate KEY_F5 on the top button (along with the laser), KEY_PAGEUP and KEY_PAGEDOWN on the left and right arrows and KEY_B on the down button.

Running evtest shows me what keys get generated:

Code
Testing ... (interrupt to exit)
Event: time 1616993982.490214, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70005
Event: time 1616993982.490214, type 1 (EV_KEY), code 48 (KEY_B), value 1
Event: time 1616993982.490214, -------------- SYN_REPORT ------------
bEvent: time 1616993982.578213, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70005
Event: time 1616993982.578213, type 1 (EV_KEY), code 48 (KEY_B), value 0
Event: time 1616993982.578213, -------------- SYN_REPORT ------------
Event: time 1616993986.546284, type 4 (EV_MSC), code 4 (MSC_SCAN), value 7004b
Event: time 1616993986.546284, type 1 (EV_KEY), code 104 (KEY_PAGEUP), value 1
Event: time 1616993986.546284, -------------- SYN_REPORT ------------
^[[5~Event: time 1616993986.626288, type 4 (EV_MSC), code 4 (MSC_SCAN), value 7004b
Event: time 1616993986.626288, type 1 (EV_KEY), code 104 (KEY_PAGEUP), value 0
Event: time 1616993986.626288, -------------- SYN_REPORT ------------
Event: time 1616993988.298314, type 4 (EV_MSC), code 4 (MSC_SCAN), value 7004e
Event: time 1616993988.298314, type 1 (EV_KEY), code 109 (KEY_PAGEDOWN), value 1
Event: time 1616993988.298314, -------------- SYN_REPORT ------------
^[[6~Event: time 1616993988.386320, type 4 (EV_MSC), code 4 (MSC_SCAN), value 7004e
Event: time 1616993988.386320, type 1 (EV_KEY), code 109 (KEY_PAGEDOWN), value 0
Event: time 1616993988.386320, -------------- SYN_REPORT ------------
Event: time 1616994002.586570, type 4 (EV_MSC), code 4 (MSC_SCAN), value 7003e
Event: time 1616994002.586570, type 1 (EV_KEY), code 63 (KEY_F5), value 1


so if I translate KEY_PAGEUP to KEY_SPACE and KEY_B to KEY_F11 I can pause the video I'm watching in firefox and toggle fullscreen. I could translate F5 to something but I don't want to activate the laser pointer.
Code
int translatetable[] = { 
KEY_PAGEUP, KEY_SPACE,
KEY_B, KEY_F11,
KEY_PAGEDOWN, KEY_SPACE
}

Code
sudo ./test6keypos /dev/input/by-id/usb-Kensington_Wireless_Presenter_with_Laser_Pointer-event-kbd 

parameters 2
parameter 0 = ./test6keypost
parameter 1 = /dev/input/by-id/usb-Kensington_Wireless_Presenter_with_Laser_Pointer-event-kbd
Reading From : Kensington Wireless Presenter with Laser Pointer 
Getting exclusive access: SUCCESS
104 => 57
48 => 87
109 => 57
Press ESC to exit
readval=24  ev type=1  key in=48  => button out=87 value=1
readval=24  ev type=1  key in=48  => button out=87 value=0
readval=24  ev type=1  key in=48  => button out=87 value=1
readval=24  ev type=1  key in=48  => button out=87 value=0
readval=24  ev type=1  key in=48  => button out=87 value=1
readval=24  ev type=1  key in=48  => button out=87 value=0
readval=24  ev type=1  key in=104  => button out=57 value=1
readval=24  ev type=1  key in=104  => button out=57 value=0

I've got a USB foot pedal for transcription around here somewhere, I think I could make that work too by translating keys to something useful.

Joined: Feb 2014
Posts: 1,016
Likes: 110
G
Very Senior Member
OP Offline
Very Senior Member
G
Joined: Feb 2014
Posts: 1,016
Likes: 110
It's interesting that USB keyboards can generate events on multiple channels: For example, one keyboard that I have has 3 separate channels that will send events to, each one having an entry in /dev/input:
Code
sudo evtest

/dev/input/event19:	USB Wired Keyboard
/dev/input/event20:	USB Wired Keyboard System Control
/dev/input/event21:	USB Wired Keyboard Consumer Control

However, only 2 of these show up in /dev/input/by-id

Code
ls /dev/input/by-id
usb-0461_USB_Wired_Keyboard-event-if01
usb-0461_USB_Wired_Keyboard-event-kbd                       

If I run evtest on each one in turn, it tells me which events can be generated by that particular channel:

This channel is the normal 104 key keyboard.

Code
sudo evtest
Select the device event number [0-28]: 19
Input device ID: bus 0x3 vendor 0x461 product 0x4e6f version 0x110
Input device name: "USB Wired Keyboard"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 1 (KEY_ESC)
    Event code 2 (KEY_1)
    Event code 3 (KEY_2)
    Event code 4 (KEY_3)
...
  Event type 4 (EV_MSC)
    Event code 4 (MSC_SCAN)
  Event type 17 (EV_LED)
    Event code 0 (LED_NUML) state 0
    Event code 1 (LED_CAPSL) state 0
    Event code 2 (LED_SCROLLL) state 0
Key repeat handling:
  Repeat type 20 (EV_REP)
    Repeat code 0 (REP_DELAY)
      Value    250
    Repeat code 1 (REP_PERIOD)
      Value     33



This channel will generate KEY_SLEEP etc. (System Control)

Code
sudo evtest
Select the device event number [0-28]: 20
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x461 product 0x4e6f version 0x110
Input device name: "USB Wired Keyboard System Control"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 116 (KEY_POWER)
    Event code 142 (KEY_SLEEP)
    Event code 143 (KEY_WAKEUP)
  Event type 4 (EV_MSC)
    Event code 4 (MSC_SCAN)
Properties:
Testing ... (interrupt to exit)

This channel will generate KEY_VOLUMEUP/KEY_VOLUMEDOWN etc. (Consumer Control)

Code
sudo evtest
Select the device event number [0-28]: 21
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x461 product 0x4e6f version 0x110
Input device name: "USB Wired Keyboard Consumer Control"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 114 (KEY_VOLUMEDOWN)
    Event code 115 (KEY_VOLUMEUP)
    Event code 128 (KEY_STOP)
    Event code 130 (KEY_PROPS)
   ...
  Event type 2 (EV_REL)
    Event code 6 (REL_HWHEEL)
    Event code 12 (REL_HWHEEL_HI_RES)
  Event type 3 (EV_ABS)
    Event code 32 (ABS_VOLUME)
      Value      0
      Min        0
      Max      572
  Event type 4 (EV_MSC)
    Event code 4 (MSC_SCAN)

So if I want to capture all the events from this particular keyboard, I would have to open all 3 channels in EVIOCGRAB mode.

This would be useful if I wanted to handle all the events from a keyboard, for example a kiosk like application.

It was baffling how certain special keys like VOL UP on the keyboard were getting processed when it was opened it in exclusive GRAB mode. Only one of the channels was getting opened in exclusive mode.

To get all of the events before the OS can get it, /dev/input/event19, event20 and event21 would have to opened in EVIOCGRAB mode.

Page 1 of 2 1 2

Moderated by  R. Belmont 

Link Copied to Clipboard
Who's Online Now
1 members (R. Belmont), 52 guests, and 2 robots.
Key: Admin, Global Mod, Mod
ShoutChat
Comment Guidelines: Do post respectful and insightful comments. Don't flame, hate, spam.
Forum Statistics
Forums9
Topics9,239
Posts120,934
Members5,061
Most Online1,283
Dec 21st, 2022
Our Sponsor
These forums are sponsored by Superior Solitaire, an ad-free card game collection for macOS and iOS. Download it today!

Superior Solitaire
Forum hosted by www.retrogamesformac.com