changeset 20637:6c5b11458f31 v8.2.0872

patch 8.2.0872: XIM code is mixed with multi-byte code Commit: https://github.com/vim/vim/commit/f15c8b6eb32fcfea88fd9ca42ef87bbee2c8fe2b Author: Bram Moolenaar <Bram@vim.org> Date: Mon Jun 1 14:34:43 2020 +0200 patch 8.2.0872: XIM code is mixed with multi-byte code Problem: XIM code is mixed with multi-byte code. Solution: Move the XIM code to a separate file. (Yegappan Lakshmanan, closes #6177)
author Bram Moolenaar <Bram@vim.org>
date Mon, 01 Jun 2020 14:45:05 +0200
parents 7226685d849a
children e856aaccd7c7
files Filelist src/Make_cyg_ming.mak src/Make_morph.mak src/Make_mvc.mak src/Make_vms.mms src/Makefile src/gui_xim.c src/mbyte.c src/proto.h src/proto/gui_xim.pro src/proto/mbyte.pro src/version.c
diffstat 12 files changed, 1822 insertions(+), 1779 deletions(-) [+]
line wrap: on
line diff
--- a/Filelist
+++ b/Filelist
@@ -419,6 +419,7 @@ SRC_UNIX =	\
 		src/gui_gtk_x11.c \
 		src/gui_gtk_res.xml \
 		src/gui_motif.c \
+		src/gui_xim.c \
 		src/gui_xmdlg.c \
 		src/gui_xmebw.c \
 		src/gui_xmebw.h \
@@ -442,6 +443,7 @@ SRC_UNIX =	\
 		src/proto/gui_gtk_x11.pro \
 		src/proto/gui_gtk_gresources.pro \
 		src/proto/gui_motif.pro \
+		src/proto/gui_xim.pro \
 		src/proto/gui_xmdlg.pro \
 		src/proto/gui_x11.pro \
 		src/proto/if_xcmdsrv.pro \
--- a/src/Make_cyg_ming.mak
+++ b/src/Make_cyg_ming.mak
@@ -741,6 +741,7 @@ OBJ = \
 	$(OUTDIR)/findfile.o \
 	$(OUTDIR)/fold.o \
 	$(OUTDIR)/getchar.o \
+	$(OUTDIR)/gui_xim.o \
 	$(OUTDIR)/hardcopy.o \
 	$(OUTDIR)/hashtab.o \
 	$(OUTDIR)/highlight.o \
--- a/src/Make_morph.mak
+++ b/src/Make_morph.mak
@@ -61,6 +61,7 @@ SRC =	arabic.c						\
 	findfile.c						\
 	fold.c							\
 	getchar.c						\
+	gui_xim.c						\
 	hardcopy.c						\
 	hashtab.c						\
 	highlight.c						\
--- a/src/Make_mvc.mak
+++ b/src/Make_mvc.mak
@@ -761,6 +761,7 @@ OBJ = \
 	$(OUTDIR)\findfile.obj \
 	$(OUTDIR)\fold.obj \
 	$(OUTDIR)\getchar.obj \
+	$(OUTDIR)\gui_xim.obj \
 	$(OUTDIR)\hardcopy.obj \
 	$(OUTDIR)\hashtab.obj \
 	$(OUTDIR)\highlight.obj \
@@ -1591,6 +1592,8 @@ testclean:
 
 $(OUTDIR)/getchar.obj:	$(OUTDIR) getchar.c  $(INCL)
 
+$(OUTDIR)/gui_xim.obj:	$(OUTDIR) gui_xim.c  $(INCL)
+
 $(OUTDIR)/hardcopy.obj:	$(OUTDIR) hardcopy.c  $(INCL) version.h
 
 $(OUTDIR)/hashtab.obj:	$(OUTDIR) hashtab.c  $(INCL)
@@ -1908,6 +1911,7 @@ proto.h: \
 	proto/filepath.pro \
 	proto/findfile.pro \
 	proto/getchar.pro \
+	proto/gui_xim.pro \
 	proto/hardcopy.pro \
 	proto/hashtab.pro \
 	proto/highlight.pro \
--- a/src/Make_vms.mms
+++ b/src/Make_vms.mms
@@ -334,6 +334,7 @@ SRC = \
 	findfile.c \
 	fold.c \
 	getchar.c \
+	gui_xim.c \
 	hardcopy.c \
 	hashtab.c \
 	highlight.c \
@@ -445,6 +446,7 @@ OBJ = \
 	findfile.obj \
 	fold.obj \
 	getchar.obj \
+	gui_xim.obj \
 	hardcopy.obj \
 	hashtab.obj \
 	highlight.obj \
@@ -818,6 +820,10 @@ getchar.obj : getchar.c vim.h [.auto]con
  ascii.h keymap.h term.h macros.h structs.h regexp.h \
  gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \
  globals.h
+gui_xim.obj : gui_xim.c vim.h [.auto]config.h feature.h os_unix.h \
+ ascii.h keymap.h term.h macros.h structs.h regexp.h \
+ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \
+ globals.h
 hardcopy.obj : hardcopy.c vim.h [.auto]config.h feature.h os_unix.h \
  ascii.h keymap.h term.h macros.h structs.h regexp.h \
  gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \
--- a/src/Makefile
+++ b/src/Makefile
@@ -1636,6 +1636,7 @@ BASIC_SRC = \
 	findfile.c \
 	fold.c \
 	getchar.c \
+	gui_xim.c \
 	hardcopy.c \
 	hashtab.c \
 	highlight.c \
@@ -1785,6 +1786,7 @@ OBJ_COMMON = \
 	objects/findfile.o \
 	objects/fold.o \
 	objects/getchar.o \
+	objects/gui_xim.o \
 	objects/hardcopy.o \
 	objects/hashtab.o \
 	objects/highlight.o \
@@ -1950,6 +1952,7 @@ PRO_AUTO = \
 	findfile.pro \
 	fold.pro \
 	getchar.pro \
+	gui_xim.pro \
 	gui_beval.pro \
 	hardcopy.pro \
 	hashtab.pro \
@@ -3301,6 +3304,9 @@ objects/gui_xmebw.o: gui_xmebw.c
 objects/gui_x11.o: gui_x11.c
 	$(CCC) -o $@ gui_x11.c
 
+objects/gui_xim.o: gui_xim.c
+	$(CCC) -o $@ gui_xim.c
+
 objects/gui_photon.o: gui_photon.c
 	$(CCC) -o $@ gui_photon.c
 
@@ -4239,6 +4245,11 @@ objects/gui_x11.o: gui_x11.c vim.h proto
  proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
  proto.h globals.h ../runtime/vim32x32.xpm ../runtime/vim16x16.xpm \
  ../runtime/vim48x48.xpm
+objects/gui_xim.o: gui_xim.c vim.h protodef.h auto/config.h feature.h os_unix.h \
+ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
+ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
+ proto.h globals.h ../runtime/vim32x32.xpm ../runtime/vim16x16.xpm \
+ ../runtime/vim48x48.xpm
 objects/gui_at_sb.o: gui_at_sb.c vim.h protodef.h auto/config.h feature.h \
  os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \
  proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \
