有没有办法从字符串中获取evdev键码?

时间:2015-08-17 20:44:01

标签: c linux c-preprocessor evdev

我想从包含以下数据的文本文件中读取按钮映射:

DPAD_LEFT = 105
DPAD_RIGHT = 106
DPAD_UP = 103
DPAD_DOWN = 108

正确的部分实际上是evdev键码(在<linux/input.h>中定义)。

这很难阅读,所以我希望能够有这样的文件:

DPAD_LEFT = KEY_LEFT
DPAD_RIGHT = KEY_RIGHT
DPAD_UP = KEY_UP
DPAD_DOWN = KEY_DOWN

但我目前无法将它们转换回来:

char[256] keyname;
some_method_to_read(&keyname, "DPAD_LEFT");
//keyname now contains "KEY_LEFT"

如何获取相应的密钥代码(例如105)?有没有标准的方法来做到这一点?

编辑:我现在能想到的唯一方法是复制源代码中的所有密钥代码并将它们放在数组或地图中,就像evtest utility一样。但是有很多的密钥代码,这对我来说似乎有点过分。此外,这可能会在某些时候与<input/linux.h>中定义的密钥代码不同步。

std::map<string, int> keynames;
#define MAP_KEYCODE(keycode) keynames[#keycode] = keycode

MAP_KEYCODE(KEY_LEFT);
MAP_KEYCODE(KEY_RIGHT);
MAP_KEYCODE(KEY_UP);
MAP_KEYCODE(KEY_DOWN);
// [...]

2 个答案:

答案 0 :(得分:0)

让您的程序从配置文件中读取名称到代码的映射,比如/usr/share/yourprogram/keycodes和/或$HOME/.yourprogram/keycodes

记录任何人都可以从/usr/include/linux/input.h重新生成该文件 - 并自行重新生成初始文件 - 例如

awk '$2 ~ /^KEY_/ { code[$2] = $3 }
     END {
       for (name in code)
         if (code[name] ~ /^KEY_/)
           code[name] = code[code[name]];
       for (name in code)
         if (code[name] !~ /KEY_/)
           printf "%-24s %s\n", name, code[name]
     }' /usr/include/linux/input.h | sort

您可能必须自己添加KEY_CNT(它的值超过KEY_MAX),因为上面的脚本不做数学,只有直接别名。

为了描述名称到代码的映射,我使用

struct keycode {
    struct keycode *next;
    unsigned int    code;
    unsigned int    hash;
    unsigned char   namelen;
    char            name[];
};

其中哈希是一个简单的哈希,比如djb2,

unsigned int djb2(const char *const str, const size_t len)
{
    unsigned int result = 5831U;
    size_t       i;
    for (i = 0; i < len; i++)
        result = result * 33U ^ (unsigned int)str[i];
    return result;
}

在当前定义的密钥代码中,只有KEY_CUTKEY_F15映射到相同的djb2哈希值1857856141.(如果您使用的是31U而不是33U,则当前设置没有碰撞,但这不能证明未来的碰撞。最好有一次碰撞,所以你可以测试它是否正确处理。)

读取配置文件的函数可以通过将新的代码添加到单链表中来返回代码,也许

int read_keycodes(const char *const filename,
                  struct keycode **list);

如果您在列表前面添加,则应稍后忽略同名的重定义。这样,如果您首先读取系统范围的配置,然后是用户特定的配置,则特定于用户的配置可以覆盖系统范围的配置。

在读取所有键码映射之后,您将构建一个哈希表,类似于

struct keytab {
    unsigned int     size; /* index = hash % size */
    struct keycode **slot;
};

(构建哈希表时,丢弃其确切名称已在密钥表中的密钥代码。这样后来的定义会覆盖之前的定义。)

这样,您只需要计算要查找的名称的哈希值,然后探测keytab结构中的链接列表。比较哈希首先,然后长度;如果两者都匹配,最后做一个strcmp()。这样,查找将非常快速,并且实现起来也相对简单。使用当前密钥代码,strcmp()KEY_F15只会执行两次慢KEY_CUT次;对于所有其他人来说,一个strcmp()就足够了。

有问题吗?

答案 1 :(得分:0)

我找到了一种正确执行此操作的方法:使用libevdev&#39; s libevdev_event_code_from_name function

unsigned int event_type = EV_KEY;
const char* name = "BTN_A";
int code = libevdev_event_code_from_name(event_type, name);
if(code < 0)
{
  puts("There was an error!");
}