說在前頭
Jollen: 對於「學習」這擋事,其實我有很多自己的想法。以「學程式庫」來講,我的看法是...
學習任何一套程式庫或 application framework,絕對不是「函數導向」的學習法,也就不能以學習函數做為主軸;正確的方式應該是「主題式(subjective)」的做法,也就是由「Hello World」開始(我想這個已經成為萬年標準了!),然後由不同主題的範例來深入,由於每個主題的範例都有其目的,因此可以透過範例,將達成目的的做法學起來。
其次,每個範例一定都包含「隱含」的觀念,除了學會做法外,也要把裡頭的觀念挖出來研究,以加強能力。Jollen 在撰寫這些程式庫、程式語言與 application framework 的教學專欄時,都是用這套思考邏輯來規劃整個大綱,並且也是我自己的寫作習慣。
在這樣的學習方式與前提下,「老師不會教、學生必須自己來」的工作是:
1. 安裝(當然囉!)
2. 準備 API 手冊
3. 瀏覽 API 手冊
4. 查 API 的能力與技巧
5. 一邊看 code 一邊查 API 手冊
6. 編譯範例
7. 改範例亂玩
歡迎提供您的意見,或是不同看法讓我參考。
Nano-X 的 "Hello World"
hello.c 是我們的第一個 Nano-X 程式。在這個範例中,我們要學習基本的 Nano-X 程式設計觀念如下:
˙ GC(Graphics Context)
˙ 視窗(windows)
˙ Nano-X程式設計流程
˙ 事件處理
先看範例:hello.c
以下是 hello.c 的完整程式碼:
/* * Copyright(c) 2003,2004 www.jollen.org * * - Microwindows nano-X API example. * - hello.c */ #include#include GR_WINDOW_ID wid; GR_GC_ID gc; void event_handler (GR_EVENT *event); int main (void) { if (GrOpen() < 0) { fprintf (stderr, "GrOpen failed"); return -1; } gc = GrNewGC(); GrSetGCForeground (gc, 0xFF0000); wid = GrNewWindowEx(GR_WM_PROPS_APPFRAME | GR_WM_PROPS_CAPTION | GR_WM_PROPS_CLOSEBOX, "jollen.org", GR_ROOT_WINDOW_ID, 0, 0, 200, 200, 0xFFFFFF); GrSelectEvents(wid, GR_EVENT_MASK_CLOSE_REQ | GR_EVENT_MASK_EXPOSURE); GrMapWindow(wid); GrMainLoop(event_handler); return 0; } void event_handler (GR_EVENT *event) { switch (event->type) { case GR_EVENT_TYPE_EXPOSURE: GrText(wid, gc, 50, 50, "Hello World", -1, GR_TFASCII); break; case GR_EVENT_TYPE_CLOSE_REQ: GrClose(); default: break; } }
再進行觀念的強化
了解怎麼使用 Nano-X 畫出「Hello World」後,我們挑出幾個重要的觀念來做加強:
1. GC(Graphics Context)
在 Nano-X 系統裡,我們可以把 GC 視為一張畫布,透過 Nano-X 的函數我們就可以在 GC 上繪圖。呼叫 GrNewGC() 函數可以建立一個 GC,若 GC 建立成功,則 Nano-X server 會傳回一個 GC 的 ID。我們會在程式一開始的時間就建立一個 GC,程式除了可以在 GC 上繪圖外,GC 的資料也能複製或透過選取函數(selection)做區域處理。
2. 視窗(windows)
Nano-X 的視窗是在程式一開始時呼叫 GrNewWindow() 函數所建立,若建立成功則可以取得 Window ID。視窗當然具備邊框(border)與按鈕等各種視窗屬性,視窗屬性可以在視窗建立時指定,或是透過 window manager 做修改。
心得小結:Nano-X 程式設計流程
以下歸納出 Nano-X 程式設計的步驟:
1、初始化與 server 的連線。呼叫 GrOpen() 函數,若傳回小於 0 的值,表示無法與 Nano-X server 連線。程式寫法如下:
if (GrOpen() < 0) {
fprintf (stderr, "GrOpen failed");
return -1;
}
2、建立新的 GC:
GR_GC_ID gc;
gc = GrNewGC();
若執行成功則傳回 GC 的 ID,其資料型別為 GR_GC_ID。
3、設定 GC 的前景色:
GrSetGCForeground (gc, 0xFF0000);
指定 GC 的 ID 並設定其前景色。
4、呼叫 GrNewWindowEx() 函數建立新視窗:
GR_WINDOW_ID wid;
wid = GrNewWindowEx(GR_WM_PROPS_APPFRAME |
GR_WM_PROPS_CAPTION |
GR_WM_PROPS_CLOSEBOX,
"jollen.org",
GR_ROOT_WINDOW_ID,
0, 0, 200, 200, 0xFFFFFF);
5、選取事件。呼叫 GrSelectEvents() 函數選擇我們想要處理的事件:
GrSelectEvents(wid, GR_EVENT_MASK_CLOSE_REQ | GR_EVENT_MASK_EXPOSURE);
以 hello.c 為例,表示我們想要處理視窗關閉請求(GR_EVENT_MASK_CLOSE_REQ)與視窗顯示(GR_EVENT_MASK_EXPOSURE)事件。
6、呼叫 GrMapWindow() 函數顯示視窗,並產生 GR_EVENT_MASK_EXPOSURE 事件:
GrMapWindow(wid);
7、呼叫 GrMainLoop() 函數進入 Nano-X 的分派迴圈:
GrMainLoop(event_handler);
傳遞給 GrMainLoop() 函數的參數為函數指標,指向處理事件的函數(event handler)。在分派迴圈裡,若產生我們所指定的事件時就會呼叫事件處理函數,因此我們必須在事件處理函數做事件處理。
事件(Event)處理
在分派迴圈裡,產生我們所指定的事件時,就會呼叫事件處理函數。以下是 hello.c 的事件處理函數:
void event_handler(GR_EVENT *event)
{
switch(event->type)
{
case GR_EVENT_TYPE_EXPOSURE:
GrText(wid, gc, 50, 50, "Hello World", -1, GR_TFASCII);
break;
case GR_EVENT_TYPE_CLOSE_REQ:
GrClose();
default: break;
}
}
分派迴圈會傳遞一個事件訊息的指標給事件處理函數,即程式中的event變數。我們可透過此變數來判斷所產生的事件,並做出相對應的處理:
Also See |
|
Jollen's Blog 使用 Github issues 與讀者交流討論。請點擊上方的文章專屬 issue,或 open a new issue
您可透過電子郵件 jollen@jollen.org,或是 Linkedin 與我連絡。更歡迎使用微信,請搜尋 WeChat ID:jollentw