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 
#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變數。我們可透過此變數來判斷所產生的事件,並做出相對應的處理:

  • GR_EVENT_TYPE_EXPOSURE:視窗出現後產生此事件,我們在此事件產生後呼叫 GrText() 函數在畫面上顯示文字。
  • GR_EVENT_TYPE_CLOSE_REQ:當視窗關閉的請求發生時,則產生此事件。我們在此事件產生後呼叫 GrClose() 函數關閉與nano-X server的連線。
Also See

Jollen's Blog 使用 Github issues 與讀者交流討論。請點擊上方的文章專屬 issue,或 open a new issue

您可透過電子郵件 jollen@jollen.org,或是 Linkedin 與我連絡。更歡迎使用微信,請搜尋 WeChat ID:jollentw