Hi. Virtual console screen readers that wanted to use the normal keyboard for review functionality always had the problem on linux that some sort of a non-standardized kernel patch was needed to get keysniffing to work.
I've been thinking about a method that allows to do this completely from within userspace without any kernel patching. Now that I had actually found another use case for this for me personally, I went ahead and implemented the idea to see if it actually works. And yes, it does! The plan is as follows: * We iterate the input event devices in /dev/input/event* to find one with criteria that we can use (we can test for bustype, usb product/vendor ID, or for the presence of individual keys). * We then create a "uinput device" to feed keypresses back to the kernel. * We "grab" the keyboard, i.e., keypresses are no longer sent to the system, but to us (in userspace) exclusively. * Whenever we read in a key from our keyboard, we check if we handle that key, if not, we pass it back to the uinput device, which in turn will pass it back to the system. The example code below is written for a Sun type 6 USB keyboard which I happen to own. This keyboard has some extra keys which I'd like to use. Most of them are not bound in Linux itself, but two (MUTE and VOLUMEDOWN) do in fact insert garbage if pressed. So I had to write this tool to be able to use these keys for my own purpuses while avoiding them to generate garbage input when used. The same method could be used in brltty to achieve the following things we've identified in the past as worthwhile to implement: * Support braille displays without navigation keys (I've heard some japanese models are built that way). * Provide screen review for speech only situations. * And last but not least, provide better bindings for displays that do have very few navigation keys (the vario or bookworm comes to mind). Since keyboards can have many different keys these days (think USB multimedia keyboards), I guess the best way to impelement this in brltty is to couple it with some configuration file parsing that would allow the user to define which keyboard actions are bound to which brltty commands. I am open to all input here. As usual, I am not the best code designer, so some starting help for getting this into brltty core would be very appreciated? Dave? P.S.: The following code checks for a specific keyboard. We will of course want to do something much more generic here, like test for a keyboard that actually has the keys we'd like to watch out for. The function has_event() can be used to do this, a call like has_event(filedescriptor, EV_KEY, KEY_ESC) will return 1 if the keyboard associated with filedescriptor does have an ESC key... #include <dirent.h> #include <fcntl.h> #include <linux/input.h> #include <linux/uinput.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> static int has_event(int fd, unsigned short type, unsigned short code) { #define MAX(a,b) a>b?a:b const unsigned int len = sizeof(unsigned long)*8; const unsigned int max = MAX(EV_MAX, MAX(KEY_MAX, MAX(REL_MAX, MAX(ABS_MAX, MAX(LED_MAX, MAX(SND_MAX, FF_MAX)))))); unsigned long bits[(max+len-1)/len]; if (ioctl(fd, EVIOCGBIT(type, max), bits)) { return (bits[code/len] >> (code%len)) & 1; } return 0; #undef MAX } static int constructKeyboard (char *name, struct input_id *id, unsigned long *keymask) { int fd; if ((fd = open("/dev/input/uinput", O_WRONLY)) > 0) { struct uinput_user_dev description; memset(&description, 0, sizeof(description)); strncpy(description.name, name, UINPUT_MAX_NAME_SIZE); memcpy(&description.id, id, sizeof(description.id)); if (write(fd, &description, sizeof(description)) == sizeof(description)) { int key; ioctl(fd, UI_SET_EVBIT, EV_KEY); ioctl(fd, UI_SET_EVBIT, EV_REP); for (key=KEY_RESERVED; key<=KEY_MAX; key++) { if ((keymask[key/(sizeof(unsigned long)*8)] >> (key%(sizeof(unsigned long)*8)))&1) { ioctl(fd, UI_SET_KEYBIT, key); } } if (ioctl(fd, UI_DEV_CREATE) != -1) { return fd; } else { perror("ioctl UI_DEV_CREATE"); } } else { perror("write (struct uinput_user_dev)"); } } else { perror("open /dev/input/uinput"); } return -1; } int main(int argc, char *argv[]) { char root[] = "/dev/input"; int rootLength = strlen(root); int foundKeyboard = 0; DIR *directory; if ((directory = opendir(root))) { struct dirent *entry; while ((entry = readdir(directory))) { size_t nameLength = strlen(entry->d_name); struct stat status; char path[rootLength + 1 + nameLength + 1]; snprintf(path, sizeof(path), "%s/%s", root, entry->d_name); if (stat(path, &status) == -1) continue; if (S_ISCHR(status.st_mode)) { int fd; if ((fd = open(path, O_RDONLY)) > 0) { struct input_id id; if (ioctl(fd, EVIOCGID, &id) != -1) { if (id.bustype == 3 && id.vendor == 0X0430 && id.product == 0X0005 && has_event(fd, EV_KEY, KEY_HELP)) { unsigned long keymask[(KEY_MAX+(sizeof(unsigned long)*8)-1)/(sizeof(unsigned long)*8)]; struct input_event event; foundKeyboard = 1; if (ioctl(fd, EVIOCGBIT(EV_KEY, KEY_MAX), keymask)) { int uinput = constructKeyboard("test", &id, keymask); if (uinput != -1) { ioctl(fd, EVIOCGRAB, 1); while (read(fd, &event, sizeof(event)) == sizeof(event)) { if (event.type == EV_KEY) { switch (event.code) { case KEY_MUTE: printf("MUTE %s\n", event.value ? "press":"release"); break; case KEY_VOLUMEDOWN: printf("VOLUMEDOWN %s\n", event.value ? "press":"release"); break; case KEY_VOLUMEUP: printf("VOLUMEUP %s\n", event.value ? "press":"release"); break; case KEY_POWER: printf("POWER %s\n", event.value ? "press":"release"); break; case KEY_STOP: printf("STOP %s\n", event.value ? "press":"release"); break; case KEY_AGAIN: printf("AGAIN %s\n", event.value ? "press":"release"); break; case KEY_PROPS: printf("PROPS %s\n", event.value ? "press":"release"); break;; case KEY_UNDO: printf("UNDO %s\n", event.value ? "press":"release"); break;; default: write(uinput, &event, sizeof(event)); printf("%d = %d\n", event.code, event.value); } } } ioctl(fd, EVIOCGRAB, 0); close(uinput); } } } } close(fd); } } } closedir(directory); if (!foundKeyboard) { printf("No supported USB keyboard found.\n"); } } else { fprintf(stderr, "Unable to open %s\n", root); } return 0; } -- CYa, Mario | Debian Developer <URL:http://debian.org/> .''`. | Get my public key via finger [EMAIL PROTECTED] : :' : | 1024D/7FC1A0854909BCCDBE6C102DDFFC022A6B113E44 `. `' `- <URL:http://delysid.org/> <URL:http://www.staff.tugraz.at/mlang/> _______________________________________________ This message was sent via the BRLTTY mailing list. To post a message, send an e-mail to: BRLTTY@mielke.cc For general information, go to: http://mielke.cc/mailman/listinfo/brltty