DirectFB自帶有兩個(gè)窗口管理器:default和unique,可以在配置文件中用wm=xxx來(lái)選擇用哪一個(gè)作為當(dāng)前的窗口管理器。
兩個(gè)窗口管理器的功能都很簡(jiǎn)單,與桌面環(huán)境流行的窗口管理器幾乎沒(méi)有可比性。尤其是前者,提供的功能更是簡(jiǎn)陋,僅僅是管理一下窗口棧而已。后者雖然簡(jiǎn)陋,但其架構(gòu)設(shè)計(jì)還算不錯(cuò),很容易在上面擴(kuò)展自己需要的功能。
什么是窗口管理器呢?根據(jù)EWMH的要求,窗口管理器的基本功能有以下這些:
模態(tài)窗口(Modality)。一般用來(lái)實(shí)現(xiàn)模態(tài)對(duì)話框,所謂模態(tài)對(duì)話框,是具有這樣特性的對(duì)話框,除非你把它關(guān)掉,否則無(wú)法切換回到它的父窗口上。
大桌面(Large Desktop)。顯示器的大小是有限的,比如顯示器的分辨率為1024x768,那么傳統(tǒng)的桌面只能這么大一點(diǎn)。窗口管理器可以實(shí)現(xiàn)一個(gè)邏輯上的大桌面,較顯示器的物理分辨率,擁有更大的顯示范圍。當(dāng)然你在某個(gè)時(shí)刻只能看到桌面的一部分,這部分也稱為viewport,通過(guò)變換viewport可以看到桌面的其它區(qū)域。
固定窗口(Sticky windows)。固定窗口要求窗口的位置被固定到顯示器的物理位置, viewport的變換對(duì)它的位置都沒(méi)有影響。
虛擬桌面(Virtual Desktops)。同時(shí)打開(kāi)的窗口太多時(shí),可以把這些窗口分成不同的組,同一時(shí)刻只顯示其中一組的窗口,每一組窗口是一個(gè)虛擬桌面。
任務(wù)條(Taskbars)、分頁(yè)器(Pagers) 。顯示當(dāng)前所有的打開(kāi)的窗口,并且可以在這些窗口之間切換。
窗口棧序(Z-Order) 。是窗口之間的上下關(guān)系。
保留區(qū)域(reserve space)。讓某個(gè)窗口獨(dú)占某塊靠邊的區(qū)域,比如任務(wù)條,一般都獨(dú)占桌面下面的一長(zhǎng)條區(qū)域。
窗口狀態(tài)(Window State) 。窗口有大化、小化、全屏等的狀態(tài),這些由窗口管理器負(fù)責(zé)管理。當(dāng)然,上層應(yīng)用也可以調(diào)用窗口管理器提供的函數(shù),來(lái)改變窗口的狀態(tài)。
窗口裝飾。在unix下,GUI的慣例是,窗口的標(biāo)題和四周的邊框,都稱為裝飾,這些裝飾的顯示是由窗口管理器負(fù)責(zé)的。這樣的好處時(shí),應(yīng)用程序無(wú)須做任何修改,僅通過(guò)配置窗口管理器,可以得到風(fēng)格各異的顯示效果。
窗口協(xié)議。這主要用于實(shí)現(xiàn)窗口僵死狀態(tài)檢測(cè),窗口之間的同步處理等功能。
對(duì)于嵌入式系統(tǒng)來(lái)說(shuō),并不要求實(shí)現(xiàn)桌面環(huán)境上的一些花梢的功能。unique的實(shí)現(xiàn)雖然簡(jiǎn)單,也可以滿足基本需求,更重要的是它提供了較好的擴(kuò)展機(jī)制。
DirectFB采用模塊化設(shè)計(jì),它并不依賴于某種具體的窗口管理器,只要具體的窗口管理器實(shí)現(xiàn)接口CoreWMFuncs中定義的函數(shù),可以掛到DirectFB中運(yùn)行。
Reactor在DirectFB中無(wú)處不在,要理解DirectFB的架構(gòu)一定要理解reactor模式才行。不過(guò),這里的reactor與POSA中講解的reactor類似,但并不完全相同,它更類似于signal機(jī)制,如果你理解glib中的signal或者boost中的signal機(jī)制,理解reactor并不難。DirectFB中reactor大的優(yōu)點(diǎn)在于它是跨進(jìn)程的,通過(guò)fusion內(nèi)核模塊中轉(zhuǎn),在一個(gè)進(jìn)程中觸發(fā)的事件可以方便的中轉(zhuǎn)到另外一個(gè)進(jìn)程。
Unqiue的源代碼在wm/unique目錄下,下面我們以輸入事件流把它們貫穿起來(lái)分析一下:
在DirectFB中,每一種輸入設(shè)備,都有一個(gè)線程掛在上面,只要輸入設(shè)備有事件上報(bào),該線程通過(guò)reactor把事件轉(zhuǎn)發(fā)給相應(yīng)的reactor處理函數(shù)。窗口管理在初始時(shí)(unique_wm_module_init),調(diào)用dfb_input_add_global把_unique_device_listener設(shè)置為事件處理函數(shù)。
在_unique_device_listener中,并沒(méi)有對(duì)事件直接處理,而又把它轉(zhuǎn)給相應(yīng)的UniqueDeviceClass對(duì)象,實(shí)現(xiàn)UniqueDeviceClass接口的有keyboard、pointer、wheel三種,每一種都有一個(gè)實(shí)例。
在這里,UniqueDeviceClass作為一個(gè)中間層,是否是多此一舉呢?開(kāi)始我也這樣認(rèn)為,過(guò)好長(zhǎng)一段時(shí)間后,我才明白這樣做是有道理的。原因是從不同設(shè)備讀到的事件格式并不統(tǒng)一,比如有的坐標(biāo),有的是相對(duì)坐標(biāo),鍵值映射關(guān)系也不一致。這個(gè)中間層可以把這些事件轉(zhuǎn)換成統(tǒng)一的格式,上層無(wú)需要再關(guān)系這些底層細(xì)節(jié)。
把事件轉(zhuǎn)換之后,然后通過(guò)reactor分發(fā)出去,到_unique_input_switch_device_listener函數(shù)里。_unique_input_switch_device_listener里面通過(guò)當(dāng)前的上下文以及事件的內(nèi)容,決定誰(shuí)是該事件的目標(biāo)。
這里有兩點(diǎn)比較有趣。
第一是StretRegion。StretRegion是一塊區(qū)域,StretRegion與窗口的關(guān)系又是什么樣的呢?這種關(guān)系很很簡(jiǎn)單,一般的窗口(帶裝飾的窗口)有十個(gè)StretRegion組成,它們分別是四邊、四角、窗口客戶區(qū)以及這九個(gè)StretRegion的父StretRegion(也稱為frame)。
對(duì)于鍵盤事件,一般是焦點(diǎn)窗口的客戶區(qū)收到事件,如果是筆點(diǎn)事件,則由筆點(diǎn)的位置決定是哪個(gè)StretRegion收到事件。
第二是決定了目標(biāo)StretRegion之后,并不是直接把事件投遞給這個(gè)StretRegion,而是調(diào)用StretRegion的GetInput函數(shù),獲取一個(gè)input_channel的對(duì)象。這似乎也是多此一舉,實(shí)則不然,窗口的四邊、四角、窗口客戶區(qū)九個(gè)區(qū)域是完全獨(dú)立的,它不知道也不應(yīng)該知道其它八的存在,另外這個(gè)九個(gè)區(qū)域的地位也不是等同的,有的要自己處理事件,有的不用,只有每個(gè)StretRegion自己才知道,所以由GetInput去決定把事件給誰(shuí)。
事件經(jīng)過(guò)input_channel之后,輪到_unique_window_input_channel_listener函數(shù)處理了。這時(shí),事件才真正被投遞到相應(yīng)的窗口,之后事情與窗口管理器無(wú)關(guān)了。
對(duì)Z-Order的管理,實(shí)際上比較簡(jiǎn)單,無(wú)非是調(diào)整窗口在棧的位置,所謂棧其實(shí)并不是真正的棧,只是一個(gè)雙向鏈表,大家都習(xí)慣的稱為棧罷了。
Unique實(shí)現(xiàn)了簡(jiǎn)單的裝飾功能,相關(guān)代碼在foo_update里(并沒(méi)有用到decoration.c/.h)。它的圖片數(shù)據(jù)在data目錄中。Unique的裝飾功能大的缺陷是,沒(méi)有根據(jù)不同類型的窗口實(shí)現(xiàn)不同的裝飾。