/*
###
### This file is part of
###
###                        TurboLinux  ZWinPro
###
###                 Copyright (C) 1999-2000 TurboLinux, Inc. 
###                        All Rights Reserved
### Distributed under the terms of the GNU General Public License (GPL)
###
###
### Authors:     TurboLinux Chinese Development Team:
###              Justin Yu   <justiny@turbolinux.com.cn>
###              Sean Chen   <seanc@turbolinux.com.cn>
###              Daniel Fang <danf@turbolinux.com.cn>
### WWW:         http://www.turbolinux.com.cn/ZWinPro/
### FTP:         ftp://ftp.turbolinux.com.cn/pub/ZWinPro/
###
*/

/*
 * Copyright 1997 by Yu Mingjian.          All Rights Reserved
 * Distributed under the terms of the GNU General Public License (GPL)
 *
 * Permission to retain, use, modify, copy, and distribute Chinput1.0
 * in source or binary and its documentation (hereafter, the Software)
 * for noncommercial purpose is hereby granted to you without a fee,
 * provided that this entire copyright and permission notice appear in
 * all such copies, that no charge be associated with such copies,
 * that distribution of derivative works (including value-added
 * distributions such as with additional input dictionaries or fonts)
 * include clarification that such added or derived parts are not from
 * the original Software, and that the names of the author(s) not be
 * used to endorse or promote such works. The author(s) of the software
 * makes no representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 *
 * THE AUTHOR(S) OF THE SOFTWARE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
 * IN NO EVENT SHALL THE AUTHOR(S) OF THE SOFTWARE BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * Author: Yu Mingjian, Institute of High Energy Physics, Academia Sinica
 * 
 */

#include "all.h"

static void chinput_util_draw_box (GC gc, int left, int top, int right, int bottom)
{
	XSetForeground(display, gc,
			WhitePixel(display, screen_num) ^
			BlackPixel(display, screen_num));
	XDrawRectangle(display, parent, gc, left,
			top, right - left, bottom - top);
}

static void chinput_util_undraw_box (GC gc, int left, int top,int  right, int bottom)
{
	chinput_util_draw_box (gc, left,top,right,bottom);
}

static int chinput_util_set_win (void)
{
	Window w, r;
	int win_x, win_y;
	unsigned int mask;

	static Cursor hand_cursor;
	static int first_time = True;

	if (first_time)
	{
		boxgc = XCreateGC (display, parent, 0, NULL);
		XSetSubwindowMode (display, boxgc, IncludeInferiors);
		XSetForeground (display, boxgc, BlackPixel (display, screen_num));
		XSetFunction (display, boxgc, GXxor);
		hand_cursor = XCreateFontCursor (display, XC_hand2);

		first_time = False;
	}

	XDefineCursor (display, window1, hand_cursor);

	XQueryPointer (display, parent,
			&r, &w, &press_x, &press_y,
			&win_x, &win_y, &mask);

	if (w == window1)
		movingwin = window1;
	else if(w == window2)
		movingwin = window2;
	else if(w == window3)
		movingwin = window3;
	else
		return -1;

	XChangeActivePointerGrab (display,
			PointerMotionMask | ButtonMotionMask
			| ButtonReleaseMask | OwnerGrabButtonMask,
			hand_cursor, CurrentTime);

	XGrabServer (display);
	return 0;
}

static void chinput_util_set_motion (void)
{
	Window w, r;
	int win_x, win_y;
	unsigned int mask;

	if (boxdrawn)
		chinput_util_undraw_box (boxgc, left, top, right, bottom);

	//while(XCheckTypedEvent(display, MotionNotify, &report));

	XQueryPointer (display, parent,
			&r, &w, &move_x, &move_y,
			&win_x, &win_y, &mask);

	if (movingwin == window1)
	{
		if (dmode == HZSERVER_DMODE_ROOT)
		{
			left = move_x - press_x + HZServer.hzIwin.x;
			top  = move_y - press_y + HZServer.hzIwin.y;
			right = left + HZServer.hzIwin.width +
					HZServer.hzBwin.width;
			bottom = top + HZServer.hzIwin.height +
					((HZServer.hzKwin.onflag)?
					 HZServer.hzKwin.height : 0);
		}
		else
		{
			left = move_x - press_x + HZServer.hzIwin.x;
			top  = move_y - press_y + HZServer.hzIwin.y;
			right = left + HZServer.hzCwin.w1;
			bottom = top + HZServer.hzCwin.h1;
		}
	}
	else if(movingwin == window2) //moving candidate window
	{
		left = move_x - press_x + HZServer.hzCwin.x2;
		top  = move_y - press_y + HZServer.hzCwin.y2;
		right = left + HZServer.hzCwin.w2;
		bottom = top + HZServer.hzCwin.h2;
	}
	else if(movingwin == window3) //moving virtual keyboard
	{
		left = move_x - press_x + HZServer.hzVKwin.x;
		top  = move_y - press_y + HZServer.hzVKwin.y;
		right = left + HZServer.hzVKwin.w;
		bottom = top + HZServer.hzVKwin.h;
	}
#if 0
	else if(movingwin == window4) //moving handwriting pad
	{
		left = move_x - press_x + HZServer.hzHWwin.x;
		top  = move_y - press_y + HZServer.hzHWwin.y;
		right = left + HZServer.hzHWwin.w;
		bottom = top + HZServer.hzHWwin.h;
	}
#endif
	chinput_util_draw_box (boxgc, left, top, right, bottom);
	boxdrawn = True;
}

