6

I'm writing a Perl script that reads data from the infamous /dev/input/event* and I didn't find a way to translate the key codes generated by the kernel into ASCII.

I'm talking about the linux key codes in this table here and I can't seem to find something that would help me translate them without hardcoding an array into the script. Am I missing something?

I'd like to skip the array part because it doesn't seem to be a good practice, so any idea? :)

1

4 Answers 4

10

Unfortunately, I don't program in Perl but here is a simple example written in C. Perhaps it might help you nevertheless.

/*
 * Based on keytable.c by Mauro Carvalho Chehab
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>

#include <linux/input.h>

#include <string.h>
#include <linux/input.h>
#include <sys/ioctl.h>

#define KEY_RELEASE 0
#define KEY_PRESS 1
#define KEY_KEEPING_PRESSED 2

#include "parse.h"

void prtcode(int codes) {
    struct parse_key *p;

    for (p = keynames; p->name != NULL; p++) {
        if (p->value == (unsigned) codes) {
            printf("scancode %s (0x%02x)\n", p->name, codes);
            return;
        }
    }

    if (isprint(codes)) {
        printf("scancode '%c' (0x%02x)\n", codes, codes);
    } else {
        printf("scancode 0x%02x\n", codes);
    }
}

int main (int argc, char *argv[]) {
    int i, fd;
    struct input_event ev[64];

    if (argc != 2) {
        fprintf(stderr, "usage: %s event-device (/dev/input/eventX)\n", argv[0]);
        return 1;
    }

    if ((fd = open(argv[1], O_RDONLY)) < 0) {
        perror("Couldn't open input device");
        return 1;
    }

    while (1) {
        size_t rb = read(fd, ev, sizeof(ev));

        if (rb < (int) sizeof(struct input_event)) {
            perror("short read");
            return 1;
        }

        for (i = 0; i < (int) (rb / sizeof(struct input_event)); i++) {
            if (EV_KEY == ev[i].type) {
                if ((ev[i].value == KEY_PRESS) || (ev[i].value == KEY_KEEPING_PRESSED)) {
                    prtcode(ev[i].code);
                    printf("type %d code %d value %d\n", ev[i].type, ev[i].code, ev[i].value);
                    printf("\n");
                }
            }
        }
    }

    return 0;
}

For generating the parse.h, put this into your Makefile:

parse.h: /usr/include/linux/input.h
    @echo generating parse.h
    @echo -en "struct parse_key {\n\tchar *name;\n\tunsigned int value;\n} " >parse.h
    @echo -en "keynames[] = {\n" >>parse.h

    @more /usr/include/linux/input.h |perl -n \
    -e 'if (m/^\#define\s+(KEY_[^\s]+)\s+(0x[\d\w]+|[\d]+)/) ' \
    -e '{ printf "\t{\"%s\", %s},\n",$$1,$$2; }' \
    -e 'if (m/^\#define\s+(BTN_[^\s]+)\s+(0x[\d\w]+|[\d]+)/) ' \
    -e '{ printf "\t{\"%s\", %s},\n",$$1,$$2; }' \
    >> parse.h
    @echo -en "\t{ NULL, 0}\n};\n" >>parse.h

Then, use it like this:

./keytable /dev/input/by-path/platform-i8042-serio-0-event-kbd
1
  • Thanks for sharing. I found it useful with ARM7 on a device that reads a barcode.
    – shwink
    Commented Feb 2, 2018 at 3:56
5

It's basically a map problem. You have to take a keycode and lookup its ASCII equivalent. What about the "array part" do you think is not a good practice?

I didn't see a module for this on CPAN, but that means that you have a chance to be the first to upload it. :)

1

Example 1 only gives you back the same key code values that are already coming from the linux kernel. For example you get KEY_A 0x1e for an 'a' key press. What you want is (and what i want) is the ascii conversion so if 'a' is pressed I want to see 0x61 for lower case and 0x41 for upper case.

1
  • Converting the keycode above to a Keysym with KeySym ks = XKeycodeToKeysym(dpy, keycode+min_keycode, modifier); (link) should already give you the widechar(unicode)... printf ("wide char:%lc\n", (wchar_t)ks);
    – olivervbk
    Commented Sep 17, 2013 at 2:10
0

To read the barcodes from a barcode reader I missed a simple application to get the pure key strokes into a string. That's by far easier to do a complete keyboard translation as the barcodes usually contain mostly numbers and some few normal ascii characters. So, perhaps, this simple python3 script may help as well others to get started. It requires python3-evdev as library. For sure, you may have to adapt the InputDevice. This works for the Manhatten reader.

from evdev import InputDevice, categorize, ecodes

dev = InputDevice('/dev/input/by-id/usb-040b_6543-if01-event-kbd')

print(dev)

shiftPressed = False
ctrlPressed = False
string = ""

for event in dev.read_loop():
    if event.type == ecodes.EV_KEY:
        keyEvent = categorize(event)
        # handle release of special keys
        if keyEvent.keystate == 0:
            if keyEvent.keycode=="KEY_LEFTSHIFT":
                shiftPressed = False
                continue
            if keyEvent.keycode=="KEY_LEFTCTRL":
                ctrlPressed = False
                continue
        # handle key presses
        if keyEvent.keystate == 1:
            if keyEvent.keycode=="KEY_LEFTSHIFT":
                shiftPressed = True
                continue
            if keyEvent.keycode=="KEY_LEFTCTRL":
                ctrlPressed = True
                continue
            if ctrlPressed:
                continue

            key = keyEvent.keycode[4:]

            if key == "ENTER":
                print(string)
                string = ""
                continue

            dict2 = {"Z" : "Y", "Y": "Z"}
            if key in dict2:
                key = dict2[key]

            if not (shiftPressed):
                key = key.lower()
            else:
                dict = {"0" : "=",
                        "1" : "!",
                        "2" : "\"",
                        "3" : "§",
                        "4" : "$",
                        "5" : "%",
                        "6" : "&",
                        "7" : "/",
                        "8" : "(",
                        "9" : ")"}
                if key in dict:
                    key = dict[key]
            string+=key

Not the answer you're looking for? Browse other questions tagged or ask your own question.