new file mode 100644
--- /dev/null
+++ b/src/gui_xim.c
@@ -0,0 +1,1777 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved	by Bram Moolenaar
+ *
+ * Do ":help uganda"  in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * gui_xim.c: functions for the X Input Method
+ */
+
+#include "vim.h"
+
+#if defined(FEAT_GUI_GTK) && defined(FEAT_XIM)
+# if GTK_CHECK_VERSION(3,0,0)
+#  include <gdk/gdkkeysyms-compat.h>
+# else
+#  include <gdk/gdkkeysyms.h>
+# endif
+# ifdef MSWIN
+#  include <gdk/gdkwin32.h>
+# else
+#  include <gdk/gdkx.h>
+# endif
+#endif
+
+/*
+ * XIM often causes trouble.  Define XIM_DEBUG to get a log of XIM callbacks
+ * in the "xim.log" file.
+ */
+// #define XIM_DEBUG
+#ifdef XIM_DEBUG
+    static void
+xim_log(char *s, ...)
+{
+    va_list arglist;
+    static FILE *fd = NULL;
+
+    if (fd == (FILE *)-1)
+	return;
+    if (fd == NULL)
+    {
+	fd = mch_fopen("xim.log", "w");
+	if (fd == NULL)
+	{
+	    emsg("Cannot open xim.log");
+	    fd = (FILE *)-1;
+	    return;
+	}
+    }
+
+    va_start(arglist, s);
+    vfprintf(fd, s, arglist);
+    va_end(arglist);
+}
+#endif
+
+#ifdef FEAT_GUI
+# define USE_IMACTIVATEFUNC (!gui.in_use && *p_imaf != NUL)
+# define USE_IMSTATUSFUNC (!gui.in_use && *p_imsf != NUL)
+#else
+# define USE_IMACTIVATEFUNC (*p_imaf != NUL)
+# define USE_IMSTATUSFUNC (*p_imsf != NUL)
+#endif
+
+#if defined(FEAT_EVAL) && \
+    (defined(FEAT_XIM) || defined(IME_WITHOUT_XIM) || defined(VIMDLL))
+    static void
+call_imactivatefunc(int active)
+{
+    typval_T argv[2];
+
+    argv[0].v_type = VAR_NUMBER;
+    argv[0].vval.v_number = active ? 1 : 0;
+    argv[1].v_type = VAR_UNKNOWN;
+    (void)call_func_retnr(p_imaf, 1, argv);
+}
+
+    static int
+call_imstatusfunc(void)
+{
+    int is_active;
+
+    // FIXME: Don't execute user function in unsafe situation.
+    if (exiting || is_autocmd_blocked())
+	return FALSE;
+    // FIXME: :py print 'xxx' is shown duplicate result.
+    // Use silent to avoid it.
+    ++msg_silent;
+    is_active = call_func_retnr(p_imsf, 0, NULL);
+    --msg_silent;
+    return (is_active > 0);
+}
+#endif
+
+#if defined(FEAT_XIM) || defined(PROTO)
+
+# if defined(FEAT_GUI_GTK) || defined(PROTO)
+static int xim_has_preediting INIT(= FALSE);  // IM current status
+
+/*
+ * Set preedit_start_col to the current cursor position.
+ */
+    static void
+init_preedit_start_col(void)
+{
+    if (State & CMDLINE)
+	preedit_start_col = cmdline_getvcol_cursor();
+    else if (curwin != NULL && curwin->w_buffer != NULL)
+	getvcol(curwin, &curwin->w_cursor, &preedit_start_col, NULL, NULL);
+    // Prevent that preediting marks the buffer as changed.
+    xim_changed_while_preediting = curbuf->b_changed;
+}
+
+static int im_is_active	       = FALSE;	// IM is enabled for current mode
+static int preedit_is_active   = FALSE;
+static int im_preedit_cursor   = 0;	// cursor offset in characters
+static int im_preedit_trailing = 0;	// number of characters after cursor
+
+static unsigned long im_commit_handler_id  = 0;
+static unsigned int  im_activatekey_keyval = GDK_VoidSymbol;
+static unsigned int  im_activatekey_state  = 0;
+
+static GtkWidget *preedit_window = NULL;
+static GtkWidget *preedit_label = NULL;
+
+static void im_preedit_window_set_position(void);
+
+    void
+im_set_active(int active)
+{
+    int was_active;
+
+    was_active = !!im_get_status();
+    im_is_active = (active && !p_imdisable);
+
+    if (im_is_active != was_active)
+	xim_reset();
+}
+
+    void
+xim_set_focus(int focus)
+{
+    if (xic != NULL)
+    {
+	if (focus)
+	    gtk_im_context_focus_in(xic);
+	else
+	    gtk_im_context_focus_out(xic);
+    }
+}
+
+    void
+im_set_position(int row, int col)
+{
+    if (xic != NULL)
+    {
+	GdkRectangle area;
+
+	area.x = FILL_X(col);
+	area.y = FILL_Y(row);
+	area.width  = gui.char_width * (mb_lefthalve(row, col) ? 2 : 1);
+	area.height = gui.char_height;
+
+	gtk_im_context_set_cursor_location(xic, &area);
+
+	if (p_imst == IM_OVER_THE_SPOT)
+	    im_preedit_window_set_position();
+    }
+}
+
+#  if 0 || defined(PROTO) // apparently only used in gui_x11.c
+    void
+xim_set_preedit(void)
+{
+    im_set_position(gui.row, gui.col);
+}
+#  endif
+
+    static void
+im_add_to_input(char_u *str, int len)
+{
+    // Convert from 'termencoding' (always "utf-8") to 'encoding'
+    if (input_conv.vc_type != CONV_NONE)
+    {
+	str = string_convert(&input_conv, str, &len);
+	g_return_if_fail(str != NULL);
+    }
+
+    add_to_input_buf_csi(str, len);
+
+    if (input_conv.vc_type != CONV_NONE)
+	vim_free(str);
+
+    if (p_mh) // blank out the pointer if necessary
+	gui_mch_mousehide(TRUE);
+}
+
+     static void
+im_preedit_window_set_position(void)
+{
+    int x, y, width, height;
+    int screen_x, screen_y, screen_width, screen_height;
+
+    if (preedit_window == NULL)
+	return;
+
+    gui_gtk_get_screen_geom_of_win(gui.drawarea,
+			  &screen_x, &screen_y, &screen_width, &screen_height);
+    gdk_window_get_origin(gtk_widget_get_window(gui.drawarea), &x, &y);
+    gtk_window_get_size(GTK_WINDOW(preedit_window), &width, &height);
+    x = x + FILL_X(gui.col);
+    y = y + FILL_Y(gui.row);
+    if (x + width > screen_x + screen_width)
+	x = screen_x + screen_width - width;
+    if (y + height > screen_y + screen_height)
+	y = screen_y + screen_height - height;
+    gtk_window_move(GTK_WINDOW(preedit_window), x, y);
+}
+
+    static void
+im_preedit_window_open()
+{
+    char *preedit_string;
+#if !GTK_CHECK_VERSION(3,16,0)
+    char buf[8];
+#endif
+    PangoAttrList *attr_list;
+    PangoLayout *layout;
+#if GTK_CHECK_VERSION(3,0,0)
+# if !GTK_CHECK_VERSION(3,16,0)
+    GdkRGBA color;
+# endif
+#else
+    GdkColor color;
+#endif
+    gint w, h;
+
+    if (preedit_window == NULL)
+    {
+	preedit_window = gtk_window_new(GTK_WINDOW_POPUP);
+	gtk_window_set_transient_for(GTK_WINDOW(preedit_window),
+						     GTK_WINDOW(gui.mainwin));
+	preedit_label = gtk_label_new("");
+	gtk_widget_set_name(preedit_label, "vim-gui-preedit-area");
+	gtk_container_add(GTK_CONTAINER(preedit_window), preedit_label);
+    }
+
+#if GTK_CHECK_VERSION(3,16,0)
+    {
+	GtkStyleContext * const context
+				  = gtk_widget_get_style_context(gui.drawarea);
+	GtkCssProvider * const provider = gtk_css_provider_new();
+	gchar		   *css = NULL;
+	const char * const fontname
+			   = pango_font_description_get_family(gui.norm_font);
+	gint	fontsize
+		= pango_font_description_get_size(gui.norm_font) / PANGO_SCALE;
+	gchar	*fontsize_propval = NULL;
+
+	if (!pango_font_description_get_size_is_absolute(gui.norm_font))
+	{
+	    // fontsize was given in points.  Convert it into that in pixels
+	    // to use with CSS.
+	    GdkScreen * const screen
+		  = gdk_window_get_screen(gtk_widget_get_window(gui.mainwin));
+	    const gdouble dpi = gdk_screen_get_resolution(screen);
+	    fontsize = dpi * fontsize / 72;
+	}
+	if (fontsize > 0)
+	    fontsize_propval = g_strdup_printf("%dpx", fontsize);
+	else
+	    fontsize_propval = g_strdup_printf("inherit");
+
+	css = g_strdup_printf(
+		"widget#vim-gui-preedit-area {\n"
+		"  font-family: %s,monospace;\n"
+		"  font-size: %s;\n"
+		"  color: #%.2lx%.2lx%.2lx;\n"
+		"  background-color: #%.2lx%.2lx%.2lx;\n"
+		"}\n",
+		fontname != NULL ? fontname : "inherit",
+		fontsize_propval,
+		(gui.norm_pixel >> 16) & 0xff,
+		(gui.norm_pixel >> 8) & 0xff,
+		gui.norm_pixel & 0xff,
+		(gui.back_pixel >> 16) & 0xff,
+		(gui.back_pixel >> 8) & 0xff,
+		gui.back_pixel & 0xff);
+
+	gtk_css_provider_load_from_data(provider, css, -1, NULL);
+	gtk_style_context_add_provider(context,
+				     GTK_STYLE_PROVIDER(provider), G_MAXUINT);
+
+	g_free(css);
+	g_free(fontsize_propval);
+	g_object_unref(provider);
+    }
+#elif GTK_CHECK_VERSION(3,0,0)
+    gtk_widget_override_font(preedit_label, gui.norm_font);
+
+    vim_snprintf(buf, sizeof(buf), "#%06X", gui.norm_pixel);
+    gdk_rgba_parse(&color, buf);
+    gtk_widget_override_color(preedit_label, GTK_STATE_FLAG_NORMAL, &color);
+
+    vim_snprintf(buf, sizeof(buf), "#%06X", gui.back_pixel);
+    gdk_rgba_parse(&color, buf);
+    gtk_widget_override_background_color(preedit_label, GTK_STATE_FLAG_NORMAL,
+								      &color);
+#else
+    gtk_widget_modify_font(preedit_label, gui.norm_font);
+
+    vim_snprintf(buf, sizeof(buf), "#%06X", (unsigned)gui.norm_pixel);
+    gdk_color_parse(buf, &color);
+    gtk_widget_modify_fg(preedit_label, GTK_STATE_NORMAL, &color);
+
+    vim_snprintf(buf, sizeof(buf), "#%06X", (unsigned)gui.back_pixel);
+    gdk_color_parse(buf, &color);
+    gtk_widget_modify_bg(preedit_window, GTK_STATE_NORMAL, &color);
+#endif
+
+    gtk_im_context_get_preedit_string(xic, &preedit_string, &attr_list, NULL);
+
+    if (preedit_string[0] != NUL)
+    {
+	gtk_label_set_text(GTK_LABEL(preedit_label), preedit_string);
+	gtk_label_set_attributes(GTK_LABEL(preedit_label), attr_list);
+
+	layout = gtk_label_get_layout(GTK_LABEL(preedit_label));
+	pango_layout_get_pixel_size(layout, &w, &h);
+	h = MAX(h, gui.char_height);
+	gtk_window_resize(GTK_WINDOW(preedit_window), w, h);
+
+	gtk_widget_show_all(preedit_window);
+
+	im_preedit_window_set_position();
+    }
+
+    g_free(preedit_string);
+    pango_attr_list_unref(attr_list);
+}
+
+    static void
+im_preedit_window_close()
+{
+    if (preedit_window != NULL)
+	gtk_widget_hide(preedit_window);
+}
+
+    static void
+im_show_preedit()
+{
+    im_preedit_window_open();
+
+    if (p_mh) // blank out the pointer if necessary
+	gui_mch_mousehide(TRUE);
+}
+
+    static void
+im_delete_preedit(void)
+{
+    char_u bskey[]  = {CSI, 'k', 'b'};
+    char_u delkey[] = {CSI, 'k', 'D'};
+
+    if (p_imst == IM_OVER_THE_SPOT)
+    {
+	im_preedit_window_close();
+	return;
+    }
+
+    if (State & NORMAL
+#ifdef FEAT_TERMINAL
+	    && !term_use_loop()
+#endif
+       )
+    {
+	im_preedit_cursor = 0;
+	return;
+    }
+    for (; im_preedit_cursor > 0; --im_preedit_cursor)
+	add_to_input_buf(bskey, (int)sizeof(bskey));
+
+    for (; im_preedit_trailing > 0; --im_preedit_trailing)
+	add_to_input_buf(delkey, (int)sizeof(delkey));
+}
+
+/*
+ * Move the cursor left by "num_move_back" characters.
+ * Note that ins_left() checks im_is_preediting() to avoid breaking undo for
+ * these K_LEFT keys.
+ */
+    static void
+im_correct_cursor(int num_move_back)
+{
+    char_u backkey[] = {CSI, 'k', 'l'};
+
+    if (State & NORMAL)
+	return;
+#  ifdef FEAT_RIGHTLEFT
+    if ((State & CMDLINE) == 0 && curwin != NULL && curwin->w_p_rl)
+	backkey[2] = 'r';
+#  endif
+    for (; num_move_back > 0; --num_move_back)
+	add_to_input_buf(backkey, (int)sizeof(backkey));
+}
+
+static int xim_expected_char = NUL;
+static int xim_ignored_char = FALSE;
+
+/*
+ * Update the mode and cursor while in an IM callback.
+ */
+    static void
+im_show_info(void)
+{
+    int	    old_vgetc_busy;
+
+    old_vgetc_busy = vgetc_busy;
+    vgetc_busy = TRUE;
+    showmode();
+    vgetc_busy = old_vgetc_busy;
+    if ((State & NORMAL) || (State & INSERT))
+	setcursor();
+    out_flush();
+}
+
+/*
+ * Callback invoked when the user finished preediting.
+ * Put the final string into the input buffer.
+ */
+    static void
+im_commit_cb(GtkIMContext *context UNUSED,
+	     const gchar *str,
+	     gpointer data UNUSED)
+{
+    int		slen = (int)STRLEN(str);
+    int		add_to_input = TRUE;
+    int		clen;
+    int		len = slen;
+    int		commit_with_preedit = TRUE;
+    char_u	*im_str;
+
+#ifdef XIM_DEBUG
+    xim_log("im_commit_cb(): %s\n", str);
+#endif
+
+    if (p_imst == IM_ON_THE_SPOT)
+    {
+	// The imhangul module doesn't reset the preedit string before
+	// committing.  Call im_delete_preedit() to work around that.
+	im_delete_preedit();
+
+	// Indicate that preediting has finished.
+	if (preedit_start_col == MAXCOL)
+	{
+	    init_preedit_start_col();
+	    commit_with_preedit = FALSE;
+	}
+
+	// The thing which setting "preedit_start_col" to MAXCOL means that
+	// "preedit_start_col" will be set forcedly when calling
+	// preedit_changed_cb() next time.
+	// "preedit_start_col" should not reset with MAXCOL on this part. Vim
+	// is simulating the preediting by using add_to_input_str(). when
+	// preedit begin immediately before committed, the typebuf is not
+	// flushed to screen, then it can't get correct "preedit_start_col".
+	// Thus, it should calculate the cells by adding cells of the committed
+	// string.
+	if (input_conv.vc_type != CONV_NONE)
+	{
+	    im_str = string_convert(&input_conv, (char_u *)str, &len);
+	    g_return_if_fail(im_str != NULL);
+	}
+	else
+	    im_str = (char_u *)str;
+
+	clen = mb_string2cells(im_str, len);
+
+	if (input_conv.vc_type != CONV_NONE)
+	    vim_free(im_str);
+	preedit_start_col += clen;
+    }
+
+    // Is this a single character that matches a keypad key that's just
+    // been pressed?  If so, we don't want it to be entered as such - let
+    // us carry on processing the raw keycode so that it may be used in
+    // mappings as <kSomething>.
+    if (xim_expected_char != NUL)
+    {
+	// We're currently processing a keypad or other special key
+	if (slen == 1 && str[0] == xim_expected_char)
+	{
+	    // It's a match - don't do it here
+	    xim_ignored_char = TRUE;
+	    add_to_input = FALSE;
+	}
+	else
+	{
+	    // Not a match
+	    xim_ignored_char = FALSE;
+	}
+    }
+
+    if (add_to_input)
+	im_add_to_input((char_u *)str, slen);
+
+    if (p_imst == IM_ON_THE_SPOT)
+    {
+	// Inserting chars while "im_is_active" is set does not cause a
+	// change of buffer.  When the chars are committed the buffer must be
+	// marked as changed.
+	if (!commit_with_preedit)
+	    preedit_start_col = MAXCOL;
+
+	// This flag is used in changed() at next call.
+	xim_changed_while_preediting = TRUE;
+    }
+
+    if (gtk_main_level() > 0)
+	gtk_main_quit();
+}
+
+/*
+ * Callback invoked after start to the preedit.
+ */
+    static void
+im_preedit_start_cb(GtkIMContext *context UNUSED, gpointer data UNUSED)
+{
+#ifdef XIM_DEBUG
+    xim_log("im_preedit_start_cb()\n");
+#endif
+
+    im_is_active = TRUE;
+    preedit_is_active = TRUE;
+    gui_update_cursor(TRUE, FALSE);
+    im_show_info();
+}
+
+/*
+ * Callback invoked after end to the preedit.
+ */
+    static void
+im_preedit_end_cb(GtkIMContext *context UNUSED, gpointer data UNUSED)
+{
+#ifdef XIM_DEBUG
+    xim_log("im_preedit_end_cb()\n");
+#endif
+    im_delete_preedit();
+
+    // Indicate that preediting has finished
+    if (p_imst == IM_ON_THE_SPOT)
+	preedit_start_col = MAXCOL;
+    xim_has_preediting = FALSE;
+
+#if 0
+    // Removal of this line suggested by Takuhiro Nishioka.  Fixes that IM was
+    // switched off unintentionally.  We now use preedit_is_active (added by
+    // SungHyun Nam).
+    im_is_active = FALSE;
+#endif
+    preedit_is_active = FALSE;
+    gui_update_cursor(TRUE, FALSE);
+    im_show_info();
+}
+
+/*
+ * Callback invoked after changes to the preedit string.  If the preedit
+ * string was empty before, remember the preedit start column so we know
+ * where to apply feedback attributes.  Delete the previous preedit string
+ * if there was one, save the new preedit cursor offset, and put the new
+ * string into the input buffer.
+ *
+ * TODO: The pragmatic "put into input buffer" approach used here has
+ *       several fundamental problems:
+ *
+ * - The characters in the preedit string are subject to remapping.
+ *   That's broken, only the finally committed string should be remapped.
+ *
+ * - There is a race condition involved:  The retrieved value for the
+ *   current cursor position will be wrong if any unprocessed characters
+ *   are still queued in the input buffer.
+ *
+ * - Due to the lack of synchronization between the file buffer in memory
+ *   and any typed characters, it's practically impossible to implement the
+ *   "retrieve_surrounding" and "delete_surrounding" signals reliably.  IM
+ *   modules for languages such as Thai are likely to rely on this feature
+ *   for proper operation.
+ *
+ * Conclusions:  I think support for preediting needs to be moved to the
+ * core parts of Vim.  Ideally, until it has been committed, the preediting
+ * string should only be displayed and not affect the buffer content at all.
+ * The question how to deal with the synchronization issue still remains.
+ * Circumventing the input buffer is probably not desirable.  Anyway, I think
+ * implementing "retrieve_surrounding" is the only hard problem.
+ *
+ * One way to solve all of this in a clean manner would be to queue all key
+ * press/release events "as is" in the input buffer, and apply the IM filtering
+ * at the receiving end of the queue.  This, however, would have a rather large
+ * impact on the code base.  If there is an easy way to force processing of all
+ * remaining input from within the "retrieve_surrounding" signal handler, this
+ * might not be necessary.  Gotta ask on vim-dev for opinions.
+ */
+    static void
+im_preedit_changed_cb(GtkIMContext *context, gpointer data UNUSED)
+{
+    char    *preedit_string = NULL;
+    int	    cursor_index    = 0;
+    int	    num_move_back   = 0;
+    char_u  *str;
+    char_u  *p;
+    int	    i;
+
+    if (p_imst == IM_ON_THE_SPOT)
+	gtk_im_context_get_preedit_string(context,
+					  &preedit_string, NULL,
+					  &cursor_index);
+    else
+	gtk_im_context_get_preedit_string(context,
+					  &preedit_string, NULL,
+					  NULL);
+
+#ifdef XIM_DEBUG
+    xim_log("im_preedit_changed_cb(): %s\n", preedit_string);
+#endif
+
+    g_return_if_fail(preedit_string != NULL); // just in case
+
+    if (p_imst == IM_OVER_THE_SPOT)
+    {
+	if (preedit_string[0] == NUL)
+	{
+	    xim_has_preediting = FALSE;
+	    im_delete_preedit();
+	}
+	else
+	{
+	    xim_has_preediting = TRUE;
+	    im_show_preedit();
+	}
+    }
+    else
+    {
+	// If preedit_start_col is MAXCOL set it to the current cursor position.
+	if (preedit_start_col == MAXCOL && preedit_string[0] != '\0')
+	{
+	    xim_has_preediting = TRUE;
+
+	    // Urgh, this breaks if the input buffer isn't empty now
+	    init_preedit_start_col();
+	}
+	else if (cursor_index == 0 && preedit_string[0] == '\0')
+	{
+	    xim_has_preediting = FALSE;
+
+	    // If at the start position (after typing backspace)
+	    // preedit_start_col must be reset.
+	    preedit_start_col = MAXCOL;
+	}
+
+	im_delete_preedit();
+
+	/*
+	 * Compute the end of the preediting area: "preedit_end_col".
+	 * According to the documentation of gtk_im_context_get_preedit_string(),
+	 * the cursor_pos output argument returns the offset in bytes.  This is
+	 * unfortunately not true -- real life shows the offset is in characters,
+	 * and the GTK+ source code agrees with me.  Will file a bug later.
+	 */
+	if (preedit_start_col != MAXCOL)
+	    preedit_end_col = preedit_start_col;
+	str = (char_u *)preedit_string;
+	for (p = str, i = 0; *p != NUL; p += utf_byte2len(*p), ++i)
+	{
+	    int is_composing;
+
+	    is_composing = ((*p & 0x80) != 0 && utf_iscomposing(utf_ptr2char(p)));
+	    /*
+	     * These offsets are used as counters when generating <BS> and <Del>
+	     * to delete the preedit string.  So don't count composing characters
+	     * unless 'delcombine' is enabled.
+	     */
+	    if (!is_composing || p_deco)
+	    {
+		if (i < cursor_index)
+		    ++im_preedit_cursor;
+		else
+		    ++im_preedit_trailing;
+	    }
+	    if (!is_composing && i >= cursor_index)
+	    {
+		// This is essentially the same as im_preedit_trailing, except
+		// composing characters are not counted even if p_deco is set.
+		++num_move_back;
+	    }
+	    if (preedit_start_col != MAXCOL)
+		preedit_end_col += utf_ptr2cells(p);
+	}
+
+	if (p > str)
+	{
+	    im_add_to_input(str, (int)(p - str));
+	    im_correct_cursor(num_move_back);
+	}
+    }
+
+    g_free(preedit_string);
+
+    if (gtk_main_level() > 0)
+	gtk_main_quit();
+}
+
+/*
+ * Translate the Pango attributes at iter to Vim highlighting attributes.
+ * Ignore attributes not supported by Vim highlighting.  This shouldn't have
+ * too much impact -- right now we handle even more attributes than necessary
+ * for the IM modules I tested with.
+ */
+    static int
+translate_pango_attributes(PangoAttrIterator *iter)
+{
+    PangoAttribute  *attr;
+    int		    char_attr = HL_NORMAL;
+
+    attr = pango_attr_iterator_get(iter, PANGO_ATTR_UNDERLINE);
+    if (attr != NULL && ((PangoAttrInt *)attr)->value
+						 != (int)PANGO_UNDERLINE_NONE)
+	char_attr |= HL_UNDERLINE;
+
+    attr = pango_attr_iterator_get(iter, PANGO_ATTR_WEIGHT);
+    if (attr != NULL && ((PangoAttrInt *)attr)->value >= (int)PANGO_WEIGHT_BOLD)
+	char_attr |= HL_BOLD;
+
+    attr = pango_attr_iterator_get(iter, PANGO_ATTR_STYLE);
+    if (attr != NULL && ((PangoAttrInt *)attr)->value
+						   != (int)PANGO_STYLE_NORMAL)
+	char_attr |= HL_ITALIC;
+
+    attr = pango_attr_iterator_get(iter, PANGO_ATTR_BACKGROUND);
+    if (attr != NULL)
+    {
+	const PangoColor *color = &((PangoAttrColor *)attr)->color;
+
+	// Assume inverse if black background is requested
+	if ((color->red | color->green | color->blue) == 0)
+	    char_attr |= HL_INVERSE;
+    }
+
+    return char_attr;
+}
+
+/*
+ * Retrieve the highlighting attributes at column col in the preedit string.
+ * Return -1 if not in preediting mode or if col is out of range.
+ */
+    int
+im_get_feedback_attr(int col)
+{
+    char	    *preedit_string = NULL;
+    PangoAttrList   *attr_list	    = NULL;
+    int		    char_attr	    = -1;
+
+    if (xic == NULL)
+	return char_attr;
+
+    gtk_im_context_get_preedit_string(xic, &preedit_string, &attr_list, NULL);
+
+    if (preedit_string != NULL && attr_list != NULL)
+    {
+	int idx;
+
+	// Get the byte index as used by PangoAttrIterator
+	for (idx = 0; col > 0 && preedit_string[idx] != '\0'; --col)
+	    idx += utfc_ptr2len((char_u *)preedit_string + idx);
+
+	if (preedit_string[idx] != '\0')
+	{
+	    PangoAttrIterator	*iter;
+	    int			start, end;
+
+	    char_attr = HL_NORMAL;
+	    iter = pango_attr_list_get_iterator(attr_list);
+
+	    // Extract all relevant attributes from the list.
+	    do
+	    {
+		pango_attr_iterator_range(iter, &start, &end);
+
+		if (idx >= start && idx < end)
+		    char_attr |= translate_pango_attributes(iter);
+	    }
+	    while (pango_attr_iterator_next(iter));
+
+	    pango_attr_iterator_destroy(iter);
+	}
+    }
+
+    if (attr_list != NULL)
+	pango_attr_list_unref(attr_list);
+    g_free(preedit_string);
+
+    return char_attr;
+}
+
+    void
+xim_init(void)
+{
+#ifdef XIM_DEBUG
+    xim_log("xim_init()\n");
+#endif
+
+    g_return_if_fail(gui.drawarea != NULL);
+    g_return_if_fail(gtk_widget_get_window(gui.drawarea) != NULL);
+
+    xic = gtk_im_multicontext_new();
+    g_object_ref(xic);
+
+    im_commit_handler_id = g_signal_connect(G_OBJECT(xic), "commit",
+					    G_CALLBACK(&im_commit_cb), NULL);
+    g_signal_connect(G_OBJECT(xic), "preedit_changed",
+		     G_CALLBACK(&im_preedit_changed_cb), NULL);
+    g_signal_connect(G_OBJECT(xic), "preedit_start",
+		     G_CALLBACK(&im_preedit_start_cb), NULL);
+    g_signal_connect(G_OBJECT(xic), "preedit_end",
+		     G_CALLBACK(&im_preedit_end_cb), NULL);
+
+    gtk_im_context_set_client_window(xic, gtk_widget_get_window(gui.drawarea));
+}
+
+    void
+im_shutdown(void)
+{
+#ifdef XIM_DEBUG
+    xim_log("im_shutdown()\n");
+#endif
+
+    if (xic != NULL)
+    {
+	gtk_im_context_focus_out(xic);
+	g_object_unref(xic);
+	xic = NULL;
+    }
+    im_is_active = FALSE;
+    im_commit_handler_id = 0;
+    if (p_imst == IM_ON_THE_SPOT)
+	preedit_start_col = MAXCOL;
+    xim_has_preediting = FALSE;
+}
+
+/*
+ * Convert the string argument to keyval and state for GdkEventKey.
+ * If str is valid return TRUE, otherwise FALSE.
+ *
+ * See 'imactivatekey' for documentation of the format.
+ */
+    static int
+im_string_to_keyval(const char *str, unsigned int *keyval, unsigned int *state)
+{
+    const char	    *mods_end;
+    unsigned	    tmp_keyval;
+    unsigned	    tmp_state = 0;
+
+    mods_end = strrchr(str, '-');
+    mods_end = (mods_end != NULL) ? mods_end + 1 : str;
+
+    // Parse modifier keys
+    while (str < mods_end)
+	switch (*str++)
+	{
+	    case '-':							break;
+	    case 'S': case 's': tmp_state |= (unsigned)GDK_SHIFT_MASK;	break;
+	    case 'L': case 'l': tmp_state |= (unsigned)GDK_LOCK_MASK;	break;
+	    case 'C': case 'c': tmp_state |= (unsigned)GDK_CONTROL_MASK;break;
+	    case '1':		tmp_state |= (unsigned)GDK_MOD1_MASK;	break;
+	    case '2':		tmp_state |= (unsigned)GDK_MOD2_MASK;	break;
+	    case '3':		tmp_state |= (unsigned)GDK_MOD3_MASK;	break;
+	    case '4':		tmp_state |= (unsigned)GDK_MOD4_MASK;	break;
+	    case '5':		tmp_state |= (unsigned)GDK_MOD5_MASK;	break;
+	    default:
+		return FALSE;
+	}
+
+    tmp_keyval = gdk_keyval_from_name(str);
+
+    if (tmp_keyval == 0 || tmp_keyval == GDK_VoidSymbol)
+	return FALSE;
+
+    if (keyval != NULL)
+	*keyval = tmp_keyval;
+    if (state != NULL)
+	*state = tmp_state;
+
+    return TRUE;
+}
+
+/*
+ * Return TRUE if p_imak is valid, otherwise FALSE.  As a special case, an
+ * empty string is also regarded as valid.
+ *
+ * Note: The numerical key value of p_imak is cached if it was valid; thus
+ * boldly assuming im_xim_isvalid_imactivate() will always be called whenever
+ * 'imak' changes.  This is currently the case but not obvious -- should
+ * probably rename the function for clarity.
+ */
+    int
+im_xim_isvalid_imactivate(void)
+{
+    if (p_imak[0] == NUL)
+    {
+	im_activatekey_keyval = GDK_VoidSymbol;
+	im_activatekey_state  = 0;
+	return TRUE;
+    }
+
+    return im_string_to_keyval((const char *)p_imak,
+			       &im_activatekey_keyval,
+			       &im_activatekey_state);
+}
+
+    static void
+im_synthesize_keypress(unsigned int keyval, unsigned int state)
+{
+    GdkEventKey *event;
+
+    event = (GdkEventKey *)gdk_event_new(GDK_KEY_PRESS);
+    g_object_ref(gtk_widget_get_window(gui.drawarea));
+					// unreffed by gdk_event_free()
+    event->window = gtk_widget_get_window(gui.drawarea);
+    event->send_event = TRUE;
+    event->time = GDK_CURRENT_TIME;
+    event->state  = state;
+    event->keyval = keyval;
+    event->hardware_keycode = // needed for XIM
+	XKeysymToKeycode(GDK_WINDOW_XDISPLAY(event->window), (KeySym)keyval);
+    event->length = 0;
+    event->string = NULL;
+
+    gtk_im_context_filter_keypress(xic, event);
+
+    // For consistency, also send the corresponding release event.
+    event->type = GDK_KEY_RELEASE;
+    event->send_event = FALSE;
+    gtk_im_context_filter_keypress(xic, event);
+
+    gdk_event_free((GdkEvent *)event);
+}
+
+    void
+xim_reset(void)
+{
+# ifdef FEAT_EVAL
+    if (USE_IMACTIVATEFUNC)
+	call_imactivatefunc(im_is_active);
+    else
+# endif
+    if (xic != NULL)
+    {
+	gtk_im_context_reset(xic);
+
+	if (p_imdisable)
+	    im_shutdown();
+	else
+	{
+	    xim_set_focus(gui.in_focus);
+
+	    if (im_activatekey_keyval != GDK_VoidSymbol)
+	    {
+		if (im_is_active)
+		{
+		    g_signal_handler_block(xic, im_commit_handler_id);
+		    im_synthesize_keypress(im_activatekey_keyval,
+						    im_activatekey_state);
+		    g_signal_handler_unblock(xic, im_commit_handler_id);
+		}
+	    }
+	    else
+	    {
+		im_shutdown();
+		xim_init();
+		xim_set_focus(gui.in_focus);
+	    }
+	}
+    }
+
+    if (p_imst == IM_ON_THE_SPOT)
+	preedit_start_col = MAXCOL;
+    xim_has_preediting = FALSE;
+}
+
+    int
+xim_queue_key_press_event(GdkEventKey *event, int down)
+{
+    if (down)
+    {
+	/*
+	 * Workaround GTK2 XIM 'feature' that always converts keypad keys to
+	 * chars., even when not part of an IM sequence (ref. feature of
+	 * gdk/gdkkeyuni.c).
+	 * Flag any keypad keys that might represent a single char.
+	 * If this (on its own - i.e., not part of an IM sequence) is
+	 * committed while we're processing one of these keys, we can ignore
+	 * that commit and go ahead & process it ourselves.  That way we can
+	 * still distinguish keypad keys for use in mappings.
+	 * Also add GDK_space to make <S-Space> work.
+	 */
+	switch (event->keyval)
+	{
+	    case GDK_KP_Add:      xim_expected_char = '+';  break;
+	    case GDK_KP_Subtract: xim_expected_char = '-';  break;
+	    case GDK_KP_Divide:   xim_expected_char = '/';  break;
+	    case GDK_KP_Multiply: xim_expected_char = '*';  break;
+	    case GDK_KP_Decimal:  xim_expected_char = '.';  break;
+	    case GDK_KP_Equal:    xim_expected_char = '=';  break;
+	    case GDK_KP_0:	  xim_expected_char = '0';  break;
+	    case GDK_KP_1:	  xim_expected_char = '1';  break;
+	    case GDK_KP_2:	  xim_expected_char = '2';  break;
+	    case GDK_KP_3:	  xim_expected_char = '3';  break;
+	    case GDK_KP_4:	  xim_expected_char = '4';  break;
+	    case GDK_KP_5:	  xim_expected_char = '5';  break;
+	    case GDK_KP_6:	  xim_expected_char = '6';  break;
+	    case GDK_KP_7:	  xim_expected_char = '7';  break;
+	    case GDK_KP_8:	  xim_expected_char = '8';  break;
+	    case GDK_KP_9:	  xim_expected_char = '9';  break;
+	    case GDK_space:	  xim_expected_char = ' ';  break;
+	    default:		  xim_expected_char = NUL;
+	}
+	xim_ignored_char = FALSE;
+    }
+
+    /*
+     * When typing fFtT, XIM may be activated. Thus it must pass
+     * gtk_im_context_filter_keypress() in Normal mode.
+     * And while doing :sh too.
+     */
+    if (xic != NULL && !p_imdisable
+		    && (State & (INSERT | CMDLINE | NORMAL | EXTERNCMD)) != 0)
+    {
+	/*
+	 * Filter 'imactivatekey' and map it to CTRL-^.  This way, Vim is
+	 * always aware of the current status of IM, and can even emulate
+	 * the activation key for modules that don't support one.
+	 */
+	if (event->keyval == im_activatekey_keyval
+	     && (event->state & im_activatekey_state) == im_activatekey_state)
+	{
+	    unsigned int state_mask;
+
+	    // Require the state of the 3 most used modifiers to match exactly.
+	    // Otherwise e.g. <S-C-space> would be unusable for other purposes
+	    // if the IM activate key is <S-space>.
+	    state_mask  = im_activatekey_state;
+	    state_mask |= ((int)GDK_SHIFT_MASK | (int)GDK_CONTROL_MASK
+							| (int)GDK_MOD1_MASK);
+
+	    if ((event->state & state_mask) != im_activatekey_state)
+		return FALSE;
+
+	    // Don't send it a second time on GDK_KEY_RELEASE.
+	    if (event->type != GDK_KEY_PRESS)
+		return TRUE;
+
+	    if (map_to_exists_mode((char_u *)"", LANGMAP, FALSE))
+	    {
+		im_set_active(FALSE);
+
+		// ":lmap" mappings exists, toggle use of mappings.
+		State ^= LANGMAP;
+		if (State & LANGMAP)
+		{
+		    curbuf->b_p_iminsert = B_IMODE_NONE;
+		    State &= ~LANGMAP;
+		}
+		else
+		{
+		    curbuf->b_p_iminsert = B_IMODE_LMAP;
+		    State |= LANGMAP;
+		}
+		return TRUE;
+	    }
+
+	    return gtk_im_context_filter_keypress(xic, event);
+	}
+
+	// Don't filter events through the IM context if IM isn't active
+	// right now.  Unlike with GTK+ 1.2 we cannot rely on the IM module
+	// not doing anything before the activation key was sent.
+	if (im_activatekey_keyval == GDK_VoidSymbol || im_is_active)
+	{
+	    int imresult = gtk_im_context_filter_keypress(xic, event);
+
+	    if (p_imst == IM_ON_THE_SPOT)
+	    {
+		// Some XIM send following sequence:
+		// 1. preedited string.
+		// 2. committed string.
+		// 3. line changed key.
+		// 4. preedited string.
+		// 5. remove preedited string.
+		// if 3, Vim can't move back the above line for 5.
+		// thus, this part should not parse the key.
+		if (!imresult && preedit_start_col != MAXCOL
+					    && event->keyval == GDK_Return)
+		{
+		    im_synthesize_keypress(GDK_Return, 0U);
+		    return FALSE;
+		}
+	    }
+
+	    // If XIM tried to commit a keypad key as a single char.,
+	    // ignore it so we can use the keypad key 'raw', for mappings.
+	    if (xim_expected_char != NUL && xim_ignored_char)
+		// We had a keypad key, and XIM tried to thieve it
+		return FALSE;
+
+	    // This is supposed to fix a problem with iBus, that space
+	    // characters don't work in input mode.
+	    xim_expected_char = NUL;
+
+	    // Normal processing
+	    return imresult;
+	}
+    }
+
+    return FALSE;
+}
+
+    int
+im_get_status(void)
+{
+#  ifdef FEAT_EVAL
+    if (USE_IMSTATUSFUNC)
+	return call_imstatusfunc();
+#  endif
+    return im_is_active;
+}
+
+    int
+preedit_get_status(void)
+{
+    return preedit_is_active;
+}
+
+    int
+im_is_preediting(void)
+{
+    return xim_has_preediting;
+}
+
+# else // !FEAT_GUI_GTK
+
+static int	xim_is_active = FALSE;  // XIM should be active in the current
+					// mode
+static int	xim_has_focus = FALSE;	// XIM is really being used for Vim
+#  ifdef FEAT_GUI_X11
+static XIMStyle	input_style;
+static int	status_area_enabled = TRUE;
+#  endif
+
+/*
+ * Switch using XIM on/off.  This is used by the code that changes "State".
+ * When 'imactivatefunc' is defined use that function instead.
+ */
+    void
+im_set_active(int active_arg)
+{
+    int active = active_arg;
+
+    // If 'imdisable' is set, XIM is never active.
+    if (p_imdisable)
+	active = FALSE;
+    else if (input_style & XIMPreeditPosition)
+	// There is a problem in switching XIM off when preediting is used,
+	// and it is not clear how this can be solved.  For now, keep XIM on
+	// all the time, like it was done in Vim 5.8.
+	active = TRUE;
+
+#  if defined(FEAT_EVAL)
+    if (USE_IMACTIVATEFUNC)
+    {
+	if (active != im_get_status())
+	{
+	    call_imactivatefunc(active);
+	    xim_has_focus = active;
+	}
+	return;
+    }
+#  endif
+
+    if (xic == NULL)
+	return;
+
+    // Remember the active state, it is needed when Vim gets keyboard focus.
+    xim_is_active = active;
+    xim_set_preedit();
+}
+
+/*
+ * Adjust using XIM for gaining or losing keyboard focus.  Also called when
+ * "xim_is_active" changes.
+ */
+    void
+xim_set_focus(int focus)
+{
+    if (xic == NULL)
+	return;
+
+    /*
+     * XIM only gets focus when the Vim window has keyboard focus and XIM has
+     * been set active for the current mode.
+     */
+    if (focus && xim_is_active)
+    {
+	if (!xim_has_focus)
+	{
+	    xim_has_focus = TRUE;
+	    XSetICFocus(xic);
+	}
+    }
+    else
+    {
+	if (xim_has_focus)
+	{
+	    xim_has_focus = FALSE;
+	    XUnsetICFocus(xic);
+	}
+    }
+}
+
+    void
+im_set_position(int row UNUSED, int col UNUSED)
+{
+    xim_set_preedit();
+}
+
+/*
+ * Set the XIM to the current cursor position.
+ */
+    void
+xim_set_preedit(void)
+{
+    XVaNestedList attr_list;
+    XRectangle spot_area;
+    XPoint over_spot;
+    int line_space;
+
+    if (xic == NULL)
+	return;
+
+    xim_set_focus(TRUE);
+
+    if (!xim_has_focus)
+    {
+	// hide XIM cursor
+	over_spot.x = 0;
+	over_spot.y = -100; // arbitrary invisible position
+	attr_list = (XVaNestedList) XVaCreateNestedList(0,
+							XNSpotLocation,
+							&over_spot,
+							NULL);
+	XSetICValues(xic, XNPreeditAttributes, attr_list, NULL);
+	XFree(attr_list);
+	return;
+    }
+
+    if (input_style & XIMPreeditPosition)
+    {
+	if (xim_fg_color == INVALCOLOR)
+	{
+	    xim_fg_color = gui.def_norm_pixel;
+	    xim_bg_color = gui.def_back_pixel;
+	}
+	over_spot.x = TEXT_X(gui.col);
+	over_spot.y = TEXT_Y(gui.row);
+	spot_area.x = 0;
+	spot_area.y = 0;
+	spot_area.height = gui.char_height * Rows;
+	spot_area.width  = gui.char_width * Columns;
+	line_space = gui.char_height;
+	attr_list = (XVaNestedList) XVaCreateNestedList(0,
+					XNSpotLocation, &over_spot,
+					XNForeground, (Pixel) xim_fg_color,
+					XNBackground, (Pixel) xim_bg_color,
+					XNArea, &spot_area,
+					XNLineSpace, line_space,
+					NULL);
+	if (XSetICValues(xic, XNPreeditAttributes, attr_list, NULL))
+	    emsg(_("E284: Cannot set IC values"));
+	XFree(attr_list);
+    }
+}
+
+#  if defined(FEAT_GUI_X11)
+static char e_xim[] = N_("E285: Failed to create input context");
+#  endif
+
+#  if defined(FEAT_GUI_X11) || defined(PROTO)
+#   if defined(XtSpecificationRelease) && XtSpecificationRelease >= 6 && !defined(SUN_SYSTEM)
+#    define USE_X11R6_XIM
+#   endif
+
+static int xim_real_init(Window x11_window, Display *x11_display);
+
+
+#  ifdef USE_X11R6_XIM
+    static void
+xim_instantiate_cb(
+    Display	*display,
+    XPointer	client_data UNUSED,
+    XPointer	call_data UNUSED)
+{
+    Window	x11_window;
+    Display	*x11_display;
+
+#   ifdef XIM_DEBUG
+    xim_log("xim_instantiate_cb()\n");
+#   endif
+
+    gui_get_x11_windis(&x11_window, &x11_display);
+    if (display != x11_display)
+	return;
+
+    xim_real_init(x11_window, x11_display);
+    gui_set_shellsize(FALSE, FALSE, RESIZE_BOTH);
+    if (xic != NULL)
+	XUnregisterIMInstantiateCallback(x11_display, NULL, NULL, NULL,
+					 xim_instantiate_cb, NULL);
+}
+
+    static void
+xim_destroy_cb(
+    XIM		im UNUSED,
+    XPointer	client_data UNUSED,
+    XPointer	call_data UNUSED)
+{
+    Window	x11_window;
+    Display	*x11_display;
+
+#   ifdef XIM_DEBUG
+    xim_log("xim_destroy_cb()\n");
+   #endif
+    gui_get_x11_windis(&x11_window, &x11_display);
+
+    xic = NULL;
+    status_area_enabled = FALSE;
+
+    gui_set_shellsize(FALSE, FALSE, RESIZE_BOTH);
+
+    XRegisterIMInstantiateCallback(x11_display, NULL, NULL, NULL,
+				   xim_instantiate_cb, NULL);
+}
+#  endif
+
+    void
+xim_init(void)
+{
+    Window	x11_window;
+    Display	*x11_display;
+
+#  ifdef XIM_DEBUG
+    xim_log("xim_init()\n");
+#  endif
+
+    gui_get_x11_windis(&x11_window, &x11_display);
+
+    xic = NULL;
+
+    if (xim_real_init(x11_window, x11_display))
+	return;
+
+    gui_set_shellsize(FALSE, FALSE, RESIZE_BOTH);
+
+#  ifdef USE_X11R6_XIM
+    XRegisterIMInstantiateCallback(x11_display, NULL, NULL, NULL,
+				   xim_instantiate_cb, NULL);
+#  endif
+}
+
+    static int
+xim_real_init(Window x11_window, Display *x11_display)
+{
+    int		i;
+    char	*p,
+		*s,
+		*ns,
+		*end,
+		tmp[1024];
+#  define IMLEN_MAX 40
+    char	buf[IMLEN_MAX + 7];
+    XIM		xim = NULL;
+    XIMStyles	*xim_styles;
+    XIMStyle	this_input_style = 0;
+    Boolean	found;
+    XPoint	over_spot;
+    XVaNestedList preedit_list, status_list;
+
+    input_style = 0;
+    status_area_enabled = FALSE;
+
+    if (xic != NULL)
+	return FALSE;
+
+    if (gui.rsrc_input_method != NULL && *gui.rsrc_input_method != NUL)
+    {
+	strcpy(tmp, gui.rsrc_input_method);
+	for (ns = s = tmp; ns != NULL && *s != NUL;)
+	{
+	    s = (char *)skipwhite((char_u *)s);
+	    if (*s == NUL)
+		break;
+	    if ((ns = end = strchr(s, ',')) == NULL)
+		end = s + strlen(s);
+	    while (isspace(((char_u *)end)[-1]))
+		end--;
+	    *end = NUL;
+
+	    if (strlen(s) <= IMLEN_MAX)
+	    {
+		strcpy(buf, "@im=");
+		strcat(buf, s);
+		if ((p = XSetLocaleModifiers(buf)) != NULL && *p != NUL
+			&& (xim = XOpenIM(x11_display, NULL, NULL, NULL))
+								      != NULL)
+		    break;
+	    }
+
+	    s = ns + 1;
+	}
+    }
+
+    if (xim == NULL && (p = XSetLocaleModifiers("")) != NULL && *p != NUL)
+	xim = XOpenIM(x11_display, NULL, NULL, NULL);
+
+    // This is supposed to be useful to obtain characters through
+    // XmbLookupString() without really using a XIM.
+    if (xim == NULL && (p = XSetLocaleModifiers("@im=none")) != NULL
+								 && *p != NUL)
+	xim = XOpenIM(x11_display, NULL, NULL, NULL);
+
+    if (xim == NULL)
+    {
+	// Only give this message when verbose is set, because too many people
+	// got this message when they didn't want to use a XIM.
+	if (p_verbose > 0)
+	{
+	    verbose_enter();
+	    emsg(_("E286: Failed to open input method"));
+	    verbose_leave();
+	}
+	return FALSE;
+    }
+
+#  ifdef USE_X11R6_XIM
+    {
+	XIMCallback destroy_cb;
+
+	destroy_cb.callback = xim_destroy_cb;
+	destroy_cb.client_data = NULL;
+	if (XSetIMValues(xim, XNDestroyCallback, &destroy_cb, NULL))
+	    emsg(_("E287: Warning: Could not set destroy callback to IM"));
+    }
+#  endif
+
+    if (XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL) || !xim_styles)
+    {
+	emsg(_("E288: input method doesn't support any style"));
+	XCloseIM(xim);
+	return FALSE;
+    }
+
+    found = False;
+    strcpy(tmp, gui.rsrc_preedit_type_name);
+    for (s = tmp; s && !found; )
+    {
+	while (*s && isspace((unsigned char)*s))
+	    s++;
+	if (!*s)
+	    break;
+	if ((ns = end = strchr(s, ',')) != 0)
+	    ns++;
+	else
+	    end = s + strlen(s);
+	while (isspace((unsigned char)*end))
+	    end--;
+	*end = '\0';
+
+	if (!strcmp(s, "OverTheSpot"))
+	    this_input_style = (XIMPreeditPosition | XIMStatusArea);
+	else if (!strcmp(s, "OffTheSpot"))
+	    this_input_style = (XIMPreeditArea | XIMStatusArea);
+	else if (!strcmp(s, "Root"))
+	    this_input_style = (XIMPreeditNothing | XIMStatusNothing);
+
+	for (i = 0; (unsigned short)i < xim_styles->count_styles; i++)
+	{
+	    if (this_input_style == xim_styles->supported_styles[i])
+	    {
+		found = True;
+		break;
+	    }
+	}
+	if (!found)
+	    for (i = 0; (unsigned short)i < xim_styles->count_styles; i++)
+	    {
+		if ((xim_styles->supported_styles[i] & this_input_style)
+			== (this_input_style & ~XIMStatusArea))
+		{
+		    this_input_style &= ~XIMStatusArea;
+		    found = True;
+		    break;
+		}
+	    }
+
+	s = ns;
+    }
+    XFree(xim_styles);
+
+    if (!found)
+    {
+	// Only give this message when verbose is set, because too many people
+	// got this message when they didn't want to use a XIM.
+	if (p_verbose > 0)
+	{
+	    verbose_enter();
+	    emsg(_("E289: input method doesn't support my preedit type"));
+	    verbose_leave();
+	}
+	XCloseIM(xim);
+	return FALSE;
+    }
+
+    over_spot.x = TEXT_X(gui.col);
+    over_spot.y = TEXT_Y(gui.row);
+    input_style = this_input_style;
+
+    // A crash was reported when trying to pass gui.norm_font as XNFontSet,
+    // thus that has been removed.  Hopefully the default works...
+#  ifdef FEAT_XFONTSET
+    if (gui.fontset != NOFONTSET)
+    {
+	preedit_list = XVaCreateNestedList(0,
+				XNSpotLocation, &over_spot,
+				XNForeground, (Pixel)gui.def_norm_pixel,
+				XNBackground, (Pixel)gui.def_back_pixel,
+				XNFontSet, (XFontSet)gui.fontset,
+				NULL);
+	status_list = XVaCreateNestedList(0,
+				XNForeground, (Pixel)gui.def_norm_pixel,
+				XNBackground, (Pixel)gui.def_back_pixel,
+				XNFontSet, (XFontSet)gui.fontset,
+				NULL);
+    }
+    else
+#  endif
+    {
+	preedit_list = XVaCreateNestedList(0,
+				XNSpotLocation, &over_spot,
+				XNForeground, (Pixel)gui.def_norm_pixel,
+				XNBackground, (Pixel)gui.def_back_pixel,
+				NULL);
+	status_list = XVaCreateNestedList(0,
+				XNForeground, (Pixel)gui.def_norm_pixel,
+				XNBackground, (Pixel)gui.def_back_pixel,
+				NULL);
+    }
+
+    xic = XCreateIC(xim,
+		    XNInputStyle, input_style,
+		    XNClientWindow, x11_window,
+		    XNFocusWindow, gui.wid,
+		    XNPreeditAttributes, preedit_list,
+		    XNStatusAttributes, status_list,
+		    NULL);
+    XFree(status_list);
+    XFree(preedit_list);
+    if (xic != NULL)
+    {
+	if (input_style & XIMStatusArea)
+	{
+	    xim_set_status_area();
+	    status_area_enabled = TRUE;
+	}
+	else
+	    gui_set_shellsize(FALSE, FALSE, RESIZE_BOTH);
+    }
+    else
+    {
+	if (!is_not_a_term())
+	    emsg(_(e_xim));
+	XCloseIM(xim);
+	return FALSE;
+    }
+
+    return TRUE;
+}
+
+#  endif // FEAT_GUI_X11
+
+/*
+ * Get IM status.  When IM is on, return TRUE.  Else return FALSE.
+ * FIXME: This doesn't work correctly: Having focus doesn't always mean XIM is
+ * active, when not having focus XIM may still be active (e.g., when using a
+ * tear-off menu item).
+ */
+    int
+im_get_status(void)
+{
+#  ifdef FEAT_EVAL
+    if (USE_IMSTATUSFUNC)
+	return call_imstatusfunc();
+#  endif
+    return xim_has_focus;
+}
+
+# endif // !FEAT_GUI_GTK
+
+# if !defined(FEAT_GUI_GTK) || defined(PROTO)
+/*
+ * Set up the status area.
+ *
+ * This should use a separate Widget, but that seems not possible, because
+ * preedit_area and status_area should be set to the same window as for the
+ * text input.  Unfortunately this means the status area pollutes the text
+ * window...
+ */
+    void
+xim_set_status_area(void)
+{
+    XVaNestedList preedit_list = 0, status_list = 0, list = 0;
+    XRectangle pre_area, status_area;
+
+    if (xic == NULL)
+	return;
+
+    if (input_style & XIMStatusArea)
+    {
+	if (input_style & XIMPreeditArea)
+	{
+	    XRectangle *needed_rect;
+
+	    // to get status_area width
+	    status_list = XVaCreateNestedList(0, XNAreaNeeded,
+					      &needed_rect, NULL);
+	    XGetICValues(xic, XNStatusAttributes, status_list, NULL);
+	    XFree(status_list);
+
+	    status_area.width = needed_rect->width;
+	}
+	else
+	    status_area.width = gui.char_width * Columns;
+
+	status_area.x = 0;
+	status_area.y = gui.char_height * Rows + gui.border_offset;
+	if (gui.which_scrollbars[SBAR_BOTTOM])
+	    status_area.y += gui.scrollbar_height;
+#ifdef FEAT_MENU
+	if (gui.menu_is_active)
+	    status_area.y += gui.menu_height;
+#endif
+	status_area.height = gui.char_height;
+	status_list = XVaCreateNestedList(0, XNArea, &status_area, NULL);
+    }
+    else
+    {
+	status_area.x = 0;
+	status_area.y = gui.char_height * Rows + gui.border_offset;
+	if (gui.which_scrollbars[SBAR_BOTTOM])
+	    status_area.y += gui.scrollbar_height;
+#ifdef FEAT_MENU
+	if (gui.menu_is_active)
+	    status_area.y += gui.menu_height;
+#endif
+	status_area.width = 0;
+	status_area.height = gui.char_height;
+    }
+
+    if (input_style & XIMPreeditArea)   // off-the-spot
+    {
+	pre_area.x = status_area.x + status_area.width;
+	pre_area.y = gui.char_height * Rows + gui.border_offset;
+	pre_area.width = gui.char_width * Columns - pre_area.x;
+	if (gui.which_scrollbars[SBAR_BOTTOM])
+	    pre_area.y += gui.scrollbar_height;
+#ifdef FEAT_MENU
+	if (gui.menu_is_active)
+	    pre_area.y += gui.menu_height;
+#endif
+	pre_area.height = gui.char_height;
+	preedit_list = XVaCreateNestedList(0, XNArea, &pre_area, NULL);
+    }
+    else if (input_style & XIMPreeditPosition)   // over-the-spot
+    {
+	pre_area.x = 0;
+	pre_area.y = 0;
+	pre_area.height = gui.char_height * Rows;
+	pre_area.width = gui.char_width * Columns;
+	preedit_list = XVaCreateNestedList(0, XNArea, &pre_area, NULL);
+    }
+
+    if (preedit_list && status_list)
+	list = XVaCreateNestedList(0, XNPreeditAttributes, preedit_list,
+				   XNStatusAttributes, status_list, NULL);
+    else if (preedit_list)
+	list = XVaCreateNestedList(0, XNPreeditAttributes, preedit_list,
+				   NULL);
+    else if (status_list)
+	list = XVaCreateNestedList(0, XNStatusAttributes, status_list,
+				   NULL);
+    else
+	list = NULL;
+
+    if (list)
+    {
+	XSetICValues(xic, XNVaNestedList, list, NULL);
+	XFree(list);
+    }
+    if (status_list)
+	XFree(status_list);
+    if (preedit_list)
+	XFree(preedit_list);
+}
+
+    int
+xim_get_status_area_height(void)
+{
+    if (status_area_enabled)
+	return gui.char_height;
+    return 0;
+}
+# endif
+
+#else // !defined(FEAT_XIM)
+
+# if defined(IME_WITHOUT_XIM) || defined(VIMDLL) || defined(PROTO)
+static int im_was_set_active = FALSE;
+
+    int
+#  ifdef VIMDLL
+mbyte_im_get_status(void)
+#  else
+im_get_status(void)
+#  endif
+{
+#  if defined(FEAT_EVAL)
+    if (USE_IMSTATUSFUNC)
+	return call_imstatusfunc();
+#  endif
+    return im_was_set_active;
+}
+
+    void
+#  ifdef VIMDLL
+mbyte_im_set_active(int active_arg)
+#  else
+im_set_active(int active_arg)
+#  endif
+{
+#  if defined(FEAT_EVAL)
+    int	    active = !p_imdisable && active_arg;
+
+    if (USE_IMACTIVATEFUNC && active != im_get_status())
+    {
+	call_imactivatefunc(active);
+	im_was_set_active = active;
+    }
+#  endif
+}
+
+#  if defined(FEAT_GUI) && !defined(FEAT_GUI_HAIKU) && !defined(VIMDLL)
+    void
+im_set_position(int row UNUSED, int col UNUSED)
+{
+}
+#  endif
+# endif
+
+#endif // FEAT_XIM
--- a/src/mbyte.c
+++ b/src/mbyte.c
@@ -111,19 +111,6 @@
 # endif
 #endif
 
