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)
页面执行时间:210.750毫秒