
第六章 Inter-Client Communication
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]
|