Linux 版 (精华区)
发信人: netiscpu (网中鸟~~可以飞了), 信区: Linux
标 题: Wrapper技术浅谈--ZhXwin剖析
发信站: 哈工大紫丁香 (Fri Jul 9 00:29:16 1999), 转信
试了各种Linux下的中文系统, 觉得有必要写一些东西. 大部分用户都曾问起过中文问
题, 觉得Linux下没有成熟的中文系统(我是指免费的:-),其实中文信息处理的技术已经
比较完善了, 只是在UNIX下处理中文的技术资料难找或是几乎没有, 只能通过看源代码
来得知一些处理的细节. 我想如果一些中文软件的作者能介绍一些技术细节的话, 对后
人来说, 一定受益非浅. 事实上, 介绍思想有时是件麻烦而费力的事, 所以我想大家在
阅读没有文献的源码时, 一定不要吝啬于写一些自己的心得同他人一起分享. 我写这篇
文章主要想让一般的用户了解一下作为Linux下中文技术的一种Wrapper的基本原理, 错
误的地方请大家纠正. 另本文是在Linux下编辑, 图示是通过xfig来画的.Wrapper技术
浅谈--ZhXwin剖析作者:缪宇成(Genius@SLUG.cx)Wrapper基本原理 UNIX提供了一种
LD_PRELOAD的机制, 允许指定共享库中的函数对标准库函数进行替换. 当程序调用共享
库例程时, UNIX首先在LD_PRELOAD环境变量所指向的共享库中查找同名的例程, 当没有
找到时才转向标准库继续查找. Wrapper利用此机制对X下的标准库函数进行替换, 这些
函数一般为XDrawString, XDrawText, XTextWidth, XNextEvent...等等, 以使得不专
为中文设计的软件能输入和显示中文. X-Window系统本身是支持双字节文字的显示
的, 以16结尾的一些函数如: XDrawString16, XDrawText16等可以用来显示汉字. Wra
pper的显示机制一般是替换调XDrawString函数, 对其中的字节高位为1的汉字同西文字
符分开处理, 以得到正确显示. X-Window下的中文输入是通过对XNextEvent和XLoo
kupString这两个函数的替换来实现的. 一般地, 一个X客户程序通过调用XNextEvent来
获得一个键盘事件, 然后通过将此事件传给XLookupString来获得和这个击键对应的字
符串(X下可通过XRebindKeySym函数将某个键符同一个字符串关联起来, 试想一个快捷
键), 和对应的键符. Wrapper替换调XNextEvent使得客户的击键动作被传入到中文输入
模比如Chinput)中, 并将结果保存下来, 当客户调用XLookupString时将得到的结果返
回.实例分析--ZhXwin ZhXwin是宫敏博士编写的一个Wrapper库, 外加Tcl/Tk编写的
安装前台. ZhXwin的输入部分是Chinput, 它的Wrapper库函数都集中在文件cxlib.c中
, 下面我简单分析一下这段代码.大致工作原理见图1. ZhXwin Event Processing(zhx
win.gif)初始化: LibInit()过程为初始化过程. NeedIni变量记录是否已经初始化
了. 1. 记录下替换前原函数的地址, 通过调用dlsym()来获得, 一般来 说下列
函数必需被包装: XDrawString() XDrawImageString() XTextWidth()
XTextExtents() XNextEvent() XLookupString() 2. 调用HZclientIn
it()注册原子, 以避免Chinput被启动多次. XNextEvent() 1. 调用原来的XNex
tEvent()获取事件, 如果是不感兴趣的事件则 马上返回交由客户程序处理. 2.
如果为KeyPress事件, 设置LookupThrough变量为1, 表明正处 在输入过程中, 以
便Chinput本身可以调用原始的XLookupString 函数(参见XLookupString), 并且将
事件交给HZsendKey送至服务 器进行处理. 3. 如果为ClientMessage, 则调用H
ZprocInput()函数来取得 Chinput的输出, 输出有三种可能: a. HZSERVER_OUT
PUT_BAD. Chinput输出出错, 或该ClientMessage 非Chinput发回, 则将此事件转交
由客户程序处理. b. HZSERVER_OUTPUT_NORMAL_KEY 或 HZSERVER_OUTPUT_FUNCTIO
N_KEY Chinput没有启动, 被disable或者松去的key对chinput没有用时 而由HZ
sendKey()直接返回, 则将最近一次保存下的KeyPress事 件返回给客户. c. HZ
SERVER_OUTPUT_HANZI. Chinput返回一个合法的汉字, 则 将汉字(或词组)保存到全
局变量buf中, 并且置LookupThrough 变量为0, 表明Chinput不在输入过程中. 客户
此时可以调用 XLookupString()得到buf中的汉字. (函数中的变量pressed,
LookupState含义有点不明, 有人发现 其用途的请E-Mail告之.) XLookupStrin
g() 通过判断LookupThrough变量获取Chinput服务器目前是否处于 输入过
程中以便能正确返回. XDrawString() 1. 设置字体(作者这里写得有点乱...,
另外如果要扩充功能以 便对不同大小的英文字选取相应的中文字体这一部分可以
加在 这段代码中). 2. CopyGB()用来从字符串中分辨出高位为1的字符, 并
用函数 Strip81()将这些字符高位的1去掉, 这个过程就是内码转换成 国标码的
过程. 然后对这些字符用XDrawString16来显示相应 的中文. 对ASCII字符用原XDr
awString处理. 这里要注意的问 题是x变量的增量通过包装后的XTextWidth获得.
XTextWidth() 对中文和西文字符分别用XTextWidth16和XTextWidth来计算其
宽度, 内码辨认基本同XDrawString(). ...其他函数略.个人观点补充: 对L
ookupThrough的方法, 可以通过另外一种途径解决. 构造一XEvent结构变量, 设ty
pe为KeyPress, keycode另取一 特定的值(比如FFFF?)表明有汉字被输入, 当从Chi
nput处得 到合法的汉字输出时, 将此结构变量返回作为一个KeyPress 事件, 当
客户调用XLookupString查字符串时, 通过检查 keycode域得知是否将缓存区中的汉
字返回. 初始化过程可以通过包装XOpenDisplay()来实现. 省得每段 可能
被调用代码前判断是否已经初始化了. 通过改变HZFONT可以定义新的用于中文显示
的字体.
附cxlib.c源程序:
/*
Copyright (C) Min Gong Helsinki University of Technology
min@foto.hut.fi
Copyright terms:
GPL V2
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
#include <stdarg.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <dlfcn.h>
#include "hzclient.h"
/* This definition should be in the resource file! Fixe it later. */
#define HZFONT "-isas-song ti-medium-r-normal--16-160-72-72-c-160-gb2312.
1980-0"
/* All NEWCODE are not working or not yet finished. Dont try them!
#define NEWCODE
*/
static void LibInit(Display *dpy);
static char *Strip81(char *Sstr, char *Dstr, int len);
static int CopyGB(char *Sstr, char *Dstr, int *left);
static int CopyASCII(char *Sstr, char *Dstr, int *left);
/* This is politically incorrect! But Linux Locale doesn't work for
Chinese yet and no one seems like to make it work, so I just make this
dirty hack for now. */
static int StelalrFindFontPair(XFontStruct *font_struct,
XFontStruct **hzfont,
XFontStruct **asciifont);
static XFontStruct *font, *hzfont=NULL;
static int NeedIni=1;
static int LookupThrough=1;
static XGCValues xgcv, usgcv, hgcv;
static GC hgc;
static void *hdl;
#ifdef DEBUG
static FILE *stderrr;
#endif
static struct string{
char buf[20];
int len;
unsigned int state;
KeySym SymRtn;
}buf;
static int (*SXD)(Display*, Drawable, GC, int, int, char*, int);
static int (*SXD16)(Display*, Drawable, GC, int, int, XChar2b*, int);
static int (*SXDImage)(Display*, Drawable, GC, int, int, char*, int);
static int (*SXDImage16)(Display*, Drawable, GC, int, int, XChar2b*, int);
static int (*SXDText)(Display*, Drawable, GC, int, int, XTextItem*, int);
static int (*SXDText16)(Display*, Drawable, GC, int, int, XTextItem16*, int
);
static int (*SXQTE)(Display*, XID, char*, int, int*, int *, int*,
XCharStruct*);
static int (*SXQTE16)(Display*, XID, XChar2b*, int, int*, int*, int*,
XCharStruct*);
static int (*SXTE)(XFontStruct*, char*, int, int*, int*, int*,
XCharStruct*);
static int (*SXTE16)(XFontStruct*, XChar2b*, int, int*, int*, int*,
XCharStruct*);
static int (*SXTW)(XFontStruct*, _Xconst char*, int);
static int (*SXTW16)(XFontStruct*, XChar2b*, int);
static int (*SXNEXTEVENT)(Display*, XEvent*);
static int (*SXLOOKUPSTRING)(XKeyEvent*, char*, int, KeySym*, XComposeStatu
s*);static KeySym (*SXLOOKUPKEYSYM)(XKeyEvent*, int);
static Font (*SXLOADFONT)(Display*, char*);
static XFontStruct* (*SXLOADQUERYFONT)(Display*, char*);
static int (*SXUNLOADFONT)(Display*, Font);
static int (*SXFREEFONTNAMES)(char *);
static int (*SXFREEFONTINFO)(char**, XFontStruct *, int);
#ifdef NEWCODE
static struct FontPair{
XFontStruct *usFont;
XFontStruct *zhFont;
XID FontID;
struct FontPair *Next;
} *FontPairHead = NULL;
int StellarFindFontPair(XFontStruct *Fstruct){
struct FontPair *FPp;
FPp = FontPairHead;
while(FPp != NULL){
if(FPp->usFont
}
void StellarLoadPair(XFontStruct *Fstruct){
}
Font XLoadFont(Display *dpy, _Xconst char *FName){
Font usFont, zhFont, tmpFont;
XFontStruct *tmpFontStruct;
if(NeedIni)LibInit(dpy);
tmpFont = (*SXLOADFONT)(dpy, (char*)FName);
tmpFontStruct = XQueryFont(dpy, (XID)tmpFont);
if(StellarFindFontPair(tmpFontStruct))
return tmpFont;
StellarLoadPair(tmpFontStruct);
return tmpFont;
}
#endif
static void LibInit(Display*);
static void LibInit(Display *dpy){
static LibIni = 1;
if(LibIni){
hdl = dlopen(LIBX11, RTLD_LAZY|RTLD_GLOBAL);
if(!hdl){
fputs(dlerror(), stderr);
exit(1);
}
SXD = dlsym(hdl, "XDrawString");
SXD16 = dlsym(hdl, "XDrawString16");
SXDImage = dlsym(hdl, "XDrawImageString");
SXDImage16 = dlsym(hdl, "XDrawImageString16");
SXDText = dlsym(hdl, "XDrawText");
SXDText16 = dlsym(hdl, "XDrawText16");
SXQTE = dlsym(hdl, "XQueryTextExtents");
SXQTE16 = dlsym(hdl, "XQueryTextExtents16");
SXTE = dlsym(hdl, "XTextExtents");
SXTE16 = dlsym(hdl, "XTextExtents16");
//SXTItem = dlsym(hdl, "XTextItem");
//SXTItem16 = dlsym(hdl, "XTextItem16");
SXTW = dlsym(hdl, "XTextWidth");
SXTW16 = dlsym(hdl, "XTextWidth16");
SXNEXTEVENT = dlsym(hdl, "XNextEvent");
SXLOOKUPSTRING = dlsym(hdl, "XLookupString");
SXLOOKUPKEYSYM = dlsym(hdl, "XLookupKeysym");
SXLOADFONT = dlsym(hdl, "XLoadFont");
SXLOADQUERYFONT = dlsym(hdl, "XLoadQueryFont");
SXUNLOADFONT = dlsym(hdl, "XUnloadFont");
SXFREEFONTNAMES = dlsym(hdl, "XFreeFontNames");
SXFREEFONTINFO = dlsym(hdl, "XFreeFontInfo");
#ifdef DEBUG
stderrr = fopen("/tmp/libst.log", "w");
#endif
LibIni = 0;
}
if(dpy != NULL){
NeedIni = 0;
HZclientInit(dpy);
}
}
int XNextEvent(Display* dpy, XEvent* e){
static XEvent saved, saved1;
static int Pressed = 0, LookupState;
if(NeedIni){
LibInit(dpy);
}
for(;;){
(*SXNEXTEVENT)(dpy, e);
if((e->type != KeyPress) && (e->type != ClientMessage) &&
(e->type != KeyRelease))
return 0;
if(e->type == KeyRelease){
LookupThrough = LookupState;
Pressed = 0;
return 0;
}
if(e->type == KeyPress){
Pressed = 1;
LookupState = LookupThrough = 1;
saved = *e;
HZsendKey(dpy, e->xkey.window, &e->xkey);
}
if(e->type == ClientMessage){
int RtnVal;
unsigned int keycode;
saved1 = *e;
RtnVal = HZprocInput(&e->xclient, &buf.len, &keycode, &buf.state,
buf.buf);
if(RtnVal == HZSERVER_OUTPUT_BAD){
*e = saved1;
return 0;
}
else if((RtnVal == HZSERVER_OUTPUT_NORMAL_KEY) ||
(RtnVal == HZSERVER_OUTPUT_FUNCTION_KEY)){
*e = saved;
return 0;
}
else if(RtnVal == HZSERVER_OUTPUT_HANZI) {
LookupState = LookupThrough = 0;
*e = saved;
return 0;
}
}
}
}
KeySym XLookupKeysym(
XKeyEvent *KEvent,
int index){
if(LookupThrough)
return (*SXLOOKUPKEYSYM)(KEvent, index);
return 0xb5da;
}
int XLookupString(
XKeyEvent *KEvent,
char *RtnBuf,
int bytes,
KeySym *KSym,
XComposeStatus *State){
if(LookupThrough){
return (*SXLOOKUPSTRING)(KEvent, RtnBuf, bytes, KSym, State);
}
bcopy(buf.buf, RtnBuf, buf.len);
RtnBuf[buf.len]=0;
if(KSym != NULL)
*KSym = 0x41;
State = NULL;
return buf.len;
}
#ifdef NEWCODE
int XQueryTextExtents(
Display *dpy,
XID FontId,
_Xconst char *String,
int Nchars,
int *DirRet,
int *FontAscRet,
int *FontDscRet,
XCharStruct *ORet){
int i, j, Extend = 0, cnt = 0, dirRet, fontAscRet, fontDscRet;
char buf[16384];
if(NeedIni) LibInit(dpy);
i = strlen(String);
*FontAscRet = *FontDscRet = 0;
while(i){
j = CopyGB(String[cnt], buf, &i);
buf[j] = 0;
if(j){
Extend += (*SXQTE16)(dpy, (XCHAR2b*)buf, j/2, &dirRet, &fontAscRet,
&fontDscRet);
FontAscRet = (fontAscRet > FontAscRet) ? fontAscRet : FontAscRet;
FontDscRet = (fontDscRet > FontDscRet) ? fontDscRet : FontDscRet;
}
j = CopyASCII(String[cnt], buf, &i);
buf[j] = 0;
if(j){
Extend += (*SXQTE)(dpy, buf, j, &dirRet, &fontAscRet,
&fontDscRet);
FontAscRet = (fontAscRet > FontAscRet) ? fontAscRet : FontAscRet;
FontDscRet = (fontDscRet > FontDscRet) ? fontDscRet : FontDscRet;
}
if((Nchars/2)*2 != Nchars)
i=Nchars/2+1;
else
i=Nchars/2;
(*SXQTE16)(dpy, hzfont->fid, (XChar2b*)String, Nchars, DirRet, FontAscRet
, FontDscRet, ORet);
FontAscRet+=10;
FontDscRet+=10;
return 0;
}
int XTextExtents(
XFontStruct *Fnt,
_Xconst char *String,
int Nchars,
int *DirRet,
int *FontAscRet,
int *FontDscRet,
XCharStruct *ORet){
int i;
if(NeedIni) LibInit();
if((Nchars/2)*2 != Nchars)
i=Nchars/2+1;
else
i=Nchars/2;
(*SXTE16)(Fnt, (XChar2b*)String, Nchars, DirRet, FontAscRet,
FontDscRet, ORet);
FontAscRet+=10;
FontDscRet+=10;
return 0;
}
#endif
/* This might be buggy! */
int XTextWidth(
XFontStruct *font_struct,
_Xconst char *Sstr,
int cnt){
int i, j = 0, k = cnt, l = 0;
char buf[16384], buf1[16384];
XFontStruct *hzfont, *asciifont;
k = cnt;
if(NeedIni){
LibInit(NULL);
return (*SXTW)(font_struct, Sstr, cnt);
}
StelalrFindFontPair(font_struct, &hzfont, &asciifont);
while(k){
i = CopyGB((char*)(Sstr+l), buf, &k);
l += i;
if(i){
buf[i] = 0;
Strip81(buf,buf1,i);
buf1[i] = 0;
j += (*SXTW16)(hzfont, (XChar2b*)buf1, i/2);
}
if(!k) break;
i = CopyASCII((char *)(Sstr+l), buf, &k);
buf[i] = 0;
if(i){
j += (*SXTW)(asciifont, buf, i);
}
}
return j;
}
int XDrawText(
Display* dpy,
Drawable d,
GC gc,
int x,
int y,
XTextItem* items,
int nitems){
return (*SXDText)(dpy, d, gc, x ,y, items, nitems);
/* No time for this yet */
}
int XDrawImageString(
Display *dpy,
Drawable d,
GC gc,
int x,
int y,
_Xconst char *string,
int len){
int left, newx = x, newy = y;
char s[4096];
if(NeedIni)LibInit(dpy);
if(hzfont == NULL){
if(!(hzfont = (*SXLOADQUERYFONT)(dpy, HZFONT))){
#ifdef DEBUG
perror("Font loading failed (XDawImageString)");
#endif
return (*SXDImage)(dpy, d, gc, x, y, (void*)string, len);
}
XGetGCValues(dpy, gc, GCFont, &xgcv);
if(xgcv.font != usgcv.font){
font = XQueryFont(dpy, xgcv.font);
hgcv = usgcv = xgcv;
hgcv.font = hzfont->fid;
hgc = XCreateGC(dpy, d, GCFont, &hgcv);
}
}
XCopyGC(dpy, gc, ~GCFont, hgc);
if(!string || !*string) return 0;
left = len;
while(left > 0){
int NumberOfBytes, CurrentPos = 0;
char TmpBuf[4096], *Pr;
if((NumberOfBytes = CopyGB((char*)string, s+CurrentPos, &left))){
Pr = (char *)Strip81(s+CurrentPos, TmpBuf, NumberOfBytes);
(*SXDImage16)(dpy, d, hgc,
newx, newy, (XChar2b *)TmpBuf, NumberOfBytes/2);
newx += XTextWidth16(hzfont,(XChar2b *)TmpBuf, NumberOfBytes/2);
string += NumberOfBytes;
CurrentPos += NumberOfBytes;
if(left <= 0) break;
}
if((NumberOfBytes = CopyASCII((char*)string, s+CurrentPos, &left))){
(*SXDImage)(dpy, d, gc,
newx, newy, s+CurrentPos, NumberOfBytes);
newx += XTextWidth(font, s+CurrentPos, NumberOfBytes);
string += NumberOfBytes;
CurrentPos += NumberOfBytes;
}
}
return 0;
}
int iszh(char*);
int ishz(char*);
int isgb(char*);
int XDrawString(
Display *dpy,
Drawable d,
GC gc,
int x,
int y,
_Xconst char *string,
int len){
int left, newx = x, newy = y;
char s[4096]; /* This might be very bad.
If strlen(string)>4096 I'm screwed up. */
if(NeedIni){
LibInit(dpy);
}
if(hzfont == NULL){
if(!(hzfont = (*SXLOADQUERYFONT)(dpy, HZFONT))){
#ifdef DEBUG
perror("Font loading failed. XDrawString");
#endif
return (*SXD)(dpy, d, gc, x, y, (char*)string, len);
}
XGetGCValues(dpy, gc, GCFont, &xgcv);
if(xgcv.font != usgcv.font){
font = XQueryFont(dpy, xgcv.font);
hgcv = usgcv = xgcv;
hgcv.font = hzfont->fid;
hgc = XCreateGC(dpy, d, GCFont, &hgcv);
}
}
XCopyGC(dpy, gc, ~GCFont, hgc);
if(!string || !*string) return 0;
left = len;
while(left > 0){
int NumberOfBytes, CurrentPos = 0;
char TmpBuf[4096], *Pr;
if((NumberOfBytes = CopyGB((char*)string, s+CurrentPos, &left))){
Pr = (char *)Strip81(s+CurrentPos, TmpBuf, NumberOfBytes);
(*SXD16)(dpy, d, hgc,
newx, newy, (XChar2b *)TmpBuf, NumberOfBytes/2);
newx += XTextWidth16(hzfont,(XChar2b *)TmpBuf, NumberOfBytes/2);
string += NumberOfBytes;
CurrentPos += NumberOfBytes;
if(left <= 0) break;
}
if((NumberOfBytes = CopyASCII((char*)string, s+CurrentPos, &left))){
(*SXD)(dpy, d, gc,
newx, newy, s+CurrentPos, NumberOfBytes);
newx += XTextWidth(font, s+CurrentPos, NumberOfBytes);
string += NumberOfBytes;
CurrentPos += NumberOfBytes;
}
}
return 0;
}
static int CopyGB(char *Sstr, char *Dstr, int *left){
int i = 0;
unsigned short *pS = (unsigned short*)Sstr;
unsigned short *pD = (unsigned short*)Dstr;
while(*left > 0){
if((*pS & 0x80) == 0x80){
*left -= 2;
*pD = *pS;
pD++; pS++;
i += 2;
}
else{
break;
}
}
if(*left < 0){
*left = 0;
// i -= 1;
}
return i;
}
static int CopyASCII(char *Sstr, char *Dstr, int *left){
int i = 0;
char *pS = Sstr;
char *pD = Dstr;
while(*left){
if((*pS & 0x80) != 0x80){
*left -= 1;
*pD = *pS;
pD++; pS++;
i += 1;
}
else{
break;
}
}
return i;
}
static char *Strip81(char *Sstr, char *Dstr, int left)
{
int i;
if(left == 0) return NULL;
for(i = 0; i < left; i++)
Dstr[i] = Sstr[i] & 0x7f;
return Dstr;
}
static int StelalrFindFontPair(XFontStruct *font_struct,
XFontStruct **Hzfont,
XFontStruct **asciifont){
*Hzfont = hzfont;
*asciifont = font_struct;
return 0;
}
--
☆ 来源:.哈工大紫丁香 bbs.hit.edu.cn.[FROM: bin@mtlab.hit.edu.cn]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:204.158毫秒