static void chinput_util_move_win (void)
{
	Window w, r;
	int release_x, release_y, win_x, win_y;
	int curx1, cury1, curx2, cury2;
	unsigned int mask;
	int dw = DisplayWidth (display, screen_num);
	int dh = DisplayHeight (display, screen_num);

	if (boxdrawn)
		chinput_util_undraw_box (boxgc, left, top, right, bottom);

	XQueryPointer (display, parent,
			&r, &w, &release_x, &release_y,
			&win_x, &win_y, &mask);

	if (movingwin == window1 || movingwin == window2)
	{
		// Check valid position before moving
		curx1 = HZServer.hzIwin.x + (release_x - press_x);
		cury1 = HZServer.hzIwin.y + (release_y - press_y);

		if (curx1 < 0)
			curx1 = 0;
		if (cury1 < 0)
			cury1 = 0;

		if (dmode == HZSERVER_DMODE_ROOT)
		{
			XMoveWindow (display, window1, curx1, cury1);
			XRaiseWindow (display, window1);
		}
		else if(dmode == HZSERVER_DMODE_ONSPOT)
		{
			if (curx1 + HZSERVER_CHOICE_WIN16_WIDTH + 4 > dw)
				curx1 = curx1 - HZSERVER_CHOICE_WIN16_WIDTH - 4;
			if (cury1 + HZSERVER_CHOICE_WIN16_HEIGHT + 4 > dh)
				cury1 = dh - HZSERVER_CHOICE_WIN16_HEIGHT - 4;

			HZServer.hzCwin.x2 = curx1;
			HZServer.hzCwin.y2 = cury1;
			XMoveWindow (display, window2, curx1, cury1);
			XRaiseWindow (display, window2);
		}
		else if (dmode == HZSERVER_DMODE_OVERSPOT)
		{
			if (curx1 + width_input_win + 4 > dw)
				curx1 = dw - width_input_win - 4;
			if (cury1 + HZSERVER_INPUT_WIN16_HEIGHT + 4 > dh)
				cury1 = cury1 - HZSERVER_INPUT_WIN16_HEIGHT - 32;
			if (cury1 + HZSERVER_INPUT_WIN16_HEIGHT + 4 > dh)
				cury1 = dh - HZSERVER_INPUT_WIN16_HEIGHT - 4;

			XMoveWindow (display, window1, curx1, cury1);
			XRaiseWindow (display, window1);

			curx2 = HZServer.hzIwin.x + (release_x - press_x)
					+ width_input_win + 4,
			cury2 = HZServer.hzIwin.y + (release_y - press_y);

			if (curx2 < curx1 + width_input_win + 4) 
				curx2 = curx1 + width_input_win + 4;
			if (cury2 < 0)
				cury2 = 0;
			if (curx2 + HZSERVER_CHOICE_WIN16_WIDTH + 4 > dw)
				curx2 = curx1 - HZSERVER_CHOICE_WIN16_WIDTH - 4;
			if (cury2 + HZSERVER_CHOICE_WIN16_HEIGHT + 4 > dh)
				cury2 = dh - HZSERVER_CHOICE_WIN16_HEIGHT - 4;

			HZServer.hzCwin.x2 = curx2;
			HZServer.hzCwin.y2 = cury2;
			XMoveWindow (display, window2, curx2, cury2);
			XRaiseWindow (display, window2);
		}
		XDefineCursor (display, window1, arrow_cursor);
		HZServer.hzIwin.x =  curx1;
		HZServer.hzIwin.y =  cury1;
	}
	else if (movingwin == window3)
	{
		curx1 = HZServer.hzVKwin.x + (release_x - press_x);
		cury1 = HZServer.hzVKwin.y + (release_y - press_y);
		XMoveWindow (display, window3, curx1, cury1);
		XRaiseWindow (display, window3);
		XDefineCursor (display, window3, arrow_cursor);
		HZServer.hzVKwin.x =  curx1;
		HZServer.hzVKwin.y =  cury1;
	}

	// XFlush(display);
	boxdrawn = False;
	XUngrabServer(display);
}

