X_Window_核心協議

**X Window 核心協定**是 X 視窗系統的基礎協定，它是一個以點陣圖顯示的網路化視窗系統，用來在 Unix、類 Unix 和其他作業系統上建立使用者圖形介面。X Window 系統基於主從式模型：單一伺服器控管硬體的輸出入，如螢幕、鍵盤和滑鼠；所有的應用程式都被視作客戶端，使用者之間透過伺服器來互動。互動部分由 X Window 核心協定來管理。還有其他與 X 視窗系統有關的協定，有的建立在 X Window 核心協定之上的，有的是獨立的協定。

在 X Window 核心協定中，只會在網路上以非同步方式傳送四種封包：請求、回應、事件和錯誤。**請求**是由客戶端傳送到伺服器，告之進行一些動作（例如建立一個視窗），並回傳以便持握的資料。**回應**是由伺服器回傳的若干資料。**事件**是由伺服器傳送的，其用來通知客戶端某些使用者的動作，或者發生了其他所關心的事件。**錯誤**是由伺服器傳送的封包，其用來通知客戶端，在處理其請求時，發生了一些錯誤。請求有可能產生回應、事件和錯誤；除此之外，協定並不要求封包中的特定指令要以網路來傳送。還有其他對核心協定的擴充，這些擴充有自己的請求、回應、事件和錯誤。

X Window 源於 1984 年的麻省理工學院（目前所發佈的 X11 發表於 1987 年 9 月）。設計者鮑伯・斯凱夫勒（Bob Scheifler）和吉姆・傑提斯（Jim Gettys）早期對核心協定的原則是 **「機制，而非策略」** ，所以核心協定並未規定客戶端之間以及客戶端和使用者之間的互動介面規範。這部分則由其他的獨立規格所規範，如 ICCCM、freedesktop.org 規範，且可由所使用的特定組件工具包自動強制執行。

## 概觀

伺服器和客戶端之間的通訊，是由通道上的交換封包所完成。由客戶端建立連線，且由客戶端發送第一個封包。封包中包括將要使用的位元組序、協定版本方面的資訊，以及客戶端期望伺服器使用的認證種類。伺服器以回傳封包來答覆，封包中陳述接受或拒絕連線，或要求進一步的驗證。如果接受連線，接受封包內會包含客戶端接下來和伺服器互動所需的資料。

建立連線之後，在客戶端和伺服器的通道上，會有四種交換封包的類型：

1. **請求**：客戶端請求伺服器的資訊，或者請求伺服器執行一個動作。
2. **回應**：伺服器回應請求。但並非所有的請求都會產生回應。
3. **事件**：伺服器傳送事件給客戶端。如：鍵盤或滑鼠的輸入，或移動、調整、顯示視窗等。
4. **錯誤**：如果請求無效時，伺服器會傳送一個錯誤封包。因為請求是以排隊方式處理，所以經由請求所產生的錯誤封包，並不會立即傳送出去。

請求和回應封包可以有各種長度，事件和錯誤封包的長度則固定是 32 位元組。

請求封包的編號順序是以伺服器的接收為順序：來自客戶端的第一個請求編號為 1、第二個編號為 2，依此類推。請求的序列編號中最小的有效 16 位元，包含在由請求所產生的回應和錯誤封包之中，如果有的話。它們也包含在事件封包中，以指出伺服器正在處理或是剛剛完成的請求序列編號。

## 視窗

在 X 視窗系統以及各種圖形化使用者介面中，視窗即為一個**頂層視窗**。視窗也用來指視窗內部的視窗，這類視窗是父視窗的子視窗。圖形化元件，如按鈕、選單、圖示等等，都是使用視窗來實現的。

客戶端可請求建立一個視窗。更嚴謹的說，客戶端可請求建立現存視窗的子視窗。所以客戶端所建立的視窗，皆以樹狀結構組織（階層結構）。樹狀結構的根即為根視窗，根視窗是伺服器在啟動時，所自動建立的特殊視窗。其餘視窗都是根視窗的子視窗，頂層視窗就是根視窗下的第一個子視窗。如圖所見，根視窗和螢幕同等大小，且在其餘視窗的後面（被子視窗遮蓋住）。

