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