键盘上的单个按键会为每个同时按下的按键产生额外的按键

键盘上的单个按键会为每个同时按下的按键产生额外的按键

我最近从 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 的带有补丁的邮件:

http://article.gmane.org/gmane.linux.kernel.input/37583

答案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 语句。他们只是帮助调试。

相关内容