視窗裡的內容並非在所有時候都能顯示出來。更精確地說，在視窗移動、調整大小、被其他視窗遮蓋、部分或整個視窗不可見時，視窗裡的內容就有可能會被銷毀。更精確地說，如果 X 伺服器無法維護視窗內容的**後備存放區**（backing store）時，這些內容就會遺失。客戶端可請求為視窗進行維護的後備存放區，但伺服器沒有義務要這樣做。因此，客戶端不可假設已得到後備存放區的維護。若視窗有一部分未指出內容時，就會傳送一個事件，通知客戶端重繪那部分內容。

每個視窗都關聯一組**屬性值**（Attribute），如視窗的幾何性質（大小和位置）、背景圖、是否請求了後備存放區等等。協定中還包含用來給客戶端檢閱和改變視窗屬性值的請求。

視窗可以是 `InputOutput`（輸出／輸入）或 `InputOnly`（僅輸入）。前者是顯示在螢幕上用於繪圖的視窗，而後者並不顯示在螢幕上，僅用來接受輸入。

平常可看到視窗周圍的裝飾性框架和標題列（可能含有按鈕），是由視窗管理器所建立的視窗，而非客戶端所建立的。視窗管理器也處理與元件有關的輸入，例如當使用者點擊並拖曳視窗的邊框時，便會調整視窗大小。客戶端所建立的視窗，通常可以忽略視窗管理器所帶來的變化。還有一個改變必須注意，那就是改變親屬關係的視窗管理器，幾乎所有新式的視窗管理器，都會將頂層視窗的親屬關係改變到一個視窗（不是根視窗）裡去。從核心協定的角度來看，視窗管理器是一個客戶端，與其他的應用程式沒有區別。

關於視窗的資料，可執行 `xwininfo` 程式來取得。加上 `-tree` 命令列參數，程式便會顯示子視窗的樹狀結構，連同識別子和幾何性質資料一起顯示。

## 圖形對映和可繪區

圖形對映（pixmap）是記憶體中可用來繪圖的區域。與視窗不同，圖形對映的內容並不會自動顯示在螢幕上。不過圖形對映的內容（或部分內容）可轉換到視窗上，反之亦然。這就讓雙緩衝得以實作。大部分可在視窗上完成的圖形化操作，也可以圖形對映完成。

視窗和圖形對映被統稱為**可繪區**（drawable），且其資料內容都保留在伺服器上。客戶端可請求從伺服器上，將可繪區的內容轉換到客戶端，反之亦然。

## 圖形脈絡和字體

客戶端可請求很多種圖形運算，如清空一塊區域、複製一塊區域到另一處，繪製一個點、線、矩形和文字。對清空而言，所有運算都有可能用在可繪區上（視窗和圖形對映）。

**圖形脈絡**（**g**raphic **c**ontext）包括了對圖形運算的大部分請求，圖形脈絡是一種結構，包含有圖形運算的參數。圖形脈絡包含前景色、背景色、文字的字體，以及各種圖形參數。當請求圖形運算時，客戶端就包含一個圖形脈絡。很明顯的，並非所有的圖形脈絡參數都會參與運算：例如，字體對於直線的繪製不產生作用。

核心協定規格使用了伺服器側的字體。如字體是以檔案形式存放，伺服器經由本機的檔案系統直接存取，或經由網路從**字體伺服器**存取。客戶端可向伺服器請求有效的字體列表，且可請求伺服器載入（沒有的話）或解除安裝（客戶端不再需要的話）字體。客戶端可請求關於字體的資訊（例如，ascent 字體），並以指定的字體來繪製指定的字串。

在 X Window 核心協定的層次上，字體的名稱可以是任意的字串。X 邏輯字體描述協定規範了如何根據字體的屬性來命名。這些協定也規範了可附屬於字體的選用屬性之值（value）。

`xlsfonts` 程式可輸出存放在伺服器上的字體列表。`xfontsel` 程式可顯示字體的標記，並讓使用者選取字體的名稱，以在其他視窗中貼上。

目前已不再重視伺服器側字體的使用，而轉向客戶端側字體的使用。例如，藉由支援 Xft 或 cairo 程式庫，以及 XRender 擴充，改由客戶端（而非伺服器）繪製字體。客戶端側字體在核心協定中尚未給出規範。

