Need to Intercept Hid Keyboard Events (And Then Block Them)

Need to intercept HID Keyboard events (and then block them)

So I whipped up a proof-of-concept app according to that post I found here

It does exactly what I require - just though I'd share my solution anyway.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <sys/time.h>
#include <termios.h>
#include <signal.h>

int main(int argc, char* argv[])
{
struct input_event ev[64];
int fevdev = -1;
int result = 0;
int size = sizeof(struct input_event);
int rd;
int value;
char name[256] = "Unknown";
char *device = "/dev/input/event3";


fevdev = open(device, O_RDONLY);
if (fevdev == -1) {
printf("Failed to open event device.\n");
exit(1);
}

result = ioctl(fevdev, EVIOCGNAME(sizeof(name)), name);
printf ("Reading From : %s (%s)\n", device, name);

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

while (1)
{
if ((rd = read(fevdev, ev, size * 64)) < size) {
break;
}

value = ev[0].value;

if (value != ' ' && ev[1].value == 1 && ev[1].type == 1) {
printf ("Code[%d]\n", (ev[1].code));
}
}

printf("Exiting.\n");
result = ioctl(fevdev, EVIOCGRAB, 1);
close(fevdev);
return 0;
}

Low level keyboard hook & keystrokes from rawinput

Ok, Silence Dogood give me a good clue:

The library Interception is perfect for my use. You can find it here:

https://github.com/oblitum/Interception

Trigger Keypress on Physical Keyboard

I think I found a solution/hack that works for me. I was avoiding the magic unicorn described in the question and stuck to the driver I already had.

The first idea came, when I realized that I'm already able to inject keyboard strokes into Kernel-Land. Just not to a specific keyboard, but a random one. I initially planned to query all available HID-Keyboards using RawInput and then correlate each of those Items with an ID in the driver. But why not do it the other way around? Inject a random device ID with a keystroke and wait for RawInput to trigger the corresponding keyboard.

So that's what I did. I told the driver to inject a keypress for a specific device and waited for the keystroke to reach RawInput. Only two Problems occur:

  1. Some keystrokes don't reach the RawInput-API with a Keyboard-Handle. So I won't be able to correlate those
  2. Once they are past the driver, I can't intercept them anymore. (can't use LowLevelHooks, because they run before RawInput does, don't want to use Global Hooks as they have many downsides)

So I just needed to find a key that doesn't do, like, anything at all. Luckily the driver works with scan codes. This document told me, that there is a range of scan codes (0x55 - 0x7f) that is free for grabs and every manufacturer kinda does something custom in there. So thats where I started scanning and looking for the results in the RawInput-API by blindly firing those ScanCodes into the driver.

Disclaimer: If you want to do this, you better have at least two screens and you make sure that your focused window is not on top of VisualStudio, because your focused window goes instant fullscreen and your keyboard stops working as long as the window is focused... also your unable to go out of fullscreen, even if you get your keyboard control back...

As a matter of fact, there seem to be quite a few free scancodes to choose from. But some of them are used in different countries or don't trigger on all keyboard drivers. I opted for the ScanCode 0x7e, which translates to KeyCode 194 and doesn't seem to be used in any language, but works on all (2) keyboards I had.

Also, this "key" always includes a proper keyboard Handle, so no issues correlating stuff. So as a final implementation, I get the number of connected keyboards using the RawInput-API and cycle through all possible IDs (0-10/20ish to prevent too many lookups/waiting) until I found an ID for every Keyboard. And because the scan code isn't used for anything, the user is not interrupted by sudden caps locks or anything. Also, this needs to be redone everytime a keyboard is replugged, because every plugged in device gets a new ID, even if it already had one earlier.

Can't block capslock with CGEventTap

You cannot block capslock like that. Capslock is a condition, that is handled by the keyboard driver, not by the Window Server or the individual applications. A keypress event propagates in OS X like that:

Keyboard Driver -> Window Server -> User Session -> Active Application

At each level propagation (indicated by "->") you can place an event tap and block and/or modify key events. Keyboard Driver and Window Server are both privileged components, the other two are normal user level components.

The earliest you can catch a keyboard event is when the event propagates from the Driver (kernel space) to the Window Server (user space, but still privileged). However, as noted above, the capslock state is handled internally in the driver, thus catching an event there is already too late. The driver will already have enabled the capslock light on the keyboard (if necessary at all and not performed by the hardware on its own) and remembered the capslock state being on, which has an effect on all future key presses.

You can turn off the capslock light programmatically for HID devices (Apple has even sample code for that), but this will not turn-off capslock, it just turns off the light.

The only way I see to implement that using event taps is to manually re-write every key press with capslock to a key press without capslock (one where the capslock flag is not set and the attached letter, if any, is lower case). Further, to not confuse the user, you'd turn off the capslock light as shown by Apple's sample code.

Other alternatives are: Write your own keyboard driver, that takes control over the keyboard instead of Apple's standard driver (not as hard as you think, but still hard hard enough) or hacking your way into the driver (evil, but works). E.g. some keyboard remappers override only single methods of the driver (drivers are C++ in OS X, thus they are OO-objects and you can dynamically override C++ methods at runtime; not really override them, but rather manipulate method tables). The problems with those two solutions are: The first one means that unless your driver is as good as the one from Apple, some USB keyboards may not work with your driver, while others do, the second one means if you make any mistake, the system punishes this mistake with a kernel panic.

I'm looking myself for a way to programmatically toggle capslock on Mac, without success so far. But I can assure you, event taps are not the way to go.

Intercept Barcode Scanner, Pass to Application with Focus When Done?

To a computer, a barcode scanner is just like a keyboard, without the keys. When you scan a barcode, the scanner converts the barcode into keyboard input. In order to capture the input in a second program, you'd need to use a keyboard hook.

Look at this project and this project. You can make a program that uses one of these keyboard hooks and any data you scan with your barcode scanner will get routed to your program too.

How to capture an input device and prevent it's default behavior

You could open the raw input device for reading (basically ioctl with parameter EVIOCGRAB for Linux and RegisterRawInputDevices() for Windows as discussed here and here). However, the mechanisms are quite different for Windows and Linux, so you will end up implementing all the low-level logic twice.

It should also be possible to read the input data stream from the standard input just like you would read an input from the keyboard (e.g. scanf() or fgets() in C) with some logic that recognizes when a data set (= tag ID) is complete - the reader device might for example terminate an input with a newline '\n' or null character '\0'.

You should probably do this in a separate thread and have some kind of producer-consumer mechanism or event model for communication with your main application.



Related Topics



Leave a reply



Submit