/*
Copyright 1990-2001 Sun Microsystems, Inc. All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions: The above copyright notice and this
permission notice shall be included in all copies or substantial
portions of the Software.


THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE OPEN GROUP OR SUN MICROSYSTEMS, INC. BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE EVEN IF
ADVISED IN ADVANCE OF THE POSSIBILITY OF SUCH DAMAGES.


Except as contained in this notice, the names of The Open Group and/or
Sun Microsystems, Inc. shall not be used in advertising or otherwise to
promote the sale, use or other dealings in this Software without prior
written authorization from The Open Group and/or Sun Microsystems,
Inc., as applicable.


X Window System is a trademark of The Open Group

OSF/1, OSF/Motif and Motif are registered trademarks, and OSF, the OSF
logo, LBX, X Window System, and Xinerama are trademarks of the Open
Group. All other trademarks and registered trademarks mentioned herein
are the property of their respective owners. No right, title or
interest in or to any trademark, service mark, logo or trade name of
Sun Microsystems, Inc. or its licensors is granted.

*/
#include <string.h>
#ifndef ARCH
#include <sys/systeminfo.h>	/* For os_arch */
#endif
#include <sys/utsname.h>	/* For os_name and os_version */
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
#include "XIMProtocol.hh"
#include "XIMPInputContext.hh"
#include "Xfactory.hh"
#include <X11/Xutil.h>

void
XIMPInputContext::
DelayBind() {
  CreateXIC();
  register_clientwin_destroy_filter();

  SetExtensionCallbacks();
  if (inputstyle & XIMPreeditCallbacks) {
    SetPreeditCallbacks();
  }
  if (inputstyle & XIMStatusCallbacks) {
    SetStatusCallbacks();
  }
  if (focus_win && xic) {
    XSetICValues(xic,
		 XNFocusWindow, focus_win,
		 0);
  }
}

void
XIMPInputContext::
CreateXIC() {
  XIM xim = 0;
  if ((xim = ximp_protocol->getXIM())) {
    xic = XCreateIC(xim,
		    XNInputStyle, inputstyle,
		    XNClientWindow, client_win,
		    0);
    if (!xic) {
      // need to reconnect
      ximp_protocol->CloseIM();
      ximp_protocol->OpenIM();
      if ((xim = ximp_protocol->getXIM())) {
	xic = XCreateIC(xim,
			XNInputStyle, inputstyle,
			XNClientWindow, client_win,
			0);
      }
    }
  }
  return;
}

void
XIMPInputContext::
SetExtensionCallbacks() {
  if (xic)
    XSetICValues(xic,
		 XNPreeditStateNotifyCallback, state_notify_cb,
		 // private XIC attribute
		 "commitStringCallback", commit_string_cb,
		 "forwardEventCallback", forward_event_cb,
		 0);
  return;
}

void
XIMPInputContext::
SetPreeditCallbacks() {
  if (xic) {
    XVaNestedList preedit_attr = XVaCreateNestedList(0,
			     XNPreeditStartCallback, preedit_start_cb,
			     XNPreeditDrawCallback, preedit_draw_cb,
			     XNPreeditCaretCallback, preedit_caret_cb,
			     XNPreeditDoneCallback, preedit_done_cb,
			     0);
    XSetICValues(xic,
		 XNPreeditAttributes, preedit_attr,
		 0);
    XFree(preedit_attr);
  }
  return;
}

void
XIMPInputContext::
SetStatusCallbacks() {
  if (xic) {
    XVaNestedList status_attr = XVaCreateNestedList(0,
			    XNStatusStartCallback, status_start_cb,
			    XNStatusDrawCallback, status_draw_cb,
			    XNStatusDoneCallback, status_done_cb,
			    0);
    XSetICValues(xic,
		 XNStatusAttributes, status_attr,
		 0);
    XFree(status_attr);
  }
}

XIMPInputContext::
XIMPInputContext(XIMProtocol *ximp, XClientMessageEvent *ev) {
  ximp_protocol = ximp;
  xic = 0;
  ev_flow_type = 0;
  ev_masks = 0;
  filters = 0;
  preedit_maxlen = 0;
  preedit_caret_position = 0;
  is_conv_on = 0;
  og_code = og_state = 0;

  // for XIMProtocol::reset() can return reset text
  being_reset = False;
  reset_text = "";

  inputstyle = 0;
  client_win = focus_win = 0;
  preedit_fontname = 0;
  status_fontname = 0;
  attr_mask = 0;
  preedit_start_cb = 0;
  preedit_draw_cb = 0;
  preedit_caret_cb = 0;
  preedit_done_cb = 0;
  status_start_cb = 0;
  status_draw_cb = 0;
  status_done_cb = 0;
  dpy = ev->display;

  user_name = getpwuid(getuid())->pw_name;
  application_name = "Unused";

  xDisplayName = DisplayString(dpy);
  xServerVendor = ServerVendor(dpy);

  /* os properties */
  {
    struct utsname name;
#if 0
    char arch[12];
#endif
    uname(&name);
    os_name = name.sysname;
    os_version = name.release;

#ifdef ARCH
    os_arch = ARCH;
#else
    sysinfo(SI_ARCHITECTURE, arch, sizeof(arch));
    if (strcmp(arch,"sparc") == 0 ) {
      os_arch = "sparc";
    } else if (strcmp(arch,"i386") == 0 ) {
      /* use x86 to match the value received on win32 */
      os_arch = "x86";
    } else if (strcmp(arch, "ppc") == 0 ) {
      os_arch = "ppc";
    } else {
      os_arch = "Unknown";
    }
#endif
  }

  memset((void*)&preedit_attr4, 0, sizeof(Ximp_PreeditPropRec4));
  memset((void*)&status_attr4, 0, sizeof(Ximp_StatusPropRec4));

  inputstyle = ev->data.l[2];
  set_client_window(ev->data.l[1]);

  char *prop_ret = (char*)0;
  proto_version = XIMP_VERSION_UNKNOWN;

  if (ximp_protocol->get_protocol_version(client_win, prop_ret)) {
    if (prop_ret != 0 && *prop_ret != 0) {
      if (!strcmp(prop_ret, "XIMP.4.0")) {
	proto_version = XIMP_VERSION_40;
      } else if (!strcmp(prop_ret, "XIMP.3.5")) {
	proto_version = XIMP_VERSION_35;
      } else if (!strcmp(prop_ret, "XIMP_1.0")) {
	proto_version = XIMP_VERSION_40;
      } else if (!strcmp(prop_ret, "XIMP.3.4")) {
	proto_version = XIMP_VERSION_35;
      }
      XFree(prop_ret);
    }
  }

  CreateXIC();

  register_clientwin_destroy_filter();

  state_notify_cb = new XIMCallback2;
  state_notify_cb->client_data = (char*)this;
  state_notify_cb->callback = state_notify_cbproc;

  commit_string_cb = new XIMCallback2;
  commit_string_cb->client_data = (char*)this;
  commit_string_cb->callback = commit_string_cbproc;

  forward_event_cb = new XIMCallback2;
  forward_event_cb->client_data = (char*)this;
  forward_event_cb->callback = forward_event_cbproc;

  SetExtensionCallbacks();

  // callback styles?
  if (inputstyle & XIMPreeditCallbacks) {
    preedit_start_cb = new XIMCallback1;
    preedit_start_cb->client_data = (char*)this;
    preedit_start_cb->callback = preedit_start_cbproc;

    preedit_draw_cb = new XIMCallback2;
    preedit_draw_cb->client_data = (char*)this;
    preedit_draw_cb->callback = preedit_draw_cbproc;

    preedit_caret_cb = new XIMCallback2;
    preedit_caret_cb->client_data = (char*)this;
    preedit_caret_cb->callback = preedit_caret_cbproc;

    preedit_done_cb = new XIMCallback2;
    preedit_done_cb->client_data = (char*)this;
    preedit_done_cb->callback = preedit_done_cbproc;

    SetPreeditCallbacks();
  }
  if (inputstyle & XIMStatusCallbacks) {
    status_start_cb = new XIMCallback2;
    status_start_cb->client_data = (char*)this;
    status_start_cb->callback = status_start_cbproc;

    status_draw_cb = new XIMCallback2;
    status_draw_cb->client_data = (char*)this;
    status_draw_cb->callback = status_draw_cbproc;

    status_done_cb = new XIMCallback2;
    status_done_cb->client_data = (char*)this;
    status_done_cb->callback = status_done_cbproc;

    SetStatusCallbacks();
  }
  unsigned long mask = ev->data.l[3];
  get_xicvalues(mask);

  if (focus_win == 0) {
    focus_win = client_win;
  }

  select_destroy_emask();

  if (is_version_40()) {
    if (((ev_flow_type == XIMProtocol::XIMP_FE_TYPE2) ||
	 (ev_flow_type == XIMProtocol::XIMP_FE_TYPE3))) {
      select_keypress_emask();
      select_keyrelease_emask();
    }
  }
}

