我想从包含如下数据的文本文件中读取按钮映射:
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
)?有什么标准的做法吗?
EDIT:我现在能想到的唯一方法是复制我的源代码中的所有键码,并将它们放在数组或映射中,就像evtest
实用程序一样。但是有很多键码,这对我来说似乎有点多余。此外,这可能在某些时候与<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);
// [...]
让您的程序从配置文件中读取名称到代码的映射,例如/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 %sn", 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_CUT
和KEY_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中有确切名称的键码。这样以后的定义就会覆盖以前的定义。)
这样,您只需要计算要查找的名称的哈希值,然后在keytab结构中探测链表。先比较哈希,然后比较长度;如果两者都匹配,最后做一个strcmp()
。这样查找将非常快,而且实现起来也相对简单。使用当前的键代码,您只需要为KEY_F15
和KEY_CUT
执行两次慢速strcmp()
;对于所有其他的,一个strcmp()
就足够了。
问题吗?
我找到了一种正确的方法:通过使用libevdev的libevdev_event_code_from_name
函数。
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!");
}