X Window 程式设计入门

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;
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

(http://www.fanqiang.com)
    进入【UNIX论坛

相关文章
X Window 程式设计入门--第六章 Inter-Client Communication (2001-06-02 18:08:00)
X Window 程式设计入门--第五章 Window (2001-06-02 00:16:10)
X Window 程式设计入门--第四章 Event (2001-06-01 21:04:00)
X Window 程式设计入门--第三章 绘图(Graphic) (2001-06-01 20:10:00)
X Window 程式设计入门--第二章 X Programming 的第一步 (2001-06-01 19:00:01)
X Window 程式设计入门--第一章 什麽是 X Window (2001-06-01 18:08:00)
X Window 程式设计入门 (2001-06-01 17:04:00)
Atom property;
Window requestor;
Time time;


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

Client Message

(http://www.fanqiang.com)
    进入【UNIX论坛

相关文章
X Window 程式设计入门--第六章 Inter-Client Communication (2001-06-02 18:08:00)
X Window 程式设计入门--第五章 Window (2001-06-02 00:16:10)
X Window 程式设计入门--第四章 Event (2001-06-01 21:04:00)
X Window 程式设计入门--第三章 绘图(Graphic) (2001-06-01 20:10:00)
X Window 程式设计入门--第二章 X Programming 的第一步 (2001-06-01 19:00:01)
X Window 程式设计入门--第一章 什麽是 X Window (2001-06-01 18:08:00)
X Window 程式设计入门 (2001-06-01 17:04:00)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值