changeset 31293:ff4473b3fc58 v9.0.0980

patch 9.0.0980: the keyboard state response may end up in a shell command Commit: https://github.com/vim/vim/commit/733a69b29f0b0c3d2ddca463a41bdd912379bc5e Author: Bram Moolenaar <Bram@vim.org> Date: Thu Dec 1 12:03:47 2022 +0000 patch 9.0.0980: the keyboard state response may end up in a shell command Problem: The keyboard state response may end up in a shell command. Solution: Only request the keyboard protocol state when the typeahead is empty, no more commands are following and not exiting. Add the t_RK termcap entry for this.
author Bram Moolenaar <Bram@vim.org>
date Thu, 01 Dec 2022 13:15:03 +0100
parents f319f6200220
children d0acff2279a3
files runtime/doc/map.txt runtime/doc/term.txt src/edit.c src/ex_getln.c src/main.c src/normal.c src/os_unix.c src/proto/main.pro src/proto/term.pro src/term.c src/termdefs.h src/version.c
diffstat 12 files changed, 111 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/map.txt
+++ b/runtime/doc/map.txt
@@ -1001,10 +1001,15 @@ mapping, see |map-bar|.
 WARNING: if you map <C-[> you may very well break any key codes that start
 with Esc.  Make sure it comes AFTER other mappings.
 
-Vim automatically detects if the modifyOtherKeys mode was enabled when it
-spots an escape sequence that must have been created by it.  To see if Vim
-detected such an escape sequence use `:verbose map`, the first line will then
-show "Seen modifyOtherKeys: true" (possibly translated).
+Starting with xterm version 377 Vim can detect the modifyOtherKeys state by
+requesting it.  For this the 't_RK' termcap entry is used.  When the response
+is found then Vim will know whether modifyOtherKeys level 2 is enabled, and
+handle mappings accordingly.
+
+Before version 377 Vim automatically detects if the modifyOtherKeys mode was
+enabled when it spots an escape sequence that must have been created by it.
+To see if Vim detected such an escape sequence use `:verbose map`, the first
+line will then show "Seen modifyOtherKeys: true" (possibly translated).
 
 This automatic detection depends on receiving an escape code starting with 
 "<1b>[27;".  This is the normal way xterm sends these key codes.  However, if
@@ -1016,6 +1021,9 @@ after the CTRL-V key.  This can be used 
 enabled: In Insert mode type CTRL-SHIFT-V CTRL-V, if you get one byte then
 modifyOtherKeys is off, if you get <1b>[27;5;118~ then it is on.
 
+Note that xterm up to version 376 has a bug that makes Shift-Esc send a
+regular Esc code, the Shift modifier is dropped.
+
 When the 'esckeys' option is off, then modifyOtherKeys will be disabled in
 Insert mode to avoid every key with a modifier causing Insert mode to end.
 
--- a/runtime/doc/term.txt
+++ b/runtime/doc/term.txt
@@ -90,6 +90,11 @@ Note: When 't_ti' is not empty, Vim assu
 alternate screen.  This may slightly change what happens when executing a
 shell command or exiting Vim.  To avoid this use 't_TI' and 't_TE'.
 
+Vim will try to detect what keyboard protocol the terminal is using with the
+'t_RK' termcap entry.  This is sent after 't_TI', but only when there is no
+work to do (no typeahead and no pending commands).  That is to avoid the
+response to end up in a shell command or arrive after Vim exits.
+
 						*xterm-bracketed-paste*
 When the 't_BE' option is set then 't_BE' will be sent to the
 terminal when entering "raw" mode and 't_BD' when leaving "raw" mode.  The
@@ -388,6 +393,8 @@ Added by Vim (there are no standard code
 		xterm and other terminal emulators)  The
 		response is stored in |v:termresponse| |xterm-8bit|
 		|'ttymouse'| |xterm-codes|
+	t_RK	request terminal keyboard protocol state;	*t_RK* *'t_RK'*
+		sent after |t_TI|
 	t_u7	request cursor position (for xterm)		*t_u7* *'t_u7'*
 		see |'ambiwidth'|
 		The response is stored in |v:termu7resp|
--- a/src/edit.c
+++ b/src/edit.c
@@ -571,6 +571,8 @@ edit(
 #ifdef USE_ON_FLY_SCROLL
 	dont_scroll = FALSE;		// allow scrolling here
 #endif
+	// May request the keyboard protocol state now.
+	may_send_t_RK();
 
 	/*
 	 * Get a character for Insert mode.  Ignore K_IGNORE and K_NOP.
@@ -1479,7 +1481,8 @@ ins_redraw(int ready)	    // not busy wi
 	aco_save_T	aco;
 	varnumber_T	tick = CHANGEDTICK(curbuf);
 
-	// save and restore curwin and curbuf, in case the autocmd changes them
+	// Save and restore curwin and curbuf, in case the autocmd changes
+	// them.
 	aucmd_prepbuf(&aco, curbuf);
 	apply_autocmds(EVENT_TEXTCHANGEDI, NULL, NULL, FALSE, curbuf);
 	aucmd_restbuf(&aco);
@@ -1499,7 +1502,8 @@ ins_redraw(int ready)	    // not busy wi
 	aco_save_T	aco;
 	varnumber_T	tick = CHANGEDTICK(curbuf);
 
-	// save and restore curwin and curbuf, in case the autocmd changes them
+	// Save and restore curwin and curbuf, in case the autocmd changes
+	// them.
 	aucmd_prepbuf(&aco, curbuf);
 	apply_autocmds(EVENT_TEXTCHANGEDP, NULL, NULL, FALSE, curbuf);
 	aucmd_restbuf(&aco);
@@ -3706,7 +3710,7 @@ ins_esc(
 	out_str(T_BE);
 
 	// Re-enable modifyOtherKeys.
-	out_str(T_CTI);
+	out_str_t_TI();
     }
 #ifdef FEAT_CONCEAL
     // Check if the cursor line needs redrawing after changing State.  If
@@ -4384,6 +4388,7 @@ bracketed_paste(paste_mode_T mode, int d
 	do
 	    c = vgetc();
 	while (c == K_IGNORE || c == K_VER_SCROLLBAR || c == K_HOR_SCROLLBAR);
+
 	if (c == NUL || got_int || (ex_normal_busy > 0 && c == Ctrl_C))
 	    // When CTRL-C was encountered the typeahead will be flushed and we
 	    // won't get the end sequence.  Except when using ":normal".
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -2908,6 +2908,9 @@ getexmodeline(
 	long    sw;
 	char_u *s;
 
+	// May request the keyboard protocol state now.
+	may_send_t_RK();
+
 	if (ga_grow(&line_ga, 40) == FAIL)
 	    break;
 
--- a/src/main.c
+++ b/src/main.c
@@ -1133,14 +1133,14 @@ may_trigger_safestateagain(void)
 	// of calling feedkeys(), we check if it's now safe again (all keys
 	// were consumed).
 	was_safe = is_safe_now();
-#ifdef FEAT_EVAL
+# ifdef FEAT_EVAL
 	if (was_safe)
 	    ch_log(NULL, "SafeState: undo reset");
-#endif
+# endif
     }
     if (was_safe)
     {
-#ifdef FEAT_EVAL
+# ifdef FEAT_EVAL
 	// Only do this message when another message was given, otherwise we
 	// get lots of them.
 	if ((did_repeated_msg & REPEATED_MSG_SAFESTATE) == 0)
@@ -1151,17 +1151,26 @@ may_trigger_safestateagain(void)
 		      "SafeState: back to waiting, triggering SafeStateAgain");
 	    did_repeated_msg = did | REPEATED_MSG_SAFESTATE;
 	}
-#endif
+# endif
 	apply_autocmds(EVENT_SAFESTATEAGAIN, NULL, NULL, FALSE, curbuf);
     }
-#ifdef FEAT_EVAL
+# ifdef FEAT_EVAL
     else
 	ch_log(NULL,
 		  "SafeState: back to waiting, not triggering SafeStateAgain");
-#endif
+# endif
 }
 #endif
 
+/*
+ * Return TRUE if there is any typeahead, pending operator or command.
+ */
+    int
+work_pending(void)
+{
+    return op_pending() || !is_safe_now();
+}
+
 
 /*
  * Main loop: Execute Normal mode commands until exiting Vim.
@@ -1477,10 +1486,11 @@ main_loop(
 	    gui_mouse_correct();
 #endif
 
-	/*
-	 * Update w_curswant if w_set_curswant has been set.
-	 * Postponed until here to avoid computing w_virtcol too often.
-	 */
+	// May request the keyboard protocol state now.
+	may_send_t_RK();
+
+	// Update w_curswant if w_set_curswant has been set.
+	// Postponed until here to avoid computing w_virtcol too often.
 	update_curswant();
 
 #ifdef FEAT_EVAL
--- a/src/normal.c
+++ b/src/normal.c
@@ -455,7 +455,7 @@ normal_cmd_get_more_chars(
 
 	    // Re-enable bracketed paste mode and modifyOtherKeys
 	    out_str(T_BE);
-	    out_str(T_CTI);
+	    out_str_t_TI();
 	}
 
 	if (langmap_active)
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -5379,7 +5379,7 @@ finished:
 
 		if (tmode == TMODE_RAW)
 		    // possibly enables modifyOtherKeys again
-		    out_str(T_CTI);
+		    out_str_t_TI();
 	    }
 # endif
 
--- a/src/proto/main.pro
+++ b/src/proto/main.pro
@@ -9,6 +9,7 @@ void may_trigger_safestate(int safe);
 void state_no_longer_safe(char *reason);
 int get_was_safe_state(void);
 void may_trigger_safestateagain(void);
+int work_pending(void);
 void main_loop(int cmdwin, int noexmode);
 void getout_preserve_modified(int exitval);
 void getout(int exitval);
--- a/src/proto/term.pro
+++ b/src/proto/term.pro
@@ -48,6 +48,8 @@ void shell_resized(void);
 void shell_resized_check(void);
 void set_shellsize(int width, int height, int mustset);
 void out_str_t_TE(void);
+void out_str_t_TI(void);
+void may_send_t_RK(void);
 void settmode(tmode_T tmode);
 void starttermcap(void);
 void stoptermcap(void);
--- a/src/term.c
+++ b/src/term.c
@@ -452,7 +452,8 @@ static tcap_entry_T builtin_xterm[] = {
     {(int)KS_TI,	"\0337\033[?47h"},
     {(int)KS_TE,	"\033[?47l\0338"},
 #  endif
-    {(int)KS_CTI,	"\033[>4;2m\033[?4m"},  // see "builtin_mok2"
+    {(int)KS_CTI,	"\033[>4;2m"},
+    {(int)KS_CRK,	"\033[?4m"},  // see "builtin_mok2"
     {(int)KS_CTE,	"\033[>4;m"},
     {(int)KS_CIS,	"\033]1;"},
     {(int)KS_CIE,	"\007"},
@@ -593,10 +594,15 @@ static tcap_entry_T builtin_xterm[] = {
  * xterm.
  */
 static tcap_entry_T builtin_mok2[] = {
+    // t_TI enables modifyOtherKeys level 2
+    {(int)KS_CTI,	"\033[>4;2m"},
+
     // XTQMODKEYS was added in xterm version 377: "CSI ? 4 m" which should
     // return "{lead} > 4 ; Pv m".  Before version 377 we expect it to have no
     // effect.
-    {(int)KS_CTI,	"\033[>4;2m\033[?4m"},
+    {(int)KS_CRK,	"\033[?4m"},
+
+    // t_TE disables modifyOtherKeys
     {(int)KS_CTE,	"\033[>4;m"},
 
     {(int)KS_NAME,	NULL}  // end marker
@@ -606,11 +612,13 @@ static tcap_entry_T builtin_mok2[] = {
  * Additions for using the Kitty keyboard protocol.
  */
 static tcap_entry_T builtin_kitty[] = {
-    // t_TI enables the kitty keyboard protocol, requests the kitty keyboard
-    // protocol state and requests the version response.
-    {(int)KS_CTI,	"\033[=1;1u\033[?u\033[>c"},
-
-    // t_TE also disabled modifyOtherKeys, because t_TI from xterm may already
+    // t_TI enables the kitty keyboard protocol.
+    {(int)KS_CTI,	"\033[=1;1u"},
+
+    // t_RK requests the kitty keyboard protocol state
+    {(int)KS_CRK,	"\033[?u"},
+
+    // t_TE also disables modifyOtherKeys, because t_TI from xterm may already
     // have been used.
     {(int)KS_CTE,	"\033[>4;m\033[=0;1u"},
 
@@ -1685,7 +1693,7 @@ get_term_entries(int *height, int *width
 			{KS_CM, "cm"}, {KS_SR, "sr"},
 			{KS_CRI,"RI"}, {KS_VB, "vb"}, {KS_KS, "ks"},
 			{KS_KE, "ke"}, {KS_TI, "ti"}, {KS_TE, "te"},
-			{KS_CTI, "TI"}, {KS_CTE, "TE"},
+			{KS_CTI, "TI"}, {KS_CRK, "RK"}, {KS_CTE, "TE"},
 			{KS_BC, "bc"}, {KS_CSB,"Sb"}, {KS_CSF,"Sf"},
 			{KS_CAB,"AB"}, {KS_CAF,"AF"}, {KS_CAU,"AU"},
 			{KS_LE, "le"},
@@ -3693,6 +3701,40 @@ out_str_t_TE(void)
 	kitty_protocol_state = KKPS_AFTER_T_KE;
 }
 
+static int send_t_RK = FALSE;
+
+/*
+ * Output T_TI and setup for what follows.
+ */
+    void
+out_str_t_TI(void)
+{
+    out_str(T_CTI);
+
+    // Send t_RK when there is no more work to do.
+    send_t_RK = TRUE;
+}
+
+/*
+ * If t_TI was recently sent and there is no typeahead or work to do, now send
+ * t_RK.  This is postponed to avoid the response arriving in a shell command
+ * or after Vim exits.
+ */
+    void
+may_send_t_RK(void)
+{
+    if (send_t_RK
+	    && !work_pending()
+	    && !ex_normal_busy
+	    && !in_feedkeys
+	    && !exiting)
+    {
+	send_t_RK = FALSE;
+	out_str(T_CRK);
+	out_flush();
+    }
+}
+
 /*
  * Set the terminal to TMODE_RAW (for Normal mode) or TMODE_COOK (for external
  * commands and Ex mode).
@@ -3751,7 +3793,7 @@ settmode(tmode_T tmode)
 		{
 		    out_str(T_BE);	// enable bracketed paste mode (should
 					// be before mch_settmode().
-		    out_str(T_CTI);	// possibly enables modifyOtherKeys
+		    out_str_t_TI();	// possibly enables modifyOtherKeys
 		}
 	    }
 	    out_flush();
@@ -3775,7 +3817,7 @@ starttermcap(void)
 	MAY_WANT_TO_LOG_THIS;
 
 	out_str(T_TI);			// start termcap mode
-	out_str(T_CTI);			// start "raw" mode
+	out_str_t_TI();			// start "raw" mode
 	out_str(T_KS);			// start "keypad transmit" mode
 	out_str(T_BE);			// enable bracketed paste mode
 
--- a/src/termdefs.h
+++ b/src/termdefs.h
@@ -69,6 +69,7 @@ enum SpecialKey
     KS_KE,	// out of "keypad transmit" mode
     KS_TI,	// put terminal in termcap mode
     KS_CTI,	// put terminal in "raw" mode
+    KS_CRK,	// request keyboard protocol state
     KS_TE,	// end of termcap mode
     KS_CTE,	// end of "raw" mode
     KS_BC,	// backspace character (cursor left)
@@ -177,6 +178,7 @@ extern char_u *(term_strings[]);    // c
 #define T_KE	(TERM_STR(KS_KE))	// out of "keypad transmit" mode
 #define T_TI	(TERM_STR(KS_TI))	// put terminal in termcap mode
 #define T_CTI	(TERM_STR(KS_CTI))	// put terminal in "raw" mode
+#define T_CRK	(TERM_STR(KS_CRK))	// request keyboard protocol status
 #define T_TE	(TERM_STR(KS_TE))	// end of termcap mode
 #define T_CTE	(TERM_STR(KS_CTE))	// end of "raw" mode
 #define T_BC	(TERM_STR(KS_BC))	// backspace character
--- a/src/version.c
+++ b/src/version.c
@@ -696,6 +696,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    980,
+/**/
     979,
 /**/
     978,