void chinput_util_err_exit (void)
{
	chinput_util_print_sys_err ("Error detected. Abort.\n");
	chinput_util_free_all ();
	exit (1);
}

void chinput_util_print_sys_err (char *err_format, ...)
{
	va_list ap;
	va_start ( ap, err_format );
	vfprintf ( HZSERVER_SYS_ERR_FP, err_format, ap );
	va_end (ap);
}

void chinput_util_print_sys_msg (char *msg_format, ...)
{
	va_list ap;
	va_start ( ap, msg_format );
	vfprintf ( HZSERVER_SYS_MSG_FP, msg_format, ap );
	va_end (ap);
}

void chinput_util_print_dbg_msg (char *msg_format, ...)
{
	va_list ap;
	va_start ( ap, msg_format );
	vfprintf ( HZSERVER_DEBUG_MSG_FP, msg_format, ap );
	va_end (ap);
}

//
// Return encoding code depends on "Locale".
// Encoding code is defined as int. Ref: hzclient.h
// Locale constant is defined as char*. Ref: hzserver.h
int chinput_util_locale_to_enc (const char *locale)
{
//	printf ("locale_to_enc, log: %s\n", locale);
	if ( (strncasecmp (locale, HZSERVER_BIG5_LOCALE,
				strlen (HZSERVER_BIG5_LOCALE)) == 0)
			|| (strncasecmp (locale, big5locale, strlen (big5locale)) == 0))
	{
		return HZSERVER_ENCODING_BIG5;
	}
	else if ( (strncasecmp (locale, HZSERVER_BIG5HKSCS_LOCALE,
				strlen (HZSERVER_BIG5HKSCS_LOCALE)) == 0)
			|| (strncasecmp (locale, big5hkscslocale,
					strlen (big5hkscslocale)) == 0))
	{
		return HZSERVER_ENCODING_BIG5HKSCS;
	}
	else if ( (strncasecmp (locale, HZSERVER_GB2312_LOCALE,
				strlen (HZSERVER_GB2312_LOCALE)) == 0)
			|| (strncasecmp (locale, gb2312locale,
					strlen (gb2312locale)) == 0))
	{
		return HZSERVER_ENCODING_GB2312;
	}
	else if ( (strncasecmp (locale, HZSERVER_GB18030_LOCALE,
				strlen (HZSERVER_GB18030_LOCALE)) == 0)
			|| (strncasecmp (locale, gb18030locale,
					strlen (gb18030locale)) == 0))
	{
		return HZSERVER_ENCODING_GB18030;
	}
	// We may need to check locale without encoding info for emgr.
	else
		return HZSERVER_ENCODING_BIG5;
}
      
//code from gtkkeyboard
Time chinput_util_get_time (void)
{       
	int  tint; 
	struct timeval  tv;
	struct timezone tz; /* is not used since ages */
	gettimeofday (&tv, &tz);
	tint = (int)tv.tv_sec * 1000;
	tint = tint/1000 * 1000;
	tint = tint + tv.tv_usec/1000;
	return ((Time)tint);
}

void chinput_util_draw_tri_btn (Display *dpy, Window win, GC dimgc, GC lightgc,
		int x1, int y1, int x2, int y2, int x3, int y3, Boolean flag)
{
	if (flag)
	{
		XDrawLine(dpy, win, dimgc, x1, y1, x2, y2);
		XDrawLine(dpy, win, dimgc, x1, y1, x2, y2);
		XDrawLine(dpy, win, dimgc, x1, y1, x3, y3);
		XDrawLine(dpy, win, dimgc, x1, y1, x3, y3);
		XDrawLine(dpy, win, lightgc, x3, y3, x2, y2);
		XDrawLine(dpy, win, lightgc, x3, y3, x2, y2);
	}
	else
	{
		XDrawLine(dpy, win, lightgc, x1, y1, x2, y2);
		XDrawLine(dpy, win, lightgc, x1, y1, x2, y2);
		XDrawLine(dpy, win, lightgc, x1, y1, x3, y3);
		XDrawLine(dpy, win, lightgc, x1, y1, x3, y3);
		XDrawLine(dpy, win, dimgc, x3, y3, x2, y2);
		XDrawLine(dpy, win, dimgc, x3, y3, x2, y2);
	}
}

