首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Linux 焦点模型分析(3)X 系统上焦点的管理-2

Linux 焦点模型分析(3)X 系统上焦点的管理-2

  • Client 响应窗口管理器
ICCCM 对 Client 如何响应窗口管理器的消息也做出了规定。ICCCM 规定 Client 只有在以下情况下才能将焦点设置到其子窗口上:在 WM_PROTOCOLS 属性中设置了 WM_TAKE_FOCUS,并且满足以下条件之一:
  • WM_HINTS 的 InputHint 域设置为 True,并且焦点已经设置到其顶层窗口上;
  • WM_HINTS 的 InputHint 域设置为 False,并且收到了 ButtonPress、ButtonRelease、Passive-grabbed KeyPress、Passive-grabbed KeyRelease 消息;
  • 收到了 WM_TAKE_FOCUS 消息。
当 Client 在满足了以上条件后,可以通过 XSetInputFocus 发送设置焦点的请求。下面的程序片断展示的是 Client 如何响应窗口管理器发来的 WM_TAKE_FOCUS 通知。
清单5 Client 响应 WM_TAKE_FOCUS 设置焦点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Display *display;
Atom take_focus_protocol;

void main_loop(XEvent *xe)
{
    if (xe->type == ClientMessage)
    {
        XClientMessageEvent *xClientEvent = (XClientMessageEvent *)xe;
        if (xClientEvent->data.l[0] == take_focus_protocol)
        {
            XSetInputFocus( display,
xClientEvent->window,
RevertToParent,
xevent->xclient.data.l[1]);
        }
}
……
}




也许读者已经注意到了,与 Windows 的 SetFocus/SetActiveWindow 不同,XSetInputFocus 参数除了有 display 和 window 外还有两个参数:revert_to 和 time。
清单6 XSetInputFocus 声明
XSetInputFocus(display, focus, revert_to, time)
      Display *display;
      Window focus;
      int revert_to;
1
Time time;




revert_to 参数用于指示窗口管理器如果窗口不可见了焦点将设置给哪个窗口。它可以设置为 RevertToParent,RevertToPointerRoot 和 RevertToNone。由于 RevertToPointerRoot和 RevertToNone 都不够安全,ICCCM 规定 Client 在调用 XSetInputFocus 时,应该把该参数设置为 RevertToParent。
time 参数指示了请求焦点的事件戳。如果使用 CurrentTime 作为 time 的值,则表示该请求使用 X Server 的事件戳。由于 XSetInputFocus 是向 X Server 发送获得焦点的请求,是一个异步的过程,如果直接使用 CurrentTime 可能产生同步问题。最好的办法是直接 WM_TAKE_FOCUS 消息的时间戳。
X Destop Group 在 ICCCM 的基础上还专门针对窗口管理器制定了一些扩展规范,称为Extended Window Manager Hints ( EWMH )spec。在本文中就不做过多讨论,有兴趣的读者可以参看 。
输入代理输入代理是 X 焦点系统上一个非常有趣的概念。它是在 XEmbed 协议中提出的一项技术。
在“ X 系统上的键盘输入模型”一节中,我们介绍了 X 上特殊的键盘输入模型。键盘事件不仅与焦点相关还与鼠标位置相关。对于一般的应用而言,这样模型可以通过应用的逻辑焦点设置来进行控制。但是对于有嵌入窗口的应用程序而言,这样的输入模型会带来很多的问题。
图 5 嵌入窗口下的焦点管理以上图为例,窗口 A、B 属于 Client1,而窗口 C 属于 Client2,窗口 B、C 是窗口 A 的子窗口。此时,窗口 A 拥有 X 焦点。在 X 系统上,当鼠标位于 P1 和 P2 位置时,键盘事件会被发送给窗口 A;如果鼠标在 P3 时,键盘事件会发送给窗口 B,但是由于 A 与 B 属于同一个 Client,应用程序可以根据自己维护的逻辑焦点将键盘事件发给正确的窗口;但是,如果鼠标位于 P4 位置时,此时,键盘事件将直接发送给 C 窗口,Client1 将得不到任何消息,也无法进行派发。为了解决这个问题,XEmbed 协议提供了“焦点代理”的概念。
它的具体实现是顶层窗口创建一个看不见,但是能够得到焦点的子窗口,称为焦点代理( focus proxy )。它一般是一个1*1大小的窗口,这个窗口一般位于(-1,-1)的位置,并且没有子窗口。当其它窗口获得焦点时,Client 必须通过 XSetInputFocus 把焦点设置到这个代理上。由于这个焦点代理不是任何其它窗口的父窗口,而且鼠标永远不可能位于这个窗口上,因此所有的键盘事件必然发到这个焦点代理上,再由它根据逻辑焦点派发键盘事件。
要实现“焦点代理”技术要求 Client 必须能够接收窗口管理器的 WM_TAKE_FOCUS 消息。换句话说,要求 Client 必须使用局部主动输入模型。
虽然焦点代理的技术是为了解决嵌入窗口的焦点管理问题,但是由于此技术能够很好的解决 X 系统下的焦点问题,给用户提供更好的体验,目前很多基于 X 系统的高级图形库都使用了这个技术。
除了输入代理的概念以外,XEmbed 协议还定义了窗口激活状态的概念,它类似于Windows 的活动窗口的概念,并为此相应的定义了一些消息。关于 XEmbed 协议的相关资料可以参看 。
返回列表