Chinaunix首页 | 论坛 | 博客
  • 博客访问: 786481
  • 博文数量: 129
  • 博客积分: 3477
  • 博客等级: 中校
  • 技术积分: 1329
  • 用 户 组: 普通用户
  • 注册时间: 2006-11-30 21:53
文章分类

全部博文(129)

文章存档

2023年(10)

2022年(4)

2021年(10)

2020年(9)

2018年(1)

2017年(1)

2014年(4)

2013年(3)

2012年(12)

2011年(24)

2010年(2)

2009年(8)

2008年(6)

2007年(34)

2006年(1)

分类: C/C++

2022-11-08 15:23:04

声明

涂鸦之作,不能保证其准确性。

作者:洪群

Email:hong-qun@hotmail.com

欢迎你转载本文,但请保留这段声明。也欢迎通过上面的方式与我交流。

1关于本文的由来

这段时间,项目上需要在X Window中实现一个自动输入的功能。 具体说来就是对于任意的控件,只要焦点落在该控件上,并且该控件为可输入的控件,就在光标位置处自动输入一串特定的字符。我是通过实现一个特殊的输入法完成了这个功能的。或者,还有更好的办法?若有请读者告知。不胜感激!

参考了几位前辈的文章,查阅了相关的文档,使我对输入法的原理有了一定的了解。所以写下本文,当作一个小结。

2XIM输入法原理

输入法的作用,主要是为了解决键盘的按键数不足的问题,其原理简单说起来就是把一串由按键所产生的字符串转变成我们所要输入的字符串,然后通过某种方式通知等待输入的应用程序可以输出字符串了。在这个过程中,等待输入的应用程序与输入法之间必然要有一个沟通的规则,XIM Protocol就是这种规则之中的一个。

当XIM Client(可以理解成应用程序的窗口)打开输入法时,通常情况下,会导致X Library和X Server之间建立一条连接。

基于XIM Protocol 的中文输入流程如图1:

图1

首先要注意,(1)按键是由X Server接收的,这一点只要明白了X Window的工作机制就不难理解了。(2)X Server在接收到按键后,会转发到相应程序的窗口中。在窗口的处理函数中,可以选择是否响应按键等,这里假定窗口是可输入的,(3)这时窗口将收到的按键回传给X Server,询问X Server关于XIM Server的相关信息。倘若输入法是打开的,(4)X Server会将事件发送给XIM Server(也就是输入法)去处理。XIM Server通过某种方式(例如,查码表)将按键转换成特定的字符串,(5)之后返回给X Server。(6)X Server得到字符串后,再通知窗口输出字符串。

事实上,图1是输入法两种体系结构中的一种,叫做Client/Server模型,还有一种叫Library模型的,它主要是用于处理欧洲语言的输入,这里也作介绍。

在windows系统中,输入法的工作过程也是比较类似的,这里就不再做说明了,请看如图2:

图2

在X Window中,有一个叫做Xi18n的概念,也就是X Window的国际化。用Xlib来设计支持国际化的widget是很麻烦的,在X window中如果你想支持中文的输入输出,除了有输入法的存在外,还必需自己在程序中编程设置Locale和XMODIFIERS环境,产生FontSet,打开输入法,对输入法进行操作等等。具体可以参考《Xlib Programming Manual》{BANNED}中国{BANNED}中国第一卷和参考3。但是现在有很多界面库如gtk、qt,都对国际化有了很好的支持,这些库说到底是使用Xi18n的相关库帮我们做了一些烦琐的事情。设置X Window的国际化软件虽然与输入法的实现并无太大的关系,但是作为初学者,有时却会把它与实现输入法混为一谈。

3基于XIM输入法的实现

首先可以阅读一下《The Input Method Protocol》(for X11R6),但是这个协议对于我们来说都太难实现,事实上也没有必要自己去实现。我们可以用现成的IMdkit来实现基于XIM的输入法。

IMdkit(IMServer Develops Kit)是X11R6的Xi18n执行工作组发布的XIM服务器开发工具,它提供了一个低级的C语言接口,把每个IM协议操作都绑定到了简单的C语言接口,这样用户就可以很容易的使你的IM服务器与XIM客户程序通讯,而不用直接处理复杂的IM协议。(参考2)

3.1 IMdkit中重要的API函数介绍

IMdkit的API文档中详细介绍了各个API函数(参考7)。现就其中的一些函数作一个简单的说明。

XIMS IMOpenIM(Display display,...):为IM Server初始化连接,并设置一个或者多个IMValues。若成功,返回XIMS一个结构,失败返回NULL。

char *IMSetIMValues(XIMS ims,...):注册IM的值。比如IMSetIMValues (ims, IMProtocolHandler, MyProtoHandler, NULL)的意思是将回调函数设置成MyProtoHandler这个函数。这里顺便说一下IMProtocolHandler ,它标识当IM Server接收到XIM Clients的输入时所调用的回调函数。

void IMCloseIM(XIMS ims):关闭IM Server的连接,并清理相关的资源。

3.2在XIM Server中主要处理的IM消息

我们在由IMProtocolHandler标识的回调函数中,要处理的消息主要有:

