|
Joined: Feb 2014
Posts: 1,016 Likes: 110
Very Senior Member
|
OP
Very Senior Member
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:
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/a721aad80ae89a5c69f7c964fa20fec1https://github.com/GrantEdwards/uinput-joystick-demohttps://www.kernel.org/doc/html/v4.12/input/uinput.htmlhttps://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.
#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]](https://i.imgur.com/KHRTIxb.png) and using the keyboard as joystick buttons in the UI: ![[Linked Image from i.imgur.com]](https://i.imgur.com/jmebOiM.png) 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
Very Senior Member
|
OP
Very Senior Member
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.
#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: 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
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
Very Senior Member
|
OP
Very Senior Member
Joined: Feb 2014
Posts: 1,016 Likes: 110 |
I hooked up 3 extra usb keyboards, ran a separate translate for each one:
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]](https://i.imgur.com/WN1AQtg.png) Pressing keys on each keyboard simultaneously in jstest-gtk.
|
|
|
|
Joined: Feb 2014
Posts: 1,016 Likes: 110
Very Senior Member
|
OP
Very Senior Member
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:
#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]](https://i.imgur.com/1FamNxn.png) 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
Very Senior Member
|
OP
Very Senior Member
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?
#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]](https://i.imgur.com/2YoVLr7.png) 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]](https://i.imgur.com/rJQvYin.png) 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
Very Senior Member
|
OP
Very Senior Member
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).
#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");
}
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]](https://i.imgur.com/PrnfRK4.png) 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
Very Senior Member
|
OP
Very Senior Member
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]](https://i.imgur.com/HS9i9MP.jpg) 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.
#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");
}
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
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]](https://i.imgur.com/MZLvWh4.png)
|
|
|
|
Joined: Feb 2014
Posts: 1,016 Likes: 110
Very Senior Member
|
OP
Very Senior Member
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: #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: 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
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.
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
Very Senior Member
|
OP
Very Senior Member
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]](https://i.imgur.com/BIUjgap.jpg) 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: 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.
int translatetable[] = {
KEY_PAGEUP, KEY_SPACE,
KEY_B, KEY_F11,
KEY_PAGEDOWN, KEY_SPACE
} 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
Very Senior Member
|
OP
Very Senior Member
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:
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 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. 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)
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) 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.
|
|
|
1 members (R. Belmont),
52
guests, and
2
robots. |
Key:
Admin,
Global Mod,
Mod
|
|
Forums9
Topics9,239
Posts120,934
Members5,061
|
Most Online1,283 Dec 21st, 2022
|
|
These forums are sponsored by Superior Solitaire, an ad-free card game collection for macOS and iOS. Download it today!
|
|
|
|