void
XIMPInputContext::state_notify_cbproc(XIC xic, XPointer client_data,
				       XPointer call_data_p) {
  XIMPreeditStateNotifyCallbackStruct *call_data =
    (XIMPreeditStateNotifyCallbackStruct *)call_data_p;
  XIMPInputContext *ic = (XIMPInputContext*)client_data;
  if (call_data->state == XIMPreeditDisable) {
    ic->conversion_end(False);
  }
  if (call_data->state == XIMPreeditEnable) {
    ic->conversion_start(False);
  }
}

// XIM callbacks
int
XIMPInputContext::preedit_start_cbproc(XIC xic, XPointer client_data,
				       XPointer call_data) {
  XIMPInputContext *ic = (XIMPInputContext*)client_data;
  ic->preedit_start((IMPreeditStruct*)0);
  return -1;
}

void
XIMPInputContext::preedit_draw_cbproc(XIC xic, XPointer client_data,
				      XPointer call_data_p) {
  XIMPInputContext *ic = (XIMPInputContext*)client_data;
  XIMPreeditDrawCallbackStruct *call_data =
    (XIMPreeditDrawCallbackStruct*)call_data_p;
  char *mb_data;
  XTextProperty prop;
  prop.value = 0;
  if (call_data->text) {
    // call_data is native encoding, so need to convert to CT
    mb_data = call_data->text->string.multi_byte;
    XmbTextListToTextProperty(ic->dpy, &mb_data,
			      1, XCompoundTextStyle, &prop);
    call_data->text->string.multi_byte = (char*)prop.value;
  }
  ic->preedit_draw(call_data);

  if (call_data->text) call_data->text->string.multi_byte = mb_data;
  if (prop.value) XFree(prop.value);
  return;
}
void
XIMPInputContext::preedit_caret_cbproc(XIC xic, XPointer client_data,
				       XPointer call_data_p) {
  XIMPInputContext *ic = (XIMPInputContext*)client_data;
  XIMPreeditCaretCallbackStruct *call_data =
    (XIMPreeditCaretCallbackStruct*)call_data_p;
  ic->preedit_caret(call_data);
}
void
XIMPInputContext::preedit_done_cbproc(XIC xic, XPointer client_data,
				       XPointer call_data) {
  XIMPInputContext *ic = (XIMPInputContext*)client_data;
  ic->preedit_done((IMPreeditStruct*)0);
}
void
XIMPInputContext::status_start_cbproc(XIC xic, XPointer client_data,
				      XPointer call_data) {
  XIMPInputContext *ic = (XIMPInputContext*)client_data;
  ic->status_start((IMStatusStruct*)0);
}
void
XIMPInputContext::status_draw_cbproc(XIC xic, XPointer client_data,
				     XPointer call_data_p) {
  XIMPInputContext *ic = (XIMPInputContext*)client_data;
  XIMStatusDrawCallbackStruct *call_data =
    (XIMStatusDrawCallbackStruct*)call_data_p;

  XTextProperty prop;
  prop.value = 0;
  if (call_data->data.text) {
    // call_data is native encoding, so need to convert to CT
    char *mb_data = call_data->data.text->string.multi_byte;
    XmbTextListToTextProperty(ic->dpy, &mb_data,
			      1, XCompoundTextStyle, &prop);
    call_data->data.text->string.multi_byte = (char*)prop.value;
  }

  ic->status_draw(call_data);

  if (prop.value) XFree(prop.value);
  return;
}
void
XIMPInputContext::status_done_cbproc(XIC xic, XPointer client_data,
				     XPointer call_data) {
  XIMPInputContext *ic = (XIMPInputContext*)client_data;
  ic->status_done((IMStatusStruct*)0);
}


void
XIMPInputContext::commit_string_cbproc(XIC xic, XPointer client_data,
					XPointer call_data_p) {
  XIMText *cbtext = (XIMText*)call_data_p;
  char *mb_data = (char*)cbtext->string.multi_byte;
  XIMPInputContext *ic = (XIMPInputContext*)client_data;

  if (mb_data == 0 || *mb_data == 0) return;

  // mb_data is native encoding, so need to convert to CT
  XTextProperty prop;
  XmbTextListToTextProperty(ic->dpy, &mb_data,
			    1, XCompoundTextStyle, &prop);
  char *ret = (char *)prop.value;
  if (ic->being_reset)
    ic->reset_text += ret;
  else
    ic->commit_string(ret);

  if (prop.value) XFree(prop.value);
  return;
}

void
XIMPInputContext::forward_event_cbproc(XIC xic, XPointer client_data,
					XPointer call_data_p) {
  XEvent *call_data = (XEvent*)call_data_p;
  XIMPInputContext *ic = (XIMPInputContext*)client_data;

  ic->forward_xevent(call_data);
  return;
}

XIMPInputContext::~XIMPInputContext() {
  delete preedit_start_cb;
  delete preedit_draw_cb;
  delete preedit_caret_cb;
  delete preedit_done_cb;
  delete status_start_cb;
  delete status_draw_cb;
  delete status_done_cb;

  delete state_notify_cb;
  delete commit_string_cb;
  delete forward_event_cb;
}

