X Window 内核协议是X Window系统的基础协议,它是一个以位图显示的网络化窗口系统,用来在Unix、类Unix和其它操作系统上建立用户图形界面。X Window系统基于主从式模型:单一服务器控管硬件的输出入,如屏幕、键盘和鼠标;所有的应用程序都被视作客户端,用户之间通过服务器来交互。交互部分由X Window内核协议来管理。还有其它与X Window系统有关的协议,有的建立在X Window内核协议之上的,有的是独立的协议。
历史沿革编辑本段回目录
在X Window内核协议中,只会在网络上以异步方式传送四种分组:请求、回应、事件和错误。请求是由客户端传送到服务器,告之进行一些动作(例如建立一个窗口),并回传以便持握的数据。回应是由服务器回传的若干数据。事件是由服务器传送的,其用来通知客户端某些用户的动作,或者发生了其它所关心的事件。错误是由服务器传送的分组,其用来通知客户端,在处理其请求时,发生了一些错误。请求有可能产生回应、事件和错误;除此之外,协议并不要求分组中的特定指令要以网络来传送。还有其它对内核协议的扩充,这些扩充有自己的请求、回应、事件和错误。
X Window 源于1984年的麻省理工学院(目前所发布的 X11 发表于1987年9月)。设计者鲍伯·斯凯夫勒(Bob Scheifler)和吉姆·杰提斯(Jim Gettys)早期对内核协议的原则是“机制,而非策略”,所以内核协议并未规定客户端之间以及客户端和用户之间的交互界面规范。这部分则由其它的独立规格所规范,如ICCCM、freedesktop.org规范,且可由所使用的特定组件工具包自动强制运行。
概观编辑本段回目录
服务器和客户端之间的通信,是由通道上的交换分组所完成。由客户端建立连接,且由客户端发送第一个分组。分组中包括将要使用的字节序、协议版本方面的信息,以及客户端期望服务器使用的认证种类。服务器以回传分组来答复,分组中陈述接受或拒绝连接,或要求进一步的验证。如果接受连接,接受分组内会包含客户端接下来和服务器交互所需的数据。
建立连接之后,在客户端和服务器的通道上,会有四种交换分组的类型:
请求:客户端请求服务器的信息,或者请求服务器运行一个动作。
回应:服务器回应请求。但并非所有的请求都会产生回应。
事件:服务器传送事件给客户端。如:键盘或鼠标的输入,或移动、调整、显示窗口等。
错误:如果请求无效时,服务器会传送一个错误分组。因为请求是以排队方式处理,所以经由请求所产生的错误分组,并不会立即传送出去。
请求和回应分组可以有各种长度,事件和错误分组的长度则固定是32字节。
请求分组的编号顺序是以服务器的接收为顺序:来自客户端的第一个请求编号为 1、第二个编号为 2,依此类推。请求的序列编号中最小的有效16位,包含在由请求所产生的回应和错误分组之中,如果有的话。它们也包含在事件分组中,以指出服务器正在处理或是刚刚完成的请求序列编号。
窗口编辑本段回目录
在X Window系统以及各种图形化用户界面中,窗口即为一个顶层窗口。窗口也用来指窗口内部的窗口,这类窗口是父窗口的子窗口。图形化元件,如按钮、菜单、图标等等,都是使用窗口来实现的。
客户端可请求建立一个窗口。更严谨的说,客户端可请求建立现存窗口的子窗口。所以客户端所建立的窗口,皆以树状结构组织(层次结构结构)。树状结构的根即为根窗口,根窗口是服务器在激活时,所自动建立的特殊窗口。其余窗口都是根窗口的子窗口,顶层窗口就是根窗口下的第一个子窗口。如同所见,根窗口和屏幕同等大小,且在其余窗口的后面(被子窗口遮盖住)。
窗口里的内容并非在所有时候都能显示出来。更精确的说,在窗口移动、调整大小、被其它窗口遮盖、部分或整个窗口不可见时,窗口里的内容就有可能会被销毁。更精确的说,如果 X 服务器无法维护窗口内容的后备存放区(backing store)时,这些内容就会遗失。客户端可请求为窗口进行维护的后备存放区,但服务器没有义务要这样做。因此,客户端不可假设已得到后备存放区的维护。若窗口有一部分未指出内容时,就会传送一个事件,通知客户端重绘那部分内容。
每个窗口都关系一组属性值(Attribute),如窗口的几何性质(大小和位置)、背景图、是否请求了后备存放区等等。协议中还包含用来给客户端检阅和改变窗口属性值的请求。
窗口可以是 InputOutput(输出/输入)或 InputOnly(仅输入)。前者是显示在屏幕上用于绘图的窗口,而后者并不显示在屏幕上,仅用来接受输入。
平常可看到窗口周围的装饰性框架和标题栏(可能含有按钮),是由窗口管理器所建立的窗口,而非客户端所建立的。窗口管理器也处理与元件有关的输入,例如当用户点击并拖曳窗口的边框时,便会调整窗口大小。客户端所建立的窗口,通常可以忽略窗口管理器所带来的变化。还有一个改变必须注意,那就是改变亲属关系的窗口管理器,几乎所有新式的窗口管理器,都会将顶层窗口的亲属关系改变到一个窗口(不是根窗口)里去。从内核协议的角度来看,窗口管理器是一个客户端,与其它的应用程序没有区别。
关于窗口的数据,可运行 xwininfo 程序来取得。加上 -tree命令行参数,程序便会显示子窗口的树状结构,连同识别子和几何性质数据一起显示。
图形映射和可绘区编辑本段回目录
图形映射(pixmap)是存储器中可用来绘图的区域。与窗口不同,图形映射的内容并不会自动显示在屏幕上。不过图形映射的内容(或部分内容)可转换到窗口上,反之亦然。这就让双缓冲得以实现。大部分可在窗口上完成的图形化操作,也可以图形映射完成。
窗口和图形映射被统称为可绘区(drawable),且其数据内容都保留在服务器上。客户端可请求从服务器上,将可绘区的内容转换到客户端,反之亦然。
图形上下文和字体编辑本段回目录
客户端可请求很多种图形运算,如清空一块区域、复制一块区域到另一处,绘制一个点、线、矩形和文本。对清空而言,所有运算都有可能用在可绘区上(窗口和图形映射)。
图形上下文(graphic context)包括了对图形运算的大部分请求,图形上下文是一种结构,包含有图形运算的参数。图形上下文包含前景色、背景色、文本的字体,以及各种图形参数。当请求图形运算时,客户端就包含一个图形上下文。很明显的,并非所有的图形上下文参数都会参与运算:例如,字体对于直线的绘制不产生作用。
内核协议规格使用了服务器侧的字体。如字体是以文件形式存放,服务器经由本机的文件系统直接访问,或经由网络从字体服务器访问。客户端可向服务器请求有效的字体列表,且可请求服务器加载(没有的话)或卸载(客户端不再需要的话)字体。客户端可请求关于字体的信息(例如,ascent 字体),并以指定的字体来绘制指定的字符串。
在X Window内核协议的层次上,字体的名称可以是任意的字符串。X 逻辑字体描述协议[6]规范了如何根据字体的属性来命名。这些协议也规范了可附属于字体的选用属性之值(value)。
xlsfonts 程序可输出存放在服务器上的字体列表。xfontsel 程序可显示字体的标记,并让用户选取字体的名称,以在其它窗口中粘贴。
已不再重视服务器侧字体的使用,而转向客户端侧字体的使用。例如,借由支持Xft或cairo程序库,以及XRender扩充,改由客户端(而非服务器)绘制字体。客户端侧字体在内核协议中尚未给出规范。
资源和识别子编辑本段回目录
所有关于窗口的数据、图形映射、字体等等,皆存放在服务器上。客户端知道那些对象的识别子,和服务器交互时,对象以整数为名称。例如,当客户端希望建立一个窗口时,便指定一个识别子,并请求服务器建立一个窗口。服务器会建立一个窗口,并与指定的识别子关系。稍后客户端可使用这个识别子进行请求,例如在窗口上画上一个字符串。以下存在于服务器上的对象,客户端可借由数值型的识别子得知:
窗口(Window)
图形映射(Pixmap)
字体(Font)
色彩映射(Colormap)(即颜色表,稍后描述)
图形上下文(Graphic context)
这些对象就称作资源。当客户端请求建立某一种资源时,同时也为资源指定了一个识别子。例如,为了建立一个新窗口,客户端指定了窗口的属性值(亲属关系、宽、高等等)和识别子,最后识别子会和窗口关系。
识别子是三个最高有效位为0的32位整数。每一个客户端都有一组自己的识别子,其可用来建立新的资源。这组识别子是由服务器以包含在接受分组(传送给客户端的分组,通知已接受连接)中的两个整数所指定的。客户端以避免冲突的方式选取识别子:在窗口、图形映射、字体、色彩映射、图形上下文之中的两个对象,不可具有相同的识别子。
资源一经建立,其识别子就用于客户端向服务器请求与之有关的运算。部分运算会影响特定的资源(例如,请求移动窗口),其它的则要求存放在服务器上的资源数据(例如,请求窗口的属性值)。
识别子在服务器上是独一无二的,在多个客户端之间也不例外。例如,即使是由两个不同客户端所建立的窗口,也不会同时具有相同的识别子。即使某个对象不是由自己的客户端所建立的,只要指定相对应的识别子,就可访问另一个客户端所建立的任何对象。
连接到同一服务器的两个客户端,对同一资源可使用同一识别子。例如,若客户端建立一个 0x1e00021 识别子的窗口,并传送数值 0x1e00021 给其它的应用程序(通过任何有效的手法。例如,把数值存放在文件里,且这个文件可让其它的应用程序轻易访问),其它的应用程序即可对同一窗口进行操作。这个例子是来自X Window版本的Ghostview:程序建立一个子窗口,在环境变量中存放其识别子,并调用Ghostscript;程序绘制PostScript文件的内容,以显示在这个窗口上。
当建立资源的客户端关闭与服务器的连接时,资源就会正常的销毁。不过在关闭连接之前,客户端可以请求服务器不要销毁资源。
事件编辑本段回目录
事件是由服务器传送到客户端用以通信的分组,传送一些客户端可能感兴趣的事情。例如,当用户按下按键或点击鼠标时,便会传送一个事件。事件不只用于输入:例如,传送的事件表明特定窗口建立了新的子窗口。
每一个事件都会涉及到窗口。例如,当用户的鼠标在窗口之内并点击时,这个事件就会涉及到那个窗口。事件分组中含有那个窗口的识别子。
客户端可以请求服务器传送事件给另一个客户端,这可用于客户端之间的通信。例如,当客户端请求目前所选取的文本时,就会传送事件给客户端,以处理目前所持有的选取内容。
当再度观看内容已被销毁的区域时,有可能会传送 Expose(显露)事件。而且在某些情况下,窗口的内容可能会被销毁。例如,当窗口被其它窗口遮盖住,且服务器没有维护后备存放区时。此时服务器会产生一个 Expose 事件,以通知客户端重绘窗口已消失的部分。
大部分的事件只会在客户端预先表示关心时才会传送。因为客户端可能只需要关心某类型的事件。例如,客户端可能会关心关于键盘的事件,但却不关心关于鼠标的事件。即使在客户端并未明确请求的情况下,某几类事件也会不断的传送给客户端。
客户端可以设置窗口的属性值(attribute),以指明想要接收哪些事件。例如,当窗口的内容已销毁时,为重绘其内容,客户端就必须接收 Expose 事件,以通知窗口需要再次重绘。客户端要能接收到 Expose 事件,就要预先指明它所关心的事件,这部分可以适当设置窗口属性值的事件掩码来完成。
不同的客户端可以请求同一窗口的事件。甚至可对同一窗口设置不同的事件掩码。例如,某个客户端可以只对窗口请求键盘事件,而另一个客户端只对窗口请求鼠标事件。这是可以的,因为服务器会为每一个客户端维护事件掩码,而且是每一个窗口都维护一份独立的事件掩码。不过偶尔也有某几类事件,只能由一个客户端选择。特别是,这类事件回报鼠标按钮的点击,且部分变化会涉及到窗口管理器。
xev 程序显示窗口所涉及到的事件。xev -id WID 可对识别子为 WID 的窗口要求所有可能的事件,并将其输出。
示例编辑本段回目录
以下是服务器和程序之间的交互示例,这个程序会建立一个黑框窗口,按下按键后退出。在本例中,服务器并未传送任何回应,因为客户端的请求并不产生回应。这些请求有可能产生错误。
客户端开通与服务器的连接,并传送初始化分组,来指定所要使用的位顺序。
服务器接受连接(本例中不涉及验证)并传送适当的分组。这些分组含有其它的信息,如根窗口的识别子(例如,0x0000002b),以及客户端可建立哪些识别子。
客户端请求以 0x00200000 识别子建立一个默认的图形上下文(此一请求如同本例中的其余请求,并不会产生来自服务器的回应)。
客户端请求服务器以 0x00200001 识别子、大小 200x200、位置 (10,10) 等等,来建立一个顶层窗口(这部分指定以根窗口 0x0000002b 为父窗口)。
客户端请求改变窗口 0x00200001 的属性值(attribute),规定窗口要接收 Expose(显露)和 KeyPress(按键)事件。
客户端请求映射(显示在屏幕上)窗口 0x00200001。
当窗口可见,且必须绘出其内容时,服务器传给客户端一个 Expose(显露)事件。
客户端对事件做出反应,请求绘制一个方框,它是通过传送与窗口 0x00200001 和图形上下文 0x00200000 一起的 PolyFillRectangle 请求来达成。
如果窗口被其它窗口遮盖住,且再次显露出来时,又刚好没有这部分的备存时:
服务器传送另一种 Expose(显露)事件,告知客户端必须再次重绘窗口。
客户端通过传送 PolyFillRectangle 请求的方式重绘窗口。
如果按下按键:
服务器传给客户端一个 KeyPress(按键)事件,通知它用户按下按键了。
客户端作出适当的反应(本例是退出程序)。
颜色编辑本段回目录
在协议层次里,颜色使用32位无负号整数来表示,称为像素值。以下因素会影响颜色的显示:
色彩深度
色彩映射(colormap),即含有红、绿、蓝强度值的表。
视觉类型(visual type),指明表如何用来表示颜色。
在最简单的情况下,色彩映射是一种每一项都含有RGB三值的表。像素值 x 所表示的颜色,就包含在表中第 x 项。如果客户端可以改变色彩映射的内容,那这种表示法就以伪彩色(PseudoColor)视觉分类(visual class)来标识。视觉分类中的静态色(StaticColor)与伪彩色类似,只不过客户端不能修改色彩映射的内容。
在此总计有六种可能的视觉分类,每一种都使用不同的方式来表示 RGB 像素值。PseudoColor 和 StaticColor 即其中两种。GrayScale 和 StaticGray 是另外的两种,其中最主要的差异是灰阶渐层的使用。
剩下的两种视觉分类和前述的差异在于,将像素值分成三个部分,并为红、绿、蓝的强度使用了三个独立的表。并根据这个表来表示颜色,如下流程将像素值转换成RGB色:
将像素值视为连续的位序列
将位序列分成三个部分
将每一个部分视为整数、并作为索引,以在这三个独立的表中查找到值。
这个机制需要以三个独立的表组成色彩映射,三个原色各一个表。转换以后仍是强度值的三联色。使用这个表示法的视觉分类有 DirectColor 和 TrueColor,其中的差异是客户端不能修改后者的色彩映射。
以上六种以像素值表示颜色的机制,都需要附加一些参数来运作。这些参数可统合为视觉类型,其包含一个视觉分类和其它参数以表示颜色。每一个服务器都有一组固定的视觉类型,每一个类型都与数值型的识别子关系。其识别子是32位无负号整数,和资源或元素的识别子没有什么不同。
当接受来自客户端的连接时,服务器所传送的接受分组里含有区块序列,每一个区块都包含有关于某一个单一屏幕的信息。就每一个屏幕而言,相关区块包含着其它区块的清单,每一个都涉及了屏幕所支持的色彩深度。就每一个屏幕所支缓的色度深度而言,这个清单中又包含视觉类型的清单。结果,每一个屏幕就关系著各种合适的色彩深度,而且每一个屏幕的每一个色彩深度都关系著各种合适的视觉类型。一个给定的视觉类型可用于更多的屏幕和各种不同的色彩深度。
就每一个视觉类型而言,接受分组中还包含它的识别子和它所包含的实际参数(视觉分类等),客户端存放这些信息,因为之后就不能再请求。此外,客户端不能改变或建立新的视觉类型。建立新窗口的请求中,还包含有色彩深度和视觉类型的识别子,以此用来表示窗口的颜色。
色彩映射和控制屏幕的硬件(即绘图卡)是否使用调色板(palette)没有关系。调色板是一个表,也是用来表示颜色的。即使硬件并不使用调色板,服务器也能使用色彩映射。当硬件使用调色板时,就只能安装相当受限的少许色彩映射。更精确的说,当硬件根据色彩映射来显示颜色时,就可以说是安装了色彩映射。客户端可请求服务器安装一个色彩映射。不过需要将另一个色彩映射解除安装:其影响是窗口如果使用了解除安装的色彩映射,就不能显示正确的颜色,而出现怪异颜色的画面。这个问题可以用标准色彩映射来解决,标准色彩映射是像素值和颜色之间关系恒定的色彩映射。由于有这一性质,就可让不同的应用程序使用标准色彩映射。
色彩映射的建立由ICCCM协议管理。标准色彩映射由ICCCM和Xlib规格管理。
元素编辑本段回目录
元素(Atoms)是用来表示字符串的32位整数。本协议的设计者之所以引入元素,是为了以简短、大小恒定的方式表示字符串[9]:由于字符串可以是任意的长度,而元素总是32位整数。某些分组可能会多次传送相同的字符串,元素的简短性可以削减指令所使用的分组长度,进而增进网络的使用效率。大小恒定的元素有利于大小恒为32位组的事件:大小恒定的分组可以包含元素,却不能包含过长的字符串。
更严谨的说,元素是存放在服务器上的字符串的识别子,相当于资源的识别子(窗口、图形映射等),但仍有两个不同点。首先,元素的识别子是由服务器选择的,而非客户端。换句话说,当客户端请求建立一个新的元素时,其仅仅传送字符串给服务器存放,而非字符串的识别子;元素的识别子是由服务器选择,并回传给客户端作为回应。其次,资源和元素之间的重大差异是,元素并未与客户端相连系。元素一经建立,就能一直存留至服务器退出或重置(此非资源的默认行为)。
元素是识别子,所以也是独一无二的。不过元素和资源的识别子可以相一致。与元素关系的字符串称作元素名。元素的名称一经建立就不能再更改,而且不能有两个相同名称的元素。故一般以元素的内容作为元素的名称:“元素 ABCD”意谓著,或更精确的说,“这个元素关系的字符串是 ABCD”或“元素的名称是 ABCD”。客户端可以请求建立新的元素,且可请求指定字符串的元素(识别子)。某些元素已预先定义(由服务器以特定的识别子和字符串所建立)。
元素运用于多个目的,主要与连接到同一服务器的不同客户端之间有关。特别是,元素用于与窗口属性的关系,以下详述。
所有存在于服务器上的元素列表,可使用 xlsatoms 程序输出。尤其这个程序可以元素的名称(元素所关系的字符串)列出每一个元素(识别子是一串数字)。
属性编辑本段回目录
每一个窗口都有一组预先定义的属性值(Attribute)和属性(Property),并存放在服务器上,客户端可以适当的请求方式取存。属性值是有关窗口方面的数据,如窗口的大小、位置、背景色等等。属性则是附属于窗口上的数据片断。与属性值相反,属性在X Window内核协议的层次中并没有其它含意。客户端可在窗口的属性中存放属性值数据。
属性是以名称、类型和值来描述,属性相当于指令式编程语言的变量,应用程序可以指定名称、类型和值来建立新的属性。属性和窗口关系:两个相同名称的属性可存在于两个不同的窗口,且可具有不同的类型和值。
属性的名称、类型和值皆为字符串;更精确的说就是元素,客户端可借由识别字,以访问存放在服务器上的字符串。客户端应用程序可以元素的识别子(含有属性的名称)访问特定的属性。
属性大多用于客户端之间的通信。例如,名称为 WM_NAME(属性名称就是元素所关系的字符串 "WM_NAME")的属性,是用来存放窗口的名称;窗口管理器通常会读取这个属性,并在窗口顶部显示名称。
某些客户端之间的通信也使用了根窗口的属性。例如,根据freedesktop窗口管理器规格[10]窗口管理器应该在根窗口的 _NET_ACTIVE_WINDOW 属性名中存放目前有效(active)窗口的识别子。X资源所含有的程序参数,同样也是存放在根窗口的属性里;借由这个方式,即使分别运行在不同计算机上,所有客户端仍可访问到那些参数。
xprop 程序可输出指定窗口的属性,xprop -root 则可输出根窗口每一个属性的名称、类型和值。