patch 8.0.1336: cannot use imactivatefunc() unless compiled with +xim

Problem:    Cannot use imactivatefunc() unless compiled with +xim.
Solution:   Allow using imactivatefunc() when not compiled with +xim.
            (Yasuhiro Matsumoto, closes #2349)
diff --git a/runtime/doc/mbyte.txt b/runtime/doc/mbyte.txt
index 1c3e2b1..8a25d83 100644
--- a/runtime/doc/mbyte.txt
+++ b/runtime/doc/mbyte.txt
@@ -26,8 +26,9 @@
 7.  Input on X11			|mbyte-XIM|
 8.  Input on MS-Windows			|mbyte-IME|
 9.  Input with a keymap			|mbyte-keymap|
-10. Using UTF-8				|mbyte-utf8|
-11. Overview of options			|mbyte-options|
+10. Input with imactivatefunc()		|mbyte-func|
+11. Using UTF-8				|mbyte-utf8|
+12. Overview of options			|mbyte-options|
 
 NOTE: This file contains UTF-8 characters.  These may show up as strange
 characters or boxes when using another encoding.
@@ -1254,7 +1255,35 @@
 ﭏ    0xfb4f  Xal  alef-lamed
 
 ==============================================================================
-10. Using UTF-8				*mbyte-utf8* *UTF-8* *utf-8* *utf8*
+10.  Input with imactivatefunc()				*mbyte-func*
+
+Vim has |imactivatefunc()| and |imstatusfunc()|. This is useful to
+activate/deativate input method from Vim in any way, also with an external
+command. For example, fcitx provide fcitx-remote command: >
+
+	set iminsert=2
+	set imsearch=2
+	set imcmdline
+
+	set imactivatefunc=ImActivate
+	function! ImActivate(active)
+	  if a:active
+	    call system('fcitx-remote -o')
+	  else
+	    call system('fcitx-remote -c')
+	  endif
+	endfunction
+
+	set imstatusfunc=ImStatus
+	function! ImStatus()
+	  return system('fcitx-remote')[0] is# '2'
+	endfunction
+
+Using this script, you can activate/deactivate XIM via Vim even when it is not
+compiled with |+xim|.
+
+==============================================================================
+11. Using UTF-8				*mbyte-utf8* *UTF-8* *utf-8* *utf8*
 							*Unicode* *unicode*
 The Unicode character set was designed to include all characters from other
 character sets.  Therefore it is possible to write text in any language using
@@ -1402,7 +1431,7 @@
 
 
 ==============================================================================
-11. Overview of options					*mbyte-options*
+12. Overview of options					*mbyte-options*
 
 These options are relevant for editing multi-byte files.  Check the help in
 options.txt for detailed information.
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index f9acff8..d71e7f9 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -4256,8 +4256,7 @@
 'imactivatefunc' 'imaf'	string (default "")
 			global
 			{not in Vi}
-			{only available when compiled with |+xim| and
-			|+GUI_GTK|}
+			{only available when compiled with |+mbyte|}
 	This option specifies a function that will be called to
 	activate/inactivate Input Method.
 
@@ -4308,8 +4307,7 @@
 'imcmdline' 'imc'	boolean (default off)
 			global
 			{not in Vi}
-			{only available when compiled with the |+xim|,
-			|+multi_byte_ime| or |global-ime| features}
+			{only available when compiled with |+mbyte|}
 	When set the Input Method is always on when starting to edit a command
 	line, unless entering a search pattern (see 'imsearch' for that).
 	Setting this option is useful when your input method allows entering
@@ -4320,8 +4318,7 @@
 'imdisable' 'imd'	boolean (default off, on for some systems (SGI))
 			global
 			{not in Vi}
-			{only available when compiled with the |+xim|,
-			|+multi_byte_ime| or |global-ime| features}
+			{only available when compiled with |+mbyte|}
 	When set the Input Method is never used.  This is useful to disable
 	the IM when it doesn't work properly.
 	Currently this option is on by default for SGI/IRIX machines.  This
@@ -4336,8 +4333,6 @@
 		0	:lmap is off and IM is off
 		1	:lmap is ON and IM is off
 		2	:lmap is off and IM is ON
-	2 is available only when compiled with the |+multi_byte_ime|, |+xim|
-	or |global-ime|.
 	To always reset the option to zero when leaving Insert mode with <Esc>
 	this can be used: >
 		:inoremap <ESC> <ESC>:set iminsert=0<CR>
@@ -4350,6 +4345,10 @@
 	The value 0 may not work correctly with Athena and Motif with some XIM
 	methods.  Use 'imdisable' to disable XIM then.
 
+	You can set 'imactivatefunc' and 'imstatusfunc' to handle IME/XIM
+	via external command if vim is not compiled with the |+xim|,
+	|+multi_byte_ime| or |global-ime|.
+
 						*'imsearch'* *'ims'*
 'imsearch' 'ims'	number (default -1)
 			local to buffer
@@ -4372,8 +4371,7 @@
 'imstatusfunc' 'imsf'	string (default "")
 			global
 			{not in Vi}
-			{only available when compiled with |+xim| and
-			|+GUI_GTK|}
+			{only available when compiled with |+mbyte|}
 	This option specifies a function that is called to obtain the status
 	of Input Method.  It must return a positive number when IME is active.
 
diff --git a/src/Makefile b/src/Makefile
index 09cf21e..7ccd766 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -2190,6 +2190,7 @@
 	test_highlight \
 	test_history \
 	test_hlsearch \
+	test_iminsert \
 	test_increment \
 	test_increment_dbcs \
 	test_ins_complete \
diff --git a/src/mbyte.c b/src/mbyte.c
index 3592ddb..54186d9c 100644
--- a/src/mbyte.c
+++ b/src/mbyte.c
@@ -4782,6 +4782,20 @@
 
 #endif /* FEAT_MBYTE */
 
+#ifdef FEAT_EVAL
+    static void
+call_imactivatefunc(int active)
+{
+    char_u *argv[1];
+
+    if (active)
+	argv[0] = (char_u *)"1";
+    else
+	argv[0] = (char_u *)"0";
+    (void)call_func_retnr(p_imaf, 1, argv, FALSE);
+}
+#endif
+
 #if defined(FEAT_XIM) || defined(PROTO)
 
 # if defined(FEAT_GUI_GTK) || defined(PROTO)
@@ -4824,7 +4838,14 @@
     im_is_active = (active && !p_imdisable);
 
     if (im_is_active != was_active)
-	xim_reset();
+    {
+#ifdef FEAT_EVAL
+	if (p_imaf[0] != NUL)
+	    call_imactivatefunc(im_is_active);
+	else
+#endif
+	    xim_reset();
+    }
 }
 
     void
