----- Forwarded message from thhsieh -----

Date: Fri, 18 Feb 2000 23:23:36 +0800
From: thhsieh
To: [EMAIL PROTECTED]
Subject: xcin-2.5 GUI Request
X-Mailer: Mutt 1.0pre3us


本文討論輸入法模組的 GUI Request: Menu Selection window 的 API 介面。
閱讀本文時,建議您可以同時參考:

http://xcin.linux.org.tw/xcin-2.5/xcin_menu.png

可以幫助您理解 :-))


1. 簡介:

自 xcin-2.5.2 起輸入法模組開始支援 GUI Request 功能,它是提供輸入法模組
開啟其他視窗以顯示資料的方式。由於 xcin 主視窗的空間有限,這對於需要顯
示複雜資訊的輸入法模組而言可能不夠用,因而有這樣的設計。

GUI Request 是與 winlist [EMAIL PROTECTED]
凡是可供輸入法模組用來顯示其資訊的,我都稱為 GUI Request 視窗。其在 winlist
的定義中, wid 是設成 WID_OVERSPOT 的 (見 include/gui.h)。

[EMAIL PROTECTED] GUI Request 的視窗供輸入法模組使用,我稱它為
Menu Selection window (簡稱 menusel), [EMAIL PROTECTED]
source 就在 gui_menusel.c 中。未來若還有需要,可以再加入其他的 GUI Request
視窗。

輸入法模組可以透過 inpinfo 結構 (見 include/module.h) 來啟動、操作 GUI 
Request 視窗。由於 inpinfo 結構屬於 IMC [EMAIL PROTECTED] (有關 IMC 介紹,請見我
[EMAIL PROTECTED] post: xcin-2.5 & OverTheSpot), 因此,若在 XCIN_SINGLE_IMC OFF 
的
狀態下,不同的 IMC [EMAIL PROTECTED] GUI Request window 群。這意思
是說,您可以做到在 XIM client A [EMAIL PROTECTED] GUI Request window, 而在 
client
B [EMAIL PROTECTED] GUI Request window, 二者互相獨立。當然,如果在 
XCIN_SINGLE_IMC
ON 的狀態下,所有的 clients (或 IC) [EMAIL PROTECTED] IMC, 則它底下的 GUI 
Request
window 也同樣被所有的 clients 分享。

[EMAIL PROTECTED] IMC (或 inpinfo) 下,輸入法模組可以開啟多個 GUI Request 
window, 
[EMAIL PROTECTED] menusel, 因此若您高興的
話,也可以同時開啟兩三個選單來用。目前我是設定每個 IMC 最多只能開出五個
GUI Request window, 我想這樣應該是很夠用了吧。

PS. 寫到這裏,我又發現 xcin-2.5.2-pre1 有 bug, 在 gui.c, update_gui_request()
    [EMAIL PROTECTED] IMC 中開啟的 GUI Request window 數目是否已達到上限,
    這部分我會馬上進行修正。


2. 啟動 GUI Request:

在 inpinfo [EMAIL PROTECTED] greq_t *qui_request 的指標,它就是用來操作 GUI
Request 用的,其定義如下:

typedef struct greq_s {
    short reqid;                        /* request id */
    short type;                         /* request type */
    ubyte_t deleted_return;             /* request window is deleted */
    void *data;                         /* request data */
    struct greq_s *next;
} greq_t;

其中 type [EMAIL PROTECTED] GUI Request window, 對於 menusel 而
言,您要在這裏設為 GREQ_MENUSEL。

[EMAIL PROTECTED] IMC 中開啟多個 GUI Request window, 在這
[EMAIL PROTECTED] window [EMAIL PROTECTED] [EMAIL PROTECTED] link list 串起來,
[EMAIL PROTECTED] next 必須設為 NULL。而不同的 window 就以 reqid 來區分。這
個 reqid 可以是大於等於 1 的任何數字,由輸入法模組自己去設,只要每個 greq_t
的 reqid [EMAIL PROTECTED]

