$NetBSD: patch-ag,v 1.2 2006/03/14 15:11:54 rxg Exp $

--- src/Cinput/chewing/xcin_chewing.c.orig	2006-03-13 22:26:59.000000000 +0800
+++ src/Cinput/chewing/xcin_chewing.c
@@ -0,0 +1,515 @@
+/*
+ * Bridge interface between libchewing and xcin
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <chewing/chewing.h>
+#include <string.h>
+
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+#include "xcintool.h"
+#include "module.h"
+
+#include <iconv.h>
+#include <langinfo.h>
+
+/* the following keystate masks are defined by xcin */
+#define CAPS_MASK (2)
+#define CTRL_MASK (4)
+
+#define XCIN_BYTE_NATIVE 	2
+#define XCIN_BYTE_UTF8 		3
+
+static int chewing_codeset;
+void preconvert(char *input, char *output, int n_char);
+wchar_t convert(wchar_t input);
+
+static char selKey_define[11] = "1234567890\0"; /* Default */
+static int bAddPhraseForward = 0;
+
+int MakeInpinfo(inpinfo_t *inpinfo, ChewingOutput *pgo);
+
+int CallSetConfig(inpinfo_t *inpinfo, ChewingData *pgdata)
+{
+	ConfigData config;
+	int i;
+
+	config.selectAreaLen = 40;
+	config.maxChiSymbolLen = 16;
+	config.bAddPhraseForward = bAddPhraseForward;
+
+	for (i = 0; i < 10;i++)
+		config.selKey[i] = selKey_define[i];
+
+	SetConfig(pgdata, &config);
+	return 0;
+}
+
+static int
+ChewingInit(void *conf, char *objname, xcin_rc_t *xc)
+{
+	char *cmd[2], kb_type_str[256], *cname;
+	ChewingConf *cf = (ChewingConf *)conf ;
+
+	char *prefix = CHEWING_DATA_DIR;
+
+	/* 
+	 * Because libchewing uses BIG-5 encoding for all its structure
+	 * so we need to check if it is UTF-8 locale and do any conv
+	 */
+	chewing_codeset = (! strcasecmp(xc->locale.encoding,"utf-8")) ?
+		XCIN_BYTE_UTF8 : 
+		XCIN_BYTE_NATIVE;
+	cname = (char *) calloc(3, sizeof(char) * chewing_codeset);
+
+	cmd[0] = objname ;
+	cmd[1] = "KB_TYPE" ;
+	kb_type_str[0] = '\0';
+	get_resource(xc, cmd, kb_type_str, 200, 2);
+	cf->kb_type = KBStr2Num(kb_type_str);
+
+	/* Support selection key definitions */
+	cmd[1] = "SELECTION_KEYS_DEFINE";
+	if (get_resource(xc, cmd, kb_type_str, 256, 2)) {
+		if (strlen(kb_type_str) == 10) {
+			strcpy(selKey_define, kb_type_str);
+			selKey_define[11] = '\0';
+		}
+	}
+	cmd[1] = "ADD_PHRASE_FORWARD";
+	if (get_resource(xc, cmd, kb_type_str, 256, 2)) {
+		if (atoi(kb_type_str) == 1) {
+			bAddPhraseForward = 1;
+		}
+	}
+
+	preconvert("sŭ", cname, 6);
+	cf->inp_cname = strdup(cname); 
+	cf->inp_ename = strdup("Chewing");
+
+	/* Initialize Chewing */
+	ReadTree(prefix);
+	InitChar(prefix);
+	InitDict(prefix);
+	ReadHash(prefix);
+
+	return True;
+}
+
+static int 
+ChewingXimInit(void *conf, inpinfo_t *inpinfo)
+{
+	static char cchBuffer[MAX_PHONE_SEQ_LEN];
+	ChewingConf *cf = (ChewingConf *) conf;
+	int i;
+
+	inpinfo->iccf = (ChewingData *) calloc(1, sizeof(ChewingData));
+
+	InitChewing(inpinfo->iccf, cf);
+	CallSetConfig(inpinfo, (ChewingData *) inpinfo->iccf);
+
+	inpinfo->lcch = (wch_t *) calloc(MAX_PHONE_SEQ_LEN, sizeof(wch_t));
+	inpinfo->lcch_grouping = (ubyte_t *) calloc(MAX_PHONE_SEQ_LEN, sizeof(ubyte_t));
+	inpinfo->cch = cchBuffer;
+
+	inpinfo->inp_cname = cf->inp_cname;
+	inpinfo->inp_ename = cf->inp_ename;
+	inpinfo->area3_len = 2 * ZUIN_SIZE + 4;
+	inpinfo->guimode = GUIMOD_LISTCHAR | GUIMOD_SELKEYSPOT;
+	inpinfo->keystroke_len = 0;
+	inpinfo->s_keystroke = (wch_t *) calloc(3 + MAX_PHRASE_LEN, sizeof(wch_t));
+
+	inpinfo->mcch = (wch_t *) calloc(MAX_CHOICE_BUF, sizeof(wch_t));
+	inpinfo->mcch_grouping = (ubyte_t *) calloc( MAX_SELKEY, sizeof(ubyte_t));
+	inpinfo->n_mcch = 0;
+
+	inpinfo->n_lcch = 0;
+	inpinfo->edit_pos = 0;
+	inpinfo->cch_publish.wch = (wchar_t) 0;
+
+	//  check_winsize(inpinfo, iccf);
+	//  [yet] check winsize is under construction.
+
+	inpinfo->n_selkey = 10;
+	inpinfo->s_selkey = (wch_t *) calloc(MAX_SELKEY, sizeof(wch_t));
+
+	for (i = 0; i < 10; i++) {
+		inpinfo->s_selkey[i].wch = (wchar_t) 0;
+		inpinfo->s_selkey[i].s[0] = selKey_define[i];
+	}
+
+	return True;
+}
+
+void CommitString(inpinfo_t *inpinfo, ChewingOutput *pgo)
+{
+	int i ;
+	char *str;
+	char *output;
+	memset(inpinfo->cch, 0, sizeof(char)*MAX_PHONE_SEQ_LEN);
+	str = (char *) calloc(MAX_PHONE_SEQ_LEN,sizeof(char));
+	output = (char *) calloc(MAX_PHONE_SEQ_LEN / 2 * chewing_codeset, sizeof(char));
+	for (i = 0; i < pgo->nCommitStr; i++)
+		strcat(str, (const char *) pgo->commitStr[i].s);
+	preconvert(str, output, strlen(str));
+	strcat(inpinfo->cch, output);
+	free(str);
+	free(output);
+}
+
+static unsigned int 
+ChewingXimEnd(void *conf, inpinfo_t *inpinfo)
+{
+	ChewingOutput gOut ;
+	int rtn ;
+
+	/* if preedit exists, commit the string */
+	OnKeyEnter(inpinfo->iccf, &gOut);
+
+	rtn = MakeInpinfo(inpinfo, &gOut);
+	free(inpinfo->iccf);
+	free(inpinfo->s_keystroke);
+	free(inpinfo->lcch);
+	free(inpinfo->mcch);
+	free(inpinfo->mcch_grouping);
+
+	inpinfo->iccf = NULL;
+	inpinfo->s_keystroke = NULL;
+	inpinfo->lcch = NULL;
+
+	return rtn ;
+}      
+
+void ShowChoose(inpinfo_t *inpinfo, ChoiceInfo *pci, ChewingOutput *pgo)
+{
+	int i,no,k,len, kk;
+	char *output;
+
+	// for new xcin, there is no need to modify the lcch buffer
+	// instead, we put phrase to choose in mcch
+	no = pci->pageNo * pci->nChoicePerPage;
+	len = 0;
+
+	for (i = 0;i < pci->nChoicePerPage; no++,i++) {
+
+		// in the last page, no will exceed nTotalChoice
+		if( no >= pci->nTotalChoice ) 
+			break;
+		output = (char *) calloc(
+			strlen(pci->totalChoiceStr[no]) * chewing_codeset + 1, 
+			sizeof(char));
+		preconvert(
+			pci->totalChoiceStr[no], output, 
+			strlen(pci->totalChoiceStr[no]));
+		// for each char in the phrase, copy to mcch
+		for (k = 0, kk = 0; output[k] != '\0'; k += chewing_codeset, kk++) {
+			memcpy(inpinfo->mcch[len++].s, &(output[k]), chewing_codeset) ;
+		}
+		free(output);
+		// set grouping to the length of the phrase
+		inpinfo->mcch_grouping[i+1] = kk;
+	}
+	// i stores how many choices are available
+	inpinfo->mcch_grouping[0] = i;
+
+	// set pgstate according to pci->pageNo & pci->nPage
+	if( pci->nPage == 1) {
+		inpinfo->mcch_pgstate = MCCH_ONEPG;
+	}
+	else {
+		if( pci->pageNo == 0 )
+			inpinfo->mcch_pgstate = MCCH_BEGIN;
+		else if( pci->pageNo == pci->nPage - 1)
+			inpinfo->mcch_pgstate = MCCH_END;
+		else
+			inpinfo->mcch_pgstate = MCCH_MIDDLE;
+	}
+
+	inpinfo->n_mcch = len ;
+}
+
+void ShowText(inpinfo_t *inpinfo, ChewingOutput *pgo)
+{
+	int i;
+	memset(inpinfo->lcch, 0, sizeof(wch_t)*MAX_PHONE_SEQ_LEN) ;
+	for (i = 0; i < pgo->chiSymbolBufLen; i++)
+		pgo->chiSymbolBuf[i].wch = convert(pgo->chiSymbolBuf[i].wch);
+	memcpy(inpinfo->lcch, pgo->chiSymbolBuf, sizeof(wch_t) * pgo->chiSymbolBufLen) ;
+	inpinfo->n_lcch = pgo->chiSymbolBufLen ;
+}
+
+void ShowInterval(inpinfo_t *inpinfo, ChewingOutput *pgo)
+{
+	int i, k, begin, len, count, nGroup ;
+	int label[MAX_PHONE_SEQ_LEN] ;
+
+	if( pgo->chiSymbolBufLen == 0) {
+		inpinfo->lcch_grouping[0] = 0 ;
+		return ;
+	}
+
+	// set label
+	for(count=0; count<pgo->chiSymbolBufLen; count++)
+		label[count] = count ;
+
+	for(i=0; i<pgo->nDispInterval; i++) {
+		len = pgo->dispInterval[i].to - pgo->dispInterval[i].from ;
+
+		if( len > 1) {
+			for(k=pgo->dispInterval[i].from; k<pgo->dispInterval[i].to; k++)
+				label[k] = count ;
+			count++ ;
+		}
+	}
+
+	// begin to set lcch_grouping by the label
+	nGroup = 0 ;
+	begin = 0 ;
+	for(i=1; i<pgo->chiSymbolBufLen; i++) {
+		if( label[i] != label[begin] ) {
+			inpinfo->lcch_grouping[++nGroup] = ( i - begin ) ;
+			begin = i ;
+		}
+	}
+	inpinfo->lcch_grouping[++nGroup] = ( i - begin ) ;
+	inpinfo->lcch_grouping[0] = nGroup ;
+}
+
+void ShowStateAndZuin(inpinfo_t *inpinfo, ChewingOutput *pgo)
+{
+	int i, a , len = 0;
+	memset( inpinfo->s_keystroke, 0, sizeof(wch_t)*(3 + MAX_PHRASE_LEN)) ;
+	if(pgo->bShowMsg) {
+		for(i = 0; i < pgo->showMsgLen; i++)
+			pgo->showMsg[i].wch = convert(pgo->showMsg[i].wch);
+		memcpy( inpinfo->s_keystroke, pgo->showMsg, sizeof(wch_t)*pgo->showMsgLen) ;
+		inpinfo->keystroke_len = pgo->showMsgLen ;
+	}
+	else {
+		/*   if(pgo->bChiSym)
+		     strcpy( (char *)inpinfo->s_keystroke[0].s, "") ;
+		     else
+		     strcpy( (char *)inpinfo->s_keystroke[0].s, "^") ;
+
+		     inpinfo->s_keystroke[1].s[0] = ' ' ;
+		     for(i=0,a=2; i<ZUIN_SIZE; i++) */
+		for(i=0,a=0; i<ZUIN_SIZE; i++)
+			if(pgo->zuinBuf[i].s[0] != '\0') {
+				inpinfo->s_keystroke[a++].wch = convert(
+					pgo->zuinBuf[i].wch) ;
+				++ len;
+			}
+		inpinfo->keystroke_len = len;
+	}
+}
+
+void SetCursor(inpinfo_t *inpinfo, ChewingOutput *pgo)
+{
+	inpinfo->edit_pos = pgo->chiSymbolCursor;
+}
+
+int MakeInpinfo(inpinfo_t *inpinfo, ChewingOutput *pgo)
+{
+	int rtnValue = 0 ;
+
+	if(pgo->keystrokeRtn & KEYSTROKE_ABSORB)
+		rtnValue |= IMKEY_ABSORB;
+	if(pgo->keystrokeRtn & KEYSTROKE_IGNORE)
+		rtnValue |= IMKEY_IGNORE;
+	if(pgo->keystrokeRtn & KEYSTROKE_BELL)
+		rtnValue |= IMKEY_BELL;
+	if(pgo->keystrokeRtn & KEYSTROKE_COMMIT) {
+		rtnValue |= IMKEY_COMMIT;
+		CommitString(inpinfo, pgo);
+	}
+
+	if(pgo->pci->nPage != 0) { // in selection mode
+		ShowChoose(inpinfo,pgo->pci,pgo);
+		inpinfo->guimode &= ~GUIMOD_LISTCHAR; 
+	}
+	else { // not in selection mode
+		ShowText(inpinfo, pgo);
+		ShowInterval(inpinfo, pgo);
+		inpinfo->guimode |= GUIMOD_LISTCHAR;
+	}
+	ShowStateAndZuin(inpinfo, pgo);
+	SetCursor(inpinfo, pgo);
+	return rtnValue;
+}
+
+static unsigned int
+ChewingKeystroke(void *conf, inpinfo_t *inpinfo, keyinfo_t *keyinfo)
+{
+	KeySym keysym = keyinfo->keysym;
+	ChewingOutput gOut ;
+	int rtn ;
+	static KeySym last_key = 0;
+
+	// set Chinese / English mode by keystate
+	if( keyinfo->keystate & CAPS_MASK ) { // uppercase
+		SetChiEngMode( inpinfo->iccf, SYMBOL_MODE);
+	}
+	else {  // lower case 
+		SetChiEngMode( inpinfo->iccf, CHINESE_MODE);
+	}
+
+
+	// check no ctrl key was pressed
+	if(keyinfo->keystate >= 0 && !(keyinfo->keystate & CTRL_MASK ) && !(keyinfo->keystate & ShiftMask) ) {
+		switch(keysym) {
+			case XK_Escape:
+				OnKeyEsc(inpinfo->iccf, &gOut) ;
+				inpinfo->n_mcch = 0;
+				break ;
+			case XK_Return:
+				OnKeyEnter(inpinfo->iccf, &gOut) ;
+				inpinfo->n_mcch = 0;
+				break ;
+			case XK_Delete:
+				OnKeyDel(inpinfo->iccf, &gOut) ;
+				break ;
+			case XK_BackSpace:
+				OnKeyBackspace(inpinfo->iccf, &gOut) ;
+				break ;
+			case XK_Up:
+				OnKeyUp(inpinfo->iccf, &gOut);
+				break ;
+			case XK_Down:
+				OnKeyDown(inpinfo->iccf, &gOut) ;
+				break ;
+			case XK_Left:
+				OnKeyLeft(inpinfo->iccf, &gOut) ;
+				break ;
+			case XK_Right:
+				OnKeyRight(inpinfo->iccf, &gOut) ;
+				break ;
+			case XK_Home:
+				OnKeyHome(inpinfo->iccf, &gOut);
+				break;
+			case XK_End:
+				OnKeyEnd(inpinfo->iccf, &gOut);
+				break;
+
+			case XK_Tab:
+				if (last_key == XK_Tab) // double click TAB
+					OnKeyDblTab(inpinfo->iccf, &gOut);
+				else
+					OnKeyTab(inpinfo->iccf, &gOut);
+				break;
+			case XK_Caps_Lock:
+				OnKeyCapslock(inpinfo->iccf, &gOut);
+				break;
+			case ' ': /* Space */
+				OnKeySpace(inpinfo->iccf, &gOut);
+				break;
+			default:
+				OnKeyDefault(inpinfo->iccf, keysym, &gOut);
+				inpinfo->n_mcch = 0;
+				break;
+		}
+	}
+	else if (keyinfo->keystate & ShiftMask) {
+		switch ( keysym ) {
+			case XK_Left:
+				OnKeyShiftLeft(inpinfo->iccf, &gOut) ;
+				break ;
+			case XK_Right:
+				OnKeyShiftRight(inpinfo->iccf, &gOut) ;
+				break;
+			default:
+				OnKeyDefault(inpinfo->iccf, keysym, &gOut);
+				inpinfo->n_mcch = 0;
+		}
+	}
+	else if (keyinfo->keystate & CTRL_MASK) {  // Ctrl-key Mask
+		// We need to fill the 'gOut' variable for output.
+		if (keysym <= '9' && keysym >= '0') 
+			OnKeyCtrlNum(inpinfo->iccf,keysym,&gOut);
+		else 
+			OnKeyCtrlOption(inpinfo->iccf, keysym - 'a' + 1, &gOut);
+	}
+
+
+	last_key = keysym;
+	rtn = MakeInpinfo(inpinfo, &gOut);
+	return rtn ;
+}
+
+static int 
+ChewingShowKeystroke(void *conf, simdinfo_t *simdinfo)
+{
+	simdinfo->s_keystroke = NULL;
+	return False;
+}                              
+
+/* UTF-8 support */
+void
+preconvert(char *input, char *output, int n_char)
+{
+	if (chewing_codeset == XCIN_BYTE_UTF8) {
+		const char *inptr = input;
+		size_t inbytesleft = n_char;
+		size_t outbytesleft = n_char / 2 * 3 + 1;
+
+		char *outptr = output;
+		iconv_t cd;
+
+		cd = iconv_open("UTF-8", "BIG-5");
+		iconv (cd, (char **)&inptr, &inbytesleft, &outptr, &outbytesleft);
+
+		iconv_close(cd);
+	} else 
+		strncpy(output, input, n_char);
+}
+
+wchar_t
+convert(wchar_t input)
+{
+	wch_t output;
+	wch_t temp;
+
+	temp.wch = input;
+	if (chewing_codeset == XCIN_BYTE_UTF8) {
+		const char *inptr = temp.s;
+		size_t inbytesleft = 2;
+		size_t outbytesleft = 3;
+		char *outptr = output.s;
+		iconv_t cd;
+		cd = iconv_open("UTF-8", "BIG-5");
+		iconv (cd, (char **)&inptr, &inbytesleft, &outptr, &outbytesleft);
+		iconv_close(cd);
+		output.s[3] = '\0';
+		return output.wch;
+	}
+
+	return input;
+}
+
+static char zh_chewing_comments[] = 
+	"Chewing : a smart phonetic input method module for XCIN.\n"
+	"By Lu-chuan Kung <lckung@iis.sinica.edu.tw>,\n"
+	"Kang-pen Chen <kpchen@iis.sinica.edu.tw>, and others.\n";
+
+static char *zh_chewing_valid_objname[] = { "chewing", NULL };
+
+module_t module_ptr = {
+	{ 
+		MTYPE_IM,
+		"zh_chewing",			/* name */
+		MODULE_VERSION,			/* version */
+		zh_chewing_comments 
+	},					/* comments */
+	zh_chewing_valid_objname,		/* valid_objname */
+	sizeof(ChewingConf),			/* conf_size */
+	ChewingInit,				/* init */
+	ChewingXimInit,				/* xim_init */
+	ChewingXimEnd,				/* xim_end */
+	ChewingKeystroke,			/* keystroke */
+	ChewingShowKeystroke,			/* show_keystroke */
+	NULL
+};
+
