xterm
[VSoRC/.git] / node_modules / xterm / src / common / input / Keyboard.ts
1 /**
2  * Copyright (c) 2014 The xterm.js authors. All rights reserved.
3  * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
4  * @license MIT
5  */
6
7 import { IKeyboardEvent, IKeyboardResult, KeyboardResultType } from 'common/Types';
8 import { C0 } from 'common/data/EscapeSequences';
9
10 // reg + shift key mappings for digits and special chars
11 const KEYCODE_KEY_MAPPINGS: { [key: number]: [string, string]} = {
12   // digits 0-9
13   48: ['0', ')'],
14   49: ['1', '!'],
15   50: ['2', '@'],
16   51: ['3', '#'],
17   52: ['4', '$'],
18   53: ['5', '%'],
19   54: ['6', '^'],
20   55: ['7', '&'],
21   56: ['8', '*'],
22   57: ['9', '('],
23
24   // special chars
25   186: [';', ':'],
26   187: ['=', '+'],
27   188: [',', '<'],
28   189: ['-', '_'],
29   190: ['.', '>'],
30   191: ['/', '?'],
31   192: ['`', '~'],
32   219: ['[', '{'],
33   220: ['\\', '|'],
34   221: [']', '}'],
35   222: ['\'', '"']
36 };
37
38 export function evaluateKeyboardEvent(
39   ev: IKeyboardEvent,
40   applicationCursorMode: boolean,
41   isMac: boolean,
42   macOptionIsMeta: boolean
43 ): IKeyboardResult {
44   const result: IKeyboardResult = {
45     type: KeyboardResultType.SEND_KEY,
46     // Whether to cancel event propagation (NOTE: this may not be needed since the event is
47     // canceled at the end of keyDown
48     cancel: false,
49     // The new key even to emit
50     key: undefined
51   };
52   const modifiers = (ev.shiftKey ? 1 : 0) | (ev.altKey ? 2 : 0) | (ev.ctrlKey ? 4 : 0) | (ev.metaKey ? 8 : 0);
53   switch (ev.keyCode) {
54     case 0:
55       if (ev.key === 'UIKeyInputUpArrow') {
56         if (applicationCursorMode) {
57           result.key = C0.ESC + 'OA';
58         } else {
59           result.key = C0.ESC + '[A';
60         }
61       }
62       else if (ev.key === 'UIKeyInputLeftArrow') {
63         if (applicationCursorMode) {
64           result.key = C0.ESC + 'OD';
65         } else {
66           result.key = C0.ESC + '[D';
67         }
68       }
69       else if (ev.key === 'UIKeyInputRightArrow') {
70         if (applicationCursorMode) {
71           result.key = C0.ESC + 'OC';
72         } else {
73           result.key = C0.ESC + '[C';
74         }
75       }
76       else if (ev.key === 'UIKeyInputDownArrow') {
77         if (applicationCursorMode) {
78           result.key = C0.ESC + 'OB';
79         } else {
80           result.key = C0.ESC + '[B';
81         }
82       }
83       break;
84     case 8:
85       // backspace
86       if (ev.shiftKey) {
87         result.key = C0.BS; // ^H
88         break;
89       } else if (ev.altKey) {
90         result.key = C0.ESC + C0.DEL; // \e ^?
91         break;
92       }
93       result.key = C0.DEL; // ^?
94       break;
95     case 9:
96       // tab
97       if (ev.shiftKey) {
98         result.key = C0.ESC + '[Z';
99         break;
100       }
101       result.key = C0.HT;
102       result.cancel = true;
103       break;
104     case 13:
105       // return/enter
106       result.key = C0.CR;
107       result.cancel = true;
108       break;
109     case 27:
110       // escape
111       result.key = C0.ESC;
112       result.cancel = true;
113       break;
114     case 37:
115       // left-arrow
116       if (ev.metaKey) {
117         break;
118       }
119       if (modifiers) {
120         result.key = C0.ESC + '[1;' + (modifiers + 1) + 'D';
121         // HACK: Make Alt + left-arrow behave like Ctrl + left-arrow: move one word backwards
122         // http://unix.stackexchange.com/a/108106
123         // macOS uses different escape sequences than linux
124         if (result.key === C0.ESC + '[1;3D') {
125           result.key = C0.ESC + (isMac ? 'b' : '[1;5D');
126         }
127       } else if (applicationCursorMode) {
128         result.key = C0.ESC + 'OD';
129       } else {
130         result.key = C0.ESC + '[D';
131       }
132       break;
133     case 39:
134       // right-arrow
135       if (ev.metaKey) {
136         break;
137       }
138       if (modifiers) {
139         result.key = C0.ESC + '[1;' + (modifiers + 1) + 'C';
140         // HACK: Make Alt + right-arrow behave like Ctrl + right-arrow: move one word forward
141         // http://unix.stackexchange.com/a/108106
142         // macOS uses different escape sequences than linux
143         if (result.key === C0.ESC + '[1;3C') {
144           result.key = C0.ESC + (isMac ? 'f' : '[1;5C');
145         }
146       } else if (applicationCursorMode) {
147         result.key = C0.ESC + 'OC';
148       } else {
149         result.key = C0.ESC + '[C';
150       }
151       break;
152     case 38:
153       // up-arrow
154       if (ev.metaKey) {
155         break;
156       }
157       if (modifiers) {
158         result.key = C0.ESC + '[1;' + (modifiers + 1) + 'A';
159         // HACK: Make Alt + up-arrow behave like Ctrl + up-arrow
160         // http://unix.stackexchange.com/a/108106
161         // macOS uses different escape sequences than linux
162         if (!isMac && result.key === C0.ESC + '[1;3A') {
163           result.key = C0.ESC + '[1;5A';
164         }
165       } else if (applicationCursorMode) {
166         result.key = C0.ESC + 'OA';
167       } else {
168         result.key = C0.ESC + '[A';
169       }
170       break;
171     case 40:
172       // down-arrow
173       if (ev.metaKey) {
174         break;
175       }
176       if (modifiers) {
177         result.key = C0.ESC + '[1;' + (modifiers + 1) + 'B';
178         // HACK: Make Alt + down-arrow behave like Ctrl + down-arrow
179         // http://unix.stackexchange.com/a/108106
180         // macOS uses different escape sequences than linux
181         if (!isMac && result.key === C0.ESC + '[1;3B') {
182           result.key = C0.ESC + '[1;5B';
183         }
184       } else if (applicationCursorMode) {
185         result.key = C0.ESC + 'OB';
186       } else {
187         result.key = C0.ESC + '[B';
188       }
189       break;
190     case 45:
191       // insert
192       if (!ev.shiftKey && !ev.ctrlKey) {
193         // <Ctrl> or <Shift> + <Insert> are used to
194         // copy-paste on some systems.
195         result.key = C0.ESC + '[2~';
196       }
197       break;
198     case 46:
199       // delete
200       if (modifiers) {
201         result.key = C0.ESC + '[3;' + (modifiers + 1) + '~';
202       } else {
203         result.key = C0.ESC + '[3~';
204       }
205       break;
206     case 36:
207       // home
208       if (modifiers) {
209         result.key = C0.ESC + '[1;' + (modifiers + 1) + 'H';
210       } else if (applicationCursorMode) {
211         result.key = C0.ESC + 'OH';
212       } else {
213         result.key = C0.ESC + '[H';
214       }
215       break;
216     case 35:
217       // end
218       if (modifiers) {
219         result.key = C0.ESC + '[1;' + (modifiers + 1) + 'F';
220       } else if (applicationCursorMode) {
221         result.key = C0.ESC + 'OF';
222       } else {
223         result.key = C0.ESC + '[F';
224       }
225       break;
226     case 33:
227       // page up
228       if (ev.shiftKey) {
229         result.type = KeyboardResultType.PAGE_UP;
230       } else {
231         result.key = C0.ESC + '[5~';
232       }
233       break;
234     case 34:
235       // page down
236       if (ev.shiftKey) {
237         result.type = KeyboardResultType.PAGE_DOWN;
238       } else {
239         result.key = C0.ESC + '[6~';
240       }
241       break;
242     case 112:
243       // F1-F12
244       if (modifiers) {
245         result.key = C0.ESC + '[1;' + (modifiers + 1) + 'P';
246       } else {
247         result.key = C0.ESC + 'OP';
248       }
249       break;
250     case 113:
251       if (modifiers) {
252         result.key = C0.ESC + '[1;' + (modifiers + 1) + 'Q';
253       } else {
254         result.key = C0.ESC + 'OQ';
255       }
256       break;
257     case 114:
258       if (modifiers) {
259         result.key = C0.ESC + '[1;' + (modifiers + 1) + 'R';
260       } else {
261         result.key = C0.ESC + 'OR';
262       }
263       break;
264     case 115:
265       if (modifiers) {
266         result.key = C0.ESC + '[1;' + (modifiers + 1) + 'S';
267       } else {
268         result.key = C0.ESC + 'OS';
269       }
270       break;
271     case 116:
272       if (modifiers) {
273         result.key = C0.ESC + '[15;' + (modifiers + 1) + '~';
274       } else {
275         result.key = C0.ESC + '[15~';
276       }
277       break;
278     case 117:
279       if (modifiers) {
280         result.key = C0.ESC + '[17;' + (modifiers + 1) + '~';
281       } else {
282         result.key = C0.ESC + '[17~';
283       }
284       break;
285     case 118:
286       if (modifiers) {
287         result.key = C0.ESC + '[18;' + (modifiers + 1) + '~';
288       } else {
289         result.key = C0.ESC + '[18~';
290       }
291       break;
292     case 119:
293       if (modifiers) {
294         result.key = C0.ESC + '[19;' + (modifiers + 1) + '~';
295       } else {
296         result.key = C0.ESC + '[19~';
297       }
298       break;
299     case 120:
300       if (modifiers) {
301         result.key = C0.ESC + '[20;' + (modifiers + 1) + '~';
302       } else {
303         result.key = C0.ESC + '[20~';
304       }
305       break;
306     case 121:
307       if (modifiers) {
308         result.key = C0.ESC + '[21;' + (modifiers + 1) + '~';
309       } else {
310         result.key = C0.ESC + '[21~';
311       }
312       break;
313     case 122:
314       if (modifiers) {
315         result.key = C0.ESC + '[23;' + (modifiers + 1) + '~';
316       } else {
317         result.key = C0.ESC + '[23~';
318       }
319       break;
320     case 123:
321       if (modifiers) {
322         result.key = C0.ESC + '[24;' + (modifiers + 1) + '~';
323       } else {
324         result.key = C0.ESC + '[24~';
325       }
326       break;
327     default:
328       // a-z and space
329       if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {
330         if (ev.keyCode >= 65 && ev.keyCode <= 90) {
331           result.key = String.fromCharCode(ev.keyCode - 64);
332         } else if (ev.keyCode === 32) {
333           result.key = C0.NUL;
334         } else if (ev.keyCode >= 51 && ev.keyCode <= 55) {
335           // escape, file sep, group sep, record sep, unit sep
336           result.key = String.fromCharCode(ev.keyCode - 51 + 27);
337         } else if (ev.keyCode === 56) {
338           result.key = C0.DEL;
339         } else if (ev.keyCode === 219) {
340           result.key = C0.ESC;
341         } else if (ev.keyCode === 220) {
342           result.key = C0.FS;
343         } else if (ev.keyCode === 221) {
344           result.key = C0.GS;
345         }
346       } else if ((!isMac || macOptionIsMeta) && ev.altKey && !ev.metaKey) {
347         // On macOS this is a third level shift when !macOptionIsMeta. Use <Esc> instead.
348         const keyMapping = KEYCODE_KEY_MAPPINGS[ev.keyCode];
349         const key = keyMapping && keyMapping[!ev.shiftKey ? 0 : 1];
350         if (key) {
351           result.key = C0.ESC + key;
352         } else if (ev.keyCode >= 65 && ev.keyCode <= 90) {
353           const keyCode = ev.ctrlKey ? ev.keyCode - 64 : ev.keyCode + 32;
354           result.key = C0.ESC + String.fromCharCode(keyCode);
355         }
356       } else if (isMac && !ev.altKey && !ev.ctrlKey && ev.metaKey) {
357         if (ev.keyCode === 65) { // cmd + a
358           result.type = KeyboardResultType.SELECT_ALL;
359         }
360       } else if (ev.key && !ev.ctrlKey && !ev.altKey && !ev.metaKey && ev.keyCode >= 48 && ev.key.length === 1) {
361         // Include only keys that that result in a _single_ character; don't include num lock, volume up, etc.
362         result.key = ev.key;
363       } else if (ev.key && ev.ctrlKey) {
364         if (ev.key === '_') { // ^_
365           result.key = C0.US;
366         }
367       }
368       break;
369   }
370
371   return result;
372 }