const CompoundString&
XIMPInputContext::
getUser() const {
  return user_name;
}

const CompoundString&
XIMPInputContext::
getApplicationName() const {
  return application_name;
}

const CompoundString&
XIMPInputContext::
getOSName() const {
  return os_name;
}

const CompoundString&
XIMPInputContext::
getOSArch() const {
  return os_arch;
}

const CompoundString&
XIMPInputContext::
getOSVersion() const {
  return os_version;
}

// Client Display Info
const CompoundString&
XIMPInputContext::
getXDisplayString() const {
  return xDisplayName;
}

const CompoundString&
XIMPInputContext::
getXServerVendor() const {
  return xServerVendor;
}

const CompoundString&
XIMPInputContext::
getLocaleName() const {
  return ximp_protocol->getPrimaryLocale();
}

const CompoundString&
XIMPInputContext::
getEngineName() const {
  return ximp_protocol->getIfName();
}

int
XIMPInputContext::commit_string(IMText *text) {
  return True;
}

int
XIMPInputContext::forward_event(IMKeyEventStruct *event) {
  return True;
}

int
XIMPInputContext::preedit_start(IMPreeditStruct *preedit) {
  int input_context_id = ximp_protocol->getICID(this);

  ximp_protocol->send_client_message(focus_win,
				     (is_version_40() ?
				      XIMProtocol::XIMP_PREEDITSTART4 :
				      XIMProtocol::XIMP_PREEDITSTART3),
				     input_context_id,
				     0, 0, 0);
  return True;
}

int
XIMPInputContext::preedit_draw(IMPreeditStruct *preedit) {
  return True;
}

int
XIMPInputContext::preedit_caret(IMPreeditStruct *preedit) {
  return True;
}

int
XIMPInputContext::preedit_done(IMPreeditStruct *preedit) {
  int input_context_id = ximp_protocol->getICID(this);

  ximp_protocol->send_client_message(focus_win,
				     (is_version_40() ?
				      XIMProtocol::XIMP_PREEDITDONE4 :
				      XIMProtocol::XIMP_PREEDITDONE3),
				     input_context_id,
				     0, 0, 0);
  return 0;
}

int
XIMPInputContext::status_start(IMStatusStruct *status) {
  int input_context_id = ximp_protocol->getICID(this);

  ximp_protocol->send_client_message(focus_win,
				     (is_version_40() ?
				      XIMProtocol::XIMP_STATUSSTART4 :
				      XIMProtocol::XIMP_STATUSSTART3),
				     input_context_id,
				     0, 0, 0);
  return True;
}

int
XIMPInputContext::status_draw(IMStatusStruct *status) {
  return True;
}

int
XIMPInputContext::status_done(IMStatusStruct *status) {
  int input_context_id = ximp_protocol->getICID(this);

  ximp_protocol->send_client_message(focus_win,
				     (is_version_40() ?
				      XIMProtocol::XIMP_STATUSDONE4 :
				      XIMProtocol::XIMP_STATUSDONE3),
				     input_context_id,
				     0, 0, 0);
  return 0;
}

int
XIMPInputContext::lookup_start(IMLookupStruct *lookup) {
  return 0;
}
 
int
XIMPInputContext::lookup_draw(IMLookupStruct *lookup) {
  return True;
}

int
XIMPInputContext::lookup_process(IMLookupStruct *lookup) {
  return 0;
}

int
XIMPInputContext::lookup_done(IMLookupStruct *lookup) {
  return 0;
}

int
XIMPInputContext::auxiliary_start(IMAuxStruct *auxiliary) {
  return 0;
}
int
XIMPInputContext::auxiliary_draw(IMAuxStruct *auxiliary) {
  return 0;
}
int
XIMPInputContext::auxiliary_done(IMAuxStruct *auxiliary) {
  return 0;
}

int
XIMPInputContext::conversion_start() {
  return conversion_start(True);
}
int
XIMPInputContext::conversion_end() {
  return conversion_end(True);
}

int
XIMPInputContext::conversion_start(Bool reply_back) {
  int input_context_id = ximp_protocol->getICID(this);
  if (is_version_40()) {
    if (is_ximp_type(XIMProtocol::XIMP_FE_TYPE1)) {
      select_keypress_emask();
      select_keyrelease_emask();
    }
  } else {
    if (is_ximp_type(XIMProtocol::XIMP_FRONTEND)) {
      select_keypress_emask();
    }
  }
  set_conv_state(True);

  if (reply_back) {
    if (!xic) DelayBind();
    if (xic) {
      XSetICValues(xic,
		   XNPreeditState, XIMPreeditEnable,
		   0);
    } else {
      conversion_end();
      return 0;
    }
  }

  // this return message is necessary for XIMP_EXTENSION EXT_CONV
  ximp_protocol->send_client_message(focus_win,
				     (is_version_40() ?
				      XIMProtocol::XIMP_SPROC_STARTED4 :
				      XIMProtocol::XIMP_PROCESS_BEGIN3),
				     input_context_id,
				     0, 0, 0);
  return 0;
}

int
XIMPInputContext::conversion_end(Bool reply_back) {
  int input_context_id = ximp_protocol->getICID(this);
  unselect_keypress_emask();
  unselect_keyrelease_emask();
  set_conv_state(False);

  if (reply_back) {
    if (!xic) DelayBind();
    XSetICValues(xic,
		 XNPreeditState, XIMPreeditDisable,
		 0);
  }

  ximp_protocol->send_client_message(focus_win,
				     (is_version_40() ?
				      XIMProtocol::XIMP_SPROC_STOPPED4 :
				      XIMProtocol::XIMP_PROCESS_END3),
				     input_context_id,
				     0, 0, 0);
  return 0;
}

Window
XIMPInputContext::focus_window() {return focus_win;}

Window
XIMPInputContext::client_window() {return client_win;}

// set IC attributes
void
XIMPInputContext::set_client_window(Window new_win) {
  if (client_win != new_win) {
    // We may have to unregister only in case of reregistering	
    filters &= ~FLT_DESTROY;
    unselect_destroy_clientwin_emask();
    unregister_clientwin_destroy_filter();
    client_win = new_win;
    if (xic) {
      // client_win might have been set already, but even for
      // such cases, we allow to set client_win again.
      XSetICValues(xic,
		   XNClientWindow, client_win,
		   0);
      filters &= ~FLT_DESTROY;
      select_destroy_clientwin_emask();
      register_clientwin_destroy_filter();
    }
  }
  return;
}
void
XIMPInputContext::set_focus_window(Window new_win) {
  if (focus_win != new_win) {
    filters &= ~FLT_KEYPRESS;
    filters &= ~FLT_KEYRELEASE;
    filters &= ~FLT_DESTROY;
    focus_win = new_win;
    select_destroy_emask();
    if (ev_flow_type == XIMProtocol::XIMP_FE_TYPE2 ||
	ev_flow_type == XIMProtocol::XIMP_FE_TYPE3 ||
	(ev_flow_type == XIMProtocol::XIMP_FE_TYPE1 && is_conv_on)) {
      select_keypress_emask();
      select_keyrelease_emask();
    }
    // as XIM client
    if (!xic) DelayBind();
    if (xic)
      XSetICValues(xic,
		   XNFocusWindow, focus_win,
		   0);
  }
  return;
}
void
XIMPInputContext::set_fwin_select_mask(long mask) {
  ev_masks = mask;
  return;
}
void
XIMPInputContext::set_preedit_maxlen(unsigned long len) {
  preedit_maxlen = len;
  return;
}