## 資源和識別子

所有關於視窗的資料、圖形對映、字體等等，皆存放在伺服器上。客戶端知道那些物件的識別子，和伺服器互動時，物件以整數為名稱。例如，當客戶端希望建立一個視窗時，便指定一個識別子，並請求伺服器建立一個視窗。伺服器會建立一個視窗，並與指定的識別子關聯。稍後客戶端可使用這個識別子進行請求，例如在視窗上畫上一個字串。以下存在於伺服器上的物件，客戶端可藉由數值型的識別子得知：

* 視窗（`Window`）
* 圖形對映（`Pixmap`）
* 字體（`Font`）
* 色彩對映（`Colormap`）（即顏色表，稍後描述）
* 圖形脈絡（`Graphic context`）

這些物件就稱作**資源**。當客戶端請求建立某一種資源時，同時也為資源指定了一個識別子。例如，為了建立一個新視窗，客戶端指定了視窗的屬性值（親屬關係、寬、高等等）和識別子，最後識別子會和視窗關聯。

識別子是三個最高有效位為 0 的 32 位元整數。每一個客戶端都有一組自己的識別子，其可用來建立新的資源。這組識別子是由伺服器以包含在接受封包（傳送給客戶端的封包，通知已接受連線）中的兩個整數所指定的。客戶端以避免衝突的方式選取識別子：在視窗、圖形對映、字體、色彩對映、圖形脈絡之中的兩個物件，不可具有相同的識別子。

資源一經建立，其識別子就用於客戶端向伺服器請求與之有關的運算。部分運算會影響特定的資源（例如，請求移動視窗），其他的則要求存放在伺服器上的資源資料（例如，請求視窗的屬性值）。

識別子在伺服器上是獨一無二的，在多個客戶端之間也不例外。例如，即使是由兩個不同客戶端所建立的視窗，也不會同時具有相同的識別子。即使某個物件不是由自己的客戶端所建立的，只要指定相對應的識別子，就可存取另一個客戶端所建立的任何物件。

連線到同一伺服器的兩個客戶端，對同一資源可使用同一識別子。例如，若客戶端建立一個 `0x1e00021` 識別子的視窗，並傳送數值 `0x1e00021` 給其他的應用程式（透過任何有效的手法。例如，把數值存放在檔案裡，且這個檔案可讓其他的應用程式輕易存取），其他的應用程式即可對同一視窗進行操作。這個例子是來自 X Window 版本的 Ghostview：程式建立一個子視窗，在環境變數中存放其識別子，並呼叫 Ghostscript；程式繪製 PostScript 檔案的內容，以顯示在這個視窗上。

當建立資源的客戶端關閉與伺服器的連線時，資源就會正常的銷毀。不過在關閉連線之前，客戶端可以請求伺服器不要銷毀資源。

## 事件

事件是由伺服器傳送到客戶端用以通訊的封包，傳送一些客戶端可能感興趣的事情。例如，當使用者按下按鍵或點擊滑鼠時，便會傳送一個事件。事件不只用於輸入：例如，傳送的事件表明特定視窗建立了新的子視窗。

每一個事件都會涉及到視窗。例如，當使用者的滑鼠在視窗之內並點擊時，這個事件就會涉及到那個視窗。事件封包中含有那個視窗的識別子。

客戶端可以請求伺服器傳送事件給另一個客戶端，這可用於客戶端之間的通訊。例如，當客戶端請求目前所選取的文字時，就會傳送事件給客戶端，以處理目前所持有的選取內容。

當再度觀看內容已被銷毀的區域時，有可能會傳送 `Expose`（顯露）事件。而且在某些情況下，視窗的內容可能會被銷毀。例如，當視窗被其他視窗遮蓋住，且伺服器沒有維護後備存放區時。此時伺服器會產生一個 `Expose` 事件，以通知客戶端重繪視窗已消失的部分。

大部分的事件只會在客戶端預先表示關心時才會傳送。因為客戶端可能只需要關心某類型的事件。例如，客戶端可能會關心關於鍵盤的事件，但卻不關心關於滑鼠的事件。即使在客戶端並未明確請求的情況下，某幾類事件也會不斷的傳送給客戶端。

