X Window 程式设计

X Window 程式设计入门
http://cnpa.yzu.edu.tw/~thinker 作者:李圭烽 (Thinker; Thinker.bbs@bbs.yzu.edu.tw) (2001-06-01 17:04:00)

--------------------------------------------------------------------------------
这只是入门,仅仅是入门而已。所以并不是很完全,却可以给你一个概观的了解,让你知道 X Window programming 过程和所需的基本知识。如果你需要更进一步的资料,请参考 Xlib - C Language X Interace Referrence。对於内容有任何意见,也欢迎指教。本文件由李圭烽(Thinker; Thinker.bbs@bbs.yzu.edu.tw) 所作。作者许可本文件於网路上自由流传,但保留其它之着作权力。
 
版权宣告 LICENSE.txt 
第一章 什麽是 X Window  
第二章 X Programming 的第一步  
第三章 绘图(Graphic) 
第四章 Event 
第五章 Window 
第六章 Inter-Client Communication 
第七章 Resource Manage 
第八章 输入装置 
第九辛 Window & Session Manager 
第十章 Take it all together!! 
未完成 尚在努力......... 



--------------------------------------------------------------------------------

相关 link
Kenton Lee:X Window System Technical Glossary (http://www.rahul.net/kenton/xglossary.html) 
The FVWM Homepage  (http://www3.hmc.edu/~tkelly/docs/proj/fvwm.html)
X man pages  (http://www.x.org/consortium/R6doc/man/X11/)
VXP: Visual X Programming Interface  (http://www.shsu.edu/~stdyxc05/VXP/)

--------------------------------------------------------------------------------

版权宣告 LICENSE.txt 

本文为文章着作权宣告

凡附本文之文章, 皆需完全遵守本文之规定.
以下所有参照到附本文之文章时, 皆称以"原文章"三字代替.

1. 原文章准予任何人, 进行任何形式的修改.
2. 原文章准予任何人, 以电子形式传送.
3. 修改原文章所得的文章称为延伸版.
4. 所有延伸版皆需附本文.
5. 最初之原文章, 即非经由修改或增加其它文章内容,为独立创作而
   得之文章.
6. 最初之原文章的原子形式, 包含纸面出版权为着作人所有.
7. 个人或团体, 经由单独修改或增加原文章内容超过 10% 得一延伸版,则
   该个人或团体得拥有该延伸版的原子形式之商业出版权.
8. 个人或团体, 经由单独修改或增加原文章内容, 但修改或增加部分未超
   过 10% 而得一延伸版, 则个人或团体不具有该延伸版的原子形式之商业
   出版权.
9. 非符合 7 和 8 两款情形的商业行为, 皆不被允许.
X Client 和 X Server 
X Window 为 一 Client-Server 架 构 之 GUI 。 Client 指 的 是 在 X Window 下 执 行 的 应 用 程 式, 需 要 X Server 为 其 服 务 ; Server 指 的是 整 个 管 理 你 的 桌 面 的 X Window 系 统 , 称 为 X Server 。 X Server 除 了 负 责 GUI 介 面 之 管 理 和 提 供 Client 端 的 服 务 之 外 , 并 且 还 管 理 系 统 资 源 ( Resource ) 和 事 件 ( Event ) 之 发 生 和 传 递 。 
Display 和 Screen
  在 一 个 X Wintow System  可 以 包 含 多 个 Screen , 而 Screen 则 是 一 个实 际 的 Monitor 或 是 Device 。 每 个 Display 则 可 以 包 含 多 个 Screen 。 Display 指 的 是 一 群 Screen 和 一 个 pointer ( 一 般 是 滑 鼠 )加 上一 个 键 盘 的 集 合 。  

Event 和 Request
  当 Client 需 要 在 显 示 幕 上 开 一 个 window 或 是 显 示 一 段 文 字 或图 形 时 , 则 Client ( 即 X 下 的 应 用 程 式 ) 向 Server 提 出 一 个 Request ; Server 收 到 Request 之 後 , 则 依 据 Request 的 内 容 , 提 供 相 对 应 的 服 务 。当 使 用 者 在 属 於 某 一 Client 的 window 内 , 按 了 一 下 滑 鼠 的 右 键 , 或 是 敲 了 键 盘 上 的 一 个 Key , 则 Server 会 发 送 一 个 Event 给 该 Client 。 而 Client 只 要 依 据 Server 送 回 来 的 Event, 即可 判 断 有 何 事 件 发 生 , 然 後 依 据 所 发 生 的 Event 发 生 相 对 应的 动 作 。  

X Window 的 网 路 特 性 
  Client 和 Server 不 一 定 要 在 同 一 部 电 脑 上 执 行 ; Client 和 Server 可 以 是 接 在 网 路 上 的 两 台 电 脑 。 当 Client 要 向 Server 发 出 Request 时 , 可 以 透 过 网 路 向 执 行 Server 的 机 器 发 出 Request , 然 後 送 达 给 Server 处 理 。 同 样 的 , 当 Server 要 向 Client 通 知 Event 的 发 生 时 , 也 可 以 透 过 网 路 向 Client 传 送 。 因 此 你 可 以在 A 地 的 电 脑 上 执 行 某 一 X Window 应 用 程 式 , 而 在 B 地 透 过 X Server 观 察 程 式 执 行 的 结 果 。  

一 个 X Window 的 程 式 , 也 就 是 Client , 在 同 一 时 间 可 以 和 一 个 以 上 的 Server 沟 通 。 也 可 以 同 时 和 多 个 Server 建 立 连 接 , 在 多个 Server 上 显 示 , 并 接 受 多 个 Server 的 讯 息 。 而 X Server 也 可 以在 同 时 间 内 , 接 受 多 个 Client 送 来 的 讯 息 , 并 做 对 应 的 处 理。 

Window 的 阶 层 性 
X Window 内 的 window 是 有 阶 层 关  的 , 这 阶 层 关  可 以 画 成 一树 状 图 。 在 树 状 的 最 上 层 是 root window , 这 个 window 是 Server 所特 有 的 。 除 了 root window 之 外 , 每 个 window 都 有 一 个 parent window , 即 是 在 树 状 图 上 的 上 一 层 window 。 在 树 状 图 的 下 一 层 window 称 为 child window 。 每 个 window 可 以有 多 个 child , 但 不 一 定 每 个 window 都 有 child 。 而 child window 下面 还 可 以 有 child 。   Win 1 和 Win 2 皆 为 root 的 child, root 则 为 Win 1 和 Win 2 的 parent 。 Win 2-1 和 Win 2-2 和 Win 2-3 则 为 Win 2 的 child , Win 2 则 为 Win 2-1 和 Win 2-2 和 Win 2-3 的 parent。而 root 和 Win 1 和 Win 2 则 皆 为 Win 2-1 和 Win 2-2 和 Win 2-3 的 ancestor。而 root 则 为 Win 1 和 Win 2 的 ancestor。  

在 显 示 幕 上 , child 永 远 在 parent 上 面 ( 即 parent 被 child 盖 住 ) 。 而 child window 可 以 比 parent window 大 , 但 超 过 parent 的 部 份 , 会 被 截 掉 而 不 显 示 在 显 示 幕 上 。 
 蓝 色 部 分 为 parent window , 而 草 绿 色 部 分 则 为 child window 和 parent window 重 叠 的 部 分 。 在 parent window 外 面 灰 色 部 分 也 为 child window 的 一 部 分 , 但 其 为 超 过 parent window 视 窗  围 之 外 的 部 分 , 所 以 不 会 被 显 示 出 来 。 草 绿 色 部 分 则 是 显 示 child window 的 内 容 。  


当 显 示 幕 上 有 多 个 window 存 在 时 , 可 能 会 发 生 window 重 叠 的 现象 。 而 在 下 面 的 window 会 被 在 上 面 的 window 盖 掉 ; 当 上 面 的 window 移 离 目 前 位 子 , 而 使 原 本 被 盖 的 部 分 重 新 暴 露 出 来 , 这 时 就 必 需 重 画 露 出 来 的 部 分 。 但 X 并 不 保 证 会 保 存 你 原 本显 示 在 window 上 的 资 料 。 因 此 这 时 Client 就 必 需 负 起 重 画 的 责 任 了 。 当 X Server 没 保 存 重 新 被 暴 露 出 来 的 部 份 的 资 料 时 , X Server 会 送 出 Expose Event 给 Client, 通 知 Client 那 些 部 分 需 要 重 画。 然 後 Client 就 必 需 决 定 是 否 要 重 画 , 或 是 采 取 其 它 的 处 理 方 式 。 

X Window 的 外 观 
在 很 多 的 GUI 中 , 如 OS/2 等 , 都 会 提 供 Button, Scroll Bar, Menu 等 等 的 基 本 元 件 , 以 方 便 建 构 程 式 的 外 观 。 但 在 X 中 并 不 提供 这 样 的 元 件 ; 相 反 的 , X Window 提 供 的 是 一 套 建 立 外 观 风 格 的 机 制 。 利 用 这 套 机 制 , 我 们 可以 建 立 各 种 不 同 风 格 的 元 件 。 这 样 我 们 即 不 必 限 制 在 系 统 内定 的 风 格 中 , 而 无 法 做 改 变 。 这 套 机 制 是 用 大 小 不 同 的 window , 组 合 建 立 如 Button , Scroll Bar 等 等 的 元 件 , 充 满 了 弹 性 。 
Window Manager 
在 X 中 有 一 种 极 为 特 殊 的 程 式 , 称 为 Window Manager 。 其 作 用 是管 理 Desktop 上 的 各 应 用 程 式 的 window , 并 提 供 特 殊 风 格 的 window 外 观 。 不 同 的 Window Manager 有 不 同 的 外 观 风 格 。 因 此 ,我 们 可 以 更 换 不 同 的 Window Manager , 以 得 到 不 同 风 格 的 window 外 观 。 而 一 般 的 Client 则 必 需 和 Window Manager 合 作 , 以 得 到 良好 而 一 致 风 格 的 window。 Window Manager 也 是 一 Client 程 式 , 和 一 般 的 Client 相 同 。 只 是 Window Manager 负 有 特 别 的 任 务 ; 管理 Desktop 和 window 外 观 。 
为 了 得 到 良 好 而 风 格 一 致 的 window, Client 和 Window Manager 必 需合 作 。 其 合 作 方 式 是 透 过 由 Client 传 送 Hint 给 Window Manager , 而 Window Manager 以 後 , 则 会 参 考 Hint 的 内 容 , 给 予 应 用 程 式 所开 出 的 window 适 当 的 外 观 。 但 Window Manager 不 一 定 会 依 据 Hint 指 示 处 理 , 纯 为 参 考 。 

X Window 程式设计入门--第二章 X Programming 的第一步
http://cnpa.yzu.edu.tw/~thinker 作者:李圭烽 (Thinker; Thinker.bbs@bbs.yzu.edu.tw) (2001-06-01 19:00:01)
Index:
基本步骤 
建立一个 display至 X Server 
取得 display的相关资料 
建立视窗 
和视窗管理程式(Window Manager)沟通 
显示视窗 
关闭(destroy)视窗 
关闭 display 
例 

--------------------------------------------------------------------------------

一个 X 的程式的几个基本步骤: 
main() {
建立一个 display 至 X Server;
取得 display 的相关资料;
设定视窗(window)特性(Attributes);
建立视窗(window);
和视窗管理程式(window manager)进行沟通;
显示(map)视窗;
......  ......
... 程式处理 ...
.............
关闭(destroy)视窗;
关闭 display;
}

1. 建立一个 display 至 X Server
在程式开始向 X Server 进行任何的动作之前,程式必需先和 X Server 之间建立一个连线(connection),我们称之为 display。 XOpenDisplay 即为 Xlib 提供给我建立 display 的函数。 
--------------------------------------------------------------------------------

Display *XOpenDisplay(display_name)
char *display_name;

display_name 指定要连接之 server。如果 display_name 设定为
NULL,则内定使用环境变数(environment variable)
DISPLAY 的内容为连接对像。


--------------------------------------------------------------------------------
呼叫 XOpenDisplay 之後,会传回一个 Display 结构。 Display 结构 存放着一些关於 display 的资讯。 虽然我们可以直接存取 Display 结构,但我们不该迳自改变其内容。 Xlib 有提供一系列的函数和巨集 (macro),我应该透过这些函数和巨集(macro)存取 Display 的内容。 以维持 Xlib 的正常运作。
display_name 或是 DISPLAY 环境变数(environment variable)的格式 如下: 


--------------------------------------------------------------------------------

hostname:number.screen_number

hostname
设定 display 所在主机(host)之名称,在主机名称之後紧接着的是单
个冒号(:)或是双冒号(::)。
number 指定主机上,display server 的编号。一台主机(host)上可能同时存
有多个 server,每个 server 都会给与一个编号,这个编号从零开始
。在 server 的编号後面有一个句点(.),这个依你是否设定後面的
screen_number 而决定是否该加。
screen_number
每一个 server 可能同时管理着多个显示幕,而每一个显示幕我们也给
与一个从零开始的编号。screen_number 也就是设定着这个编号,做为
default 的 screen。当你使用 DefaultScreen 巨集或是 
XDefaultScreen 函数时,即会存取到这个值。


--------------------------------------------------------------------------------
举例: 
Display *display;

display = XOpenDisplay("cnpa.yzu.edu.tw:0");

建立与 cnpa.yzu.edu.tw 上第零个 server 的 display。 
2. 取得 display 的相关资料
在我们建立视窗之前,我们必需对目标 display 和萤幕(screen)的属 性状况有所了解。 我们可以透过 Xlib 所提供的巨集(macro)和函数 (function)取得指定 display 和萤幕(screen)的资料,利用这些资料 以设定视窗参数,以应不同的萤幕建构合适的视窗。

建立视窗,我们必需设定多种和目标萤幕(screen)相关的参数。我们就 这些参数设定的需要,介绍如何利用 Xlib 来取得关於 display 的资 料。


--------------------------------------------------------------------------------

DefaultRootWindow(display)

Window XDefaultRootWindow(display)
Display *display;

display 指定至 X server 的连结(connection),即
XOpenDisplay 所传回之结构。


--------------------------------------------------------------------------------
传回预定萤幕(default screen)的根(root)视窗。每一个视窗都有父视 窗(parent window),当你要在程式开启一个最上层的视窗(top window);不是程式其它视窗的子视窗。那麽,由於己没有其它更上层 的视窗可以当父视窗,所以必需设根视窗(root window)为该视窗的父视窗 (parent window)。我们使用 DefaultRootWindow 取得预定萤幕的根视 窗(root window),可以在建立新视窗时,以任一根视窗(root window) 做为新视窗的父视窗(parent window)。透过指定父视窗(root window) ,我们也指定了负责显示新视窗的萤幕(screen)。


--------------------------------------------------------------------------------

DefaultDepth(display, screen_number)

int XDefaultDepth(display, screen_number)
Display *display;
int screen_number;

display 指定连至 X Server 的连接(connection)。
screen_number 指定萤幕的编号。


--------------------------------------------------------------------------------
传回指定萤幕根视窗(root window)的预定深度(depth)。每个视窗都有 自己的深度(depth),深度影着该视窗所能显示的颜色数。当一个视窗 的的深度大,则其同时能显示的颜色总数也会随之增加。但,深度( depth)并非无限量的增加,会受限於硬的限制。一般我们会参考根视 窗(root window)的设定。


--------------------------------------------------------------------------------

DefaultScreenOfDisplay(display)

Screen *XDefaultScreenOfDisplay(display)
Display *display;

display 指定一个 X Server 的连结(connection)


--------------------------------------------------------------------------------
传回指向预定萤幕(default screen)的指标。预定萤幕(default screen)即建立 display 时,在 display name 指定的 screen number。


--------------------------------------------------------------------------------

DefaultVisualOfScreen(screen)

Visual *XDefaultVisualOfScreen(screen)
Screen *screen;

screen 指定适当的 Screen 结构。


--------------------------------------------------------------------------------
传回预定萤幕(default screen)的预定视觉(default visual)。在某些 显示设备上,允许同时以多种不同的方式理颜色的显示。我们可以任意 的方式,将深度(depth)为 8-bits 的图素(pixel)对映到显示的颜色。 也可以将深度(depth)为 24-bits 的图素(pixel),以红、黄、蓝各为 8-bits 的方式对映到实际的颜色。图素(pixel)指的是画面上的一个点 ,这指的是代表该点颜色的一个编号。例如,我们可能以 7 做为RGB 值为 0x9f7071 的颜色的编码,则任何图素(pixel)为 7 的点,其颜色 则为 RGB 0x7f7071。

3. 建立视窗
我现在开始建立新视窗(window)。视窗(window)建立之後,并不会马上 在我们指定的显示器(screen)上显示出来。我们要经过一道 map 的手 序後,视窗(window)才会正式在显示器上显示出来。在我们建立视窗( window)之後,在 map 之前,我们可以对新视窗(window)做一些设定的 动作,以设定视窗(window)的行为特性。

建立新视窗(window)要透过 Xlib 所提供的 XCreateWindow 函数或者 XCreateSimpleWindow 函数,XCreateSimpleWindow 是 XCreateWindow 的简化版。这两个函数可用来建立新的子视窗。 
--------------------------------------------------------------------------------

Window XCreateWindow(display, parent, x, y, width, height,
border_width, depth, class, visual, valuemask,
attributes)

Display *display;
Window parent;
int x, y;
unsigned int width, height;
unsigned int border_width;
int depth;
unsigned int class;
Visual *visual;
unigned long valuemask;
XSetWindowAttributes *attributes;

display 指定到 X Server 的连结。
parent 指定父视窗(parent window)。
x, y 指定视窗边框(border)的左上角相对於父视窗(parent
window的座标。也就是以父视窗(parent window)内部
的左上角做为原点所求得的相对座标。此座标用来指
定视窗的显示位子。
width, height 视窗内部尺寸的宽度和高度,高度和宽度并不包括边框
(border)的部分。这些尺寸不能为度,否则会造成
BadValue 的错误结果。
border_width 设定视窗边框(border)的宽度,其单位为图素(pixels)
,也就是指定其边框的宽度是几个图素(pixels)。
depth 设定新视窗的颜色深度(depth),若指定 depth 的值为
CopyFromParent,则深度(depth)将会从父视窗(parent
window)取得。
class 指定视窗的类别(class)。你可以指定为 InputOutput
,InputOnly 或 CopyFromParent 其中一种。若指定为
CopyFromParent 则表示将由父视窗(parent window)取
得。
visual 设定视觉(visual)的种类。设为 CopyFromParent 则会
取自父视窗。
valuemask 用以设定在 attributes 参数设定了那些视窗属性
(attribut)的遮罩(mask)。在这个遮罩(mask),每一
bit 代表着一项属性(attribut),我们以 OR 位元运算
,将代表各项属性的遮罩(mask)组合起来。若为零,则
会乎略 attributes 参数。
attributes 这是一个存放视窗属性(attribut)的结构(structure)
,配合设定正确的遮罩(mask),用以设定视窗的属性。


--------------------------------------------------------------------------------

/* Values */

typedef struct {
Pixmap background_pixmap;
unsigned long background_pixel;
Pixmap border_pixmap;
unsigned long border_pixel;
int bit_gravity;
int win_gravity;
int backing__store;
unsigned long backing_planes;
unsigned long backing_pixel;
Bool save_under;
long event_mask;
long do_not_propagate_mask;
Bool override_redirect;
long event_mask;
long do_not_propagate_mask;
Bool override_redirect;
Colormap colormap;
Cursor cursor;
} XSetWindowAttributes;

/* Window attribute value mask bits */

#define CWBackPixmap (1L<<0)
#define CWBackPixel (1L<<1)
#define CWBorderPixmap (1L<<2)
#define CWBorderPixel (1L<<3)
#define CWBitGravity (1L<<4)
#define CWWinGravity (1L<<5)
#define CWBackingStore (1L<<6)
#define CWBackingPlanes (1L<<7)
#define CWBackingPixel (1L<<8)
#define CWOverrideRedirect (1L<<9)
#define CWSaveUnder (1L<<10)
#define CWEventMask (1L<<11)
#define CWDontPropagate (1L<<12)
#define CWColormap (1L<<13)
#define CWCursor (1L<<14)


--------------------------------------------------------------------------------
使用 XCreateWindow 可以建立任一视窗的子视窗(child window)。但在 程式一开始时,还没有建立任何的视窗,因此也就无法建立任何视窗的 子视窗。我们使用根视窗(root window)做为父视窗(parent window)建 立其子视窗(child window),以根视窗(root window)的子视窗(child window)做为我们程式最上阶层的视窗。

每个都有各种的属性,我们设定其属性即会改变视窗表现出来的行为。 例如,深度(depth),边框(border)的宽度等等的。Xlib 提供 XChangeWindowAttributes 这个函数,设定任一 window 大部分的 Attributes(属性)。 
--------------------------------------------------------------------------------

XChangeWindowAttributes(display, w, valuemask, attributes)
Display *display;
Window w;
unsigned long valuemask;
XSetWindowAttributes *attributes;

w 指定设定的 window。
valuemask 指定在 attributes 这个参数,设定了那些 window
attributes。这个 mask 也是用 OR 运算,将所有的属性的 mask
(遮罩)值组合起来。
attributes 指定储存属性设定值的 XSetWindowAttributes 结构。


--------------------------------------------------------------------------------

4. 和视窗管理程式(Window Manager)沟通
在 X Window 环境下的程式,由於其视窗外观并不是直接由 X Server 处理,代而之的是交由 Window Manager 处理。因此,我们的程式,必 需和 Window Manager 沟通合作,才能得到合适的视窗外观并和其它视 窗和平共处。Window Manager 只会处理 top window,其它的非 top level 的 window 并不在其处理的围。
和 Window Manager 沟通的方式,是透过传送 Hint 的方式。因为 Window Manager 对 client 对其视窗的设定,并不一定要完全接受, 只是做为参考而已,所以我们称之为 Hint。除了这些 Hint 之外, 我们还可以透过 Window Manager ,操作 top level 的视窗,使之 缩成 icon ,设定视窗 title 的名称等等的。



--------------------------------------------------------------------------------

XStoreName(display, w, window_name)
Display *display;
Window w;
char *window_name;

window_name 指定视窗名称,此名称会被显示在 title 上。


--------------------------------------------------------------------------------
此函数用来设定视窗的名称,这个名称将会被显示在该视窗的 title 处。title 就像文章的标题一样,用来指明此一视窗,让使用者可以 依据 title 辨别不同的视窗。


--------------------------------------------------------------------------------

XSetIconName(display, w, icon_name)
Display *display;
Window w;
char *icon_name;

icon_name 指定 icon 的名称,此名称在视窗缩成 icon 时显示
出来。


--------------------------------------------------------------------------------
XSetIconName 用来设定 icon 的名称。有时侯,当我们在萤幕上同时 开太多个视窗时,整个画面可能会显的很杂乱。因此,我们会借由把一 些暂时用不到的视窗缩小成一小图示,也就是 icon,以减少所占的空 间,清理一下桌面。等到要用到该视窗时,才将之放大回原来的大小。 而 XSetIconName 所设定的名称,则会在视窗变成 icon 时显示出来。 


--------------------------------------------------------------------------------

void XSetWMNormalHints(display, w, hints)
Display *display;
Window w;
XSizeHints *hints;

hints 指定视窗在一般状况下的 size hints。


--------------------------------------------------------------------------------

#define USPosition (1L << 0)
#define USSize (1L << 1)
#define PPosition (1L << 2)
#define PSize (1L << 3)
#define PMinSize (1L << 4)
#define PMaxSize (1L << 5)
#define PResizeInc (1L << 6)
#define PAspect (1L << 7)
#define PBaseSize (1L << 8)
#define PWinGravity (1L << 9)
#define PAllHints

typedef struct {
long flags;
int x, y;
int width, height;
int min_width, min_height;
int max_width, max_height;
int width_inc, height_inc;
struct {
int x;
int y;
} min_aspect, max_aspect;
int base_width, base_height;
int win_gravity;
} XSizeHints;


--------------------------------------------------------------------------------

XSizeHints *XAllocSizeHints()


--------------------------------------------------------------------------------

XFree(data)
void *data;

data 要释放掉的记忆。


--------------------------------------------------------------------------------
XSetWMNormalHints 用以设定有关视窗大小的和缩放的限制等等的。 由於 XSizeHints 的内容以後可能会有所增长,所以必需透动态记忆 的配,以避免以後因为改版之後,而造成和新版的 Xlib 不和的 情形。Xlib 提供 XAllocSizeHints 配置一个 XSizeHints 的 structure。所有将由 Xlib 所提供的函数所配的记忆,都要使用 XFree 释放。

5. 显示视窗
视窗建好之後, 仍然不会出现在萤幕上, 而是要经过一道 mapping 的手序。 视窗都已经设定好了,再来正式显示在显示器上就很容易了。这个步骤,mapping ,只要呼叫一个函数就 ok 了!! 
--------------------------------------------------------------------------------

XMapWindow(display, w)
Display *display;
Window w;

w 要显示的视窗。


--------------------------------------------------------------------------------

XFlush(display)
Display *display;


--------------------------------------------------------------------------------
嗯!! 就这麽简单。但,当你程式执行到这一个步骤时,也许你会发现, 显示器上跟本就没有视窗出现。这是因为 Xlib 设有 buffer,将所有 要传送的讯息都先存在一个 buffer 内,待 buffer 满了之後才会将之 一起送出,以减少网路的流量,加过程式执行的速度。然而,我们无法 知道什麽时侯才会满,我们总不能一直等下去,等到程式结束了,也许 画面都还没出现。为了解决这个问题,Xlib 提供 XFlush 这个函数, 可以强迫 Xlib 立即将 buffer 内,现有的全部讯息都传送出去,以让 X Server 立即可以做处理。

6. 关闭(destroy)视窗

--------------------------------------------------------------------------------

XDestroy(display, w)
Display *display;
Window w;

w 要关闭的视窗。


--------------------------------------------------------------------------------

7. 关闭 display

--------------------------------------------------------------------------------

XCloseDisplay(display);
Display display;


--------------------------------------------------------------------------------

8. 例

--------------------------------------------------------------------------------

/* --- Xtest.c --- */

#include 
#include 
#include 
#include 

main() {
Display *display;
Window window;
XSetWindowAttributes attr;
XSizeHints *sz;

/* 建立一个 display 的 connection */
display = XOpenDisplay("0:0");

/* 建立和设定 window 的属性 */
window = XCreateWindow(display, XDefaultRootWindow(display),
100, 100, 300, 300, 2, XDefaultDepth(display, 0),
InputOutput, CopyFromParent, 0, &attr);

/* 和 Window Manager 进行沟通 */
XStoreName(display, window, "hello!! world!!");
sz = XAllocSizeHints();
sz->x = 100;
sz->y = 100;
sz->width = 300;
sz->height = 300;
sz->flags = USPosition | USSize;
XSetNormalHints(display, window, sz);

/* Mapping Window  正式影射到显示器画面*/
printf("Map window\n");
XMapWindow(display, window);
getchar(); /* 至此,视窗已执行 Map 的动作了,但
   显示器上,却可能看不到。*/

printf("XFlush\n");
XFlush(display);
getchar(); /* 这,你应该就看到显示器上的变化了 */

/*
   .................
   .... 程式处理部分 ..
   ....................
*/

/* 关闭视窗 */
printf("Destory Window\n");
XDestroyWindow(display, window);
getchar();

printf("XFlush\n");
XFlush(display);
getchar();

/* 关闭 display */
printf("close display\n");
XCloseDisplay(display);
getchar();
}


--------------------------------------------------------------------------------

gcc -o Xtest Xtest.c -L/usr/X11R6/lib -lX11


--------------------------------------------------------------------------------
上面是一个简单的例程式和 compile 的方法。

X Window 程式设计入门--第三章 绘图(Graphic)
http://cnpa.yzu.edu.tw/~thinker 作者:李圭烽 (Thinker; Thinker.bbs@bbs.yzu.edu.tw) (2001-06-01 20:10:00)
Index:
颜色 
Graphics Context 
Graphics 
Image 
例 

--------------------------------------------------------------------------------


1. 颜色
  在 X Window 视窗系统,程式使用颜色都是透过配置 color cell。 color cell 存放着颜色的 RGB 色值,即绘图时显示器上所显示出来 的色值。在我们使用某种颜色之前,必需先配置一个正确的 cell, 使用该 cell 做为绘图时的参数。当 server 将图形输出到显示器时 ,显示器(显示卡)会取出 cell 内所设定之 RGB 值,输出到画面。 RGB 即光学三元色红、绿、蓝,经由三元色的比例不同,可以得到不 同的颜色。  


color cells 分成两种,read-only cell 和 read/write cell。 read-only cell 只能被使用,不能被应用程式修改,但是由各个应用 程式之间一起分享,共同使用。read/write cell 被配置(allocate) 之後可以修改其 RGB 值,但一般是不在各应用程式之间分享,是私有 的。

colormap 是 color cell 的集合。每个 client 都可以有自己的 colormap,所以同时可能会有多个 colormap 存在。而系统在同一 时间内,只安装一个 colormap 到显示器上(有些硬容许同时安装多 个 colormap),所以当一个视窗能显示正确的颜色时,其它视窗的颜色 可能会不正确。这是因为不同的 colormap,其 cell 和 RGB 值的对 应可能不同,所以当 server 把某个视窗的 colormap 安装到硬体上 时,其它视窗内的 cell 和 RGB 的对映,就变成新被安装的 colormap 的对映方式,导至不正确的颜色显示。也就是当 server 把某个视窗 的 colormap 安装到显示硬上时,该视窗的颜色显示就会正确。反 之,则会显示目前被安装的 colormap 的对映状况,显示不正确的颜 色。为了避免这个问题,一般建议使用预定(default)共用的 colormap ,每个视窗都使用同一个 colormap。default 的 colormap 可由 函数 XDefaultColormap 取得。

颜色是以 R、G、B 三种元色光的量值来表示,当某元色的量值越大时 ,则显示出来的颜色就越偏向该元色。Xlib 使用 XColor 记录 RGB 值。 
--------------------------------------------------------------------------------

typedef struct {
unsigned long pixel; /* pixel value */
unsigned short red, green, blue; /* rgb values */
char flags; /* DoRed, DoGreen, DoBlue */
char pad;
} XColor;


--------------------------------------------------------------------------------
red、green、blue 的围从 0 到 65535。Black 用 (0,0,0) 表示, White 用 (65535,65535,65535) 表示。

每个 color cell 在 colormap 上都占有一个位子,为了分别不同的 color cell,我们为每个 color cell 指定一个编号,称之为 pixel。 所以当我们需要指定一个 cell 时,我是指定其 pixel 值。pixel 是一个 long 整数,每一个 bit 我们称之为一个 plane。因此,当我 们在 colormap 上分配 x 个 plane 时,我们就是分配 2^x^ 个 cell。

cell 有分 read-only 和 read/write:read-only cell的 RGB 值是由 server 设定的,不能更改。但是可由各 client 并同使用。每当有一 个 client 配置(allocate)read-only cell,则 server 会纪录下来, 当所有配置该 cell 的 client 都释放该 cell 後,该 cell 才算真正 被释放。否则该 cell 就只能维持其原来的值不能改变,也不能被当成 read/write cell 配置。而同一个 client 多次配置同一个 cell 则视 为多次的配置,client 也必需释放该 cell 相同次数。read/write cell 则不会有初值,但却可以由 client 设定更改其 RGB 值。当 client 分 配到 read/write cell 後,虽然其它 client 也可以设定其内容 (RGB 值),但一般我们还是认为 read/write cell是属私人的,不被分 享共用的。


--------------------------------------------------------------------------------

Status XAllocColor(display, colormap, screen_in_out)
Display *display;
Colormap colormap;
XColor *screen_in_out;

colormap 指定使用的 colormap。
screen_in_out 指定和传回 colormap 内,实际的值。


--------------------------------------------------------------------------------
可配置一个 read-only cell。client 指定一个 RGB 值,XAllocColor 分配一个对映到硬体所能提供最接近该 RGB 值的 cell。RGB 值设定在 screen_in_out 的 red、green、blue,而 cell 的 pixel 值会将由 pixel 传回。硬体实际提供的 RGB 值,则经由 red、green、blue 传 回。


--------------------------------------------------------------------------------

Status XAllocColorCells(display, colormap, contig
plane_masks_return, nplanes, pixels_return,
npixels)
Display *display;
Colormap colormap;
Bool contig;
unsigned long plane_masks_return[];
unsigned int nplanes;
unsigned long pixels_return[];
unsigned int npixels;

colormap 指定使用的 colormap
contig 一个 Boolean 值,指示配置的 planes 是否必
需是连续的。
plane_mask_return
传回 plane masks 的 array。
nplanes 指定在 plane_mask_return 要传回多少个 plane
masks。
pixels_return 传回 pixel 阵列。
npixels 指定在 pixels_return 传回多少个 pixel values。


--------------------------------------------------------------------------------
分配多个 read/write cell。这些 cell 都未指定内容,分配之後, client 程式可以更改其 RGB 值。在这你必需在 nplanes 指定你要分 配几个 plane,分配到的 plane,会经由 plane_mask_return 传回。 plane_mask_return 这个阵列内的每个元素,指定了分配到的 plane 的 mask。npixels 则指定了要分配多少个 pixel,pixel 则由 pixels_return 这个阵列传回。所有分配到的 cell 的数目为 npixels * 2^nplanes^,而 cell 的 pixel 值则为 pixels_return 传回之 pixel 值和 plane mask 做 OR 运算所能产生 的所有值。如果 contig 设为 true,则所有分配到之 plane 会是相 邻连续的,也就是所有传回之 plane mask 做 OR 运算,会得到一群 设为 1 的连续 bits。


--------------------------------------------------------------------------------

XStoreColor(display, colormap, color)
Display *display;
Colormap colormap;
XColor *color;

colormap 指定 colormap。
color 指定 pixel 和 RGB 值。


--------------------------------------------------------------------------------
使用 XStoreColor 设定指定之 cell 的 RGB 值,但只限於 read/write cell。


--------------------------------------------------------------------------------

XStoreColors(display, colormap, color, ncolors)
Display *display;
Colormap colormap;
XColor color[];
int ncolor;

colormap 指定 colormap。
color 指定要设定之颜色的结构阵列。
ncolor 指定 color 阵列中有多少个设定值。


--------------------------------------------------------------------------------
XStoreColors 可以同时设定多个 color cells 的 RGB 值,但只 限於 read/write cell,read-only cell 不能更改。


--------------------------------------------------------------------------------

XFreeColors(display, colormap, pixels, npixels, planes)
Display *display;
Colormap colormap;
unsigned long pixels[];
int npixels;
unsigned long planes;

colormap 指定使用之 colormap。
pixels 指定对映到 cells 的 pixels 值的阵列。
npixels 指定 pixels 阵列中有多少个 pixel 值。
planes 指定你要释放的 planes。


--------------------------------------------------------------------------------
当你所配置到的 color cell 不再被需要时,你可以使用 XFreeColors 释放。XFreeColors 同一时间内可以释放多个 cell ,pixels 是指定要释放掉的 cell 的阵列,npixels 则指定 cell 的数目。planes 则是要释放之 planes 的 plane mask,将要释放 之所有 plane 的 mask OR 起来所得到的 mask。所释放的 pixels 为,任一指定之 pixel 和 plane mask 之部分集合做 OR 运算後,所 有可能产生的 pixel 集合。

使用颜色名称
除了使用 RGB 值之外,你可以使用颜色的名称,并取得适当的 pixel 值。 
--------------------------------------------------------------------------------

status XAllocNamedColor(display, colormap, color_name,
screen_def_return, exact_def_return)
Display *display;
Colormap colormap;
char *color_name;
XColor *screen_def_return, *exact_def_return;

colormap 指定 colormap。
color_name 颜色之名称。
screen_def_return
传回硬所能提供最接近之 RGB 值。
screen_def_exact
传回精确的 RGB 值。


--------------------------------------------------------------------------------
screen_def_exact 传回的是原本正确颜色所该有的 RGB 色值,而 screen_def_return 传回的则是目前硬所能提供颜色最接近之 RGB 色值。

操作 Colormaps
除了使用预定公用的 colormap (从 parent 视窗拷贝而来的) 分配我 们需要的颜色之外,我们也可以为每个个别的视窗建立独立的 colormap,让每个视窗能独力拥有 colormap,或者是由一群视窗来 分享(share)一个 colormap。


--------------------------------------------------------------------------------

Colormap XCreateColormap(display, w, visual, alloc)
Display *display;
Window w;
Visual *visual;
int alloc;

w 指定视窗。
visual 指定一个该 screen 所提供之 visual。
alloc 指定是否要分配在 colormap 的所有 entry。
可以是 AllocNone 或 AllocAll。


--------------------------------------------------------------------------------
XCreateColormap 会传回一个在指定视窗所在的 screen 上建立之新的 colormap。visual 指定该 colormap 所提供之 visual。

我们可以随时为视窗设定新的 colormap,但是 colormap 的 visual 必需和视窗的 visual 相同。指定视窗的 colormap,我们可以透过 XSetWindowColormap: 
--------------------------------------------------------------------------------

XSetWindowColormap(display, w, colormap)
Display *display;
Window w;
Colormap colormap;


--------------------------------------------------------------------------------



--------------------------------------------------------------------------------

XFreeColormap(display, colormap)
Display *display;
Colormap colormap;

colormap 要释放之 colormap。


--------------------------------------------------------------------------------
释放一个 colormap,但对於预定之 colormap 没有作用。如果要 释放之 colormap 已被使用在某个视窗上时,则 XFreeColormap 会将该视窗之 colormap 设成 None,并对该视窗产生 ColormapNotify event ( event 在後面的章节会谈到 )。

2. Graphics Context
Graphics Context 简称 GC,是存在 server 上的一种资源。GC 是用来 存放绘图时所需要的各项资讯(例如: 线的宽度和长度,前景和背景颜色 等等),在大部分的绘图功能中,都需要使用 GC 做为参数。其实,我们 可以把 GC 看做是我们在做画时的画笔,不同的画笔会产生不同的效果 。同样的,我们也可以使用不同的 GC 内容的变化,来组合达成我们所 需要的画面效果。同一个 GC 可以在不同的视窗使用,也可以在不同的 client 间使用,但是一般来说 ,并不鼓励由不同的 client 使用,因为 Xlib 会对 GC 暂存,可能会造 成同步上的问题。GC 是和 screen 结合在一起的,同时也和 depth 有关 ,只有 screen 和 depth 和 GC 和同的视窗,才可以使用该 GC。也就是 该视窗必需和 GC 是在同一个 screen,并且要有相同的 depth。

Xlib 提供 XGCValues 这个结构, 以存放 GC 的相关资讯。我们将要 设定的值,存於这个结构,同时也经由这个结构传回 GC 的内存值。 
--------------------------------------------------------------------------------

/* GC attribute value mask bits */

#define GCFunction (1L<<0)
#define GCPlaneMask (1L<<1)
#define GCForeground (1L<<2)
#define GCBackground (1L<<3)
#define GCLineWidth (1L<<4)
#define GCLineStyle (1L<<5)
#define GCCapStyle (1L<<6)
#define GCJoinStyle (1L<<7)
#define GCFillStyle (1L<<8)
#define GCFillRule (1L<<9)
#define GCTile (1L<<10)
#define GCStipple (1L<<11)
#define GCTileStipXOrigin (1L<<12)
#define GCTileStipYOrigin (1L<<13)
#define GCFont (1L<<14)
#define GCSubwindowMode (1L<<15)
#define GraphicsExposures (1L<<16)
#define GCClipXOrigin (1L<<17)
#define GCClipYOrigin (1L<<18)
#define GCClipMask (1L<<19)
#define GCDashOffset (1L<<20)
#define GCDashList (1L<<21)
#define GCArcMode (1L<<22)

/* Values */

typedef struct {
int function;
unsigned long plane_mask;
unsigned long foreground;
unsigned long background;
int line_width;
int line_style;
int cap_style;
int join_style;
int fill_style;
int fill_rule;
int arc_mode;
Pixmap tile;
Pixmap stipple;
int ts_x_origin;
int ts_y_origin;
Font font;
int subwindow_mode;
Bool graphics_exposures;
int clip_x_origin;
int clip_y_origin;
Pixmap clip_mask;
int dash_offset;
char dashes;
} XGCValues;


--------------------------------------------------------------------------------
下面是 XGCValues 的栏位说明。 栏位 预设值 说明 
function GXcopy Xlib 定义了 16 种 function, 用以定义各种 X 所提供的绘图形式. Xlib 提供了一些绘图函数, 当我们在一个 drawable 上绘图时, 新绘上的 图该如何和在原本位置上的图形配合呢? function 定义了 X 所提供的 16 种可能中的一种. 当我们为 GC 设好新的 function 之後, 下一次我们使用 GC 进行绘图时, 新的 function 就开始发生了作用. 下面是 Xlib 定义的 16 种 function. GXclear 把输出图素清除为 0 
GXand 把输出之图与原图素做 and 运算 
GXandReverse 先把原图素反相, 然後和输出图素做 and 运算 
GXcopy 直接用输出图素替代原图素 
GXandInverted 先将输出图素和原图素做 and 运算後, 再将结果反相 
GXnoop 维持原图素 
GXxor 输出图素和原图素做 xor 运算 
GXor 输出图素和原图素做 or 运算 
GXnor 先分别把输出图素和原图素做反相, 再将反相後的两图做 and 运算 
GXequiv 先反相输出图素,然後和原图素做 xor 运算  
GXinvert 把原图素反相 
GXorReverse 反相原图素, 然後和输出图素做 xor 运算 
GXcopyInverted 把输出图素反相当为最後结果 
GXorInverted 把输出图素反相後和原图素做 or 运算 
GXnand 把输出图素和原图素做反相, 然後两图做 or 运算 
GXset 把输出部分全设为 1 
 
plane_mask AllPlanes 指定会被影的 planes, 会被影的 planes 设为 1。Xlib 中定义 AllPlanes 常数, 指定所有的 planes。绘图的最後结果是: 
((输出图素 function 原图素) AND plane_mask)

 
foreground 1 指定图形输出时的前景所使用的图素值(pixel)  
background 0 指定图形输出时的背景所使用的图素值(pixel)  
line_width 0 如果输出中有线条时, 线条的宽度.  
line_style LineSolid 线条的样式, Xlib 定义三个常数, 代表三种样式. LineSolid 实线 
LineOnOffDash 虚线 
LineDoubleDash 另一种虚线 
 
cap_style CapButt 指定线条端线点(起点和终点)的样式。 CapNotLast 和 CapButt 相似, 只是 line width 为 0 时, 不画出端点.  
CapButt 方形长角的端点 
CapRound 圆弧形的端点 
CapProjecting 和 CapButt 相似, 但端点会再延伸 line width 的一半长度 
 
join_style JoinMiter 折线的折点形式。 JoinMiter 角状的折点 
JoinRound 图弧状的折点 
JoinBevel 像是两个 CapButt 的端点重叠在一起 
 
fill_style FillSolid 设定线段,文字,和填充画面的来源。 FillSolid 前景填满 foreground 颜色 
FillTiled 以 tile 填满 
FillStippled 前景以填上 foreground 但以 stipple 遮罩起来. 
FillOpaqueStippled 和 FillStippled 相似, 但被遮罩的部分(stipple 内为 0 的部分) 填上 background。  
 
fill_rule EvenOddRule 设定呼叫 XFillPolygon 时,如何定义出内部和外部。 EvenOddRule 以通过指定点的线为基准, 通过 path 奇数次的为 inside 
WindingRule 通过顺时钟方向的 path 和逆时钟方向的 path 的次数如果不同 即为 inside。  
 
arc_mode ArcPieSlice 控制 XFillArcs 如何填满圆弧。 ArcChord 以琴弦般的填满弧 
ArcPieSlice 像被切开的 pie 一样的填满弧 
 
tile 0 和 GC 有相同 root 和深度(depth)的 pixmap 
stipple 0 深度(depth)为 1 的 pixmap 
ts_x_origin 0 设定 tile/stipple 的原点的 x 座标 
ts_y_origin 0 设定 tile/stipple 的原点的 y 座标 
font Implementation dependent 字形,XLoadFont 的传回值 
subwindow_mode ClipByChildren ClipByChildren 
IncludeInferiors  
 
graphics_exposures True 控制 XCopyArea 和 XCopyPlane 的 GraphicsExpose 事件(event)的产生。  
clip_x_origin 0 clip_mask 的原点相对於 drawable 的位置 
clip_y_origin 0 clip_mask 的原点相对於 drawable 的位置 
clip_mask None clip_mask 为深度(depth)为 1 并和 GC 相同 root 的 pixmap, 做为输出的 mask  
dash_offset 
dashes 


你可以透过 XCreateGC 建立一新的 GC: 
--------------------------------------------------------------------------------

GC XCreateGC(display, d, valuemask, values)
Display *display;
Drawable d;
unsigned long valuemask;
XGCValues *values;

d 指定 drawable。
valuemask 指定使用了那些 GC 元件。这个参数是把各元件之
mask OR 起来得到的 mask,用以指定设了那些元
件。
value 指定设定的 GC 内容。


--------------------------------------------------------------------------------
呼叫 XCreateGC 後,建立一个新的 GC,并会传回 GC。所谓的 drawable 指的是一个可以使用绘图功能,在其上进行绘图的视窗或是其它 X 上的 物件。


--------------------------------------------------------------------------------

XFreeGC(display, gc)
Display *display;
GC gc;

gc 指定要释放之 GC。


--------------------------------------------------------------------------------
释放一个己建立之 GC。

下面我们介绍一些设定 GC 的方便函数,以方便我们做 GC 设定。


--------------------------------------------------------------------------------

XSetForeground(display, gc, foreground)
Display *display;
GC gc;
usigned long foreground;

gc 指定作用对像之 GC。
foreground 指定前景颜色。


--------------------------------------------------------------------------------
设定 GC 内容的前景。当你使用该 GC 绘图时,前景颜色即为 GC 内所设的前景颜色。


--------------------------------------------------------------------------------

XSetBackground(display, gc, background)
Display *display;
GC gc;
usigned long background;

gc 指定作用对像之 GC。
background 指定背景颜色。


--------------------------------------------------------------------------------
设定 GC 的背景颜色。当你使用该 GC 绘图时,前景颜色即为 GC 内所设的前景颜色。


--------------------------------------------------------------------------------

XSetLineAttributes(display, gc, line_width, line_style,
cap_style, join_style)
Display *display;
GC gc;
unsigned int line_width;
int line_style;
int cap_style;
int join_style;

gc 指定作用对像之 GC。
line_width 线条之宽度。
line_style 线条型式,有 LineSolid、LineOnOffDash、
LineDoubleDash。
cap_style 指定线条端点之型式,有 CapNotLast、
CapButt、CapRound、CapProjecting。
join_style 指定线条转折点的型式,有 JoinMiter、
JoinRound、JoinBevel。


--------------------------------------------------------------------------------
设定线的形式。


--------------------------------------------------------------------------------

XSetFont(display, gc, font)
Display *display;
GC gc;
Font font;

gc 指定作用对像之 GC。
font 指定字型。


--------------------------------------------------------------------------------
设定字形。


--------------------------------------------------------------------------------

XSetArcMode(display, gc, arc_mode)
Display *display;
GC gc;
int arc_mode;

gc 指定作用对像之 GC。
arc_mode 指定画弧时,封口的型式,有 ArcChord、
ArcPieSlice。


--------------------------------------------------------------------------------
设定画弧时,封口的型式。 
3. Graphics
Xlib 提供大量的函数,处理图形的输出。 
--------------------------------------------------------------------------------

XClearWindow(display, w)
Display *display;
Window w;


--------------------------------------------------------------------------------
清除视窗。


--------------------------------------------------------------------------------

XDrawPoint(display, d, gc, x, y)
Display *display;
Drawable d;
GC gc;
int x, y;

d 指定 drawable。
gc 指定要使用之 GC。
x, y 指定画点的座标。


--------------------------------------------------------------------------------
在视窗上画一点。


--------------------------------------------------------------------------------

XDrawLine(display, d, gc, x1, y1, x2, y2)
Display *display;
Drawable d;
GC gc;
int x1, y1, x2, y2;

d 指定目的 drawable。
gc 指定使用之 GC。
x1, y1, x2, y2 指定线的两端点座标。


--------------------------------------------------------------------------------
在视窗上画一条线。


--------------------------------------------------------------------------------

XDrawRectangle(display, d, gc, x, y, width, height)
Display *display;
Drawable d;
GC gc;
int x, y;
unsigned int width, height;

x, y 指定矩形的左上角座标。
width, height 指定矩形的大小。


--------------------------------------------------------------------------------
在视窗上画一个矩形。


--------------------------------------------------------------------------------

XDrawArc(display, d, gc, x, y, width, height, angle1, 
angle2)
Display *display;
Drawable d;
GC gc;
int x, y;
unsigned int width, height;
int angle1, angle2;

x, y 指定弧的中心点(原点)。
width, height  指定弧的 x 轴和 y 轴的比例。
angle1, angle2 弧的起始角度和结止角度。


--------------------------------------------------------------------------------
在视窗上画一个弧形。


--------------------------------------------------------------------------------

XFillRectangle(display, d, gc, x, y, width, height)
Display *display;
Drawable d;
GC gc;
int x, y;
unsigned int width, height;


--------------------------------------------------------------------------------
在视窗上画一个填满颜色的矩形。


--------------------------------------------------------------------------------

XFillArc(display, d, gc, x, y, width, height, angle1,
angle2)
Display *display;
Drawable d;
GC gc;
int x, y;
unsigned int width, height;
int angle1, angle2;


--------------------------------------------------------------------------------
在视窗上画一个填满颜色的弧形。


--------------------------------------------------------------------------------

Font XLoadFont(display, name)
Display *display;
char *name;

name 指定字形名称。


--------------------------------------------------------------------------------
载入字形,当我要在视窗上显示某一字形时,我们必需先载入字形, 然後才能使用。使用这个函数之後,会传回一个 Font 的 ID,这 就是我们在使用 XSetFont 函数设定 GC 所要传入的参数之一。 当我们使用传回来的 Font 设定 GC 後,就可以使用该 GC 当做 参数在萤幕上显示该字形的字串。


--------------------------------------------------------------------------------

XUnloadFont(display, font)
Display *display;
Font font;

font 指定要 unload 的字形。


--------------------------------------------------------------------------------
移除字形。当我们不再使用一字形时,我们必需将之移除。


--------------------------------------------------------------------------------

XDrawString(display, d, gc, x, y, string, lengtth)
Display *display;
Drawable d;
GC gc;
int x, y;
char *string;
int length;

gc 画字时所要用的 GC。
x, y 指定字串显示的座标。
string 要显示的字串。
length 字串的长度。


--------------------------------------------------------------------------------
在萤幕上显示一字串。


--------------------------------------------------------------------------------

XDrawString16(display, d, gc, x, y, string, length)
Display *display;
Drawable d;
GC gc;
int x, y;
XChar2b *string;
int length;

gc 指定使用之 GC。
x, y 指定字串显示的座标。
string 指定显示之字串。
length 指定字串内有多少个字。


--------------------------------------------------------------------------------
在萤幕上显示一双位元组(或 16bits)的字串。X Window 有支援双位元组 字集,用以显示双位元组的语言(如中文等)。双位元组的字元是以 XChar2b 这个 structure 来表示: 
typedef struct {
unsigned char byte1;
unsigned char byte2;
} XChar2b;

byte1 存放的是双位元组字元的高位元组(MSB:most significant byte) ,byte2 存放的则是低位元组(LSB:least significant byte)。一个双 位元组的字串就是 XChar2b 的阵列。

4. Image 
在 GUI 介面下, 常会需要将一张图片直接 show 在视窗上, 像是 show 照片. Xlib 提供一些可以直接在 client 和 server 间传送影像(image)的函数, 以直接处理一张张的影像, 例如, 图形档. 在这提到的 image 相关函数, 都会使用 XImage 结构做为函数的输入参数. Xlib 在 client 端使用 XImage 描述影像资料, 当你要使用 Xlib 的函数处理影像资料时, 必需透过 XImage 进行操作. XImage 提供一个物件化的介面, 透过物件提供的函数, 我们可以相同的方式, 处理不同的影像资料. 
--------------------------------------------------------------------------------

/*
 * Data structure for "image" data, used by image manipulation routines.
 */
typedef struct _XImage {
    int width, height;          /* size of image */
    int xoffset;                /* number of pixels offset in X direction */
    int format;                 /* XYBitmap, XYPixmap, ZPixmap */
    char *data;                 /* pointer to image data */
    int byte_order;             /* data byte order, LSBFirst, MSBFirst */
    int bitmap_unit;            /* quant. of scanline 8, 16, 32 */
    int bitmap_bit_order;       /* LSBFirst, MSBFirst */
    int bitmap_pad;             /* 8, 16, 32 either XY or ZPixmap */
    int depth;                  /* depth of image */
    int bytes_per_line;         /* accelarator to next line */
    int bits_per_pixel;         /* bits per pixel (ZPixmap) */
    unsigned long red_mask;     /* bits in z arrangment */
    unsigned long green_mask;
    unsigned long blue_mask;
    XPointer obdata;            /* hook for the object routines to hang on */
    struct funcs {              /* image manipulation routines */
        struct _XImage *(*create_image)();
#if NeedFunctionPrototypes
        int (*destroy_image)        (struct _XImage *);
        unsigned long (*get_pixel)  (struct _XImage *, int, int);
        int (*put_pixel)            (struct _XImage *, int, int, unsigned long);
        struct _XImage *(*sub_image)(struct _XImage *, int, int, unsigned int, unsigned int);
        int (*add_pixel)            (struct _XImage *, long);
#else
        int (*destroy_image)();
        unsigned long (*get_pixel)();
        int (*put_pixel)();
        struct _XImage *(*sub_image)();
        int (*add_pixel)();
#endif
        } f;
} XImage;


--------------------------------------------------------------------------------
XImage 可以建过 XInitImage 和 XCreateImage 建立. 建立之後的 object, 可以使用 XGetPixel, XPutPixel, XSubImage 和 XAddPixel 读取和修改内容. 使用 XPutImage 输出到 drawable (视窗或 pixmap), 使用 XGetImage 从 drawable 读取. object 最後必需使用 XDestroyImage 释放.


--------------------------------------------------------------------------------

       Status XInitImage(image)
             XImage *image;


--------------------------------------------------------------------------------
XImage 在使用之前, 必需先经过 XInitImage 进行 initialize. 在呼叫 XInitImage 之前, 除了 manipulate functions 之外, 其它的栏位都必需 先设定好. 成功的话, 传回非0值, 否则传回 0.


--------------------------------------------------------------------------------

       XImage *XCreateImage(display, visual, depth, format, off-
       set, data, width, height, bitmap_pad,
                               bytes_per_line)
             Display *display;
             Visual *visual;
             unsigned int depth;
             int format;
             int offset;
             char *data;
             unsigned int width;
             unsigned int height;
             int bitmap_pad;
             int bytes_per_line;


--------------------------------------------------------------------------------
XCreateImage 会为输入之影像资料产生一个 XImage 结构, 并传回结构. 是一个包装 XInitImage 的函数. 'format' 指定影像储存的形式, 有 ZPixmap, XYPixmap 和 XYBitmap. ZPixmap 的储存方式是一个 pixel 接 着一个 pixel 存放. XYPixmap 则是 plane 接着 plane, 将所有 pixel 特定 plane 的内容集成 bitmap, 然後依 plane 的顺序储存各 plane 形式的 bitmap. XYBitmap 和 XYPixmap 一样, 但是 XYBitmap只有一个 plane. 而, 影像的内容则存在 data 所指定的 memory block . 使用者必需指定一块记忆(data)以储存影像, XCreateImage 并不会主动为您配置. data 的大小和 image 的大小和深度(depth)有关, format 也会影响. 下面是 data 大小和 bytes_per_line 的计算公式. 
format  size of data  bytes_per_line  
ZPixmap width * height * ((depth + 7) / 8) width * ((depth + 7) / 8) 
XYPixmap ((width + 7) / 8) * height * depth (width + 7) / 8 
XYBitmap ((width + 7) / 8) * height * 1  (width + 7) / 8 



--------------------------------------------------------------------------------

       unsigned long XGetPixel(ximage, x, y)
             XImage *ximage;
             int x;
             int y;


--------------------------------------------------------------------------------
取得影像内的一个图点.


--------------------------------------------------------------------------------

       XPutPixel(ximage, x, y, pixel)
             XImage *ximage;
             int x;
             int y;
             unsigned long pixel;


--------------------------------------------------------------------------------
在影像上放上一个点.


--------------------------------------------------------------------------------

       XImage *XSubImage(ximage, x, y, subimage_width,
       subimage_height)
             XImage *ximage;
             int x;
             int y;
             unsigned int subimage_width;
             unsigned int subimage_height; 


--------------------------------------------------------------------------------
读取影像的一部分内容, 并传回 XImage. x, y, subimage_width, 和 subimage_height 指定 image 内的一个方框的位置和大小, 读 取方框内的资料.


--------------------------------------------------------------------------------

       XAddPixel(ximage, value)
             XImage *ximage;
             long value;


--------------------------------------------------------------------------------
把 image 内每个点的 pixel 值都加上指定的 valuex.


--------------------------------------------------------------------------------

       XDestroyImage(ximage)
               XImage *ximage;


--------------------------------------------------------------------------------
由上面和下面各函数所产生的 XImage 物件, 最後不用时, 都要使用 XDestroyImage 释放掉。注意, XDestroyImage 会主动将 data 释放 


--------------------------------------------------------------------------------

       XPutImage(display, d, gc, image, src_x, src_y, dest_x,
       dest_y, width, height)
               Display *display;
               Drawable d;
               GC gc;
               XImage *image;
               int src_x, src_y;
               int dest_x, dest_y;
               unsigned int width, height;


--------------------------------------------------------------------------------
将 image 输出 'd' 所指定的 drawable.


--------------------------------------------------------------------------------

       XImage *XGetImage(display, d, x, y, width, height,
       plane_mask, format)
               Display *display;
               Drawable d;
               int x, y;
               unsigned int width, height;
               unsigned long plane_mask;
               int format;


--------------------------------------------------------------------------------
读取 'd' 所指定之 drawable 的影像. 'plane_mask' 指定要读取的 planes. 若指定的 planes, 为 drawable 所有 planes 的 subset, 那麽传回的 image 的 depth 将和指定的 planes 数目相同.


--------------------------------------------------------------------------------

       XImage *XGetSubImage(display, d, x, y, width, height,
       plane_mask, format, dest_image, dest_x,
                            dest_y)
             Display *display;
             Drawable d;
             int x, y;
             unsigned int width, height;
             unsigned long plane_mask;
             int format;
             XImage *dest_image;
             int dest_x, dest_y;


--------------------------------------------------------------------------------


下面是处理影像的例. 
--------------------------------------------------------------------------------

/* -- Image-test.c -- */
#include 
#include 
#include 
#include 
#include 
#include 
#include "gnu.xpm"
#include "doomface.xpm"
#include "Boss2.xpm"


struct ColorElm {
char *tag;
unsigned long pixel;
};


unsigned long
get_pixel(Display *display, Colormap colormap, char *str)
{
char *cp = str;
XColor color, excolor;

if(*cp == '#') {
int j, k;
int t;
int rgbl;
unsigned short rgb[3];

cp++;
if(strlen(cp) == 6)
rgbl = 2;
else
rgbl = 4;

for(k = 0; k < 3; k++) {
t = 0;
for(j = 0; j < rgbl; j++) {
char c = *(cp++);

t <<= 4;
if(c >= 'A' && c <= 'F')
t += c - 'A' + 10;
else if(c >= 'a' && c <= 'f')
t += c - 'a' + 10;
else
t += c - '0';
}
rgb[k] = t;
}

color.red = rgb[0];
color.green = rgb[1];
color.blue = rgb[2];
color.flags = DoRed | DoGreen | DoBlue;
XAllocColor(display, colormap, &color);
} else {
char *cp;

if(strcasecmp(str, "None") == 0)
cp = "black";
else
cp = str;
XAllocNamedColor(display, colormap, cp, &color, &excolor);
}

return color.pixel;
}


/*
 * 从 *.xpm 转换成 Xlib 能处理的 image 资料形式 (ZPixmap)
 */
char *
xpm_to_data(char **xpm, Display *display,
Colormap colormap, int depth, int *width, int *height)
{
int nc, el; /* # of color, and element length */
int i;
int bpp; /* byte per pixel */
struct ColorElm *ce, *cep;
unsigned short rgb[3];
XColor color;
char *data, *dp;

sscanf(*(xpm++), "%d %d %d %d", width, height, &nc, &el);
bpp = (depth + 7) / 8;

data = (char *)malloc(sizeof(char) * bpp *
*width * *height);
ce = (struct ColorElm *)malloc(sizeof(struct ColorElm) * nc);

cep = ce;
for(i = 0; i < nc; i++) {
char *cp;

cep->tag = (char *)malloc(sizeof(char) * el);
memcpy(cep->tag, *xpm, el);

cp = *xpm + el;
/*
 * skip redundant character
 */
while(isspace(*cp)) cp++;
while(*(cp++) != 'c') {
while(isspace(*cp)) cp++;
while(!isspace(*cp)) cp++;
while(isspace(*cp)) cp++;
}
/*
 * get pixel of color
 */
while(isspace(*cp)) cp++;
cep->pixel = get_pixel(display, colormap, cp);

cep++;
xpm++;
}

/*
 * generate image data
 */
dp = data;
for(i = 0; i < *height; i++) {
int j;
char *p;

p = *(xpm++);
for(j = 0; j < *width; j++) {
int idx;
unsigned long pixel;

/*
 * find pixel of point
 */
for(idx = 0; idx < nc; idx++)
if(!memcmp(p, ce[idx].tag, el)) {
memcpy(dp, &ce[idx].pixel, bpp);
break;
}
dp += bpp;
p += el;
}
}

free(ce);
return data;
}


main() 
{
Display *display;
Window window;
XSetWindowAttributes attr;
Colormap colormap;
XColor color1, color2;
XGCValues gcvalue;
GC gc;
XSizeHints *sz;
XImage *img1, *img2, *img3;
int screen;
char *data; /* image data */
int w, h; /* width & height */
int bpp; /* byte per pixel */

display = XOpenDisplay("0:0");

colormap = DefaultColormap(display, screen = DefaultScreen(display));
color1.red = color1.blue = 0xffff;
color1.green = 0;
color2.red = color2.green = color2.blue = 0xff;
color1.flags = color2.flags = DoRed | DoGreen | DoBlue; 
XAllocColor(display, colormap, &color1);
XAllocColor(display, colormap, &color2);

attr.background_pixel = color2.pixel;
window = XCreateWindow(display, XDefaultRootWindow(display),
100, 100, 500, 300, 2, XDefaultDepth(display, 0),
InputOutput, CopyFromParent, CWBackPixel, &attr);

XStoreName(display, window, "hello!! world!!");
sz = XAllocSizeHints();
sz->x = 100;
sz->y = 100;
sz->width = 300;
sz->height = 500;
sz->flags = USPosition | USSize;
XSetNormalHints(display, window, sz);
XMapWindow(display, window);

gc = XCreateGC(display, window, 0, &gcvalue);
XSetForeground(display, gc, color1.pixel);
XSetBackground(display, gc, color2.pixel);
XFlush(display);

printf("Show image!!\n");
bpp = (DefaultDepth(display, screen) + 7) / 8;
/*
 * Create gnu.xpm
 */
data = xpm_to_data(image_name, display, colormap,
DefaultDepth(display, screen), &w, &h);
img1 = XCreateImage(display,
DefaultVisual(display, screen),
DefaultDepth(display, screen),
ZPixmap, 0, data,
w, h, 8, w * bpp);
/* (w, h) 是影像的宽和高 */

/*
 * Create doomface.xpm
 */
data = xpm_to_data(xpm, display, colormap,
DefaultDepth(display, screen), &w, &h);
img2 = XCreateImage(display,
DefaultVisual(display, screen),
DefaultDepth(display, screen),
ZPixmap, 0, data,
w, h, 8, w * bpp);
/* (w, h) 是影像的宽和高 */

/*
 * Create Boss2.xpm
 */
data = xpm_to_data(Boss2_xpm, display, colormap,
DefaultDepth(display, screen), &w, &h);
img3 = XCreateImage(display,
DefaultVisual(display, screen),
DefaultDepth(display, screen),
ZPixmap, 0, data,
w, h, 8, w * bpp);
/* (w, h) 是影像的宽和高 */

/*
 * Show images
 */
XPutImage(display, window, gc, img1, 0, 0, 10, 10, w, h);
XPutImage(display, window, gc, img2, 0, 0, 10, 100, w, h);
XPutImage(display, window, gc, img3, 0, 0, 200, 50, w, h);
XFlush(display);

/*
 * Destroy images
 */
XDestroyImage(img1);
XDestroyImage(img2);
XDestroyImage(img3);
sleep(3);

XDestroyWindow(display, window);
XFlush(display);

XCloseDisplay(display);
}


--------------------------------------------------------------------------------
执行结果

颜色和原图有些不同, 主要原因是使用 default 的 colormap. 上面的程式 在视窗显示三张 .xpm 的图形档, 分别是 gnu.xpm, doomface.xpm 和 Boss2.xpm. 

5. 例

--------------------------------------------------------------------------------

/* ---- XGraph.c ---- */

#include 
#include 
#include 
#include 

main() {
Display *display;
Window window;
XSetWindowAttributes attr;
Colormap colormap;
XColor color1, color2;
XGCValues gcvalue;
GC gc;
XSizeHints *sz;

display = XOpenDisplay("0:0");

/* 取得预设之 colormap */
colormap = DefaultColormap(display, 
DefaultScreen(display));
/* 取得 colorcell */
color1.red = color1.blue = 0xffff;
color1.green = 0;
color2.red = color2.green = color2.blue = 0xff;
color1.flags = color2.flags = DoRed | DoGreen | DoBlue; 
XAllocColor(display, colormap, &color1);
XAllocColor(display, colormap, &color2);

/* 设定视窗的 attribute 和建设 */
attr.background_pixel = color2.pixel; /* 背景颜色 */
window = XCreateWindow(display,
XDefaultRootWindow(display), 100, 100, 300, 300,
2, XDefaultDepth(display, 0), InputOutput, 
CopyFromParent, CWBackPixel, &attr);

/* 设定和 window manager 进行沟通 */
XStoreName(display, window, "hello!! world!!");
sz = XAllocSizeHints();
sz->x = 100;
sz->y = 100;
sz->width = 300;
sz->height = 300;
sz->flags = USPosition | USSize;
XSetNormalHints(display, window, sz);

/* 显示视窗 */
printf("Map window\n");
XMapWindow(display, window);
XFlush(display);
getchar();

/* 建立并设定 GC */
gc = XCreateGC(display, window, 0, &gcvalue);
XSetForeground(display, gc, color1.pixel);
XSetBackground(display, gc, color2.pixel);

/* 画一个矩形 */
printf("Draw rectangle\n");
XDrawRectangle(display, window, gc, 10, 10, 100, 100);
XFlush(display);
getchar();

/* 清除视窗 */
XClearWindow(display, window);

/* 设定 GC 内,线的形式 */
XSetLineAttributes(display, gc, 5, LineOnOffDash,
CapButt, JoinRound);
/* 画线 (200, 10) - (200, 290) */
printf("Draw line\n");
XDrawLine(display, window, gc, 200, 10, 200, 290);
XFlush(display);
getchar();

/* 关闭视窗 */
printf("Destory Window\n");
XDestroyWindow(display, window);
XFlush(display);
getchar();

printf("close display\n");
XCloseDisplay(display);
getchar();
}


--------------------------------------------------------------------------------

gcc -o XGraph XGraph.c -L/usr/X11R6/lib -lX11


--------------------------------------------------------------------------------
上面是一个简单的例程式和 compile 的方法。
X Window 程式设计入门--第四章 Event
http://cnpa.yzu.edu.tw/~thinker 作者:李圭烽 (Thinker; Thinker.bbs@bbs.yzu.edu.tw) (2001-06-01 21:04:00)
Index:
Event Types and Event Masks 
Events Propagation 
Event Handling 
Events 
MapNotify 
UnmapNotify 
Expose 
ButtonPress, ButtonRelease, KeyPress, KeyRelease, MotionNotify 
CreateNotify 
DestroyNotify 
ResizeRequest 
FocusIn, FocusOut 
例 

--------------------------------------------------------------------------------

X Window 的 client 应用程式,透过和 X Server 之间的 connection 和 Server 进行通讯。client 应用程式会通过 Xlib,向 X Server 发出 request (要求)。而有时侯,X Server 也会产生 Event (事件), 需要经由 connection 通知 client 程式。这些 Event 可能是因为使用 者按下按键或是移动滑鼠,也可能是因为 client 发出的 request 而使 得 X Server 产生的。X Server 可以经由产生 Event,通知 client 所 发生的所有事件。

Event Types and Event Masks
由 Server 传给 client 的 Event,依不同的 Event,各使用不同的 structure 表示。而在不同 Event 之间,还是有其相同存在。不同 Event 使用的 structure 有一部分是相同的,每个 sturcture 最前面 相同的部分可以使用 XAnyEvent 这个 structure 表示。 
--------------------------------------------------------------------------------

typedef struct {
int type;
unsigned long serial;
Bool send_event;
Display *display;
Window window;
} XAnyEvent;


--------------------------------------------------------------------------------
这个 sturcture 收集各个 Event 相同的部分,包括 type。透过 type ,我可以辨识出各个不同的 Event,以做对应的处理。

Event 可以 _XEvent 这个 union type 表示: 
--------------------------------------------------------------------------------

typedef union _XEvent {
int type;
XAnyEvent xany;
XKeyEvent xkey;
XButtonEvent xbutton;
XMotionEvent xmotion;
XCrossingEvent xcrossing;
XFocusChangeEvent xfocus;
XExposeEvent xexpose;
XGraphicsExposeEvent xgraphicsexpose;
XNoExposeEvent xnoexpose;
XVisibilityEvent xvisibility;
XCreateWindowEvent xcreatewindow;
XDestroyWindowEvent xdestroywindow;
XUnmapEvent xunmap;
XMapEvent xmap;
XMapRequestEvent xmaprequest;
XReparentEvent xreparent;
XConfigureEvent xconfigure;
XGravityEvent xgravity;
XResizeRequestEvent xresizerequest;
XConfigureeRequestEvent xconfigurerequest;
XCirculateEvent xcirculate;  
XCirculateRequestEvent xcirculaterequest;
XPropertyEvent xproperty;
XSelectionClearEvent xselectionclear;
XSelectionRequestEvent xselectionrequest;
XSelectionEvent xselection;
XColormapEvent xcolormap;
XClientMessageEvent xclient;
XMappingEvent xmapping;
XErrorEvent xerror;
XKeymapEvent xkeymap;
long pad[24];
} XEvent;


--------------------------------------------------------------------------------
根据 type 这个栏位, 我们可以分辨出不同的 Event。依据不同的 Event, 我就可以透过对映该 Event 的栏位, 取得该 Event 的相关 讯息和资料。

下面是用来标示各种 Event type 的 mask: 
--------------------------------------------------------------------------------

NoEventMask
KeyPressMask
KeyReleaseMask
ButtonPressMask
ButtonReleaseMask
EnterWindowMask
LeaveWindowMask
PointerMotionMask
PointerMotionHintMask
Button1MotionMask
Button2MotionMask
Button3MotionMask
Button4MotionMask
Button5MotionMask
ButtonMotionMask
KeymapStateMask
ExposureMask
VisibilityChangeMask
StructureNotifyMask
ResizeRedirectMask
SubstructureNotifyMask
SubstructureRedirectMask
FocusChangeMask
PropertyChangeMask
ColormapChangeMask
OwnerGrabButtonMask


--------------------------------------------------------------------------------
使用这些 mask 检查 _XEvent 中表示 Event type 的 type 栏位, 可以得知处理之 Event 的 type。

下表是不同的 Event type, 在 _XEvent 中所该对映的栏位。多个 type 可能对映到同一个栏位。 
Event Mask Event Type Structure Generic Structure 

--------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------
 
ButtonMotionMask MotionNotify XPointerMoveEvent XMotionEvent 
Button1MotionMask 
Button2MotionMask 
Button3MotionMask 
Button4MotionMask 
Button5MotionMask 
ButtonPressMask ButtonPress XButtonPressedEvent XButtonEvent 
ButtonReleaseMask ButtonRelease XButtonReleaseEvent XButtonEvent 
ColormapChangeMask ColormapNotify XColormapEvent 
EnterWindowMask EnterNotify XEnterWindowEvent XCrossingEvent 
LeaveWindowMask LeaveNotify XLeaveWindowEvent XCrossingEvent 
ExposureMask Expose XExposeEvent 
GCGraphicsExposures in GC GraphicsExpose XGraphicsExposeEvent 
 NoExpose XNoExposeEvent 
FocusChangeMask FocusIn XFocusInEvent XFocusChangeEvent 
 FocusOut XFocusOutEvent XFocusChangeEvent 
KeymapStateMask KeymapNotify XKeymapEvent 
KeyPressMask KeyPress XKeyPressEvent XKeyEvent 
KeyReleaseMask KeyRelease XKeyReleaseEvent XKeyEvent 
OwnerGrabButtonMask N.A. N.A. 
PointerMotionMask MotionNotify XPointerMovedEvent XMotionEvent 
PointerMotionHintMask N.A. N.A. 
PropertyChangeMask PropertyNotify XPropertyEvent 
ResizeRedirectMask ResizeRequest XResizeRequestEvent 
StructureNotifyMask CirculateNotify XCirculateEvent 
 ConfigureNotify XConfigureEvent 
 DestroyNotify XDestroyWindowEvent 
 GravityNotify XGravityEvent 
 MapNotify XMapEvent 
 ReparentNotify XReparentEvent 
 UnmapNotify XUnmapEvent 
SubstructureNotifyMask CirculateNotify XCirculateEvent 
 ConfigureNotify XConfigureNotify 
 CreateNotify XCreateWindowEvent 
 DestroyNotify XDestroyWindowEvent 
 GravityNotify  
 MapNotify XMapEvent 
 ReparentNotify XReparentEvent 
 UnmapNotify XUnmapEvent 
SubstructureRedirectMask CirculateRequest XCirculateRequestEvent 
 ConfigureRequest XConfigureRequestEvent 
 MapRequest XMapRequestEvent 
N.A. ClientMessage XClientMessageEvent 
N.A. MappingNotify XMappingEvent 
N.A. SelectionClear XSelectionClearEvent 
N.A. SelectionNotify XSelectionEvent 
N.A. SelectionRequest XSelectionRequestEvent 
VisibilityChangeMask VisibilityNotify XVisibilityEvent 
 

第一栏是代表的 Event Mask, 第二栏是 Event type, 第三栏是在 _XEvent 中所对映之 structure type。当有两个以上的 Event type 在 _XEvent 中共用一个 structure type, 则该 structure type 会出现在第四栏。表中的 N.A. 表示该栏位在该列不具任何意义。

Events Propagation 
当视窗有事件发生时,对相对应的 Event 却可能不是由该视窗产生, 由其它的视窗产生。在我们设定视窗属性时,有一个 do_not_propagate_mask 栏位。do_not_propagate_mask 用来选择那 些 event 不能往先视窗(ancestor window)传递。一般来说 KeyPress KeyRelease ButtonPress ButtonRelease PointerMotion Button1Motion - Button2Motion 这些 event 发生之後,如果在 发生的视窗要求知道这些讯息,则这 evnet 会往父视窗(parent window)传递(propagation)。如果父视窗也不要,就会再往父视窗 的父视窗传递,一直到遇到其中的一个先视窗(ancester window) 有 client 向 其要求这项 evnet 为止,这时 Event 就会在这个祖先视窗产生。 我们称这个祖先视窗为 Event Window,而发生 event 的视窗我们 称之为 Source Window。如果在传递 event 的过程中,若有任何 一个传递经过的视窗,在该视窗的 do_not_propagate_mask 设定 设 evnet,则传递将会停止下来,不再继续往先视窗传递。

Event Handling
X Server 会针对各个视窗产生各种的 Event, 但是对於 client 程 式而言, 并不是所有的 Event 都是需要的。而大量的讯息传送, 会 使的整个系统的效率降低, 所以我们并不希望 X Server 对 client 送出所有的 Evnet。因此在 X 的设计上, X Server 让 client 可 以选择每个视窗所需要的 Event, 只传送需要的 Event 给 client 。在 client 程式端, 由 X Server 送过来的 Event 都会存在 Event Queue 等待程式读取和处理。程式可以透过 XLib 所提供的 函数, 读取 Event Queue 内的 Event 并取得关於 Event Queue 的 资讯。当 request 产生时, XLib 并不会马上将之传到 X Server。 因为小量而多次的资料传送, 对於网路的效率是一个很大的伤害。 为了提高传送的效率, XLib 会将产生的 Request 先暂存在 output buffer, 到达某一程度的量时, 再一口气将之传送到 X Server。 然而这个使的 X Server 的反应延迟, 我们可能希望 X Server 立 即做出反应和处理。诸如此类, 我们可以要求 XLib 立即将 output buffer 的 request 全部传送给 X Server,让 X Server 可以即 时收到 request 并做出反应。

设定视窗需要产生那些 Event 的方法有很多种, 我们可以在呼叫 XCreateWindow 和 XChangeWindowAttributes 时, 设定 XSetWindowAttributes 的 event_mask 栏位, 将之作为 XCreateWindow 和 XChangeWindowAttributes 的参数。另外就是透 过 XSelectInput这个函数来选择。 
--------------------------------------------------------------------------------

XSelectInput(display, w, event_mask)
Display *display;
Window w;
long event_mask;

w 指定选择事件的视窗
event_mask 指定 event mask


--------------------------------------------------------------------------------
XSelectInput 可以为你的 client 选取任何视窗上的 Event, 只要该视窗有你需要的 Event 发生, X Server 就会送 Event 给 client。而 X Server 会为任何向他登记需要该 Event 的 client 发送 Event。

在选择好所需要的 Event 之後, 我们就可以开始接受 X Server 送来的 Event。一个 client 程式, 可能同时会使用到多个 视窗, 但所有视窗的 Event 传送到 client 後, 都会存在 Event Queue。虽然各个视窗的 Event 会因而混在一起, 但是我们可以 透过 XEvent 这个 union type 的 XAnyEvent 结构的 window 栏位区分出该 Event 所属的视窗。

透过 XNextEvent, 可以从 Event Queue 取出下一个从 X Server 送来的 Event, 并将之从 Event Queue 移除。 
--------------------------------------------------------------------------------

XNextEvent(display, event_return)
Display *display;
XEvent *event_return;

event_return 传回 event queue 的下一个 event


--------------------------------------------------------------------------------


XPeekEvent 和 XNextEvent 的功能大致相同, 只差於 XPeekEvent 不会将传回的 Event 从 Event Queue 移除。 
--------------------------------------------------------------------------------

XPeekEvent(display, event_return)
Display *display;
XEvent *event_return;

event_return 传回 event queue 的下一个 event


--------------------------------------------------------------------------------


XFlush 会将暂存在 output buffer 的所有 request 立即传送出 去。 
--------------------------------------------------------------------------------

XFlush(display)
Display *display;


--------------------------------------------------------------------------------
一般程式中并不会使用到这个函数, 因为 XPending, XNextEvent 和 XWindowEvent 会自动 flush output buffer。

XSync 会像 XFlush 一样, 将 output buffer 所有的 request 传送出去, 并且还会等待所有的 request 都被 X Server 处理。 
--------------------------------------------------------------------------------

XSync(display, discard)
Display *display;
Bool discard;

discard 指示 XSync 是否要放弃现在 Event Queue
的所有 Event。


--------------------------------------------------------------------------------


当 Event 源源不断的从 X Server 传来时, Event Queue 可能 放满了一些尚未被取出处理的 Event。XEventsQueued 会传回 Event Queue , 目前所存放的 Event 数量。 
--------------------------------------------------------------------------------

int XEventsQueued(display, mode)
Display *display;
int mode;

mode 指定设定模式。你可以指定 QueuedAlready,
QueuedAfterFlush, 或 QueuedAfterReading。


--------------------------------------------------------------------------------
XEventsQueued 可以指定三种模式: 
QueuedAlready: 
传回目前 Event Queue 的 evnet数量。 
QueuedAfterFlush: 
如果 Event Queue 不是空的, 则传回 Event Queue 的 Event 数量。若是空的, 则先 flush output buffer, 并试着从 connection 读出更 多的 Event, 然後传回读到的 Event 数量。 
QueuedAfterReading: 
如果 Event Queue 不是空的, 则传 回 Event Queue 的 Event 数量。反之, 则试着从 connection 读出所有的 Event, 但是并不 flush output buffer, 然後传回所读到的 Event 数量。 

传回 Event Queue , 第一个和指定视窗和 evnet mask 符 合之 Event。 
--------------------------------------------------------------------------------

XWindowEvent(display, w, event_mask, event_return)
Display *display;
Window w;
long event_mask;
XEvent *event_return;

w 指定视窗
event_mask 指定 event mask
event_return 传回符合的 event structure


--------------------------------------------------------------------------------


传回 Event Queue , 第一个符合指定之 evnet mask 的 Event。 
--------------------------------------------------------------------------------

XMaskEvent(display, event_mask, event_return)
Display *display;
long event_mask;
XEvent *event_return;

event_mask 指定要求之 event mask
event_return 传回符合条件的 event structure


--------------------------------------------------------------------------------


Events
MapNotify
当视窗从未映射(unmapped)的状态变为映射(mapped)状态时, 会发生 MapNotify Event,并传送到需要该 Event 的 client。 而使视窗从未映射的状态转变成映射状态进而使 X Server 产生 MapNotify Event 的原因,可能是因为 client 呼叫 XMapWindow XMapRaised XMapSubwindows 或 XReparentWindow 等 Xlib 函数。 而需要视窗 MapNotify Event 的 client,必需设定 event mask 的 StructureNotifyMask bit 或是 SubstructureNotifyMask, 如此 X Server 才会将产生的 MapNotify Event 传送给 client。SubstructureNotifyMask 是设在父视窗(parent window) 的,一旦 client 在视窗的 event mask 设定 SubstructureNotifyMask bit,则只要有任何的子视窗(child window) 被映射(mapped),就会产生 MapNotify。下面是 MapNotify Event 的结构: 
--------------------------------------------------------------------------------

typedef struct {
int type;
unsigned long serial;
Bool send_event; /* true 如果是以 SendEvent 产生 */
Display *display;
Window event;
Window window;
Bool override_redirect;
} XMapEvent;


--------------------------------------------------------------------------------
event 所存的 Window ID 要看 UnmapNotify Event 是因为设定 StructureNotifyMask 或是 SbustructureNotifyMask 而产生。 如果是因为 StructureNotifyMask ,则 event 的内容是变成 未映射(unmapped) 视窗的 Window ID,反之若是因为 SubstructureNotifyMask 而产生的,则 event 栏位被设定为被 变成未映射之视窗的父视窗(parent window) 的 Window ID。 window 则是被映射的视窗的 Window ID。

UnmapNotify
这是和 MapNotify 相反的情况。当视窗从映射(mapped)状态转变 为未映射(unmapped)状态时,X Server 会产生 UnmapNotify Evnet ,并传送给需要该 Evnet 的 client。client 若需要接收 UnmapNotify Event,则必需在 event mask 设定 StructureNotifyMask bit 或 SubstructureNotifyMask bit。SubstructureNotifyMask 是 设在父视窗(parent window),当 client 在视窗的 event mask 设定 SubstructureNotifyMask bit 时,任何的子视窗(child window)变成 未映射(unmapped)时,就会产生 UnmapNotify。这个 event type 的结构(structure) 如下所示: 
--------------------------------------------------------------------------------

typedef struct {
int type;
unsigned long serial;
Bool send_event; /* true 如果是以 SendEvent 产生 */
Display *display;
Window event;
Window window;
Bool from_configure;
} XUnmapEvent;


--------------------------------------------------------------------------------
event 所存的 Window ID 要看 UnmapNotify Event 是因为设定 StructureNotifyMask 或是 SbustructureNotifyMask 而产生。 如果是因为 StructureNotifyMask ,则 event 的内容是变成 未映射(unmapped) 视窗的 Window ID,反之若是因为 SubstructureNotifyMask 而产生的,则 event 栏位被设定为被 变成未映射之视窗的父视窗(parent window) 的 Window ID。 window 则是被映射的视窗的 Window ID。

Expose
Expose 产生的时机是在,当视窗的某一个区块的资料己经 遗失了,而且该区域也看的见时,这时 X Server 必需通知那 些想要得知这个讯息的 client,以便这些 client 补上这块被 遗失的资料或做其它合适处置。当我们在同一个画面上同时开 上两个以上的视窗时,往往会觉的画面不够大而使的视窗相互 重叠。在重叠的部分,下面的视窗中,也许原本在该区有显示 什麽资料,但现在被上面的视窗盖掉了。经过我们的操作,经 过一段时间,原本被盖住的部分又会重新被显示出来。如我把 在上面的视窗关掉,其它视窗被该视窗盖住的部分又会显示出 来。或是我们把某个视窗显小成小图示(icon)那麽其它视窗被 其盖住的部分也会再度显示出来。当这些被上层盖住的区块被 再度裸露出来时,X Server 必需为这些区块重新画上在下层 视窗的资料,以使产生上层视窗被移掉,而下层视窗浮出来 的效果,但往往 X Server 并没有记录下这些被遮盖区块的 内容。这时 X Server 就需要 client 的协助,重新把这个失 落的环节补上。视窗被重新裸露出来时,被裸露出来的区块往往 并不是规则的四方形。被裸露出来的部分可能成梯状或某种由 四方形方块相叠而形成的图形。每个 Expose Event 都包含着 一个在视窗内被裸露出来的方形区块的位置和大小,X Server 会为每个该被重新补上的方形区块产生一个 Expose Event。 而 X Server 会把同一次裸露事件所产生的 Expose Evnet 一 起且连续的送出。要接收 Expose Event 的 client 必需在 event mask 设定 ExposureMask。Expose Event 的结构如下: 
--------------------------------------------------------------------------------

typedef struct {
int type;
unsigned long serial;
Bool send_event;
Display *display;
Window window;
int x, y;
int width, height;
int count;
} XExposeEvent;


--------------------------------------------------------------------------------
window 是被裸露出来的视窗的 Window ID。x 和 y 则是该 被重绘的方形区块的右上角座标,width 和 height 则是该 区块的宽度和高度。而 count 若为 0,则表示这个 Expose Event 是这次视窗被裸所产生的最後一个 Expose Event。 若不为 0,则表示後面至还有这麽多个 Expose Event 在等 着,而且还可能比 count 所指示的数量多。

ButtonPress
ButtonRelease
KeyPress
KeyRelease
MotionNotify
X Window 提供 KeyPress 和 KeyRelease Event。当使用者 按下键盘上的按键时,X Server 会产生 KeyPress Event 给登记需要按键按下去的讯息的 client。当使用者放开按 键时,X Server 又会产生 KeyRelease Evnet 给登记需要 KeyRelease Event 这项讯息的 client。X Server 会对键盘 上所有的按键产生这两种 Event。ButtonPress 和 ButtonRelease 则是在滑鼠(mouse) 被按下和放开时产生。 client 要接收 KeyPress KeyRelease ButtonPress ButtonRelease 等 Event,需要在 event mask 设定 KeyPressMask KeyReleaseMask ButtonPressMask ButtonReleaseMask 对等的 bit。

当滑鼠在视窗内移动时,会产生一连串的 MotionNotify Event。滑鼠不断的移动而 MotionNotify 也不断的产生, 难道你的滑鼠每移动一点 X Server 就产生一个 MotionNotify 吗? 当然不是这麽张。X Server 并不包证多远的距离 产生一个 MotionNotify Event,但包证至少在移动的起 始点和终点各会产生一次 MotionNotify Event。 在 event mask 设定 Button1MotionMask - Button5MotionMask,ButtonMotionMask PointerMotionMask 等,可以使 X Server 传送 MotionNotify Event 给 client。 
--------------------------------------------------------------------------------

Button1MotionMask - Button5MotionMask 
当被指定的滑鼠按钮(button)被按住且滑鼠一边在移动时,X Server 会产生 MotionNotify。 
ButtonMotionMask 
当任何一个滑鼠按钮(button)按住且滑鼠一边移动时,X Server 会产生 MotionNotify。 
PointerMotionMask 
不论滑鼠按钮(button)是否被按下,只要滑鼠移动 X Server 就会产生 MotionNotify。 
--------------------------------------------------------------------------------
除了上面几个之外,还有 PointerMotionHintMask。 PointerMotionMask 是和上面几个 Mask 合使用的, 当指定任何上面所提的 Mask 後,再加上 PointerMotionHintMask ,X Server 可以只产生一次 MotionNotify 给 client。除此之外,滑鼠按钮的状态改变 (按下变放开或反之),滑鼠的标离开视窗,或 client 呼叫 XQueryPointer 或 XGetMotionEvents,X Server 也会产生 MotionNotify Event。这些状况产生的 MotionNotify 都会设定 Event Structure 的 is_hint 栏位(is_hint memeber)。除了上面的状况外, X Server 可能依然产生没有设定 is_hint 栏位的 MotionNotify Event (也就是没在 event mask 设定 PointerMotionMask 情况下所产生的 MotionNotify)。 
--------------------------------------------------------------------------------

typedef struct {
int type;
unsigned long serial;
Bool send_event;
Display *display;
Window window;
Window root;
Window subwindow;
Time time;
int x, y;
int x_root, y_root;
unsigned int state;
unsigned int botton;
Bool same_screen;
} XButtonEvent;
typedef XButtonEvent XButtonPressedEvent;
typedef XButtonEvent XButtonReleasedEvent;

typedef struct {
int type;
unsigned long serial;
Bool send_event;
Display *display;
Window window;
Window root;
Window subwindow;
Time time;
int x, y;
int x_root, y_root;
unsigned int state;
unsigned int keycode;
Bool same_screen;
} XKeyEvent;
typedef XKeyEvent XKeyPressedEvent;
typedef XKeyEvent XKeyReleasedEvent;

typedef struct {
int type;
unsigned long serial;
Bool send_event;
Display *display;
Window window;
Window root;
Window subwindow;
Time time;
int x, y;
int x_root, y_root;
unsigned int state;
unsigned int is_hint;
Bool same_screen;
} XMotionEvent;
typedef XMotionEvent XPointerMoveEvent;


--------------------------------------------------------------------------------
window 是指 event window,也就是产生事件的 window。root 是指 source window 所在 screen 之 root window。前面有提及 事件会寻着视窗的阶层架构,往祖先视窗(ancester window)传递 (propagate)。若 event window 和 source window 是不同个视 窗时(从视窗阶层下层传递上来),则 subwindow 则是 event window 的 child window,这个 child window 是事件传递过程经过的视 窗。若 event window 和 source window 是同一个时(也就是产生 Event 和事件发生是同一个视窗),则 subwindow 设为 None。 x 和 y 则是事件发生时,标相对於 event window 左上角的 座标。x_root 和 y_root 则是标在 root window 上的座标。 state 是事件发生时滑鼠按钮和键盘上修饰键的状态,是由一个 或数个状态做 OR 运算的组合。这些状态是 Button1Mask, Button2Mask,Button3Mask,Button4Mask,Button5Mask, ShiftMask,LockMask,ControlMask,Mod1Mask,Mod2Mask, Mod3Mask,Mod4Mask,Mod5Mask。same_screen 是指示 root window 和 event window 是否在同一个 screen,内容是 True or False。 button 是 XButtonPressedEvent 和 XButtonReleasedEvent 事件结构内的栏位,用以纪录滑鼠按钮状态改变。XKeyPressedEvent 和 XKeyReleasedEvent 的 keycode 是任何按键的 keycode 即 键盘按键的代码(在後面会述及)。XPointerMovedEvent 事件结构 内的 is_hint 在前面有说明,内容是 NotifyNormal 或 NotifyHint。 

CreateNotify
CreateNotify 是在视窗建立时产生的事件,client 在视窗的 evnet mask 设定 SubstructureNotifyMask,则任何子视窗(child window)的建立都会产生 CreateNotify。这个事件的结构包含下面 几个资讯: 
--------------------------------------------------------------------------------

typedef struct {
int type;
unsigned long serial;
Bool send_event;
Display *display;
Window parent;
Window window;
int x, y;
int width, height;
int border_width;
Bool override_redirect;
} XCreateWindowEvent;


--------------------------------------------------------------------------------
parent 是被建立之视窗的 parent window 的 Window ID,window 则是新建立的 window。x 和 y 则是新视窗相於 parent window 左上角的座标。width 和 height 则是新视窗的宽度和高度。 override_redirect 则是视窗的 override_redirect 属性,若 为 True 则 window manager 不会帮这个视窗做处理,乎略这个 视窗。

DestroyNotify
DestroyNotify 是在视窗被删除 (destroy) 时产生的事件。当视窗 被删除时,其所有的子视窗也会跟着被删除,而子视窗 (child window) 的 DestroyNotify 会在父视窗 (parent window) 之前产生。client 在视窗的 event mask 设定 StructureNotifyMask,则视窗被删 除时会收到 DestroyNotify,若设定 SubstructureNotifyMask 则是子视窗 (child window) 被删除时会收到 DestroyNotify。 
--------------------------------------------------------------------------------

typedef struct {
int type;
unsigned long serial;
Bool send_event;
Display *display;
Window event;
Window window;
} XDestroyWindowEvent;


--------------------------------------------------------------------------------
若是设定 StructureNotifyMask 则 event 是被删除(destroyed) 视窗的 Window ID,若是 SubstructureNotifyMask 则 event 是被删除视窗的父视窗 (parent window) 的 window ID。

ResizeRequest
如果有任何的 client 试途要改变视窗的大小, 则 X Server 会向其它要求知道这项讯息的 client 传送 ResizeRequest。 当 client 需要知道这个讯息时,必需在视窗的 event mask 设定 ResizeRedirect,这样子只要有任何的其它 client 试 图改变该视窗大小,X Server 就会传送 ResizeRequest 给 client。这个事件的结构如下: 
--------------------------------------------------------------------------------

typedef struct {
int type;
unsigned long serial;
Bool send_event;
Display *display;
Window window;
int width, height;
} XResizeRequestEvent;


--------------------------------------------------------------------------------
width 和 height 是另一个 client 试图要把视窗改成的大小。

FocusIn
FocusOut
当你在键盘上敲下 'A' 时,X Server 会将「按下按键'A'」这个 event 送到目前 focus 的视窗。也就是当视窗为目前的 focus 时 ,X Server 才会把键盘输入的事件传送给视窗。而 focus 会在不 同的视窗之间转移,不同的 Window Manager 有不同的转移方式。 可能是你把滑鼠指标移到那某个视窗,focus 就会转移到该视窗。 也可能是你要在该视窗的 title 上面按一下,focus 才会转移过 去。当 focus 进入某一视窗时,代表 focus 从另一个视窗移走。 focus 进入视窗时,会在该视窗产生 FocusIn event。focus 从 视窗移除时,X Server 会在该视窗产生 FocusOut event。如果你 希望 client 程式接收这两个 event,必需在视窗的 event-mask 设定 FocusChangeMask 这个 bit。

下面是 FocusIn 和 FocusOut 这两个 event 的结构: 
--------------------------------------------------------------------------------

typedef struct {
       int type;
       unsigned long serial;
       Bool send_event;
       Display *display;
       Window window;
       int mode;
       int detail;
} XFocusChangeEvent;

typedef XFocusChangeEvent XFocusInEvent;
typedef XFocusChangeEvent XFocusOutEvent;


--------------------------------------------------------------------------------



--------------------------------------------------------------------------------

/* ------ XEvent.c ------ */
#include 
#include 
#include 
#include 


GC gc;
Display *display;
Window window;


void
reshow(void)
{
/*
 * 画方框
 */
XDrawRectangle(display, window, gc, 10, 10, 100, 100);

/*
 * 画直线
 */
XDrawLine(display, window, gc, 200, 10, 200, 290);
}


int
main_loop(XEvent *xe)
{
if(xe->type == Expose)
{
printf("Expose! \n");
reshow();
}

return False;
}


void
init(void)
{
XSetWindowAttributes attr;
Colormap colormap;
XColor color1, color2;
XGCValues gcvalue;
XSizeHints *sz;

display = XOpenDisplay("0:0");

colormap = DefaultColormap(display, DefaultScreen(display));
/*
 * 配置 colorcell
 */
color1.red = color1.blue = 0xffff;
color1.green = 0;
color2.red = color2.green = color2.blue = 0xff;
color1.flags = color2.flags = DoRed | DoGreen | DoBlue; 
XAllocColor(display, colormap, &color1);
XAllocColor(display, colormap, &color2);

attr.background_pixel = color2.pixel; /* background color */
attr.event_mask = ExposureMask; /* receive expose event */

window = XCreateWindow(display,
       XDefaultRootWindow(display),
       100, 100, /* 左上角位置 */
       300, 300, /* 视窗大小 */
       2, /* border 的宽度 */
       XDefaultDepth(display, 0),
       InputOutput, /* window class */
       CopyFromParent, /* visual */
       CWBackPixel | CWEventMask,
       &attr); /* attributes */

XStoreName(display, window, "hello!! world!!");

sz = XAllocSizeHints();
sz->x = 100;
sz->y = 100;
sz->width = 300;
sz->height = 300;
sz->flags = USPosition | USSize;
XSetNormalHints(display, window, sz);

/*
 * Mapping
 */
XMapWindow(display, window);

gcvalue.foreground = color1.pixel;
gcvalue.background = color2.pixel;
gcvalue.line_width = 5;
gcvalue.line_style = LineOnOffDash;
gcvalue.cap_style = CapButt;
gcvalue.join_style = JoinRound;
gc = XCreateGC(display, window,
       GCForeground | GCBackground | 
       GCLineWidth | GCLineStyle |
       GCCapStyle | GCJoinStyle,
       &gcvalue);
}


main() 
{
XEvent xe;
int i = 0;

init();

while(1)
{
printf("while %d\n", i++);
XFlush(display);
XNextEvent(display, &xe); /* 取得下一个 event */
if(main_loop(&xe)) /* 处理 event */
return 0;
}
}


--------------------------------------------------------------------------------
这个程式和第三章的例程式相似, 但处理了 expose event. 执行之後 会发现处理 expose event 之後, 图形不会因为被其它视窗盖掉而无 法恢复了. 

X Window 程式设计入门--第五章 Window
http://cnpa.yzu.edu.tw/~thinker 作者:李圭烽 (Thinker; Thinker.bbs@bbs.yzu.edu.tw) (2001-06-02 00:16:10)
Window
作 者 : Thinker
E-Mail: Thinker.bbs@bbs.yzu.edu.tw 
--------------------------------------------------------------------------------
    
Index:
Create windows/Destroy windows 
    Class 
    Visual Types 
    Depth 
    Create 
    Destroy 
Map windows/Unmap windows 
    Events 
    SubstructureRedirectMask 
    Mapping/UnMapping Fuctions 
Configure windows 
    XWindowChanges 
    Convenient functions 
Change the stacking order 
Change window attributes 
    background 
    border 
    bit-gravity 
    win-gravity 
    backing-store 
    save-under 
    event-mask 
    do-not-propagate-mask 
    override-redirect 
    colormap 
    cursor 

--------------------------------------------------------------------------------


Create windows/Destroy windows
Class
X Window 系统的视窗可分为两类(两种 Class),InputOnly 和 InputOutput。 InputOnly 的视窗,
顾名思意就是只能做为 Input 使用,也就是 client 程式只能透过这个视窗来收取 event,但是不
能在这个视 窗做任何的输出。而 InputOutput 视窗则不只可以接受输入而己, 而且也可以使用该
视窗做输出。你可以在 InputOutput 视窗上输出 文字图形,却不能在 InputOnly 视窗上这麽做。
InputOnly 视窗 由於并没有输出的能力,所以并不会出现在画面上。虽然如此,实 际上它还是存在
的。其主要功能是用来做输入的控制,例如让 标进入其区域时,立即变成另一种的形式。这样的可
以让使用者感 觉到标在 parent 视窗的某个区域采用不同的标。既然 InputOnly 没有输出的能力,
所以 InputOnly 视窗也不能做为 drawable 使用。

Visual
在有些硬体上可能提供多种颜色的对映方式。例如有些硬体可能提供 了 16bits 的模式,也就是显示
出来的每个点使用 16bits 长度的 pixel 值表示。同时也可能提供 8bits 的模式,每个点的颜色使用
 8bits 长度的 pixel 值表示。这种 pixel 值的大小称 depth,例如 使用 8bits 来表示颜色,则其 
 depth 为 8。除了表示的位元数不同外 ,颜色和 pixel 值之间的对映方式也不同。像一般以 RGB 为
 基础的 硬,如果采用 8bits 的模式可能 3bits 对映为 red,3bits 对映 为 green,2bits 对映为 
 blue。这种对映的方式称为 visual。

Xlib 使用 Visual 这个结构存放对映的 visual 资料,而 Visual 实际内容被包装起来,不让使用者
直接接触。Xlib 提供一些应用的 函数和巨集,让 programmer 透过这些功能来操作 Visual 结构。 
下面是所有的 visual 种类及其对 colormap 和 RGB 的影。 
PseudoColor 
pixel 值对映到 colormap 而产生 RGB 值,RGB 值可以随时更改而不 影到已输出的点。 
GrayScale 
和 PseudoColor 差不多,主要的不同在 RGB 值不能更改,否则会影 到已输出的点的颜色。 
DirectColor 
pixel 被分成 R,G,B 三个部分,这三个部分分别对映 colormap 得到 三个值。从 colormap 得到的
三个值,可以组合成对映的颜色值。 colormap 的 RGB 值可以随时更换,并且不会因而影项到已输出 
的点之颜色。 
TrueColor 
和 DirectColor 近似,但是 colormap 内的 RGB 值是内定的,而且不能更改。不同的硬所提供的 
colormap 不尽相同,但在 RGB 各个原色的 colormap 中,大多会提供线性或接进线性变化的 RGB 值。 
StaticColor 
和 PseudoColor 的不同在於其 colormap 的 RGB 值是预定的,无法修改的。 
StaticGray 
和 StaticColor 相似,但任何单一点的 RGB 值都一样,其实就是单色的意思。 
Depth
depth 就如先前所说的,用以表示对映颜色的资料长度。以 8bits 的 pixel 值表示对映颜色,则 
depth 为 8。 
Create
Xlib 提供 XCreateWindow 建立 unmapped 的视窗: 
--------------------------------------------------------------------------------

     Window XCreateWindow(display, parent, x, y, width, height,
border_width, depth, class, visual, valuemask, attributes)
           Display *display;
           Window parent;
           int x, y;
           unsigned int width, height;
           unsigned int border_width;
           int depth;
           unsigned int class;
           Visual *visual
           unsigned long valuemask;
           XSetWindowAttributes *attributes;


--------------------------------------------------------------------------------
如果成功建立视窗的话,XCreateWindow 会传回一 Window ID,用以 往後指定视窗,并且产生 
CreateNotify event。新建立的视窗,会是 parent 参数所指定之视窗的子 视窗(subwindow)。x 和 y 
则指定视窗左上角相对於 parent 视窗左 上角的座标。width 和 hieght 指定视窗的宽和高。border_width 
和 depth 分别是视窗边框的宽度和视窗的 depth,depth 并不需要和 parent 一样。视窗的 class 为 
InputOnly ,border_width 和 depth 必需指定为零,否则会产生 BadMatch error。InputOutput 视窗的 
parent 规定必需也是 InputOutput,否则会产生 BadMatch error,InputOnly 则无这种限 制。visual 
必需设为硬体有提供的 visual。valuemask 和 attributes 则合力指定视窗的属性,在後面会谈到。

XCreateSimpleWindow 则是简易版,用於建立 InputOutput 视窗: 
--------------------------------------------------------------------------------

     Window XCreateSimpleWindow(display, parent, x, y, width, height,
      border_width, border, background)
           Display *display;
           Window parent;
           int x, y;
           unsigned int width, height;
           unsigned int border_width;
           unsigned long border;
           unsigned long background;


--------------------------------------------------------------------------------
参数的指定和 XCreateWindow 都差不多,border 是指定边框的 pixel 值,background 指定背景的 
pixel 值。

Destroy
任何一个视窗被关掉(移除; destroy)都会产生 DestroyNotify event。 parent 视窗被关掉,子视窗也
会因而被关掉,而且会 parent 视窗关 掉之前先自动关掉子视窗。这时,任何被关掉的视窗,包括子视窗
和 parent 视窗,都会产生 DestroyNotify event。

XDestroyWindow 是用以关闭指定之视窗及其子视窗。 
--------------------------------------------------------------------------------

  XDestroyWindow(display, w)
        Display *display;
        Window w;


--------------------------------------------------------------------------------


XDestroySubwindows 关闭指定视窗的所有子视窗,但不包括指定 之视窗。 
--------------------------------------------------------------------------------

  XDestroySubwindows(display, w)
        Display *display;
        Window w;


--------------------------------------------------------------------------------

Map windows/Unmap windows
Events
视窗被建立之後,并不会立即出现在萤幕上,在显示之前还要经过一个 称为 mapping 的动作。map 一个
子视窗後,如果其任何一个 ancest 视窗(先视窗)是在 unmapped 状态(未曾被 map, 或者曾被 map 但又
 被 unmap 了)则子视窗还是不会显示出来,我们称之为 unviewable。 子视窗必需等到其所有 ancest 
 视窗都在 mapped 的状态下时,才会从 unviewable 的状态进入到 viewable 状况。视窗只有在 viewable 
 的状 态下才会被显示。将视窗 unmap 会使的视窗和其子视窗都隐形起来。任 何一个视窗只要有任何一个
  ancest 视窗被 unmap,都会使该视窗从显 示器上消失(隐形起来),也就是进入 unviewable 的状态。

当视窗被 map 时,X Server 会产生 MapNotify evnet。而 unmap 时,会 产件 UnmapNotify event。当视
窗由 unviewable 进入 viewable 状态, 并且没有全部被其它视窗遮住,则 X Server 会产生 Expose event。
 任何时候,只要视窗的任何一部分从在显示上看不到 的状态变成看的到的状态,就会产生 Expose evnet。

SubstructureRedirectMask
如果有 client 程式对 parent 视窗设定 SubstructureRedirectMask,则其它的 client 对子视窗发出的
 Map request 都会被转送给设定 SubstructureRedirectMask 的 client,而且该 Map request 并不会被
 进行。Window Manager 就是采用这个方式,对 root 视窗设定 SubstructureRedirectMask ,因而能攫取
 其它 client 对 root 视窗的子视窗(top window)所送 出的 Map request,而收到 MapRequest Event。
 透过这样的方式, Window Manager 能控制视窗的外观和行为。但,事情并不完全这样 的,如果子视窗
 (child window)的 override-redirect flag 被设为 True,会使这种转送(redirect)机制失效,其它视窗
 所产生的 Map request 将不再被转送给在父视窗设定 SubstructureRedirectMask 的 client。

类似於 SubstructureRedirectMask 的情形也发生在 ResizeRedirectMask。如果有 client 对 parent window
 设定 ResizeRedirectMask,则其它 client 对 child window 的 Resize request 同样也会被转送给对 
 parent window 设定 ResizeRedirectMask 的 client。如果 child window 的 override-redirect flag 被
 设为 True,同样的,这种转送机制 也会失效。

Mapping/UnMapping Fuctions

--------------------------------------------------------------------------------

  XMapWindow(display, w)
        Display *display;
        Window w;

  XMapRaised(display, w)
        Display *display;
        Window w;

  XMapSubwindows(display, w)
        Display *display;
        Window w;


--------------------------------------------------------------------------------
XMapRaised() 除了 map 指定的视窗之外,还会使的视窗上到 window stack 的最上面,使的视窗出现在画面
的最上面,不会被 其它的视窗挡住。而 XMapSubwindows() 则是 map 指定视窗的所 有子视窗。

Configure windows
XWindowChanges

--------------------------------------------------------------------------------

     /* Configure window value mask bits */
     #define   CWX                         (1<<0)
     #define   CWY                         (1<<1)
     #define   CWWidth                     (1<<2)
     #define   CWHeight                    (1<<3)
     #define   CWBorderWidth               (1<<4)
     #define   CWSibling                   (1<<5)
     #define   CWStackMode                 (1<<6)
     /* Values */

     typedef struct {
          int x, y;
          int width, height;
          int border_width;
          Window sibling;
          int stack_mode;
     } XWindowChanges;


--------------------------------------------------------------------------------

x,y 
顾名思意, x,y 栏位设定视窗的左上角位置. 
width,height 
视窗的宽(width)和高(height) 
border_width 
视窗边框的宽度 
sibling,stack_mode 
sibling 和 stack_mode 是相关的, 在 sibling 设定一个 window ID, 此视窗和 欲设定其属性的视窗
为兄弟(sibling;同一个 parent window). stack_mode 指定 视窗重叠顺序如何调整. 
我们可以使用 XConfigureWindow 来对视窗设定一些属性. 
--------------------------------------------------------------------------------

     XConfigureWindow(display, w, value_mask, values)
           Display *display;
           Window w;
           unsigned int value_mask;
           XWindowChanges *values;


--------------------------------------------------------------------------------
values 传入一些视窗的属性, 是一个 (XWindowChanges *)的指标. XWindowChanges 请参考上表. 
value_mask 用以表示被修改的 属性部分. 若指定 values->sibling 和 values->stack_mode, 则 w 
视窗的重叠次序会如下改变: 
Above 
w 视窗的重叠次序会重新排在 sibling 的上面. 
Below 
w 视窗的重叠次序会重新排在 sibling 的下面. 
TopIf 
若 sibling 视窗完全盖住 w 视窗, 则 w 视窗的次序会重新排在最上面. 
ButtomIf 
若 w 视窗完全盖住 sibling 视窗, 则 w 视窗的次序会重新排在最下面. 
Opposite 
若 sibling 视窗完全盖住 w 视窗, 则 w 视窗的次序会重新排在最上面. 若 w 视窗完全盖住 sibling
 视窗, 则 w 视窗的次序会重新排在最下面. 
若只指定 values->stack_mode, 而未指定 values->sibling, 则 w 视窗的重叠次序会如下改变: 
Above 
w 视窗的重叠次序会重新排在任何其它兄弟视窗的上面(最上面;top). 
Below 
w 视窗的重叠次序会重新排在任何其它兄弟视窗的下面(最下面;buttom). 
TopIf 
若有任何其它兄弟视窗盖住 w, 则 w 的重叠次序会重新排在最上面. 
ButtomIf 
若 w 盖住任何其它兄弟视窗, 则 w 的重叠次序会重新排在最下面. 
Opposite 
若有任可其它兄弟视窗盖住 w, 则 w 的重叠次序会重新排在最上面. 若 w 盖住任何其它兄弟视窗, 
则 w 的重叠次序会重新排在最下面. 

--------------------------------------------------------------------------------

#include 
#include 
#include 
#include 
#include 


Display *display;
Window window;
GC gc;


void
draw()
{
XDrawRectangle(display, window, gc, 10, 10, 100, 100);

XDrawLine(display, window, gc, 200, 10, 200, 290);
}


main()
{
XSetWindowAttributes attr;
Colormap colormap;
XColor color1, color2;
XGCValues gcvalue;
XSizeHints *sz;
unsigned int vmask;
XWindowChanges values;

display = XOpenDisplay("0:0");

colormap = DefaultColormap(display, DefaultScreen(display));
color1.red = color1.blue = 0xffff;
color1.green = 0;
color2.red = color2.green = color2.blue = 0xff;
color1.flags = color2.flags = DoRed | DoGreen | DoBlue; 
XAllocColor(display, colormap, &color1);
XAllocColor(display, colormap, &color2);

attr.background_pixel = color2.pixel;
window = XCreateWindow(display, XDefaultRootWindow(display),
50, 100, 400, 300, 2, XDefaultDepth(display, 0),
InputOutput, CopyFromParent, CWBackPixel, &attr);

XStoreName(display, window, "hello!! world!!");
sz = XAllocSizeHints();
sz->x = 50;
sz->y = 100;
sz->width = 400;
sz->height = 300;
sz->flags = USPosition | USSize;
XSetNormalHints(display, window, sz);
XMapWindow(display, window);
XFlush(display);

gc = XCreateGC(display, window, 0, &gcvalue);
XSetLineAttributes(display, gc, 5, LineOnOffDash,
CapButt, JoinRound);
XSetForeground(display, gc, color1.pixel);
XSetBackground(display, gc, color2.pixel);
XFlush(display);
sleep(2);
draw();
XFlush(display);
sleep(3);

printf("XConfigureWindow()\n");
values.x = 100;
values.y = 50;
values.width = 300;
values.height = 200;
vmask = CWX | CWY | CWWidth | CWHeight;
XConfigureWindow(display, window, vmask, &values);
XFlush(display);
sleep(2);
draw();
XFlush(display);
sleep(3);

XDestroyWindow(display, window);
XFlush(display);

XCloseDisplay(display);
}


--------------------------------------------------------------------------------
上例是改变视窗的位置和大小, 位置在萤幕上 (100, 50) 的位置, 大小为宽高各为 300 和 200. 
Convenient functions

--------------------------------------------------------------------------------

     XMoveWindow(display, w, x, y)
           Display *display;
           Window w;
           int x, y;

     XResizeWindow(display, w, width, height)
           Display *display;
           Window w;
           unsigned int width, height;

     XMoveResizeWindow(display, w, x, y, width, height)
           Display *display;
           Window w;
           int x, y;
           unsigned int width, height;

     XSetWindowBorderWidth(display, w, width)
           Display *display;
           Window w;
           unsigned int width;


--------------------------------------------------------------------------------

XMoveWindow, XResizeWindow, XMoveResizeWindow, XSetWindowBorderWidth, 分别为移动视窗, 
改变视窗大小, 移动并改变视窗大小, 和设定视窗的外框的 宽度, 是一些简单方便的函数, 可以简
化 XConfigureWindow 的使用. 
Change the stacking order
在 X 的 window 常因为相互重叠, 而使的视窗部方的画面被其它视窗挡 住. 这种视窗重叠的上下次
序称为 stack order. 
--------------------------------------------------------------------------------

  XRaiseWindow(display, w)
        Display *display;
        Window w;

  XLowerWindow(display, w)
        Display *display;
        Window w;


--------------------------------------------------------------------------------
上面两个函数正是把视窗在 stack 的顺序移至最上面(raise)和 最下面(lower). 
--------------------------------------------------------------------------------

  XCirculateSubwindows(display, w, direction)
        Display *display;
        Window w;
        int direction;


--------------------------------------------------------------------------------
XCirculateSubwindows 改变有子视窗的 stack order, 'direction ' 为 移动的方向, RaiseLowest 把
所有视窗往上移动一个位子, 最上面的视窗 则回到 stack 的最下面, LowerHighest 则反之. 
--------------------------------------------------------------------------------

  XCirculateSubwindowsUp(display, w)
        Display *display;
        Window w;

  XCirculateSubwindowsDown(display, w)
        Display *display;
        Window w;


--------------------------------------------------------------------------------
和 XCirculateSubwindows 功能重, 但比较易於使用. 
--------------------------------------------------------------------------------

  XRestackWindows(display, windows, nwindows);
        Display *display;
        Window windows[];
        int nwindows;


--------------------------------------------------------------------------------
透过 XRestackWindows 可以直接指定 stack order. windows 传入 依次序存放的 Window 阵列, 
nwindows 则是阵列的长度. 
Change window attributes
视窗除了位置和大小之外, 还有其它不同的属性, 控制视窗的外观和行为. 
--------------------------------------------------------------------------------

     /* Window attribute value mask bits */
     #define   CWBackPixmap                (1L<<0)
     #define   CWBackPixel                 (1L<<1)
     #define   CWBorderPixmap              (1L<<2)
     #define   CWBorderPixel               (1L<<3)
     #define   CWBitGravity                (1L<<4)
     #define   CWWinGravity                (1L<<5)
     #define   CWBackingStore              (1L<<6)
     #define   CWBackingPlanes             (1L<<7)
     #define   CWBackingPixel              (1L<<8)
     #define   CWOverrideRedirect          (1L<<9)
     #define   CWSaveUnder                 (1L<<10)
     #define   CWEventMask                 (1L<<11)
     #define   CWDontPropagate             (1L<<12)
     #define   CWColormap                  (1L<<13)
     #define   CWCursor                    (1L<<14)
     /* Values */

  typedef struct {
       Pixmap background_pixmap;/* background, None, or ParentRelative */
       unsigned long background_pixel;/* background pixel */
       Pixmap border_pixmap;    /* border of the window or CopyFromParent */
       unsigned long border_pixel;/* border pixel value */
       int bit_gravity;         /* one of bit gravity values */
       int win_gravity;         /* one of the window gravity values */
       int backing_store;       /* NotUseful, WhenMapped, Always */
       unsigned long backing_planes;/* planes to be preserved if possible */
       unsigned long backing_pixel;/* value to use in restoring planes */
       Bool save_under;         /* should bits under be saved? (popups) */
       long event_mask;         /* set of events that should be saved */
       long do_not_propagate_mask;/* set of events that should not propagate */
       Bool override_redirect;  /* boolean value for override_redirect */
       Colormap colormap;       /* color map to be associated with window */
       Cursor cursor;           /* cursor to be displayed (or None) */
  } XSetWindowAttributes;


  XChangeWindowAttributes(display, w, valuemask, attributes)
        Display *display;
        Window w;
        unsigned long valuemask;
        XSetWindowAttributes *attributes;


--------------------------------------------------------------------------------
XChangeWindowAttributes 设定视窗的各种属性, 各种属性的意义在後面 的小节会一一介绍. 'valuemask' 
是根据设定的 attributes 栏位而 定, 把有设定的栏位的对应 mask 做 OR 运算. 
--------------------------------------------------------------------------------

Display *d;
Window w;
XSetWindowAttributes attr;
unsigned long vmask;

...............
........

attr.save_under = True;
attr.override_redirect = False;
vmask = CWOverrideRedirect | CWSaveUnder;
XChangeWindowAttributes(d, w, vmask, &attr);


--------------------------------------------------------------------------------

background
视窗的背景可以涂上单一颜色, 或者是贴上不同的图案. 
--------------------------------------------------------------------------------

  XSetWindowBackground(display, w, background_pixel)
        Display *display;
        Window w;
        unsigned long background_pixel;

  XSetWindowBackgroundPixmap(display, w, background_pixmap)
        Display *display;
        Window w;
        Pixmap background_pixmap;


--------------------------------------------------------------------------------
XSetWindowBackground 用以设定背景颜色, 'background_pixel' 则为 背景的颜色. 功能和设定 
XSetWindowAttributes 中的 background_pixel 栏位的功能相同. XSetWindowBackgroundPixmap 
则是设定背景为一张图 (pixmap), 相当於设定 background_pixmap 栏位. 
border
设定视窗的外框颜色或图形. 
--------------------------------------------------------------------------------

  XSetWindowBorder(display, w, border_pixel)
        Display *display;
        Window w;
        unsigned long border_pixel;

  XSetWindowBorderPixmap(display, w, border_pixmap)
        Display *display;
        Window w;
        Pixmap border_pixmap;


--------------------------------------------------------------------------------

bit-gravity
设定视窗的 bit-gravity, bit-gravity 控制 InputOutput 视窗 resize 时, 视窗的内容如何维持. 
当视窗的大小改变时, 视窗的内容不会改变, 但是会因为视窗大小的改变而移动其在视窗内的相对位置
(相对於左上角). 当视窗的宽和高改变了 w 和 h, 则任何一点(x,y)的位置会如下改变: 
NorthWestGravity (0,0) 
NorthGravity (w/2,0) 
NorthEastGravity (w,0) 
WestGravity (0,h/2) 
CenterGravity (w/2,h/2) 
EastGravity (w,h/2) 
SouthWestGravity (0,h) 
SouthGravity (w/2,h) 
SouthEastGravity (w,h) 

win-gravity
win-gravity 和 bit-gravity 类似, 但控制的对像是视窗内的子视窗的位置. 
backing-store
有些 X Server 会帮视窗储存其内容, 当视窗的内容 lost 而需要重绘时, Server 会使用先前储存的
内容自动补回去, 这样的动作称为 backing store. backing-store 控制这项功能是否作用, 其状态可
为 NotUseful (default), WhenMapped, Always.

backing-store 为 NotUseful, 则议 X Server 不要进行 backing store. 但这只是议, X Server 可能
还是选择进行 backing store. WhenMapped 则议 X Server 在视窗映射时(mapped)进行 backing store,
 同样的, 这也只是议, 并不保 X Server 一定会进行, 所以 client 还是得注 意 Expose evnet.Always
  则是议随时都进行 backing store, 不论是 映射(mapped)或未映射. 
save-under
save under 和 backing store 类似, 但其处理的对象是被指定视窗盖住 的其它视窗内容. save-under 
可为 True 或 False(default), 为 True, X Server 会自动维护被该视窗盖到的其它视窗内容, 在该部分
重新 Expose 时, 自动回补. 
event-mask
do-not-propagate-mask
do-not-propagate-mask 是一个 KeyPress, KeyRelease, ButtonPress, ButtonRelease, PointerMotion, 
Button1Motion, Button2Motion, Button3Motion, Button4Motion, Buttton5Motion, ButtonMotion 的 
bitwise inclusive or. 当视窗产生或从子视窗 propagate 一个被 mask 的 event, 该 event 将不会继续
往 parent propagate. 
override-redirect
将使的 parent 上设定 SubstructureRedirectMask 失效. 
colormap

--------------------------------------------------------------------------------

  XSetWindowColormap(display, w, colormap)
        Display *display;
        Window w;
        Colormap colormap;


--------------------------------------------------------------------------------

cursor
cursor 指定标进入视窗後所使用的指标, 指标可用 XCreatePixmapCursor 建立. 
--------------------------------------------------------------------------------

  Cursor XCreatePixmapCursor(display, source, mask,
  foreground_color, background_color, x, y)
Display *display;
Pixmap source;
Pixmap mask;
XColor *foreground_color;
XColor *background_color;
unsigned int x, y;


--------------------------------------------------------------------------------
XCreatePixmapCursor 可由 pixmap 建立一个新的标, source 是标的 图案, mask 则是图形的 mask, 用以 
mask 标的图形, 以定义图形内那 一部分属於标图形(设为1), 那一部分不属於(设为0), 建立不规则形状的 
标. 
X Window 程式设计入门--第六章 Inter-Client Communication
http://cnpa.yzu.edu.tw/~thinker 作者:李圭烽 (Thinker; Thinker.bbs@bbs.yzu.edu.tw) (2001-06-02 18:08:00)
Index:
Property & Atom 
   Atom 
   Property 
Cut Buffer 
Window Manager 
   WM_NAME 
   WM_ICON_NAME 
   WM_NORMAL_HINTS 
   WM_HINTS 
   WM_CALSS 
Selection 
   Owner & Requestor 
   例 1 
   传输大量资料 - INCR 
   例 2 
   Selection Atom 
   Functions of Selection 
   Client Message 



Property & Atom



在 X 的世界中, 每一个视窗都随附着一些资料, 称为 property. 每个 property 都有一个名称, 和 type. 每个 property 的 type 指定了资料的资料形态(type), 如 INTEGER, PIXMAP, CURSOR, ... etc. 而 名称则为存取 property 的途径, client 透过名称以存取特定视窗 的某个 property. property 可以是由 client 自定, 或由 system 内建. X 内建了许多 property , 用於视窗管理(window manager 管理). 如: WM_CLASS, WM_NAME, WM_ICON_NAME, ... etc.

Atom
因为效率因素的考量, 这些 property 的名称和 type 以 atom 表示. 在 系统, 每一个 atom 以一个整数(atom ID)表示, 代表着一个字串. 而且 每一个 atom 都是唯一的, 没有任何两个 atom 拥有相同的 ID. 当我对 X Server 发出 request 而需要这些字串时为参数时, 我们就以对应的 atom ID 为参数, 这样可以降低字串在网路上传轮的 overhead, 改进系统效率. Xlib 提供 一些 function, 让我们在 atom 和字串之间转换. 当 client 导入一个新的 字串进入系统时, Xlib 也提供 function 以取得一个唯一,而且不重的 atom ID, 以对应新加入的字串. 因此, 由 client 导入的字串, 在每一次 执行时, 不一定会得到同一个 atom ID. 但, 由系统内建的 atom 则有固定 的 ID, 这些 atom 定义在 (XA_prefix 形式, 如 XA_WM_CLASS).这些 atom 可以 hard code 在程式碥, 不需再经过 Xlib 所提供的 function 加以转换.

atom 除了用於 property 名称和 property type 之外, 还用於 selection , Font property, type of client message 等等.

下面是 Xlib 提供的 function, XInterAtom 可从 string 转换成 atom ID, XInterAtoms 则一次可以转换多个字串. XGetAtomNames 则是由 atom 取得字串. 
--------------------------------------------------------------------------------

  Atom XInternAtom(display, atom_name, only_if_exists)
Display *display;
char *atom_name;
Bool only_if_exists;


--------------------------------------------------------------------------------
由 atom_name 指定要转换的字串, XInternAtom 传回对应的 atom ID. 当 only_if_exists 为 True, 若字串原本不存在系统, 则会为其分配 一个不重的 ID. 若 only_if_exists 为 False 时, 只有在 atom 早己 存在时才会传回 atom ID, 否则会传回 None.


--------------------------------------------------------------------------------

  Status XInternAtoms(display, names, count, only_if_exists,
  atoms_return)
Display *display;
char **names;
int count;
Bool only_if_exists;
Atom *atoms_return;


--------------------------------------------------------------------------------
XInternAtoms 和 XInternAtom 功能相同, 但一次转换多个 atom, 由 names 传入每个字串, count 是字串的数目, 由 atoms_return 传回 atom ID 阵列. only_if_exists 为 False, 只有己存在的 atom 会 传回 atom ID, 不存在的 atom 会传回 None. only_if_exists 为 True 时, 则会为不存在的 atom 配置一个 atom ID. 这个函数只有在所有的字串都 传回 atom ID 时, 才传回不为 0 的数字, 否则传为 0.


--------------------------------------------------------------------------------

  char *XGetAtomName(display, atom)
Display *display;
Atom atom;


--------------------------------------------------------------------------------
XGetAtomName 可从 atom ID 取得对应字串.


--------------------------------------------------------------------------------

  Status XGetAtomNames(display, atoms, count, names_return)
Display *display;
Atom *atoms;
int count;
char **names_return;


--------------------------------------------------------------------------------
和 XGetAtomName 相同, 但可一次转换多个 atom.

Property

--------------------------------------------------------------------------------

  int XGetWindowProperty(display, w, property, long_offset,
  long_length, delete, req_type, actual_type_return,
  actual_format_return, nitems_return, bytes_after_return,
  prop_return)
Display *display;
Window w;
Atom property;
long long_offset, long_length;
Bool delete;
Atom req_type;
Atom *actual_type_return;
int *actual_format_return;
unsigned long *nitems_return;
unsigned long *bytes_after_return;
unsigned char **prop_return;


--------------------------------------------------------------------------------
XGetWindowProperty 为我们传回 property 的内容, client 指定 display, 视窗(w), property, 从资料那个位置(long_offset)开始读取和 读取长度(long_length). delete 为 True or False, 指定是否在读取之後, 将 property 删除. long_length 是以 4 bytes 为单位, 也就是你实际指定 的长度是 long_length * 4. 另外 req_type 指定 property 资料的 type, 若你不计较 property 资料的 type, 那麽你可以指定 AnyPropertyType. 而此 function 则传回资料的实际 type(actual_type_return), 资料的 format(单位长度 8, 16, 32;actual_format_return), 传回多少个单位 的资料(实际长度/format;nitems_return), 结尾还有多少资料 (bytes_after_return)和 property 内容(prop_return).注意: 改变 property 或 delete property 会产生 PropertyNotify event.

若我们所要读取的 property 并不存在, 那麽 XGetWindowProperty 会 传回 None 实际资料 type(actual_type_return). 
--------------------------------------------------------------------------------

  int XGetWindowProperty(display, w, property, long_offset,
  long_length, delete, req_type, actual_type_return,
  actual_format_return, nitems_return, bytes_after_return,
  prop_return)
Display *display;
Window w;
Atom property;
        long long_offset, long_length;
        Bool delete;
        Atom req_type;
        Atom *actual_type_return;
        int *actual_format_return;
        unsigned long *nitems_return;
        unsigned long *bytes_after_return;
        unsigned char **prop_return;


--------------------------------------------------------------------------------
XGetWindowProperty 为我们传回 property 的内容, client 指定 display, 视窗(w), property, 从资料那个位置(long_offset)开始读取和 读取长度(long_length). delete 为 True or False, 指定是否在读取之後, 将 property 删除. long_length 是以 4 bytes 为单位, 也就是你实际指定 的长度是 long_length * 4. 另外 req_type 指定 property 资料的 type, 若你不计较 property 资料的 type, 那麽你可以指定 AnyPropertyType. 而此 function 则传回资料的实际 type(actual_type_return), 资料的 format(单位长度 8 bits, 16 bits, 32 bits;actual_format_return), 传回多少个单位的资料(实际长度/format;nitems_return), 结尾还有多 少资料(bytes_after_return)和 property 内容(prop_return).

因为 X 的网路特性, 让我们不得注意 byte order 的问题. 资料在网路 上传送, 我们无法知道在网对面的接收端电脑的资料表示形式是否和我们 相同. 如一个 32 bits - 4bytes 的整数, 低位元和高位元的存放次序在 不同的电脑上就不太相同. 也许在 A 机器的储放方式是先最低位的 byte 然後次低位,然後次高位,最後最高位. 然 B 机器却可能相反. 因此, 每 个 property 都必需指定 format, 以确定资料单位的单位长度, 这样 Xlib 才能自动进行 byte order 的转换, 确保资料的 byte order 不会 错乱.

若我们所要读取的 property 并不存在, 那麽 XGetWindowProperty 会 传回 None 实际资料 type(actual_type_return). actual_format_return 和 bytes_after_return 也皆为 0. nitem_return 则传回 0(empty).

若 property 存在, 但是 req_type(request type) 和 property 的实际 type 不合(不相同), 那麽 XGetWindowProperty 在 actual_type_return 传回实 际的 type, 在 actual_format_return 传回 property 资料的 format, bytes_after_return 则以 byte 为单位, 传回 property 的实际长度. 这时, nitem_return 则为 0, 也就是不传回任何资料(空的;empty).

若 property 存在, 且指定 AnyPropertyType 或 property type 和指定 的 type 吻合, 则 XGetWindowProperty prop_return 传回 property 的内容. 
--------------------------------------------------------------------------------

N = property 的实际长度(以 byte 为单位)
I = 4 * long_offset
T = N - T
L = MINIMUM(T, 4 * long_length)
A = N - (I + L)


--------------------------------------------------------------------------------
prop_return 传回的资料长度为 L, 但 XGetWindowProperty 总是会多配置 一个 byte 的空间, 在这个多馀的 byte 填上 0, 这样方便字串的使用, 不需进行 null terminate, 增加 copy 的动作. bytes_after_return 则 传回 A, 告知 client 在传回这些资料後, 还有多少资料在後面. prop_return 的内容是从 property 的第 I 个 byte 开始, 一直到 (I + L - 1) byte, prop_return 的空间是於 Xlib 自动配置, client 程式最後必需透过 XFree() 将之释放.


--------------------------------------------------------------------------------

  Atom *XListProperties(display, w, num_prop_return)
Display *display;
Window w;
int *num_prop_return;


--------------------------------------------------------------------------------
XListProperties 可传回随附在视窗(w)的所有 property 名称 (名称字串的 atom). num_prop_return 是实际 property 的个数, 名称 atom 直接从 return value 传回 atom list. 若视窗(w)没有任有 property, 则 function 传回 null pointer. 传回的 atom list 最後必需以 XFree() 释放.


--------------------------------------------------------------------------------

  XChangeProperty(display, w, property, type, format, mode,
  data, nelements)
Display *display;
Window w;
Atom property, type;
int format;
int mode;
unsigned char *data;
int nelements;


--------------------------------------------------------------------------------
透过 XChangeProperty 可以修改增加 property 的内容. property 和 type 分别传入 property 的名称 atom 和 type atom, format 可以指定 8, 16, 32. nelements 则是传入资料的单位个数, data 则为资料内容. mode 则指 定修改方式, PropModeReplace, PropModePrepend, 或 PropModeAppend. 
PropModeReplace 
以新的资料完全取代旧内容. 
PropModePrepend 
新资料插入到旧资料之前 
PropModeAppend 
新资料插入到旧资料之後 
PropModePrepend 和 PropModeAppend mode, 新资料的 type 和 format 必需和旧资料相同.


--------------------------------------------------------------------------------

  XDeleteProperty(display, w, property)
Display *display;
Window w;
Atom property;


--------------------------------------------------------------------------------
删除 property.

Cut Buffer
Cut Buffer 是一种简单, 但是功能、效果较不好的 peer-to-peer 讯通架构. Cut Buffer 是属於一种被动的形式, 资料提供者直接将资料放在 cut buffer。 当其它 client 有需要时,直接从 cut buffer 将资取出,资料的要求者和资 料的提供者之间没有直接的互动。

Cut buffer 机制包含 8 个在 screen 0 的 root window 的 property, 分别以 atoms CUT_BUFFER0 ... CUT_BUFFER7 命名。存在 cut buffer property 的资料,必需 是 STRING type 并且 format 8。资料提供者在储存资料之前,必需先确定这些 property 是否存在。确定的方式是透过 XChangeProperty() 函数, append 长度 为 0 的资料至 CUT_BUFFER0 ... CUT_BUFFER7。

资料提供者在每次储存资料至 CUT_BUFFER0 ... CUT_BUFFER7 之前,必需先 做 rotate property 的顺序。透过 XRotateWindowProperties 函数,将 CUT_BUFFER0 改名为 CUT_BUFFER1, CUT_BUFFER1 改为 CUT_BUFFER2 ...... CUT_BUFFER7 改名为 CUT_BUFFER0。 写入 Cut buffer 的机制如下: 
资料提供者确定 CUT_BUFFER0 ... CUT_BUFFER7 存在.(XChangeProperty) 
Rotate Properties 
将资料存入 CUT_BUFFER0 

Client 在读取资料时,也会希望输替读取 CUT_BUFFER0 ... CUT_BUFFER7 的 内容,那麽需要在读取资料之後,透过 XRotateWindowProperties 函数,将 CUT_BUFFER0 ... CUT_BUFFER7 改名,CUT_BUFFER7 变成 CUT_BUFFER6 ,CUT_BUFFER6 变 CUT_BUFFER5, ......, CUT_BUFFER0 变成 CUT_BUFFER7。 读取 cut buffer 的机制如下: 
读取 CUT_BUFFER0 
Rotate Properties 

Window Manager
当 client 执行时, 除了要处理视窗的内容外, 还需要和 Window Manager 配合, 提供 Window Manager 必要的资讯, 如视窗的名称(WM_NAME),icon 等等, 让 Window Manager 进行装饰工作(如显示 title, 提供视窗的外框). 这 些由 client 提供给 Window Manager 的资讯称为 hint, 是透过 property 的机制附属於 top window.我们可以直设定 property, 或经由 Xlib 提供的 function, 提供 Window Manager Hint

以 client 的观点而言, top window 可分为三种状态 
Normal 
Iconic 
Withdrawn 
视窗刚被建立时, top window 初始在 Withdrawn, 此时视窗尚未 map. 一旦 map 之後, top window 即进入 Normal 或 Iconic state. 之後, 因 map 和 unmap 而在 Normal 和 Iconic 之间转换. Normal state 即一般的视窗模式, 相对於 Iconic state, 视窗只以一个小 icon 表示.

WM_NAME
通常 Window Manager 会在 Window 的上方放置一个 title bar, 用以 显示 Window 的名称. Window Manager 透过 Client 设定 WM_NAME property, 取得 Client 希望设定的讯息. WM_NAME 是一个经过编码的 字串, 而字串的 encoding 则由 property 的 type 决定. 例如以 STRING 为 property type, 则字串的内容为 ISO Latin-1 character 再加上一些控制字元; COMPOUND_TEXT 则为 Compound Text interchange format 字串, 为一种可以包含 Multi-language 的字串格式(此格式内 容烦长, 需另写文章介绍). 
--------------------------------------------------------------------------------

                                                
  void XSetWMName(display, w, text_prop)
Display *display;
Window w;
XTextProperty *text_prop;

  typedef struct {
      unsigned char *value;/* property data */
       Atom encoding;      /* type of property */
       int format;         /* 8, 16, or 32 */
       unsigned long nitems;/* number of items in value */
  } XTextProperty;


--------------------------------------------------------------------------------
Xlib 提供 XSetWMName 做为方便函数, 但使用起来似乎没有比直接 使用 XChangeProperty 方便到那去. 使用 XChangeProperty 修改 WM_NAME property 时, type 参数即和 XTextProperty::encoding 相当, 可以为 STRING, COMPOUND_TEXT 或 C_STRING 等 type.

WM_ICON_NANE
WM_ICON_NAME 指定 window 的 icon 名称. Window Manager 将一个 视窗变成 icon 形式时, 通常会在 icon 下方显示字串, 以提醒使用 者该 icon 和代表的内容. 当 window 进入 icon 状态时, 由於 icon 的面积往往较小, title bar 上的讯息通常太长, 以致於不适合做为 icon 名称. 所以 WM_ICON_NAME 需要由 Client 设定一较精简的讯息, 以反应其内容.

WM_NORMAL_HINTS
WM_NORMAL_HINTS property 的 type 为 WM_SIZE_HINTS, 设定和 window 大小相关的资料, 如视窗的最大宽度和高度. 当使用者欲 改变视窗大小时(如将视窗拉大), Window Manager 会参考 WM_NORMAL_HINTS, 以控制 window 的行为.

WM_NORMAL_HINTS 的内容如下: 
--------------------------------------------------------------------------------
 
Field Type Comments 

--------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------
 
flags CARD32 见下表 
pad 4*CARD32 For backward compatibility 
min_width INT32 宽度最小值 
min_height INT32 高度最小值 
max_width INT32 宽度最大值 
max_height INT32 高度最大值 
width_inc INT32 视窗宽度的变化值 
height_inc INT32 视窗高度的变化值 
min_aspect (INT32,INT32)  
max_aspect (INT32,INT32)  
base_width INT32 初始的宽 
base_height INT32 初始的高 
win_gravity INT32 default=NorthWest 

--------------------------------------------------------------------------------
 
下面定义 WM_SIZE_HINTS.flags bits: 
--------------------------------------------------------------------------------
 
Name Value Field 

--------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------
 
USPosition 1 User-specified x, y 
USSize 2 User-specified width, height 
PPosition 4 Program-specified position 
PSize 8 Program-specified size 
PMinSize 16 Program-specified minimum size 
PMaxSize 32 Program-specified maximum size 
PResizeInc 64 Program-specified resize increments 
PAspect 128 Program-specified min and max aspect ratios 
PBaseSize 256 Program-specified base size 
PWinGravity 512 Program-specified window gravity 

--------------------------------------------------------------------------------
 
WM_SIZE_HINTS.flags 用以告知 Window Manager, Client 设定 WM_SIZE_HINTS 那些栏位. 但 USPosition 和 USSize 则是例外, 告知 Window Manager 视窗第一次 map 时, 可由使用者指定视窗 位置和大小. PPosition 和 PSize 则是另一个另外, 告知 Window Manager 视窗第一次 map 时, 位置和大小全由 client 自行控制, 不需经过使用者指定.

PMinSize 和 PMaxSize 所指定的栏位 min_width, min_height, max_width, max_height 告知 Window Manager, 当使用调整视窗 的大小时, 希望视窗大小不超过这几个极限值.

PResizeInc 和 PBaseSize 的栏位 width_inc, height_inc, base_width 和 base_height 形成下面两修公式: 
width = base_width + (i * width_inc)
height = base_height + (j * height_inc)

i 和 j 是大於零的整数. 当使用者调整视窗大小时, client 希望 Window Manager 只让 user 将视窗调整为符合上列公式所得的宽和高, 成为 perferred window size. 若 base_width 和 base_height 没有指定, 则 Window Manager 以 min_width 和 min_height 做为 base.

PAspect 和 PBaseSize 的栏位形成下面公式: 
min_aspect[0] / min_aspect[1] <
(width - base_width) / (height - base_height) <
max_aspect[0] / max_aspect[1]

在每次改变视窗大小时, Window Manager 会检查上面的不等式是否成立, width 和 height 为视窗的宽和高. 若 client 没有指定 base size, 那麽 width 和 height 就不 需减去 base size, 也就是使用下面的 式子: 
min_aspect[0] / min_aspect[1] <
width / height <
max_aspect[0] / max_aspect[1]

这些不等式, 更进一步限制 preferred window size 的比例.

PWinGravity 指定 client window 要如何移位(shift), 以维持 window manager frame 和 client window 的位置 关. PWinGravity 可以是 Static, NorthWest, NorthEast, SouthWest, SouthEast, North, South, East, West 和 Center. 若指定 Static, 则 client window 保位置, window manager frame 临接在 client window 的外缘; window manager frame 的内缘和 client window 的 border 位在相同位置. 其它 win_gravity 指定的值, 指定了一个参考 点(referrence point). North, South, East, West 指定参考点於 於相对应的外围边缘(outer border; 视窗的外框线)的中心点. NorthWest, NorthEast, SouthWest 和 SouthEast 指定对应的角落为 参考点(referrence point). Center 则指定视窗之中央为 referrence point. 若是指定 Static 以外的 PWinGravity 值, 则 window manager frame 的 referrence point 将会位於 client window 从 Withdrawn state 变成其它 state 时, client window 的 referrence point 的 位置. 即 client window 的位置会适当的位移(shift), 以使的 frame 的 referrence point 能置於原本 client window referrence point 的位置.

WM_HINTS
WM_HINTS property 的 type 为 WM_HINTS, 提供 window manager 除了位置大小和名称以外的资讯, 让 client 能向 window manager 进行一些视窗行为的建议. 
--------------------------------------------------------------------------------
 
Field Type Comment 

--------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------
 
flags CARD32 请见下表 
input CARD32 The client's input model 
initial_state CARD32 第一次 map 时的状态 
icon_pixmap PIXMAP icon 的图形 
icon_window WINDOW 显示 icon 图形的视窗 
icon_x INT32 icon 位置的 x 座标 
icon_y INT32 icon 位置的 y 座标 
icon_mask PIXMAP icon 形状的 mask 
window_group WINDOW group leader 的 window ID 

--------------------------------------------------------------------------------
 
下表定义 WM_HINTS.flags bits: 
--------------------------------------------------------------------------------
 
Name Value Field 

--------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------
 
InputHint 1 input 
StateHint 2 initial_state, 参考下表 
IconPixmapHint 4 icon_pixmap 
IconWindowHint 8 icon_window 
IconPositionHint 16 icon_x & icon_y 
IconMaskHint 32 icon_mask 
WindowGroupHint 64 window_group 
MessageHint 128 (obsolete) 
UrgencyHint 256 urgency 

--------------------------------------------------------------------------------
 
initial_state: 
--------------------------------------------------------------------------------
 
State Value Comment 

--------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------
 
NormalState 1 视窗内容可见 
IconicState 3 视窗在 icon 状态 

--------------------------------------------------------------------------------
 
WM_HINTS.flags 指定 client 设定的 WM_HINTS property 栏 位. 上面第二表定义各栏位的对应 flag bit.

input 栏位的值为 True or False. 当使用者点选视窗时, 通常 window manager 会将 Focus 转移至 user 点选的视窗, 以供使 用者在该视窗进行输入资料的动作. input 栏位若为 True, window manager 就会主动将 focus 设定在 user 指定的 top window. 若 input 栏位为 False, 则 window manager 将不会设定 focus 至 该 top window. 因此, 一些 input only 的视窗, 例如小时钟之类的 程式, 就不需要 focus, 可以将 input 栏位设为 False. 虽然 window manager 不主动转移 focus 至 input 栏位为 False 的 top window, 但是 client 还是可以自行主动进行 focus 的转移, 取得 focus.

initial_state 可为 NormalState 或 IconicState. 为 NormalState 时, top window 第一次 map 会进入 normal state. 为 IconicState 时, 则会进入 iconic state.

icon_pixmap 指定该视窗的 icon 图形. 关於 icon pixmap 有几个规定: 
若有 WM_ICON_SIZE property 存在於 root 视窗, 则 pixmap 必需是 WM_ICON_SIZE 所指的其中一个大小. 
pixmap 的 deep 必需为 1 bit. 
icon_mask 用来指定 icon 的形状, 去除不必要的陪份, 让 icon 不只是 矩形, 也可以是不规则形.

也许你希望使用自己指定的视窗做为 icon, 那麽你就必需将 window id 设定於 icon_window. 如此, client 可以自行控制 icon 行, 做一些不同 的变化.

flags bit, UrgencyHint 告知 window manager 视窗的内容是属於紧急 性的, 如此 window manager 可以做特别的处理, 以提醒 user. UrgencyHint 状态可以随时改便, 随时 set or unset. 当一 top window 离开 Withdrawn state 後, window manager 就必需随时注意 UrgencyHint 的变化, 以及时做出反应.

WM_CLASS
WM_CLASS property 的 type 为 STRING, 包含两个连续, null-terminated 的字串, 分别为 application 的 instance name 和 class name. 这两个 name 用於 window manager 或 client 存取 application 的 resource 时使用, 做为识别名称(identify). 关於 resource 在往後 的章节另有说明.

Selection
在 X 环境, 两个 client 之间要如何交换讯息呢? client 之间不能 像一般的程式一样, 开个共同存取的档案, 而且 client 可能各自使用 不同的协定和 X Server 通讯, 因此不能假定能透过一般的方式和其它 client 沟通. X client 之间主要的通讯方式是透过 selection 机制. Atom 在这也扮演 selection 的名称, 做为存取的媒介.

selection 主要是用於 client 之间传输资料, 如: 常用於视窗之间的 copy & paste 动作. 我们先在 A 视窗 mark 要 copy 的资料, 然於 再於 B 视窗 paste, 於是 A 视窗成了资料的拥有者, B 视窗成了资 料的要求者. 当在 A 视窗完 mark 资料的动作之後, A 视 窗取得一个 selection S 的拥有权, 成为 S 的拥有者, 等待资料要求者的要求. 然後我们在 B 视窗进行 paste 动作时, B 视窗就向 S 进行要求(SelectionRequest), 这个对 selection S 的要会转送到 S 的拥有者 A 视窗. A 视窗 收到要求後就将资料传送给 B 视窗. 接我们又在 C 视窗进行 paste 动作, 同样的 C 视窗也对 S 进行要求, 而 A 视窗则继续服务 对 S 提出的要求, 直到其它视窗夺走(取得) S 的拥有权, 或着 A 视 窗自动放弃 S 的拥有权.

Owner & Requestor
系统内可以有多个 selection 存在, 每个 selection 有自已的名称. 这些 selection 是整个 display 共用的, 除了系统预定的 selection , client 之也可以定义自己的 selection. 每个 selection 可以有一 个拥有者(owner)视窗, 但 selection 不一有拥有者. 当视窗准备好 资料, 视窗的拥有者 client 透过 XSetSelectionOwner() 函数宣告视窗 成为 selection 的新拥有者. 若 selection 原本就有一个拥有者, 在改变拥有者时, 原拥有者会得 SelectionClear event, 得知不再拥有 该 selection.

Selection 的要求者(requestor)则透过 XConvertSelection() 函数对 selection 进 行要求, 这时拥有者会收到 SelectionRequest event. SelectionRequest 包含几个参数, selection, target, property, 和 requestor. target 指定要求的资料形态, 例如 INTEGER, PIXMAP, AnyPropertyType. 拥有者 将资料转换成 target 指定的 type, 然後才传送给 requstor 指定的视窗. 传送流程 中, 若拥有者有能力提供 target 指定的资料 type, 则拥有者将资料写入 SelectionRequest 指定的 property, 然後拥 有者传送 SelectionNotify 给 requestor, 告知资料己经备妥. 拥有者必需设定 SelectionNotify 的 selection, target, property 和 time 等参数. 这些参数必需和 SelectionRequest 得到的对应参 数相同. 若拥有者无法提供 target 指定的资料 type, 或者无法顺利写入 property, 则 SelectionNotify 的 property 参数需设为 None, 以示无法提供资料.

要求者在收到 SelectionNotify 之後, requestor 就从 SelectionNotify 指定的 property(!= None) 读取资料, 最後将 property delete (透过 XGetWindowProperty, delete=True). property 被 delete 之後, 拥有者会得到 PropertyNotify, 以得知 requestor 己传完资料. 拥有者在得知资料 己传送完毕前, 必需保持资料的完整性, 直到传送完成之後, 才可以 对使用者做回馈反应. 拥有者(owner)为了要在最後能收到 PropertyNotify 必需在传送 SelectionNotify 之前, 对 requestor 视窗的 PropertyNotify 表示兴趣(XSelectInput), 才能正确的收到 event.

要求者若要求一个没有拥有者(owner)的 selection 时, 这明显的得不到 任何资料, X Server 会自动产生一个 property=None 的 SelectionNotify. 

Request selection 的流程: 
要求者: XConvertSelection() 
拥有者: 
收到 SelectionRequest 
将资料转换成 target 指定的 type 
然後将资料 replace property 的资料, 传送 SelectionNotify 要求者. 
要求者: 
收到 SelectionNotify 
读取 property 并 delete property. 
要求者完成所有步骤. 
拥有者: 
收到 PropertyNotify(state=Deleted). 
拥有者完成 request 的 service. 
下面是本章各 event 的结构: 
--------------------------------------------------------------------------------

typedef struct {
    int type;
    unsigned long serial;   /* # of last request processed by server */ 
    Bool send_event;    /* true if this came from a SendEvent request */
    Display *display;   /* Display the event was read from */
    Window owner;
    Window requestor;
    Atom selection;
    Atom target;  
    Atom property;
    Time time;
} XSelectionRequestEvent;


--------------------------------------------------------------------------------


Structure of SelectionNotify: 
--------------------------------------------------------------------------------

typedef struct {
    int type;   
    unsigned long serial;   /* # of last request processed by server */
    Bool send_event;    /* true if this came from a SendEvent request */
    Display *display;   /* Display the event was read from */
    Window requestor;
    Atom selection;  
    Atom target;     
    Atom property;      /* ATOM or None */
    Time time;
} XSelectionEvent;


--------------------------------------------------------------------------------



--------------------------------------------------------------------------------

typedef struct {
    int type;
    unsigned long serial;   /* # of last request processed by server */
    Bool send_event;    /* true if this came from a SendEvent request */
    Display *display;   /* Display the event was read from */
    Window window;
    Atom selection;
    Time time;
} XSelectionClearEvent;


--------------------------------------------------------------------------------


Structure of PropertyNotify: 
--------------------------------------------------------------------------------

typedef struct {
    int type;
    unsigned long serial;   /* # of last request processed by server */
    Bool send_event;    /* true if this came from a SendEvent request */
    Display *display;   /* Display the event was read from */
    Window window;
    Atom atom;
    Time time;
    int state;      /* NewValue, Deleted */
} XPropertyEvent;


--------------------------------------------------------------------------------


例 1
程式
signature

传输大量资料 - INCR
利用 property 做为传输媒介时, 由於 property 是属於系统资源, 当 资料量大时, 耗用系统大量的资源, 因此实在不适合一次塞进这麽多资 料, 造成系统资源的使用效率低落. 因此 selection 在 ICCCM 提供 了一个传送大量资料时的成规.

当 selection 传送大量资料的完整流程如下: 
拥有者传送 property type 为 INCR 的 SelectionNotify 给拥有 者, type INCR (incrementally) 是一种整数, 记录整个 selection 的资料大小. 
要求者收到 SelectionNotify, property 的 type 为 INCR, 这时 要求者可以从 property 取得整个 selection 的资料量(integer) ; 以byte 为单位计算. 并 delete property. delete property 会造生 PropertyNotify(state=Deleted). 
拥有者收到 PropertyNotify(state=Deleted), 将一小部分还未传输 的资料附加(append)到 property. 这个 property 和前面的 SelectionNotify 的 property 相同. 由於 property 改变, 这导致 PropertyNotify(state=NewValue) event. 
要求者收到 PropertyNotify(state=NewValue) event, 从 property 读取资料, 并 delete property 产生 PropertyNotify(state= Deleted). 
goto step 3 until 没有未传资料. 
拥有者收到 PropertyNotify(state=Deleted)对 property append 长度 0 的资料. 
要求者收到 PropertyNotif(state=NewValue), 从 property 读取 资料长度 0, 并 delete property 产生 PropertyNotify(state= Deleted). 要求者完成 selection request. 
拥有者收到 PropertyNotify(state=Deleted), 完成对 selection request 的 service. 
Selection Atom
前面说过, selection 可以由 client 自行定义, 每一个 selection 都以 atom 命名. X 环境定义三个 selection atom, 让各种 client 可以遵循, 让各种不特定的 client 能够相互沟通. 
PRIMARY 
主要用於当作 command 的第一个 selection 参数 
SECONDARY 
当作需要两个 selection 参数的 command 的第二个参数, 或其它原因不愿使 用 PRIMARY 时使用. 
CLIPBOARD 
做为一般 copy & paste 动作使用. 
在 ICCCM 说道, 习惯上, 一般的 client 只也支援上面三个 selection, 反过来说, client 至少要能支援上面三个 selection. 否则很难 和其它 client 做 inter-client 的通讯.

例 2
程式
signature

DELETE INSERT_SELECTION INSERT_PROPERTY 
Functions of Selection

--------------------------------------------------------------------------------

  XSetSelectionOwner(display, selection, owner, time)
Display *display;
Atom selection;
Window owner;
Time time;


--------------------------------------------------------------------------------

--------------------------------------------------------------------------------

  Window XGetSelectionOwner(display, selection)
Display *display;
Atom selection;


--------------------------------------------------------------------------------

--------------------------------------------------------------------------------

  XConvertSelection(display, selection, target, property,
  requestor, time)
Display *display;
Atom selection, target;
Atom property;
Window requestor;
Time time;


--------------------------------------------------------------------------------

Client Message

posted on 2007-05-18 15:37 zmj 阅读(2669) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理