![键盘上的单个按键会为每个同时按下的按键产生额外的按键](https://linux22.com/image/45892/%E9%94%AE%E7%9B%98%E4%B8%8A%E7%9A%84%E5%8D%95%E4%B8%AA%E6%8C%89%E9%94%AE%E4%BC%9A%E4%B8%BA%E6%AF%8F%E4%B8%AA%E5%90%8C%E6%97%B6%E6%8C%89%E4%B8%8B%E7%9A%84%E6%8C%89%E9%94%AE%E4%BA%A7%E7%94%9F%E9%A2%9D%E5%A4%96%E7%9A%84%E6%8C%89%E9%94%AE.png)
我最近从 QPAD 购买了 MK-85 机械 USB 键盘。键盘在 Windows 上完美运行。它在 Syslinux 中完美运行。它在 Linux 上几乎完美运行。 Linux 上的唯一问题是单个密钥行为异常(Gentoo (3.6.11)、Arch Linux 和 Linux Mint (2.6.38) 均受到影响)。
该键盘是 105 键的德式布局键盘,有问题的键是 Ä 和 ENTER 之间的键。在美国布局上,这对应于键\,在德国布局上,这对应于#,在斯堪的纳维亚布局上,它对应于'。
当该键与其他键一起按下时,它会为同时按下的每个其他键产生额外的按键。例如,在斯堪的纳维亚布局下,如果我想输入单词“不“很快我就得出结论:不要”
可以使用程序 showkeys 观察该行为:
kb mode was UNICODE
[ if you are trying this under X, it might not work
since the X server is also reading /dev/console ]
press any key (program terminates 10s after last keypress)...
keycode 28 release
keycode 32 press // d pressed
keycode 24 press // o pressed
keycode 49 press // n pressed
keycode 32 release // d released
keycode 43 press // ' pressed
keycode 24 release // o released
keycode 43 release // ' released
keycode 43 press // ' pressed, extra ' produced
keycode 49 release // n released
keycode 43 release // ' released
keycode 43 press // ' pressed, extra ' produced
keycode 20 press // t pressed
keycode 43 release // ' released
keycode 43 press // ' pressed, extra ' produced
keycode 20 release // t released
keycode 43 release // ' released
keycode 43 press // ' pressed, extra ' produced
keycode 43 release // ' released (REAL)
无论键盘布局如何,它只会发生在这个单一的键上。它表现出来的另一种方式是,如果我按住一个键,它会重复,并且我按住另一个键,它也应该开始重复:
aaaaaaaaaakkkkkkkkkkkkk (works as intended)
¨¨¨¨¨¨¨¨¨¨fffffffffffff (works as intended)
''''''''''a'''''''''''' (a is not repeated, instead ' continues)
在 Windows 上不存在此问题:
OnKeyDown, Key code=68, Control keys=, Key name d
OnKeyPress d
OnKeyDown, Key code=79, Control keys=, Key name o
OnKeyPress o
OnKeyDown, Key code=78, Control keys=, Key name n
OnKeyPress n
OnKeyup, Key code=68, Control keys=, Key name d
OnKeyDown, Key code=191, Control keys=, Key name ........OEM specific
OnKeyPress '
OnKeyup, Key code=79, Control keys=, Key name o
OnKeyup, Key code=78, Control keys=, Key name n
OnKeyDown, Key code=84, Control keys=, Key name t
OnKeyPress t
OnKeyup, Key code=191, Control keys=, Key name ........OEM specific
OnKeyup, Key code=84, Control keys=, Key name t
你觉得SE怎么样?硬件问题?它在 Syslinux 中工作得很好,这让我感觉 Linux 端有问题。有什么指示、想法或更好的调试方法吗?如果要让它正常工作需要修补内核,我愿意这样做。
答案1
我已经尝试为这个错误制作适当的补丁。这是内核的问题,而不是键盘的问题,尽管可以说键盘的行为方式很奇怪。无论如何,该补丁已提交到 linux-input 列表进行审查,但还没有任何评论。
这应该可以解决这里提到的 QPAD MK-85 的问题,但 Corsair K70、Gigabyte Osmium 和其他类似键盘也存在同样的问题。如果您的键盘存在该错误,那么您可以测试该补丁,那就太好了。如果您测试它,请告诉我它是否有效以及您拥有什么键盘,您使用的语言版本也很重要,美国和非美国键盘的行为会有所不同。请注意,美国键盘上的反斜杠键在其他版本的键盘上会有其他标签。
这是来自 linux-input 的带有补丁的邮件:
答案2
好吧,我设法整理了一个解决这个问题的技巧。我会把它写在这里,以防有人遇到同样的问题。
首先,如果您对调整内核源代码不感兴趣,您可能还有另一个选择:http://kbd-mangler.sourceforge.net/- 我没有测试它,但描述看起来很有希望。它允许您在输入传递到系统之前对其进行调整。
我的解决方案是编辑文件 drivers/hid/hid-input.c。在文件的开头我添加了三个新的变量定义;
static bool CODE43TRUE = 0; // If true, code43 has been pressed
static bool CODEXXTRUE = 0; // If true, any other key has been pressed
static int CODESKIP = 0; // Counter for skipping extra code43 events
找到函数
void hidinput_hid_event
这个函数的底部是
input_event(input, usage->type, usage->code, value);
输入是控制器,类型是指事件类型(1 是按键。2 是鼠标运动?),代码是按键代码,值是 0 表示按下,1 表示按下。
每次按下任何按键,HID 系统都会循环遍历所有键盘按键 4 次。我不知道为什么它会执行 4 次,但 4 次对应于我用有问题的按键所获得的额外按键次数。在第一个循环中,按下的键值为 0,在第二个循环中值为 1,在第三次和第四次循环中再次值为 0。
解决方案是修改此函数,以便在按下其他键时或在原始按键的 4 个循环内,不允许再次按下有问题的键。这是通过以下代码实现的(我有没有提到我至少有十年没有编写 C 代码了?抱歉)
/* report the usage code as scancode if the key status has changed */
if (usage->type == EV_KEY && !!test_bit(usage->code, input->key) != value)
input_event(input, EV_MSC, MSC_SCAN, usage->hid);
// NEW CODE STARTS HERE
if (usage->type == 1 && value == 1) // Keypress ahead
{
if (usage->code == 43) { // Keypress is code 43
if (CODE43TRUE == 0) { // Key not yet pressed
CODE43TRUE = 1;
printk(KERN_INFO "CODE43 SET TRUE\n");
}
else { // Key already pressed, so force value 1
printk(KERN_INFO "CODE43 ALREADY TRUE SET VALUE 1\n");
value = 0;
}
}
else { // Some other key pressed, set XX true
CODEXXTRUE = 1;
printk(KERN_INFO "CODEXX SET TRUE\n");
}
printk(KERN_INFO "Keypress type:%u code:%u value%d\n", (unsigned int) usage->type, (unsigned int) usage->code, (int) value);
}
if (usage->type == 1 && value == 0) { // Non-pressed key ahead
if (usage->code == 43) { // If its a 43
printk(KERN_INFO "43 call..\n");
if (CODE43TRUE == 1) { // And 43 is fake pressed still
if (CODEXXTRUE == 1 || CODESKIP < 4) { // If other buttons are pressed OR we are less than 5 ticks into the press..
printk(KERN_INFO "FAKE PRESS 43. CODESKIP %d\n",CODESKIP);
value = 0;
CODESKIP ++;
}
else { // No other buttons pressed and over five ticks have passed
printk(KERN_INFO "43 RELEASED\n");
CODE43TRUE = 0;
CODESKIP = 0;
}
}
// Reset the CODEXXTRUE (next time we get info about 43, we have looped through all the other keys so we know if something is pressed)
CODEXXTRUE = 0;
}
}
// NEW CODE ENDS HERE
input_event(input, usage->type, usage->code, value);
如果您要实现此功能,您可能需要在验证其按预期工作后删除 printk 语句。他们只是帮助调试。