You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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控件的默认事件分发逻辑导致:

  1. XmDrawingArea默认不开启焦点遍历能力,左键点击后不会主动获取键盘焦点
  2. Ctrl、Shift属于修饰键,单独按下的修饰键事件默认不会被分发到没有持有键盘焦点的控件
  3. 按下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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 20:00:04