@@ -5666,15 +5687,7 @@
 
 #  ifdef FEAT_EVAL
 	    if (p_imaf[0] != NUL)
-	    {
-		char_u *argv[1];
-
-		if (im_is_active)
-		    argv[0] = (char_u *)"1";
-		else
-		    argv[0] = (char_u *)"0";
-		(void)call_func_retnr(p_imaf, 1, argv, FALSE);
-	    }
+		call_imactivatefunc(im_is_active);
 	    else
 #  endif
 		if (im_activatekey_keyval != GDK_VoidSymbol)
@@ -6442,6 +6455,45 @@
 }
 # endif
 
+#else /* !defined(FEAT_XIM) */
+
+# ifndef FEAT_GUI_W32
+    int
+im_get_status()
+{
+#  ifdef FEAT_EVAL
+    if (p_imsf[0] != NUL)
+    {
+	int is_active;
+
+	/* FIXME: Don't execute user function in unsafe situation. */
+	if (exiting
+#   ifdef FEAT_AUTOCMD
+		|| is_autocmd_blocked()
+#   endif
+		)
+	    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, FALSE);
+	--msg_silent;
+	return (is_active > 0);
+    }
+#  endif
+    return FALSE;
+}
+
+    void
+im_set_active(int active)
+{
+#  ifdef(USE_IM_CONTROL) && defined(FEAT_EVAL)
+    if (p_imaf[0] != NUL)
+	call_imactivatefunc(p_imdisable ? FALSE : active);
+#  endif
+}
+# endif
+
 #endif /* FEAT_XIM */
 
 #if defined(FEAT_MBYTE) || defined(PROTO)
