1.输入法
世 界上大多数语言是基于字母表的,一些字母的集合组成了单词,当在计算机中输入这些语言时,用户通常是在键盘上键入相应的字符或一些组合键来实现。但表意文 字(如中,日,韩等)却不能在键盘上找到相应的键,如果想在计算机中输入这些文字,就需要相应的输入法。输入法有很多种,如拼音,五笔等,但这些输入法的 一个共同的特征是用户敲多个键来组成一个文字(或一组文字),统称为编码输入。
2.XIM协议
XIM(X Input Method)是X-Window系统下的符合国际化标准的输入法协议,只要应用程序和系统都支持这种输入协议,应用程序就不必具体考虑在不同语言环境下的输入问题,系统可以根据相应的locale去寻找相应的输入法,从而达到国际化的要求。
XIM的原理
1.体系结构
-
1.1 实现模型 在X-Window 系统环境下,XIM的实现有以下两种典型的体系结构:
1)Client/Server模型:
IM服务器是一个独立的进程,由它来处理输入、预编辑、转换和确认。IM库存在于应用程序中,就象IM服务器的一个客户,它只是简单的从IM服务器接收确认字符串。
2)Library模型:
所有的输入都由应用程序中的IM库来处理。事件处理在IM库中就被关闭了,所以就不再需要一个独立的IM服务器。
大多数语言,如亚洲语言一般都有复杂的预编辑,所以都采用Client/Server模型来实现,其他的只有一些死键或组合键的语言,如欧洲语言一般都采用Library模型来实现。
下面主要都讨论Client/Server模型,如图1所示:
-
-
-
客户程序通过连接IM服务器来实现XIM输入,它们之间的通讯是利用XIM的协议来实现的。IM子系统完成文字查找和文本的组合。
-
1.2 IM结构 当客户程序向服务器发出连接或断开请求时,在客户和服务器之间会产生打开和关闭操作。
函数XopenIM()设置或修改客户的locale,IM是根据相应的locale来指定的。另外,客户支持的IM类型可以通过函数XGetIMValues()来获得。
一 个客户程序通常有多个输入域,Xlib提供了一个结构“Input Context”(IC)来管理每个输入域。函数XCreateIC()可以指定XIM并创建一个相应的IC,函数XDestroyIC()用来删除此 IC。许多重要的信息,比如确认字符串都通过IC来从IM服务器送到客户程序。每一个IC与一个输入域相关,函数XSetICFocus()用来通知IM 服务器当前IC获得了焦点(XUnSetICFocus()表示失去焦点)。
-
1.3 事件处理模型 现存的输入法都支持前端输入法和后端输入法或其中的一种。XIM把后端输入法作为一种默认的输入法,但也支持前端输入法。
1)后端输入法:
在后端输入法中,客户窗口的输入事件总是送到IM库中,然后IM库把此事件送到IM服务器中。事件以传送的顺序来处理,因此在IM库和IM服务器中没有需要同步的问题。
2)前端输入法:
在 前端输入法中,客户窗口的输入事件由XServer直接传送到IM库和IM服务器中。因此这种方法提供了更好的接口性能(尤其在IM服务器运行在一台工作 站而客户程序运行在另一台工作站上,且网络又相对较慢时)。而,前端模型在处理键的时候有同步问题,时会引起事件丢失或时间重复。因此后端输入法是由核心 输入法支持的,前端输入法是从扩展性能的目的出发的。
-
1.4 事件流控制
XIM协议在IM库和IM服务器之间的通讯支持两种事件流:静态和动态。
1)静态事件流是客户程序的输入事件总是发送到IM服务器。
2) 动态事件流是输入事件中需要处理的事件发送到IM服务器。例如,在即输入ASCII字符又输入中文字符的时候,ASCII字符就不必送到IM服务器了。因 此,采用动态事件流后,在XServer、客户和IM服务器之间的需求事件大大减少了,从而性能有所提高。IM服务器发送 XIM_REGISTER_TRIGGERKEYS事件来切换动态事件流。
2.简单协议处理模型
下图2是一个{BANNED}{BANNED}最佳佳简单的协议流的例子:
当 应用程序发生按键事件时,它调用Xlib API的XNextEvent函数,对于后端输入法模型,事件都由IM库来处理,IM库先发出XIM_FORWARD_EVENT作为同步请求,IM服务 器接收到此协议后发出XIM_FORWARD_EVENT或XIM_COMMIT作为同步应答,双方完成同步。然后,IM服务器阻塞从IM库接收到的 XIM_FORWARD_EVENT消息,进行处理,结束后把结果返回到应用程序。
XIM开发工具IMdkit简介
IMdkit(IMServer Develops Kit)是X11R6的Xi18n执行工作组发布的XIM服务器开发工具,它提供了一个低级的C语言接口,把每个IM协议操作都绑定到了简单的C语言接 口,这样用户就可以很容易的使你的IM服务器与XIM客户程序很容易的通讯,而不用直接处理复杂的IM协议。XIM客户程序是指利用在X11R6中定义的 XIM API国际化过的应用程序。IMdkit封装了实际的IM协议操作,这样用户自己就不必处理实际包,可以利用它提供的数据结构,简化了许多细节麻烦;封装 了不同的传输机制(包括X协议,TCP/IP,DECNET),这些协议用来在IM库和IM服务器之间传输数据包;封装了多种IM协议模型,包括Ximp 和Xi18n。
IMdkit定义了一个不透明的数据结构XIMS,用来抽象输入法服务器的结构。它由函数IMOpenIM返回,函数IMOpenIM如果成功的话,返回一个XIMS结构,否则返回NULL。
对于每一个XIM协议输入,在IMdkit的头文件中都定义了一些相应的结构,对于R6标准的IM协议模型,中定义了所有的IM协议结构。
1)IM协议的通用数据结构
在R6的标准IM协议模型中,所有的事件结构有如下的通用成员:
-
typedef struct{
-
int major_code;
-
int minor_code;
-
CARD16 connect_id;
-
}
major_code和minor_code指定了唯一鉴别这个IM协议类型本身的常量名。connect_id指定了连接的客户ID。
2)协议处理
一些由IM库发送的IM协议请求由IMdkit内部处理了,不需要发送到IM服务器。因为IMdkit会根据你在XIMS结构中设置的值来决定对这个IM协议请求的应答。需要处理的一些IM协议请求参看下面介绍的XIM的实现。
XIM的实现
以下讨论的具体实现是利用IMdkit开发的XIM服务器。
1.初始化输入法服务
-
ims = IMOpenIM(display,IMModifiers,”Xi18n”,IMServerWindow,window,IMServerName,imname,IMLocale,DEFAULT_LOCALE, IMServerTransport,transport,IMInputStyles,input_styles,IMProtocolHandler,MyProtoHandler,IMEncodingList,encodings,NULL);
函数IMOpenIM初始化输入法服务的连接,并设置一个或多个由变长的参数列表指定的IM的属性。
下面简介上面函数中的参数:
Display是当前的屏幕显示。
IMModifiers定义XIM的协议模型,有两种:
“Xi18n”指定R6标准的IM协议模型。
“XIMP”指定R5标准的Ximp模型。
IMServerWindow指定IM服务器的窗口以便于与XIM客户程序进行通讯。
IMServerName指定IM服务器的名字。
IMLocale指定IM服务器支持的locale列表。
IMServerTransport指定IM服务器用来与客户程序间的通讯技术。
IMIputStyles指定IM服务器支持的输入风格列表。如:XIMPeeditPosition|XIMStatusNothing为光标跟随,不需要状态显示风格。下面的具体协议处理就是根据这种风格来介绍的。
IMProtocolHandler指定当IM主循环接收到客户程序的协议时的时间处理函数。
IMEncodingList指定IM服务器支持的传输编码列表。
2.协议处理函数
下面函数用于处理整个客户协议,由于篇幅原因,本文只列出了相应的协议,而没有具体的处理函数,实际应用中,每一个分支都由一个相应的函数来处理。
-
MyProtoHandler(XIMS ims,IMProtocol *call_data)
-
{
-
switch(call_data->major_code){
-
case XIM_OPEN:
-
case XIM_CLOSE:
-
case XIM_CREATE_IC:
-
case XIM_DESTROY_IC:
-
case XIM_SET_IC_VALUES:
-
case XIM_GET_IC_VALUES:
-
case XIM_FORWARD_EVENT:
-
case XIM_SET_IC_FOCUS:
-
case XIM_UNSET_IC_FOCUS:
-
case XIM_RESET_IC:
-
case XIM_TRIGGER_NOTIFY:
-
}
-
}
函数MyProtoHandler是由 IMOpenIM登记的处理客户程序发送到IM服务器的协议的函数,在参数call_data中有相应的协议。上面列出了一些主要的协议,对于不同的情况 (主要是根据不同的输入法风格),有一些协议不必处理,或根本不会发生。下面简介一些主要的协议处理过程。
XIM_OPEN:XIM客户程序启动时,要在IM库和IM服务器之间建立逻辑连接。
XIM_CLOSE:关闭在IM库和IM服务器之间建立 的逻辑连接。
XIM_CREATE_IC:当客户程序创建了一个输入法上下文(IC)时,发送此协议到IM服务器,这时在IM服务器中为此IC申请了一个相应的结构,用于记录一些必要的信息,包括字体、位置、前背景色等等。
XIM_DESTROY_IC:当客户程序退出时,删除相应的IC,释放一些与IC相关的存储空间。
XIM_SET_IC_VALUES:设置当前连接IM服务器的IC的属性。实际XIM服务器定期发送此协议来修改IC的属性值。
XIM_GET_IC_VALUES:取得当前连接IM服务器的IC的属性。
XIM_FORWARD_EVENT: 当有按键发生时,客户程序发送此协议到IM服务器,此按键事件放在call_data的event结构中。在相应的处理函数中,读出相应的键值,如果是一 些控制键,就作相应的操作,如输入法的开启,不同输入法之间的切换等;如果是输入法不需要处理的键,就直接送回客户程序,如一些功能键,组合键等;否则调 用输入法的处理程序,输入法处理程序对这个字符进行处理,此时在预编辑区和选择区会有相应的变化,如果有结果字符串生成,输入法服务器会按照开始时确定的 编码方式把这个字符串发送到客户程序,这样就完成了一次输入过程。
XIM_SET_IC_FOCUS:当在不同的应用程序间进行切换时,当前的IC会发生变化,此时新的应用程序会发送此协议到IM服务器,输入法服务器会改变当前的焦点,当前的一些与IC有关的属性和全局变量也要有相应的变化。
XIM_UNSET_IC_FOCUS:当前IC失去焦点。在此处可以释放一些与当前IC相关的资源。
XIM_RESET_IC:重置在IM服务器中的IC的状态。
XIM_TRIGGER_NOTIFY:IM库通知IM服务器匹配启动(on-keys)和关闭(off-keys)的事件发生了。
3.主事件循环
在主程序中要用XselectInput选择输入法窗口要处理的所有X事件,
-
XSelectInput(display,window,ExposureMask|ButtonPressMask|ButtonReleaseMask|ButtonMotionMask|VisibilityChangeMask);
然后在主循环中要处理所有这些事件,如下由函数MyXEventHandler完成。
-
for(::){
-
XNext event;
-
XNextEvent(display,&event);
-
if (XFilterEvent(&event,NULL)= =True)
-
continue;
-
MyXEventHandler(&event);
-
}
函数MyEventHandler要处理输入法窗口的事件。包括在屏幕上的显示,窗口的改变,拖动窗口,及与其他窗口进行通讯等消息。通过此函数,可以使程序达到对用户友好的目的。
以上三点是一个XIM服务器的必须要处理的,我们还需要包含以下头文件:X11/Xlib.h、IMdkit.h和Xi18n.h,后面两个头文件是IMdkit提供的面向R6标准的一些结构定义。另外连接的时候要连接IMdkit的libXimd.so。
五、小结
以 前的X Window下的输入法的实现大都是通过截取X的通讯函数的方法来实现在X下的应用程序中进行文本输入。具体方法是修改libX11.so.6里面的 XNextEvent函数,当时按键事件时,如果此是在中文状态,就把按键消息发送到输入法去处理,并等待输入法的回应。相应的要修改 XlookupString函数,和一些显示函数。这种方法属于非标准的方法,在实际应用中如果有一些应用程序通过其它方法接收输入时,输入法就会起不到 作用,为了达到目的需要修改很多应用程序,另外对于其他语言不兼容,每一种语言都要有不同的输入法,给程序的开发者和应用者带来了很多困难。采用了XIM 协议后,完全符合国际化标准,这样X下的应用程序根本不必过多的考虑输入问题,就可以达到国际化的要求,在不同的语言环境下可以自由的应用,减少了应用程 序开发者的麻烦。
参考资料
-
参考文献
-
[1] Open Software Foundation:X Window System Version 11 Input Method Secifications,November 1990
-
[2] X11R6 Xi18n Implementation Group:IM Server Developers Kit-C Language Interface,May 15 1994