各 GUI Request window 真正的資料結構是存放在 data 所指的區域。由於不同的
GUI Request 其所需的資料結構不見得相同,像 menusel 的需的資料結構就稱為
greq_menusel_t (見後述), [EMAIL PROTECTED] void * 指標來代表,因此您要
[EMAIL PROTECTED] GUI Request, 就必須將 data 指向正確的資料結構,不可弄錯了。
有人曾建議我說這裏最好是弄成 union 指標, 但老實說我有點笨,不曉得該怎麼
弄 :-)) [EMAIL PROTECTED] :-))

輸入法可以自由控制 greq_t 的 window 的產生與關閉 (destroy), 若要產生它,
[EMAIL PROTECTED] greq_t 即可,若要關閉它,就將相對應的 greq_t 從 list 拿掉即
可。 xcin 完全不會[EMAIL PROTECTED] list 的內容。因此,若此 inpinfo 要轉讓給其
他輸入法模組使用 (也就是本輸入法模組的 xim_end 函式被呼叫時),則必須自己
將已開啟的所有 greq_t 拿掉 (或 free 掉),否則的話將會有 memory leakage 
的問題。

但是,在有些情況下 xcin 會[EMAIL PROTECTED] GUI Request window, 例如:

        1. 使用者用滑鼠點了視窗上的按鈕將它關掉時。
        2. 使用者突然按 ctrl+alt+... 切換到別的輸入法時。

這時候, xcin 只會將 GUI Request window 關掉,但仍然不會去動 inpinfo 裏頭
的 greq_t, 它只會在已視窗已關掉的 greq_t 裏頭的 deleted_return 設為 1, 藉
此通知輸入法模組說該視窗已關掉了,您必須做後續處理。通常的情況是,輸入法
模組必須馬上將此 greq_t 自 list 中拿掉。若不去理會,那多半會[EMAIL PROTECTED]
玩的結果,然後我們就會聽到使用者的抱怨了 :-)) 因此,每次當輸入法模組的
keystroke 函式被執行時,它必須檢查每個 greq_t 中的 deleted_return 是不是
有異動。

至於是否可以暫時隱藏、顯示 GUI Request window (即 Map, Unmap), 則視該 window
的定義而定,並非所有的 window 都需要此功能。例如 menusel 就沒有。

綜上所述,如果輸入法模組決定要開啟兩個 GUI Request 視窗 (假設都是 menusel),
則程式這樣寫即可:

