X Window核心協議

X Window 核心協議是X Window系統的基礎協議，它是一個以點陣圖顯示的網路化視窗系統，用來在Unix、類Unix和其他作業系統上建立使用者圖形介面。X Window 系統基於主從式模型：單一伺服器控管硬體的輸出入，如螢幕、鍵盤和滑鼠；所有的應用程式都被視作客戶端，使用者之間透過伺服器來互動。互動部分由X Window核心協議來管理。還有其他與X Window系統有關的協議，有的建立在X Window核心協議之上的，有的是獨立的協議。在X Window核心協議中，只會在網路上以非同步方式傳送四種封包：請求、回應、事件和錯誤。請求是由客戶端傳送到伺服器，告之進行一些動作，並回傳以便持握的資料。回應是由伺服器回傳的若干資料。事件是由伺服器傳送的，其用來通知客戶端某些使用者的動作，或者發生了其他所關心的事件。錯誤是由伺服器傳送的封包，其用來通知客戶端，在處理其請求時，發生了一些錯誤。請求有可能產生回應、事件和錯誤；除此之外，協議並不要求封包中的特定指令要以網路來傳送。還有其他對核心協議的擴充，這些擴充有自己的請求、回應、事件和錯誤。X Window 源於1984年的麻省理工學院早期對核心協議的原則是「機制，而非策略」，所以核心協議並未規定客戶端之間以及客戶端和使用者之間的互動介面規範。這部分則由其它的獨立規格所規範，如ICCCM、freedesktop.org規範，且可由所使用的特定元件工具包自動強制執行。概觀.伺服器和客戶端之間的通訊，是由通道上的交換封包所完成。由客戶端建立連線，且由客戶端傳送第一個封包。封包中包括將要使用的位元組序、協議版本方面的資訊，以及客戶端期望伺服器使用的認證種類。伺服器以回傳封包來答覆，封包中陳述接受或拒絕連線，或要求進一步的驗證。如果接受連線，接受封包內會包含客戶端接下來和伺服器互動所需的資料。建立連線之後，在客戶端和伺服器的通道上，會有四種交換封包的型別：請求和回應封包可以有各種長度，事件和錯誤封包的長度則固定是32位元組。請求封包的編號順序是以伺服器的接收為順序：來自客戶端的第一個請求編號為 1、第二個編號為 2，依此類推。請求的序列編號中最小的有效16位元，包含在由請求所產生的回應和錯誤封包之中，如果有的話。它們也包含在事件封包中，以指出伺服器正在處理或是剛剛完成的請求序列編號。視窗.在X Window系統以及各種圖形化使用者介面中，視窗即為一個頂層視窗。視窗也用來指視窗內部的視窗，這類視窗是父視窗的子視窗。圖形化元件，如按鈕、選單、圖示等等，都是使用視窗來實現的。客戶端可請求建立一個視窗。更嚴謹的說，客戶端可請求建立現存視窗的子視窗。所以客戶端所建立的視窗，皆以樹狀結構組織。視窗裡的內容並非在所有時候都能顯示出來。更精確地說，在視窗移動、調整大小、被其他視窗遮蓋、部分或整個視窗不可見時，視窗裡的內容就有可能會被銷毀。更精確地說，如果 X 伺服器無法維護視窗內容的後備存放區時，這些內容就會遺失。客戶端可請求為視窗進行維護的後備存放區，但伺服器沒有義務要這樣做。因此，客戶端不可假設已得到後備存放區的維護。若視窗有一部分未指出內容時，就會傳送一個事件，通知客戶端重繪那部分內容。每個視窗都關聯一組屬性值、背景圖、是否請求了後備存放區等等。協議中還包含用來給客戶端檢閱和改變視窗屬性值的請求。視窗可以是 codice_1。前者是顯示在螢幕上用於繪圖的視窗，而後者並不顯示在螢幕上，僅用來接受輸入。平常可看到視窗周圍的裝飾性框架和標題列裡去。從核心協議的角度來看，視窗管理器是一個客戶端，與其他的應用程式沒有區別。關於視窗的資料，可執行 codice_3 程式來取得。加上 codice_4命令列引數，程式便會顯示子視窗的樹狀結構，連同識別子和幾何性質資料一起顯示。圖形對映和可繪區.圖形對映可轉換到視窗上，反之亦然。這就讓雙緩衝得以實作。大部分可在視窗上完成的圖形化操作，也可以圖形對映完成。視窗和圖形對映被統稱為可繪區，且其資料內容都保留在伺服器上。客戶端可請求從伺服器上，將可繪區的內容轉換到客戶端，反之亦然。圖形脈絡和字型.客戶端可請求很多種圖形運算，如清空一塊區域、複製一塊區域到另一處，繪製一個點、線、矩形和文字。對清空而言，所有運算都有可能用在可繪區上。圖形脈絡包括了對圖形運算的大部分請求，圖形脈絡是一種結構，包含有圖形運算的引數。圖形脈絡包含前景色、背景色、文字的字型，以及各種圖形引數。當請求圖形運算時，客戶端就包含一個圖形脈絡。很明顯的，並非所有的圖形脈絡引數都會參與運算：例如，字型對於直線的繪製不產生作用。核心協議規格使用了伺服器側的字型。如字型是以檔案形式存放，伺服器經由本機的檔案系統直接存取，或經由網路從字型伺服器存取。客戶端可向伺服器請求有效的字型列表，且可請求伺服器載入，並以指定的字型來繪製指定的字串。在X Window核心協議的層次上，字型的名稱可以是任意的字串。X 邏輯字型描述協定規範瞭如何根據字型的屬性來命名。這些協定也規範了可附屬於字型的選用屬性之值。codice_5 程式可輸出存放在伺服器上的字型列表。codice_6 程式可顯示字型的標記，並讓使用者選取字型的名稱，以在其他視窗中貼上。目前已不再重視伺服器側字型的使用，而轉向客戶端側字型的使用。例如，藉由支援Xft或cairo程式庫，以及XRender擴充，改由客戶端繪製字型。客戶端側字型在核心協議中尚未給出規範。資源和識別子.所有關於視窗的資料、圖形對映、字型等等，皆存放在伺服器上。客戶端知道那些物件的識別子，和伺服器互動時，物件以整數為名稱。例如，當客戶端希望建立一個視窗時，便指定一個識別子，並請求伺服器建立一個視窗。伺服器會建立一個視窗，並與指定的識別子關聯。稍後客戶端可使用這個識別子進行請求，例如在視窗上畫上一個字串。以下存在於伺服器上的物件，客戶端可藉由數值型的識別子得知：這些物件就稱作資源。當客戶端請求建立某一種資源時，同時也為資源指定了一個識別子。例如，為了建立一個新視窗，客戶端指定了視窗的屬性值和識別子，最後識別子會和視窗關聯。識別子是三個最高有效位為0的32位元整數。每一個客戶端都有一組自己的識別子，其可用來建立新的資源。這組識別子是由伺服器以包含在接受封包中的兩個整數所指定的。客戶端以避免衝突的方式選取識別子：在視窗、圖形對映、字型、色彩對映、圖形脈絡之中的兩個物件，不可具有相同的識別子。資源一經建立，其識別子就用於客戶端向伺服器請求與之有關的運算。部分運算會影響特定的資源。識別子在伺服器上是獨一無二的，在多個客戶端之間也不例外。例如，即使是由兩個不同客戶端所建立的視窗，也不會同時具有相同的識別子。即使某個物件不是由自己的客戶端所建立的，只要指定相對應的識別子，就可存取另一個客戶端所建立的任何物件。連線到同一伺服器的兩個客戶端，對同一資源可使用同一識別子。例如，若客戶端建立一個 codice_12 識別子的視窗，並傳送數值 codice_12 給其他的應用程式，其他的應用程式即可對同一視窗進行操作。這個例子是來自X Window版本的Ghostview：程式建立一個子視窗，在環境變數中存放其識別子，並呼叫Ghostscript；程式繪製PostScript檔案的內容，以顯示在這個視窗上。當建立資源的客戶端關閉與伺服器的連線時，資源就會正常的銷毀。不過在關閉連線之前，客戶端可以請求伺服器不要銷毀資源。事件.事件是由伺服器傳送到客戶端用以通訊的封包，傳送一些客戶端可能感興趣的事情。例如，當使用者按下按鍵或點選滑鼠時，便會傳送一個事件。事件不只用於輸入：例如，傳送的事件表明特定視窗建立了新的子視窗。每一個事件都會涉及到視窗。例如，當使用者的滑鼠在視窗之內並點選時，這個事件就會涉及到那個視窗。事件封包中含有那個視窗的識別子。客戶端可以請求伺服器傳送事件給另一個客戶端，這可用於客戶端之間的通訊。例如，當客戶端請求目前所選取的文字時，就會傳送事件給客戶端，以處理目前所持有的選取內容。當再度觀看內容已被銷毀的區域時，有可能會傳送 codice_14事件。而且在某些情況下，視窗的內容可能會被銷毀。例如，當視窗被其他視窗遮蓋住，且伺服器沒有維護後備存放區時。此時伺服器會產生一個 codice_14 事件，以通知客戶端重繪視窗已消失的部分。大部分的事件只會在客戶端預先表示關心時才會傳送。因為客戶端可能只需要關心某型別的事件。例如，客戶端可能會關心關於鍵盤的事件，但卻不關心關於滑鼠的事件。即使在客戶端並未明確請求的情況下，某幾類事件也會不斷的傳送給客戶端。客戶端可以設定視窗的屬性值，以指明想要接收哪些事件。例如，當視窗的內容已銷毀時，為重繪其內容，客戶端就必須接收 codice_14 事件，以通知視窗需要再次重繪。客戶端要能接收到 codice_14 事件，就要預先指明它所關心的事件，這部分可以適當設定視窗屬性值的事件掩碼來完成。不同的客戶端可以請求同一視窗的事件。甚至可對同一視窗設定不同的事件掩碼。例如，某個客戶端可以只對視窗請求鍵盤事件，而另一個客戶端只對視窗請求滑鼠事件。這是可以的，因為伺服器會為每一個客戶端維護事件掩碼，而且是每一個視窗都維護一份獨立的事件掩碼。不過偶爾也有某幾類事件，只能由一個客戶端選擇。特別是，這類事件回報滑鼠按鈕的點選，且部分變化會涉及到視窗管理器。codice_18 程式顯示視窗所涉及到的事件。codice_19 可對識別子為 codice_20 的視窗要求所有可能的事件，並將其輸出。範例.以下是伺服器和程式之間的互動範例，這個程式會建立一個黑框視窗，按下按鍵後結束。在本例中，伺服器並未傳送任何回應，因為客戶端的請求並不產生回應。這些請求有可能產生錯誤。如果視窗被其他視窗遮蓋住，且再次顯露出來時，又剛好沒有這部分的備存時：如果按下按鍵：顏色.在協議層次裡，顏色使用32位元無負號整數來表示，稱為畫素值。以下因素會影響顏色的顯示：在最簡單的情況下，色彩對映是一種每一項都含有RGB三值的表。畫素值 codice_36 所表示的顏色，就包含在表中第 codice_36 項。如果客戶端可以改變色彩對映的內容，那這種表示法就以偽彩色與偽彩色類似，只不過客戶端不能修改色彩對映的內容。在此總計有六種可能的視覺分類，每一種都使用不同的方式來表示 RGB 畫素值。codice_38 和 codice_39 即其中兩種。codice_42 和 codice_43 是另外的兩種，其中最主要的差異是灰階漸層的使用。剩下的兩種視覺分類和前述的差異在於，將畫素值分成三個部分，並為紅、綠、藍的強度使用了三個獨立的表。並根據這個表來表示顏色，如下流程將畫素值轉換成RGB色：這個機制需要以三個獨立的表組成色彩對映，三個原色各一個表。轉換以後仍是強度值的三聯色。使用這個表示法的視覺分類有 codice_44 和 codice_45，其中的差異是客戶端不能修改後者的色彩對映。以上六種以畫素值表示顏色的機制，都需要附加一些引數來運作。這些引數可統合為視覺型別，其包含一個視覺分類和其他引數以表示顏色。每一個伺服器都有一組固定的視覺型別，每一個型別都與數值型的識別子關聯。其識別子是32位元無負號整數，和資源或元素的識別子沒有什麼不同。當接受來自客戶端的連線時，伺服器所傳送的接受封包裡含有區塊序列，每一個區塊都包含有關於某一個單一螢幕的資訊。就每一個螢幕而言，相關區塊包含著其他區塊的清單，每一個都涉及了螢幕所支援的色彩深度。就每一個螢幕所支緩的色度深度而言，這個清單中又包含視覺型別的清單。結果，每一個螢幕就關聯著各種合適的色彩深度，而且每一個螢幕的每一個色彩深度都關聯著各種合適的視覺型別。一個給定的視覺型別可用於更多的螢幕和各種不同的色彩深度。就每一個視覺型別而言，接受封包中還包含它的識別子和它所包含的實際引數，客戶端存放這些資訊，因為之後就不能再請求。此外，客戶端不能改變或建立新的視覺型別。建立新視窗的請求中，還包含有色彩深度和視覺型別的識別子，以此用來表示視窗的顏色。色彩對映和控制螢幕的硬體沒有關係。調色盤是一個表，也是用來表示顏色的。即使硬體並不使用調色盤，伺服器也能使用色彩對映。當硬體使用調色盤時，就只能安裝相當受限的少許色彩對映。更精確地說，當硬體根據色彩對映來顯示顏色時，就可以說是安裝了色彩對映。客戶端可請求伺服器安裝一個色彩對映。不過需要將另一個色彩對映解除安裝：其影響是視窗如果使用瞭解除安裝的色彩對映，就不能顯示正確的顏色，而出現怪異顏色的畫面。這個問題可以用標準色彩對映來解決，標準色彩對映是畫素值和顏色之間關聯恆定的色彩對映。由於有這一性質，就可讓不同的應用程式使用標準色彩對映。色彩對映的建立由ICCCM協定管理。標準色彩對映由ICCCM和Xlib規格管理。元素.元素是用來表示字串的32位元整數。本協議的設計者之所以引入元素，是為了以簡短、大小恆定的方式表示字串：由於字串可以是任意的長度，而元素總是32位元整數。某些封包可能會多次傳送相同的字串，元素的簡短性可以削減指令所使用的封包長度，進而增進網路的使用效率。大小恆定的元素有利於大小恆為32位元組的事件：大小恆定的封包可以包含元素，卻不能包含過長的字串。更嚴謹的說，元素是存放在伺服器上的字串的識別子，相當於資源的識別子。元素是識別子，所以也是獨一無二的。不過元素和資源的識別子可以相一致。與元素關聯的字串稱作元素名。元素的名稱一經建立就不能再更改，而且不能有兩個相同名稱的元素。故一般以元素的內容作為元素的名稱：「元素 codice_46」意謂著，或更精確地說，「這個元素關聯的字串是 codice_46」或「元素的名稱是 codice_46」。客戶端可以請求建立新的元素，且可請求指定字串的元素。元素運用於多個目的，主要與連線到同一伺服器的不同客戶端之間有關。特別是，元素用於與視窗屬性的關聯，以下詳述。所有存在於伺服器上的元素列表，可使用 codice_49 程式輸出。尤其這個程式可以元素的名稱。屬性.每一個視窗都有一組預先定義的屬性值，並存放在伺服器上，客戶端可以適當的請求方式取存。屬性值是有關視窗方面的資料，如視窗的大小、位置、背景色等等。屬性則是附屬於視窗上的資料片斷。與屬性值相反，屬性在X Window核心協議的層次中並沒有其他含意。客戶端可在視窗的屬性中存放屬性值資料。屬性是以名稱、型別和值來描述，屬性相當於指令式程式語言的變數，應用程式可以指定名稱、型別和值來建立新的屬性。屬性和視窗關聯：兩個相同名稱的屬性可存在於兩個不同的視窗，且可具有不同的型別和值。屬性的名稱、型別和值皆為字串；更精確地說就是元素，客戶端可藉由識別字，以存取存放在伺服器上的字串。客戶端應用程式可以元素的識別子存取特定的屬性。屬性大多用於客戶端之間的通訊。例如，名稱為 codice_50的屬性，是用來存放視窗的名稱；視窗管理器通常會讀取這個屬性，並在視窗頂部顯示名稱。某些客戶端之間的通訊也使用了根視窗的屬性。例如，根據freedesktop視窗管理器規格視窗管理器應該在根視窗的 codice_52 屬性名中存放目前有效視窗的識別子。X資源所含有的程式引數，同樣也是存放在根視窗的屬性裡；藉由這個方式，即使分別執行在不同電腦上，所有客戶端仍可存取到那些引數。codice_53 程式可輸出指定視窗的屬性，codice_54 則可輸出根視窗每一個屬性的名稱、型別和值。對映.在X Window系統中，鍵盤上的每一個物理按鍵都關聯有 8～255 之間的號碼，這些號碼就稱作鍵碼。字元或功能鍵則由鍵符來標識。鍵碼僅僅取決於實際按下的按鍵，鍵符則可取決於按下的 Shift 鍵或其他的修飾鍵。當按下或放開按鍵時，伺服器會傳送 codice_27 或 codice_56 的事件型別給適當的客戶端。這些事件包含：因此伺服器傳送鍵碼和修飾狀態，而無須嘗試將其轉成特定的字元，這部份的轉換工作由客戶端自己的協定來完成。例如，客戶端可能接收到一個事件，而這個事件表示已按下「a」鍵，且 Shift 修飾鍵也已按下，客戶端就會把這個事件關聯為「A」。客戶端完成鍵碼至鍵符的轉換以後，表示其關聯的表就由伺服器來維護，並將表集中存放在所有客戶端都存取得到的地方。客戶端只需請求對映，並使用它對鍵碼和其修飾鍵域解碼成鍵符。客戶端也可以任意改變此一對映。當按下修飾鍵時，就會改變另一個按鍵的解碼。常見的修飾鍵有Shift鍵：平常會產生小寫字母「a」的 a 鍵和 Shift 鍵一起按下時，就會產生一個大寫字母「A」。其他常見的修飾鍵還有「Control」、「Alt」和「Meta」。X 伺服器最多可用八個修飾鍵，不過每個修飾鍵可關聯一個以上的按鍵。例如，很多鍵盤有兩個「Shift」鍵。按下這兩顆按鍵時，會產生兩個不同的鍵碼，不過 X 伺服器會把那兩者都關聯到「Shift」修飾鍵。X 伺服器為八個修飾鍵維護一份可以認出修飾鍵的鍵碼清單。舉個例子，如果清單中的第一個修飾鍵包含鍵碼 codice_57，然後有某個按鍵會產生鍵碼 codice_57，X 伺服器就認為那個按鍵是 Shift 鍵。修飾鍵對映的清單由 X 伺服器維護，也可以讓所有的客戶端修改。例如，客戶端可以請求將「F1 鍵」加到「Shift」修飾鍵的清單中。從此，這顆按鍵的效果就如同 Shift 鍵一樣，不過 F1 仍會產生本身的鍵碼。結果，F1 仍做它以前所做的。X 伺服器也為滑鼠按鍵維護並使用修飾鍵對映，不過只能改變順序。其最大的用處就是為左撇子調換左右兩邊的按鍵。codice_59 程式可以顯示並改變按鍵、修飾鍵和滑鼠按鍵的對映。擷取.擷取即所有鍵盤或滑鼠的事件，都傳送到單一客戶端上。客戶端可以請求鍵盤、滑鼠或兩者的擷取：如果伺服器履行此一請求，所有鍵盤和滑鼠的事件，都會傳送到擷取中客戶端，直到解除擷取為止。此時，其他的客戶端將接收不到這些事件。請求擷取時，客戶端會指定一個擷取視窗：所有事件都會傳送到擷取中客戶端，彷彿和擷取視窗相關。不過其他的客戶端接收不到事件，即使是在擷取視窗中進行選取。在此有兩種擷取：客戶端可擷取鍵盤、遊標或兩者。擷取請求中也可包含用來凍結鍵盤或滑鼠的請求。擷取和凍截的不同處在於，擷取改變事件的接收者，而凍結只是完全停止遞送。當裝置凍結時，它所產生的事件會存放在佇列中，並在結束凍結時照常遞送。對於遊標事件來說，額外的引數會影響到事件的遞送：事件掩碼指定要遞送或拋棄哪些型別的事件。擷取請求中還包含一組欄位，欄位用來在還沒建立擷取之前，就指明傳送給擷取中客戶端的事件將要發生什麼。更精確地說，客戶端可請求它們照常傳送或進行擷取。這兩種情況和它們所表現出來的有所不同。例如，在第一個視窗上正常接收鍵盤事件的客戶端，可能會透過第二個視窗來請求擷取鍵盤。事件將會正常傳送給第一個視窗，而未必重新導向給擷取視窗，這取決於擷取請求裡所下的引數。客戶端也可請求擷取整個伺服器。在這種情況下，伺服器將不會處理任何請求，除了來自擷取中客戶端的請求以外。其他.還有其他的請求和事件，有一種請求是有關視窗之間的親屬關係：客戶端可請求改變視窗的父視窗，或請求關於父視窗的資訊。其他的請求則是關於選取，這部分大多由其他的協議來管理。還有的請求是關於輸入焦點和遊標的形狀。客戶端也可請求將資源的所有者殺死，這會使伺服器終止和那個所有者的連線。最後，客戶端還可傳送空運算請求給伺服器。擴充.X Window 核心協議被設計成可擴充。核心協議指定一個機制用來查詢可用的擴充，以及如何產生擴充的請求、事件和錯誤封包。更精確地說，客戶溯可以請求所有可用的擴充的清單，擴充的封包相當於核心協議的封包。核心協議指定請求、事件和錯誤封包要包含一個指明其型別的整數。一部分範圍的整數已保留給擴充。授權.在客戶端和伺服器剛開始建立連線的時候，伺服器的回應可以是接受、拒絕或要求驗證。驗證請求包含要使用的驗證方法的名稱。核心協議並不規範驗證程式，這部分要看所使用的驗證型別，並在伺服器傳送接受或拒絕封包之後結束。在客戶端和伺服器之間正常互動的時候，只有在涉及基於主機的存取方式的驗證才要請求。更精確地說，客戶端可要求啟用這個方式，並請求讀入和改變主機的清單。一般的應用程式不使用這類請求；codice_60 程式使用這類請求，給使用者或Shell script存取主機存取清單。基於主機的存取方式被認為是不安全的。Xlib 和其他的客戶端程式庫.大部分的客戶端程式藉由 Xlib 客戶端程式庫與伺服器交流。特別是客戶端大多使用 Xaw、Motif、GTK+、Qt 之類使用到 Xlib 的程式庫，方便和伺服器互動。Xlib 有以下作用：高階程式庫，如Xt，讓客戶端程式指定與事件關聯的返回函式。程式庫維護輪詢事件佇列，並在必要時呼叫適當的函式；某些事件是在 Xt 內部處理，如需要重繪的視窗。低階程式庫，如XCB，提供協議不同步存取，容許較佳的延遲隱藏。X Window 核心協議不規定什麼.X Window 核心協議並不硬性規定客戶端之間的通訊方法，也不指明如何用視窗作成視覺化元件，GUI 元件則交由客戶端程式庫實作相關的元件工具包。客戶端之間的通訊方法則交由其他的標準，如ICCCM和freedesktop規格 。客戶端之間的通訊和選取、剪下緩衝區、拖曳有關，這些方法是用來讓使用者從某個視窗轉移資料到另一個視窗。視窗有可能由不同的程式來控制，所以一個用來交換資料的協議是必要的。客戶端之間的通訊和X視窗管理器同樣也有關係，它是用來管理視窗的外觀並統一使用者介面。另一個和客戶端之間的通訊有某種程度上的關係的還有期間管理。使用者的期間要如何開始，也是核心協議未能涵蓋到的問題，這部分通常由X顯示管理器自動完成。使用者也可以執行xinit或startx程式，以手動的方式開始一個X會話。