| 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check |
| 2 | // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com |
| 3 | |
| 4 | #include <assert.h> |
| 5 | #include <inttypes.h> |
| 6 | #include <limits.h> |
| 7 | |
| 8 | #include "nvim/vim.h" |
| 9 | #include "nvim/ascii.h" |
| 10 | #include "nvim/keymap.h" |
| 11 | #include "nvim/charset.h" |
| 12 | #include "nvim/memory.h" |
| 13 | #include "nvim/edit.h" |
| 14 | #include "nvim/eval.h" |
| 15 | #include "nvim/message.h" |
| 16 | #include "nvim/strings.h" |
| 17 | #include "nvim/mouse.h" |
| 18 | |
| 19 | #ifdef INCLUDE_GENERATED_DECLARATIONS |
| 20 | # include "keymap.c.generated.h" |
| 21 | #endif |
| 22 | |
| 23 | /* |
| 24 | * Some useful tables. |
| 25 | */ |
| 26 | |
| 27 | static const struct modmasktable { |
| 28 | uint16_t mod_mask; ///< Bit-mask for particular key modifier. |
| 29 | uint16_t mod_flag; ///< Bit(s) for particular key modifier. |
| 30 | char_u name; ///< Single letter name of modifier. |
| 31 | } mod_mask_table[] = { |
| 32 | { MOD_MASK_ALT, MOD_MASK_ALT, (char_u)'M' }, |
| 33 | { MOD_MASK_META, MOD_MASK_META, (char_u)'T' }, |
| 34 | { MOD_MASK_CTRL, MOD_MASK_CTRL, (char_u)'C' }, |
| 35 | { MOD_MASK_SHIFT, MOD_MASK_SHIFT, (char_u)'S' }, |
| 36 | { MOD_MASK_MULTI_CLICK, MOD_MASK_2CLICK, (char_u)'2' }, |
| 37 | { MOD_MASK_MULTI_CLICK, MOD_MASK_3CLICK, (char_u)'3' }, |
| 38 | { MOD_MASK_MULTI_CLICK, MOD_MASK_4CLICK, (char_u)'4' }, |
| 39 | { MOD_MASK_CMD, MOD_MASK_CMD, (char_u)'D' }, |
| 40 | // 'A' must be the last one |
| 41 | { MOD_MASK_ALT, MOD_MASK_ALT, (char_u)'A' }, |
| 42 | { 0, 0, NUL } |
| 43 | // NOTE: when adding an entry, update MAX_KEY_NAME_LEN! |
| 44 | }; |
| 45 | |
| 46 | /* |
| 47 | * Shifted key terminal codes and their unshifted equivalent. |
| 48 | * Don't add mouse codes here, they are handled separately! |
| 49 | */ |
| 50 | #define MOD_KEYS_ENTRY_SIZE 5 |
| 51 | |
| 52 | static char_u modifier_keys_table[] = |
| 53 | { |
| 54 | /* mod mask with modifier without modifier */ |
| 55 | MOD_MASK_SHIFT, '&', '9', '@', '1', /* begin */ |
| 56 | MOD_MASK_SHIFT, '&', '0', '@', '2', /* cancel */ |
| 57 | MOD_MASK_SHIFT, '*', '1', '@', '4', /* command */ |
| 58 | MOD_MASK_SHIFT, '*', '2', '@', '5', /* copy */ |
| 59 | MOD_MASK_SHIFT, '*', '3', '@', '6', /* create */ |
| 60 | MOD_MASK_SHIFT, '*', '4', 'k', 'D', /* delete char */ |
| 61 | MOD_MASK_SHIFT, '*', '5', 'k', 'L', /* delete line */ |
| 62 | MOD_MASK_SHIFT, '*', '7', '@', '7', /* end */ |
| 63 | MOD_MASK_CTRL, KS_EXTRA, (int)KE_C_END, '@', '7', /* end */ |
| 64 | MOD_MASK_SHIFT, '*', '9', '@', '9', /* exit */ |
| 65 | MOD_MASK_SHIFT, '*', '0', '@', '0', /* find */ |
| 66 | MOD_MASK_SHIFT, '#', '1', '%', '1', /* help */ |
| 67 | MOD_MASK_SHIFT, '#', '2', 'k', 'h', /* home */ |
| 68 | MOD_MASK_CTRL, KS_EXTRA, (int)KE_C_HOME, 'k', 'h', /* home */ |
| 69 | MOD_MASK_SHIFT, '#', '3', 'k', 'I', /* insert */ |
| 70 | MOD_MASK_SHIFT, '#', '4', 'k', 'l', /* left arrow */ |
| 71 | MOD_MASK_CTRL, KS_EXTRA, (int)KE_C_LEFT, 'k', 'l', /* left arrow */ |
| 72 | MOD_MASK_SHIFT, '%', 'a', '%', '3', /* message */ |
| 73 | MOD_MASK_SHIFT, '%', 'b', '%', '4', /* move */ |
| 74 | MOD_MASK_SHIFT, '%', 'c', '%', '5', /* next */ |
| 75 | MOD_MASK_SHIFT, '%', 'd', '%', '7', /* options */ |
| 76 | MOD_MASK_SHIFT, '%', 'e', '%', '8', /* previous */ |
| 77 | MOD_MASK_SHIFT, '%', 'f', '%', '9', /* print */ |
| 78 | MOD_MASK_SHIFT, '%', 'g', '%', '0', /* redo */ |
| 79 | MOD_MASK_SHIFT, '%', 'h', '&', '3', /* replace */ |
| 80 | MOD_MASK_SHIFT, '%', 'i', 'k', 'r', /* right arr. */ |
| 81 | MOD_MASK_CTRL, KS_EXTRA, (int)KE_C_RIGHT, 'k', 'r', /* right arr. */ |
| 82 | MOD_MASK_SHIFT, '%', 'j', '&', '5', /* resume */ |
| 83 | MOD_MASK_SHIFT, '!', '1', '&', '6', /* save */ |
| 84 | MOD_MASK_SHIFT, '!', '2', '&', '7', /* suspend */ |
| 85 | MOD_MASK_SHIFT, '!', '3', '&', '8', /* undo */ |
| 86 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_UP, 'k', 'u', /* up arrow */ |
| 87 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_DOWN, 'k', 'd', /* down arrow */ |
| 88 | |
| 89 | /* vt100 F1 */ |
| 90 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_XF1, KS_EXTRA, (int)KE_XF1, |
| 91 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_XF2, KS_EXTRA, (int)KE_XF2, |
| 92 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_XF3, KS_EXTRA, (int)KE_XF3, |
| 93 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_XF4, KS_EXTRA, (int)KE_XF4, |
| 94 | |
| 95 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F1, 'k', '1', /* F1 */ |
| 96 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F2, 'k', '2', |
| 97 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F3, 'k', '3', |
| 98 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F4, 'k', '4', |
| 99 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F5, 'k', '5', |
| 100 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F6, 'k', '6', |
| 101 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F7, 'k', '7', |
| 102 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F8, 'k', '8', |
| 103 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F9, 'k', '9', |
| 104 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F10, 'k', ';', /* F10 */ |
| 105 | |
| 106 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F11, 'F', '1', |
| 107 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F12, 'F', '2', |
| 108 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F13, 'F', '3', |
| 109 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F14, 'F', '4', |
| 110 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F15, 'F', '5', |
| 111 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F16, 'F', '6', |
| 112 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F17, 'F', '7', |
| 113 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F18, 'F', '8', |
| 114 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F19, 'F', '9', |
| 115 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F20, 'F', 'A', |
| 116 | |
| 117 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F21, 'F', 'B', |
| 118 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F22, 'F', 'C', |
| 119 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F23, 'F', 'D', |
| 120 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F24, 'F', 'E', |
| 121 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F25, 'F', 'F', |
| 122 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F26, 'F', 'G', |
| 123 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F27, 'F', 'H', |
| 124 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F28, 'F', 'I', |
| 125 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F29, 'F', 'J', |
| 126 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F30, 'F', 'K', |
| 127 | |
| 128 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F31, 'F', 'L', |
| 129 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F32, 'F', 'M', |
| 130 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F33, 'F', 'N', |
| 131 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F34, 'F', 'O', |
| 132 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F35, 'F', 'P', |
| 133 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F36, 'F', 'Q', |
| 134 | MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F37, 'F', 'R', |
| 135 | |
| 136 | /* TAB pseudo code*/ |
| 137 | MOD_MASK_SHIFT, 'k', 'B', KS_EXTRA, (int)KE_TAB, |
| 138 | |
| 139 | NUL |
| 140 | }; |
| 141 | |
| 142 | static const struct key_name_entry { |
| 143 | int key; // Special key code or ascii value |
| 144 | const char *name; // Name of key |
| 145 | } key_names_table[] = { |
| 146 | { ' ', "Space" }, |
| 147 | { TAB, "Tab" }, |
| 148 | { K_TAB, "Tab" }, |
| 149 | { NL, "NL" }, |
| 150 | { NL, "NewLine" }, // Alternative name |
| 151 | { NL, "LineFeed" }, // Alternative name |
| 152 | { NL, "LF" }, // Alternative name |
| 153 | { CAR, "CR" }, |
| 154 | { CAR, "Return" }, // Alternative name |
| 155 | { CAR, "Enter" }, // Alternative name |
| 156 | { K_BS, "BS" }, |
| 157 | { K_BS, "BackSpace" }, // Alternative name |
| 158 | { ESC, "Esc" }, |
| 159 | { CSI, "CSI" }, |
| 160 | { K_CSI, "xCSI" }, |
| 161 | { '|', "Bar" }, |
| 162 | { '\\', "Bslash" }, |
| 163 | { K_DEL, "Del" }, |
| 164 | { K_DEL, "Delete" }, // Alternative name |
| 165 | { K_KDEL, "kDel" }, |
| 166 | { K_KDEL, "KPPeriod" }, // libtermkey name |
| 167 | { K_UP, "Up" }, |
| 168 | { K_DOWN, "Down" }, |
| 169 | { K_LEFT, "Left" }, |
| 170 | { K_RIGHT, "Right" }, |
| 171 | { K_XUP, "xUp" }, |
| 172 | { K_XDOWN, "xDown" }, |
| 173 | { K_XLEFT, "xLeft" }, |
| 174 | { K_XRIGHT, "xRight" }, |
| 175 | { K_KUP, "kUp" }, |
| 176 | { K_KUP, "KP8" }, |
| 177 | { K_KDOWN, "kDown" }, |
| 178 | { K_KDOWN, "KP2" }, |
| 179 | { K_KLEFT, "kLeft" }, |
| 180 | { K_KLEFT, "KP4" }, |
| 181 | { K_KRIGHT, "kRight" }, |
| 182 | { K_KRIGHT, "KP6" }, |
| 183 | |
| 184 | { K_F1, "F1" }, |
| 185 | { K_F2, "F2" }, |
| 186 | { K_F3, "F3" }, |
| 187 | { K_F4, "F4" }, |
| 188 | { K_F5, "F5" }, |
| 189 | { K_F6, "F6" }, |
| 190 | { K_F7, "F7" }, |
| 191 | { K_F8, "F8" }, |
| 192 | { K_F9, "F9" }, |
| 193 | { K_F10, "F10" }, |
| 194 | |
| 195 | { K_F11, "F11" }, |
| 196 | { K_F12, "F12" }, |
| 197 | { K_F13, "F13" }, |
| 198 | { K_F14, "F14" }, |
| 199 | { K_F15, "F15" }, |
| 200 | { K_F16, "F16" }, |
| 201 | { K_F17, "F17" }, |
| 202 | { K_F18, "F18" }, |
| 203 | { K_F19, "F19" }, |
| 204 | { K_F20, "F20" }, |
| 205 | |
| 206 | { K_F21, "F21" }, |
| 207 | { K_F22, "F22" }, |
| 208 | { K_F23, "F23" }, |
| 209 | { K_F24, "F24" }, |
| 210 | { K_F25, "F25" }, |
| 211 | { K_F26, "F26" }, |
| 212 | { K_F27, "F27" }, |
| 213 | { K_F28, "F28" }, |
| 214 | { K_F29, "F29" }, |
| 215 | { K_F30, "F30" }, |
| 216 | |
| 217 | { K_F31, "F31" }, |
| 218 | { K_F32, "F32" }, |
| 219 | { K_F33, "F33" }, |
| 220 | { K_F34, "F34" }, |
| 221 | { K_F35, "F35" }, |
| 222 | { K_F36, "F36" }, |
| 223 | { K_F37, "F37" }, |
| 224 | |
| 225 | { K_XF1, "xF1" }, |
| 226 | { K_XF2, "xF2" }, |
| 227 | { K_XF3, "xF3" }, |
| 228 | { K_XF4, "xF4" }, |
| 229 | |
| 230 | { K_HELP, "Help" }, |
| 231 | { K_UNDO, "Undo" }, |
| 232 | { K_INS, "Insert" }, |
| 233 | { K_INS, "Ins" }, // Alternative name |
| 234 | { K_KINS, "kInsert" }, |
| 235 | { K_KINS, "KP0" }, |
| 236 | { K_HOME, "Home" }, |
| 237 | { K_KHOME, "kHome" }, |
| 238 | { K_KHOME, "KP7" }, |
| 239 | { K_XHOME, "xHome" }, |
| 240 | { K_ZHOME, "zHome" }, |
| 241 | { K_END, "End" }, |
| 242 | { K_KEND, "kEnd" }, |
| 243 | { K_KEND, "KP1" }, |
| 244 | { K_XEND, "xEnd" }, |
| 245 | { K_ZEND, "zEnd" }, |
| 246 | { K_PAGEUP, "PageUp" }, |
| 247 | { K_PAGEDOWN, "PageDown" }, |
| 248 | { K_KPAGEUP, "kPageUp" }, |
| 249 | { K_KPAGEUP, "KP9" }, |
| 250 | { K_KPAGEDOWN, "kPageDown" }, |
| 251 | { K_KPAGEDOWN, "KP3" }, |
| 252 | { K_KORIGIN, "kOrigin" }, |
| 253 | { K_KORIGIN, "KP5" }, |
| 254 | |
| 255 | { K_KPLUS, "kPlus" }, |
| 256 | { K_KPLUS, "KPPlus" }, |
| 257 | { K_KMINUS, "kMinus" }, |
| 258 | { K_KMINUS, "KPMinus" }, |
| 259 | { K_KDIVIDE, "kDivide" }, |
| 260 | { K_KDIVIDE, "KPDiv" }, |
| 261 | { K_KMULTIPLY, "kMultiply" }, |
| 262 | { K_KMULTIPLY, "KPMult" }, |
| 263 | { K_KENTER, "kEnter" }, |
| 264 | { K_KENTER, "KPEnter" }, |
| 265 | { K_KPOINT, "kPoint" }, |
| 266 | { K_KCOMMA, "kComma" }, |
| 267 | { K_KCOMMA, "KPComma" }, |
| 268 | { K_KEQUAL, "kEqual" }, |
| 269 | { K_KEQUAL, "KPEquals" }, |
| 270 | |
| 271 | { K_K0, "k0" }, |
| 272 | { K_K1, "k1" }, |
| 273 | { K_K2, "k2" }, |
| 274 | { K_K3, "k3" }, |
| 275 | { K_K4, "k4" }, |
| 276 | { K_K5, "k5" }, |
| 277 | { K_K6, "k6" }, |
| 278 | { K_K7, "k7" }, |
| 279 | { K_K8, "k8" }, |
| 280 | { K_K9, "k9" }, |
| 281 | |
| 282 | { '<', "lt" }, |
| 283 | |
| 284 | { K_MOUSE, "Mouse" }, |
| 285 | { K_LEFTMOUSE, "LeftMouse" }, |
| 286 | { K_LEFTMOUSE_NM, "LeftMouseNM" }, |
| 287 | { K_LEFTDRAG, "LeftDrag" }, |
| 288 | { K_LEFTRELEASE, "LeftRelease" }, |
| 289 | { K_LEFTRELEASE_NM, "LeftReleaseNM" }, |
| 290 | { K_MIDDLEMOUSE, "MiddleMouse" }, |
| 291 | { K_MIDDLEDRAG, "MiddleDrag" }, |
| 292 | { K_MIDDLERELEASE, "MiddleRelease" }, |
| 293 | { K_RIGHTMOUSE, "RightMouse" }, |
| 294 | { K_RIGHTDRAG, "RightDrag" }, |
| 295 | { K_RIGHTRELEASE, "RightRelease" }, |
| 296 | { K_MOUSEDOWN, "ScrollWheelUp" }, |
| 297 | { K_MOUSEUP, "ScrollWheelDown" }, |
| 298 | { K_MOUSELEFT, "ScrollWheelRight" }, |
| 299 | { K_MOUSERIGHT, "ScrollWheelLeft" }, |
| 300 | { K_MOUSEDOWN, "MouseDown" }, // OBSOLETE: Use |
| 301 | { K_MOUSEUP, "MouseUp" }, // ScrollWheelXXX instead |
| 302 | { K_X1MOUSE, "X1Mouse" }, |
| 303 | { K_X1DRAG, "X1Drag" }, |
| 304 | { K_X1RELEASE, "X1Release" }, |
| 305 | { K_X2MOUSE, "X2Mouse" }, |
| 306 | { K_X2DRAG, "X2Drag" }, |
| 307 | { K_X2RELEASE, "X2Release" }, |
| 308 | { K_DROP, "Drop" }, |
| 309 | { K_ZERO, "Nul" }, |
| 310 | { K_SNR, "SNR" }, |
| 311 | { K_PLUG, "Plug" }, |
| 312 | { K_COMMAND, "Cmd" }, |
| 313 | { 0, NULL } |
| 314 | // NOTE: When adding a long name update MAX_KEY_NAME_LEN. |
| 315 | }; |
| 316 | |
| 317 | static struct mousetable { |
| 318 | int pseudo_code; /* Code for pseudo mouse event */ |
| 319 | int button; /* Which mouse button is it? */ |
| 320 | int is_click; /* Is it a mouse button click event? */ |
| 321 | int is_drag; /* Is it a mouse drag event? */ |
| 322 | } mouse_table[] = |
| 323 | { |
| 324 | {(int)KE_LEFTMOUSE, MOUSE_LEFT, TRUE, FALSE}, |
| 325 | {(int)KE_LEFTDRAG, MOUSE_LEFT, FALSE, TRUE}, |
| 326 | {(int)KE_LEFTRELEASE, MOUSE_LEFT, FALSE, FALSE}, |
| 327 | {(int)KE_MIDDLEMOUSE, MOUSE_MIDDLE, TRUE, FALSE}, |
| 328 | {(int)KE_MIDDLEDRAG, MOUSE_MIDDLE, FALSE, TRUE}, |
| 329 | {(int)KE_MIDDLERELEASE, MOUSE_MIDDLE, FALSE, FALSE}, |
| 330 | {(int)KE_RIGHTMOUSE, MOUSE_RIGHT, TRUE, FALSE}, |
| 331 | {(int)KE_RIGHTDRAG, MOUSE_RIGHT, FALSE, TRUE}, |
| 332 | {(int)KE_RIGHTRELEASE, MOUSE_RIGHT, FALSE, FALSE}, |
| 333 | {(int)KE_X1MOUSE, MOUSE_X1, TRUE, FALSE}, |
| 334 | {(int)KE_X1DRAG, MOUSE_X1, FALSE, TRUE}, |
| 335 | {(int)KE_X1RELEASE, MOUSE_X1, FALSE, FALSE}, |
| 336 | {(int)KE_X2MOUSE, MOUSE_X2, TRUE, FALSE}, |
| 337 | {(int)KE_X2DRAG, MOUSE_X2, FALSE, TRUE}, |
| 338 | {(int)KE_X2RELEASE, MOUSE_X2, FALSE, FALSE}, |
| 339 | /* DRAG without CLICK */ |
| 340 | {(int)KE_IGNORE, MOUSE_RELEASE, FALSE, TRUE}, |
| 341 | /* RELEASE without CLICK */ |
| 342 | {(int)KE_IGNORE, MOUSE_RELEASE, FALSE, FALSE}, |
| 343 | {0, 0, 0, 0}, |
| 344 | }; |
| 345 | |
| 346 | /// Return the modifier mask bit (#MOD_MASK_*) corresponding to mod name |
| 347 | /// |
| 348 | /// E.g. 'S' for shift, 'C' for ctrl. |
| 349 | int name_to_mod_mask(int c) |
| 350 | FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT |
| 351 | { |
| 352 | c = TOUPPER_ASC(c); |
| 353 | for (size_t i = 0; mod_mask_table[i].mod_mask != 0; i++) { |
| 354 | if (c == mod_mask_table[i].name) { |
| 355 | return mod_mask_table[i].mod_flag; |
| 356 | } |
| 357 | } |
| 358 | return 0; |
| 359 | } |
| 360 | |
| 361 | /// Check if there is a special key code for "key" with specified modifiers |
| 362 | /// |
| 363 | /// @param[in] key Initial key code. |
| 364 | /// @param[in,out] modifiers Initial modifiers, is adjusted to have simplified |
| 365 | /// modifiers. |
| 366 | /// |
| 367 | /// @return Simplified key code. |
| 368 | int simplify_key(const int key, int *modifiers) |
| 369 | FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL |
| 370 | { |
| 371 | if (*modifiers & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT)) { |
| 372 | // TAB is a special case. |
| 373 | if (key == TAB && (*modifiers & MOD_MASK_SHIFT)) { |
| 374 | *modifiers &= ~MOD_MASK_SHIFT; |
| 375 | return K_S_TAB; |
| 376 | } |
| 377 | const int key0 = KEY2TERMCAP0(key); |
| 378 | const int key1 = KEY2TERMCAP1(key); |
| 379 | for (int i = 0; modifier_keys_table[i] != NUL; i += MOD_KEYS_ENTRY_SIZE) { |
| 380 | if (key0 == modifier_keys_table[i + 3] |
| 381 | && key1 == modifier_keys_table[i + 4] |
| 382 | && (*modifiers & modifier_keys_table[i])) { |
| 383 | *modifiers &= ~modifier_keys_table[i]; |
| 384 | return TERMCAP2KEY(modifier_keys_table[i + 1], |
| 385 | modifier_keys_table[i + 2]); |
| 386 | } |
| 387 | } |
| 388 | } |
| 389 | return key; |
| 390 | } |
| 391 | |
| 392 | /// Change <xKey> to <Key> |
| 393 | int handle_x_keys(const int key) |
| 394 | FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT |
| 395 | { |
| 396 | switch (key) { |
| 397 | case K_XUP: return K_UP; |
| 398 | case K_XDOWN: return K_DOWN; |
| 399 | case K_XLEFT: return K_LEFT; |
| 400 | case K_XRIGHT: return K_RIGHT; |
| 401 | case K_XHOME: return K_HOME; |
| 402 | case K_ZHOME: return K_HOME; |
| 403 | case K_XEND: return K_END; |
| 404 | case K_ZEND: return K_END; |
| 405 | case K_XF1: return K_F1; |
| 406 | case K_XF2: return K_F2; |
| 407 | case K_XF3: return K_F3; |
| 408 | case K_XF4: return K_F4; |
| 409 | case K_S_XF1: return K_S_F1; |
| 410 | case K_S_XF2: return K_S_F2; |
| 411 | case K_S_XF3: return K_S_F3; |
| 412 | case K_S_XF4: return K_S_F4; |
| 413 | } |
| 414 | return key; |
| 415 | } |
| 416 | |
| 417 | /* |
| 418 | * Return a string which contains the name of the given key when the given |
| 419 | * modifiers are down. |
| 420 | */ |
| 421 | char_u *get_special_key_name(int c, int modifiers) |
| 422 | { |
| 423 | static char_u string[MAX_KEY_NAME_LEN + 1]; |
| 424 | |
| 425 | int i, idx; |
| 426 | int table_idx; |
| 427 | char_u *s; |
| 428 | |
| 429 | string[0] = '<'; |
| 430 | idx = 1; |
| 431 | |
| 432 | /* Key that stands for a normal character. */ |
| 433 | if (IS_SPECIAL(c) && KEY2TERMCAP0(c) == KS_KEY) |
| 434 | c = KEY2TERMCAP1(c); |
| 435 | |
| 436 | /* |
| 437 | * Translate shifted special keys into unshifted keys and set modifier. |
| 438 | * Same for CTRL and ALT modifiers. |
| 439 | */ |
| 440 | if (IS_SPECIAL(c)) { |
| 441 | for (i = 0; modifier_keys_table[i] != 0; i += MOD_KEYS_ENTRY_SIZE) |
| 442 | if ( KEY2TERMCAP0(c) == (int)modifier_keys_table[i + 1] |
| 443 | && (int)KEY2TERMCAP1(c) == (int)modifier_keys_table[i + 2]) { |
| 444 | modifiers |= modifier_keys_table[i]; |
| 445 | c = TERMCAP2KEY(modifier_keys_table[i + 3], |
| 446 | modifier_keys_table[i + 4]); |
| 447 | break; |
| 448 | } |
| 449 | } |
| 450 | |
| 451 | /* try to find the key in the special key table */ |
| 452 | table_idx = find_special_key_in_table(c); |
| 453 | |
| 454 | /* |
| 455 | * When not a known special key, and not a printable character, try to |
| 456 | * extract modifiers. |
| 457 | */ |
| 458 | if (c > 0 |
| 459 | && (*mb_char2len)(c) == 1 |
| 460 | ) { |
| 461 | if (table_idx < 0 |
| 462 | && (!vim_isprintc(c) || (c & 0x7f) == ' ') |
| 463 | && (c & 0x80)) { |
| 464 | c &= 0x7f; |
| 465 | modifiers |= MOD_MASK_ALT; |
| 466 | /* try again, to find the un-alted key in the special key table */ |
| 467 | table_idx = find_special_key_in_table(c); |
| 468 | } |
| 469 | if (table_idx < 0 && !vim_isprintc(c) && c < ' ') { |
| 470 | c += '@'; |
| 471 | modifiers |= MOD_MASK_CTRL; |
| 472 | } |
| 473 | } |
| 474 | |
| 475 | /* translate the modifier into a string */ |
| 476 | for (i = 0; mod_mask_table[i].name != 'A'; i++) |
| 477 | if ((modifiers & mod_mask_table[i].mod_mask) |
| 478 | == mod_mask_table[i].mod_flag) { |
| 479 | string[idx++] = mod_mask_table[i].name; |
| 480 | string[idx++] = (char_u)'-'; |
| 481 | } |
| 482 | |
| 483 | if (table_idx < 0) { /* unknown special key, may output t_xx */ |
| 484 | if (IS_SPECIAL(c)) { |
| 485 | string[idx++] = 't'; |
| 486 | string[idx++] = '_'; |
| 487 | string[idx++] = (char_u)KEY2TERMCAP0(c); |
| 488 | string[idx++] = KEY2TERMCAP1(c); |
| 489 | } else { |
| 490 | // Not a special key, only modifiers, output directly. |
| 491 | if (utf_char2len(c) > 1) { |
| 492 | idx += utf_char2bytes(c, string + idx); |
| 493 | } else if (vim_isprintc(c)) { |
| 494 | string[idx++] = (char_u)c; |
| 495 | } else { |
| 496 | s = transchar(c); |
| 497 | while (*s) |
| 498 | string[idx++] = *s++; |
| 499 | } |
| 500 | } |
| 501 | } else { // use name of special key |
| 502 | size_t len = STRLEN(key_names_table[table_idx].name); |
| 503 | |
| 504 | if ((int)len + idx + 2 <= MAX_KEY_NAME_LEN) { |
| 505 | STRCPY(string + idx, key_names_table[table_idx].name); |
| 506 | idx += (int)len; |
| 507 | } |
| 508 | } |
| 509 | string[idx++] = '>'; |
| 510 | string[idx] = NUL; |
| 511 | return string; |
| 512 | } |
| 513 | |
| 514 | /// Try translating a <> name ("keycode"). |
| 515 | /// |
| 516 | /// @param[in,out] srcp Source from which <> are translated. Is advanced to |
| 517 | /// after the <> name if there is a match. |
| 518 | /// @param[in] src_len Length of the srcp. |
| 519 | /// @param[out] dst Location where translation result will be kept. Must have |
| 520 | /// at least six bytes. |
| 521 | /// @param[in] keycode Prefer key code, e.g. K_DEL in place of DEL. |
| 522 | /// @param[in] in_string Inside a double quoted string |
| 523 | /// |
| 524 | /// @return Number of characters added to dst, zero for no match. |
| 525 | unsigned int trans_special(const char_u **srcp, const size_t src_len, |
| 526 | char_u *const dst, const bool keycode, |
| 527 | const bool in_string) |
| 528 | FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT |
| 529 | { |
| 530 | int modifiers = 0; |
| 531 | int key; |
| 532 | unsigned int dlen = 0; |
| 533 | |
| 534 | key = find_special_key(srcp, src_len, &modifiers, keycode, false, in_string); |
| 535 | if (key == 0) { |
| 536 | return 0; |
| 537 | } |
| 538 | |
| 539 | // Put the appropriate modifier in a string. |
| 540 | if (modifiers != 0) { |
| 541 | dst[dlen++] = K_SPECIAL; |
| 542 | dst[dlen++] = KS_MODIFIER; |
| 543 | dst[dlen++] = (char_u)modifiers; |
| 544 | } |
| 545 | |
| 546 | if (IS_SPECIAL(key)) { |
| 547 | dst[dlen++] = K_SPECIAL; |
| 548 | dst[dlen++] = (char_u)KEY2TERMCAP0(key); |
| 549 | dst[dlen++] = KEY2TERMCAP1(key); |
| 550 | } else if (!keycode) { |
| 551 | dlen += (unsigned int)utf_char2bytes(key, dst + dlen); |
| 552 | } else { |
| 553 | char_u *after = add_char2buf(key, dst + dlen); |
| 554 | assert(after >= dst && (uintmax_t)(after - dst) <= UINT_MAX); |
| 555 | dlen = (unsigned int)(after - dst); |
| 556 | } |
| 557 | |
| 558 | return dlen; |
| 559 | } |
| 560 | |
| 561 | /// Try translating a <> name |
| 562 | /// |
| 563 | /// @param[in,out] srcp Translated <> name. Is advanced to after the <> name. |
| 564 | /// @param[in] src_len srcp length. |
| 565 | /// @param[out] modp Location where information about modifiers is saved. |
| 566 | /// @param[in] keycode Prefer key code, e.g. K_DEL in place of DEL. |
| 567 | /// @param[in] keep_x_key Don’t translate xHome to Home key. |
| 568 | /// @param[in] in_string In string, double quote is escaped |
| 569 | /// |
| 570 | /// @return Key and modifiers or 0 if there is no match. |
| 571 | int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, |
| 572 | const bool keycode, const bool keep_x_key, |
| 573 | const bool in_string) |
| 574 | FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL |
| 575 | { |
| 576 | const char_u *last_dash; |
| 577 | const char_u *end_of_name; |
| 578 | const char_u *src; |
| 579 | const char_u *bp; |
| 580 | const char_u *const end = *srcp + src_len - 1; |
| 581 | int modifiers; |
| 582 | int bit; |
| 583 | int key; |
| 584 | uvarnumber_T n; |
| 585 | int l; |
| 586 | |
| 587 | if (src_len == 0) { |
| 588 | return 0; |
| 589 | } |
| 590 | |
| 591 | src = *srcp; |
| 592 | if (src[0] != '<') { |
| 593 | return 0; |
| 594 | } |
| 595 | |
| 596 | // Find end of modifier list |
| 597 | last_dash = src; |
| 598 | for (bp = src + 1; bp <= end && (*bp == '-' || ascii_isident(*bp)); bp++) { |
| 599 | if (*bp == '-') { |
| 600 | last_dash = bp; |
| 601 | if (bp + 1 <= end) { |
| 602 | l = utfc_ptr2len_len(bp + 1, (int)(end - bp) + 1); |
| 603 | // Anything accepted, like <C-?>. |
| 604 | // <C-"> or <M-"> are not special in strings as " is |
| 605 | // the string delimiter. With a backslash it works: <M-\"> |
| 606 | if (end - bp > l && !(in_string && bp[1] == '"') && bp[l+1] == '>') { |
| 607 | bp += l; |
| 608 | } else if (end - bp > 2 && in_string && bp[1] == '\\' |
| 609 | && bp[2] == '"' && bp[3] == '>') { |
| 610 | bp += 2; |
| 611 | } |
| 612 | } |
| 613 | } |
| 614 | if (end - bp > 3 && bp[0] == 't' && bp[1] == '_') { |
| 615 | bp += 3; // skip t_xx, xx may be '-' or '>' |
| 616 | } else if (end - bp > 4 && STRNICMP(bp, "char-" , 5) == 0) { |
| 617 | vim_str2nr(bp + 5, NULL, &l, STR2NR_ALL, NULL, NULL, 0); |
| 618 | bp += l + 5; |
| 619 | break; |
| 620 | } |
| 621 | } |
| 622 | |
| 623 | if (bp <= end && *bp == '>') { // found matching '>' |
| 624 | end_of_name = bp + 1; |
| 625 | |
| 626 | /* Which modifiers are given? */ |
| 627 | modifiers = 0x0; |
| 628 | for (bp = src + 1; bp < last_dash; bp++) { |
| 629 | if (*bp != '-') { |
| 630 | bit = name_to_mod_mask(*bp); |
| 631 | if (bit == 0x0) { |
| 632 | break; // Illegal modifier name |
| 633 | } |
| 634 | modifiers |= bit; |
| 635 | } |
| 636 | } |
| 637 | |
| 638 | // Legal modifier name. |
| 639 | if (bp >= last_dash) { |
| 640 | if (STRNICMP(last_dash + 1, "char-" , 5) == 0 |
| 641 | && ascii_isdigit(last_dash[6])) { |
| 642 | // <Char-123> or <Char-033> or <Char-0x33> |
| 643 | vim_str2nr(last_dash + 6, NULL, NULL, STR2NR_ALL, NULL, &n, 0); |
| 644 | key = (int)n; |
| 645 | } else { |
| 646 | int off = 1; |
| 647 | |
| 648 | // Modifier with single letter, or special key name. |
| 649 | if (in_string && last_dash[1] == '\\' && last_dash[2] == '"') { |
| 650 | // Special case for a double-quoted string |
| 651 | off = l = 2; |
| 652 | } else { |
| 653 | l = mb_ptr2len(last_dash + 1); |
| 654 | } |
| 655 | if (modifiers != 0 && last_dash[l + 1] == '>') { |
| 656 | key = PTR2CHAR(last_dash + off); |
| 657 | } else { |
| 658 | key = get_special_key_code(last_dash + off); |
| 659 | if (!keep_x_key) { |
| 660 | key = handle_x_keys(key); |
| 661 | } |
| 662 | } |
| 663 | } |
| 664 | |
| 665 | // get_special_key_code() may return NUL for invalid |
| 666 | // special key name. |
| 667 | if (key != NUL) { |
| 668 | // Only use a modifier when there is no special key code that |
| 669 | // includes the modifier. |
| 670 | key = simplify_key(key, &modifiers); |
| 671 | |
| 672 | if (!keycode) { |
| 673 | // don't want keycode, use single byte code |
| 674 | if (key == K_BS) { |
| 675 | key = BS; |
| 676 | } else if (key == K_DEL || key == K_KDEL) { |
| 677 | key = DEL; |
| 678 | } |
| 679 | } |
| 680 | |
| 681 | // Normal Key with modifier: |
| 682 | // Try to make a single byte code (except for Alt/Meta modifiers). |
| 683 | if (!IS_SPECIAL(key)) { |
| 684 | key = extract_modifiers(key, &modifiers); |
| 685 | } |
| 686 | |
| 687 | *modp = modifiers; |
| 688 | *srcp = end_of_name; |
| 689 | return key; |
| 690 | } // else { ELOG("unknown key: '%s'", src); } |
| 691 | } |
| 692 | } |
| 693 | return 0; |
| 694 | } |
| 695 | |
| 696 | /// Try to include modifiers (except alt/meta) in the key. |
| 697 | /// Changes "Shift-a" to 'A', "Ctrl-@" to <Nul>, etc. |
| 698 | static int (int key, int *modp) |
| 699 | { |
| 700 | int modifiers = *modp; |
| 701 | |
| 702 | if (!(modifiers & MOD_MASK_CMD)) { // Command-key is special |
| 703 | if ((modifiers & MOD_MASK_SHIFT) && ASCII_ISALPHA(key)) { |
| 704 | key = TOUPPER_ASC(key); |
| 705 | modifiers &= ~MOD_MASK_SHIFT; |
| 706 | } |
| 707 | } |
| 708 | if ((modifiers & MOD_MASK_CTRL) |
| 709 | && ((key >= '?' && key <= '_') || ASCII_ISALPHA(key))) { |
| 710 | key = Ctrl_chr(key); |
| 711 | modifiers &= ~MOD_MASK_CTRL; |
| 712 | if (key == 0) { // <C-@> is <Nul> |
| 713 | key = K_ZERO; |
| 714 | } |
| 715 | } |
| 716 | |
| 717 | *modp = modifiers; |
| 718 | return key; |
| 719 | } |
| 720 | |
| 721 | /* |
| 722 | * Try to find key "c" in the special key table. |
| 723 | * Return the index when found, -1 when not found. |
| 724 | */ |
| 725 | int find_special_key_in_table(int c) |
| 726 | { |
| 727 | int i; |
| 728 | |
| 729 | for (i = 0; key_names_table[i].name != NULL; i++) { |
| 730 | if (c == key_names_table[i].key) { |
| 731 | break; |
| 732 | } |
| 733 | } |
| 734 | if (key_names_table[i].name == NULL) { |
| 735 | i = -1; |
| 736 | } |
| 737 | return i; |
| 738 | } |
| 739 | |
| 740 | /// Find the special key with the given name |
| 741 | /// |
| 742 | /// @param[in] name Name of the special. Does not have to end with NUL, it is |
| 743 | /// assumed to end before the first non-idchar. If name starts |
| 744 | /// with "t_" the next two characters are interpreted as |
| 745 | /// a termcap name. |
| 746 | /// |
| 747 | /// @return Key code or 0 if not found. |
| 748 | int get_special_key_code(const char_u *name) |
| 749 | FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT |
| 750 | { |
| 751 | for (int i = 0; key_names_table[i].name != NULL; i++) { |
| 752 | const char *const table_name = key_names_table[i].name; |
| 753 | int j; |
| 754 | for (j = 0; ascii_isident(name[j]) && table_name[j] != NUL; j++) { |
| 755 | if (TOLOWER_ASC(table_name[j]) != TOLOWER_ASC(name[j])) { |
| 756 | break; |
| 757 | } |
| 758 | } |
| 759 | if (!ascii_isident(name[j]) && table_name[j] == NUL) { |
| 760 | return key_names_table[i].key; |
| 761 | } |
| 762 | } |
| 763 | |
| 764 | return 0; |
| 765 | } |
| 766 | |
| 767 | /* |
| 768 | * Look up the given mouse code to return the relevant information in the other |
| 769 | * arguments. Return which button is down or was released. |
| 770 | */ |
| 771 | int get_mouse_button(int code, bool *is_click, bool *is_drag) |
| 772 | { |
| 773 | int i; |
| 774 | |
| 775 | for (i = 0; mouse_table[i].pseudo_code; i++) |
| 776 | if (code == mouse_table[i].pseudo_code) { |
| 777 | *is_click = mouse_table[i].is_click; |
| 778 | *is_drag = mouse_table[i].is_drag; |
| 779 | return mouse_table[i].button; |
| 780 | } |
| 781 | return 0; /* Shouldn't get here */ |
| 782 | } |
| 783 | |
| 784 | /// Replace any terminal code strings with the equivalent internal |
| 785 | /// representation |
| 786 | /// |
| 787 | /// Used for the "from" and "to" part of a mapping, and the "to" part of |
| 788 | /// a menu command. Any strings like "<C-UP>" are also replaced, unless |
| 789 | /// `special` is false. K_SPECIAL by itself is replaced by K_SPECIAL |
| 790 | /// KS_SPECIAL KE_FILLER. |
| 791 | /// |
| 792 | /// @param[in] from What characters to replace. |
| 793 | /// @param[in] from_len Length of the "from" argument. |
| 794 | /// @param[out] bufp Location where results were saved in case of success |
| 795 | /// (allocated). Will be set to NULL in case of failure. |
| 796 | /// @param[in] do_lt If true, also translate <lt>. |
| 797 | /// @param[in] from_part If true, trailing <C-v> is included, otherwise it is |
| 798 | /// removed (to make ":map xx ^V" map xx to nothing). |
| 799 | /// When cpo_flags contains #FLAG_CPO_BSLASH, a backslash |
| 800 | /// can be used in place of <C-v>. All other <C-v> |
| 801 | /// characters are removed. |
| 802 | /// @param[in] special Replace keycodes, e.g. <CR> becomes a "\n" char. |
| 803 | /// @param[in] cpo_flags Relevant flags derived from p_cpo, see |
| 804 | /// #CPO_TO_CPO_FLAGS. |
| 805 | /// |
| 806 | /// @return Pointer to an allocated memory in case of success, "from" in case of |
| 807 | /// failure. In case of success returned pointer is also saved to |
| 808 | /// "bufp". |
| 809 | char_u *replace_termcodes(const char_u *from, const size_t from_len, |
| 810 | char_u **bufp, const bool from_part, const bool do_lt, |
| 811 | const bool special, int cpo_flags) |
| 812 | FUNC_ATTR_NONNULL_ALL |
| 813 | { |
| 814 | ssize_t i; |
| 815 | size_t slen; |
| 816 | char_u key; |
| 817 | size_t dlen = 0; |
| 818 | const char_u *src; |
| 819 | const char_u *const end = from + from_len - 1; |
| 820 | int do_backslash; // backslash is a special character |
| 821 | char_u *result; // buffer for resulting string |
| 822 | |
| 823 | do_backslash = !(cpo_flags&FLAG_CPO_BSLASH); |
| 824 | |
| 825 | // Allocate space for the translation. Worst case a single character is |
| 826 | // replaced by 6 bytes (shifted special key), plus a NUL at the end. |
| 827 | const size_t buf_len = from_len * 6 + 1; |
| 828 | result = xmalloc(buf_len); |
| 829 | |
| 830 | src = from; |
| 831 | |
| 832 | // Check for #n at start only: function key n |
| 833 | if (from_part && from_len > 1 && src[0] == '#' |
| 834 | && ascii_isdigit(src[1])) { // function key |
| 835 | result[dlen++] = K_SPECIAL; |
| 836 | result[dlen++] = 'k'; |
| 837 | if (src[1] == '0') { |
| 838 | result[dlen++] = ';'; // #0 is F10 is "k;" |
| 839 | } else { |
| 840 | result[dlen++] = src[1]; // #3 is F3 is "k3" |
| 841 | } |
| 842 | src += 2; |
| 843 | } |
| 844 | |
| 845 | // Copy each byte from *from to result[dlen] |
| 846 | while (src <= end) { |
| 847 | // Check for special <> keycodes, like "<C-S-LeftMouse>" |
| 848 | if (special && (do_lt || ((end - src) >= 3 |
| 849 | && STRNCMP(src, "<lt>" , 4) != 0))) { |
| 850 | // Replace <SID> by K_SNR <script-nr> _. |
| 851 | // (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14) |
| 852 | if (end - src >= 4 && STRNICMP(src, "<SID>" , 5) == 0) { |
| 853 | if (current_sctx.sc_sid <= 0) { |
| 854 | EMSG(_(e_usingsid)); |
| 855 | } else { |
| 856 | src += 5; |
| 857 | result[dlen++] = K_SPECIAL; |
| 858 | result[dlen++] = (int)KS_EXTRA; |
| 859 | result[dlen++] = (int)KE_SNR; |
| 860 | snprintf((char *)result + dlen, buf_len - dlen, "%" PRId64, |
| 861 | (int64_t)current_sctx.sc_sid); |
| 862 | dlen += STRLEN(result + dlen); |
| 863 | result[dlen++] = '_'; |
| 864 | continue; |
| 865 | } |
| 866 | } |
| 867 | |
| 868 | slen = trans_special(&src, (size_t)(end - src) + 1, result + dlen, true, |
| 869 | false); |
| 870 | if (slen) { |
| 871 | dlen += slen; |
| 872 | continue; |
| 873 | } |
| 874 | } |
| 875 | |
| 876 | if (special) { |
| 877 | char_u *p, *s, len; |
| 878 | |
| 879 | // Replace <Leader> by the value of "mapleader". |
| 880 | // Replace <LocalLeader> by the value of "maplocalleader". |
| 881 | // If "mapleader" or "maplocalleader" isn't set use a backslash. |
| 882 | if (end - src >= 7 && STRNICMP(src, "<Leader>" , 8) == 0) { |
| 883 | len = 8; |
| 884 | p = get_var_value("g:mapleader" ); |
| 885 | } else if (end - src >= 12 && STRNICMP(src, "<LocalLeader>" , 13) == 0) { |
| 886 | len = 13; |
| 887 | p = get_var_value("g:maplocalleader" ); |
| 888 | } else { |
| 889 | len = 0; |
| 890 | p = NULL; |
| 891 | } |
| 892 | |
| 893 | if (len != 0) { |
| 894 | // Allow up to 8 * 6 characters for "mapleader". |
| 895 | if (p == NULL || *p == NUL || STRLEN(p) > 8 * 6) { |
| 896 | s = (char_u *)"\\" ; |
| 897 | } else { |
| 898 | s = p; |
| 899 | } |
| 900 | while (*s != NUL) { |
| 901 | result[dlen++] = *s++; |
| 902 | } |
| 903 | src += len; |
| 904 | continue; |
| 905 | } |
| 906 | } |
| 907 | |
| 908 | // Remove CTRL-V and ignore the next character. |
| 909 | // For "from" side the CTRL-V at the end is included, for the "to" |
| 910 | // part it is removed. |
| 911 | // If 'cpoptions' does not contain 'B', also accept a backslash. |
| 912 | key = *src; |
| 913 | if (key == Ctrl_V || (do_backslash && key == '\\')) { |
| 914 | src++; // skip CTRL-V or backslash |
| 915 | if (src > end) { |
| 916 | if (from_part) { |
| 917 | result[dlen++] = key; |
| 918 | } |
| 919 | break; |
| 920 | } |
| 921 | } |
| 922 | |
| 923 | // skip multibyte char correctly |
| 924 | for (i = utfc_ptr2len_len(src, (int)(end - src) + 1); i > 0; i--) { |
| 925 | // If the character is K_SPECIAL, replace it with K_SPECIAL |
| 926 | // KS_SPECIAL KE_FILLER. |
| 927 | // If compiled with the GUI replace CSI with K_CSI. |
| 928 | if (*src == K_SPECIAL) { |
| 929 | result[dlen++] = K_SPECIAL; |
| 930 | result[dlen++] = KS_SPECIAL; |
| 931 | result[dlen++] = KE_FILLER; |
| 932 | } else { |
| 933 | result[dlen++] = *src; |
| 934 | } |
| 935 | ++src; |
| 936 | } |
| 937 | } |
| 938 | result[dlen] = NUL; |
| 939 | |
| 940 | *bufp = xrealloc(result, dlen + 1); |
| 941 | |
| 942 | return *bufp; |
| 943 | } |
| 944 | |
| 945 | /// Logs a single key as a human-readable keycode. |
| 946 | void log_key(int log_level, int key) |
| 947 | { |
| 948 | if (log_level < MIN_LOG_LEVEL) { |
| 949 | return; |
| 950 | } |
| 951 | char *keyname = key == K_EVENT |
| 952 | ? "K_EVENT" |
| 953 | : (char *)get_special_key_name(key, mod_mask); |
| 954 | LOG(log_level, "input: %s" , keyname); |
| 955 | } |
| 956 | |