void chinput_util_draw_btn (Display *dpy, Window win, GC dimgc, GC lightgc, 
		int x1, int y1, int x2, int y2, Boolean flag)
{
	if (flag)
	{ /* normal */
		XDrawLine(dpy, win, lightgc, x1, y1, x2-1, y1);
		XDrawLine(dpy, win, lightgc, x1, y1, x1, y2-1);
		XDrawLine(dpy, win, dimgc, x1, y2-1, x2-1, y2-1);
		XDrawLine(dpy, win, dimgc, x2-1, y1, x2-1, y2-1);
	}
	else
	{
		XDrawLine(dpy, win, dimgc, x1, y1, x2-1, y1);
		XDrawLine(dpy, win, dimgc, x1, y1, x1, y2-1);
		XDrawLine(dpy, win, lightgc, x1, y2-1, x2-1, y2-1);
		XDrawLine(dpy, win, lightgc, x2-1, y1, x2-1, y2-1);
	}
}

void chinput_util_draw_btn_ret (Display *dpy, Window win,
		GC dimgc, GC lightgc, int x, int y,
		int bw, int bh, Boolean flag)
{
        if(flag) { /* normal */
                XDrawLine(dpy, win, lightgc, x, y, x+bw*1.5-1, y);
                XDrawLine(dpy, win, lightgc, x, y, x, y+bh-1);
                XDrawLine(dpy, win, dimgc, x+bw*1.5-2, y, x+bw*1.5-2, y+bh*2-1);
                XDrawLine(dpy, win, lightgc, x-bw/2, y+bh, x, y+bh);
                XDrawLine(dpy, win, dimgc, x-bw/2, y+bh*2-2, x+bw*1.5-2,y+bh*2-2);
                XDrawLine(dpy, win, lightgc, x-bw/2, y+bh, x-bw/2, y+bh*2-2);
        } else {
                XDrawLine(dpy, win, dimgc, x, y, x+bw*1.5-1, y);
                XDrawLine(dpy, win, dimgc, x, y, x, y+bh-1);
                XDrawLine(dpy, win, lightgc,x+bw*1.5-2, y, x+bw*1.5-2, y+bh*2-1);
                XDrawLine(dpy, win, dimgc, x-bw/2, y+bh, x, y+bh);
                XDrawLine(dpy, win, lightgc, x-bw/2, y+bh*2-2, x+bw*1.5-2,y+bh*2-2);
                XDrawLine(dpy, win, dimgc, x-bw/2, y+bh, x-bw/2, y+bh*2-2);
        }
}

void chinput_util_wait_btn_release (void)
{
	XEvent ev;
	XGrabServer (display);
	while (1)
	{
		XNextEvent (display, &ev);
		switch  (ev.type)
		{
			case ButtonRelease:
				butx = ev.xbutton.x;
				buty = ev.xbutton.y;
				XUngrabServer (display);
				return;
			default:
				break;
		}
	}
}

// Check if point x0,y0 is within the area, x1,y1 and x2,y2
Bool chinput_util_in_box(int x0, int y0, int x1, int y1, int x2, int y2)
{
	if ( x0 >= x1 && x0 <=x2 && y0 >= y1 && y0 <=y2 )
		return True;
	else
		return False;
}

