UNPKG

16.7 kBJavaScriptView Raw
1"use strict";
2// *****************************************************************************
3// Copyright (C) 2019 TypeFox and others.
4//
5// This program and the accompanying materials are made available under the
6// terms of the Eclipse Public License v. 2.0 which is available at
7// http://www.eclipse.org/legal/epl-2.0.
8//
9// This Source Code may also be made available under the following Secondary
10// Licenses when the conditions for such availability set forth in the Eclipse
11// Public License v. 2.0 are satisfied: GNU General Public License, version 2
12// with the GNU Classpath Exception which is available at
13// https://www.gnu.org/software/classpath/license.html.
14//
15// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
16// *****************************************************************************
17Object.defineProperty(exports, "__esModule", { value: true });
18exports.KeyboardLayoutService = void 0;
19const tslib_1 = require("tslib");
20const inversify_1 = require("inversify");
21const os_1 = require("../../common/os");
22const keyboard_layout_provider_1 = require("../../common/keyboard/keyboard-layout-provider");
23const event_1 = require("../../common/event");
24const keys_1 = require("./keys");
25let KeyboardLayoutService = class KeyboardLayoutService {
26 constructor() {
27 this.keyboardLayoutChanged = new event_1.Emitter();
28 }
29 updateLayout(newLayout) {
30 const transformed = this.transformNativeLayout(newLayout);
31 this.currentLayout = transformed;
32 this.keyboardLayoutChanged.fire(transformed);
33 return transformed;
34 }
35 get onKeyboardLayoutChanged() {
36 return this.keyboardLayoutChanged.event;
37 }
38 async initialize() {
39 this.layoutChangeNotifier.onDidChangeNativeLayout(newLayout => this.updateLayout(newLayout));
40 const initialLayout = await this.layoutProvider.getNativeLayout();
41 this.updateLayout(initialLayout);
42 }
43 /**
44 * Resolve a KeyCode of a keybinding using the current keyboard layout.
45 * If no keyboard layout has been detected or the layout does not contain the
46 * key used in the KeyCode, the KeyCode is returned unchanged.
47 */
48 resolveKeyCode(inCode) {
49 const layout = this.currentLayout;
50 if (layout && inCode.key) {
51 for (let shift = 0; shift <= 1; shift++) {
52 const index = this.getCharacterIndex(inCode.key, !!shift);
53 const mappedCode = layout.key2KeyCode[index];
54 if (mappedCode) {
55 const transformed = this.transformKeyCode(inCode, mappedCode, !!shift);
56 if (transformed) {
57 return transformed;
58 }
59 }
60 }
61 }
62 return inCode;
63 }
64 /**
65 * Return the character shown on the user's keyboard for the given key.
66 * Use this to determine UI representations of keybindings.
67 */
68 getKeyboardCharacter(key) {
69 var _a;
70 const layout = this.currentLayout;
71 if (layout) {
72 const value = (_a = layout.code2Character[key.code]) === null || _a === void 0 ? void 0 : _a.trim();
73 // Special cases from native keymap
74 if (value === '\u001b') {
75 return 'escape';
76 }
77 if (value === '\u007f') {
78 return 'delete';
79 }
80 if (value === '\u0008') {
81 return 'backspace';
82 }
83 if (value === null || value === void 0 ? void 0 : value.replace(/[\n\r\t]/g, '')) {
84 return value;
85 }
86 }
87 return key.easyString;
88 }
89 /**
90 * Called when a KeyboardEvent is processed by the KeybindingRegistry.
91 * The KeyValidator may trigger a keyboard layout change.
92 */
93 validateKeyCode(keyCode) {
94 if (this.keyValidator && keyCode.key && keyCode.character) {
95 this.keyValidator.validateKey({
96 code: keyCode.key.code,
97 character: keyCode.character,
98 shiftKey: keyCode.shift,
99 ctrlKey: keyCode.ctrl,
100 altKey: keyCode.alt
101 });
102 }
103 }
104 transformKeyCode(inCode, mappedCode, keyNeedsShift) {
105 if (!inCode.shift && keyNeedsShift) {
106 return undefined;
107 }
108 if (mappedCode.alt && (inCode.alt || inCode.ctrl || inCode.shift && !keyNeedsShift)) {
109 return undefined;
110 }
111 return new keys_1.KeyCode({
112 key: mappedCode.key,
113 meta: inCode.meta,
114 ctrl: inCode.ctrl || mappedCode.alt,
115 shift: inCode.shift && !keyNeedsShift || mappedCode.shift,
116 alt: inCode.alt || mappedCode.alt
117 });
118 }
119 transformNativeLayout(nativeLayout) {
120 const key2KeyCode = new Array(2 * (keys_1.Key.MAX_KEY_CODE + 1));
121 const code2Character = {};
122 const mapping = nativeLayout.mapping;
123 for (const code in mapping) {
124 if (mapping.hasOwnProperty(code)) {
125 const keyMapping = mapping[code];
126 const mappedKey = keys_1.Key.getKey(code);
127 if (mappedKey && this.shouldIncludeKey(code)) {
128 if (os_1.isWindows) {
129 this.addWindowsKeyMapping(key2KeyCode, mappedKey, keyMapping.vkey, keyMapping.value);
130 }
131 else {
132 if (keyMapping.value) {
133 this.addKeyMapping(key2KeyCode, mappedKey, keyMapping.value, false, false);
134 }
135 if (keyMapping.withShift) {
136 this.addKeyMapping(key2KeyCode, mappedKey, keyMapping.withShift, true, false);
137 }
138 if (keyMapping.withAltGr) {
139 this.addKeyMapping(key2KeyCode, mappedKey, keyMapping.withAltGr, false, true);
140 }
141 if (keyMapping.withShiftAltGr) {
142 this.addKeyMapping(key2KeyCode, mappedKey, keyMapping.withShiftAltGr, true, true);
143 }
144 }
145 }
146 if (keyMapping.value) {
147 code2Character[code] = keyMapping.value;
148 }
149 }
150 }
151 return { key2KeyCode, code2Character };
152 }
153 shouldIncludeKey(code) {
154 // Exclude all numpad keys because they produce values that are already found elsewhere on the keyboard.
155 // This can cause problems, e.g. if `Numpad3` maps to `PageDown` then commands bound to `PageDown` would
156 // be resolved to `Digit3` (`Numpad3` is associated with `Key.DIGIT3`), effectively blocking the user
157 // from typing `3` in an editor.
158 return !code.startsWith('Numpad');
159 }
160 addKeyMapping(key2KeyCode, mappedKey, value, shift, alt) {
161 const key = VALUE_TO_KEY[value];
162 if (key) {
163 const index = this.getCharacterIndex(key.key, key.shift);
164 if (key2KeyCode[index] === undefined) {
165 key2KeyCode[index] = new keys_1.KeyCode({
166 key: mappedKey,
167 shift,
168 alt,
169 character: value
170 });
171 }
172 }
173 }
174 addWindowsKeyMapping(key2KeyCode, mappedKey, vkey, value) {
175 const key = VKEY_TO_KEY[vkey];
176 if (key) {
177 const index = this.getCharacterIndex(key);
178 if (key2KeyCode[index] === undefined) {
179 key2KeyCode[index] = new keys_1.KeyCode({
180 key: mappedKey,
181 character: value
182 });
183 }
184 }
185 }
186 getCharacterIndex(key, shift) {
187 if (shift) {
188 return keys_1.Key.MAX_KEY_CODE + key.keyCode + 1;
189 }
190 else {
191 return key.keyCode;
192 }
193 }
194};
195(0, tslib_1.__decorate)([
196 (0, inversify_1.inject)(keyboard_layout_provider_1.KeyboardLayoutProvider),
197 (0, tslib_1.__metadata)("design:type", Object)
198], KeyboardLayoutService.prototype, "layoutProvider", void 0);
199(0, tslib_1.__decorate)([
200 (0, inversify_1.inject)(keyboard_layout_provider_1.KeyboardLayoutChangeNotifier),
201 (0, tslib_1.__metadata)("design:type", Object)
202], KeyboardLayoutService.prototype, "layoutChangeNotifier", void 0);
203(0, tslib_1.__decorate)([
204 (0, inversify_1.inject)(keyboard_layout_provider_1.KeyValidator),
205 (0, inversify_1.optional)(),
206 (0, tslib_1.__metadata)("design:type", Object)
207], KeyboardLayoutService.prototype, "keyValidator", void 0);
208KeyboardLayoutService = (0, tslib_1.__decorate)([
209 (0, inversify_1.injectable)()
210], KeyboardLayoutService);
211exports.KeyboardLayoutService = KeyboardLayoutService;
212/**
213 * Mapping of character values to the corresponding keys on a standard US keyboard layout.
214 */
215const VALUE_TO_KEY = {
216 '`': { key: keys_1.Key.BACKQUOTE },
217 '~': { key: keys_1.Key.BACKQUOTE, shift: true },
218 '1': { key: keys_1.Key.DIGIT1 },
219 '!': { key: keys_1.Key.DIGIT1, shift: true },
220 '2': { key: keys_1.Key.DIGIT2 },
221 '@': { key: keys_1.Key.DIGIT2, shift: true },
222 '3': { key: keys_1.Key.DIGIT3 },
223 '#': { key: keys_1.Key.DIGIT3, shift: true },
224 '4': { key: keys_1.Key.DIGIT4 },
225 '$': { key: keys_1.Key.DIGIT4, shift: true },
226 '5': { key: keys_1.Key.DIGIT5 },
227 '%': { key: keys_1.Key.DIGIT5, shift: true },
228 '6': { key: keys_1.Key.DIGIT6 },
229 '^': { key: keys_1.Key.DIGIT6, shift: true },
230 '7': { key: keys_1.Key.DIGIT7 },
231 '&': { key: keys_1.Key.DIGIT7, shift: true },
232 '8': { key: keys_1.Key.DIGIT8 },
233 '*': { key: keys_1.Key.DIGIT8, shift: true },
234 '9': { key: keys_1.Key.DIGIT9 },
235 '(': { key: keys_1.Key.DIGIT9, shift: true },
236 '0': { key: keys_1.Key.DIGIT0 },
237 ')': { key: keys_1.Key.DIGIT0, shift: true },
238 '-': { key: keys_1.Key.MINUS },
239 '_': { key: keys_1.Key.MINUS, shift: true },
240 '=': { key: keys_1.Key.EQUAL },
241 '+': { key: keys_1.Key.EQUAL, shift: true },
242 'a': { key: keys_1.Key.KEY_A },
243 'A': { key: keys_1.Key.KEY_A, shift: true },
244 'b': { key: keys_1.Key.KEY_B },
245 'B': { key: keys_1.Key.KEY_B, shift: true },
246 'c': { key: keys_1.Key.KEY_C },
247 'C': { key: keys_1.Key.KEY_C, shift: true },
248 'd': { key: keys_1.Key.KEY_D },
249 'D': { key: keys_1.Key.KEY_D, shift: true },
250 'e': { key: keys_1.Key.KEY_E },
251 'E': { key: keys_1.Key.KEY_E, shift: true },
252 'f': { key: keys_1.Key.KEY_F },
253 'F': { key: keys_1.Key.KEY_F, shift: true },
254 'g': { key: keys_1.Key.KEY_G },
255 'G': { key: keys_1.Key.KEY_G, shift: true },
256 'h': { key: keys_1.Key.KEY_H },
257 'H': { key: keys_1.Key.KEY_H, shift: true },
258 'i': { key: keys_1.Key.KEY_I },
259 'I': { key: keys_1.Key.KEY_I, shift: true },
260 'j': { key: keys_1.Key.KEY_J },
261 'J': { key: keys_1.Key.KEY_J, shift: true },
262 'k': { key: keys_1.Key.KEY_K },
263 'K': { key: keys_1.Key.KEY_K, shift: true },
264 'l': { key: keys_1.Key.KEY_L },
265 'L': { key: keys_1.Key.KEY_L, shift: true },
266 'm': { key: keys_1.Key.KEY_M },
267 'M': { key: keys_1.Key.KEY_M, shift: true },
268 'n': { key: keys_1.Key.KEY_N },
269 'N': { key: keys_1.Key.KEY_N, shift: true },
270 'o': { key: keys_1.Key.KEY_O },
271 'O': { key: keys_1.Key.KEY_O, shift: true },
272 'p': { key: keys_1.Key.KEY_P },
273 'P': { key: keys_1.Key.KEY_P, shift: true },
274 'q': { key: keys_1.Key.KEY_Q },
275 'Q': { key: keys_1.Key.KEY_Q, shift: true },
276 'r': { key: keys_1.Key.KEY_R },
277 'R': { key: keys_1.Key.KEY_R, shift: true },
278 's': { key: keys_1.Key.KEY_S },
279 'S': { key: keys_1.Key.KEY_S, shift: true },
280 't': { key: keys_1.Key.KEY_T },
281 'T': { key: keys_1.Key.KEY_T, shift: true },
282 'u': { key: keys_1.Key.KEY_U },
283 'U': { key: keys_1.Key.KEY_U, shift: true },
284 'v': { key: keys_1.Key.KEY_V },
285 'V': { key: keys_1.Key.KEY_V, shift: true },
286 'w': { key: keys_1.Key.KEY_W },
287 'W': { key: keys_1.Key.KEY_W, shift: true },
288 'x': { key: keys_1.Key.KEY_X },
289 'X': { key: keys_1.Key.KEY_X, shift: true },
290 'y': { key: keys_1.Key.KEY_Y },
291 'Y': { key: keys_1.Key.KEY_Y, shift: true },
292 'z': { key: keys_1.Key.KEY_Z },
293 'Z': { key: keys_1.Key.KEY_Z, shift: true },
294 '[': { key: keys_1.Key.BRACKET_LEFT },
295 '{': { key: keys_1.Key.BRACKET_LEFT, shift: true },
296 ']': { key: keys_1.Key.BRACKET_RIGHT },
297 '}': { key: keys_1.Key.BRACKET_RIGHT, shift: true },
298 ';': { key: keys_1.Key.SEMICOLON },
299 ':': { key: keys_1.Key.SEMICOLON, shift: true },
300 "'": { key: keys_1.Key.QUOTE },
301 '"': { key: keys_1.Key.QUOTE, shift: true },
302 ',': { key: keys_1.Key.COMMA },
303 '<': { key: keys_1.Key.COMMA, shift: true },
304 '.': { key: keys_1.Key.PERIOD },
305 '>': { key: keys_1.Key.PERIOD, shift: true },
306 '/': { key: keys_1.Key.SLASH },
307 '?': { key: keys_1.Key.SLASH, shift: true },
308 '\\': { key: keys_1.Key.BACKSLASH },
309 '|': { key: keys_1.Key.BACKSLASH, shift: true },
310 '\t': { key: keys_1.Key.TAB },
311 '\r': { key: keys_1.Key.ENTER },
312 '\n': { key: keys_1.Key.ENTER },
313 ' ': { key: keys_1.Key.SPACE },
314};
315/**
316 * Mapping of Windows Virtual Keys to the corresponding keys on a standard US keyboard layout.
317 */
318const VKEY_TO_KEY = {
319 VK_SHIFT: keys_1.Key.SHIFT_LEFT,
320 VK_LSHIFT: keys_1.Key.SHIFT_LEFT,
321 VK_RSHIFT: keys_1.Key.SHIFT_RIGHT,
322 VK_CONTROL: keys_1.Key.CONTROL_LEFT,
323 VK_LCONTROL: keys_1.Key.CONTROL_LEFT,
324 VK_RCONTROL: keys_1.Key.CONTROL_RIGHT,
325 VK_MENU: keys_1.Key.ALT_LEFT,
326 VK_COMMAND: keys_1.Key.OS_LEFT,
327 VK_LWIN: keys_1.Key.OS_LEFT,
328 VK_RWIN: keys_1.Key.OS_RIGHT,
329 VK_0: keys_1.Key.DIGIT0,
330 VK_1: keys_1.Key.DIGIT1,
331 VK_2: keys_1.Key.DIGIT2,
332 VK_3: keys_1.Key.DIGIT3,
333 VK_4: keys_1.Key.DIGIT4,
334 VK_5: keys_1.Key.DIGIT5,
335 VK_6: keys_1.Key.DIGIT6,
336 VK_7: keys_1.Key.DIGIT7,
337 VK_8: keys_1.Key.DIGIT8,
338 VK_9: keys_1.Key.DIGIT9,
339 VK_A: keys_1.Key.KEY_A,
340 VK_B: keys_1.Key.KEY_B,
341 VK_C: keys_1.Key.KEY_C,
342 VK_D: keys_1.Key.KEY_D,
343 VK_E: keys_1.Key.KEY_E,
344 VK_F: keys_1.Key.KEY_F,
345 VK_G: keys_1.Key.KEY_G,
346 VK_H: keys_1.Key.KEY_H,
347 VK_I: keys_1.Key.KEY_I,
348 VK_J: keys_1.Key.KEY_J,
349 VK_K: keys_1.Key.KEY_K,
350 VK_L: keys_1.Key.KEY_L,
351 VK_M: keys_1.Key.KEY_M,
352 VK_N: keys_1.Key.KEY_N,
353 VK_O: keys_1.Key.KEY_O,
354 VK_P: keys_1.Key.KEY_P,
355 VK_Q: keys_1.Key.KEY_Q,
356 VK_R: keys_1.Key.KEY_R,
357 VK_S: keys_1.Key.KEY_S,
358 VK_T: keys_1.Key.KEY_T,
359 VK_U: keys_1.Key.KEY_U,
360 VK_V: keys_1.Key.KEY_V,
361 VK_W: keys_1.Key.KEY_W,
362 VK_X: keys_1.Key.KEY_X,
363 VK_Y: keys_1.Key.KEY_Y,
364 VK_Z: keys_1.Key.KEY_Z,
365 VK_OEM_1: keys_1.Key.SEMICOLON,
366 VK_OEM_2: keys_1.Key.SLASH,
367 VK_OEM_3: keys_1.Key.BACKQUOTE,
368 VK_OEM_4: keys_1.Key.BRACKET_LEFT,
369 VK_OEM_5: keys_1.Key.BACKSLASH,
370 VK_OEM_6: keys_1.Key.BRACKET_RIGHT,
371 VK_OEM_7: keys_1.Key.QUOTE,
372 VK_OEM_PLUS: keys_1.Key.EQUAL,
373 VK_OEM_COMMA: keys_1.Key.COMMA,
374 VK_OEM_MINUS: keys_1.Key.MINUS,
375 VK_OEM_PERIOD: keys_1.Key.PERIOD,
376 VK_F1: keys_1.Key.F1,
377 VK_F2: keys_1.Key.F2,
378 VK_F3: keys_1.Key.F3,
379 VK_F4: keys_1.Key.F4,
380 VK_F5: keys_1.Key.F5,
381 VK_F6: keys_1.Key.F6,
382 VK_F7: keys_1.Key.F7,
383 VK_F8: keys_1.Key.F8,
384 VK_F9: keys_1.Key.F9,
385 VK_F10: keys_1.Key.F10,
386 VK_F11: keys_1.Key.F11,
387 VK_F12: keys_1.Key.F12,
388 VK_F13: keys_1.Key.F13,
389 VK_F14: keys_1.Key.F14,
390 VK_F15: keys_1.Key.F15,
391 VK_F16: keys_1.Key.F16,
392 VK_F17: keys_1.Key.F17,
393 VK_F18: keys_1.Key.F18,
394 VK_F19: keys_1.Key.F19,
395 VK_BACK: keys_1.Key.BACKSPACE,
396 VK_TAB: keys_1.Key.TAB,
397 VK_RETURN: keys_1.Key.ENTER,
398 VK_CAPITAL: keys_1.Key.CAPS_LOCK,
399 VK_ESCAPE: keys_1.Key.ESCAPE,
400 VK_SPACE: keys_1.Key.SPACE,
401 VK_PRIOR: keys_1.Key.PAGE_UP,
402 VK_NEXT: keys_1.Key.PAGE_DOWN,
403 VK_END: keys_1.Key.END,
404 VK_HOME: keys_1.Key.HOME,
405 VK_INSERT: keys_1.Key.INSERT,
406 VK_DELETE: keys_1.Key.DELETE,
407 VK_LEFT: keys_1.Key.ARROW_LEFT,
408 VK_UP: keys_1.Key.ARROW_UP,
409 VK_RIGHT: keys_1.Key.ARROW_RIGHT,
410 VK_DOWN: keys_1.Key.ARROW_DOWN,
411 VK_NUMLOCK: keys_1.Key.NUM_LOCK,
412 VK_NUMPAD0: keys_1.Key.DIGIT0,
413 VK_NUMPAD1: keys_1.Key.DIGIT1,
414 VK_NUMPAD2: keys_1.Key.DIGIT2,
415 VK_NUMPAD3: keys_1.Key.DIGIT3,
416 VK_NUMPAD4: keys_1.Key.DIGIT4,
417 VK_NUMPAD5: keys_1.Key.DIGIT5,
418 VK_NUMPAD6: keys_1.Key.DIGIT6,
419 VK_NUMPAD7: keys_1.Key.DIGIT7,
420 VK_NUMPAD8: keys_1.Key.DIGIT8,
421 VK_NUMPAD9: keys_1.Key.DIGIT9,
422 VK_MULTIPLY: keys_1.Key.MULTIPLY,
423 VK_ADD: keys_1.Key.ADD,
424 VK_SUBTRACT: keys_1.Key.SUBTRACT,
425 VK_DECIMAL: keys_1.Key.DECIMAL,
426 VK_DIVIDE: keys_1.Key.DIVIDE
427};
428//# sourceMappingURL=keyboard-layout-service.js.map
\No newline at end of file