----- 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 -----

