tcl/tk: 关键事件队列溢出错误?
tcl/tk: key event queue overflow bug?
我在使用 Tcl/Tk(版本 8.6,Ubuntu 14.04)时遇到了一个神秘的问题。当我按住一个按钮(例如 Return)一段时间然后释放它时,程序不再对进一步的按键做出正确反应:它忽略一些按键并产生错误的键码(通常是那个从该键按住的时间更长,即使按下了其他键)。如果事件处理程序需要一些时间(此处使用 after
模拟),则会出现此问题。
这是我的脚本testKey.tcl
:
proc keyHandler {keySym keyCode keySymNum} {
puts "keyHandler (t=[clock clicks]): ($keySym) ($keyCode) ($keySymNum)"
flush stdout
if {$keySym == "Return"} { after 500 }
}
bind . <KeyPress> "keyHandler %K %k %N"
如果我 运行 使用 wish testKey.tcl
的脚本,将焦点移动到 window 并按住 Return 键几秒钟,我不断得到像这样输出行
keyHandler (t=1474120548284090): (Return) (36) (65293)
在释放密钥后也有一段时间,我认为这是预期的行为。但是当这些输出结束时,按下更多的键(不同于 Return)会导致错误行为(忽略按键,传递错误的键代码)。
对我来说,好像某些关键事件队列溢出了。
非常感谢任何帮助,谢谢!
编辑:我试图用一个普通的 X11 程序重现错误,我认为它与 Tk 主循环有类似的作用,但这里的效果不可见:
// modified from https://gist.github.com/javiercantero/7753445
// g++ -o xreadkeys xreadkeys.C -lX11
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
Display *display;
Window window;
XEvent event;
int s;
/* open connection with the server */
display = XOpenDisplay(NULL);
if (display == NULL) {
fprintf(stderr, "Cannot open display\n");
exit(1);
}
s = DefaultScreen(display);
/* create window */
window = XCreateSimpleWindow(display, RootWindow(display, s),
10, 10, 200, 200, 1,
BlackPixel(display, s),
WhitePixel(display, s));
/* select kind of events we are interested in */
XSelectInput(display, window, KeyPressMask | KeyReleaseMask );
/* map (show) the window */
XMapWindow(display, window);
/* event loop */
long cnt = 0;
while (1) {
XNextEvent(display, &event);
/* keyboard events */
if (event.type == KeyPress) {
printf( "KeyPress (%ld): %x\n", cnt, event.xkey.keycode );
/* exit on ESC key press */
if ( event.xkey.keycode == 0x09 )
break;
/* Return */
if (event.xkey.keycode == 0x24) {
printf("Enter\n");
for (int i = 0; i < 10000; i++)
for (int j = 0; j < 40000; j++) {}
}
}
else if (event.type == KeyRelease){
printf( "KeyRelease (%ld): %x\n", cnt, event.xkey.keycode );
}
cnt++;
}
/* close connection to server */
XCloseDisplay(display);
return 0;
}
(您可能需要为您的机器调整循环迭代次数。)这不是表明问题出在 Tcl/Tk 吗?
Tk 将在这两种情况下使用相同的代码来处理事件,至少除非在一种情况下与输入法有奇怪的交互,而在另一种情况下不会。据我所知,不同系统之间的键盘事件排队是相同的运行 Ubuntu;这只是通常的 X11 键盘处理,KeyEvent
s 通过 GUI 系统的事件队列传递。理论上,您可能在 Thinkpad 情况下在服务器端填充缓冲区,而系统与桌面的不同速度更能够跟上。也许……?
虽然是的,但我建议您编写代码以更快地为事件队列提供服务(我知道这可能根本不是一件微不足道的事情)失败最终植根于 Tcl/Tk 的系统部分不负责.
一位同事建议我 运行 im-config -a
和 select "none" 作为 "active configuration" 而不是更早的 "ibus" (这会产生文件 ~/.xinputrc
包含一行 run_im none
)。如果我这样做,问题似乎在笔记本电脑上消失了(在重新启动 X 之后),但到目前为止我不知道为什么。在我的 PC 上(未出现问题),im-config -a
将 "missing" 列为 "active configuration"。
看来 Donal Fellows 对 "weird interaction with an input method" 的预感是正确的。 @Donal Fellows:你能详细说明一下你的评论吗?
@Brad Lanam:我刚刚看到您可能还用 "IME" 表示 "input method editor",所以您也是对的。
我在使用 Tcl/Tk(版本 8.6,Ubuntu 14.04)时遇到了一个神秘的问题。当我按住一个按钮(例如 Return)一段时间然后释放它时,程序不再对进一步的按键做出正确反应:它忽略一些按键并产生错误的键码(通常是那个从该键按住的时间更长,即使按下了其他键)。如果事件处理程序需要一些时间(此处使用 after
模拟),则会出现此问题。
这是我的脚本testKey.tcl
:
proc keyHandler {keySym keyCode keySymNum} {
puts "keyHandler (t=[clock clicks]): ($keySym) ($keyCode) ($keySymNum)"
flush stdout
if {$keySym == "Return"} { after 500 }
}
bind . <KeyPress> "keyHandler %K %k %N"
如果我 运行 使用 wish testKey.tcl
的脚本,将焦点移动到 window 并按住 Return 键几秒钟,我不断得到像这样输出行
keyHandler (t=1474120548284090): (Return) (36) (65293)
在释放密钥后也有一段时间,我认为这是预期的行为。但是当这些输出结束时,按下更多的键(不同于 Return)会导致错误行为(忽略按键,传递错误的键代码)。
对我来说,好像某些关键事件队列溢出了。
非常感谢任何帮助,谢谢!
编辑:我试图用一个普通的 X11 程序重现错误,我认为它与 Tk 主循环有类似的作用,但这里的效果不可见:
// modified from https://gist.github.com/javiercantero/7753445
// g++ -o xreadkeys xreadkeys.C -lX11
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
Display *display;
Window window;
XEvent event;
int s;
/* open connection with the server */
display = XOpenDisplay(NULL);
if (display == NULL) {
fprintf(stderr, "Cannot open display\n");
exit(1);
}
s = DefaultScreen(display);
/* create window */
window = XCreateSimpleWindow(display, RootWindow(display, s),
10, 10, 200, 200, 1,
BlackPixel(display, s),
WhitePixel(display, s));
/* select kind of events we are interested in */
XSelectInput(display, window, KeyPressMask | KeyReleaseMask );
/* map (show) the window */
XMapWindow(display, window);
/* event loop */
long cnt = 0;
while (1) {
XNextEvent(display, &event);
/* keyboard events */
if (event.type == KeyPress) {
printf( "KeyPress (%ld): %x\n", cnt, event.xkey.keycode );
/* exit on ESC key press */
if ( event.xkey.keycode == 0x09 )
break;
/* Return */
if (event.xkey.keycode == 0x24) {
printf("Enter\n");
for (int i = 0; i < 10000; i++)
for (int j = 0; j < 40000; j++) {}
}
}
else if (event.type == KeyRelease){
printf( "KeyRelease (%ld): %x\n", cnt, event.xkey.keycode );
}
cnt++;
}
/* close connection to server */
XCloseDisplay(display);
return 0;
}
(您可能需要为您的机器调整循环迭代次数。)这不是表明问题出在 Tcl/Tk 吗?
Tk 将在这两种情况下使用相同的代码来处理事件,至少除非在一种情况下与输入法有奇怪的交互,而在另一种情况下不会。据我所知,不同系统之间的键盘事件排队是相同的运行 Ubuntu;这只是通常的 X11 键盘处理,KeyEvent
s 通过 GUI 系统的事件队列传递。理论上,您可能在 Thinkpad 情况下在服务器端填充缓冲区,而系统与桌面的不同速度更能够跟上。也许……?
虽然是的,但我建议您编写代码以更快地为事件队列提供服务(我知道这可能根本不是一件微不足道的事情)失败最终植根于 Tcl/Tk 的系统部分不负责.
一位同事建议我 运行 im-config -a
和 select "none" 作为 "active configuration" 而不是更早的 "ibus" (这会产生文件 ~/.xinputrc
包含一行 run_im none
)。如果我这样做,问题似乎在笔记本电脑上消失了(在重新启动 X 之后),但到目前为止我不知道为什么。在我的 PC 上(未出现问题),im-config -a
将 "missing" 列为 "active configuration"。
看来 Donal Fellows 对 "weird interaction with an input method" 的预感是正确的。 @Donal Fellows:你能详细说明一下你的评论吗?
@Brad Lanam:我刚刚看到您可能还用 "IME" 表示 "input method editor",所以您也是对的。