XmDrawingArea的input callback丢失Ctrl/Shift按键事件问题咨询
Motif绘图区事件丢失问题排查与解决方案
问题现象
- 在绘图区内点击鼠标按键1,可正常获取ButtonPress和ButtonRelease事件,但之后无法收到Ctrl、Shift键的Key事件,其余按键接收正常;该状态下按下ESC等其他按键,收到对应按键的KeyPress和KeyRelease事件后,即可恢复接收Ctrl、Shift的新按键事件,之前丢失的事件不会补发。
- 再次点击鼠标按键1,Ctrl/Shift按键事件会再次“消失”;点击鼠标按键3即可恢复接收。
- 按住Ctrl后点击鼠标按键1再松开Ctrl,仅能收到Ctrl的KeyPress、鼠标ButtonRelease、Ctrl的KeyRelease事件,缺失鼠标ButtonPress事件。
复现环境
- 服务端:Windows下的eXceed、x2go连接的原生X Server环境均可复现
- 客户端:Linux、Solaris平台均存在该问题
前期排查结论
实际应用使用自定义事件循环,仅完成事件抓取、可选日志记录后分发,排查确认X11事件循环可以捕获到丢失的事件,但事件未传递到绘图区的XmNinputCallback回调函数。
最小复现代码
#include <stdio.h> #include <Xm/Xm.h> #include <Xm/DrawingA.h> void inputCb(Widget w, XtPointer client, XtPointer call) { char *type; char data[16] = {0}; XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct *) call; switch(cbs->event->type) { case ButtonPress: type="ButtonPress"; break; case ButtonRelease: type="ButtonRelease"; break; case KeyPress: type="KeyPress"; break; case KeyRelease: type="KeyRelease"; break; default: type="Other"; sprintf(data, " (%d)", cbs->event->type); break; } printf("%s%s\n", type, data); } int main(int argc, char *argv[]) { XtAppContext appContext; Widget toplevel, drawingArea; toplevel = XtVaAppInitialize(&appContext, "drawingArea", NULL, 0, &argc, argv, NULL, NULL); drawingArea= XtVaCreateManagedWidget("drawingArea", xmDrawingAreaWidgetClass, toplevel, NULL); XtAddCallback(drawingArea, XmNinputCallback, inputCb, (XtPointer)0); XtRealizeWidget(toplevel); XtAppMainLoop(appContext); return 0; }
根因说明
该问题由Motif XmDrawingArea控件的默认事件分发逻辑导致:
- XmDrawingArea默认不开启焦点遍历能力,左键点击后不会主动获取键盘焦点
- Ctrl、Shift属于修饰键,单独按下的修饰键事件默认不会被分发到没有持有键盘焦点的控件
- 按下ESC等非修饰键、点击鼠标右键时,控件会临时获得焦点,后续修饰键事件就能正常分发
解决方案
方案1:配置绘图区主动获取焦点
创建绘图区时添加XmNtraversalOn和XmNfocusPolicy配置,让绘图区支持焦点遍历,点击后自动获取焦点:
drawingArea= XtVaCreateManagedWidget("drawingArea", xmDrawingAreaWidgetClass, toplevel, XmNtraversalOn, True, XmNfocusPolicy, XmEXPLICIT, NULL);
同时在inputCb的ButtonPress分支下添加主动请求焦点的逻辑:
if(cbs->event->xbutton.button == Button1) { XmProcessTraversal(w, XmTRAVERSE_CURRENT); }
方案2:自定义事件层分发(适配自定义事件循环场景)
因已确认X11事件层可以拿到全部事件,可在自定义事件分发逻辑中新增判断,针对目标绘图区控件的修饰键事件,直接调用对应回调处理,绕过Motif默认的焦点校验逻辑。
内容的提问来源于stack exchange,提问作者John3136
相关产品推荐
相关产品推荐