void
XIMPInputContext::set_preedit_caret_position(unsigned long pos) {
  preedit_caret_position = pos;
  return;
}

void
XIMPInputContext::set_values(XClientMessageEvent *ev) {
  unsigned long mask = ev->data.l[2];
  if (is_version_40()) {
    mask &= ~(XIMP_FOCUS_WIN_MASK4|XIMP_SERVERTYPE_MASK4);
  }
  get_xicvalues(mask);
  return;
}

void
XIMPInputContext::get_values(XClientMessageEvent *ev) {
  unsigned long mask = ev->data.l[2];
  set_xicvalues(mask);
  return;
}

void
XIMPInputContext::set_match(int kcode, int state) {
  og_code = kcode; og_state = state;
  return;
}

Bool
XIMPInputContext::process_match(int kcode, int state) {
  if (og_code == kcode && og_state == state) {
    return True;
  }
  return False;
}

Bool
XIMPInputContext::
get_xicvalues(unsigned long &u_mask) {

  long mask = (long)u_mask;
  if (is_version_40()) {
    if (mask & XIMP_FOCUS_WIN_MASK4) {
      if (get_focus_win())
	attr_mask |= XIMP_FOCUS_WIN_MASK4;
      else
	return False;
    }
    if (mask & XIMP_PRE_FONT_MASK4) {
      if (get_preedit_font())
	attr_mask |= XIMP_PRE_FONT_MASK4;
      else
	return False;
    }
    if (mask & XIMP_STS_FONT_MASK4) {
      if (get_status_font())
	attr_mask |= XIMP_STS_FONT_MASK4;
      else
	return False;
    }
    if (mask & PREEDIT_MASK4) {
      if (get_preedit4(mask))
	attr_mask |= mask & PREEDIT_MASK4;
      else
	return False;
    }
    if (mask & STATUS_MASK4) {
      if (get_status4(mask))
	attr_mask |= mask & STATUS_MASK4;
      else
	return False;
    }
    if (mask & XIMP_SERVERTYPE_MASK4) {
      if (get_server_type4())
	attr_mask |= mask & XIMP_SERVERTYPE_MASK4;
      else
	return False;
    }
  } else {
    if (mask & XIMP_FOCUS_WIN_MASK3) {
      if (get_focus_win())
	attr_mask |= XIMP_FOCUS_WIN_MASK3;
      else
	return False;
    }
    if (mask & XIMP_PRE_FONT_MASK3) {
      if (get_preedit_font())
	attr_mask |= XIMP_PRE_FONT_MASK3;
      else
	return False;
    }
    if (mask & XIMP_STS_FONT_MASK3) {
      if (get_status_font())
	attr_mask |= XIMP_STS_FONT_MASK3;
      else
	return False;
    }
    if (mask & PREEDIT_MASK3) {
      if (get_preedit3(mask))
	attr_mask |= mask & PREEDIT_MASK3;
      else
	return False;
    }
    if (mask & STATUS_MASK3) {
      if (get_status3(mask))
	attr_mask |= mask & STATUS_MASK3;
      else
	return False;
    }
  }
  return True;
}

Bool
XIMPInputContext::
get_focus_win() {
  Window new_focus_win;
  if (!ximp_protocol->get_focus_window(client_win,
				       new_focus_win)) {
    return False;
  }
  set_focus_window(new_focus_win);
  return True;
}

Bool
XIMPInputContext::
get_preedit4(unsigned long mask) {
  if (!ximp_protocol->get_preedit4(client_win,
				   preedit_attr4)) {
    return False;
  }
  if (mask & XIMP_PRE_SPOTL_MASK4) {
    set_preedit_spot_location(preedit_attr4.spot_location.x,
			      preedit_attr4.spot_location.y);
  }
  if (mask & XIMP_PRE_AREA_MASK4) {
    set_preedit_area(preedit_attr4.area.x, preedit_attr4.area.y,
		     preedit_attr4.area.width, preedit_attr4.area.height);
  }
  if (mask & XIMP_PRE_AREANEED_MASK4) {
    set_preedit_area_needed(preedit_attr4.area_needed.width,
			    preedit_attr4.area_needed.height);
  } else if (preedit_attr4.area_needed.width == 0 ||
	     preedit_attr4.area_needed.height == 0) {
    // the values was reset, so need to restore
    set_preedit_area_needed(preedit_attr4.area_needed.width,
			    preedit_attr4.area_needed.height);
  }
  if (mask & XIMP_PRE_COLORMAP_MASK4) {
    set_preedit_colormap(preedit_attr4.colormap);
  }
  if (mask & XIMP_PRE_FG_MASK4) {
    set_preedit_fg(preedit_attr4.foreground);
  }
  if (mask & XIMP_PRE_BG_MASK4) {
    set_preedit_bg(preedit_attr4.background);
  }
  if (mask & XIMP_PRE_BGPIXMAP_MASK4) {
    set_preedit_bg_pixmap(preedit_attr4.bg_pixmap);
  }
  if (mask & XIMP_PRE_LINESP_MASK4) {
    set_preedit_line_spacing(preedit_attr4.line_spacing);
  }
  if (mask & XIMP_PRE_CURSOR_MASK4) {
    set_preedit_cursor(preedit_attr4.cursor);
  }
  return True;
}

Bool
XIMPInputContext::
get_status4(unsigned long mask) {
  if (!ximp_protocol->get_status4(client_win,
				  status_attr4)) {
    return False;
  }
  if (mask & XIMP_STS_AREA_MASK4) {
    set_status_area(status_attr4.area.x, status_attr4.area.y,
		    status_attr4.area.width, status_attr4.area.height);
  }
  if (mask & XIMP_STS_AREANEED_MASK4) {
    set_status_area_needed(status_attr4.area_needed.width,
			   status_attr4.area_needed.height);
  } else if (status_attr4.area_needed.width == 0 ||
	     status_attr4.area_needed.height == 0) {
    // the values was reset, so need to restore
    set_status_area_needed(status_attr4.area_needed.width,
			   status_attr4.area_needed.height);
  }
  if (mask & XIMP_STS_COLORMAP_MASK4) {
    set_status_colormap(status_attr4.colormap);
  }
  if (mask & XIMP_STS_FG_MASK4) {
    set_status_fg(status_attr4.foreground);
  }
  if (mask & XIMP_STS_BG_MASK4) {
    set_status_bg(status_attr4.background);
  }
  if (mask & XIMP_STS_BGPIXMAP_MASK4) {
    set_status_bg_pixmap(status_attr4.bg_pixmap);
  }
  if (mask & XIMP_STS_LINESP_MASK4) {
    set_status_line_spacing(status_attr4.line_spacing);
  }
  if (mask & XIMP_STS_CURSOR_MASK4) {
    set_status_cursor(status_attr4.cursor);
  }
  return True;
}