//draw sunken 3d border
void chinput_util_draw_sunken_border(Display *dpy, Window win,
		GC lightgc, GC dimgc, int x1, int y1, int x2, int y2)
{
	// Draw border
	// Top line
	XDrawLine (dpy, win, lightgc, x1, y1, x2-1, y1);
	XDrawLine (dpy, win, lightgc, x1, y1+1, x2-1, y1+1);
	XDrawLine (dpy, win, dimgc, x1, y1+2, x2-1, y1+2);
	XDrawLine (dpy, win, dimgc, x1, y1+3, x2-1, y1+3);
	
	// Bottom
	XDrawLine (dpy, win, lightgc, x1, y2-4, x2-1, y2-4);
	XDrawLine (dpy, win, lightgc, x1, y2-3, x2-1, y2-3);
	XDrawLine (dpy, win, dimgc, x1, y2-2, x2-1, y2-2);
	XDrawLine (dpy, win, dimgc, x1, y2-1, x2-1, y2-1);
	
	// Left
	XDrawLine (dpy, win, lightgc, x1, y1+1, x1, y2-1);
	XDrawLine (dpy, win, lightgc, x1+1, y1+1, x1+1, y2-1);
	XDrawLine (dpy, win, dimgc, x1+2, y1+1, x1+2, y2-1);
	XDrawLine (dpy, win, dimgc, x1+3, y1+1, x1+3, y2-1);

	// Right
	XDrawLine (dpy, win, lightgc, x2-4, y1+2, x2-4, y2-2);
	XDrawLine (dpy, win, lightgc, x2-3, y1+2, x2-3, y2-2);
	XDrawLine (dpy, win, dimgc, x2-2, y1+2, x2-2, y2-2);
	XDrawLine (dpy, win, dimgc, x2-1, y1+2, x2-1, y2-2);
}

//code from gtkkeyboard
static KeySym chinput_util_char_to_keysym (unsigned char i)
{
	KeySym x;
	char subbuf[15];

	if (i == 0xf6)
		return XK_odiaeresis;

	//warning: isalpha,isdigit maybe affected by locale! Justiny
	if (isalpha(i))  /* Hopefully most common event */
	{
		sprintf(subbuf,"%c",i);
		x = XStringToKeysym(subbuf);
		return(x);
	} /* End if */
	else if(isdigit(i))
	{
		switch(i)
		{
			case '0': return(XK_0); case '1': return(XK_1);
			case '2': return(XK_2); case '3': return(XK_3);
			case '4': return(XK_4); case '5': return(XK_5);
			case '6': return(XK_6); case '7': return(XK_7);
			case '8': return(XK_8); case '9': return(XK_9);
			default:  return(XK_0); /* Shouldn't happen */
		} /* End switch */
	} /* End if */

	switch(i)
	{
		case '>':  return(XK_greater);   /* Cover likely HTML tag elements 1st */
		case '<':  return(XK_less);
		case '/':  return(XK_slash);
		case ' ':  return(XK_space);
		case '=':  return(XK_equal);
		case '+':  return(XK_plus);
		case '!':  return(XK_exclam);
		case '&':  return(XK_ampersand);
		case '(':  return(XK_parenleft);
		case ')':  return(XK_parenright);
		case '"':  return(XK_quotedbl);
		case '-':  return(XK_minus);
		case '*':  return(XK_asterisk);
		case '.':  return(XK_period);
		case '\b': return(XK_BackSpace);
		case '\t': return(XK_Tab);
// 		case '\n': return(XK_Return);
		default:   return((KeySym)i);
	} /* End switch */
	/* I have no idea, so we'll just cast it and hope we're right.  :) */
	return 0;
} /* End chinput_util_char_to_keysym () */

void chinput_util_send_keysym (unsigned long keysym,
		Boolean f_shift, Boolean f_control, Boolean f_alt)
{
	Window twin;
	int revert_to_return;
        XEvent xkp;

	XGetInputFocus(display, &twin, &revert_to_return);

	if (!twin)
		return;

	xkp.type                = KeyPress;
	xkp.xkey.type           = KeyPress;
	xkp.xkey.display        = display;
	xkp.xkey.serial         = 0L;
	xkp.xkey.send_event     = True;
	xkp.xkey.display        = display;
	xkp.xkey.x = xkp.xkey.y = xkp.xkey.x_root = xkp.xkey.y_root = 0;
	xkp.xkey.time           = chinput_util_get_time();
	xkp.xkey.same_screen    = True;
	xkp.xkey.subwindow      = None;
	xkp.xkey.window         = twin;
	xkp.xkey.root           = DefaultRootWindow(display);
	xkp.xkey.keycode        = XKeysymToKeycode(display, keysym);
	xkp.xkey.state          = (f_shift ? ShiftMask : 0) |
								(f_control ? ControlMask : 0) |
								(f_alt ? Mod1Mask : 0);

	XSendEvent(display, InputFocus, True,
			KeyPressMask, (XEvent *)&xkp);

	xkp.xkey.type		= KeyRelease;
	xkp.xkey.time		= chinput_util_get_time();

	XSendEvent(display, InputFocus, True,
			KeyReleaseMask, (XEvent *)&xkp);
}

