Bogdan Constantinescu
Bogdan Constantinescu

Reputation: 5356

How can I translate Linux keycodes from /dev/input/event* to ASCII in Perl?

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? :)

Upvotes: 6

Views: 23433

Answers (4)

Chris from FreeMind
Chris from FreeMind

Reputation: 55

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

Upvotes: 0

Frank Rizzo
Frank Rizzo

Reputation: 11

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.

Upvotes: 1

user206268
user206268

Reputation: 908

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

Upvotes: 10

brian d foy
brian d foy

Reputation: 132896

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. :)

Upvotes: 5

Related Questions