greq_t *new_gui_request(int type)
{
    static int reqid=0
    greq_t *greq;

    greq = malloc(sizeof(greq_t));
    greq->reqid = (++reqid);
    greq->type  = type
    greq->deleted_return = (ubyte_t)0;
    greq->next = NULL;
    switch (type) {
    case GREQ_MENUSEL:
        greq->data = (void *)malloc(sizeof(greq_menusel_t);
        .......
        break;
    }
    return greq;
}

inpinfo->gui_request = new_gui_request(GREQ_MENUSEL);
inpinfo->gui_request->next = new_gui_request(GREQ_MENUSEL);
/* 這裏就是開兩個 GUI Request, 用 link list [EMAIL PROTECTED]
   從 list 裏拿掉即可 */


3. Menu Selection Window:

menusel 所需的資料結構如下:

typedef struct {
    unsigned short n_item;              /* number of item lists. */
    unsigned short head_item;           /* head index of the item lists. */
    unsigned short n_sel;               /* number of selection keys. */
    unsigned short n_sel_return;        /* num of selection returned by xcin. */
    char *selkeys;                      /* the selection keys. */
    unsigned short focus_item;          /* index of the focused item. */
    unsigned short focus_elem;          /* index of the focused element. */
    ubyte_t enable_focus_elem;          /* use focus element facility or not. */
    menu_item_t *item;                  /* the item lists. */
} greq_menusel_t;

Menusel 視窗分兩部分,左半邊稱為 item head, 右半邊稱為 item list, 如下圖:

        head1  |  item1-1  item1-2  item1-3 ....
        head2  |  item2-1  item2-2  item2-3 ....
        .....  |  .......  .......  .......

該視窗的大小是固定的,長度等於 xcin 主視窗的長度,高度最多是五列,如果
n_item 的數目小於五的話,則列數就會等於 n_item 的值。 n_item 可以超過 5,
而視窗顯示時是從 head_item [EMAIL PROTECTED]@[EMAIL PROTECTED]@有 8
個 item list, 但 head_item 是設成 2, 則顯示如下:

   (item head)   (item elements)
        head2  |  item2-1  item2-2  item2-3 ....
        head3  |  item3-1  item3-2  item3-3 ....
        head4  |  item4-1  item4-2  item4-3 ....
        head5  |  item5-1  item5-2  item5-3 ....
        head6  |  item6-1  item6-2  item6-3 ....

視窗本身不會去攔捷處理任何輸入字鍵,因此,如果您要實作 menu 上下捲頁的功
能,您可以在 module function keystroke() 接到上鍵時,將 head_item 減 1,
接到下鍵時將它加 1 即可。

n_sel [EMAIL PROTECTED] item list 中最多有幾個選擇項,但它只有在 selkeys 有設定
時才有用, selkeys 是設定顯示出的選擇鍵,而 n_sel 就是 selkeys 的數目。
例如,我可以設定:

        greq_menusel->selkeys = "asdfghjkl;";
        greq_menusel->n_sel = strlen(selkeys);

如此顯示出來的 item list 就是:

        headN  |  a itemN-a  s itemN-s  d itemN-d  ....

注意每個 item 前面都會[EMAIL PROTECTED] selkey, 就是您在 greq_menusel->selkeys 
中
所設的內容。當然您在這裏可以設成 NULL, 則預設就直接以 "1234567890" 來使用。
然而, menusel 的寬度不見得足以容納所有的 item element, 如視窗畫不下時,xcin
會透過 n_sel_return 回報說實際上只畫了幾個 element,故輸入法模組可以藉此做
出必要的調整。

focus_item [EMAIL PROTECTED] item [EMAIL PROTECTED]@
樣。在這裏反白會標示在該 item list 的 head 上,圖示如下 (標示在第二個 item):

        head1  |  item1-1  item1-2  item1-3  ....
       <head2> |  item2-1  item2-2  item2-3  ....
        .....  |  .......  .......  .......

如果將 enable_focus_elem 設為 1 [EMAIL PROTECTED] item list 中
的某個 element 上 (由 focus_elem 來指定),例如 (標示在第二個 item 的第三個 
element):

        head1  |  item1-1  item1-2  item1-3  ....
       <head2> |  item2-1  item2-2 <item2-3> ....
        .....  |  .......  .......  .......

若將 enable_focus_elem 設為 0, 則 item list 中不會有 element 的反白標示,且
focus_elem 也不會[EMAIL PROTECTED]

最後的 menu_item_t *item [EMAIL PROTECTED] item list 的陣列,如:

        item[0].title     ==> head1
               .elements  ==> item1-1 item1-2 item1-3 ....
               .........
        item[1].title     ==> head2
               .elements  ==> item2-1 item2-2 item2-3 ....
               .........


4. Elements in each items:

每個 item list 的細部定義如下:

typedef struct {
    wch_t *title;                       /* title of the item list. */
    wch_t *elements;                    /* elements of the item list. */
    ubyte_t *elem_group;                /* grouping info. of the elements. */
    unsigned short n_elem;              /* size of "elements" array. */
    unsigned short head_idx;            /* head index of the item list. */
} menu_item_t;

其中 title 就是上面那幾個圖中某一 item 的 head, 而它的 element list 則是
設在 wch_t *elements 中, n_elem 是 elements 陣列的個數。如果 elem_group
是 NULL, 則每個 elements [EMAIL PROTECTED] elem_group 不是 NULL,
則可以用它來指定多個 elements [EMAIL PROTECTED]:

        elem_group = NULL               /* 每個 element 原素都是各別選項 */
        title = head1
        elements = {A}, {B}, {C}, {D}
        -------------------------------------------------------------------
        head1  |  1.{A}  2.{B}  3.{C}  4.{D}

        elem_group = ....               /* 可以指定若干 element 原素合成一 */
        title = head2                   /* 個選項                          */
        elements = {A}, {B}, {C}, {D}
        -------------------------------------------------------------------
        head2  |  1.{AB}  2.{C}  3.{D}

至於 elem_group 的用法,與 inpinfo 中 lcch_grouping 與 mcch_grouping 的用
[EMAIL PROTECTED]@樣,您可以參考 xcin-2.5/doc/internal/module 
有關這兩個東東的使用
說明,即可了解。

[EMAIL PROTECTED] head_idx [EMAIL PROTECTED] gui_menusel_t->head_item [EMAIL 
PROTECTED]
告訴 xcin 說此 item list [EMAIL PROTECTED] element 開始畫起。

PS. 寫到這裏,我突然想到, greq_menusel_t 裏頭的 n_sel_return 其實應該要
    放在 menu_item_t [EMAIL PROTECTED] item lists 的長度可能不
    [EMAIL PROTECTED] (就算它們的 element 數都相同),有可能 item A 畫到第十個 
element
    都還沒超過長度,但 item B 畫到第八個就超過了,因此必須每個 item 都回
    [EMAIL PROTECTED] n_sel_return。這部分我再來改。

[EMAIL PROTECTED]: 上述所提的所有的 index, 如 head_item, focus_item, focus_elem,
head_idx 等,全部都是由 1 開始計數,而不是從 0 開始,請不要弄錯了。


5. Example:

底下這段 example code, 就是我在那個 screen shot 中的 menusel window 。這段
code 寫得很醜,並沒有放在 xcin-2.5.2-pre1 裏頭,只是純測試用的,各位可以參
考。

static greq_menusel_t *data;
static menu_item_t mitem[3];
static wch_t mtitle1[10], element1[20];
static wch_t mtitle2[10], element2[20];
static wch_t mtitle3[10], element3[20];
static ubyte_t elem_group[10];

/* initialize gui_request */
inpinfo->gui_request = malloc(sizeof(greq_t));
inpinfo->gui_request->reqid = 1;
inpinfo->gui_request->type = GREQ_MENUSEL;
inpinfo->gui_request->deleted_return = (ubyte_t)0;
inpinfo->gui_request->next = NULL;
data = malloc(sizeof(greq_menusel_t));
inpinfo->gui_request->data = (void *)data;

/* initialize menusel */
data->n_item = 3;
data->head_item = 1;
data->n_sel = 10;
data->n_sel_return = 10;
data->selkeys = NULL;
data->focus_item = 1;
data->focus_elem = 1;
data->enable_focus_elem = (ubyte_t)1;
data->item = mitem;

/* initialize item 1 */
mitem[0].title = mtitle1;
        big5str_to_wch(mtitle1, "台灣大學");
        mitem[0].elements = element1;
        mitem[0].elem_group = NULL;
        mitem[0].n_elem = 4;
        mitem[0].head_idx = 1;
        big5str_to_wch(element1, "現在時間");

/* initialize item 2 */
mitem[1].title = mtitle2;
        big5str_to_wch(mtitle2, "物理系");
        mitem[1].elements = element2;
        mitem[1].elem_group = NULL;
        mitem[1].n_elem = 8;
        mitem[1].head_idx = 1;
        big5str_to_wch(element2, "我是居士正在測試");

/* initialize item 3 */
mitem[2].title = mtitle3;
        big5str_to_wch(mtitle3, "謝東翰");
        mitem[2].elements = element2;
        mitem[2].elem_group = elem_group;
        mitem[2].n_elem = 8;
        mitem[2].head_idx = 1;
        mitem[2].elem_group[0] = (ubyte_t)4;
        mitem[2].elem_group[1] = (ubyte_t)2;
        mitem[2].elem_group[2] = (ubyte_t)2;
        mitem[2].elem_group[3] = (ubyte_t)2;
        mitem[2].elem_group[4] = (ubyte_t)4;


6. 結語:

看到了這,您是不是已頭昏腦脹了呢?我是已經寫得頭昏腦脹了 :-))
我想我大概可以很自豪地表示, xcin 可以算是自由軟體界中寫得最 ugly 的 XIM
server 了 ....

[EMAIL PROTECTED] xcin & OverTheSpot 中提到
的問題,我會盡快做出 patch release 出來。



T.H.Hsieh

----- End forwarded message -----

Reply via email to