void chinput_util_send_event (unsigned char c, 
	Boolean f_shift, Boolean f_control, Boolean f_alt)
{
	KeySym keysym;

	keysym = chinput_util_char_to_keysym (c);
	if (!keysym)
		return;

	chinput_util_send_keysym (keysym, f_shift, f_control, f_alt);
}

int chinput_util_err_hdlr (Display *dpy, XErrorEvent *ev)
{
	/* one error is cause by invalid window id when sending event */
	//chinput_root_unlock_window ();
	//DestroyInput (&HZServer.cxtermInput.InputMd);
	//fprintf(stderr, "Error occurred.\n");
	//fflush(stderr);
	return 0;
}

void chinput_util_exit(void)
{
//	printf ("chinput_util_exit ()\n");
	chinput_util_free_all ();
	exit(0);
}

void chinput_util_free_all (void)
{
	/* unload fonts */
	XFreeFont(display, font_info);
	XFreeFont(display, hzgb2312font_info);
	XFreeFont(display, hzgb18030font_info);
	XFreeFont(display, hzbig5font_info);
	XFreeFont(display, hzbig5hkscsfont_info);

	/* free gcs */
	XFreeGC (display, HZServer.normalGC);
	XFreeGC (display, HZServer.ledGC);
	XFreeGC (display, HZServer.dimGC);
	XFreeGC (display, HZServer.lightGC);
	XFreeGC (display, HZServer.panelGC);
	XFreeGC (display, HZServer.barGC);
	XFreeGC (display, offspot_gc);
	XFreeGC (display, offspot_bggc);
	XFreeFontSet (display, fontset_gb2312);
	XFreeFontSet (display, fontset_gb18030);
	XFreeFontSet (display, fontset_big5);
	XFreeFontSet (display, fontset_big5hkscs);
	XDestroyWindow (display, window1);
	XDestroyWindow (display, window2);
	XDestroyWindow (display, window3);
	XCloseDisplay (display);

	chinput_free_all_ic ();

	if (chinput_imm)
	{
		IMM_CloseInput (chinput_imm);
	}

	if (chinput_imserver)
	{
		IMM_CloseClient(chinput_imserver);
	}

	LibRelease ();

	IMCloseIM (this_xims);
}

Window chinput_util_create_win (int x, int y, int w, int h)
{
	Window win;
	XSizeHints size_hints;
	XSetWindowAttributes attrib;
	unsigned long attribmask;
	Visual *visual;

	attrib.override_redirect = True;
	attribmask = CWOverrideRedirect;

	// get screen_num size from display structure macro
	screen_num = DefaultScreen (display);
	screen_ptr = DefaultScreenOfDisplay (display);
	visual = DefaultVisual (display, screen_num);

	parent = RootWindow (display, screen_num);

	arrow_cursor = XCreateFontCursor (display, XC_top_left_arrow);

	// create opaque window
	win = XCreateWindow (display, RootWindow (display, screen_num), x, y,
			w, h, 0, DefaultDepth (display, screen_num),
			InputOutput, visual, attribmask, &attrib);

	// Set resize hints
	size_hints.flags = PPosition | PSize; 
	size_hints.x = x;
	size_hints.y = y;
	size_hints.width = w;
	size_hints.height = h;
	size_hints.min_width = 100;
	size_hints.min_height = 100;

	// set Properties for window manager (always before mapping)
	XSetStandardProperties (display, win, "" , "", 0, NULL, 0, &size_hints);

	// Select event types wanted
	XSelectInput (display, win,
			ExposureMask | KeyPressMask
			| ButtonPressMask | ButtonReleaseMask
			| StructureNotifyMask | EnterWindowMask
			| PointerMotionMask | LeaveWindowMask
			| VisibilityChangeMask);

	// set the cursor of this window to arrow cursor, or else
	// a cross cursor appear which is boring... 
	XDefineCursor (display, win, arrow_cursor);

	return win; 
}

void chinput_util_draw_str (Window win, GC gc, int x, int y, char *str, int num)
{
//	int cnt;

	if (num==0)
		return;
//	printf ("chinput_util_draw_str, HZServer.encoding = %d\n", HZServer.encoding);
#if 0
	if (HZServer.encoding == HZSERVER_ENCODING_GB18030)
	{
		for (cnt=0;cnt<num;cnt++)
			printf ("#%02x ", (unsigned char)str[cnt]);
		printf ("\n");
	}
#endif
	if (HZServer.encoding == HZSERVER_ENCODING_GB2312)
		XmbDrawString (display, win, fontset_gb2312, gc, x, y, str, num);
	else if (HZServer.encoding == HZSERVER_ENCODING_GB18030)
	{
//		printf ("Use GB18030 fontset to draw string\n");
		XmbDrawString (display, win, fontset_gb18030, gc, x, y, str, num);
	}
	else if (HZServer.encoding == HZSERVER_ENCODING_BIG5)
		XmbDrawString (display, win, fontset_big5, gc, x, y, str, num);
	else if (HZServer.encoding == HZSERVER_ENCODING_BIG5HKSCS)
		XmbDrawString (display, win, fontset_big5hkscs, gc, x, y, str, num);
}