Bool
XIMPInputContext::
get_server_type4() {
  unsigned long ximp_type_mask;
  if (!ximp_protocol->get_server_type4(client_win,
				       ximp_type_mask)) {
    return False;
  }
  ev_flow_type = ximp_type_mask;
  return True;
}

Bool
XIMPInputContext::
get_preedit3(unsigned long mask) {
  Ximp_PreeditPropRec3 preedit_attr3;
  if (!ximp_protocol->get_preedit3(client_win,
				   preedit_attr3)) {
    return False;
  }
  if (mask & XIMP_PRE_AREA_MASK3) {
    preedit_attr4.area.x = preedit_attr3.area.x;
    preedit_attr4.area.y = preedit_attr3.area.y;
    preedit_attr4.area.width = preedit_attr3.area.width;
    preedit_attr4.area.height = preedit_attr3.area.height;
    set_preedit_area(preedit_attr4.area.x, preedit_attr4.area.y,
		     preedit_attr4.area.width, preedit_attr4.area.height);
  }
  if (mask & XIMP_PRE_AREANEED_MASK3) {
    preedit_attr4.area_needed.width = preedit_attr3.area_needed.width;
    preedit_attr4.area_needed.height = preedit_attr3.area_needed.height;
    set_preedit_area_needed(preedit_attr4.area_needed.width,
			    preedit_attr4.area_needed.height);
  }

  if (mask & XIMP_PRE_SPOTL_MASK3) {
    preedit_attr4.spot_location.x = preedit_attr3.spot_location.x;
    preedit_attr4.spot_location.y = preedit_attr3.spot_location.y;
    set_preedit_spot_location(preedit_attr4.spot_location.x,
			      preedit_attr4.spot_location.y);
  }

  if (mask & XIMP_PRE_COLORMAP_MASK3) {
    preedit_attr4.colormap = preedit_attr3.colormap;
    set_preedit_colormap(preedit_attr4.colormap);
  }

  if (mask & XIMP_PRE_FG_MASK3) {
    preedit_attr4.foreground = preedit_attr3.foreground;
    set_preedit_fg(preedit_attr4.foreground);
  }

  if (mask & XIMP_PRE_BG_MASK3) {
    preedit_attr4.background = preedit_attr3.background;
    set_preedit_bg(preedit_attr4.background);
  }

  if (mask & XIMP_PRE_BGPIXMAP_MASK3) {
    preedit_attr4.bg_pixmap = preedit_attr3.bg_pixmap;
    set_preedit_bg_pixmap(preedit_attr4.bg_pixmap);
  }

  if (mask & XIMP_PRE_LINESP_MASK3) {
    preedit_attr4.line_spacing = preedit_attr3.line_spacing;
    set_preedit_line_spacing(preedit_attr4.line_spacing);
  }

  if (mask & XIMP_PRE_CURSOR_MASK3) {
    preedit_attr4.cursor = preedit_attr3.cursor;
    set_preedit_cursor(preedit_attr4.cursor);
  }

  return True;
}

Bool
XIMPInputContext::
get_status3(unsigned long mask) {
  Ximp_StatusPropRec3 status_attr3;
  if (!ximp_protocol->get_status3(client_win,
				  status_attr3)) {
    return False;
  }
  if (mask & XIMP_STS_AREA_MASK3) {
    status_attr4.area.x = status_attr3.area.x;
    status_attr4.area.y = status_attr3.area.y;
    status_attr4.area.width = status_attr3.area.width;
    status_attr4.area.height = status_attr3.area.height;
    set_status_area(status_attr4.area.x, status_attr4.area.y,
		    status_attr4.area.width, status_attr4.area.height);
  }

  if (mask & XIMP_STS_AREANEED_MASK3) {
    status_attr4.area_needed.width = status_attr3.area_needed.width;
    status_attr4.area_needed.height = status_attr3.area_needed.height;
    set_status_area_needed(status_attr4.area_needed.width,
			   status_attr4.area_needed.height);
  }

  if (mask & XIMP_STS_COLORMAP_MASK3) {
    status_attr4.colormap = status_attr3.colormap;
    set_status_colormap(status_attr4.colormap);
  }

  if (mask & XIMP_STS_FG_MASK3) {
    status_attr4.foreground = status_attr3.foreground;
    set_status_fg(status_attr3.foreground);
  }

  if (mask & XIMP_STS_BG_MASK3) {
    status_attr4.background = status_attr3.background;
    set_status_bg(status_attr3.background);
  }

  if (mask & XIMP_STS_BGPIXMAP_MASK3) {
    status_attr4.bg_pixmap = status_attr3.bg_pixmap;
    set_status_bg_pixmap(status_attr4.bg_pixmap);
  }

  if (mask & XIMP_STS_LINESP_MASK3) {
    status_attr4.line_spacing = status_attr3.line_spacing;
    set_status_line_spacing(status_attr4.line_spacing);
  }

  if (mask & XIMP_STS_CURSOR_MASK3) {
    status_attr4.cursor = status_attr3.cursor;
    set_status_cursor(status_attr4.cursor);
  }
  return True;
}

Bool
XIMPInputContext::
set_xicvalues(unsigned long &u_mask) {

  long mask = (long)u_mask;
  if (is_version_40()) {
    if (mask & XIMP_FOCUS_WIN_MASK4) {
      if (set_focus_win())
	attr_mask |= XIMP_FOCUS_WIN_MASK4;
      else
	return False;
    }
    if (mask & XIMP_PRE_FONT_MASK4) {
      if (set_preedit_font())
	attr_mask |= XIMP_PRE_FONT_MASK4;
      else
	return False;
    }
    if (mask & XIMP_STS_FONT_MASK4) {
      if (set_status_font())
	attr_mask |= XIMP_STS_FONT_MASK4;
      else
	return False;
    }
    if (mask & PREEDIT_MASK4) {
      if (set_preedit4())
	attr_mask |= mask & PREEDIT_MASK4;
      else
	return False;
    }
    if (mask & STATUS_MASK4) {
      if (set_status4())
	attr_mask |= mask & STATUS_MASK4;
      else
	return False;
    }
    if (mask & XIMP_SERVERTYPE_MASK4) {
      if (set_server_type4())
	attr_mask |= mask & XIMP_SERVERTYPE_MASK4;
      else
	return False;
    }
  } else {
    if (mask & XIMP_FOCUS_WIN_MASK3) {
      if (set_focus_win())
	attr_mask |= XIMP_FOCUS_WIN_MASK3;
      else
	return False;
    }
    if (mask & XIMP_PRE_FONT_MASK3) {
      if (set_preedit_font())
	attr_mask |= XIMP_PRE_FONT_MASK3;
      else
	return False;
    }
    if (mask & XIMP_STS_FONT_MASK3) {
      if (set_status_font())
	attr_mask |= XIMP_STS_FONT_MASK3;
      else
	return False;
    }
    if (mask & PREEDIT_MASK3) {
      if (set_preedit3())
	attr_mask |= mask & PREEDIT_MASK3;
      else
	return False;
    }
    if (mask & STATUS_MASK3) {
      if (set_status3())
	attr_mask |= mask & STATUS_MASK3;
      else
	return False;
    }
  }
  return True;
}