diff --git a/src/option.c b/src/option.c
index e09191f..ac0918d 100644
--- a/src/option.c
+++ b/src/option.c
@@ -1539,7 +1539,7 @@
 			    (char_u *)&p_ic, PV_NONE,
 			    {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT},
     {"imactivatefunc","imaf",P_STRING|P_VI_DEF|P_SECURE,
-# if defined(FEAT_EVAL) && defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
+#if defined(FEAT_EVAL) && defined(USE_IM_CONTROL)
 			    (char_u *)&p_imaf, PV_NONE,
 			    {(char_u *)"", (char_u *)NULL}
 # else
@@ -1582,7 +1582,7 @@
 			    {(char_u *)B_IMODE_USE_INSERT, (char_u *)0L}
 			    SCRIPTID_INIT},
     {"imstatusfunc","imsf",P_STRING|P_VI_DEF|P_SECURE,
-#if defined(FEAT_EVAL) && defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
+#if defined(FEAT_EVAL) && defined(USE_IM_CONTROL)
 			    (char_u *)&p_imsf, PV_NONE,
 			    {(char_u *)"", (char_u *)NULL}
 #else
diff --git a/src/option.h b/src/option.h
index d963c1f..57126a3 100644
--- a/src/option.h
+++ b/src/option.h
@@ -581,11 +581,13 @@
 EXTERN int	p_ic;		/* 'ignorecase' */
 #if defined(FEAT_XIM) && defined(FEAT_GUI_GTK)
 EXTERN char_u	*p_imak;	/* 'imactivatekey' */
+#define IM_ON_THE_SPOT		0L
+#define IM_OVER_THE_SPOT	1L
+EXTERN long	p_imst;		/* 'imstyle' */
+#endif
+#if defined(FEAT_EVAL) && defined(USE_IM_CONTROL)
 EXTERN char_u	*p_imaf;	/* 'imactivatefunc' */
 EXTERN char_u	*p_imsf;	/* 'imstatusfunc' */
-EXTERN long	p_imst;		/* 'imstyle' */
-# define IM_ON_THE_SPOT		0L
-# define IM_OVER_THE_SPOT	1L
 #endif
 #ifdef USE_IM_CONTROL
 EXTERN int	p_imcmdline;	/* 'imcmdline' */
diff --git a/src/structs.h b/src/structs.h
index 774104c..6aaccc5 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -2091,12 +2091,8 @@
 #define B_IMODE_USE_INSERT -1	/*	Use b_p_iminsert value for search */
 #define B_IMODE_NONE 0		/*	Input via none */
 #define B_IMODE_LMAP 1		/*	Input via langmap */
-#ifndef USE_IM_CONTROL
-# define B_IMODE_LAST 1
-#else
-# define B_IMODE_IM 2		/*	Input via input method */
-# define B_IMODE_LAST 2
-#endif
+#define B_IMODE_IM 2		/*	Input via input method */
+#define B_IMODE_LAST 2
 
 #ifdef FEAT_KEYMAP
     short	b_kmap_state;	/* using "lmap" mappings */
diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak
index d9ff056..df79e9a 100644
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -115,6 +115,7 @@
 	    test_highlight.res \
 	    test_history.res \
 	    test_hlsearch.res \
+	    test_iminsert.res \
 	    test_increment.res \
 	    test_increment_dbcs.res \
 	    test_ins_complete.res \
diff --git a/src/testdir/test_iminsert.vim b/src/testdir/test_iminsert.vim
new file mode 100644
index 0000000..603135a
--- /dev/null
+++ b/src/testdir/test_iminsert.vim
@@ -0,0 +1,29 @@
+if !has('multi_byte')
+  finish
+endif
+
+source view_util.vim
+
+let s:imactivatefunc_called = 0
+let s:imstatusfunc_called = 0
+
+func IM_activatefunc(active)
+  let s:imactivatefunc_called = 1
+endfunc
+
+func IM_statusfunc()
+  let s:imstatusfunc_called = 1
+  return 0
+endfunc
+
+func Test_iminsert2()
+  set imactivatefunc=IM_activatefunc
+  set imstatusfunc=IM_statusfunc
+  set iminsert=2
+  normal! i
+  set iminsert=0
+  set imactivatefunc=
+  set imstatusfunc=
+  call assert_equal(1, s:imactivatefunc_called)
+  call assert_equal(1, s:imstatusfunc_called)
+endfunc
diff --git a/src/version.c b/src/version.c
index e0ba19e..57c4576 100644
--- a/src/version.c
+++ b/src/version.c
@@ -772,6 +772,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1336,
+/**/
     1335,
 /**/
     1334,
diff --git a/src/vim.h b/src/vim.h
index 83ffb29..1d481ca 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -536,9 +536,7 @@
 /*
  * Check input method control.
  */
-#if defined(FEAT_XIM) \
-    || (defined(FEAT_GUI) && (defined(FEAT_MBYTE_IME) || defined(GLOBAL_IME))) \
-    || (defined(FEAT_GUI_MAC) && defined(FEAT_MBYTE))
+#if defined(FEAT_MBYTE)
 # define USE_IM_CONTROL
 #endif