In this article the design principle and the overall structer of xcin-2.5 is described. The design principles of xcin-2.5 are: 1. All the input methods are independent to each other and modulized. 2. As usual, the users only have to prepare a .cin file, then it could be used under xcin immediately. In most cases they don't have to write new IM modules for their input methods. 3. XCIN follows the standard of locale and XIM as much as possible, in hope that the Chinese input problem under X Window could be solved completely. Under these principles, the implementation of xcin-2.5 is very different to the old xcin versions. The basic structer is the following: IC manager <------> IMC system || | || | XIM system <----------+ || | || | GUI system module <----- cinput (winlist subsystem) | || | || | xccore <------ xcintool || | || | xcin_rc <------ siod Here each component of the above figure will be described. -------------------------------------- A. xcintool: (include/xcintool.h, lib/xcintool/*.c) This is the lowest level tool library of xcin. It only serves for some simple and basic operations. -------------------------------------- B. siod & xcin_rc: (lib/siod/*, lib/xcintool/xcin_rc.c) This is the rcfile reading system of xcin. Its API is contained in the xcintool library; while the kernel is the siod lib. SIOD library is an interpreter of lisp and Scheme mixed language. Using this the format of rcfile can be written in a solid and flexible lisp/Scheme language. xcin_rc can provide the rcfile data-reading for xccore and modules. It also could be the common rcfile reading interface for other extern programs (e.g., cin2tab). Therefore we only need a single rcfile to do all the configurations of xcin. All the global variables (excluding the detailed settings of CINPUT) read via siod and rcfile reading system will be held in xcin_rc data structer. When there is any module of xcin starting and initializing, this data structer will be passed into the initialization function of that module so that the module could refer to the data it needs. -------------------------------------- C. xccore: (include/constant.h, include/xcin.h, xcin_main.c) This is the main data structer of xcin. It is initially designed by clkao (in early days it is called xcin_core module). It is responsible for all the initialization when xcin starts to run. This includes reading command line options, setting locale, running rcfile reading system to read the configurations, excuting initialization for other parts .... etc. It also contains all the current status information of xcin, including GUI, XIM and input method statuses. This data structer is a private part of xcin main program and is controlled directly by xcin core. The input methods cannot refer to its contents directly. In this structer, the variable "xccore.xcin_mode" is used to record the status of xcin. It has two functions: the first is holding the configurations of the rcfile, and the other is the run-time status of xcin. -------------------------------------- D. module: (include/module.h include/imodule.h include/cinput.h module.c) This is the IM module kernel of xcin. All the dynamic loadable input methods enter xcin system through here. This kernel maintains all the input method related information for IMC system. For the details, please refer to the document "module". -------------------------------------- E. GUI system: (include/gui.h, gui.c) This is the kernel part of the GUI system. All the window drawing and operations of xcin are handled here. The "gui_t" data structer holds the global variables of this part, for example, the colors of xcin windows, the font names, and other information of X server .... etc. The GUI system could be divided into 2 sub-systems. The 1st is the FontSet management part, and the other is the winlist management part, as shown in the following: GUI core | +------------+------------+ | | FontSet sys winlist sys | +----------+----------+-------------+ | | | | gui_main.c gui_main2.c gui_menusel.c gui_overspot.c The FontSet part is a little simpler. It maintains the usage of the fonts in a uniform way, to avoid multiply opening the same font or close a font which should not be closed. The winlist part provides the ability of xcin to open multiple windows. In fact it could be treated as a simple widget set. The data structer is as following: =========================================================================== typedef struct winlist_s winlist_t; struct winlist_s { Window window; /* window of the winlist */ xtype_t wtype; /* winlist type */ int imid; /* IMC number */ xmode_t winmode; /* the current mode of the window */ int pos_x, pos_y; unsigned int width, height, c_width, c_height; font_t *font; unsigned short n_gc; GC *wingc; void *data; /* Data for window drawing */ void (*win_draw_func)(gui_t *, winlist_t *); /* Function to draw the window */ void (*win_attrib_func)(gui_t *, winlist_t *, XConfigureEvent *, int); /* Function when XConfigureEvent received */ void (*win_destroy_func)(gui_t *, winlist_t *); /* Function to destroy window */ winlist_t *next; }; =========================================================================== where the "wtype" has 3 types: WTYPE_MAIN: The xcin main window, e.g., gui_main, gui_main2. WTYPE_GUIREQ: The xcin GUI Request window, e.g., gui_menusel ... etc. WTYPE_OVERSPOT: The xcin OverTheSpot window. and "winmode" denotes the current status of the window: WMODE_MAP: denotes the window is pupped-up of hided. WMODE_EXIT: denotes if the window is going to destroy or not. and the facilities of the functions are: win_draw_func: For drawing the window. If during the operation it is needed to hide or pup-up the window, you have to call the gui_winmap_change() function here. The data needed for drawing the window is pointed by the "data" pointer. win_attrib_func: If the window received a XConfigureEvent (e.g., the window is dragged by the mouse, or the size of it is changed .... etc), this function will be called. Then we can trace the new pos_x, pos_y, width, and height .... etc of the window. win_destroy_func: This function will be called when the window is going to be destroyed. We can do some final handling here before the window being destroyed (if really needed). The win_attrib_func() and win_destroy_func() could be set to NULL, then xcin will handle them by default methods. From the release of xcin-2.5.2, there are 2 kinds of xcin main window: +-------------------------------------------+ The 1st main window: | a | +---------+---------+--------------+--------+ | b | c | d | e | +---------+---------+--------------+--------+ +-------+-------+-------+ The 2nd main window: | b | d | e | +-------+-------+-------+ a. Multiple characters for choosing displaying area. b. Input method status displaying area. c. Character composing area. d. Character composing callback area. After the composing, the entire keystroke used for composing will be displayed here. e. Input method English name displaying area (e.g., zh_hex, phone ....). For the 1st main window, it is mainly for Root input_style. In this style all the xcin character composing and status information will be displayed here. Furthermore, if the option "SINMD_IN_LINE1" of a input method is set to "YES" in the rcfile, then the information displayed in d will move to a area. For the 2nd main window, it is mainly for OverTheSpot input_style. Since all the character composing information is moved to OverTheSpot window, so the information remained here is reduced and the size of the window can also be reduced. The GUI system will draw the window only in 2 conditions. One is when it receives an Expose event, and the other is when any input method changes its status. The GUI system will determine if there is any status changes of the input mehtods via the value of gui_t->winchange. Therefore, the XIM system can just change its value if there is any status change in the input methods, then the result will be displayed in the xcin windows. The value definition of winchange is as following: WIN_CHANGE_IM: When it is ON, it means the status of the input method is changed. WIN_CHANGE_FOCUS: When it is ON, it means the focus window of the XIM clients is changed. WIN_CHANGE_REDRAW: When it is ON, it means the xcin windows need to be redraw. In most cases the redrawing is only needed when the above 2 cases occure. WIN_CHANGE_BELL: It means the input method need to beep in first kind. WIN_CHANGE_BELL2: It means the input method need to beep in second kind. WIN_CHANGE_BELLALL: It means the input method need just a beep, no matter the first kind or the second kind. The GUI system draws the windows according to the input method status in the xccore, and these status information comes from the xccore.ic structer, i.e., the current working IC (see the description in the following). Hence, any changes in the xccore.ic will be shown in the GUI system. Besides, many of the drawing modes of the GUI system could be changed via the configuration of the interface between the IM modules and the XIM system: the inpinfo structer of IC (see the following and "module" doc for details). This will be useful to display the current status of the input methods. After xcin completing all the initialization, it will enter the gui_loop() function of the GUI system until it terminates. In this function it will continuously wait for the next X event (XNextEvent()) and pass that event to XFilterEvent(). If this event comes from the XIM client (i.e., the XIM client is doing the Chinese input actions), Xlib will pass the event back to the XIM system of xcin (see the description of the following). If it is not the event from the XIM client, then it will be passed back to gui_loop() for further processing (e.g., redraw the window). The whole process will run once again and again. -------------------------------------- F. XIM system (include/xcin_core.h, xim.c, fkey.c, lib/IMdkit) This is the kernel to handle the XIM protocols. It is also the interface between the IM module system and the XIM clients. Every X app. supporting XIM protocol and can accept the input from xcin starts will send a event to here. Then the XIM system will create an IC (Input Context) for it. An IC keeps tracing of current input method status and other related XIM protocol information. This system does not communicate with the Xlib directly, but via IMdkit lib instead. IMdkit is not developed by us. It is introduced by Mr. yhsiao and Mr. gamete. It handle the basic data structers and protocols such that the XIM protocol handling could be greatly simplified. During the initiali- zation, it will register some xcin related information into Xlib, then the XIM clients will be informed that there is a XIM server called "xcin" could be used. The registered information includes: IMServerName: The XIM server name. If in the zh_TW.Big5 locale environment, the IMServerName will be "xcin", otherwise it will be set to "xcin-<locale name>". IMLocale: The LC_CTYPE category locale xcin is running. IMInputStyles: The input_styles xcin supports. Currently xcin only supports the Root and OverTheSpot input_styles. In the future other new styles will be supported, too. IMProtocolHandler: The XIM events handling function. This is the XIM processing center. IMOnKeysList: Register the trigger keys. XCIN uses the dynamic connecting model with XIM clients. Which means, under the English input mode, the user keyins will not be passed into xcin, until the user press the trigger keys to start the connection between xcin and the XIM clients. The trigger keys xcin registered includes the following: Switch between English/Chinese: default is ctrl+space. Switch between wide ASCII/single byte ASCII: default is shift+space. Switch between the input methods: defaults are ctrl+shift, shift+ctrl, and ctrl+alt+[0123456789-=] Quick phrases input: defaults are shift+alt+<ascii key> The above trigger keys are configurable in the settings in rcfile. The trigger key configuratiions is maintained in fkey.c. The function: int im_protocol_handler(XIMS ims, IMProtocol *call_data) is registered as the IMProtocolHandler by xcin. All the XIM events comes from Xlib (or IMdkit) will be passed into here, where the "call_data" is the contents of the event. In fact these events came from the XNextEvent() call and are filtered by XFilterEvent() call in the gui_loop() of the GUI system. If it is found that they are belong to the XIM events, then they be passed into here through IMdkit instead of passing back to gui_loop(). These events includes: XIM_OPEN: When a XIM client starts and decides to use xcin as its XIM server, it will send this event to xcin. XIM_CLOSE: Before a XIM client terminating, it should send this event to xcin to inform xcin for doing necessary reactions. XIM_CREATE_IC: When a XIM client start, every window using XIM protocol opens, it will send this event to xcin. Then xcin will create a corresponding structer: IC to handle the communication to that window. XIM_DESTROY_IC: When a XIM window is going to close, it should send this event to xcin to inform xcin for doing necessary reactions. Then xcin will release its corresponding IC back to the system. XIM_SET_IC_FOCUS: When the mouse klick on a XIM window, that window should send this event to xcin. Then xcin will do current IC switching work. XIM_UNSET_IC_FOCUS: When a XIM window is going to leave the focus state, it should send this event to xcin. XIM_TRIGGER_NOTIFY: In the beginning when the XIM window is not connected to xcin (i.e., in the English input mode), if then the Chinese input is needed, the user will press the trigger keys, and xcin will receive this event. Then xcin will initialize its cooresponding IC. XIM_FORWARD_EVENT: After the trigger key being pressed, all the keyboard input will be passed into xcin via this event (the key information is stored inside the "call_data" structer). Then xcin will handle this key event in the following steps: 1. Is it a trigger key or a function key (because the user may change the input method or go back to the English input mode). If it is a trigger key, then xcin will do the input method switching task. 2. If it is not a trigger key or a function key, then xcin will pass this key into the keystroke() function of the current IM module. The current status information of the input method "inpinfo_t" will also be passed into it, so that it will do the character composing works. Then xcin will decide the next step according to the return value of that function. 3. If this key event is not interested by the IM module and currently it is under the wide ASCII input mode, or the IM module wish xcin to process this key in the wide ASCII input mode, then xcin will do it. 4. If any character is composed and want to commit the characters to the XIM client, then xcin will do it. 5. If this key event is not meaningful to any part of xcin and its IM modules, then xcin will send this event back to Xlib, and Xlib will pass it back to the XIM client. The most important data structer of the XIM system is "xccore.ic". It is a pointer which points to the current working IC structer. -------------------------------------- G. IC manager (include/IC.h, xim_IC.c) This is directly related to the XIM system. It manages the whole IC link list. The IC is a very complicated data structer. It records current status of the input method and its corresponding XIM window. The definition is (partially) =========================================================================== typedef struct _IC IC; /* forward declaration */ CARD16 id; /* ic id */ CARD16 connect_id; /* id of connected client */ time_t exec_time; /* recent excution time */ xmode_t ic_state; /* status of the IC */ ic_rec_t ic_rec; /* the IC resource setting by client */ IM_Context_t *imc; /* the IM Context */ IC *next; }; =========================================================================== where the meaning of each item is id: The number of this IC. connect_id: The number of the XIM window connecting to this IC. exec_time: The most recent time which the IC is used by the XIM client. This is used for garbage collection algorithm. See the following for details. ic_state: The current status of the IC, which includes: IC_NEWIC: It means this IC is a newly created IC. IC_CONNECT: It means this IC is under the connection with the XIM window. IC_FOCUS: It means this IC is a current working IC. ic_rec: This keeps all the information from the XIM window, which includes: ic_value_set: It means which IC variables are set by the XIM window. ic_value_update: It means which IC variables are updated by the XIM window. input_style: The input style of this IC. client_win, focus_win: The window of the XIM client under connection. pre_attr: The composing variables of this IC. imc: The pointer which points to the IMC link list (see the following). Because the IC is the communicating interface between the XIM clients and xcin, for every XIM client window there will be an IC to provide the services. However, they will not handle the input method reactions. So the input method content (IMC) might be independent for each IC, or all the ICs might share the same input method content (see the following). IC garbage collection: Because not all of the XIM client will send the XIM_CLOSE or XIM_DESTROY_IC event to xcin when it is going to terminate, this will lead to the opened IC continuously increasing. Under the consideration of performance and the complexity of the algorithm, and also consider the racing condition, we designed the following garbage collection algorithm to handle the above circumstance: XCIN will check all the ICs roughly every 5 minutes to make sure that their corresponding XIM windows are actually working. The reason to use the "roughly" word is that we do not use a multi-thread techenique or fork another process to keep the time precisely. We only insert the checking function into im_protocol_handler() function. This function will be called the most frequently. For example, the user press any key or use the mouse to klick in any XIM client, this function will most likely be excuted. Therefore, insert the checking function in here is the most simple and effective way. In order to avoid the racing condition, we have a "time_t exec_time" field in the IC structer. Everytime this IC is working for its XIM window, this field will be set to the current system time. The checking function will only choose IC with the idling time over 10 minutes to check. If it finds that the cooresponding XIM window is disappeared, the this IC will be released. --------------------------------- H. IM Context (IMC) System IMC refers to Input Method Context. It is the actual data structer of the input methods. In the xcin-2.5.1 this structer is combined with the IC structer directly. Therefore when you change the focus window of the XIM clients, the contents of the xcin window (i.e., the status of the input methods) will change accordingly. However, in some circustances we do not want this, especially when we are using the bimsphone input method. We may hope that changing the focus of the XIM windows will not cause the changes in the input method contents. To acomplish this requirement, we decide to separate the IMC from the IC structer. Now xcin has two modes: XCIN_SINGLE_IMC ON and XCIN_SINGLE_IMC OFF. For XCIN_SINGLE_IMC OFF, every IC has its distinct IMC, then the input method status is independent for each XIM window. For XCIN_SINGLE_IMC ON, all the ICs will share the same IMC, then all the XIM window will have the same input method status. The IC has its lifetime, which is the same as that of its cooresponding XIM window. When a new XIM window is created, then a new IC will be created in the xcin side. When a XIM window is destroyed, then its cooresponding IC is also closed. The IMC has its lifetime, too. But it depends on the mode of xcin. For XCIN_SINGLE_IMC ON, then its lifetime is the same as the xcin main program. For XCIN_SINGLE_IMC OFF, then it is the same as the IC. The IMC structer is defined as the following: ============================================================================== typedef struct _IMC IM_Context_t; struct _IMC { unsigned short id; /* id of this IMC */ unsigned int icid; /* id of the current attached IC */ ic_rec_t *ic_rec; /* point to the current IC resource */ inp_state_t inp_state; /* ic cinput state */ inp_state_t inp_num; /* ic cinput num */ inp_state_t sinp_num; /* ic cinput num (sinmd) */ imodule_t *imodp; /* current binding cinput module */ imodule_t *s_imodp; /* show keystroke cinput module */ inpinfo_t inpinfo; /* inp info referenced by gui */ unsigned int skey_size; /* sinmd_keystroke buf size. */ wch_t *sinmd_keystroke;/* for keystroke of a published cch. */ unsigned int cch_size; /* cch buf size. */ char *cch; /* composed char for commit. */ int n_gwin; /* IM GUI request window recorder. */ greq_win_t gwin[MAX_GREQ_CNT]; Window overspot_win; /* OverTheSpot candidate window. */ IM_Context_t *next; IM_Context_t *prev; }; ============================================================================== Here "id" is the serial number of this IMC. In many other places it is the same as the value "imid". If SINGLE_IM_CONTEXT is ON, the there is always a single IMC, and the "imid" is 1. On the other hand, if it is OFF, then the "imid" will be set to the value of "icid". We see that this structer contains all the data of the input methods, including which IM module is used (imodp and s_imodp), the status of the input methods (inp_state, inp_num and sinp_num), the interface to the IM modules (inpinfo), and the OverTheSpot window and the GUI Request windows .... etc. The pointer "ic_rec" points to the "ic_rec" field of the IC structer, so that the IM modules and the GUI systems could obtain the status of XIM clients via here. The IMC structer could be treated as the bridge between the IM modules and the XIM system. Finally the IMC status is described here: IM_CINPUT ON: The IMC enters the Chinese input mode. IM_CINPUT OFF: The IMC is in the English input mode. IM_2BYTES ON: The IMC enters the wide ASCII input mode. IM_2BYTES OFF: The IMC is in the single byte ASCII input mode. IM_XIMFOCUS ON: The IMC enters the Chinese focus status. IM_XIMFOCUS OFF:The IMC leaves the Chinese focus status. IM_2BFOCUS ON: The IMC enters the wide ASCII focus status. IM_2BFOCUS OFF: The IMC leaves the wide ASCII focus status. Here we only discuss the IM_CINPUT and IM_XIMFOCUS, while the IM_2BYTES is similar to IM_CINPUT. The "entering the Chinese focus status" means that the IMC is switched to the Chinese input mode, and its cooresponding XIM window is also in input focus (i.e., the foreground window). Then the following keyboard input will go to that client window. T.H.Hsieh