OP
Senior Member
Joined: Feb 2014
Posts: 985 Likes: 100 |
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)
|