Bool
XIMPInputContext::set_focus_win() {
  if (!ximp_protocol->set_focus_window(client_win,
				       focus_win)) {
    return False;
  }
  return True;
}

Bool
XIMPInputContext::set_preedit_font() {
  if (!ximp_protocol->set_preedit_font(client_win,
				       preedit_fontname)) {
    return False;
  }
  return True;
}

Bool
XIMPInputContext::set_status_font() {
  if (!ximp_protocol->set_status_font(client_win,
				      status_fontname)) {
    return False;
  }
  return True;
}

Bool
XIMPInputContext::set_preedit4() {
  // check area needed width/height to see if 0
#if 0
  const int MIN_PREEDIT_CHARNUM = 20;
#endif
  if (!ximp_protocol->set_preedit4(client_win,
				   preedit_attr4)) {
    return False;
  }
  return True;
}

Bool
XIMPInputContext::set_status4() {
  // check area needed width/height to see if 0
  if (!ximp_protocol->set_status4(client_win,
				  status_attr4)) {
    return False;
  }
  return True;
}

Bool
XIMPInputContext::set_server_type4() {
  if (!ximp_protocol->set_server_type4(client_win,
				       ev_flow_type)) {
    return False;
  }
  return True;
}

Bool
XIMPInputContext::set_preedit3() {
  // check area needed width/height to see if 0
#if 0
  const int MIN_PREEDIT_CHARNUM = 20; 
#endif
  Ximp_PreeditPropRec3 preedit_attr3;
  preedit_attr3.area.x = preedit_attr4.area.x;
  preedit_attr3.area.y = preedit_attr4.area.y;
  preedit_attr3.area.width = preedit_attr4.area.width;
  preedit_attr3.area.height = preedit_attr4.area.height;
  preedit_attr3.area_needed.width = preedit_attr4.area_needed.width;
  preedit_attr3.area_needed.height = preedit_attr4.area_needed.height;
  preedit_attr3.spot_location.x = preedit_attr4.spot_location.x;
  preedit_attr3.spot_location.y = preedit_attr4.spot_location.y;
  preedit_attr3.colormap = preedit_attr4.colormap;
  preedit_attr3.foreground = preedit_attr4.foreground;
  preedit_attr3.background = preedit_attr4.background;
  preedit_attr3.bg_pixmap = preedit_attr4.bg_pixmap;
  preedit_attr3.line_spacing = preedit_attr4.line_spacing;
  preedit_attr3.cursor = preedit_attr4.cursor;
  if (!ximp_protocol->set_preedit3(client_win,
				   preedit_attr3)) {
    return False;
  }
  return True;
}

Bool
XIMPInputContext::set_status3() {
  // check area needed width/height to see if 0
  Ximp_StatusPropRec3 status_attr3;
  status_attr3.area.x = status_attr4.area.x;
  status_attr3.area.y = status_attr4.area.y;
  status_attr3.area.width = status_attr4.area.width;
  status_attr3.area.height = status_attr4.area.height;
  status_attr3.area_needed.width = status_attr4.area_needed.width;
  status_attr3.area_needed.height = status_attr4.area_needed.height;
  status_attr3.colormap = status_attr4.colormap;
  status_attr3.foreground = status_attr4.foreground;
  status_attr3.background = status_attr4.background;
  status_attr3.bg_pixmap = status_attr4.bg_pixmap;
  status_attr3.line_spacing = status_attr4.line_spacing;
  status_attr3.cursor = status_attr4.cursor;
  if (!ximp_protocol->set_status3(client_win,
				  status_attr3)) {
    return False;
  }
  return True;
}

Bool
XIMPInputContext::destroy_clientwin_filter(Display *display,
					   Window win, XEvent *ev,
					   XPointer client_data) {
  XIMPInputContext *ic = (XIMPInputContext*)client_data;
  ic->unregister_clientwin_destroy_filter();
  // client_window is destroyed, so xic is no longer valid
  if (ic->xic) {
    ic->destroy_xic();
    return True;
  }
  return False;
}

void
XIMPInputContext::register_clientwin_destroy_filter() {
#if 0
  XFactory::register_filter(dpy, client_win,
			    DestroyNotify, DestroyNotify,
			    destroy_clientwin_filter, (XPointer)this);
#endif
}
void
XIMPInputContext::unregister_clientwin_destroy_filter() {
#if 0
  XFactory::unregister_filter(dpy, client_win,
			      destroy_clientwin_filter, (XPointer)this);
#endif
}

void
XIMPInputContext::unselect_destroy_emask() {
  if (filters & FLT_DESTROY) {
    ximp_protocol->unselect_destroy_emask(focus_win);
    filters &= ~FLT_DESTROY;
  }
}

void
XIMPInputContext::unselect_destroy_clientwin_emask() {
  if (filters & FLT_DESTROY) {
    ximp_protocol->unselect_destroy_emask(client_win);
    filters &= ~FLT_DESTROY;
  }
}

void
XIMPInputContext::unselect_keypress_emask() {
  if (filters & FLT_KEYPRESS) {
    ximp_protocol->unselect_keypress_emask(focus_win);
    filters &= ~FLT_KEYPRESS;
  }
}
void
XIMPInputContext::unselect_keyrelease_emask() {
  if (filters & FLT_KEYRELEASE) {
    ximp_protocol->unselect_keyrelease_emask(focus_win);
    filters &= ~FLT_KEYRELEASE;
  }
}

void
XIMPInputContext::select_destroy_emask() {
  if (!(filters & FLT_DESTROY)) {
    ximp_protocol->select_destroy_emask(focus_win);
    filters |= FLT_DESTROY;
  }
}

void
XIMPInputContext::select_destroy_clientwin_emask() {
  if (!(filters & FLT_DESTROY)) {
    ximp_protocol->select_destroy_emask(client_win);
    filters |= FLT_DESTROY;
  }
}

void
XIMPInputContext::select_keypress_emask() {
  if (!(filters & FLT_KEYPRESS)) {
    ximp_protocol->select_keypress_emask(focus_win);
    filters |= FLT_KEYPRESS;
  }
}
void
XIMPInputContext::select_keyrelease_emask() {
  if (!(filters & FLT_KEYRELEASE)) {
    ximp_protocol->select_keyrelease_emask(focus_win);
    filters |= FLT_KEYRELEASE;
  }
}

