作者
魏永明
MiniGUI 提供的非 GUI/GDI 接口
本文講述了 MiniGUI 為應用程序提供的非 GUI/GDI 接口
這些接口能夠幫助應用程序更好地和操作系統交互
擴展應用程序功能
並提高應用程序的可移植性
內容主要涉及到如下幾個方面
MiniGUI
Lite 和 select 系統調用
基於 UNIX Domain Socket 的進程間通訊
編寫可移植性代碼等
引言
一般而言
GUI 系統的應用程序編程接口主要集中於窗口
消息隊列
圖形設備等相關方面
但因為 GUI 系統在處理系統事件時通常會提供自己的機制
而這些機制往往會和操作系統本身提供的機制不相兼容
比如
MiniGUI 提供了消息循環機制
而應用程序的結構一般是消息驅動的
也就是說
應用程序通過被動接收消息來工作
但很多情況下
應用程序需要主動監視某個系統事件
比如在 UNIX 操作系統中
可以通過 select 系統調用監聽某個文件描述符上是否有可讀數據
這樣
如何將 MiniGUI 的消息隊列機制和現有操作系統的其他機制融合在一起
就成了一個較為困難的問題
本文將講述幾種解決這一問題的方法
我們知道
MiniGUI
Lite 采用 UNIX Domain Socket 實現客戶程序和服務器程序之間的交互
應用程序也可以利用這一機制
完成自己的通訊任務――客戶向服務器提交請求
而服務器完成對客戶的請求處理並應答
一方面
在 MiniGUI
Lite 的服務器程序中
你可以擴展這一機制
注冊自己的請求處理函數
完成定制的請求/響應通訊任務
另一方面
MiniGUI
Lite 當中也提供了若干用來創建和操作 UNIX Domain Socket 的函數
任何 MiniGUI
Lite 的應用程序都可以建立 UNIX Domain Socket
並完成和其他 MiniGUI
Lite 應用程序之間的數據交換
本文將舉例講述如何利用 MiniGUI
Lite 提供的函數完成此類通訊任務
嵌入式 Linux 系統現在能夠在許多不同架構的硬件平台上運行
MiniGUI 也能夠在這些硬件平台上運行
但由於許多硬件平台具有和其他硬件平台不同的特性
比如說
常見的 CPU 是 Little Endian 的
而某些 CPU 則是 Big Endian 的
這要求我們在編寫代碼
尤其是文件 I/O 相關代碼時
必須編寫可移植代碼
以便適合具有不同架構的平台
本文將描述 MiniGUI 為應用程序提供的可移植性函數及其用法
除了與上述內容相關的函數之外
MiniGUI 還提供了其他一些函數
本文最後部分將描述這些函數的用途和用法
包括配置文件讀寫以及定點數運算
MiniGUI
Lite和 select 系統調用
我們知道
在 MiniGUI
Lite 之上運行的應用程序只有一個消息隊列
應用程序在初始化之後
會建立一個消息循環
然後不停地從這個消息隊列當中獲得消息並處理
直到接收到 MSG_QUIT 消息為止
應用程序的窗口過程在處理消息時
要在處理完消息之後立即返回
以便有機會獲得其他的消息並處理
現在
如果應用程序在處理某個消息時監聽某個文件描述符而調用 select 系統調用
就有可能會出現問題――因為 select 系統調用可能會長時間阻塞
而由 MiniGUI
Lite 服務器發送給客戶的事件得不到及時處理
這樣
消息驅動的方式和 select 系統調用就難於很好地融合
在 MiniGUI
Threads 中
因為每個線程都有自己相應的消息隊列
而系統消息隊列是由單獨運行的 desktop 線程管理的
所以任何一個應用程序建立的線程都可以長時間阻塞
從而可以調用類似 select 的系統調用
但在 MiniGUI
Lite 當中
如果要監聽某個應用程序自己的文件描述符事件
必須進行恰當的處理
以避免長時間阻塞
在 MiniGUI
Lite 當中
有幾種解決這一問題的辦法
在調用 select 系統調用時
傳遞超時值
保證 select 系統調用不會長時間阻塞
設置定時器
定時器到期時
利用 select 系統調用查看被監聽的文件描述符
如果沒有相應的事件發生
則立即返回
否則進行讀寫操作
利用 MiniGUI
Lite 提供的 RegisterListenFD 函數在系統中注冊監聽文件描述符
並在被監聽的文件描述符上發生指定的事件時
向某個窗口發送 MSG_FDEVENT 消息
由於前兩種解決方法比較簡單
這裡我們重點講述的第三種解決辦法
MiniGUI
Lite 為應用程序提供了如下兩個函數及一個宏
#define MAX_NR_LISTEN_FD
/* Return TRUE if all OK
and FALSE on error
*/
BOOL GUIAPI RegisterListenFD (int fd
int type
HWND hwnd
void* context);
/* Return TRUE if all OK
and FALSE on error
*/
BOOL GUIAPI UnregisterListenFD (int fd);
MAX_NR_LISTEN_FD 宏定義了系統能夠監聽的最多文件描述符數
默認定義為
RegisterListenFD 函數在系統當中注冊一個需要監聽的文件描述符
並指定監聽的事件類型(type 參數
可取 POLLIN
POLLOUT 或者 POLLERR)
接收 MSG_FDEVENT 消息的窗口句柄以及一個上下文信息
UnregisterListenFD 函數注銷一個被注冊的監聽文件描述符
在應用程序使用RegisterListenFD 函數注冊了被監聽的文件描述符之後
當指定的事件發生在該文件描述符上時
系統會將 MSG_FDEVENT 消息發送到指定的窗口
應用程序可在窗口過程中接收該消息並處理
MiniGUI 中的 libvcongui 就利用了上述函數監聽來自主控偽終端上的可讀事件
如下面的程序段所示(vcongui/vcongui
c)
/* 注冊主控偽終端偽監聽文件描述符 */
RegisterListenFD (pConInfo
>masterPty
POLLIN
hMainWnd
);
/* 進入消息循環 */
while (!pConInfo
>terminate && GetMessage (&Msg
hMainWnd)) {
DispatchMessage (&Msg);
}
/* 注銷監聽文件描述符 */
UnregisterListenFD (pConInfo
>masterPty);
/* 虛擬控制台的窗口過程 */
static int VCOnGUIMainWinProc (HWND hWnd
int message
WPARAM wParam
LPARAM lParam)
{
PCONINFO pConInfo;
pConInfo = (PCONINFO)GetWindowAdditionalData (hWnd);
switch (message) {
/* 接收到 MSG_FDEVENT 消息
則處理主控偽終端上的輸入數據 */
case MSG_FDEVENT:
ReadMasterPty (pConInfo);
break;
}
/* 調用默認窗口過程 */
if (pConInfo
>DefWinProc)
return (*pConInfo
>DefWinProc)(hWnd
message
wParam
lParam);
else
return DefaultMainWinProc (hWnd
message
wParam
lParam);
}
在
節當中
我們還可以看到RegisterListenFD 函數的使用
顯然
通過這種簡單的注冊監聽文件描述符的接口
MiniGUI
Lite 程序能夠方便地利用底層的消息機制完成對異步事件的處理
MiniGUI
Lite 與進程間通訊
簡單請求/應答處理
我們知道
MiniGUI
Lite 利用了 UNIX Domain Socket 實現服務器和客戶程序之間的通訊
為了實現客戶和服務器之間的簡單方便的通訊
MiniGUI
Lite 中定義了一種簡單的請求/響應結構
客戶程序通過指定的結構將請求發送到服務器
服務器處理請求並應答
在客戶端
一個請求定義如下(include/gdi
h)
typedef struct tagREQUEST {
int id;
const void* data;
size_t len_data;
} REQUEST;
typedef REQUEST* PREQUEST;
其中
id 是用來標識請求類型的整型數
data 是發送給該請求的關聯數據
len_data 則是數據的長度
客戶在初始化 REQUEST 結構之後
就可以調用 cli_request 向服務器發送請求
並等待服務器的應答
該函數的原型如下
/* send a request to server and wait reply */
int cli_request (PREQUEST request
void* result
int len_rslt);
服務器程序(即 mginit)會在自己的消息循環當中獲得來自客戶的請求
並進行處理
最終會將處理結果發送給客戶
在上述這種簡單的客戶/服務器通訊中
客戶和服務器必須就每個請求類型達成一致
也就是說
客戶和服務器必須了解每種類型請求的數據含義並進行恰當的處理
MiniGUI
Lite 利用上述這種簡單的通訊方法
實現了若干系統級的通訊任務
鼠標光標的管理
鼠標光標是一個全局資源
當客戶需要創建或者銷毀鼠標光標
改變鼠標光標的形狀
位置
顯示或者隱藏鼠標時
就發送請求到服務器
服務器程序完成相應任務並將結果發送給客戶
層及活動客戶管理
當客戶查詢層的信息
新建層
加入某個已有層
或者設置層中的活動客戶時
通過該接口發送請求到服務器
其他一些系統級的任務
比如在新的 GDI 接口中
服務器程序統一管理顯示卡中可能用來建立內存 DC 的顯示內存
當客戶要申請建立在顯示內存中的內存 DC 時
就會發送請求到服務器
為了讓應用程序也能夠通過這種簡單的方式實現客戶和服務器之間的通訊
服務器程序可以注冊一些定制的請求處理函數
然後客戶就可以向服務器發送這些請求
為此
MiniGUI
Lite 提供了如下接口
#define MAX_SYS_REQID
x
#define MAX_REQID
x
/*
* Register user defined request handlers for server
* Note that user defined request id should larger
From:http://tw.wingwit.com/Article/program/Oracle/201311/17973.html