客戶端可以設定視窗的屬性值（attribute），以指明想要接收哪些事件。例如，當視窗的內容已銷毀時，為重繪其內容，客戶端就必須接收 `Expose` 事件，以通知視窗需要再次重繪。客戶端要能接收到 `Expose` 事件，就要預先指明它所關心的事件，這部分可以適當設定視窗屬性值的**事件掩碼**來完成。

不同的客戶端可以請求同一視窗的事件。甚至可對同一視窗設定不同的事件掩碼。例如，某個客戶端可以只對視窗請求鍵盤事件，而另一個客戶端只對視窗請求滑鼠事件。這是可以的，因為伺服器會為每一個客戶端維護事件掩碼，而且是每一個視窗都維護一份獨立的事件掩碼。不過偶爾也有某幾類事件，只能由一個客戶端選擇。特別是，這類事件回報滑鼠按鈕的點擊，且部分變化會涉及到視窗管理器。

`xev` 程式顯示視窗所涉及到的事件。`xev -id WID` 可對識別子為 `WID` 的視窗要求所有可能的事件，並將其輸出。

## 範例

以下是伺服器和程式之間的互動範例，這個程式會建立一個黑框視窗，按下按鍵後結束。在本例中，伺服器並未傳送任何回應，因為客戶端的請求並不產生回應。這些請求有可能產生錯誤。

1. 客戶端開通與伺服器的連線，並傳送初始化封包，來指定所要使用的位元順序。
2. 伺服器接受連線（本例中不涉及驗證）並傳送適當的封包。這些封包含有其他的資訊，如根視窗的識別子（例如，`0x0000002b`），以及客戶端可建立哪些識別子。
3. 客戶端請求以 `0x00200000` 識別子建立一個預設的圖形脈絡（此一請求如同本例中的其餘請求，並不會產生來自伺服器的回應）。
4. 客戶端請求伺服器以 `0x00200001` 識別子、大小 200x200、位置 (10,10) 等等，來建立一個頂層視窗（這部分指定以根視窗 `0x0000002b` 為父視窗）。
5. 客戶端請求改變視窗 `0x00200001` 的屬性值（attribute），規定視窗要接收 `Expose`（顯露）和 `KeyPress`（按鍵）事件。
6. 客戶端請求對映（顯示在螢幕上）視窗 `0x00200001`。
7. 當視窗可見，且必須繪出其內容時，伺服器傳給客戶端一個 `Expose`（顯露）事件。
8. 客戶端對事件做出反應，請求繪製一個方框，它是透過傳送與視窗 `0x00200001` 和圖形脈絡 `0x00200000` 一起的 `PolyFillRectangle` 請求來達成。

如果視窗被其他視窗遮蓋住，且再次顯露出來時，又剛好沒有這部分的備存時：

1. 伺服器傳送另一種 `Expose`（顯露）事件，告知客戶端必須再次重繪視窗。
2. 客戶端透過傳送 `PolyFillRectangle` 請求的方式重繪視窗。

如果按下按鍵：

1. 伺服器傳給客戶端一個 `KeyPress`（按鍵）事件，通知它使用者按下按鍵了。
2. 客戶端作出適當的反應（本例是結束程式）。

## 顏色

在協定層次裡，顏色使用 32 位元無負號整數來表示，稱為**像素值**。以下因素會影響顏色的顯示：

1. 色彩深度
2. **色彩對映**（colormap），即含有紅、綠、藍強度值的表。
3. **視覺類型**（visual type），指明表如何用來表示顏色。

在最簡單的情況下，色彩對映是一種每一項都含有 RGB 三值的表。像素值 `x` 所表示的顏色，就包含在表中第 `x` 項。如果客戶端可以改變色彩對映的內容，那這種表示法就以偽彩色（`PseudoColor`）**視覺分類**（visual class）來標識。視覺分類中的靜態色（`StaticColor`）與偽彩色類似，只不過客戶端不能修改色彩對映的內容。

在此總計有六種可能的視覺分類，每一種都使用不同的方式來表示 RGB 像素值。`PseudoColor` 和 `StaticColor` 即其中兩種。`GrayScale` 和 `StaticGray` 是另外的兩種，其中最主要的差異是灰階漸層的使用。