// backward compativility
int
XIMPInputContext::forward_xevent(XEvent *event) {
  int input_context_id = ximp_protocol->getICID(this);
  switch (event->type) {
  case KeyPress:
    ximp_protocol->send_client_message(focus_win,
				       (is_version_40() ?
					XIMProtocol::XIMP_KEYPRESS4 :
					XIMProtocol::XIMP_KEYPRESS3),
				       input_context_id,
				       event->xkey.keycode,
				       event->xkey.state,
				       (is_version_40() ?
					event->xkey.time : 0));
    if (is_version_40()) {
      set_match(event->xkey.keycode, event->xkey.state);
    }
    break;
  case KeyRelease:
    if (process_match(event->xkey.keycode, event->xkey.state)) {
      ximp_protocol->send_client_message(focus_win,
				 XIMProtocol::XIMP_KEYRELEASE4,
				 input_context_id,
				 event->xkey.keycode,
				 event->xkey.state,
				 event->xkey.time);
    }
    break;
  }
  return True;
}

int
XIMPInputContext::commit_string(char *text) {
  int input_context_id = ximp_protocol->getICID(this);
  int length = strlen(text);

  if (length > 20) {
    Atom atom = ximp_protocol->set_ctext_property((unsigned char*)text ,
						  length, True);
    ximp_protocol->send_client_message(focus_win,
				       (is_version_40() ?
					XIMProtocol::XIMP_READPROP4 :
					XIMProtocol::XIMP_READPROP3),
				       input_context_id,
				       atom, 0, 0);
  } else {
    ximp_protocol->commit_by_client_message(focus_win,
					    input_context_id,
					    text, length);
  }
  return True;
}

int
XIMPInputContext::preedit_draw(XIMPreeditDrawCallbackStruct *draw) {
  int input_context_id = ximp_protocol->getICID(this);
  int length;

  if (!(inputstyle & XIMPreeditCallbacks)) {
    return True;
  }
  
  if (draw->text) {
    length = strlen((char*)draw->text->string.multi_byte);
  } else {
    length = 0;
  }

  if (length <= COMMIT_CMSG_MAX) {
    /* send by client message */
    preedit_draw_by_cm(draw);
  } else {
    Atom atom1, atom2, atom3;
    CARD32 l[3];
    char *ctext;
    CARD32 *feedback;
    int n;

    l[0] = draw->caret; l[1] = draw->chg_first; l[2] = draw->chg_length;
    atom1 = ximp_protocol->set_preedit_draw_data_property((unsigned char*)l,
							  3);

    if (draw->text) {
      ctext = draw->text->string.multi_byte;
      atom2 = ximp_protocol->set_ctext_property((unsigned char*)ctext,
						length);
    } else {
      atom2 = ximp_protocol->set_ctext_property((unsigned char*)0,
						0);
    }

    if (draw->text) {
      feedback = draw->text->feedback;
      for (n = 0; (feedback[n] != 0) && (n < draw->text->length); n++) ;
      atom3 = ximp_protocol->set_feedback_property((unsigned char*)feedback,
						   n);
    } else {
      atom3 = ximp_protocol->set_feedback_property((unsigned char*)0, 0);
    }
    ximp_protocol->send_client_message(focus_win,
				       (is_version_40() ?
					XIMProtocol::XIMP_PREEDITDRAW4 :
					XIMProtocol::XIMP_PREEDITDRAW3),
				       input_context_id,
				       atom1, atom2, atom3);
  }
  return True;
}

int
XIMPInputContext::preedit_draw_feedback(XIMPreeditDrawCallbackStruct *draw) {
  CARD32 l[5];
  int count;
  int length = draw->text->length;
  int chg_first = draw->chg_first;
  XIMFeedback	*feedback = draw->text->feedback;
  XIMFeedback	 first_feedback;
  int input_context_id = ximp_protocol->getICID(this);

  first_feedback = *feedback;
  l[0] = XIMProtocol::XIMP_PREEDITDRAW_CM_FEEDBACK4;
  l[1] = input_context_id;
  l[2] = chg_first << 24;
  l[3] = (unsigned long)first_feedback;
  for (count = 0; ((count < length ) && (*feedback == first_feedback));
       count++, chg_first++, feedback++)
    ;

  l[2] |= count << 16;
  if (count < length) {
    l[2] |= chg_first << 8;
    l[2] |= length - count;
    l[4] = (unsigned long)*feedback;
  } else {
    l[2] |= 0xffff;
  }
  ximp_protocol->send_client_message(focus_win,
				     l[0], l[1], l[2], l[3], l[4]);
  return True;
}

int
XIMPInputContext::preedit_draw_tiny(XIMPreeditDrawCallbackStruct *draw) {
  CARD32 l[5];
  char *text = (char *)&l[3];
  int ct_length = strlen(draw->text->string.multi_byte);
  int input_context_id = ximp_protocol->getICID(this);

  l[0] = XIMProtocol::XIMP_PREEDITDRAW_CM_TINY4;
  l[1] = input_context_id;
  l[2] = ct_length;
  l[2] |= draw->chg_length << 8;
  l[2] |= draw->chg_first << 16;
  memmove(text, draw->text->string.multi_byte, ct_length); // l[3]-l[4]
  ximp_protocol->send_client_message(focus_win,
				     l[0], l[1], l[2], l[3], l[4]);
  return True;
}

int
XIMPInputContext::preedit_caret(XIMPreeditCaretCallbackStruct *caret) {
  int input_context_id = ximp_protocol->getICID(this);

  ximp_protocol->send_client_message(focus_win,
				     (is_version_40() ?
				      XIMProtocol::XIMP_PREEDITCARET4 :
				      XIMProtocol::XIMP_PREEDITCARET3),
				     input_context_id,
				     caret->position,
				     caret->direction,
				     caret->style);
  return True;
}

int
XIMPInputContext::status_draw(XIMStatusDrawCallbackStruct *draw) {
  int input_context_id = ximp_protocol->getICID(this);
  Atom atom1, atom2;
  char *ctext;
  CARD32 *feedback;
  int len;
  int n;

  switch (draw->type) {
  case XIMTextType:
    ctext = draw->data.text->string.multi_byte;
    len = strlen(draw->data.text->string.multi_byte);
    atom1 = ximp_protocol->set_ctext_property((unsigned char*)ctext, len);

    feedback = draw->data.text->feedback;
    for (n = 0;
	 draw->data.text->feedback[n] != 0 && n < draw->data.text->length;
	 n++) ;
    atom2 = ximp_protocol->set_feedback_property((unsigned char*)feedback,
						 n);

    ximp_protocol->send_client_message(focus_win,
				       (is_version_40() ?
					XIMProtocol::XIMP_STATUSDRAW4 :
					XIMProtocol::XIMP_STATUSDRAW3),
				       input_context_id,
				       XIMTextType, atom1, atom2);
    break;
  case XIMBitmapType:
    ximp_protocol->send_client_message(focus_win,
				       (is_version_40() ?
					XIMProtocol::XIMP_STATUSDRAW4 :
					XIMProtocol::XIMP_STATUSDRAW3),
				       input_context_id,
				       XIMBitmapType,
				       (CARD32)draw->data.bitmap, 0);
    break;
  }
  return True;
}

