[ 永遠的UNIX::UNIX技術資料的寶庫 ]   GB | BIG5

首頁 > 編程技術 > 其它 > 正文
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

(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)
 

★  樊強制作 歡迎分享  ★