-#if defined(FEAT_GUI_GTK) && defined(FEAT_XIM)
-# if GTK_CHECK_VERSION(3,0,0)
-#  include <gdk/gdkkeysyms-compat.h>
-# else
-#  include <gdk/gdkkeysyms.h>
-# endif
-# ifdef MSWIN
-#  include <gdk/gdkwin32.h>
-# else
-#  include <gdk/gdkx.h>
-# endif
-#endif
-
 #ifdef HAVE_WCHAR_H
 # include <wchar.h>
 #endif
@@ -179,37 +166,6 @@ static char utf8len_tab_zero[256] =
     3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,0,0,
 };
 
-/*
- * XIM often causes trouble.  Define XIM_DEBUG to get a log of XIM callbacks
- * in the "xim.log" file.
- */
-// #define XIM_DEBUG
-#ifdef XIM_DEBUG
-    static void
-xim_log(char *s, ...)
-{
-    va_list arglist;
-    static FILE *fd = NULL;
-
-    if (fd == (FILE *)-1)
-	return;
-    if (fd == NULL)
-    {
-	fd = mch_fopen("xim.log", "w");
-	if (fd == NULL)
-	{
-	    emsg("Cannot open xim.log");
-	    fd = (FILE *)-1;
-	    return;
-	}
-    }
-
-    va_start(arglist, s);
-    vfprintf(fd, s, arglist);
-    va_end(arglist);
-}
-#endif
-
 
 /*
  * Canonical encoding names and their properties.
@@ -4778,1726 +4734,6 @@ iconv_end(void)
 #  endif // DYNAMIC_ICONV
 # endif // USE_ICONV
 
-
-#ifdef FEAT_GUI
-# define USE_IMACTIVATEFUNC (!gui.in_use && *p_imaf != NUL)
-# define USE_IMSTATUSFUNC (!gui.in_use && *p_imsf != NUL)
-#else
-# define USE_IMACTIVATEFUNC (*p_imaf != NUL)
-# define USE_IMSTATUSFUNC (*p_imsf != NUL)
-#endif
-
-#if defined(FEAT_EVAL) && \
-    (defined(FEAT_XIM) || defined(IME_WITHOUT_XIM) || defined(VIMDLL))
-    static void
-call_imactivatefunc(int active)
-{
-    typval_T argv[2];
-
-    argv[0].v_type = VAR_NUMBER;
-    argv[0].vval.v_number = active ? 1 : 0;
-    argv[1].v_type = VAR_UNKNOWN;
-    (void)call_func_retnr(p_imaf, 1, argv);
-}
-
-    static int
-call_imstatusfunc(void)
-{
-    int is_active;
-
-    // FIXME: Don't execute user function in unsafe situation.
-    if (exiting || is_autocmd_blocked())
-	return FALSE;
-    // FIXME: :py print 'xxx' is shown duplicate result.
-    // Use silent to avoid it.
-    ++msg_silent;
-    is_active = call_func_retnr(p_imsf, 0, NULL);
-    --msg_silent;
-    return (is_active > 0);
-}
-#endif
-
-#if defined(FEAT_XIM) || defined(PROTO)
-
-# if defined(FEAT_GUI_GTK) || defined(PROTO)
-static int xim_has_preediting INIT(= FALSE);  // IM current status
-
-/*
- * Set preedit_start_col to the current cursor position.
- */
-    static void
-init_preedit_start_col(void)
-{
-    if (State & CMDLINE)
-	preedit_start_col = cmdline_getvcol_cursor();
-    else if (curwin != NULL && curwin->w_buffer != NULL)
-	getvcol(curwin, &curwin->w_cursor, &preedit_start_col, NULL, NULL);
-    // Prevent that preediting marks the buffer as changed.
-    xim_changed_while_preediting = curbuf->b_changed;
-}
-
-static int im_is_active	       = FALSE;	// IM is enabled for current mode
-static int preedit_is_active   = FALSE;
-static int im_preedit_cursor   = 0;	// cursor offset in characters
-static int im_preedit_trailing = 0;	// number of characters after cursor
-
-static unsigned long im_commit_handler_id  = 0;
-static unsigned int  im_activatekey_keyval = GDK_VoidSymbol;
-static unsigned int  im_activatekey_state  = 0;
-
-static GtkWidget *preedit_window = NULL;
-static GtkWidget *preedit_label = NULL;
-
-static void im_preedit_window_set_position(void);
-
-    void
-im_set_active(int active)
-{
-    int was_active;
-
-    was_active = !!im_get_status();
-    im_is_active = (active && !p_imdisable);
-
-    if (im_is_active != was_active)
-	xim_reset();
-}
-
-    void
-xim_set_focus(int focus)
-{
-    if (xic != NULL)
-    {
-	if (focus)
-	    gtk_im_context_focus_in(xic);
-	else
-	    gtk_im_context_focus_out(xic);
-    }
-}
-
-    void
-im_set_position(int row, int col)
-{
-    if (xic != NULL)
-    {
-	GdkRectangle area;
-
-	area.x = FILL_X(col);
-	area.y = FILL_Y(row);
-	area.width  = gui.char_width * (mb_lefthalve(row, col) ? 2 : 1);
-	area.height = gui.char_height;
-
-	gtk_im_context_set_cursor_location(xic, &area);
-
-	if (p_imst == IM_OVER_THE_SPOT)
-	    im_preedit_window_set_position();
-    }
-}
-
-#  if 0 || defined(PROTO) // apparently only used in gui_x11.c
-    void
-xim_set_preedit(void)
-{
-    im_set_position(gui.row, gui.col);
-}
-#  endif
-
-    static void
-im_add_to_input(char_u *str, int len)
-{
-    // Convert from 'termencoding' (always "utf-8") to 'encoding'
-    if (input_conv.vc_type != CONV_NONE)
-    {
-	str = string_convert(&input_conv, str, &len);
-	g_return_if_fail(str != NULL);
-    }
-
-    add_to_input_buf_csi(str, len);
-
-    if (input_conv.vc_type != CONV_NONE)
-	vim_free(str);
-
-    if (p_mh) // blank out the pointer if necessary
-	gui_mch_mousehide(TRUE);
-}
-
-     static void
-im_preedit_window_set_position(void)
-{
-    int x, y, width, height;
-    int screen_x, screen_y, screen_width, screen_height;
-
-    if (preedit_window == NULL)
-	return;
-
-    gui_gtk_get_screen_geom_of_win(gui.drawarea,
-			  &screen_x, &screen_y, &screen_width, &screen_height);
-    gdk_window_get_origin(gtk_widget_get_window(gui.drawarea), &x, &y);
-    gtk_window_get_size(GTK_WINDOW(preedit_window), &width, &height);
-    x = x + FILL_X(gui.col);
-    y = y + FILL_Y(gui.row);
-    if (x + width > screen_x + screen_width)
-	x = screen_x + screen_width - width;
-    if (y + height > screen_y + screen_height)
-	y = screen_y + screen_height - height;
-    gtk_window_move(GTK_WINDOW(preedit_window), x, y);
-}
-
-    static void
-im_preedit_window_open()
-{
-    char *preedit_string;
-#if !GTK_CHECK_VERSION(3,16,0)
-    char buf[8];
-#endif
-    PangoAttrList *attr_list;
-    PangoLayout *layout;
-#if GTK_CHECK_VERSION(3,0,0)
-# if !GTK_CHECK_VERSION(3,16,0)
-    GdkRGBA color;
-# endif
-#else
-    GdkColor color;
-#endif
-    gint w, h;
-
-    if (preedit_window == NULL)
-    {
-	preedit_window = gtk_window_new(GTK_WINDOW_POPUP);
-	gtk_window_set_transient_for(GTK_WINDOW(preedit_window),
-						     GTK_WINDOW(gui.mainwin));
-	preedit_label = gtk_label_new("");
-	gtk_widget_set_name(preedit_label, "vim-gui-preedit-area");
-	gtk_container_add(GTK_CONTAINER(preedit_window), preedit_label);
-    }
-
-#if GTK_CHECK_VERSION(3,16,0)
-    {
-	GtkStyleContext * const context
-				  = gtk_widget_get_style_context(gui.drawarea);
-	GtkCssProvider * const provider = gtk_css_provider_new();
-	gchar		   *css = NULL;
-	const char * const fontname
-			   = pango_font_description_get_family(gui.norm_font);
-	gint	fontsize
-		= pango_font_description_get_size(gui.norm_font) / PANGO_SCALE;
-	gchar	*fontsize_propval = NULL;
-
-	if (!pango_font_description_get_size_is_absolute(gui.norm_font))
-	{
-	    // fontsize was given in points.  Convert it into that in pixels
-	    // to use with CSS.
-	    GdkScreen * const screen
-		  = gdk_window_get_screen(gtk_widget_get_window(gui.mainwin));
-	    const gdouble dpi = gdk_screen_get_resolution(screen);
-	    fontsize = dpi * fontsize / 72;
-	}
-	if (fontsize > 0)
-	    fontsize_propval = g_strdup_printf("%dpx", fontsize);
-	else
-	    fontsize_propval = g_strdup_printf("inherit");
-
-	css = g_strdup_printf(
-		"widget#vim-gui-preedit-area {\n"
-		"  font-family: %s,monospace;\n"
-		"  font-size: %s;\n"
-		"  color: #%.2lx%.2lx%.2lx;\n"
-		"  background-color: #%.2lx%.2lx%.2lx;\n"
-		"}\n",
-		fontname != NULL ? fontname : "inherit",
-		fontsize_propval,
-		(gui.norm_pixel >> 16) & 0xff,
-		(gui.norm_pixel >> 8) & 0xff,
-		gui.norm_pixel & 0xff,
-		(gui.back_pixel >> 16) & 0xff,
-		(gui.back_pixel >> 8) & 0xff,
-		gui.back_pixel & 0xff);
-
-	gtk_css_provider_load_from_data(provider, css, -1, NULL);
-	gtk_style_context_add_provider(context,
-				     GTK_STYLE_PROVIDER(provider), G_MAXUINT);
-
-	g_free(css);
-	g_free(fontsize_propval);
-	g_object_unref(provider);
-    }
-#elif GTK_CHECK_VERSION(3,0,0)
-    gtk_widget_override_font(preedit_label, gui.norm_font);
-
-    vim_snprintf(buf, sizeof(buf), "#%06X", gui.norm_pixel);
-    gdk_rgba_parse(&color, buf);
-    gtk_widget_override_color(preedit_label, GTK_STATE_FLAG_NORMAL, &color);
-
-    vim_snprintf(buf, sizeof(buf), "#%06X", gui.back_pixel);
-    gdk_rgba_parse(&color, buf);
-    gtk_widget_override_background_color(preedit_label, GTK_STATE_FLAG_NORMAL,
-								      &color);
-#else
-    gtk_widget_modify_font(preedit_label, gui.norm_font);
-
-    vim_snprintf(buf, sizeof(buf), "#%06X", (unsigned)gui.norm_pixel);
-    gdk_color_parse(buf, &color);
-    gtk_widget_modify_fg(preedit_label, GTK_STATE_NORMAL, &color);
-
-    vim_snprintf(buf, sizeof(buf), "#%06X", (unsigned)gui.back_pixel);
-    gdk_color_parse(buf, &color);
-    gtk_widget_modify_bg(preedit_window, GTK_STATE_NORMAL, &color);
-#endif
-
-    gtk_im_context_get_preedit_string(xic, &preedit_string, &attr_list, NULL);
-
-    if (preedit_string[0] != NUL)
-    {
-	gtk_label_set_text(GTK_LABEL(preedit_label), preedit_string);
-	gtk_label_set_attributes(GTK_LABEL(preedit_label), attr_list);
-
-	layout = gtk_label_get_layout(GTK_LABEL(preedit_label));
-	pango_layout_get_pixel_size(layout, &w, &h);
-	h = MAX(h, gui.char_height);
-	gtk_window_resize(GTK_WINDOW(preedit_window), w, h);
-
-	gtk_widget_show_all(preedit_window);
-
-	im_preedit_window_set_position();
-    }
-
-    g_free(preedit_string);
-    pango_attr_list_unref(attr_list);
-}
-
-    static void
-im_preedit_window_close()
-{
-    if (preedit_window != NULL)
-	gtk_widget_hide(preedit_window);
-}
-
-    static void
-im_show_preedit()
-{
-    im_preedit_window_open();
-
-    if (p_mh) // blank out the pointer if necessary
-	gui_mch_mousehide(TRUE);
-}
-
-    static void
-im_delete_preedit(void)
-{
-    char_u bskey[]  = {CSI, 'k', 'b'};
-    char_u delkey[] = {CSI, 'k', 'D'};
-
-    if (p_imst == IM_OVER_THE_SPOT)
-    {
-	im_preedit_window_close();
-	return;
-    }
-
-    if (State & NORMAL
-#ifdef FEAT_TERMINAL
-	    && !term_use_loop()
-#endif
-       )
-    {
-	im_preedit_cursor = 0;
-	return;
-    }
-    for (; im_preedit_cursor > 0; --im_preedit_cursor)
-	add_to_input_buf(bskey, (int)sizeof(bskey));
-
-    for (; im_preedit_trailing > 0; --im_preedit_trailing)
-	add_to_input_buf(delkey, (int)sizeof(delkey));
-}
-
-/*
- * Move the cursor left by "num_move_back" characters.
- * Note that ins_left() checks im_is_preediting() to avoid breaking undo for
- * these K_LEFT keys.
- */
-    static void
-im_correct_cursor(int num_move_back)
-{
-    char_u backkey[] = {CSI, 'k', 'l'};
-
-    if (State & NORMAL)
-	return;
-#  ifdef FEAT_RIGHTLEFT
-    if ((State & CMDLINE) == 0 && curwin != NULL && curwin->w_p_rl)
-	backkey[2] = 'r';
-#  endif
-    for (; num_move_back > 0; --num_move_back)
-	add_to_input_buf(backkey, (int)sizeof(backkey));
-}
-
-static int xim_expected_char = NUL;
-static int xim_ignored_char = FALSE;
-
-/*
- * Update the mode and cursor while in an IM callback.
- */
-    static void
-im_show_info(void)
-{
-    int	    old_vgetc_busy;
-
-    old_vgetc_busy = vgetc_busy;
-    vgetc_busy = TRUE;
-    showmode();
-    vgetc_busy = old_vgetc_busy;
-    if ((State & NORMAL) || (State & INSERT))
-	setcursor();
-    out_flush();
-}
-
-/*
- * Callback invoked when the user finished preediting.
- * Put the final string into the input buffer.
- */
-    static void
-im_commit_cb(GtkIMContext *context UNUSED,
-	     const gchar *str,
-	     gpointer data UNUSED)
-{
-    int		slen = (int)STRLEN(str);
-    int		add_to_input = TRUE;
-    int		clen;
-    int		len = slen;
-    int		commit_with_preedit = TRUE;
-    char_u	*im_str;
-
-#ifdef XIM_DEBUG
-    xim_log("im_commit_cb(): %s\n", str);
-#endif
-
-    if (p_imst == IM_ON_THE_SPOT)
-    {
-	// The imhangul module doesn't reset the preedit string before
-	// committing.  Call im_delete_preedit() to work around that.
-	im_delete_preedit();
-
-	// Indicate that preediting has finished.
-	if (preedit_start_col == MAXCOL)
-	{
-	    init_preedit_start_col();
-	    commit_with_preedit = FALSE;
-	}
-
-	// The thing which setting "preedit_start_col" to MAXCOL means that
-	// "preedit_start_col" will be set forcedly when calling
-	// preedit_changed_cb() next time.
-	// "preedit_start_col" should not reset with MAXCOL on this part. Vim
-	// is simulating the preediting by using add_to_input_str(). when
-	// preedit begin immediately before committed, the typebuf is not
-	// flushed to screen, then it can't get correct "preedit_start_col".
-	// Thus, it should calculate the cells by adding cells of the committed
-	// string.
-	if (input_conv.vc_type != CONV_NONE)
-	{
-	    im_str = string_convert(&input_conv, (char_u *)str, &len);
-	    g_return_if_fail(im_str != NULL);
-	}
-	else
-	    im_str = (char_u *)str;
-
-	clen = mb_string2cells(im_str, len);
-
-	if (input_conv.vc_type != CONV_NONE)
-	    vim_free(im_str);
-	preedit_start_col += clen;
-    }
-
-    // Is this a single character that matches a keypad key that's just
-    // been pressed?  If so, we don't want it to be entered as such - let
-    // us carry on processing the raw keycode so that it may be used in
-    // mappings as <kSomething>.
-    if (xim_expected_char != NUL)
-    {
-	// We're currently processing a keypad or other special key
-	if (slen == 1 && str[0] == xim_expected_char)
-	{
-	    // It's a match - don't do it here
-	    xim_ignored_char = TRUE;
-	    add_to_input = FALSE;
-	}
-	else
-	{
-	    // Not a match
-	    xim_ignored_char = FALSE;
-	}
-    }
-
-    if (add_to_input)
-	im_add_to_input((char_u *)str, slen);
-
-    if (p_imst == IM_ON_THE_SPOT)
-    {
-	// Inserting chars while "im_is_active" is set does not cause a
-	// change of buffer.  When the chars are committed the buffer must be
-	// marked as changed.
-	if (!commit_with_preedit)
-	    preedit_start_col = MAXCOL;
-
-	// This flag is used in changed() at next call.
-	xim_changed_while_preediting = TRUE;
-    }
-
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
-}
-
-/*
- * Callback invoked after start to the preedit.
- */
-    static void
-im_preedit_start_cb(GtkIMContext *context UNUSED, gpointer data UNUSED)
-{
-#ifdef XIM_DEBUG
-    xim_log("im_preedit_start_cb()\n");
-#endif
-
-    im_is_active = TRUE;
-    preedit_is_active = TRUE;
-    gui_update_cursor(TRUE, FALSE);
-    im_show_info();
-}
-
-/*
- * Callback invoked after end to the preedit.
- */
-    static void
-im_preedit_end_cb(GtkIMContext *context UNUSED, gpointer data UNUSED)
-{
-#ifdef XIM_DEBUG
-    xim_log("im_preedit_end_cb()\n");
-#endif
-    im_delete_preedit();
-
-    // Indicate that preediting has finished
-    if (p_imst == IM_ON_THE_SPOT)
-	preedit_start_col = MAXCOL;
-    xim_has_preediting = FALSE;
-
-#if 0
-    // Removal of this line suggested by Takuhiro Nishioka.  Fixes that IM was
-    // switched off unintentionally.  We now use preedit_is_active (added by
-    // SungHyun Nam).
-    im_is_active = FALSE;
-#endif
-    preedit_is_active = FALSE;
-    gui_update_cursor(TRUE, FALSE);
-    im_show_info();
-}
-
-/*
- * Callback invoked after changes to the preedit string.  If the preedit
- * string was empty before, remember the preedit start column so we know
- * where to apply feedback attributes.  Delete the previous preedit string
- * if there was one, save the new preedit cursor offset, and put the new
- * string into the input buffer.
- *
- * TODO: The pragmatic "put into input buffer" approach used here has
- *       several fundamental problems:
- *
- * - The characters in the preedit string are subject to remapping.
- *   That's broken, only the finally committed string should be remapped.
- *
- * - There is a race condition involved:  The retrieved value for the
- *   current cursor position will be wrong if any unprocessed characters
- *   are still queued in the input buffer.
- *
- * - Due to the lack of synchronization between the file buffer in memory
- *   and any typed characters, it's practically impossible to implement the
- *   "retrieve_surrounding" and "delete_surrounding" signals reliably.  IM
- *   modules for languages such as Thai are likely to rely on this feature
- *   for proper operation.
- *
- * Conclusions:  I think support for preediting needs to be moved to the
- * core parts of Vim.  Ideally, until it has been committed, the preediting
- * string should only be displayed and not affect the buffer content at all.
- * The question how to deal with the synchronization issue still remains.
- * Circumventing the input buffer is probably not desirable.  Anyway, I think
- * implementing "retrieve_surrounding" is the only hard problem.
- *
- * One way to solve all of this in a clean manner would be to queue all key
- * press/release events "as is" in the input buffer, and apply the IM filtering
- * at the receiving end of the queue.  This, however, would have a rather large
- * impact on the code base.  If there is an easy way to force processing of all
- * remaining input from within the "retrieve_surrounding" signal handler, this
- * might not be necessary.  Gotta ask on vim-dev for opinions.
- */
-    static void
-im_preedit_changed_cb(GtkIMContext *context, gpointer data UNUSED)
-{
-    char    *preedit_string = NULL;
-    int	    cursor_index    = 0;
-    int	    num_move_back   = 0;
-    char_u  *str;
-    char_u  *p;
-    int	    i;
-
-    if (p_imst == IM_ON_THE_SPOT)
-	gtk_im_context_get_preedit_string(context,
-					  &preedit_string, NULL,
-					  &cursor_index);
-    else
-	gtk_im_context_get_preedit_string(context,
-					  &preedit_string, NULL,
-					  NULL);
-
-#ifdef XIM_DEBUG
-    xim_log("im_preedit_changed_cb(): %s\n", preedit_string);
-#endif
-
-    g_return_if_fail(preedit_string != NULL); // just in case
-
-    if (p_imst == IM_OVER_THE_SPOT)
-    {
-	if (preedit_string[0] == NUL)
-	{
-	    xim_has_preediting = FALSE;
-	    im_delete_preedit();
-	}
-	else
-	{
-	    xim_has_preediting = TRUE;
-	    im_show_preedit();
-	}
-    }
-    else
-    {
-	// If preedit_start_col is MAXCOL set it to the current cursor position.
-	if (preedit_start_col == MAXCOL && preedit_string[0] != '\0')
-	{
-	    xim_has_preediting = TRUE;
-
-	    // Urgh, this breaks if the input buffer isn't empty now
-	    init_preedit_start_col();
-	}
-	else if (cursor_index == 0 && preedit_string[0] == '\0')
-	{
-	    xim_has_preediting = FALSE;
-
-	    // If at the start position (after typing backspace)
-	    // preedit_start_col must be reset.
-	    preedit_start_col = MAXCOL;
-	}
-
-	im_delete_preedit();
-
-	/*
-	 * Compute the end of the preediting area: "preedit_end_col".
-	 * According to the documentation of gtk_im_context_get_preedit_string(),
-	 * the cursor_pos output argument returns the offset in bytes.  This is
-	 * unfortunately not true -- real life shows the offset is in characters,
-	 * and the GTK+ source code agrees with me.  Will file a bug later.
-	 */
-	if (preedit_start_col != MAXCOL)
-	    preedit_end_col = preedit_start_col;
-	str = (char_u *)preedit_string;
-	for (p = str, i = 0; *p != NUL; p += utf_byte2len(*p), ++i)
-	{
-	    int is_composing;
-
-	    is_composing = ((*p & 0x80) != 0 && utf_iscomposing(utf_ptr2char(p)));
-	    /*
-	     * These offsets are used as counters when generating <BS> and <Del>
-	     * to delete the preedit string.  So don't count composing characters
-	     * unless 'delcombine' is enabled.
-	     */
-	    if (!is_composing || p_deco)
-	    {
-		if (i < cursor_index)
-		    ++im_preedit_cursor;
-		else
-		    ++im_preedit_trailing;
-	    }
-	    if (!is_composing && i >= cursor_index)
-	    {
-		// This is essentially the same as im_preedit_trailing, except
-		// composing characters are not counted even if p_deco is set.
-		++num_move_back;
-	    }
-	    if (preedit_start_col != MAXCOL)
-		preedit_end_col += utf_ptr2cells(p);
-	}
-
-	if (p > str)
-	{
-	    im_add_to_input(str, (int)(p - str));
-	    im_correct_cursor(num_move_back);
-	}
-    }
-
-    g_free(preedit_string);
-
-    if (gtk_main_level() > 0)
-	gtk_main_quit();
-}
-
-/*
- * Translate the Pango attributes at iter to Vim highlighting attributes.
- * Ignore attributes not supported by Vim highlighting.  This shouldn't have
- * too much impact -- right now we handle even more attributes than necessary
- * for the IM modules I tested with.
- */
-    static int
-translate_pango_attributes(PangoAttrIterator *iter)
-{
-    PangoAttribute  *attr;
-    int		    char_attr = HL_NORMAL;
-
-    attr = pango_attr_iterator_get(iter, PANGO_ATTR_UNDERLINE);
-    if (attr != NULL && ((PangoAttrInt *)attr)->value
-						 != (int)PANGO_UNDERLINE_NONE)
-	char_attr |= HL_UNDERLINE;
-
-    attr = pango_attr_iterator_get(iter, PANGO_ATTR_WEIGHT);
-    if (attr != NULL && ((PangoAttrInt *)attr)->value >= (int)PANGO_WEIGHT_BOLD)
-	char_attr |= HL_BOLD;
-
-    attr = pango_attr_iterator_get(iter, PANGO_ATTR_STYLE);
-    if (attr != NULL && ((PangoAttrInt *)attr)->value
-						   != (int)PANGO_STYLE_NORMAL)
-	char_attr |= HL_ITALIC;
-
-    attr = pango_attr_iterator_get(iter, PANGO_ATTR_BACKGROUND);
-    if (attr != NULL)
-    {
-	const PangoColor *color = &((PangoAttrColor *)attr)->color;
-
-	// Assume inverse if black background is requested
-	if ((color->red | color->green | color->blue) == 0)
-	    char_attr |= HL_INVERSE;
-    }
-
-    return char_attr;
-}
-
-/*
- * Retrieve the highlighting attributes at column col in the preedit string.
- * Return -1 if not in preediting mode or if col is out of range.
- */
-    int
-im_get_feedback_attr(int col)
-{
-    char	    *preedit_string = NULL;
-    PangoAttrList   *attr_list	    = NULL;
-    int		    char_attr	    = -1;
-
-    if (xic == NULL)
-	return char_attr;
-
-    gtk_im_context_get_preedit_string(xic, &preedit_string, &attr_list, NULL);
-
-    if (preedit_string != NULL && attr_list != NULL)
-    {
-	int idx;
-
-	// Get the byte index as used by PangoAttrIterator
-	for (idx = 0; col > 0 && preedit_string[idx] != '\0'; --col)
-	    idx += utfc_ptr2len((char_u *)preedit_string + idx);
-
-	if (preedit_string[idx] != '\0')
-	{
-	    PangoAttrIterator	*iter;
-	    int			start, end;
-
-	    char_attr = HL_NORMAL;
-	    iter = pango_attr_list_get_iterator(attr_list);
-
-	    // Extract all relevant attributes from the list.
-	    do
-	    {
-		pango_attr_iterator_range(iter, &start, &end);
-
-		if (idx >= start && idx < end)
-		    char_attr |= translate_pango_attributes(iter);
-	    }
-	    while (pango_attr_iterator_next(iter));
-
-	    pango_attr_iterator_destroy(iter);
-	}
-    }
-
-    if (attr_list != NULL)
-	pango_attr_list_unref(attr_list);
-    g_free(preedit_string);
-
-    return char_attr;
-}
-
-    void
-xim_init(void)
-{
-#ifdef XIM_DEBUG
-    xim_log("xim_init()\n");
-#endif
-
-    g_return_if_fail(gui.drawarea != NULL);
-    g_return_if_fail(gtk_widget_get_window(gui.drawarea) != NULL);
-
-    xic = gtk_im_multicontext_new();
-    g_object_ref(xic);
-
-    im_commit_handler_id = g_signal_connect(G_OBJECT(xic), "commit",
-					    G_CALLBACK(&im_commit_cb), NULL);
-    g_signal_connect(G_OBJECT(xic), "preedit_changed",
-		     G_CALLBACK(&im_preedit_changed_cb), NULL);
-    g_signal_connect(G_OBJECT(xic), "preedit_start",
-		     G_CALLBACK(&im_preedit_start_cb), NULL);
-    g_signal_connect(G_OBJECT(xic), "preedit_end",
-		     G_CALLBACK(&im_preedit_end_cb), NULL);
-
-    gtk_im_context_set_client_window(xic, gtk_widget_get_window(gui.drawarea));
-}
-
-    void
-im_shutdown(void)
-{
-#ifdef XIM_DEBUG
-    xim_log("im_shutdown()\n");
-#endif
-
-    if (xic != NULL)
-    {
-	gtk_im_context_focus_out(xic);
-	g_object_unref(xic);
-	xic = NULL;
-    }
-    im_is_active = FALSE;
-    im_commit_handler_id = 0;
-    if (p_imst == IM_ON_THE_SPOT)
-	preedit_start_col = MAXCOL;
-    xim_has_preediting = FALSE;
-}
-
-/*
- * Convert the string argument to keyval and state for GdkEventKey.
- * If str is valid return TRUE, otherwise FALSE.
- *
- * See 'imactivatekey' for documentation of the format.
- */
-    static int
-im_string_to_keyval(const char *str, unsigned int *keyval, unsigned int *state)
-{
-    const char	    *mods_end;
-    unsigned	    tmp_keyval;
-    unsigned	    tmp_state = 0;
-
-    mods_end = strrchr(str, '-');
-    mods_end = (mods_end != NULL) ? mods_end + 1 : str;
-
-    // Parse modifier keys
-    while (str < mods_end)
-	switch (*str++)
-	{
-	    case '-':							break;
-	    case 'S': case 's': tmp_state |= (unsigned)GDK_SHIFT_MASK;	break;
-	    case 'L': case 'l': tmp_state |= (unsigned)GDK_LOCK_MASK;	break;
-	    case 'C': case 'c': tmp_state |= (unsigned)GDK_CONTROL_MASK;break;
-	    case '1':		tmp_state |= (unsigned)GDK_MOD1_MASK;	break;
-	    case '2':		tmp_state |= (unsigned)GDK_MOD2_MASK;	break;
-	    case '3':		tmp_state |= (unsigned)GDK_MOD3_MASK;	break;
-	    case '4':		tmp_state |= (unsigned)GDK_MOD4_MASK;	break;
-	    case '5':		tmp_state |= (unsigned)GDK_MOD5_MASK;	break;
-	    default:
-		return FALSE;
-	}
-
-    tmp_keyval = gdk_keyval_from_name(str);
-
-    if (tmp_keyval == 0 || tmp_keyval == GDK_VoidSymbol)
-	return FALSE;
-
-    if (keyval != NULL)
-	*keyval = tmp_keyval;
-    if (state != NULL)
-	*state = tmp_state;
-
-    return TRUE;
-}
-
-/*
- * Return TRUE if p_imak is valid, otherwise FALSE.  As a special case, an
- * empty string is also regarded as valid.
- *
- * Note: The numerical key value of p_imak is cached if it was valid; thus
- * boldly assuming im_xim_isvalid_imactivate() will always be called whenever
- * 'imak' changes.  This is currently the case but not obvious -- should
- * probably rename the function for clarity.
- */
-    int
-im_xim_isvalid_imactivate(void)
-{
-    if (p_imak[0] == NUL)
-    {
-	im_activatekey_keyval = GDK_VoidSymbol;
-	im_activatekey_state  = 0;
-	return TRUE;
-    }
-
-    return im_string_to_keyval((const char *)p_imak,
-			       &im_activatekey_keyval,
-			       &im_activatekey_state);
-}
-
-    static void
-im_synthesize_keypress(unsigned int keyval, unsigned int state)
-{
-    GdkEventKey *event;
-
-    event = (GdkEventKey *)gdk_event_new(GDK_KEY_PRESS);
-    g_object_ref(gtk_widget_get_window(gui.drawarea));
-					// unreffed by gdk_event_free()
-    event->window = gtk_widget_get_window(gui.drawarea);
-    event->send_event = TRUE;
-    event->time = GDK_CURRENT_TIME;
-    event->state  = state;
-    event->keyval = keyval;
-    event->hardware_keycode = // needed for XIM
-	XKeysymToKeycode(GDK_WINDOW_XDISPLAY(event->window), (KeySym)keyval);
-    event->length = 0;
-    event->string = NULL;
-
-    gtk_im_context_filter_keypress(xic, event);
-
-    // For consistency, also send the corresponding release event.
-    event->type = GDK_KEY_RELEASE;
-    event->send_event = FALSE;
-    gtk_im_context_filter_keypress(xic, event);
-
-    gdk_event_free((GdkEvent *)event);
-}
-
-    void
-xim_reset(void)
-{
-# ifdef FEAT_EVAL
-    if (USE_IMACTIVATEFUNC)
-	call_imactivatefunc(im_is_active);
-    else
-# endif
-    if (xic != NULL)
-    {
-	gtk_im_context_reset(xic);
-
-	if (p_imdisable)
-	    im_shutdown();
-	else
-	{
-	    xim_set_focus(gui.in_focus);
-
-	    if (im_activatekey_keyval != GDK_VoidSymbol)
-	    {
-		if (im_is_active)
-		{
-		    g_signal_handler_block(xic, im_commit_handler_id);
-		    im_synthesize_keypress(im_activatekey_keyval,
-						    im_activatekey_state);
-		    g_signal_handler_unblock(xic, im_commit_handler_id);
-		}
-	    }
-	    else
-	    {
-		im_shutdown();
-		xim_init();
-		xim_set_focus(gui.in_focus);
-	    }
-	}
-    }
-
-    if (p_imst == IM_ON_THE_SPOT)
-	preedit_start_col = MAXCOL;
-    xim_has_preediting = FALSE;
-}
-
-    int
-xim_queue_key_press_event(GdkEventKey *event, int down)
-{
-    if (down)
-    {
-	/*
-	 * Workaround GTK2 XIM 'feature' that always converts keypad keys to
-	 * chars., even when not part of an IM sequence (ref. feature of
-	 * gdk/gdkkeyuni.c).
-	 * Flag any keypad keys that might represent a single char.
-	 * If this (on its own - i.e., not part of an IM sequence) is
-	 * committed while we're processing one of these keys, we can ignore
-	 * that commit and go ahead & process it ourselves.  That way we can
-	 * still distinguish keypad keys for use in mappings.
-	 * Also add GDK_space to make <S-Space> work.
-	 */
-	switch (event->keyval)
-	{
-	    case GDK_KP_Add:      xim_expected_char = '+';  break;
-	    case GDK_KP_Subtract: xim_expected_char = '-';  break;
-	    case GDK_KP_Divide:   xim_expected_char = '/';  break;
-	    case GDK_KP_Multiply: xim_expected_char = '*';  break;
-	    case GDK_KP_Decimal:  xim_expected_char = '.';  break;
-	    case GDK_KP_Equal:    xim_expected_char = '=';  break;
-	    case GDK_KP_0:	  xim_expected_char = '0';  break;
-	    case GDK_KP_1:	  xim_expected_char = '1';  break;
-	    case GDK_KP_2:	  xim_expected_char = '2';  break;
-	    case GDK_KP_3:	  xim_expected_char = '3';  break;
-	    case GDK_KP_4:	  xim_expected_char = '4';  break;
-	    case GDK_KP_5:	  xim_expected_char = '5';  break;
-	    case GDK_KP_6:	  xim_expected_char = '6';  break;
-	    case GDK_KP_7:	  xim_expected_char = '7';  break;
-	    case GDK_KP_8:	  xim_expected_char = '8';  break;
-	    case GDK_KP_9:	  xim_expected_char = '9';  break;
-	    case GDK_space:	  xim_expected_char = ' ';  break;
-	    default:		  xim_expected_char = NUL;
-	}
-	xim_ignored_char = FALSE;
-    }
-
-    /*
-     * When typing fFtT, XIM may be activated. Thus it must pass
-     * gtk_im_context_filter_keypress() in Normal mode.
-     * And while doing :sh too.
-     */
-    if (xic != NULL && !p_imdisable
-		    && (State & (INSERT | CMDLINE | NORMAL | EXTERNCMD)) != 0)
-    {
-	/*
-	 * Filter 'imactivatekey' and map it to CTRL-^.  This way, Vim is
-	 * always aware of the current status of IM, and can even emulate
-	 * the activation key for modules that don't support one.
-	 */
-	if (event->keyval == im_activatekey_keyval
-	     && (event->state & im_activatekey_state) == im_activatekey_state)
-	{
-	    unsigned int state_mask;
-
-	    // Require the state of the 3 most used modifiers to match exactly.
-	    // Otherwise e.g. <S-C-space> would be unusable for other purposes
-	    // if the IM activate key is <S-space>.
-	    state_mask  = im_activatekey_state;
-	    state_mask |= ((int)GDK_SHIFT_MASK | (int)GDK_CONTROL_MASK
-							| (int)GDK_MOD1_MASK);
-
-	    if ((event->state & state_mask) != im_activatekey_state)
-		return FALSE;
-
-	    // Don't send it a second time on GDK_KEY_RELEASE.
-	    if (event->type != GDK_KEY_PRESS)
-		return TRUE;
-
-	    if (map_to_exists_mode((char_u *)"", LANGMAP, FALSE))
-	    {
-		im_set_active(FALSE);
-
-		// ":lmap" mappings exists, toggle use of mappings.
-		State ^= LANGMAP;
-		if (State & LANGMAP)
-		{
-		    curbuf->b_p_iminsert = B_IMODE_NONE;
-		    State &= ~LANGMAP;
-		}
-		else
-		{
-		    curbuf->b_p_iminsert = B_IMODE_LMAP;
-		    State |= LANGMAP;
-		}
-		return TRUE;
-	    }
-
-	    return gtk_im_context_filter_keypress(xic, event);
-	}
-
-	// Don't filter events through the IM context if IM isn't active
-	// right now.  Unlike with GTK+ 1.2 we cannot rely on the IM module
-	// not doing anything before the activation key was sent.
-	if (im_activatekey_keyval == GDK_VoidSymbol || im_is_active)
-	{
-	    int imresult = gtk_im_context_filter_keypress(xic, event);
-
-	    if (p_imst == IM_ON_THE_SPOT)
-	    {
-		// Some XIM send following sequence:
-		// 1. preedited string.
-		// 2. committed string.
-		// 3. line changed key.
-		// 4. preedited string.
-		// 5. remove preedited string.
-		// if 3, Vim can't move back the above line for 5.
-		// thus, this part should not parse the key.
-		if (!imresult && preedit_start_col != MAXCOL
-					    && event->keyval == GDK_Return)
-		{
-		    im_synthesize_keypress(GDK_Return, 0U);
-		    return FALSE;
-		}
-	    }
-
-	    // If XIM tried to commit a keypad key as a single char.,
-	    // ignore it so we can use the keypad key 'raw', for mappings.
-	    if (xim_expected_char != NUL && xim_ignored_char)
-		// We had a keypad key, and XIM tried to thieve it
-		return FALSE;
-
-	    // This is supposed to fix a problem with iBus, that space
-	    // characters don't work in input mode.
-	    xim_expected_char = NUL;
-
-	    // Normal processing
-	    return imresult;
-	}
-    }
-
-    return FALSE;
-}
-
-    int
-im_get_status(void)
-{
-#  ifdef FEAT_EVAL
-    if (USE_IMSTATUSFUNC)
-	return call_imstatusfunc();
-#  endif
-    return im_is_active;
-}
-
-    int
-preedit_get_status(void)
-{
-    return preedit_is_active;
-}
-
-    int
-im_is_preediting(void)
-{
-    return xim_has_preediting;
-}
-
-# else // !FEAT_GUI_GTK
-
-static int	xim_is_active = FALSE;  // XIM should be active in the current
-					// mode
-static int	xim_has_focus = FALSE;	// XIM is really being used for Vim
-#  ifdef FEAT_GUI_X11
-static XIMStyle	input_style;
-static int	status_area_enabled = TRUE;
-#  endif
-
-/*
- * Switch using XIM on/off.  This is used by the code that changes "State".
- * When 'imactivatefunc' is defined use that function instead.
- */
-    void
-im_set_active(int active_arg)
-{
-    int active = active_arg;
-
-    // If 'imdisable' is set, XIM is never active.
-    if (p_imdisable)
-	active = FALSE;
-    else if (input_style & XIMPreeditPosition)
-	// There is a problem in switching XIM off when preediting is used,
-	// and it is not clear how this can be solved.  For now, keep XIM on
-	// all the time, like it was done in Vim 5.8.
-	active = TRUE;
-
-#  if defined(FEAT_EVAL)
-    if (USE_IMACTIVATEFUNC)
-    {
-	if (active != im_get_status())
-	{
-	    call_imactivatefunc(active);
-	    xim_has_focus = active;
-	}
-	return;
-    }
-#  endif
-
-    if (xic == NULL)
-	return;
-
-    // Remember the active state, it is needed when Vim gets keyboard focus.
-    xim_is_active = active;
-    xim_set_preedit();
-}
-
-/*
- * Adjust using XIM for gaining or losing keyboard focus.  Also called when
- * "xim_is_active" changes.
- */
-    void
-xim_set_focus(int focus)
-{
-    if (xic == NULL)
-	return;
-
-    /*
-     * XIM only gets focus when the Vim window has keyboard focus and XIM has
-     * been set active for the current mode.
-     */
-    if (focus && xim_is_active)
-    {
-	if (!xim_has_focus)
-	{
-	    xim_has_focus = TRUE;
-	    XSetICFocus(xic);
-	}
-    }
-    else
-    {
-	if (xim_has_focus)
-	{
-	    xim_has_focus = FALSE;
-	    XUnsetICFocus(xic);
-	}
-    }
-}
-
-    void
-im_set_position(int row UNUSED, int col UNUSED)
-{
-    xim_set_preedit();
-}
-
-/*
- * Set the XIM to the current cursor position.
- */
-    void
-xim_set_preedit(void)
-{
-    XVaNestedList attr_list;
-    XRectangle spot_area;
-    XPoint over_spot;
-    int line_space;
-
-    if (xic == NULL)
-	return;
-
-    xim_set_focus(TRUE);
-
-    if (!xim_has_focus)
-    {
-	// hide XIM cursor
-	over_spot.x = 0;
-	over_spot.y = -100; // arbitrary invisible position
-	attr_list = (XVaNestedList) XVaCreateNestedList(0,
-							XNSpotLocation,
-							&over_spot,
-							NULL);
-	XSetICValues(xic, XNPreeditAttributes, attr_list, NULL);
-	XFree(attr_list);
-	return;
-    }
-
-    if (input_style & XIMPreeditPosition)
-    {
-	if (xim_fg_color == INVALCOLOR)
-	{
-	    xim_fg_color = gui.def_norm_pixel;
-	    xim_bg_color = gui.def_back_pixel;
-	}
-	over_spot.x = TEXT_X(gui.col);
-	over_spot.y = TEXT_Y(gui.row);
-	spot_area.x = 0;
-	spot_area.y = 0;
-	spot_area.height = gui.char_height * Rows;
-	spot_area.width  = gui.char_width * Columns;
-	line_space = gui.char_height;
-	attr_list = (XVaNestedList) XVaCreateNestedList(0,
-					XNSpotLocation, &over_spot,
-					XNForeground, (Pixel) xim_fg_color,
-					XNBackground, (Pixel) xim_bg_color,
-					XNArea, &spot_area,
-					XNLineSpace, line_space,
-					NULL);
-	if (XSetICValues(xic, XNPreeditAttributes, attr_list, NULL))
-	    emsg(_("E284: Cannot set IC values"));
-	XFree(attr_list);
-    }
-}
-
-#  if defined(FEAT_GUI_X11)
-static char e_xim[] = N_("E285: Failed to create input context");
-#  endif
-
-#  if defined(FEAT_GUI_X11) || defined(PROTO)
-#   if defined(XtSpecificationRelease) && XtSpecificationRelease >= 6 && !defined(SUN_SYSTEM)
-#    define USE_X11R6_XIM
-#   endif
-
-static int xim_real_init(Window x11_window, Display *x11_display);
-
-
-#  ifdef USE_X11R6_XIM
-    static void
-xim_instantiate_cb(
-    Display	*display,
-    XPointer	client_data UNUSED,
-    XPointer	call_data UNUSED)
-{
-    Window	x11_window;
-    Display	*x11_display;
-
-#   ifdef XIM_DEBUG
-    xim_log("xim_instantiate_cb()\n");
-#   endif
-
-    gui_get_x11_windis(&x11_window, &x11_display);
-    if (display != x11_display)
-	return;
-
-    xim_real_init(x11_window, x11_display);
-    gui_set_shellsize(FALSE, FALSE, RESIZE_BOTH);
-    if (xic != NULL)
-	XUnregisterIMInstantiateCallback(x11_display, NULL, NULL, NULL,
-					 xim_instantiate_cb, NULL);
-}
-
-    static void
-xim_destroy_cb(
-    XIM		im UNUSED,
-    XPointer	client_data UNUSED,
-    XPointer	call_data UNUSED)
-{
-    Window	x11_window;
-    Display	*x11_display;
-
-#   ifdef XIM_DEBUG
-    xim_log("xim_destroy_cb()\n");
-   #endif
-    gui_get_x11_windis(&x11_window, &x11_display);
-
-    xic = NULL;
-    status_area_enabled = FALSE;
-
-    gui_set_shellsize(FALSE, FALSE, RESIZE_BOTH);
-
-    XRegisterIMInstantiateCallback(x11_display, NULL, NULL, NULL,
-				   xim_instantiate_cb, NULL);
-}
-#  endif
-
-    void
-xim_init(void)
-{
-    Window	x11_window;
-    Display	*x11_display;
-
-#  ifdef XIM_DEBUG
-    xim_log("xim_init()\n");
-#  endif
-
-    gui_get_x11_windis(&x11_window, &x11_display);
-
-    xic = NULL;
-
-    if (xim_real_init(x11_window, x11_display))
-	return;
-
-    gui_set_shellsize(FALSE, FALSE, RESIZE_BOTH);
-
-#  ifdef USE_X11R6_XIM
-    XRegisterIMInstantiateCallback(x11_display, NULL, NULL, NULL,
-				   xim_instantiate_cb, NULL);
-#  endif
-}
-
-    static int
-xim_real_init(Window x11_window, Display *x11_display)
-{
-    int		i;
-    char	*p,
-		*s,
-		*ns,
-		*end,
-		tmp[1024];
-#  define IMLEN_MAX 40
-    char	buf[IMLEN_MAX + 7];
-    XIM		xim = NULL;
-    XIMStyles	*xim_styles;
-    XIMStyle	this_input_style = 0;
-    Boolean	found;
-    XPoint	over_spot;
-    XVaNestedList preedit_list, status_list;
-
-    input_style = 0;
-    status_area_enabled = FALSE;
-
-    if (xic != NULL)
-	return FALSE;
-
-    if (gui.rsrc_input_method != NULL && *gui.rsrc_input_method != NUL)
-    {
-	strcpy(tmp, gui.rsrc_input_method);
-	for (ns = s = tmp; ns != NULL && *s != NUL;)
-	{
-	    s = (char *)skipwhite((char_u *)s);
-	    if (*s == NUL)
-		break;
-	    if ((ns = end = strchr(s, ',')) == NULL)
-		end = s + strlen(s);
-	    while (isspace(((char_u *)end)[-1]))
-		end--;
-	    *end = NUL;
-
-	    if (strlen(s) <= IMLEN_MAX)
-	    {
-		strcpy(buf, "@im=");
-		strcat(buf, s);
-		if ((p = XSetLocaleModifiers(buf)) != NULL && *p != NUL
-			&& (xim = XOpenIM(x11_display, NULL, NULL, NULL))
-								      != NULL)
-		    break;
-	    }
-
-	    s = ns + 1;
-	}
-    }
-
-    if (xim == NULL && (p = XSetLocaleModifiers("")) != NULL && *p != NUL)
-	xim = XOpenIM(x11_display, NULL, NULL, NULL);
-
-    // This is supposed to be useful to obtain characters through
-    // XmbLookupString() without really using a XIM.
-    if (xim == NULL && (p = XSetLocaleModifiers("@im=none")) != NULL
-								 && *p != NUL)
-	xim = XOpenIM(x11_display, NULL, NULL, NULL);
-
-    if (xim == NULL)
-    {
-	// Only give this message when verbose is set, because too many people
-	// got this message when they didn't want to use a XIM.
-	if (p_verbose > 0)
-	{
-	    verbose_enter();
-	    emsg(_("E286: Failed to open input method"));
-	    verbose_leave();
-	}
-	return FALSE;
-    }
-
-#  ifdef USE_X11R6_XIM
-    {
-	XIMCallback destroy_cb;
-
-	destroy_cb.callback = xim_destroy_cb;
-	destroy_cb.client_data = NULL;
-	if (XSetIMValues(xim, XNDestroyCallback, &destroy_cb, NULL))
-	    emsg(_("E287: Warning: Could not set destroy callback to IM"));
-    }
-#  endif
-
-    if (XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL) || !xim_styles)
-    {
-	emsg(_("E288: input method doesn't support any style"));
-	XCloseIM(xim);
-	return FALSE;
-    }
-
-    found = False;
-    strcpy(tmp, gui.rsrc_preedit_type_name);
-    for (s = tmp; s && !found; )
-    {
-	while (*s && isspace((unsigned char)*s))
-	    s++;
-	if (!*s)
-	    break;
-	if ((ns = end = strchr(s, ',')) != 0)
-	    ns++;
-	else
-	    end = s + strlen(s);
-	while (isspace((unsigned char)*end))
-	    end--;
-	*end = '\0';
-
-	if (!strcmp(s, "OverTheSpot"))
-	    this_input_style = (XIMPreeditPosition | XIMStatusArea);
-	else if (!strcmp(s, "OffTheSpot"))
-	    this_input_style = (XIMPreeditArea | XIMStatusArea);
-	else if (!strcmp(s, "Root"))
-	    this_input_style = (XIMPreeditNothing | XIMStatusNothing);
-
-	for (i = 0; (unsigned short)i < xim_styles->count_styles; i++)
-	{
-	    if (this_input_style == xim_styles->supported_styles[i])
-	    {
-		found = True;
-		break;
-	    }
-	}
-	if (!found)
-	    for (i = 0; (unsigned short)i < xim_styles->count_styles; i++)
-	    {
-		if ((xim_styles->supported_styles[i] & this_input_style)
-			== (this_input_style & ~XIMStatusArea))
-		{
-		    this_input_style &= ~XIMStatusArea;
-		    found = True;
-		    break;
-		}
-	    }
-
-	s = ns;
-    }
-    XFree(xim_styles);
-
-    if (!found)
-    {
-	// Only give this message when verbose is set, because too many people
-	// got this message when they didn't want to use a XIM.
-	if (p_verbose > 0)
-	{
-	    verbose_enter();
-	    emsg(_("E289: input method doesn't support my preedit type"));
-	    verbose_leave();
-	}
-	XCloseIM(xim);
-	return FALSE;
-    }
-
-    over_spot.x = TEXT_X(gui.col);
-    over_spot.y = TEXT_Y(gui.row);
-    input_style = this_input_style;
-
-    // A crash was reported when trying to pass gui.norm_font as XNFontSet,
-    // thus that has been removed.  Hopefully the default works...
-#  ifdef FEAT_XFONTSET
-    if (gui.fontset != NOFONTSET)
-    {
-	preedit_list = XVaCreateNestedList(0,
-				XNSpotLocation, &over_spot,
-				XNForeground, (Pixel)gui.def_norm_pixel,
-				XNBackground, (Pixel)gui.def_back_pixel,
-				XNFontSet, (XFontSet)gui.fontset,
-				NULL);
-	status_list = XVaCreateNestedList(0,
-				XNForeground, (Pixel)gui.def_norm_pixel,
-				XNBackground, (Pixel)gui.def_back_pixel,
-				XNFontSet, (XFontSet)gui.fontset,
-				NULL);
-    }
-    else
-#  endif
-    {
-	preedit_list = XVaCreateNestedList(0,
-				XNSpotLocation, &over_spot,
-				XNForeground, (Pixel)gui.def_norm_pixel,
-				XNBackground, (Pixel)gui.def_back_pixel,
-				NULL);
-	status_list = XVaCreateNestedList(0,
-				XNForeground, (Pixel)gui.def_norm_pixel,
-				XNBackground, (Pixel)gui.def_back_pixel,
-				NULL);
-    }
-
-    xic = XCreateIC(xim,
-		    XNInputStyle, input_style,
-		    XNClientWindow, x11_window,
-		    XNFocusWindow, gui.wid,
-		    XNPreeditAttributes, preedit_list,
-		    XNStatusAttributes, status_list,
-		    NULL);
-    XFree(status_list);
-    XFree(preedit_list);
-    if (xic != NULL)
-    {
-	if (input_style & XIMStatusArea)
-	{
-	    xim_set_status_area();
-	    status_area_enabled = TRUE;
-	}
-	else
-	    gui_set_shellsize(FALSE, FALSE, RESIZE_BOTH);
-    }
-    else
-    {
-	if (!is_not_a_term())
-	    emsg(_(e_xim));
-	XCloseIM(xim);
-	return FALSE;
-    }
-
-    return TRUE;
-}
-
-#  endif // FEAT_GUI_X11
-
-/*
- * Get IM status.  When IM is on, return TRUE.  Else return FALSE.
- * FIXME: This doesn't work correctly: Having focus doesn't always mean XIM is
- * active, when not having focus XIM may still be active (e.g., when using a
- * tear-off menu item).
- */
-    int
-im_get_status(void)
-{
-#  ifdef FEAT_EVAL
-    if (USE_IMSTATUSFUNC)
-	return call_imstatusfunc();
-#  endif
-    return xim_has_focus;
-}
-
-# endif // !FEAT_GUI_GTK
-
-# if !defined(FEAT_GUI_GTK) || defined(PROTO)
-/*
- * Set up the status area.
- *
- * This should use a separate Widget, but that seems not possible, because
- * preedit_area and status_area should be set to the same window as for the
- * text input.  Unfortunately this means the status area pollutes the text
- * window...
- */
-    void
-xim_set_status_area(void)
-{
-    XVaNestedList preedit_list = 0, status_list = 0, list = 0;
-    XRectangle pre_area, status_area;
-
-    if (xic == NULL)
-	return;
-
-    if (input_style & XIMStatusArea)
-    {
-	if (input_style & XIMPreeditArea)
-	{
-	    XRectangle *needed_rect;
-
-	    // to get status_area width
-	    status_list = XVaCreateNestedList(0, XNAreaNeeded,
-					      &needed_rect, NULL);
-	    XGetICValues(xic, XNStatusAttributes, status_list, NULL);
-	    XFree(status_list);
-
-	    status_area.width = needed_rect->width;
-	}
-	else
-	    status_area.width = gui.char_width * Columns;
-
-	status_area.x = 0;
-	status_area.y = gui.char_height * Rows + gui.border_offset;
-	if (gui.which_scrollbars[SBAR_BOTTOM])
-	    status_area.y += gui.scrollbar_height;
-#ifdef FEAT_MENU
-	if (gui.menu_is_active)
-	    status_area.y += gui.menu_height;
-#endif
-	status_area.height = gui.char_height;
-	status_list = XVaCreateNestedList(0, XNArea, &status_area, NULL);
-    }
-    else
-    {
-	status_area.x = 0;
-	status_area.y = gui.char_height * Rows + gui.border_offset;
-	if (gui.which_scrollbars[SBAR_BOTTOM])
-	    status_area.y += gui.scrollbar_height;
-#ifdef FEAT_MENU
-	if (gui.menu_is_active)
-	    status_area.y += gui.menu_height;
-#endif
-	status_area.width = 0;
-	status_area.height = gui.char_height;
-    }
-
-    if (input_style & XIMPreeditArea)   // off-the-spot
-    {
-	pre_area.x = status_area.x + status_area.width;
-	pre_area.y = gui.char_height * Rows + gui.border_offset;
-	pre_area.width = gui.char_width * Columns - pre_area.x;
-	if (gui.which_scrollbars[SBAR_BOTTOM])
-	    pre_area.y += gui.scrollbar_height;
-#ifdef FEAT_MENU
-	if (gui.menu_is_active)
-	    pre_area.y += gui.menu_height;
-#endif
-	pre_area.height = gui.char_height;
-	preedit_list = XVaCreateNestedList(0, XNArea, &pre_area, NULL);
-    }
-    else if (input_style & XIMPreeditPosition)   // over-the-spot
-    {
-	pre_area.x = 0;
-	pre_area.y = 0;
-	pre_area.height = gui.char_height * Rows;
-	pre_area.width = gui.char_width * Columns;
-	preedit_list = XVaCreateNestedList(0, XNArea, &pre_area, NULL);
-    }
-
-    if (preedit_list && status_list)
-	list = XVaCreateNestedList(0, XNPreeditAttributes, preedit_list,
-				   XNStatusAttributes, status_list, NULL);
-    else if (preedit_list)
-	list = XVaCreateNestedList(0, XNPreeditAttributes, preedit_list,
-				   NULL);
-    else if (status_list)
-	list = XVaCreateNestedList(0, XNStatusAttributes, status_list,
-				   NULL);
-    else
-	list = NULL;
-
-    if (list)
-    {
-	XSetICValues(xic, XNVaNestedList, list, NULL);
-	XFree(list);
-    }
-    if (status_list)
-	XFree(status_list);
-    if (preedit_list)
-	XFree(preedit_list);
-}
-
-    int
-xim_get_status_area_height(void)
-{
-    if (status_area_enabled)
-	return gui.char_height;
-    return 0;
-}
-# endif
-
-#else // !defined(FEAT_XIM)
-
-# if defined(IME_WITHOUT_XIM) || defined(VIMDLL)
-static int im_was_set_active = FALSE;
-
-    int
-#  ifdef VIMDLL
-mbyte_im_get_status(void)
-#  else
-im_get_status(void)
-#  endif
-{
-#  if defined(FEAT_EVAL)
-    if (USE_IMSTATUSFUNC)
-	return call_imstatusfunc();
-#  endif
-    return im_was_set_active;
-}
-
-    void
-#  ifdef VIMDLL
-mbyte_im_set_active(int active_arg)
-#  else
-im_set_active(int active_arg)
-#  endif
-{
-#  if defined(FEAT_EVAL)
-    int	    active = !p_imdisable && active_arg;
-
-    if (USE_IMACTIVATEFUNC && active != im_get_status())
-    {
-	call_imactivatefunc(active);
-	im_was_set_active = active;
-    }
-#  endif
-}
-
-#  if defined(FEAT_GUI) && !defined(FEAT_GUI_HAIKU) && !defined(VIMDLL)
-    void
-im_set_position(int row UNUSED, int col UNUSED)
-{
-}
-#  endif
-# endif
-
-#endif // FEAT_XIM
-
 #if defined(FEAT_EVAL) || defined(PROTO)
 /*
  * "getimstatus()" function
--- a/src/proto.h
+++ b/src/proto.h
@@ -92,6 +92,7 @@ extern int _stricoll(char *a, char *b);
 # include "findfile.pro"
 # include "fold.pro"
 # include "getchar.pro"
+# include "gui_xim.pro"
 # include "hardcopy.pro"
 # include "hashtab.pro"
 # include "highlight.pro"
new file mode 100644
--- /dev/null
+++ b/src/proto/gui_xim.pro
@@ -0,0 +1,17 @@
+/* gui_xim.c */
+void im_set_active(int active);
+void xim_set_focus(int focus);
+void im_set_position(int row, int col);
+void xim_set_preedit(void);
+int im_get_feedback_attr(int col);
+void xim_init(void);
+void im_shutdown(void);
+int im_xim_isvalid_imactivate(void);
+void xim_reset(void);
+int xim_queue_key_press_event(GdkEventKey *event, int down);
+int im_get_status(void);
+int preedit_get_status(void);
+int im_is_preediting(void);
+void xim_set_status_area(void);
+int xim_get_status_area_height(void);
+/* vim: set ft=c : */
--- a/src/proto/mbyte.pro
+++ b/src/proto/mbyte.pro
@@ -74,21 +74,6 @@ int encname2codepage(char_u *name);
 void *my_iconv_open(char_u *to, char_u *from);
 int iconv_enabled(int verbose);
 void iconv_end(void);
-void im_set_active(int active);
-void xim_set_focus(int focus);
-void im_set_position(int row, int col);
-void xim_set_preedit(void);
-int im_get_feedback_attr(int col);
-void xim_init(void);
-void im_shutdown(void);
-int im_xim_isvalid_imactivate(void);
-void xim_reset(void);
-int xim_queue_key_press_event(GdkEventKey *event, int down);
-int im_get_status(void);
-int preedit_get_status(void);
-int im_is_preediting(void);
-void xim_set_status_area(void);
-int xim_get_status_area_height(void);
 void f_getimstatus(typval_T *argvars, typval_T *rettv);
 int convert_setup(vimconv_T *vcp, char_u *from, char_u *to);
 int convert_setup_ext(vimconv_T *vcp, char_u *from, int from_unicode_is_utf8, char_u *to, int to_unicode_is_utf8);
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    872,
+/**/
     871,
 /**/
     870,