剩下的兩種視覺分類和前述的差異在於，將像素值分成三個部分，並為紅、綠、藍的強度使用了三個獨立的表。並根據這個表來表示顏色，如下流程將像素值轉換成 RGB 色：

1. 將像素值視為連續的位元序列
2. 將位元序列分成三個部分
3. 將每一個部分視為整數、並作為索引，以在這三個獨立的表中尋找到值。

這個機制需要以三個獨立的表組成色彩對映，三個原色各一個表。轉換以後仍是強度值的三聯色。使用這個表示法的視覺分類有 `DirectColor` 和 `TrueColor`，其中的差異是客戶端不能修改後者的色彩對映。

以上六種以像素值表示顏色的機制，都需要附加一些參數來運作。這些參數可統合為**視覺類型**，其包含一個視覺分類和其他參數以表示顏色。每一個伺服器都有一組固定的視覺類型，每一個類型都與數值型的識別子關聯。其識別子是 32 位元無負號整數，和資源或元素的識別子沒有什麼不同。

當接受來自客戶端的連線時，伺服器所傳送的接受封包裡含有區塊序列，每一個區塊都包含有關於某一個單一螢幕的資訊。就每一個螢幕而言，相關區塊包含著其他區塊的清單，每一個都涉及了螢幕所支援的色彩深度。就每一個螢幕所支緩的色度深度而言，這個清單中又包含視覺類型的清單。結果，每一個螢幕就關聯著各種合適的色彩深度，而且每一個螢幕的每一個色彩深度都關聯著各種合適的視覺類型。一個給定的視覺類型可用於更多的螢幕和各種不同的色彩深度。

就每一個視覺類型而言，接受封包中還包含它的識別子和它所包含的實際參數（視覺分類等），客戶端存放這些資訊，因為之後就不能再請求。此外，客戶端不能改變或建立新的視覺類型。建立新視窗的請求中，還包含有色彩深度和視覺類型的識別子，以此用來表示視窗的顏色。

色彩對映和控制螢幕的硬體（即繪圖卡）是否使用調色盤（palette）沒有關係。調色盤是一個表，也是用來表示顏色的。即使硬體並不使用調色盤，伺服器也能使用色彩對映。當硬體使用調色盤時，就只能安裝相當受限的少許色彩對映。更精確地說，當硬體根據色彩對映來顯示顏色時，就可以說是安裝了色彩對映。客戶端可請求伺服器安裝一個色彩對映。不過需要將另一個色彩對映解除安裝：其影響是視窗如果使用了解除安裝的色彩對映，就不能顯示正確的顏色，而出現怪異顏色的畫面。這個問題可以用**標準色彩對映**來解決，標準色彩對映是像素值和顏色之間關聯恆定的色彩對映。由於有這一性質，就可讓不同的應用程式使用標準色彩對映。

色彩對映的建立由 ICCCM 協定管理。標準色彩對映由 ICCCM 和 Xlib 規格管理。

## 元素

元素（Atoms）是用來表示字串的 32 位元整數。本協定的設計者之所以引入元素，是為了以簡短、大小恆定的方式表示字串：由於字串可以是任意的長度，而元素總是 32 位元整數。某些封包可能會多次傳送相同的字串，元素的簡短性可以削減指令所使用的封包長度，進而增進網路的使用效率。大小恆定的元素有利於大小恆為 32 位元組的事件：大小恆定的封包可以包含元素，卻不能包含過長的字串。

更嚴謹的說，元素是存放在伺服器上的字串的識別子，相當於資源的識別子（視窗、圖形對映等），但仍有兩個不同點。首先，元素的識別子是由伺服器選擇的，而非客戶端。換句話說，當客戶端請求建立一個新的元素時，其僅僅傳送字串給伺服器存放，而非字串的識別子；元素的識別子是由伺服器選擇，並回傳給客戶端作為回應。其次，資源和元素之間的重大差異是，元素並未與客戶端相連繫。元素一經建立，就能一直存留至伺服器結束或重設（此非資源的預設行為）。

元素是識別子，所以也是獨一無二的。不過元素和資源的識別子可以相一致。與元素關聯的字串稱作**元素名**。元素的名稱一經建立就不能再更改，而且不能有兩個相同名稱的元素。故一般以元素的內容作為元素的名稱：「元素 `ABCD`」意謂著，或更精確地說，「這個元素關聯的字串是 `ABCD`」或「元素的名稱是 `ABCD`」。客戶端可以請求建立新的元素，且可請求指定字串的元素（識別子）。某些元素已**預先定義**（由伺服器以特定的識別子和字串所建立）。

