PPSM嵌入式RTOS中的窗口系統(tǒng)開發(fā)
在目前的中低檔PDA中,很多廠商都采用Motorola M68K系列芯片。Motorola為其M68K CPU提供了一套免費(fèi)的實(shí)時操作系統(tǒng)PPSM(Personal Portable System Manager)。但該系統(tǒng)中沒有提供窗口系統(tǒng)。我們在實(shí)踐中開發(fā)了一套窗口系統(tǒng),如圖1和圖2所示。該系統(tǒng)為事件驅(qū)動方式,并有一系列控件支持。軟件開發(fā)人員采用添加控件的方式構(gòu)建所需的窗口,編寫對控件和窗口事件的響應(yīng)方式。下面介紹這套窗口系統(tǒng)的設(shè)計(jì)方案。
RTOS中的窗口系統(tǒng)開發(fā)%20%20src="http://img.ddvip.com/2008_07/1215881633_ddvip_1671.gif">
1 PPSM系統(tǒng)簡介
(1)虛擬多任務(wù)方式
PPSM系統(tǒng)中可以創(chuàng)建多個主任務(wù),但只有一個主任務(wù)處于活動狀態(tài)。每個主任務(wù)可以創(chuàng)建多個子任務(wù)。主任務(wù)和子任務(wù)均有唯一的TaskId。任務(wù)之間可以發(fā)送消息。接收消息的任務(wù)及其主任務(wù)可以取得CPU的控制權(quán)。
(2)觸摸屏輸入
PPSM系統(tǒng)采用了“活動區(qū)”的概念。“活動區(qū)”是用戶設(shè)定的屏幕上的一個矩形區(qū)域,只有筆在這樣的區(qū)域中的動作才能引起PPSM向活動任務(wù)發(fā)送消息。每個活動區(qū)均屬于其創(chuàng)建的任務(wù)。換一種方式表達(dá)為:每個任務(wù)保存和管理一系列活動區(qū),活動主任務(wù)的所有子任務(wù)的活動區(qū)均處于活動狀態(tài),后創(chuàng)建的活動區(qū)覆蓋之前創(chuàng)建的活動區(qū)。
(3)圖形界面支持
PPSM系統(tǒng)以一部分系統(tǒng)內(nèi)存作為屏幕緩存。每個任務(wù)可以擁有自己獨(dú)立的屏幕緩存,也可以共享一個屏幕緩存。屏幕緩存的尺寸可以與實(shí)際的屏幕大小不同。系統(tǒng)顯示活動任務(wù)的屏幕緩存中的圖像。每個主任務(wù)擁有自己獨(dú)立的屏幕緩存,可以使主任務(wù)切換時迅速切換屏幕;而各主任務(wù)共享一個屏幕緩存可以節(jié)約內(nèi)存空間,同時,應(yīng)用程序還可以創(chuàng)建獨(dú)立于任務(wù)的屏幕緩存。它具有與屏幕緩存同樣的結(jié)構(gòu),但不能直接輸出到屏幕上。用戶可設(shè)置當(dāng)前的屏幕緩存。PPSM提供了一組GUI函數(shù),用于在當(dāng)前的屏幕緩存中作圖。
(4)事件驅(qū)動
PPSM采用中斷方式處理各類事件,如時鐘、UART輸入/輸出、筆輸入等。各種事件均向活動任務(wù)發(fā)送消息。各任務(wù)(主任務(wù)或子任務(wù))均有各自的消息隊(duì)列。各任務(wù)從其消息隊(duì)列中取得并處理消息。
2 窗口的基本任務(wù)及界面系統(tǒng)的總體考慮
由于PPSM提供了靈活的屏幕緩存操作方式,開發(fā)的系統(tǒng)可能會因各應(yīng)用程序采用了不同的屏幕緩存方式而沖突,并且難以協(xié)調(diào)。我們開發(fā)PPSM系統(tǒng)上的窗口系統(tǒng),就是為了使其應(yīng)用程序界面開發(fā)變得容易而快速,使編程人員的精力集中在應(yīng)用程序本身的功能上,提高開發(fā)的效率和可靠性。
(1)關(guān)于界面繪制、切換、恢復(fù)的考慮
由于本窗口系統(tǒng)的目標(biāo)是基于Motorola EZ/VZ328的便攜設(shè)備,其特點(diǎn)是內(nèi)存較小、LCD屏幕較小、CPU速度和屏幕刷新速度均較慢;而窗口系統(tǒng)則要求刷新速度快,占有內(nèi)存小。通過分析系統(tǒng)特點(diǎn),較小的 LCD屏幕上,一般很少要求子窗口之間的切換,因而本窗口系統(tǒng)中,子窗口不能切換。換言之,子窗口均為有模式的,只有關(guān)閉上層子窗口,才能顯示下一層的窗口。下一層的窗口被上層子窗口覆蓋的部分可以由上層子窗口保存并恢復(fù),或由下一層的窗口自己重畫。前一種恢復(fù)方式雖然節(jié)約內(nèi)存,但速度較慢,而且如果被最頂層窗口覆蓋的窗口只有一個,則每個窗口均需按順序重畫。這在速度較慢的CPU上是不能容忍的,因而我們采用了后一種保存并恢復(fù)窗口覆蓋區(qū)域的方式。
一個應(yīng)用程序(主任務(wù))擁有一個主窗口。主窗口之間的切換等同于應(yīng)用程序的切換。如果以重畫的方式恢復(fù)一個主窗口,意味著該主窗口連同其所有子窗口必須依次重畫,這樣的刷新速度是不能滿足要求的。PPSM提供了這樣一種能力:如果主任務(wù)具有自己的屏幕緩存,在任務(wù)切換時,屏幕自動切換。因而我們采用這種方式。應(yīng)用程序具有自己的屏幕緩存,而各窗口均在該緩存上繪出。每個應(yīng)用程序均保存了一屏自己的窗口圖形,當(dāng)切換時,自動恢復(fù)。
由于消息只由最頂層窗口處理,見(3)消息的處理部分,因而,在我們的窗口系統(tǒng)中不存在下層窗口界面繪制問題;同時,在小的屏幕上,實(shí)現(xiàn)窗口的移動和縮放并無太大的實(shí)用性,因而我們也不實(shí)現(xiàn)窗口的這些功能。
另外有一類比較特殊的窗口,即POPUP屬性的窗口。這類窗口主要應(yīng)用于菜單和提示窗口,特點(diǎn)是:單擊窗口之外的區(qū)域?qū)⒆詣雨P(guān)閉該窗口。我們的處理方法是在這類窗口顯示時,設(shè)定一個全屏的活動區(qū),以取得窗口外區(qū)域的筆輸入;再設(shè)定一個窗口區(qū)域的活動區(qū)覆蓋在全屏的活動區(qū)之上,以將窗口區(qū)域排除在點(diǎn)擊自動關(guān)閉的區(qū)域之外。
(2)關(guān)于界面輸入的考慮
由于PPSM采用活動區(qū)的輸入方式,每個任務(wù)管理自己的活動區(qū),任務(wù)激活時,其活動區(qū)處于有效狀態(tài)。因而應(yīng)用程序切換時,其活動區(qū)自動切換;但一個應(yīng)用程序中,各窗口的活動區(qū)可能互相干擾。應(yīng)用程序中,每個窗口均有各種的輸入?yún)^(qū)域,而各窗口的活動區(qū)域可能相互覆蓋,顯示上層窗口時必須使下次窗口的所有活動區(qū)無效。有兩種方式可實(shí)現(xiàn)這樣的要求。第一種方式是,采用PPSM中子任務(wù)的方式:各子任務(wù)管理自己的活動區(qū),當(dāng)該子任務(wù)掛起或激活時,其活動區(qū)隨之掛起或激活。這要求一個窗口必須對應(yīng)有一個子任務(wù)。由于每個新的子任務(wù)需要較大的系統(tǒng)內(nèi)存空間,而我們并不需要應(yīng)用程序內(nèi)各窗口之間的切換,因而我們不采用這種方式。第二種方式是,采用PPSM中掛起活動區(qū)和恢復(fù)活動區(qū)的方法。PPSM支持對每個任務(wù)多次掛起活動區(qū)和恢復(fù)活動區(qū),因而我們可以在顯示新的窗口時,先掛起原有的活動區(qū);窗口關(guān)閉時,恢復(fù)原有的活動區(qū)。
(3)消息的處理
PPSM中定義了一系統(tǒng)硬件中斷產(chǎn)生的消息,如IRPT_TIMER、IRPT_RTC、IRPT_UART等等;同時,提供 SendMessage和AdvSendMessage函數(shù)允許發(fā)送用戶自定義消息。用戶自定義消息從IRPT_USER開始。一個程序可以發(fā)送到另一個應(yīng)用程序或自己。這些消息和所有系統(tǒng)消息均由應(yīng)用程序的頂層窗口處理。應(yīng)用程序從其消息隊(duì)列中取得消息后,首先,由預(yù)定義的應(yīng)用程序消息處理函數(shù)處理公共的消息。然后,由為頂層窗口定義的消息處理函數(shù)處理。頂層窗口不處理的消息由預(yù)定義的窗口消息處理函數(shù)處理。[!--empirenews.page--]
消息發(fā)送采用兩種方式。第一種是Send Message(),該函數(shù)將消息放在應(yīng)用程序的消息隊(duì)列中并立即返回;第二種是直接調(diào)用應(yīng)用程序頂層窗口的消息處理函數(shù),這樣函數(shù)便在消息處理之后返回。
(4)控件體系
窗口只是提供了界面設(shè)計(jì)操作的基礎(chǔ)。窗口中需要一系列按功能和操作方式分類的可視的界面元素,以便編程人員能夠方便地設(shè)計(jì)窗口的界面,實(shí)現(xiàn)窗口的特定功能。這樣的界面元素叫控件。
由于嵌入式設(shè)備內(nèi)存有限的原因,控件并不采用子窗口的方式。根據(jù)設(shè)計(jì)控件的目的,各類控件具有較為確定的外觀和規(guī)定的動作,并在特定的條件下,向其父窗口發(fā)出預(yù)定義的消息以供其處理。
控件屬于父窗口。在父窗口顯示時自動顯示,在父窗口關(guān)閉時自動釋放其占用的內(nèi)存空間;父窗口接收的消息首先在各控件中分發(fā)處理。按照這一要求,窗口必須保存、維護(hù)其控件的一個列表。列表中控件的指針按控件創(chuàng)建的順序存放。控件按創(chuàng)建的順序顯示,而消息在控件中按反序傳遞,以保證后創(chuàng)建的控件在可以覆蓋之前創(chuàng)建的控件的圖形和操作。
為了提高控件開發(fā)效率,我們需要各控件可以作為一種新定義控件的子控件。這樣新定義的控件可以利用已有的控件功能。如文本框控件中可以包含水平和垂直滾動條子控件。
控件可分為有焦點(diǎn)和無焦點(diǎn)的。有焦點(diǎn)的控件可以處理輸入法發(fā)出的字符消息。
3 窗口體系的實(shí)現(xiàn)
3.1 窗口的運(yùn)行結(jié)構(gòu)
根據(jù)以上對窗口體系的總體考慮,確定窗口的運(yùn)行結(jié)構(gòu)需要以下內(nèi)容:
① 窗口的位置、大小和標(biāo)題。
② 窗口的風(fēng)格:
WS_MAINWND——應(yīng)用程序主窗口。關(guān)閉主窗口將自動關(guān)閉應(yīng)用程序。
WS_POPUP——彈出式窗口。單擊非窗口區(qū)域?qū)⒆詣雨P(guān)閉該窗口。該屬性不能和WS_MAINWND同時出現(xiàn)。
WS_NOBORDER——無邊框窗口。
WS_NOSTATEBAR——無狀態(tài)條控件的窗口。一般窗口均有一個在窗口底部的狀態(tài)條,提供彈出該窗口的命令菜單、顯示窗口標(biāo)題、關(guān)閉窗口、打開選擇輸入法、顯示系統(tǒng)日期和時間等功能。
③ WS_POPUP類窗口的屏幕活動區(qū)和窗口活動區(qū)的ID。
④ 窗口當(dāng)前的光標(biāo)位置。
⑤ 窗口中控件的列表。
⑥ 窗口保存其覆蓋區(qū)域的內(nèi)存指針。
⑦ 前一個窗口的指針。用以在關(guān)閉窗口時重設(shè)應(yīng)用程序的頂層窗口。
⑧ 窗口的缺省輸入法類型和打開的輸入法控件的句柄。
⑨ 窗口的焦點(diǎn)控件的句柄。焦點(diǎn)控件將最先處理字符輸入的消息。
⑩ 窗口的消息處理函數(shù)指針。
3.2 窗口的創(chuàng)建和操作函數(shù)介紹
① HWND CreateWindow(WNDCLASS &wndCls);
WNDCLASS結(jié)構(gòu)定義窗口的基本屬性,如位置、大小、標(biāo)題、風(fēng)格等,見上面所述。該函數(shù)為窗口運(yùn)行時的結(jié)構(gòu)分配內(nèi)存,初始化屬性,并返回窗口結(jié)構(gòu)的指針。
② BOOL ShowWindow(HWND hWnd);
顯示一個窗口。其工作包括:掛起以前的活動區(qū);保存窗口的覆蓋區(qū)域的圖形;如果是有WS_POPUP屬性的窗口,須設(shè)定屏幕和窗口的活動區(qū);向該窗口的消息處理函數(shù)傳遞WM_ONSHOW消息(事實(shí)上是直接調(diào)用該函數(shù)),以提供編程人員在窗口上繪制控件以外的圖形的機(jī)會;如果是沒有 WS_NOSTATEBAR的窗口,添加Statebar控件;依次調(diào)用窗口中各控件的繪制函數(shù)以顯示控件;設(shè)第一個有焦點(diǎn)控件為窗口當(dāng)前的焦點(diǎn)控件。
③ BOOL CloseWindow(HWND hWnd);
關(guān)閉一個窗口。其工作包括:向該窗口的事件處理函數(shù)發(fā)送WM_CLOSE消息,如果返回FALSE則退出本函數(shù),如果返回TRUE則繼續(xù)以下工作 ——恢復(fù)窗口覆蓋區(qū)圖形;釋放POPUP類窗口的屏幕活動區(qū)和窗口活動區(qū); 依次釋放該窗口包含的控件;隱藏光標(biāo);釋放該窗口結(jié)構(gòu)占用的內(nèi)存;向上層窗口發(fā)送WM_TOPWNDCLOSE的消息,該消息用于下層窗口更新需要自動變化的界面,如股票實(shí)時大盤數(shù)據(jù)表;設(shè)置該窗口的前一層窗口為應(yīng)用程序的頂層窗口。
④ WNDPROC函數(shù)指針類型。
typedef BOOL (*WNDEVENTHANDLE)
(HWND hWnd, U16 msgType, U32 id,P_U32 data, U32 size);
⑤ DefWndProc(HWND hWnd, U16 msgType, U32 id,P_U32 data, U32 size);
處理如POPUP窗口區(qū)外的點(diǎn)擊自動關(guān)閉窗口之類的消息和行為。
⑥U32 WndAddCommand(HWND hWnd, P_S8 cmdName, U16 cmdLen, P_U8 cmdIcon);
向有狀態(tài)條控件的窗口增加應(yīng)用程序定義的命令。命令出現(xiàn)在狀態(tài)條的彈出菜單中。該函數(shù)返回一個唯一的命令I(lǐng)D,用于窗口處理WM_COMMAND消息時區(qū)分命令。
⑦ BOOL WndDelCommand(HWND hWnd, HCMD cmdId);
刪除一條命令。
⑧ U32 WndSetCommand(HWND, U32 cmdId, P_S8 newCaption);
修改一條命令。
⑨ 其它函數(shù)。因篇幅原因,不能完全列出和解釋所有的窗口操作函數(shù)。
3.3 控件的實(shí)現(xiàn)
(1)控件的基本結(jié)構(gòu)
我們使用控件的基本結(jié)構(gòu)定義各類控件的公共屬性。具體的控件結(jié)構(gòu)在此基礎(chǔ)上擴(kuò)展,以包含其它屬性。以下論述控件的基本屬性。
首先,在窗口的顯示過程中,各控件的外觀由自己繪制,因而各種控件需要一個繪制函數(shù)。該函數(shù)在定義具體控件時定義,在控件結(jié)構(gòu)中保留該類函數(shù)的指針。其次,各種控件需要各自的消息處理函數(shù),該函數(shù)的指針也保存在結(jié)構(gòu)中。最后,一些控件可能會動態(tài)分配內(nèi)存空間以保存自身的數(shù)據(jù)。控件需要在被釋放時釋放這樣的內(nèi)存,因而控件結(jié)構(gòu)中也保存控件釋放函數(shù)的指針。
控件是窗口上的一個可操作區(qū)域,主要由筆輸入來操作,因而控件需要響應(yīng)筆操作的活動區(qū)。各種控件的活動區(qū)數(shù)量不同,因而在控件結(jié)構(gòu)中需要保存一個可增長的活動區(qū)列表;但控件的屏幕區(qū)域可能覆蓋部分窗口中的其它活動區(qū)(如其它控件),從而造成控件操作的混亂,因此需要一個控件占用區(qū)域的活動區(qū),以屏蔽其它可能造成干擾的活動區(qū)。
由于我們需要各種控件能在定義新控件時使用,即作為新控件的子控件,在控件的結(jié)構(gòu)中,須保存子控件列表。各類控件的繪圖、消息處理和釋放函數(shù),必須為控件系統(tǒng)定義的相應(yīng)的缺省處理函數(shù)。這些缺省處理函數(shù)根據(jù)控件的子控件列表,首先調(diào)用子控件的相應(yīng)函數(shù)。[!--empirenews.page--]
(2)具體控件定義的方法
① 一個具體的控件對應(yīng)一個特定的結(jié)構(gòu)。該結(jié)構(gòu)首先包含控件的基本結(jié)構(gòu),其次定義該控件所需要的其它屬性。如按鍵控件,需要有按鍵的類型、顯示的文本或圖形、筆操作所需的活動區(qū)ID和按下狀態(tài)等屬性。
② 定義控件的繪制、消息處理和釋放函數(shù)。
③ 定義該控件的創(chuàng)建函數(shù),如CreateButton()。在該函數(shù)的參數(shù)中包含該控件所需的初始屬性、參數(shù)中標(biāo)準(zhǔn)的部分是控件的位置和大小。該函數(shù)初始化該控件結(jié)構(gòu)的屬性,包括初始化在基本控件結(jié)構(gòu)中的控件繪制、消息處理和釋放函數(shù)指針,使之指向相應(yīng)的函數(shù)。
④ 定義操作控件、存取控件中數(shù)據(jù)所需的其它函數(shù)。
(3)系統(tǒng)預(yù)定義的控件
在系統(tǒng)中,已經(jīng)使用這種定義方式定義了一些常用的控件。它們有:
① 按鍵:Button。文本或圖形按鍵,Check方式按鍵。
② 標(biāo)簽:Label。
③ 復(fù)選框:CheckBox。
④ 單選框:RadioBox。
⑤ 組合框:CombBox。
⑥ 滾動條:ScrollBar。水平或垂直,簡單類型滾動條可作為Spin使用。
⑦ 編輯框:TextBox。單行或多行,可編輯或不可編輯,有選塊功能。
⑧ 列表框:ListBox。單列或多列,選項(xiàng)可帶有圖標(biāo)。
⑨ 狀態(tài)條:StateBar。含有命令菜單彈出按鍵、窗口關(guān)閉按鍵(在窗口關(guān)閉按鍵上顯示窗口標(biāo)題)、輸入法按鍵、輸入法選擇按鍵、日期和時間顯示區(qū)。
日歷控件:Canlendar。顯示任意年月的日期,可切換公歷和農(nóng)歷。
組合日歷控件:Date。單行顯示日期,有彈出日歷的按鍵。
電子表格:Excel。顯示數(shù)據(jù)庫的記錄,可按每列對應(yīng)的字段排序。
組控件:Group。用于控件分組。
3.4 菜單系統(tǒng)
菜單是通過在POPUP窗口中加入ListBox控件實(shí)現(xiàn)的。菜單窗口的消息處理函數(shù)在用戶選擇列表框項(xiàng)目后自動關(guān)閉,并向應(yīng)用程序發(fā)送WM_ MENUCLICK消息。該消息帶有選項(xiàng)的序號。
使用PopupMenu()函數(shù)彈出菜單。PopupMenu()函數(shù)根據(jù)列表的項(xiàng)目數(shù)和列表文本的最大寬度,確定窗口的大小,創(chuàng)建窗口并添加列表框控件,在指定的位置顯示。