Minigui IAL引擎實現
MiniGUI移植的核心所在就是GAL和IAL的移植。在嵌入式linux應用中,內核通常會提供對framebuffer設備的驅動,而在GAL中對於fb的支持,MiniGUI是默認的。因此,通過簡單的MiniGUI.cfg配置ial=fbcon,就可以完成GAL的支持了。
對IAL的支持並不是那麼容易。Linux對輸入設備目前還沒有一個統一的標準,也未形成任何規範。雖然keyboard在桌面領域已佔據主導地位,在嵌入式中不可能使用類似於鍵盤那麼複雜的設備。嵌入式中多使用觸摸屏和4×4的小鍵盤,而這類設備雖然表面雷同,功能鍵的定義千差萬別。移植這類設備的IAL實現更有意義。 MiniGUI不論是庫還是應用程序都不是內核的一部分,因此,MiniGUI沒有直接訪問設備的權限。爲了實現觸摸屏和小鍵盤的支持,內核中必須提供這二者的驅動程序。當然,這裏的驅動程序可以是最底層的實現,更爲複雜的通過MiniGUI的IAL驅動擴展實現。
因此在MiniGUI中實現一種新設備的IAL支持,通常提供如下步驟:
1.內核提供該設備(觸摸屏或者小鍵盤)的驅動程序。
2.在libminigui的src/ial/目錄建立需要的.c和.h文件,編寫高層IAL驅動程序。
3.將新的IAL驅動程序編譯到MiniGUI的庫中,以便爲應用程序提供新的IAL支持。
4.修改MiniGUI.cfg的ial配置,使應用程序運行時使用移植好的IAL設備。
實現一個輸入引擎,可以將觸摸屏按照鼠標處理,將按鍵按照鍵盤處理。
如何在MiniGUI中添加新的IAL引擎:(假設新的IAL引擎爲_NAME_IAL)
1. 在ial.c文件中添加新引擎的入口:
例如:(ial.c文件中)
A) #ifdef _NAME _IAL
#include "NAME.h"
#endif
B) 在input數組中添加
#ifdef _NAME _IAL
{"NAME ", InitNAMEInput, TermNAMEInput},
#endif
2. 把新的 .c 添加到 Makefile.am 文件中即可。
3. 修改配置文件IAL引擎項,使用這個新的IAL引擎_NAME_IAL.
² 如何編寫IAL引擎:
1. 在MiniGUI中引入了輸入抽象層(Input Abstract Layer ,IAL)的概念,它大大提高了MiniGUI的可移植性。
2. IAL是定義的一組不依賴於任何特殊硬件的抽象接口。而我們這裏所說的IAL引擎則是對IAL定義的抽象接口的實現的底層代碼。
3. IAL接口對下是IAL引擎,這些引擎使用了輸入設備驅動提供的一些函數;對上則是應用程序或是GDI,上層MiniGUI庫中ParseEvent()函數將通過這些IAL引擎將收集到的鼠標鍵盤事件轉換爲MiniGUI中的消息。
4. MiniGUI IAL結構如下:
在代碼實現上,MiniGUI通過INPUT數據結構來表示輸入引擎
typedef struct tagINPUT
{
char* id;
// Initialization and termination
BOOL (*init_input) (struct tagINPUT *input, const char* mdev, const char* mtype);
void (*term_input) (void);
// Mouse operations
int (*update_mouse) (void);
void (*get_mouse_xy) (int* x, int* y);
void (*set_mouse_xy) (int x, int y);
int (*get_mouse_button) (void);
void (*set_mouse_range) (int minx, int miny, int maxx, int maxy);
void (*suspend_mouse) (void);
int (*resume_mouse) (void);
// Keyboard operations
int (*update_keyboard) (void);
const char* (*get_keyboard_state) (void);
void (*suspend_keyboard) (void);
int (*resume_keyboard) (void);
void (*set_leds) (unsigned int leds);
// Event
#ifdef _LITE_VERSION
int (*wait_event) (int which, int maxfd, fd_set *in, fd_set *out, fd_set *except,
struct timeval *timeout);
#else
int (*wait_event) (int which, fd_set *in, fd_set *out, fd_set *except,
struct timeval *timeout);
#endif
char mdev [MAX_PATH + 1];
}INPUT;
extern INPUT* cur_input;
系統啓動後,將根據配置文件尋找特定的輸入引擎作爲當前的輸入引擎,並對全局變量cur_input(表當前使用的輸入引擎)賦值。
爲書寫方便,定義了當前引擎的C語言宏。
#define IAL_InitInput (*cur_input->init_input)
#define IAL_TermInput (*cur_input->term_input)
#define IAL_UpdateMouse (*cur_input->update_mouse)
#define IAL_GetMouseXY (*cur_input->get_mouse_xy)
#define IAL_GetMouseButton (*cur_input->get_mouse_button)
#define IAL_SetMouseXY if (cur_input->set_mouse_xy) (*cur_input->set_mouse_xy)
#define IAL_SetMouseRange if (cur_input->set_mouse_range) (*cur_input->set_mouse_range)
#define IAL_SuspendMouse if (cur_input->suspend_mouse) (*cur_input->suspend_mouse)
#define IAL_UpdateKeyboard (*cur_input->update_keyboard)
#define IAL_GetKeyboardState (*cur_input->get_keyboard_state)
#define IAL_SuspendKeyboard if (cur_input->suspend_keyboard) (*cur_input->suspend_keyboard)
#define IAL_SetLeds(leds) if (cur_input->set_leds) (*cur_input->set_leds) (leds)
5. 我們編寫IAL引擎要做的就是在_NAME_IAL.c中實現輸入引擎結構中定義的這些函數。如:_NAME_IAL.c:
#include ………
…………..變量定義
/************************ Low Level Input Operations **********************/
mouse_update()
{
……………
}
mouse_getxy()
{
………………..
}
mouse_getbutton()
{
………………..
}
………….IAL引擎結構中定義的其它有關mouse的函數一般無需實現。
Keyboard_update()
{
……………..
}
keyboard_getstate()
{
………
}
……………..同樣結構中定義的其它有關keyboard的函數一般無需實現。
Wait_event()
{
………………..
}
InitNAMEInput(INPUT* input, const char* mdev, const char* mtype)
{
…………
input->update_mouse = mouse_update;
input->get_mouse_xy = mouse_getxy;
input->set_mouse_xy = NULL;
input->get_mouse_button = mouse_getbutton;
input->set_mouse_range = NULL;
input->update_keyboard = keyboard_update;
input->get_keyboard_state = keyboard_getstate;
input->set_leds = NULL;
input->wait_event = wait_event;
………………..
}
void TermNAMEInput (void)
{
…………….
}
6. 各函數功能說明:開發一輸入引擎一般也就是實現以下幾個函數。
InitNAMEInput函數就是在src/ial/ial.c中定義的NAME輸入引擎的初始化函數,它打開觸摸屏(鼠標)和鍵盤設備文件。在成功打開這兩個設備文件後,該函數設置了INPUT結構的其它一些成員。其中一些成員被賦值爲NULL。
Mouse_update函數始終返回1,表明更新鼠標狀態成功。
Mouse_getxy函數返回由其它函數準備好的鼠標位置,有可能做了適當的邊界檢查和支持屏幕顯示旋轉時對座標的轉換。函數的參數是兩個指針變量。
Mouse_getbutton函數返回了觸摸屏狀態,即用戶是否觸摸了屏幕,相當於是否按下了左鍵。(或者鼠標哪個鍵按下,左鍵、右鍵還是當中的那個?)
返回值爲觸摸屏(鼠標)狀態。
Keyboard_update函數根據其它函數準備好的鍵盤消息,適當填充了state數組。返回值是NR_KEYS。
Keyboard_state函數直接返回了state數組的地址:“return state”。
Wait_event函數是輸入引擎的核心函數。這個函數首先將先前打開的兩個設備的文件描述符與傳入的in文件描述符集合併在了一起,然後調用了select系統調用。當select系統調用返回大於0的值時,該函數檢查在兩個文件描述符上是否有可讀的數據等待讀取,如果是,則分別從兩個文件描述符讀取觸摸屏和按鍵數據。
返回值int型retvalue變量,其中包含了信息:鼠標事件發生、鍵盤事件發生或者鼠標和鍵盤事件都發生了(retvalue |= IAL_MOUSEEVENT,retvalue |= IAL_KEYEVENT)。
7. 開發IAL引擎,實現以上函數:
開發IAL引擎準備工作:需對輸入設備有些瞭解:
a) 是鼠標、觸摸屏還是觸摸板?鍵盤都有哪些鍵?
b) 數據包格式怎樣?(同樣是觸摸屏,不同型號其通過設備文件獲得數據結構也不一樣,得到的若是A/D轉換得來的原始數據處理起來就有些麻煩;鍵盤的釋放與按下狀態是通過判斷讀得的字符最高位0與1來判斷,還是對同一鍵其按下和釋放對應兩個毫無聯繫的字符)
c) 鍵盤的編碼怎樣,和MiniGUI在include/common.h中定義的一樣嗎?
d) 在讀得的座標和屏幕顯示座標間需不需要進行座標轉換?Mouse_getxy中最終返回的座標爲屏幕顯示座標。
e) 輸入設備驅動支持select系統調用嗎?
f) 觸摸屏(觸摸板)驅動中是否解決了抖動消除的問題,是否需要在IAL引擎中解決觸摸屏消抖?
總結代碼中幾個針對具體系統開發的IAL 引擎:
a) 引擎包含的各個函數所要做的工作在上面的函數功能說明中已經反應出來,但是具體處理起來,不一定要求有的工作就得在某個函數中完成。比如,從設備文件讀取鼠標座標的信息並對其進行處理,不一定都在Mouse_getxy中完成,可以在Wait_event或update_mouse中完成,在mouse_getxy中只是將最終的顯示屏幕座標賦給mouse_getxy函數的兩個參數。
b) IAL引擎的作用是正確分析從設備文件讀得的數據,獲得鼠標(觸摸屏、觸摸板)座標和狀態及鍵盤按鍵情況(哪個鍵按下?該鍵是不是釋放了?).這也是我們開發IAL引擎的指南。
c) IAL引擎在MiniGUI的使用(位置):
在/src/sever/server.c L396 函數IdleHandler4Server()中用了輸入引擎中的IAL_WaitEvent檢查是否有底層輸入事件發生,
當有事件發生時檢查是鼠標(觸摸屏、觸摸板)事件發生還是鍵盤事件發生,並分別用parseEvent(msg_que,event)函數(/src/sever/server.c)處理這些事件
ParseEvent (msg_queue, IAL_MOUSEEVENT)
ParseEvent (msg_queue, IAL_KEYEVENT)
ParseEvent (msg_queue, 0)
parseEvent(msg_que,event)函數中首先調用了Getlwevent(event,&lwe) (lwe是在parseEvent中定義的),Getlwevent(event,&lwe)中分析event:
當event是IAL_KEYEVENT時:
調用了引擎IAL_UpdateKeyboard ()和IAL_GetKeyboardState ()
當event是IAL_MOUSEEVENT時:
調用了引擎IAL_UpdateMouse ()和函數RefreshCursor(&me->x, &me->y, &button)(該函數中調用了IAL_GetMouseXY (x, y)和IAL_GetMouseButton )
Getlwevent(event,&lwe)最終得到了鼠標鍵盤的座標狀態等具體信息,這些信息由lwe變量帶回。
變量Lwe數據結構:
typedef struct _LWEVENT
{
int type;
int count;
DWORD status;
LWEVENTDATA data;
}LWEVENT;
typedef union _LWEVENTDATA {
MOUSEEVENT me;
KEYEVENT ke;
}LWEVENTDATA;
parseEvent(msg_que,event)中利用了獲得的lwe,將其轉化爲消息,放到消息隊列中。如對鍵盤:
Msg.message=Msg-KEYDOWN
Msg.wparam=ke->scancode
Msg.iparam=ke->status
Getlwevent(event,&lwe)收集底層輸入事件lwe(利用了IAL引擎)
parseEvent(msg_que,event)將收集到的這些事件轉化爲上層能理解的消息。
d) 有關鍵盤的IAL引擎就是給state數組賦值。
MiniGUI在include/common.h中列舉了每種按鍵在state數組中的位置(如#define SCANCODE_Z 44可以知道state數組中第44個元素表示Z鍵的狀態),我們必須分析從鍵盤配置文件讀取的字符字節的信息,根據這些信息給state數組中相應元素賦值,這樣MiniGUI才能根據state數組正確處理這些事件(即MiniGUI是通過state數組中的元素來正確識別哪個鍵按下的。必須嚴格根據state數組的規定)。
8. 編寫輸入引擎可參考ipaq.c,它是針對了比較普通的觸摸屏的寫的。
9. 寫出觸摸屏接口代碼,注意:鼠標獲取的數據一般是相對座標,而典型的觸摸屏則是絕對座標(差勁的驅動是直接返回A/D轉換得來的原始數據)。