元素運用於多個目的，主要與連接到同一伺服器的不同客戶端之間有關。特別是，元素用於與視窗屬性的關聯，以下詳述。

所有存在於伺服器上的元素列表，可使用 `xlsatoms` 程式輸出。尤其這個程式可以元素的名稱（元素所關聯的字串）列出每一個元素（識別子是一串數字）。

## 屬性

每一個視窗都有一組預先定義的屬性值（Attribute）和屬性（Property），並存放在伺服器上，客戶端可以適當的請求方式取存。屬性值是有關視窗方面的資料，如視窗的大小、位置、背景色等等。屬性則是附屬於視窗上的資料片斷。與屬性值相反，屬性在 X Window 核心協定的層次中並沒有其他含意。客戶端可在視窗的屬性中存放屬性值資料。

屬性是以名稱、類型和值來描述，屬性相當於指令式程式語言的變數，應用程式可以指定名稱、類型和值來建立新的屬性。屬性和視窗關聯：兩個相同名稱的屬性可存在於兩個不同的視窗，且可具有不同的類型和值。

屬性的名稱、類型和值皆為字串；更精確地說就是元素，客戶端可藉由識別字，以存取存放在伺服器上的字串。客戶端應用程式可以元素的識別子（含有屬性的名稱）存取特定的屬性。

屬性大多用於客戶端之間的通訊。例如，名稱為 `WM_NAME`（屬性名稱就是元素所關聯的字串 `"WM_NAME"`）的屬性，是用來存放視窗的名稱；視窗管理器通常會讀取這個屬性，並在視窗頂部顯示名稱。

某些客戶端之間的通訊也使用了根視窗的屬性。例如，根據 freedesktop 視窗管理器規格視窗管理器應該在根視窗的 `_NET_ACTIVE_WINDOW` 屬性名中存放目前有效（active）視窗的識別子。X 資源所含有的程式參數，同樣也是存放在根視窗的屬性裡；藉由這個方式，即使分別執行在不同電腦上，所有客戶端仍可存取到那些參數。

`xprop` 程式可輸出指定視窗的屬性，`xprop -root` 則可輸出根視窗每一個屬性的名稱、類型和值。

## 對映

在 X 視窗系統中，鍵盤上的每一個物理按鍵都關聯有 8～255 之間的號碼，這些號碼就稱作**鍵碼**（keycode）。一個鍵碼僅僅標識一個按鍵，而非特定的字元或功能鍵（如「Page Up」）。字元或功能鍵則由**鍵符**來標識。鍵碼僅僅取決於實際按下的按鍵，鍵符則可取決於按下的 Shift 鍵或其他的修飾鍵。

當按下或放開按鍵時，伺服器會傳送 `KeyPress` 或 `KeyRelease` 的事件類型給適當的客戶端。這些事件包含：

1. 按下按鍵所產生的鍵碼。
2. 修飾鍵（Shift、Control 等）和滑鼠按鍵當下的狀態。

因此伺服器傳送鍵碼和修飾狀態，而無須嘗試將其轉成特定的字元，這部份的轉換工作由客戶端自己的協定來完成。例如，客戶端可能接收到一個事件，而這個事件表示已按下「a」鍵，且 Shift 修飾鍵也已按下，客戶端（不是伺服器）就會把這個事件關聯為「A」。

客戶端完成鍵碼至鍵符的轉換以後，表示其關聯的表就由伺服器來維護，並將表集中存放在所有客戶端都存取得到的地方。客戶端只需請求對映，並使用它對鍵碼和其修飾鍵域解碼成鍵符。客戶端也可以任意改變此一對映。

當按下修飾鍵時，就會改變另一個按鍵的解碼。常見的修飾鍵有 Shift 鍵：平常會產生小寫字母「a」的 a 鍵和 Shift 鍵一起按下時，就會產生一個大寫字母「A」。其他常見的修飾鍵還有「Control」、「Alt」和「Meta」。