int
XIMPInputContext::preedit_draw_by_cm(XIMPreeditDrawCallbackStruct *draw) {
  CARD32 l[5];
  int chg_count;
  int chg_first = draw->chg_first;
  int chg_length = draw->chg_length;
  int i;
  int input_context_id = ximp_protocol->getICID(this);
  XIMFeedback first_feedback;
  XIMFeedback *feedback;
  int size;

  l[0] = (is_version_40() ?
	  XIMProtocol::XIMP_PREEDITDRAW_CM4 :
	  XIMProtocol::XIMP_PREEDITDRAW_CM3);
  l[1] = input_context_id;
  l[2] = (unsigned long)draw->caret;

  if (!draw->text) {
    l[2] |= (XIMP_PDCBSTATUS_NOTEXT | XIMP_PDCBSTATUS_NOFEEDBACK) << 16;
    l[3] = chg_length;
    l[3] |= chg_first << 16;
    l[4]  = 0;
  } else if (!draw->text->feedback) {
#ifdef USE_PREEDITDRAW_TINY
    cont int PREEDITDRAW_TINY_SIZE = 8;
    if (draw->text->length < PREEDITDRAW_TINY_SIZE) {
      return preedit_draw_tiny(draw);
    }
#endif /* USE_PREEDITDRAW_TINY */
    l[2] |= XIMP_PDCBSTATUS_NOFEEDBACK << 16;
    l[3]  = chg_length;
    l[3] |= chg_first << 16;
    l[4]  = 0;
  } else if (!draw->text->string.wide_char) {
#ifdef USE_PREEDITDRAW_FEEDBACK
    chg_count = 0;
    first_feedback = *draw->text->feedback;
    for (i = 0, feedback = draw->text->feedback;
	 i < draw->text->length; i++, feedback++) {
      if (first_feedback != *feedback) {
	chg_count++;
	first_feedback = *feedback;
      }
    }
#endif /* USE_PREEDITDRAW_FEEDBACK */
    if (is_version_40()) {
#ifdef USE_PREEDITDRAW_FEEDBACK
      if (chg_count < 2) {
	return preedit_draw_feedback(draw);
      } else {
#endif /* USE_PREEDITDRAW_FEEDBACK */
	l[2] |= (XIMP_PDCBSTATUS_FEEDBACKS_VIA_PROP
		 | XIMP_PDCBSTATUS_NOTEXT ) << 16;
	l[3] = chg_length;
	l[3] |= chg_first << 16;
	l[4] = ximp_protocol->replace_prop_pool((unsigned char *)draw->text->feedback,
						draw->text->length);
#ifdef USE_PREEDITDRAW_FEEDBACK
      }
#endif /* USE_PREEDITDRAW_FEEDBACK */
    } else {
      l[2] |= XIMP_PDCBSTATUS_NOTEXT << 16;
      l[3] = chg_length;
      l[3] |= chg_first << 16;
      l[4] = *draw->text->feedback;
    }
  } else {
    chg_count = 0;
    first_feedback = *draw->text->feedback;
    for (i = 0, feedback = draw->text->feedback;
	 i < (int)draw->text->length; i++, feedback++ ) {
      if (first_feedback != *feedback) {
	chg_count++;
	first_feedback = *feedback;
      }
    }
    if (chg_count == 0) {
      l[3] = chg_length;
      l[3] |= chg_first << 16;
      l[4] = *draw->text->feedback;
    } else if (is_version_40()) {
      l[2] |= XIMP_PDCBSTATUS_FEEDBACKS_VIA_PROP << 16;
      l[3] = chg_length;
      l[3] |= chg_first << 16;
      l[4] = ximp_protocol->replace_prop_pool((unsigned char *)draw->text->feedback,
					      draw->text->length);
    } else {
      int first_chg_first = chg_first;
      first_feedback = *draw->text->feedback;
      size = 0;
      for (i = 0, feedback = draw->text->feedback;
	   i < (int)draw->text->length;
	   i++, size++, feedback++, chg_first++) {
	if (first_feedback != *feedback) {
	  l[3] = chg_length;
	  l[3] |= first_chg_first << 16;
	  l[4] = first_feedback;
	  first_chg_first = chg_first;
	  first_feedback = *feedback;
	  ximp_protocol->send_client_message(focus_win,
					     l[0], l[1], l[2], l[3], l[4]);
	  const char *text = draw->text->string.multi_byte;
	  ximp_protocol->commit_by_client_message(focus_win,
						  input_context_id,
						  text, strlen(text));
	  chg_length = 0;
	}
      }
      l[3]  = chg_length;
      l[3] |= first_chg_first << 16;
      l[4]  = first_feedback;
      first_feedback = *feedback;
      ximp_protocol->send_client_message(focus_win,
					 l[0], l[1], l[2], l[3], l[4]);
      const char *text = draw->text->string.multi_byte;
      ximp_protocol->commit_by_client_message(focus_win,
					      input_context_id,
					      text, strlen(text));
      return True;
    }
  }
  ximp_protocol->send_client_message(focus_win,
				     l[0], l[1], l[2], l[3], l[4]);
  if (draw->text != NULL && draw->text->string.multi_byte != NULL) {
    const char *text = draw->text->string.multi_byte;
    ximp_protocol->commit_by_client_message(focus_win,
					    input_context_id,
					    text, strlen(text));
  }
  return True;
}

char*
XIMPInputContext::get_commit_string() {
  being_reset = False;
  return (char*)reset_text;
}

char*
XIMPInputContext::reset_xic() {
  char *ret = NULL;
  being_reset = True;
  reset_text = "";
  if (!xic) DelayBind();
  if (xic) 
    ret = XmbResetIC(xic);
  return ret;
}
void
XIMPInputContext::destroy_xic() {
  unregister_clientwin_destroy_filter();
  if (xic) XDestroyIC(xic);
  xic = 0;
}
void
XIMPInputContext::set_xicfocus() {
  if (!xic) DelayBind();
  if (xic) XSetICFocus(xic);
}
void
XIMPInputContext::unset_xicfocus() {
  if (!xic) DelayBind();
  if (xic) XUnsetICFocus(xic);
}

Bool
XIMPInputContext::is_sync() {
  return (ev_flow_type & XIMProtocol::XIMP_SYNC);
}

int
XIMPInputContext::focus_window(Window &ret) const {
  if (!(attr_mask & XIMP_FOCUS_WIN_MASK4)) return False;
  ret = focus_win;
  return True;
}

int
XIMPInputContext::client_window(Window &ret) const {
  ret = client_win;
  return client_win != 0;
}

Bool
XIMPInputContext::checkAltKey(XClientMessageEvent *ev) {
  unsigned int kcode = ev->data.l[2];
  unsigned int kstate = ev->data.l[3];

  if (is_version_40()) {
    switch (ev_flow_type) {
    case XIMProtocol::XIMP_SYNC_BE_TYPE2: // Solaris XIMP support this
      return ximp_protocol->isConversionKey(kcode, kstate, is_conv_on);
    case XIMProtocol::XIMP_FE_TYPE1:	// Solaris XIMP support this
    default:
      return False;
    }
  } else {
    return ximp_protocol->isConversionKey(kcode, kstate, is_conv_on);
  }
}
