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
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for sharing. I found it useful with ARM7 on a device that reads a barcode.
The correct header file on my linux system is /usr/include/linux/input-event-codes.h, thanks for the example!
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. :)

Comments

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 Comment

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);
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.