X 伺服器最多可用八個修飾鍵，不過每個修飾鍵可關聯一個以上的按鍵。例如，很多鍵盤有兩個「Shift」鍵（一左一右）。按下這兩顆按鍵時，會產生兩個不同的鍵碼，不過 X 伺服器會把那兩者都關聯到「Shift」修飾鍵。

X 伺服器為八個修飾鍵維護一份可以認出修飾鍵的鍵碼清單。舉個例子，如果清單中的第一個修飾鍵（「Shift」修飾鍵）包含鍵碼 `0x37`，然後有某個按鍵會產生鍵碼 `0x37`，X 伺服器就認為那個按鍵是 Shift 鍵。

修飾鍵對映的清單由 X 伺服器維護，也可以讓所有的客戶端修改。例如，客戶端可以請求將「F1 鍵」加到「Shift」修飾鍵的清單中。從此，這顆按鍵的效果就如同 Shift 鍵一樣，不過 F1 仍會產生本身的鍵碼。結果，F1 仍做它以前所做的（例如，按下 F1 時，會開啟說明視窗），不過又和 Shift 一樣（在文字編輯器裡，按下「a」和 F1，就會打出「A」）。

X 伺服器也為滑鼠按鍵維護並使用修飾鍵對映，不過只能改變順序。其最大的用處就是為左撇子調換左右兩邊的按鍵。

`xmodmap` 程式可以顯示並改變按鍵、修飾鍵和滑鼠按鍵的對映。

## 截取

**截取**（grab）即所有鍵盤或滑鼠的事件，都傳送到單一客戶端上。客戶端可以請求鍵盤、滑鼠或兩者的截取：如果伺服器履行此一請求，所有鍵盤和滑鼠的事件，都會傳送到截取中客戶端，直到解除截取為止。此時，其他的客戶端將接收不到這些事件。

請求截取時，客戶端會指定一個**截取視窗**：所有事件都會傳送到截取中客戶端，彷彿和截取視窗相關。不過其他的客戶端接收不到事件，即使是在截取視窗中進行選取。在此有兩種截取：

**主動式**
: 立即進行截取。

**被動式**
: 只在按下預先指定的按鍵（或滑鼠按鍵）時才會進行截取，並在放開時結束。

客戶端可截取鍵盤、游標或兩者。截取請求中也可包含用來**凍結**鍵盤或滑鼠的請求。截取和凍截的不同處在於，截取改變事件的接收者，而凍結只是完全停止遞送。當裝置凍結時，它所產生的事件會存放在佇列中，並在結束凍結時照常遞送。

對於游標事件來說，額外的參數會影響到事件的遞送：事件掩碼指定要遞送或拋棄哪些類型的事件。

截取請求中還包含一組欄位，欄位用來在還沒建立截取之前，就指明傳送給截取中客戶端的事件將要發生什麼。更精確地說，客戶端可請求它們照常傳送或進行截取。這兩種情況和它們所表現出來的有所不同。例如，在第一個視窗上正常接收鍵盤事件的客戶端，可能會透過第二個視窗來請求截取鍵盤。事件將會正常傳送給第一個視窗，而未必重新導向給截取視窗，這取決於截取請求裡所下的參數。

客戶端也可請求截取整個伺服器。在這種情況下，伺服器將不會處理任何請求，除了來自截取中客戶端的請求以外。

## 其他

還有其他的請求和事件，有一種請求是有關視窗之間的親屬關係：客戶端可請求改變視窗的父視窗，或請求關於父視窗的資訊。其他的請求則是關於選取，這部分大多由其他的協定來管理。還有的請求是關於輸入焦點和游標的形狀。客戶端也可請求將資源（視窗、圖形對映等等）的所有者殺死，這會使伺服器終止和那個所有者的連線。最後，客戶端還可傳送空運算請求給伺服器。

## 擴充

X Window 核心協定被設計成可擴充。核心協定指定一個機制用來查詢可用的擴充，以及如何產生擴充的請求、事件和錯誤封包。

更精確地說，客戶溯可以請求所有可用的擴充的清單，擴充的封包相當於核心協定的封包。核心協定指定請求、事件和錯誤封包要包含一個指明其類型的整數（例如，建立一個新視窗的請求是號碼 1）。一部分範圍的整數已保留給擴充。

