Linux 焦点模型分析.docx
- 文档编号:24261353
- 上传时间:2023-05-25
- 格式:DOCX
- 页数:19
- 大小:101.73KB
Linux 焦点模型分析.docx
《Linux 焦点模型分析.docx》由会员分享,可在线阅读,更多相关《Linux 焦点模型分析.docx(19页珍藏版)》请在冰豆网上搜索。
Linux焦点模型分析
Linux焦点模型分析
冯遥遥(***************.com),软件工程师,IBM中国软件开发中心
2008年1月21日
本文以SUSE10为例,通过实例帮助读者理解Linux环境下焦点系统与鼠标、键盘输入的关系,并讨论其与Windows环境下焦点系统的区别。
另外,本文还将介绍Xserver、windowmanager以及应用程序之间如何相互配合以管理焦点。
最后本文将简要介绍GTK的焦点模型。
本文面向Linux应用程序开发人员,以及GUI应用程序移植人员。
帮助读者深入理解Linux系统的焦点系统与管理方式。
概述
无论是Windows应用程序还是Linux应用程序的开发人员,焦点(focus)都是一个非常常见的一个概念。
那么焦点究竟是什么呢?
简单的说,焦点决定了由哪个窗口或者控件接收键盘输入信息,因此,它又被称作输入焦点。
对于用户来说,最直观的感觉是,有光标闪动的窗口或者被高亮的控件就有焦点。
很多初级应用程序员或者Windows用户有这样一个误解,认为凡是鼠标点击的窗口就是焦点窗口。
当出现有的窗口或者控件点击后没有反应的现象时,就认为是焦点出现了问题。
事实上,焦点仅仅控制着键盘的输入,而鼠标输入与焦点没有直接关系。
用户之所以有这样的误解是由于另一个概念,系统的焦点模式(focusmode)。
焦点模式决定了鼠标如何使一个窗口获得焦点。
一般来说,焦点模式被分为三种:
1.click-tofocus:
这种模式就是指鼠标点击的窗口就可获得焦点。
这是Windows用户最常见的一种模式。
鼠标点击的窗口会被激活,被置于所有窗口的最前面,并接收键盘输入。
2.focus-follow-mouse:
有的地方也称这种模式为pointerfocus。
它是指鼠标下的窗口可以获取焦点。
当鼠标移到一个可以获得焦点的窗口的范围内,用户不需要点击窗口的某个地方就可以激活这个窗口,接收键盘输入。
但是,这个窗口不一定会被置于所有窗口的最前面。
当鼠标移出这个窗口的范围时,这个窗口也会随之失去焦点。
3.sloppyfocus:
这种模式与focus-follow-mouse一样,当鼠标移至某窗口的范围内的时候,该窗口会获得焦点。
与focus-follow-mouse不同的是,当鼠标移出这个窗口范围时,焦点也不会随之改变。
只有当鼠标移动到别的可以接收焦点的窗口时,系统焦点才改变。
不同的系统对焦点模式的支持不同,所使用的焦点模型也有很大的区别。
Linux与Windows焦点系统
Linux使用的焦点模型与Windows有着很大的区别。
对于大多数用户来说,Windows的焦点模型较为直观、易于理解,因此,本节中我们将以Windows上的焦点模型为引子,着重介绍Windows与Linux上焦点系统的基本概念,并讨论两个平台上焦点系统的区别。
Windows上的焦点系统
∙Windows上的焦点模式
Windows上默认采用click-to-focus的焦点模式。
这是因为Windows操作系统采用的资源管理器explorer.exe只支持这一种焦点模式。
这也是造成之前所提到的用户认为鼠标点击的窗口就是焦点窗口的错觉的原因之一。
现在有一些基于X的Windows窗口管理器,如blackboxforWindows等,可以替代explorer。
这些窗口管理器就可以支持以上提到的焦点模式。
∙Windows上的键盘输入模型
正如前面所介绍的,焦点决定了哪个窗口可以获得键盘输入。
那么,介绍系统的焦点模型就不能不提到键盘输入。
下图展示的就是Windows上的键盘输入模型。
图1Windows上键盘输入模型
当键盘中的一个键被按下或者被释放时,键盘驱动会收到键盘中断,获得该按键的扫描码(scancode)。
这是一个与硬件相关的数值。
驱动会根据键盘布局将这个扫描码转换成设备无关的虚拟键盘码(virtual-keycode),并生成一个键盘消息(WM_KEYDOWN或者WM_KEYUP消息)放在系统输入队列中。
在任何给定的时刻,只有一个线程与系统输入队列连接。
系统会将这个消息从系统输入队列中取出,发送给这个线程的输入消息队列。
该线程的消息循环又会从本线程的消息队里取出这个消息,传递给合适的窗口处理过程。
这样的输入模型保证了一个线程的行为不会对其它前程产生影响。
例如,如果一个线程挂起了,不会妨碍其他线程接收键盘输入。
那么,哪个线程是“与系统输入队列连接的线程”呢,哪个窗口又是这个“合适的窗口”呢?
Windows有它自己的管理方式。
∙Windows上的焦点管理
在Windows上,窗口消息是以线程为单位进行管理的。
每个进程可能有多个线程在执行,每个线程都可以创建窗口。
用户当前正在使用的顶层窗口被称为前景窗口(foregroundwindow),它位于所有窗口的上面。
而创建该窗口的线程就被称为前景线程(foregroundthread)。
相应地,别的窗口被称为背景窗口(backgroundwindow),创建它们的线程则被称为背景线程(backgroundthread)。
应用程序可以使用SetForegroundWindow来设置前景窗口。
用户也可以用鼠标,或者ALT+TAB,ALT+ESC来切换前景窗口。
每个线程内部还维护着自己的活动窗口(activewindow)和焦点窗口(focuswindow)。
焦点窗口(focuswindow)实际上是一个窗口的临时的属性。
拥有焦点的窗口可以从线程的消息队列中获得键盘消息。
焦点窗口的顶层窗口被称为活动窗口(ActiveWindow)。
程序员可以使用SetFocus和SetActiveWindow来为该线程设置焦点窗口和活动窗口。
但是,焦点窗口只是一个局部的概念,并不是所有的焦点窗口都可以获得键盘事件。
只有前景线程的焦点窗口才能从系统队列中得到键盘事件,而前景线程中的活动窗口是前景窗口。
在任何时刻系统中都只可能有一个被激活的窗口,这就是前景窗口。
这也就回答了上一节中的问题:
与系统队列相连接的线程就是前景线程,而那个可以得到键盘事件的窗口就是前景线程的焦点窗口。
当然,Windows还提供了AttachThreadInput方法来合并两个线程的输入队列。
本文主要介绍焦点系统,对输入也就不做过多介绍了。
当一个线程的焦点窗口从一个窗口改变到另一个窗口的时候,失去焦点的窗口会收到系统发出的WM_KILLFOCUS消息,而得到焦点的窗口会收到WM_SETFOCUS消息。
当另一个窗口被激活时,系统会向这两个窗口发送WM_ACTIVATE,并用wParam来通知窗口是被激活了或者去激活了。
如果被激活的窗口属于另一个应用,那么系统将给这两个应用发送WM_ACTIVATEAPP消息。
事实上,Windows上采用的是一套简单、易于理解的焦点系统。
Linux的焦点系统远比Windows的复杂得多。
Linux上的焦点系统
∙Linux上的焦点模式
几乎所有Linux系统都支持前面介绍的三种焦点模式。
以SUSE10的GNOME桌面系统为例。
SUSE10上焦点模式的默认焦点模式也是采用click-focus模式。
同时,用户也可以通过ControlCenter->Windows来修改系统所使用的焦点模式(见图2)。
“WindowSelection”下的复选框如果没有被选中,如图中所示,则使用的是click-focus模式,也就是默认的焦点模式。
如果用户将此复选框选中,那么系统将采用sloppyfocus模式。
图2SUSE10上设置焦点模式
用户所设置的焦点模式由GConf管理保存,它所对应的键值为“/apps/metacity/general/focus_mode”。
用户也可以通过gconf-editor修改此键对应的值达到修改焦点模式的目的(见图3)。
图3GConf中对焦点模式的设置
从GConf中对这个键的描述我们可以看出,该键支持的值有“click”、“sloppy”和“mouse”,分别对应了焦点模式中的click-focus,sloppyfocus和focus-follow-mouse。
∙X系统中的焦点
Linux上的界面系统使用的是X窗口系统。
X是一个面向网络的窗口系统,它采用了Client-Server的模型。
Client可以看做是X系统下运行的应用程序。
XServer为各个Client提供了界面方面的服务。
Client和Server可以不在同一台机器上运行,它们通过网络发送请求和消息。
Windows上的焦点是一个局部的概念,每个线程都拥有自己的焦点窗口。
但是在X系统中,焦点是一个全局的概念。
整个系统中只有一个X窗口拥有X焦点。
应用程序可以通过XSetInputFocus来设置焦点窗口。
与Windows不同的是,在Linux上不可见的窗口是不能被设置为焦点窗口的。
在X系统中,XServer会在窗口获得或者失去焦点时发送FocusIn和FocusOut事件通知Client。
但是和Windows不同的是,并不是所有窗口都能在焦点切换时收到通知,只有创建时选择了FocusChangeMask的窗口才能接收到焦点变化事件。
以下代码就是创建一个这样的窗口。
清单1创建接收焦点变化事件的窗口
{
Windowwindow;
XSetWindowAttributesattr;
attr.event_mask=FocusChangeMask;
//如果窗口还希望接收其它事件还可以设置其它掩码
//例如:
attr.event_mask|=StructureNotifyMask;
window=XCreateWindow(display,
XDefaultRootWindow(display),
200,200,
300,300,
0,
XDefaultDepth(display,0),
InputOutput,
CopyFromParent,
CWBackPixel,
&attr);
}
∙X系统上的键盘输入模型
X系统有着非常特殊的键盘输入模型。
在一般情况下(没有应用调用了XGrabKey或者XGrabKeyboard的情况下),X系统下键盘输入不仅与焦点有关,还与鼠标位置相关。
如果当前鼠标位于焦点窗口之外,那么,和Windows一样,XServer会将键盘事件发送给焦点窗口。
但是,如果鼠标位于焦点窗口的子窗口上,那么,键盘事件会直接发送给这个子窗口。
这样的输入模型往往使Linux的初级用户尤其是习惯了Windows焦点模型的用户相当困惑。
图4X系统键盘模型实例
以图4为例,当前窗口A拥有X焦点,窗口B是窗口A的子窗口。
当鼠标在P1位置时,它位于窗口A范围之外,那么键盘的KeyPress/KeyRelease事件会发给A窗口;
当鼠标在P2位置时,虽然该位置处于窗口A范围内,但是不属于任何A窗口的子窗口上,因此,键盘事件也会发给A窗口;
当鼠标位于P3位置时,由于P3位于窗口A的子窗口B上,因此,键盘事件将直接发给窗口B。
由于X系统上输入模型的特殊性,基于X系统开发的较为高级的图形库一般会将X焦点设置在顶层窗口上,而系统内部维护着自己的逻辑焦点。
这些图形库会统一处理发送到本应用的键盘事件,将键盘事件再分发给拥有逻辑焦点的控件。
关于X系统上焦点管理的具体规范将在下一章中进行详细的讨论。
X系统上焦点的管理
本章将具体讨论X系统上焦点的管理与控制方法和规范。
XServer与窗口管理器(windowmanager)
上一章介绍了在Linux上的界面系统使用的是X窗口系统。
在这个系统中,XServer起了很重要的作用,它管理着系统中各种资源,包括窗口、光标、字体以及所有的图形元素,它使得这些资源可以以统一的格式在各个Client之间通过网络共享。
但是,这些资源以何种风格显示出来却是由窗口管理器(windowmanager)控制的。
窗口管理器是运行在X系统上的一种特殊的应用程序,它实际上也是一个XClient。
它为X系统上运行的窗口程序提供了一套统一的外观风格机制。
例如,它决定了窗口的标题栏使用蓝色还是灰色;它规定了窗口的滚动条是扁还是圆;当两个窗口请求的位置重叠时,它控制着这两个窗口具体显示位置是否重叠,等等。
简单的说,在X系统上,所有的可见的控件都是窗口,窗口管理器就负责管理这些窗口。
一般情况下,Client需要配合窗口管理器,以使窗口达到预期的效果。
ICCCM(Inter-ClientCommunicationConventionsManual)定义了这种配合的规范。
目前流行的窗口管理器很多,比如,KWin,Metacity,FVWM,TWM,wm2等等。
虽然这是X系统广泛支持的规范,但是,ICCCM对窗口管理器的实现仅仅是一个参考各窗口管理器对ICCCM支持的程度也不同。
而对于ICCCM没有定义到的功能,各个窗口管理器的实现更有着很大的差别。
因此当应用程序在X系统下请求完成某个操作后,在收到相应前永远不能假设实际结果会是怎样。
ICCCM定义的焦点模型
在X系统中,每个窗口都具有一些属性,被称为property。
这些属性的名称和类型都以atom的形式存在。
每个atom在系统中由唯一的整数标识。
Client可以自己定义的属性,也可以由Server创建,它们都由XServer统一进行维护,在各个Client之间共享。
应用程序可以通过XSetWindowProperty,XGetWindowProperty等方法来访问这些属性。
ICCCM通过对一些property的定义,制定了XClient之间通信的规范,包括Client与Client,Client与窗口管理器,Client和会话管理器,以及颜色特征的通信规范。
在这里我们将主要介绍ICCCM就Client与窗口管理器关于焦点的规定。
ICCCM规定,所有拥有顶层窗口的Client都必须通过设置指定的property来指示窗口管理器如何管理Client。
所有与窗口管理器相关的属性都以“WM_***”的形式出现,如WM_NAME,WM_ICON_NAME等等。
其中两个属性与焦点管理相关,一个是WM_HINTS,一个是WM_PROTOCOLS。
∙WM_HINTS中的InputHint
WM_HINTS为窗口管理器提供了Client的信息包括图标的信息和位置,初始状态,是否需要窗口管理器设置焦点。
下表是Xlib中对XWMHins的声明。
程序可以通过XSetWMHints来设置该属性值。
清单2XWMHints的声明
/*Windowmanagerhintsmaskbits*/
#defineInputHint(1L<<0)
#defineStateHint(1L<<1)
#defineIconPixmapHint(1L<<2)
#defineIconWindowHint(1L<<3)
#defineIconPositionHint(1L<<4)
#defineIconMaskHint(1L<<5)
#defineWindowGroupHint(1L<<6)
#defineUrgencyHint(1L<<8)
#defineAllHints(InputHint|StateHint|IconPixmapHint|
IconWindowHint|IconPositionHint|IconMaskHint|WindowGroupHint)
/*Values*/
typedefstruct{
longflags;
Boolinput;
intinitial_state;
Pixmapicon_pixmap;
Windowicon_window;
inticon_x,icon_y;
Pixmapicon_mask;
XIDwindow_group;
}XWMHints;
其中的InputHint标识了应用程序是否依赖窗口管理器获取焦点。
只有当该域被设置为True时,窗口管理器才会在希望将其设置为焦点窗口时主动调用XSetInputFocus将其设置为焦点窗口。
例如,在click-focus模式下,当用户用鼠标点击了某个窗口,如果该标志被设置为True,窗口管理器会将此窗口设置为焦点窗口。
但是如果此标志被置为False,那么窗口管理器将不会主动设置其为焦点窗口。
下面这段示例程序显示了如何为窗口设置InputHint标志。
清单3设置inputhint
voidset_input_hint(Display*dispay,Windoww,BoolbInput)
{
XWMHints*wm_hints;
wm_hints=XGetWMHints(display,w);
wm_hints->flags|=InputHint;
wm_hints->input=bInput;
XSetWMHints(display,window,wm_hints);
XFree(wm_hints);
}
∙WM_PROTOCOLS中的WM_TAKE_FOCUS
WM_PROTOCOLS实际上是一组Atom。
每一个属性的设置实际上决定了Client是否要参与窗口管理器的某些管理。
在ICCCM中定义的标准协议Atom包括:
WM_TAKE_FOCUS、WM_SAVE_YOURSELF和WM_DELETE_WINDOW。
不同的窗口管理器还可以定义自己的协议。
Client可以通过XSetWMProtocols指定需要接受协议。
如果窗口设置了其中的协议Atom,当窗口管理器要进行某些操作的时候将会以ClientMessage事件的形式将此协议Atom发送给窗口,通知Client做出相应的行动。
其中,WM_TAKE_FOCUS协议规定了Client是否要参与到设置焦点窗口的行为中。
以下程序片断显示了用户如何设置WM_TAKE_FOCUS协议。
清单4设置WM_TAKE_FOCUS
voidset_wm_protocol(Display*display,Windoww)
{
Atomprotocols[3];
protocols[0]=XInternAtom(display,“WM_TAKE_FOCUS”,false);
…..//设置其它协议
XSetWMProtocols(display,w,protocols,3);
}
当WM_TAKE_FOCUS协议Atom被设置表明该窗口希望参与到焦点的管理中。
那么,当窗口管理器打算将该窗口设置为焦点窗口时,会给窗口发送包含了WM_TAKE_FOCUS的ClientMessage事件。
一般来说,当窗口收到这个事件后会为根据需要设置焦点窗口。
如果该Atom没有设置,那么是否设置焦点窗口就完全由窗口管理器决定。
∙输入焦点模型分类
ICCCM根据窗口对属性的设置不同分为四种输入焦点模型(InputFocusModel):
无输入(NoInput),被动输入(PassiveInput),局部主动输入(LocallyActiveInput)以及全局主动输入(GloballyActiveInput)。
每种模型对应的input域和WM_TAKE_FOCUSatom的设置如下:
表1四种输入模型的属性设置
输入模型
InputHint域
WM_TAKE_FOCUS
无输入
False
不设置
被动输入
True
不设置
局部主动输入
True
设置
全局主动输入
False
设置
从前面对InputHint域以及WM_TAKE_FOCUS的介绍,我们可以大致可以推测出这些模型的工作方式。
被动输入和局部主动输入的WM_HINTS的InputHint域设为True,说明这种模型下,窗口依赖窗口管理器来获取输入焦点。
而无输入和全局主动输入模型中,InputHint域设置为False,则表明窗口管理器不会把焦点设置到它们的顶层窗口上。
而其中,局部主动和全局主动输入设置了WM_TAKE_FOCUS,说明它们希望控制焦点的设置。
1.无输入:
使用此种模型的client永远不会希望得到键盘输入。
这是我们在平时开发中很少使用的一种模型。
2.被动输入:
这种模型的client希望能够得到键盘输入,但是它不会主动设置输入焦点,而是完全依赖窗口管理器的控制。
这种模型比较适合没有子窗口简单窗口。
在focus-follow-mouse模式下这样的设置可以使它可以接受键盘输入,同样,在click-focus模式下,窗口管理器会在鼠标点击使将该窗口设置为焦点窗口。
3.局部主动输入:
这样的client既希望得到输入,也会显式的设置焦点,但是它只会在本client的其中一个窗口已经获得焦点的情况下设置焦点。
这样的模型一般用于有很多子窗口或者子控件可以接受键盘输入的情况。
这样的模型可以方便的实现用户使用Tab键,或者方向键在各个控件间切换焦点的功能。
4.全局主动输入:
这样的client完全依靠自己的逻辑控制焦点,窗口管理器仅仅是在合适的时候发出WM_TAKE_FOCUS的客户消息通知client。
比如,有的复杂应用程序会希望在用户在点击滚动条的时候不设置焦点,而点击客户区域的时候才设置焦点。
再比如X本身不支持模态对话框,但是应用程序可以使用全局主动输入的模态实现模式对话框,使得当有模式对话框弹出的情况,主窗口不能接收键盘输入。
∙Client响应窗口管理器
ICCCM对Client如何响应窗口管理器的消息也做出了规定。
ICCCM规定Client只有在以下情况下才能将焦点设置到其子窗口上:
在WM_PROTOCOLS属性中设置了WM_TAKE_FOCUS,并且满足以下条件之一:
1.WM_HINTS的InputHint域设置为True,并且焦点已经设置到其顶层窗口上;
2.WM_HINTS的InputHint域设置为False,并且收到了ButtonPress、ButtonRelease、Passive-grabbedKeyPress、Passive-grabbedKeyRelease消息;
3.收到了WM_TAKE_FOCUS消息。
当Client在满足了以上条件后,可以通过XSet
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux 焦点模型分析 焦点 模型 分析