|
| 1 | +/*--------------------------------------------------------------------------------------------- |
| 2 | + * Copyright (c) Microsoft Corporation. All rights reserved. |
| 3 | + * Licensed under the MIT License. See License.txt in the project root for license information. |
| 4 | + *--------------------------------------------------------------------------------------------*/ |
| 5 | + |
| 6 | +import { IKeyboardLayoutInfo } from 'vs/workbench/services/keybinding/common/keymapService'; |
| 7 | +import { isWindows } from 'vs/base/common/platform'; |
| 8 | + |
| 9 | +function deserializeMapping(serializedMapping: ISerializedMapping) { |
| 10 | + let mapping = serializedMapping; |
| 11 | + |
| 12 | + let ret = {}; |
| 13 | + for (let key in mapping) { |
| 14 | + let result: (string | number)[] = mapping[key]; |
| 15 | + if (result.length) { |
| 16 | + let value = result[0]; |
| 17 | + let withShift = result[1]; |
| 18 | + let withAltGr = result[2]; |
| 19 | + let withShiftAltGr = result[3]; |
| 20 | + let mask = Number(result[4]); |
| 21 | + let vkey = result.length === 6 ? result[5] : undefined; |
| 22 | + ret[key] = { |
| 23 | + 'value': value, |
| 24 | + 'vkey': vkey, |
| 25 | + 'withShift': withShift, |
| 26 | + 'withAltGr': withAltGr, |
| 27 | + 'withShiftAltGr': withShiftAltGr, |
| 28 | + 'valueIsDeadKey': (mask & 1) > 0, |
| 29 | + 'withShiftIsDeadKey': (mask & 2) > 0, |
| 30 | + 'withAltGrIsDeadKey': (mask & 4) > 0, |
| 31 | + 'withShiftAltGrIsDeadKey': (mask & 8) > 0 |
| 32 | + }; |
| 33 | + } else { |
| 34 | + ret[key] = { |
| 35 | + 'value': '', |
| 36 | + 'valueIsDeadKey': false, |
| 37 | + 'withShift': '', |
| 38 | + 'withShiftIsDeadKey': false, |
| 39 | + 'withAltGr': '', |
| 40 | + 'withAltGrIsDeadKey': false, |
| 41 | + 'withShiftAltGr': '', |
| 42 | + 'withShiftAltGrIsDeadKey': false |
| 43 | + }; |
| 44 | + } |
| 45 | + } |
| 46 | + |
| 47 | + return ret; |
| 48 | +} |
| 49 | + |
| 50 | +interface IKeyboardMapping { |
| 51 | + [key: string]: { |
| 52 | + value: string, |
| 53 | + withShift: string; |
| 54 | + withAltGr: string; |
| 55 | + withShiftAltGr: string; |
| 56 | + valueIsDeadKey?: boolean; |
| 57 | + withShiftIsDeadKey?: boolean; |
| 58 | + withAltGrIsDeadKey?: boolean; |
| 59 | + withShiftAltGrIsDeadKey?: boolean; |
| 60 | + |
| 61 | + }; |
| 62 | +} |
| 63 | + |
| 64 | +interface ISerializedMapping { |
| 65 | + [key: string]: (string | number)[]; |
| 66 | +} |
| 67 | + |
| 68 | +export class KeyboardLayoutInfo { |
| 69 | + value: IKeyboardMapping; |
| 70 | + |
| 71 | + constructor(public layout: IKeyboardLayoutInfo, public secondaryLayouts: IKeyboardLayoutInfo[], keyboardMapping: ISerializedMapping) { |
| 72 | + this.value = deserializeMapping(keyboardMapping); |
| 73 | + } |
| 74 | + |
| 75 | + fuzzyEqual(other: IKeyboardMapping): boolean { |
| 76 | + for (let key in other) { |
| 77 | + if (isWindows && (key === 'Backslash' || key === 'KeyQ')) { |
| 78 | + // keymap from Chromium is probably wrong. |
| 79 | + continue; |
| 80 | + } |
| 81 | + if (this.value[key] === undefined) { |
| 82 | + return false; |
| 83 | + } |
| 84 | + |
| 85 | + let currentMapping = this.value[key]; |
| 86 | + let otherMapping = other[key]; |
| 87 | + |
| 88 | + if (currentMapping.value !== otherMapping.value) { |
| 89 | + return false; |
| 90 | + } |
| 91 | + } |
| 92 | + |
| 93 | + return true; |
| 94 | + } |
| 95 | +} |
| 96 | + |
| 97 | +export const EN_US = new KeyboardLayoutInfo( |
| 98 | + { id: 'com.apple.keylayout.US', lang: 'en' }, |
| 99 | + [], |
| 100 | + { |
| 101 | + KeyA: ['a', 'A', 'å', 'Å', 0], |
| 102 | + KeyB: ['b', 'B', '∫', 'ı', 0], |
| 103 | + KeyC: ['c', 'C', 'ç', 'Ç', 0], |
| 104 | + KeyD: ['d', 'D', '∂', 'Î', 0], |
| 105 | + KeyE: ['e', 'E', '´', '´', 4], |
| 106 | + KeyF: ['f', 'F', 'ƒ', 'Ï', 0], |
| 107 | + KeyG: ['g', 'G', '©', '˝', 0], |
| 108 | + KeyH: ['h', 'H', '˙', 'Ó', 0], |
| 109 | + KeyI: ['i', 'I', 'ˆ', 'ˆ', 4], |
| 110 | + KeyJ: ['j', 'J', '∆', 'Ô', 0], |
| 111 | + KeyK: ['k', 'K', '˚', '', 0], |
| 112 | + KeyL: ['l', 'L', '¬', 'Ò', 0], |
| 113 | + KeyM: ['m', 'M', 'µ', 'Â', 0], |
| 114 | + KeyN: ['n', 'N', '˜', '˜', 4], |
| 115 | + KeyO: ['o', 'O', 'ø', 'Ø', 0], |
| 116 | + KeyP: ['p', 'P', 'π', '∏', 0], |
| 117 | + KeyQ: ['q', 'Q', 'œ', 'Œ', 0], |
| 118 | + KeyR: ['r', 'R', '®', '‰', 0], |
| 119 | + KeyS: ['s', 'S', 'ß', 'Í', 0], |
| 120 | + KeyT: ['t', 'T', '†', 'ˇ', 0], |
| 121 | + KeyU: ['u', 'U', '¨', '¨', 4], |
| 122 | + KeyV: ['v', 'V', '√', '◊', 0], |
| 123 | + KeyW: ['w', 'W', '∑', '„', 0], |
| 124 | + KeyX: ['x', 'X', '≈', '˛', 0], |
| 125 | + KeyY: ['y', 'Y', '¥', 'Á', 0], |
| 126 | + KeyZ: ['z', 'Z', 'Ω', '¸', 0], |
| 127 | + Digit1: ['1', '!', '¡', '⁄', 0], |
| 128 | + Digit2: ['2', '@', '™', '€', 0], |
| 129 | + Digit3: ['3', '#', '£', '‹', 0], |
| 130 | + Digit4: ['4', '$', '¢', '›', 0], |
| 131 | + Digit5: ['5', '%', '∞', 'fi', 0], |
| 132 | + Digit6: ['6', '^', '§', 'fl', 0], |
| 133 | + Digit7: ['7', '&', '¶', '‡', 0], |
| 134 | + Digit8: ['8', '*', '•', '°', 0], |
| 135 | + Digit9: ['9', '(', 'ª', '·', 0], |
| 136 | + Digit0: ['0', ')', 'º', '‚', 0], |
| 137 | + Enter: [], |
| 138 | + Escape: [], |
| 139 | + Backspace: [], |
| 140 | + Tab: [], |
| 141 | + Space: [' ', ' ', ' ', ' ', 0], |
| 142 | + Minus: ['-', '_', '–', '—', 0], |
| 143 | + Equal: ['=', '+', '≠', '±', 0], |
| 144 | + BracketLeft: ['[', '{', '“', '”', 0], |
| 145 | + BracketRight: [']', '}', '‘', '’', 0], |
| 146 | + Backslash: ['\\', '|', '«', '»', 0], |
| 147 | + Semicolon: [';', ':', '…', 'Ú', 0], |
| 148 | + Quote: ['\'', '"', 'æ', 'Æ', 0], |
| 149 | + Backquote: ['`', '~', '`', '`', 4], |
| 150 | + Comma: [',', '<', '≤', '¯', 0], |
| 151 | + Period: ['.', '>', '≥', '˘', 0], |
| 152 | + Slash: ['/', '?', '÷', '¿', 0], |
| 153 | + CapsLock: [], |
| 154 | + F1: [], |
| 155 | + F2: [], |
| 156 | + F3: [], |
| 157 | + F4: [], |
| 158 | + F5: [], |
| 159 | + F6: [], |
| 160 | + F7: [], |
| 161 | + F8: [], |
| 162 | + F9: [], |
| 163 | + F10: [], |
| 164 | + F11: [], |
| 165 | + F12: [], |
| 166 | + Insert: [], |
| 167 | + Home: [], |
| 168 | + PageUp: [], |
| 169 | + Delete: [], |
| 170 | + End: [], |
| 171 | + PageDown: [], |
| 172 | + ArrowRight: [], |
| 173 | + ArrowLeft: [], |
| 174 | + ArrowDown: [], |
| 175 | + ArrowUp: [], |
| 176 | + NumLock: [], |
| 177 | + NumpadDivide: ['/', '/', '/', '/', 0], |
| 178 | + NumpadMultiply: ['*', '*', '*', '*', 0], |
| 179 | + NumpadSubtract: ['-', '-', '-', '-', 0], |
| 180 | + NumpadAdd: ['+', '+', '+', '+', 0], |
| 181 | + NumpadEnter: [], |
| 182 | + Numpad1: ['1', '1', '1', '1', 0], |
| 183 | + Numpad2: ['2', '2', '2', '2', 0], |
| 184 | + Numpad3: ['3', '3', '3', '3', 0], |
| 185 | + Numpad4: ['4', '4', '4', '4', 0], |
| 186 | + Numpad5: ['5', '5', '5', '5', 0], |
| 187 | + Numpad6: ['6', '6', '6', '6', 0], |
| 188 | + Numpad7: ['7', '7', '7', '7', 0], |
| 189 | + Numpad8: ['8', '8', '8', '8', 0], |
| 190 | + Numpad9: ['9', '9', '9', '9', 0], |
| 191 | + Numpad0: ['0', '0', '0', '0', 0], |
| 192 | + NumpadDecimal: ['.', '.', '.', '.', 0], |
| 193 | + IntlBackslash: ['§', '±', '§', '±', 0], |
| 194 | + ContextMenu: [], |
| 195 | + NumpadEqual: ['=', '=', '=', '=', 0], |
| 196 | + F13: [], |
| 197 | + F14: [], |
| 198 | + F15: [], |
| 199 | + F16: [], |
| 200 | + F17: [], |
| 201 | + F18: [], |
| 202 | + F19: [], |
| 203 | + F20: [], |
| 204 | + AudioVolumeMute: [], |
| 205 | + AudioVolumeUp: ['', '=', '', '=', 0], |
| 206 | + AudioVolumeDown: [], |
| 207 | + NumpadComma: [], |
| 208 | + IntlRo: [], |
| 209 | + KanaMode: [], |
| 210 | + IntlYen: [], |
| 211 | + ControlLeft: [], |
| 212 | + ShiftLeft: [], |
| 213 | + AltLeft: [], |
| 214 | + MetaLeft: [], |
| 215 | + ControlRight: [], |
| 216 | + ShiftRight: [], |
| 217 | + AltRight: [], |
| 218 | + MetaRight: [] |
| 219 | + } |
| 220 | +); |
| 221 | + |
| 222 | +export class KeyboardLayoutProvider { |
| 223 | + public static readonly INSTANCE: KeyboardLayoutProvider = new KeyboardLayoutProvider(); |
| 224 | + |
| 225 | + private _layoutInfos: KeyboardLayoutInfo[] = []; |
| 226 | + private _mru: KeyboardLayoutInfo[] = []; |
| 227 | + private _active: KeyboardLayoutInfo | null; |
| 228 | + |
| 229 | + private constructor() { |
| 230 | + this._active = null; |
| 231 | + } |
| 232 | + |
| 233 | + registerKeyboardLayout(layout: KeyboardLayoutInfo) { |
| 234 | + this._layoutInfos.push(layout); |
| 235 | + this._mru = this._layoutInfos; |
| 236 | + } |
| 237 | + |
| 238 | + get activeKeyboardLayout() { |
| 239 | + return this._active; |
| 240 | + } |
| 241 | + |
| 242 | + isActive(keymap: IKeyboardMapping) { |
| 243 | + return this._active && this._active.fuzzyEqual(keymap); |
| 244 | + } |
| 245 | + |
| 246 | + setActive(keymap: IKeyboardMapping) { |
| 247 | + this._active = this.getMatchedKeyboardLayout(keymap); |
| 248 | + |
| 249 | + if (!this._active) { |
| 250 | + return; |
| 251 | + } |
| 252 | + const index = this._mru.indexOf(this._active); |
| 253 | + |
| 254 | + if (index === 0) { |
| 255 | + return; |
| 256 | + } |
| 257 | + |
| 258 | + this._mru.splice(index, 1); |
| 259 | + this._mru.unshift(this._active); |
| 260 | + } |
| 261 | + |
| 262 | + getMatchedKeyboardLayout(keymap: IKeyboardMapping): KeyboardLayoutInfo | null { |
| 263 | + // TODO go through mru list instead of _layoutInfos |
| 264 | + for (let i = 0; i < this._mru.length; i++) { |
| 265 | + if (this._mru[i].fuzzyEqual(keymap)) { |
| 266 | + return this._mru[i]; |
| 267 | + } |
| 268 | + } |
| 269 | + |
| 270 | + return null; |
| 271 | + } |
| 272 | + |
| 273 | + getKeyboardLayouts(): KeyboardLayoutInfo[] { |
| 274 | + return this._layoutInfos.slice(0); |
| 275 | + } |
| 276 | +} |
0 commit comments