void chinput_util_draw_mbstr (Display *dpy, Window win,
		XFontSet fs, GC gc,
		int x, int y, char *str, int num)
{
	chinput_util_draw_str(win, gc, x, y, str, num);
}

void chinput_util_draw_btn_label (Display *dpy, Window win,
		GC pgc, int x1, int y1, int x2, int y2,
		int offset, char *str, int len)
{
	//char tmp[3];
	//strncpy(tmp, str, 3);
	XFillRectangle(dpy, win, pgc, x1+1, y1+1 , x2-x1-2, y2-y1-2);
	//XDrawString16(dpy, win, gc, x1 + 1, y1 + offset,
	//        (XChar2b *)tmp, len);
	chinput_util_draw_str(win, HZServer.normalGC, x1 + 1, y1 + offset, str, len);
}

int chinput_util_mb_strlen (char *s, int len)
{   
	int i, mb_len = 0;
	if (len < 0) len = strlen(s);
	for (i=0;i<len;i++) {
		if (s[i] & 0x80) i++;
		mb_len++;
	}
	return mb_len;
}

void chinput_util_candidate_preprocess (char *s)
{
	char *p;

	flag_up = False;
	flag_down = False;

	if (!s || !s[0])
		return;
// Why so inconsistent?
	if (strchr(s, '<'))
		flag_up = True;

	p = strrchr(s, '>');
	if (p)
		flag_down = True;
	else
		return;

	//remove spaces
	p --;
	if (p == s)
		return;
	while (*p == ' ')
		p --;
	*(p+1) = '>';
	*(p+2) = '\0';
}

// code from ami
static Window chinput_util_find_parent (Window win, Atom WM_STATE)
{
	Window root, parent;
	Window *children = NULL;
	unsigned int nchildren;
	Atom type = None;
	int format;
	unsigned long nitems, after;
	unsigned char *data;

	if (!XQueryTree(display, win, &root,
				&parent, &children, &nchildren))
		return 0;
	if (children)
		XFree((char *)children);
	if (root == parent)
		return win;

	XGetWindowProperty(display, parent, WM_STATE, 0, 0, False,
   		AnyPropertyType, &type, &format, &nitems, &after, &data);
	if (type)
		return parent;

	return chinput_util_find_parent(parent, WM_STATE);
}

// code from ami
Window chinput_util_find_top_win (Window win)
{
	Atom WM_STATE;
	Atom type = None;
	int format;
	unsigned long nitems, after;
	unsigned char *data;
	Window inf;

	WM_STATE = XInternAtom(display, "WM_STATE", TRUE);
	if (!WM_STATE)
		return win;

	XGetWindowProperty(display, win, WM_STATE, 0, 0, 
		False, AnyPropertyType, &type, &format,
		&nitems, &after, &data);
	if (type)
		return win;
	inf = chinput_util_find_parent(win, WM_STATE);
	if (!inf)
		inf = win;

	return inf;
}

