Nano-X 程式設計, #2:「Hello World」
jollen 發表於 April 26, 2004 8:10 PM
說在前頭
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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "nano-X.h"
#include "nxcolors.h"
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變數。我們可透過此變數來判斷所產生的事件,並做出相對應的處理:
- GR_EVENT_TYPE_EXPOSURE :視窗出現後產生此事件,我們在此事件產生後呼叫 GrText() 函數在畫面上顯示文字。
- GR_EVENT_TYPE_CLOSE_REQ :當視窗關閉的請求發生時,則產生此事件。我們在此事件產生後呼叫 GrClose() 函數關閉與nano-X server的連線。