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 | |