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.