## 授權

在客戶端和伺服器剛開始建立連線的時候，伺服器的回應可以是接受、拒絕或要求驗證。驗證請求包含要使用的驗證方法的名稱。核心協定並不規範驗證程式，這部分要看所使用的驗證類型，並在伺服器傳送接受或拒絕封包之後結束。

在客戶端和伺服器之間正常互動的時候，只有在涉及**基於主機的存取方式**的驗證才要請求。更精確地說，客戶端可要求啟用這個方式，並請求讀入和改變主機（客戶端）（經授權的連線）的清單。一般的應用程式不使用這類請求；`xhost` 程式使用這類請求，給使用者或 Shell script 存取主機存取清單。基於主機的存取方式被認為是不安全的。

## Xlib 和其他的客戶端程式庫

大部分的客戶端程式藉由 Xlib 客戶端程式庫與伺服器交流。特別是客戶端大多使用 Xaw、Motif、GTK+、Qt 之類使用到 Xlib 的程式庫，方便和伺服器互動。Xlib 有以下作用：

1. Xlib 使客戶端的回應和事件同步化：
  1. Xlib 函式會傳送請求區塊，直到得到合理的回應。換句話說，不使用 Xlib 的 X Window 客戶端可傳送請求給伺服器，並在等待回覆的期間，先做其他的事。不過使用 Xlib 的客戶端只能呼叫 Xlib 函式來傳送請求，並等待回覆。藉此阻斷客戶端額外的動作（除非客戶端在呼叫 Xlib 函式之前，就執行另一個新執行緒）。
  2. 當伺服器傳送的事件不同步時，Xlib 會把客戶端接收到的事件存放在佇列裡，客戶端程式只能以明確呼叫 X11 程式庫函式的方式來存取。換句話說，如果在等待事件時，會讓客戶端強制阻斷或忙碌等待。
2. Xlib 不會立即傳送請求給伺服器，而是先存放在佇列中，這部分稱為**輸出緩衝**；輸出緩衝裡的請求會在以下情況真正傳送出去：
  1. 程式以程式庫所提供的函式，如 `XFlush`，明確要求。
  2. 程式所呼叫的函式，涉及伺服器的回應，如 `XGetWindowAttributes`。
  3. 程式要求在事件佇列中的一個事件（例如，呼叫 `XNextEvent`）和呼叫區塊（例如，`XNextEvent` 區塊，如果佇列是空的）。

高階程式庫，如 Xt（Xaw 和 Motif 所使用的），讓客戶端程式指定與事件關聯的返回函式。程式庫維護輪詢事件佇列，並在必要時呼叫適當的函式；某些事件是在 Xt 內部處理，如需要重繪的視窗。

低階程式庫，如 XCB，提供協定不同步存取，容許較佳的延遲隱藏。

## X Window 核心協定不規定什麼

X Window 核心協定並不硬性規定客戶端之間的通訊方法，也不指明如何用視窗作成視覺化元件（圖形化使用者介面如按鈕、選單等），GUI 元件則交由客戶端程式庫實作相關的組件工具包。客戶端之間的通訊方法則交由其他的標準，如 ICCCM 和 freedesktop 規格 。

客戶端之間的通訊和選取、剪下緩衝區、拖曳有關，這些方法是用來讓使用者從某個視窗轉移資料到另一個視窗。視窗有可能由不同的程式來控制，所以一個用來交換資料的協定是必要的。客戶端之間的通訊和 X 視窗管理器同樣也有關係，它是用來管理視窗的外觀並統一使用者介面。另一個和客戶端之間的通訊有某種程度上的關係的還有期間管理。

使用者的期間要如何開始，也是核心協定未能涵蓋到的問題，這部分通常由 X 顯示管理器自動完成。使用者也可以執行 xinit 或 startx 程式，以手動的方式開始一個 X 對談。

## 參閱

* X 視窗系統的協定和架構
* Xlib
* Intrinsics

## 參考文獻

## 外部連結

* X.Org 基金會 （頁面存檔備份，存於網際網路檔案館）（官方首頁）
* X.Org 基金會維基頁 （頁面存檔備份，存於網際網路檔案館）
* Kenton Lee's pages on X Window and Motif （頁面存檔備份，存於網際網路檔案館）