void chinput_util_proc_left_btn (XEvent report)
{
	int x0 = report.xbutton.x;
	int y0 = report.xbutton.y;
	//int step = ((HZServer.f_width == 16) ? 20 : 28);
	int step = 20;

	// For non-XIM aware program, no control event will forward to 
	// Input Server. Hence, capture the button press event to let 
	// user hide the window explicitly
	if (report.xbutton.state & ControlMask
			&& report.xbutton.button == Button1)
	{
		chinput_toggle_window_map ();
		return;
	}

	if (report.xbutton.window == window3)
	{
		// Virtual keypad
		chinput_vk_proc_window (x0, y0);
		return;
	}

	if (dmode == HZSERVER_DMODE_ROOT)
	{
		// Toggle 2 Byte / 1 Byte Input
		if (chinput_util_in_box (x0, y0,
				HZServer.hzBwin.q_x1, HZServer.hzBwin.q_y1,
				HZServer.hzBwin.q_x2, HZServer.hzBwin.q_y2))
			chinput_root_proc_2byte (report);

		// Toggle 2 Byte Punctuation / 1 Byte Punctuation
		else if (chinput_util_in_box (x0, y0,
				HZServer.hzBwin.p_x1, HZServer.hzBwin.p_y1,
				HZServer.hzBwin.p_x2, HZServer.hzBwin.p_y2))
			chinput_root_proc_punct (report);

		// Lock or Close
		else if (chinput_util_in_box (x0, y0, 
				HZServer.hzBwin.ledx1, HZServer.hzBwin.ledy1,
				HZServer.hzBwin.ledx2, HZServer.hzBwin.ledy2))
			chinput_root_proc_close_window (report);
	
		else if (chinput_util_in_box (x0, y0, 
				HZServer.hzBwin.btnx1, HZServer.hzBwin.btny1,
				HZServer.hzBwin.btnx2, HZServer.hzBwin.btny3))
			chinput_root_proc_pop_kwindow ();
	
		else if (HZServer.hzKwin.onflag && 
				chinput_util_in_box (x0, y0, HZServer.hzKwin.offx1, 
				HZServer.hzKwin.offy1 + HZServer.hzIwin.height,
				HZServer.hzKwin.offx1 + step * 16 - 1,
				HZServer.hzKwin.offy1 + HZServer.hzIwin.height + step * 12 - 1))
			chinput_root_proc_kwindow (x0, y0);
			
		else if (HZServer.hzKwin.onflag && 
				(chinput_util_in_box (x0, y0,
						HZServer.hzKwin.prevx2, HZServer.hzKwin.prevy3,
						HZServer.hzKwin.prevx1, HZServer.hzKwin.prevy1)
				 || chinput_util_in_box (x0, y0,
						HZServer.hzKwin.nextx3, HZServer.hzKwin.nexty3,
						HZServer.hzKwin.nextx1, HZServer.hzKwin.nexty2) ) )
				chinput_root_proc_change_kwindow (x0, y0);

		else
			chinput_util_proc_move_win ();
	}
	else if (dmode == HZSERVER_DMODE_OVERSPOT
			|| dmode == HZSERVER_DMODE_ONSPOT)
	{
		if (flag_up && chinput_util_in_box (x0, y0,
				HZServer.hzCwin.b1_x2, HZServer.hzCwin.b1_y1,
				HZServer.hzCwin.b1_x3, HZServer.hzCwin.b1_y3) )
			chinput_overspot_proc_pointer_pageup (x0, y0);

		else if (flag_down && chinput_util_in_box (x0, y0,
				HZServer.hzCwin.b2_x1, HZServer.hzCwin.b2_y1,
				HZServer.hzCwin.b2_x3, HZServer.hzCwin.b2_y2) )
			chinput_overspot_proc_pointer_pagedown (x0, y0);

		else if (chinput_util_in_box (x0, y0,
				HZServer.hzCwin.s_x1, HZServer.hzCwin.s_y1,
				HZServer.hzCwin.s_x2, HZServer.hzCwin.s_y2) )
			chinput_overspot_proc_pointer_select (x0, y0);

		else
			chinput_util_proc_move_win ();
	}
}

void chinput_util_proc_right_btn (void)
{
	//switching virtual keyboard
	if (HZServer.hzVKwin.onflag)
	{
		HZServer.hzVKwin.onflag = False;
		XUnmapWindow (display, window3);
	}
	else
	{
		HZServer.hzVKwin.onflag = True;
		XMapRaised (display, window3);
	}
}

void chinput_util_proc_move_win (void)
{
	XEvent report;

	if (chinput_util_set_win () == -1)
		return;

	while (1)
	{
		XNextEvent (display, &report);

		switch (report.type)
		{
			case ButtonRelease:
				if (report.xbutton.button == Button1)
				{
					chinput_util_move_win ();
					XRaiseWindow (display, window1);

					if (HZServer.hzVKwin.onflag)	
						XRaiseWindow (display, window3);

					return;
				}
				break;

			case MotionNotify:
				chinput_util_set_motion ();
				break;

			default:
				break;
		}
	}
}

Boolean chinput_util_file_exist (char *fname)
{
	char name[256];
	if ((!strcmp(fname, "gb"))||(strstr(fname,"pyinput"))
			||(!strcmp(fname,"cce")))
		return True;		//ZNPY
	sprintf(name, "%s/%s", dict_path, fname);

	if (access(name, F_OK) == 0)
		return True;
	else
		return False;
}
