To: vim_dev@googlegroups.com Subject: Patch 8.1.2134 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.2134 Problem: Modifier keys are not always recognized. Solution: Handle key codes generated by xterm with modifyOtherKeys set. Add this to libvterm so we can debug it. Files: src/term.c, src/getchar.c, src/libvterm/src/vterm_internal.h, src/libvterm/src/state.c, src/libvterm/src/keyboard.c, src/libvterm/include/vterm.h, src/globals.h, src/terminal.c *** ../vim-8.1.2133/src/term.c 2019-10-08 20:15:36.430654834 +0200 --- src/term.c 2019-10-10 21:12:13.294797665 +0200 *************** *** 4199,4204 **** --- 4199,4297 ---- #endif /* + * Put "string[new_slen]" in typebuf, or in "buf[bufsize]" if "buf" is not NULL. + * Remove "slen" bytes. + * Returns FAIL for error. + */ + static int + put_string_in_typebuf( + int offset, + int slen, + char_u *string, + int new_slen, + char_u *buf, + int bufsize, + int *buflen) + { + int extra = new_slen - slen; + + string[new_slen] = NUL; + if (buf == NULL) + { + if (extra < 0) + // remove matched chars, taking care of noremap + del_typebuf(-extra, offset); + else if (extra > 0) + // insert the extra space we need + ins_typebuf(string + slen, REMAP_YES, offset, FALSE, FALSE); + + // Careful: del_typebuf() and ins_typebuf() may have reallocated + // typebuf.tb_buf[]! + mch_memmove(typebuf.tb_buf + typebuf.tb_off + offset, string, + (size_t)new_slen); + } + else + { + if (extra < 0) + // remove matched characters + mch_memmove(buf + offset, buf + offset - extra, + (size_t)(*buflen + offset + extra)); + else if (extra > 0) + { + // Insert the extra space we need. If there is insufficient + // space return -1. + if (*buflen + extra + new_slen >= bufsize) + return FAIL; + mch_memmove(buf + offset + extra, buf + offset, + (size_t)(*buflen - offset)); + } + mch_memmove(buf + offset, string, (size_t)new_slen); + *buflen = *buflen + extra + new_slen; + } + return OK; + } + + /* + * Decode a modifier number as xterm provides it into MOD_MASK bits. + */ + static int + decode_modifiers(int n) + { + int code = n - 1; + int modifiers = 0; + + if (code & 1) + modifiers |= MOD_MASK_SHIFT; + if (code & 2) + modifiers |= MOD_MASK_ALT; + if (code & 4) + modifiers |= MOD_MASK_CTRL; + if (code & 8) + modifiers |= MOD_MASK_META; + return modifiers; + } + + static int + modifiers2keycode(int modifiers, int *key, char_u *string) + { + int new_slen = 0; + + if (modifiers != 0) + { + // Some keys have the modifier included. Need to handle that here to + // make mappings work. + *key = simplify_key(*key, &modifiers); + if (modifiers != 0) + { + string[new_slen++] = K_SPECIAL; + string[new_slen++] = (int)KS_MODIFIER; + string[new_slen++] = modifiers; + } + } + return new_slen; + } + + /* * Check if typebuf.tb_buf[] contains a terminal key code. * Check from typebuf.tb_buf[typebuf.tb_off] to typebuf.tb_buf[typebuf.tb_off * + max_offset]. *************** *** 4229,4236 **** int modifiers; char_u *modifiers_start = NULL; int key; ! int new_slen; ! int extra; char_u string[MAX_KEY_CODE_LEN + 1]; int i, j; int idx = 0; --- 4322,4328 ---- int modifiers; char_u *modifiers_start = NULL; int key; ! int new_slen; // Length of what will replace the termcode char_u string[MAX_KEY_CODE_LEN + 1]; int i, j; int idx = 0; *************** *** 4401,4416 **** modifiers_start = tp + slen - 2; ! /* Match! Convert modifier bits. */ ! n = atoi((char *)modifiers_start) - 1; ! if (n & 1) ! modifiers |= MOD_MASK_SHIFT; ! if (n & 2) ! modifiers |= MOD_MASK_ALT; ! if (n & 4) ! modifiers |= MOD_MASK_CTRL; ! if (n & 8) ! modifiers |= MOD_MASK_META; slen = j; } --- 4493,4501 ---- modifiers_start = tp + slen - 2; ! // Match! Convert modifier bits. ! n = atoi((char *)modifiers_start); ! modifiers |= decode_modifiers(n); slen = j; } *************** *** 4751,4759 **** winpos_status.tr_progress = STATUS_GOT; } ! // TODO: key with modifier: // {lead}27;{modifier};{key}~ // {lead}{key};{modifier}u // else: Unknown CSI sequence. We could drop it, but then the // user can't create a map for it. --- 4836,4867 ---- winpos_status.tr_progress = STATUS_GOT; } ! // Key with modifier: // {lead}27;{modifier};{key}~ // {lead}{key};{modifier}u + else if ((arg[0] == 27 && argc == 3 && trail == '~') + || (argc == 2 && trail == 'u')) + { + if (trail == 'u') + key = arg[0]; + else + key = arg[2]; + + // insert modifiers with KS_MODIFIER + modifiers = decode_modifiers(arg[1]); + new_slen = modifiers2keycode(modifiers, &key, string); + slen = csi_len; + + if (has_mbyte) + new_slen += (*mb_char2bytes)(key, string + new_slen); + else + string[new_slen++] = key; + + if (put_string_in_typebuf(offset, slen, string, new_slen, + buf, bufsize, buflen) == FAIL) + return -1; + return len + new_slen - slen + offset; + } // else: Unknown CSI sequence. We could drop it, but then the // user can't create a map for it. *************** *** 5138,5156 **** /* * Add any modifier codes to our string. */ ! new_slen = 0; /* Length of what will replace the termcode */ ! if (modifiers != 0) ! { ! /* Some keys have the modifier included. Need to handle that here ! * to make mappings work. */ ! key = simplify_key(key, &modifiers); ! if (modifiers != 0) ! { ! string[new_slen++] = K_SPECIAL; ! string[new_slen++] = (int)KS_MODIFIER; ! string[new_slen++] = modifiers; ! } ! } /* Finally, add the special key code to our string */ key_name[0] = KEY2TERMCAP0(key); --- 5246,5252 ---- /* * Add any modifier codes to our string. */ ! new_slen = modifiers2keycode(modifiers, &key, string); /* Finally, add the special key code to our string */ key_name[0] = KEY2TERMCAP0(key); *************** *** 5176,5218 **** string[new_slen++] = key_name[0]; string[new_slen++] = key_name[1]; } ! string[new_slen] = NUL; ! extra = new_slen - slen; ! if (buf == NULL) ! { ! if (extra < 0) ! /* remove matched chars, taking care of noremap */ ! del_typebuf(-extra, offset); ! else if (extra > 0) ! /* insert the extra space we need */ ! ins_typebuf(string + slen, REMAP_YES, offset, FALSE, FALSE); ! ! /* ! * Careful: del_typebuf() and ins_typebuf() may have reallocated ! * typebuf.tb_buf[]! ! */ ! mch_memmove(typebuf.tb_buf + typebuf.tb_off + offset, string, ! (size_t)new_slen); ! } ! else ! { ! if (extra < 0) ! /* remove matched characters */ ! mch_memmove(buf + offset, buf + offset - extra, ! (size_t)(*buflen + offset + extra)); ! else if (extra > 0) ! { ! /* Insert the extra space we need. If there is insufficient ! * space return -1. */ ! if (*buflen + extra + new_slen >= bufsize) ! return -1; ! mch_memmove(buf + offset + extra, buf + offset, ! (size_t)(*buflen - offset)); ! } ! mch_memmove(buf + offset, string, (size_t)new_slen); ! *buflen = *buflen + extra + new_slen; ! } ! return retval == 0 ? (len + extra + offset) : retval; } #ifdef FEAT_TERMRESPONSE --- 5272,5281 ---- string[new_slen++] = key_name[0]; string[new_slen++] = key_name[1]; } ! if (put_string_in_typebuf(offset, slen, string, new_slen, ! buf, bufsize, buflen) == FAIL) ! return -1; ! return retval == 0 ? (len + new_slen - slen + offset) : retval; } #ifdef FEAT_TERMRESPONSE *** ../vim-8.1.2133/src/getchar.c 2019-10-06 22:00:08.297244105 +0200 --- src/getchar.c 2019-10-10 21:02:42.889159485 +0200 *************** *** 1733,1738 **** --- 1733,1757 ---- case K_XRIGHT: c = K_RIGHT; break; } + if (!no_reduce_keys) + { + // A modifier was not used for a mapping, apply it to ASCII + // keys. + if ((mod_mask & MOD_MASK_CTRL) + && ((c >= '`' && c <= 0x7f) + || (c >= '@' && c <= '_'))) + { + c &= 0x1f; + mod_mask &= ~MOD_MASK_CTRL; + } + if ((mod_mask & (MOD_MASK_META | MOD_MASK_ALT)) + && c >= 0 && c <= 127) + { + c += 0x80; + mod_mask &= ~MOD_MASK_META; + } + } + // For a multi-byte character get all the bytes and return the // converted character. // Note: This will loop until enough bytes are received! *** ../vim-8.1.2133/src/libvterm/src/vterm_internal.h 2019-09-13 22:30:07.588524105 +0200 --- src/libvterm/src/vterm_internal.h 2019-10-10 19:44:39.936448251 +0200 *************** *** 124,129 **** --- 124,130 ---- unsigned int leftrightmargin:1; unsigned int bracketpaste:1; unsigned int report_focus:1; + unsigned int modify_other_keys:1; } mode; VTermEncodingInstance encoding[4], encoding_utf8; *** ../vim-8.1.2133/src/libvterm/src/state.c 2019-09-13 22:30:07.588524105 +0200 --- src/libvterm/src/state.c 2019-10-10 20:13:41.773784902 +0200 *************** *** 1334,1339 **** --- 1334,1344 ---- vterm_state_setpen(state, args, argcount); break; + case LEADER('>', 0x6d): // xterm resource modifyOtherKeys + if (argcount == 2 && args[0] == 4) + state->mode.modify_other_keys = args[1] == 2; + break; + case 0x6e: // DSR - ECMA-48 8.3.35 case LEADER('?', 0x6e): // DECDSR val = CSI_ARG_OR(args[0], 0); *** ../vim-8.1.2133/src/libvterm/src/keyboard.c 2019-08-18 20:41:10.692526067 +0200 --- src/libvterm/src/keyboard.c 2019-10-10 20:30:04.177590235 +0200 *************** *** 4,13 **** --- 4,24 ---- #include "utf8.h" + int vterm_is_modify_other_keys(VTerm *vt) + { + return vt->state->mode.modify_other_keys; + } + + void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod) { int needs_CSIu; + if (vt->state->mode.modify_other_keys && mod != 0) { + vterm_push_output_sprintf_ctrl(vt, C1_CSI, "27;%d;%d~", mod+1, c); + return; + } + // The shift modifier is never important for Unicode characters // apart from Space if(c != ' ') *** ../vim-8.1.2133/src/libvterm/include/vterm.h 2019-08-18 20:41:10.692526067 +0200 --- src/libvterm/include/vterm.h 2019-10-10 20:13:01.325977444 +0200 *************** *** 200,205 **** --- 200,206 ---- size_t vterm_output_read(VTerm *vt, char *buffer, size_t len); + int vterm_is_modify_other_keys(VTerm *vt); void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod); void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod); *** ../vim-8.1.2133/src/globals.h 2019-10-09 22:52:48.996043767 +0200 --- src/globals.h 2019-10-10 20:02:19.212608485 +0200 *************** *** 1006,1011 **** --- 1006,1013 ---- EXTERN int no_zero_mapping INIT(= 0); // mapping zero not allowed EXTERN int allow_keys INIT(= FALSE); // allow key codes when no_mapping // is set + EXTERN int no_reduce_keys INIT(= FALSE); // do not apply Ctrl, Shift and Alt + // to the key EXTERN int no_u_sync INIT(= 0); // Don't call u_sync() #ifdef FEAT_EVAL EXTERN int u_sync_once INIT(= 0); // Call u_sync() once when evaluating *** ../vim-8.1.2133/src/terminal.c 2019-10-06 22:00:08.301244080 +0200 --- src/terminal.c 2019-10-10 21:02:03.465324996 +0200 *************** *** 1371,1381 **** break; } /* * Convert special keys to vterm keys: * - Write keys to vterm: vterm_keyboard_key() * - Write output to channel. - * TODO: use mod_mask */ if (key != VTERM_KEY_NONE) /* Special key, let vterm convert it. */ --- 1371,1383 ---- break; } + // add modifiers for the typed key + mod |= mod_mask; + /* * Convert special keys to vterm keys: * - Write keys to vterm: vterm_keyboard_key() * - Write output to channel. */ if (key != VTERM_KEY_NONE) /* Special key, let vterm convert it. */ *************** *** 1902,1916 **** --- 1904,1924 ---- { int c; int save_State = State; + int modify_other_keys = + vterm_is_modify_other_keys(curbuf->b_term->tl_vterm); State = TERMINAL; got_int = FALSE; #ifdef MSWIN ctrl_break_was_pressed = FALSE; #endif + if (modify_other_keys) + ++no_reduce_keys; c = vgetc(); got_int = FALSE; State = save_State; + if (modify_other_keys) + --no_reduce_keys; return c; } *************** *** 2255,2260 **** --- 2263,2269 ---- terminal_loop(int blocking) { int c; + int raw_c; int termwinkey = 0; int ret; #ifdef UNIX *************** *** 2307,2312 **** --- 2316,2328 ---- if (c == K_IGNORE) continue; + // vgetc may not include CTRL in the key when modify_other_keys is set. + raw_c = c; + if ((mod_mask & MOD_MASK_CTRL) + && ((c >= '`' && c <= 0x7f) + || (c >= '@' && c <= '_'))) + c &= 0x1f; + #ifdef UNIX /* * The shell or another program may change the tty settings. Getting *************** *** 2417,2423 **** c = wc; } # endif ! if (send_keys_to_term(curbuf->b_term, c, TRUE) != OK) { if (c == K_MOUSEMOVE) /* We are sure to come back here, don't reset the cursor color --- 2433,2439 ---- c = wc; } # endif ! if (send_keys_to_term(curbuf->b_term, raw_c, TRUE) != OK) { if (c == K_MOUSEMOVE) /* We are sure to come back here, don't reset the cursor color *** ../vim-8.1.2133/src/version.c 2019-10-10 16:46:13.590506986 +0200 --- src/version.c 2019-10-10 20:14:40.789508641 +0200 *************** *** 755,756 **** --- 755,758 ---- { /* Add new patch number below this line */ + /**/ + 2134, /**/ -- To the optimist, the glass is half full. To the pessimist, the glass is half empty. To the engineer, the glass is twice as big as it needs to be. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///