XIM_OPEN:XIM客户程序(也就是我们的应用程序窗口)打开输入法时,要在IM LIBRARY和IM SERVER之间建立逻辑连接。

XIM_CLOSE:关闭在IM LIBRARY和IM SERVER之间建立的逻辑连接。

XIM_CREATE_IC:当客户程序创建了一个输入法上下文(Input Context, IC)时,发送这个消息协议到IM SERVER,这时在IM SERVER中为此IC申请了一个相应的结构,用于记录一些必要的信息,包括字体、位置、前背景色等等。

XIM_DESTROY_IC:当客户程序退出时,删除相应的IC,释放一些与IC相关的存储空间。

XIM_SET_IC_VALUES:设置当前连接IM SERVER的IC的属性。这个消息会定期产生。

XIM_GET_IC_VALUES:取得当前连接IM SERVER的IC属性。

XIM_FORWARD_EVENT:当有按键发生时,客户程序发送这个消息到IM SERVER。在相应的处理函数中,读出相应的键值,如果是一些控制键,就作相应的操作,如输入法的开启,不同输入法之间的切换等;如果是输入法不需要处理的键,就直接送回客户程序,如一些功能键,组合键等;否则调用输入法的处理程序,输入法处理程序对这个字符进行处理,此时在预编辑区和选择区会有相应的变化,如果有结果字符串生成,输入法服务器会按照开始时确定的编码方式把这个字符串发送到客户程序,这样就完成了一次输入过程。这也就是整个输入法的核心部分的。

XIM_SET_IC_FOCUS:当在不同的应用程序间进行切换时,当前的IC会发生变化,此时新的应用程序会发送这个消息到IM SERVER,输入法服务器会改变当前的焦点,当前的一些与IC有关的属性和全局变量也要有相应的变化。

XIM_UNSET_IC_FOCUS:当前IC失去焦点。在此处可以释放一些与当前IC相关的资源。

XIM_RESET_IC:重置在IM SERVER中的IC的状态。

XIM_TRIGGER_NOTIFY:IM LIBRARY通知IM SERVER匹配启动(on-keys)和关闭(off-keys)的事件发生了。

下面是一段fcitx运行时记录下来的日志,可以看出在打开输入法的过程中消息的顺序:

XIM_OPEN:icid=0connect_id=1

XIM_CLOSE:icid=0connect_id=1

XIM_OPEN:icid=0connect_id=1

XIM_CREATE_IC:icid=0connect_id=1

XIM_GET_IC_VALUES:icid=1connect_id=1

XIM_SET_IC_FOCUS:icid=1connect_id=1

XIM_SET_IC_VALUES:icid=1connect_id=1

3.3一个简单输入法的代码结构

1.初始化X环境,创建窗口:

if ((dpy = XOpenDisplay ((char *) NULL)) == NULL)

{

… …

return False;

}

iScreen = DefaultScreen (dpy);

mainWin = XCreateSimpleWindow(dpy,DefaultRootWindow (dpy),0,0,50,50,0,0,0);

XMapRaised(dpy,mainWin);

… …

上面代码段的mainWin就是输入法的状态栏了。当然了,如果要实现一个真正的XIM 输入法,还要有候选词窗口,输入窗口等等。

2.初始化输入法:

ims = IMOpenIM (dpy, IMModifiers, "Xi18n",IMServerWindow, mainWin,IMServerName, "youIMName", IMLocale, strLocale, IMServerTransport, "X/",NULL);

if (ims == (XIMS) NULL)

{

… …

return False;

}

IMSetIMValues (ims, IMProtocolHandler, MyProtoHandler, NULL);

IMSetIMValues (ims, IMFilterEventMask, KeyPressMask | KeyReleaseMask, NULL);

在IMOpenIM函数的参数设置可以参考IMdkit API文档,这里不再多讲了。IMProtocolHandler的值就是主要的IM消息处理函数了。下面还会讲到。

3.在主函数的结尾处放置X消息循环:

for (;;)

{

XNextEvent (dpy, &event);

if (XFilterEvent (&event, None) == True)

continue;

}

4.编写IM消息处理函数:

这个函数的格式为:

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

}

}

要对每个major_code的值进行不同的处理。特别要指出的是XIM_FORWARD_EVENT,这个消息的处理过程实现上就是判断按键、查码表、返回字符,这也是整个输入法的核心部分了。

3.4关于XIM 输入法的实现建议

上面所讲的一切,是不足以让一个初学者完全实现一个基于XIM protocol的输入法的。所以建议从阅读开源代码(如fcitx)开始,先了解XIM的工作原理和IMdkit API,然后尝试修改开源代码。我想这样比从{BANNED}中国{BANNED}中国第一行代码开始编起容易得多。{BANNED}{BANNED}最佳佳后,如果你想实现自己的输入法,你不妨考虑一下采用更先的输入法技术如ibus。

参考:

6.The Input Method Protocol

7.Xlib Programming Manual

8.IM Server Developers Kit - C Language Interface(IMDkit的API)

原文地址:https://blog.csdn.net/weixin_36473464/article/details/116971843?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2-116971843-blog-5737403.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2-116971843-blog-5737403.pc_relevant_default&utm_relevant_index=3

阅读(1536) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~