Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/keybinding/browser/keyboardLayoutService.ts
3296 views
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 * as nls from '../../../../nls.js';
7
import { Emitter, Event } from '../../../../base/common/event.js';
8
import { AppResourcePath, FileAccess } from '../../../../base/common/network.js';
9
import { Disposable } from '../../../../base/common/lifecycle.js';
10
import { KeymapInfo, IRawMixedKeyboardMapping, IKeymapInfo } from '../common/keymapInfo.js';
11
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
12
import { DispatchConfig, readKeyboardConfig } from '../../../../platform/keyboardLayout/common/keyboardConfig.js';
13
import { IKeyboardMapper, CachedKeyboardMapper } from '../../../../platform/keyboardLayout/common/keyboardMapper.js';
14
import { OS, OperatingSystem, isMacintosh, isWindows } from '../../../../base/common/platform.js';
15
import { WindowsKeyboardMapper } from '../common/windowsKeyboardMapper.js';
16
import { FallbackKeyboardMapper } from '../common/fallbackKeyboardMapper.js';
17
import { IKeyboardEvent } from '../../../../platform/keybinding/common/keybinding.js';
18
import { MacLinuxKeyboardMapper } from '../common/macLinuxKeyboardMapper.js';
19
import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js';
20
import { URI } from '../../../../base/common/uri.js';
21
import { IFileService } from '../../../../platform/files/common/files.js';
22
import { RunOnceScheduler } from '../../../../base/common/async.js';
23
import { parse, getNodeType } from '../../../../base/common/json.js';
24
import * as objects from '../../../../base/common/objects.js';
25
import { IEnvironmentService } from '../../../../platform/environment/common/environment.js';
26
import { Registry } from '../../../../platform/registry/common/platform.js';
27
import { Extensions as ConfigExtensions, IConfigurationRegistry, IConfigurationNode } from '../../../../platform/configuration/common/configurationRegistry.js';
28
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
29
import { INavigatorWithKeyboard } from './navigatorKeyboard.js';
30
import { INotificationService } from '../../../../platform/notification/common/notification.js';
31
import { ICommandService } from '../../../../platform/commands/common/commands.js';
32
import { IStorageService } from '../../../../platform/storage/common/storage.js';
33
import { getKeyboardLayoutId, IKeyboardLayoutInfo, IKeyboardLayoutService, IKeyboardMapping, IMacLinuxKeyboardMapping, IWindowsKeyboardMapping } from '../../../../platform/keyboardLayout/common/keyboardLayout.js';
34
35
export class BrowserKeyboardMapperFactoryBase extends Disposable {
36
// keyboard mapper
37
protected _initialized: boolean;
38
protected _keyboardMapper: IKeyboardMapper | null;
39
private readonly _onDidChangeKeyboardMapper = new Emitter<void>();
40
public readonly onDidChangeKeyboardMapper: Event<void> = this._onDidChangeKeyboardMapper.event;
41
42
// keymap infos
43
protected _keymapInfos: KeymapInfo[];
44
protected _mru: KeymapInfo[];
45
private _activeKeymapInfo: KeymapInfo | null;
46
private keyboardLayoutMapAllowed: boolean = (navigator as any).keyboard !== undefined;
47
48
get activeKeymap(): KeymapInfo | null {
49
return this._activeKeymapInfo;
50
}
51
52
get keymapInfos(): KeymapInfo[] {
53
return this._keymapInfos;
54
}
55
56
get activeKeyboardLayout(): IKeyboardLayoutInfo | null {
57
if (!this._initialized) {
58
return null;
59
}
60
61
return this._activeKeymapInfo?.layout ?? null;
62
}
63
64
get activeKeyMapping(): IKeyboardMapping | null {
65
if (!this._initialized) {
66
return null;
67
}
68
69
return this._activeKeymapInfo?.mapping ?? null;
70
}
71
72
get keyboardLayouts(): IKeyboardLayoutInfo[] {
73
return this._keymapInfos.map(keymapInfo => keymapInfo.layout);
74
}
75
76
protected constructor(
77
private readonly _configurationService: IConfigurationService,
78
// private _notificationService: INotificationService,
79
// private _storageService: IStorageService,
80
// private _commandService: ICommandService
81
) {
82
super();
83
this._keyboardMapper = null;
84
this._initialized = false;
85
this._keymapInfos = [];
86
this._mru = [];
87
this._activeKeymapInfo = null;
88
89
if ((<INavigatorWithKeyboard>navigator).keyboard && (<INavigatorWithKeyboard>navigator).keyboard.addEventListener) {
90
(<INavigatorWithKeyboard>navigator).keyboard.addEventListener!('layoutchange', () => {
91
// Update user keyboard map settings
92
this._getBrowserKeyMapping().then((mapping: IKeyboardMapping | null) => {
93
if (this.isKeyMappingActive(mapping)) {
94
return;
95
}
96
97
this.setLayoutFromBrowserAPI();
98
});
99
});
100
}
101
102
this._register(this._configurationService.onDidChangeConfiguration((e) => {
103
if (e.affectsConfiguration('keyboard')) {
104
this._keyboardMapper = null;
105
this._onDidChangeKeyboardMapper.fire();
106
}
107
}));
108
}
109
110
registerKeyboardLayout(layout: KeymapInfo) {
111
this._keymapInfos.push(layout);
112
this._mru = this._keymapInfos;
113
}
114
115
removeKeyboardLayout(layout: KeymapInfo): void {
116
let index = this._mru.indexOf(layout);
117
this._mru.splice(index, 1);
118
index = this._keymapInfos.indexOf(layout);
119
this._keymapInfos.splice(index, 1);
120
}
121
122
getMatchedKeymapInfo(keyMapping: IKeyboardMapping | null): { result: KeymapInfo; score: number } | null {
123
if (!keyMapping) {
124
return null;
125
}
126
127
const usStandard = this.getUSStandardLayout();
128
129
if (usStandard) {
130
let maxScore = usStandard.getScore(keyMapping);
131
if (maxScore === 0) {
132
return {
133
result: usStandard,
134
score: 0
135
};
136
}
137
138
let result = usStandard;
139
for (let i = 0; i < this._mru.length; i++) {
140
const score = this._mru[i].getScore(keyMapping);
141
if (score > maxScore) {
142
if (score === 0) {
143
return {
144
result: this._mru[i],
145
score: 0
146
};
147
}
148
149
maxScore = score;
150
result = this._mru[i];
151
}
152
}
153
154
return {
155
result,
156
score: maxScore
157
};
158
}
159
160
for (let i = 0; i < this._mru.length; i++) {
161
if (this._mru[i].fuzzyEqual(keyMapping)) {
162
return {
163
result: this._mru[i],
164
score: 0
165
};
166
}
167
}
168
169
return null;
170
}
171
172
getUSStandardLayout() {
173
const usStandardLayouts = this._mru.filter(layout => layout.layout.isUSStandard);
174
175
if (usStandardLayouts.length) {
176
return usStandardLayouts[0];
177
}
178
179
return null;
180
}
181
182
isKeyMappingActive(keymap: IKeyboardMapping | null) {
183
return this._activeKeymapInfo && keymap && this._activeKeymapInfo.fuzzyEqual(keymap);
184
}
185
186
setUSKeyboardLayout() {
187
this._activeKeymapInfo = this.getUSStandardLayout();
188
}
189
190
setActiveKeyMapping(keymap: IKeyboardMapping | null) {
191
let keymapUpdated = false;
192
const matchedKeyboardLayout = this.getMatchedKeymapInfo(keymap);
193
if (matchedKeyboardLayout) {
194
// let score = matchedKeyboardLayout.score;
195
196
// Due to https://bugs.chromium.org/p/chromium/issues/detail?id=977609, any key after a dead key will generate a wrong mapping,
197
// we shoud avoid yielding the false error.
198
// if (keymap && score < 0) {
199
// const donotAskUpdateKey = 'missing.keyboardlayout.donotask';
200
// if (this._storageService.getBoolean(donotAskUpdateKey, StorageScope.APPLICATION)) {
201
// return;
202
// }
203
204
// the keyboard layout doesn't actually match the key event or the keymap from chromium
205
// this._notificationService.prompt(
206
// Severity.Info,
207
// nls.localize('missing.keyboardlayout', 'Fail to find matching keyboard layout'),
208
// [{
209
// label: nls.localize('keyboardLayoutMissing.configure', "Configure"),
210
// run: () => this._commandService.executeCommand('workbench.action.openKeyboardLayoutPicker')
211
// }, {
212
// label: nls.localize('neverAgain', "Don't Show Again"),
213
// isSecondary: true,
214
// run: () => this._storageService.store(donotAskUpdateKey, true, StorageScope.APPLICATION)
215
// }]
216
// );
217
218
// console.warn('Active keymap/keyevent does not match current keyboard layout', JSON.stringify(keymap), this._activeKeymapInfo ? JSON.stringify(this._activeKeymapInfo.layout) : '');
219
220
// return;
221
// }
222
223
if (!this._activeKeymapInfo) {
224
this._activeKeymapInfo = matchedKeyboardLayout.result;
225
keymapUpdated = true;
226
} else if (keymap) {
227
if (matchedKeyboardLayout.result.getScore(keymap) > this._activeKeymapInfo.getScore(keymap)) {
228
this._activeKeymapInfo = matchedKeyboardLayout.result;
229
keymapUpdated = true;
230
}
231
}
232
}
233
234
if (!this._activeKeymapInfo) {
235
this._activeKeymapInfo = this.getUSStandardLayout();
236
keymapUpdated = true;
237
}
238
239
if (!this._activeKeymapInfo || !keymapUpdated) {
240
return;
241
}
242
243
const index = this._mru.indexOf(this._activeKeymapInfo);
244
245
this._mru.splice(index, 1);
246
this._mru.unshift(this._activeKeymapInfo);
247
248
this._setKeyboardData(this._activeKeymapInfo);
249
}
250
251
setActiveKeymapInfo(keymapInfo: KeymapInfo) {
252
this._activeKeymapInfo = keymapInfo;
253
254
const index = this._mru.indexOf(this._activeKeymapInfo);
255
256
if (index === 0) {
257
return;
258
}
259
260
this._mru.splice(index, 1);
261
this._mru.unshift(this._activeKeymapInfo);
262
263
this._setKeyboardData(this._activeKeymapInfo);
264
}
265
266
public setLayoutFromBrowserAPI(): void {
267
this._updateKeyboardLayoutAsync(this._initialized);
268
}
269
270
private _updateKeyboardLayoutAsync(initialized: boolean, keyboardEvent?: IKeyboardEvent) {
271
if (!initialized) {
272
return;
273
}
274
275
this._getBrowserKeyMapping(keyboardEvent).then(keyMap => {
276
// might be false positive
277
if (this.isKeyMappingActive(keyMap)) {
278
return;
279
}
280
this.setActiveKeyMapping(keyMap);
281
});
282
}
283
284
public getKeyboardMapper(): IKeyboardMapper {
285
const config = readKeyboardConfig(this._configurationService);
286
if (config.dispatch === DispatchConfig.KeyCode || !this._initialized || !this._activeKeymapInfo) {
287
// Forcefully set to use keyCode
288
return new FallbackKeyboardMapper(config.mapAltGrToCtrlAlt, OS);
289
}
290
if (!this._keyboardMapper) {
291
this._keyboardMapper = new CachedKeyboardMapper(BrowserKeyboardMapperFactory._createKeyboardMapper(this._activeKeymapInfo, config.mapAltGrToCtrlAlt));
292
}
293
return this._keyboardMapper;
294
}
295
296
public validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): void {
297
if (!this._initialized) {
298
return;
299
}
300
301
const isCurrentKeyboard = this._validateCurrentKeyboardMapping(keyboardEvent);
302
303
if (isCurrentKeyboard) {
304
return;
305
}
306
307
this._updateKeyboardLayoutAsync(true, keyboardEvent);
308
}
309
310
public setKeyboardLayout(layoutName: string) {
311
const matchedLayouts: KeymapInfo[] = this.keymapInfos.filter(keymapInfo => getKeyboardLayoutId(keymapInfo.layout) === layoutName);
312
313
if (matchedLayouts.length > 0) {
314
this.setActiveKeymapInfo(matchedLayouts[0]);
315
}
316
}
317
318
private _setKeyboardData(keymapInfo: KeymapInfo): void {
319
this._initialized = true;
320
321
this._keyboardMapper = null;
322
this._onDidChangeKeyboardMapper.fire();
323
}
324
325
private static _createKeyboardMapper(keymapInfo: KeymapInfo, mapAltGrToCtrlAlt: boolean): IKeyboardMapper {
326
const rawMapping = keymapInfo.mapping;
327
const isUSStandard = !!keymapInfo.layout.isUSStandard;
328
if (OS === OperatingSystem.Windows) {
329
return new WindowsKeyboardMapper(isUSStandard, <IWindowsKeyboardMapping>rawMapping, mapAltGrToCtrlAlt);
330
}
331
if (Object.keys(rawMapping).length === 0) {
332
// Looks like reading the mappings failed (most likely Mac + Japanese/Chinese keyboard layouts)
333
return new FallbackKeyboardMapper(mapAltGrToCtrlAlt, OS);
334
}
335
336
return new MacLinuxKeyboardMapper(isUSStandard, <IMacLinuxKeyboardMapping>rawMapping, mapAltGrToCtrlAlt, OS);
337
}
338
339
//#region Browser API
340
private _validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): boolean {
341
if (!this._initialized) {
342
return true;
343
}
344
345
const standardKeyboardEvent = keyboardEvent as StandardKeyboardEvent;
346
const currentKeymap = this._activeKeymapInfo;
347
if (!currentKeymap) {
348
return true;
349
}
350
351
if (standardKeyboardEvent.browserEvent.key === 'Dead' || standardKeyboardEvent.browserEvent.isComposing) {
352
return true;
353
}
354
355
const mapping = currentKeymap.mapping[standardKeyboardEvent.code];
356
357
if (!mapping) {
358
return false;
359
}
360
361
if (mapping.value === '') {
362
// The value is empty when the key is not a printable character, we skip validation.
363
if (keyboardEvent.ctrlKey || keyboardEvent.metaKey) {
364
setTimeout(() => {
365
this._getBrowserKeyMapping().then((keymap: IRawMixedKeyboardMapping | null) => {
366
if (this.isKeyMappingActive(keymap)) {
367
return;
368
}
369
370
this.setLayoutFromBrowserAPI();
371
});
372
}, 350);
373
}
374
return true;
375
}
376
377
const expectedValue = standardKeyboardEvent.altKey && standardKeyboardEvent.shiftKey ? mapping.withShiftAltGr :
378
standardKeyboardEvent.altKey ? mapping.withAltGr :
379
standardKeyboardEvent.shiftKey ? mapping.withShift : mapping.value;
380
381
const isDead = (standardKeyboardEvent.altKey && standardKeyboardEvent.shiftKey && mapping.withShiftAltGrIsDeadKey) ||
382
(standardKeyboardEvent.altKey && mapping.withAltGrIsDeadKey) ||
383
(standardKeyboardEvent.shiftKey && mapping.withShiftIsDeadKey) ||
384
mapping.valueIsDeadKey;
385
386
if (isDead && standardKeyboardEvent.browserEvent.key !== 'Dead') {
387
return false;
388
}
389
390
// TODO, this assumption is wrong as `browserEvent.key` doesn't necessarily equal expectedValue from real keymap
391
if (!isDead && standardKeyboardEvent.browserEvent.key !== expectedValue) {
392
return false;
393
}
394
395
return true;
396
}
397
398
private async _getBrowserKeyMapping(keyboardEvent?: IKeyboardEvent): Promise<IRawMixedKeyboardMapping | null> {
399
if (this.keyboardLayoutMapAllowed) {
400
try {
401
return await (navigator as any).keyboard.getLayoutMap().then((e: any) => {
402
const ret: IKeyboardMapping = {};
403
for (const key of e) {
404
ret[key[0]] = {
405
'value': key[1],
406
'withShift': '',
407
'withAltGr': '',
408
'withShiftAltGr': ''
409
};
410
}
411
412
return ret;
413
414
// const matchedKeyboardLayout = this.getMatchedKeymapInfo(ret);
415
416
// if (matchedKeyboardLayout) {
417
// return matchedKeyboardLayout.result.mapping;
418
// }
419
420
// return null;
421
});
422
} catch {
423
// getLayoutMap can throw if invoked from a nested browsing context
424
this.keyboardLayoutMapAllowed = false;
425
}
426
}
427
if (keyboardEvent && !keyboardEvent.shiftKey && !keyboardEvent.altKey && !keyboardEvent.metaKey && !keyboardEvent.metaKey) {
428
const ret: IKeyboardMapping = {};
429
const standardKeyboardEvent = keyboardEvent as StandardKeyboardEvent;
430
ret[standardKeyboardEvent.browserEvent.code] = {
431
'value': standardKeyboardEvent.browserEvent.key,
432
'withShift': '',
433
'withAltGr': '',
434
'withShiftAltGr': ''
435
};
436
437
const matchedKeyboardLayout = this.getMatchedKeymapInfo(ret);
438
439
if (matchedKeyboardLayout) {
440
return ret;
441
}
442
443
return null;
444
}
445
446
return null;
447
}
448
449
//#endregion
450
}
451
452
export class BrowserKeyboardMapperFactory extends BrowserKeyboardMapperFactoryBase {
453
constructor(configurationService: IConfigurationService, notificationService: INotificationService, storageService: IStorageService, commandService: ICommandService) {
454
// super(notificationService, storageService, commandService);
455
super(configurationService);
456
457
const platform = isWindows ? 'win' : isMacintosh ? 'darwin' : 'linux';
458
459
import(FileAccess.asBrowserUri(`vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.${platform}.js` satisfies AppResourcePath).path).then((m) => {
460
const keymapInfos: IKeymapInfo[] = m.KeyboardLayoutContribution.INSTANCE.layoutInfos;
461
this._keymapInfos.push(...keymapInfos.map(info => (new KeymapInfo(info.layout, info.secondaryLayouts, info.mapping, info.isUserKeyboardLayout))));
462
this._mru = this._keymapInfos;
463
this._initialized = true;
464
this.setLayoutFromBrowserAPI();
465
});
466
}
467
}
468
469
class UserKeyboardLayout extends Disposable {
470
471
private readonly reloadConfigurationScheduler: RunOnceScheduler;
472
protected readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
473
readonly onDidChange: Event<void> = this._onDidChange.event;
474
475
private _keyboardLayout: KeymapInfo | null;
476
get keyboardLayout(): KeymapInfo | null { return this._keyboardLayout; }
477
478
constructor(
479
private readonly keyboardLayoutResource: URI,
480
private readonly fileService: IFileService
481
) {
482
super();
483
484
this._keyboardLayout = null;
485
486
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(changed => {
487
if (changed) {
488
this._onDidChange.fire();
489
}
490
}), 50));
491
492
this._register(Event.filter(this.fileService.onDidFilesChange, e => e.contains(this.keyboardLayoutResource))(() => this.reloadConfigurationScheduler.schedule()));
493
}
494
495
async initialize(): Promise<void> {
496
await this.reload();
497
}
498
499
private async reload(): Promise<boolean> {
500
const existing = this._keyboardLayout;
501
try {
502
const content = await this.fileService.readFile(this.keyboardLayoutResource);
503
const value = parse(content.value.toString());
504
if (getNodeType(value) === 'object') {
505
const layoutInfo = value.layout;
506
const mappings = value.rawMapping;
507
this._keyboardLayout = KeymapInfo.createKeyboardLayoutFromDebugInfo(layoutInfo, mappings, true);
508
} else {
509
this._keyboardLayout = null;
510
}
511
} catch (e) {
512
this._keyboardLayout = null;
513
}
514
515
return existing ? !objects.equals(existing, this._keyboardLayout) : true;
516
}
517
518
}
519
520
export class BrowserKeyboardLayoutService extends Disposable implements IKeyboardLayoutService {
521
public _serviceBrand: undefined;
522
523
private readonly _onDidChangeKeyboardLayout = new Emitter<void>();
524
public readonly onDidChangeKeyboardLayout: Event<void> = this._onDidChangeKeyboardLayout.event;
525
526
private _userKeyboardLayout: UserKeyboardLayout;
527
528
private readonly _factory: BrowserKeyboardMapperFactory;
529
private _keyboardLayoutMode: string;
530
531
constructor(
532
@IEnvironmentService environmentService: IEnvironmentService,
533
@IFileService fileService: IFileService,
534
@INotificationService notificationService: INotificationService,
535
@IStorageService storageService: IStorageService,
536
@ICommandService commandService: ICommandService,
537
@IConfigurationService private configurationService: IConfigurationService,
538
) {
539
super();
540
const keyboardConfig = configurationService.getValue<{ layout: string }>('keyboard');
541
const layout = keyboardConfig.layout;
542
this._keyboardLayoutMode = layout ?? 'autodetect';
543
this._factory = new BrowserKeyboardMapperFactory(configurationService, notificationService, storageService, commandService);
544
545
this._register(this._factory.onDidChangeKeyboardMapper(() => {
546
this._onDidChangeKeyboardLayout.fire();
547
}));
548
549
if (layout && layout !== 'autodetect') {
550
// set keyboard layout
551
this._factory.setKeyboardLayout(layout);
552
}
553
554
this._register(configurationService.onDidChangeConfiguration(e => {
555
if (e.affectsConfiguration('keyboard.layout')) {
556
const keyboardConfig = configurationService.getValue<{ layout: string }>('keyboard');
557
const layout = keyboardConfig.layout;
558
this._keyboardLayoutMode = layout;
559
560
if (layout === 'autodetect') {
561
this._factory.setLayoutFromBrowserAPI();
562
} else {
563
this._factory.setKeyboardLayout(layout);
564
}
565
}
566
}));
567
568
this._userKeyboardLayout = new UserKeyboardLayout(environmentService.keyboardLayoutResource, fileService);
569
this._userKeyboardLayout.initialize().then(() => {
570
if (this._userKeyboardLayout.keyboardLayout) {
571
this._factory.registerKeyboardLayout(this._userKeyboardLayout.keyboardLayout);
572
573
this.setUserKeyboardLayoutIfMatched();
574
}
575
});
576
577
this._register(this._userKeyboardLayout.onDidChange(() => {
578
const userKeyboardLayouts = this._factory.keymapInfos.filter(layout => layout.isUserKeyboardLayout);
579
580
if (userKeyboardLayouts.length) {
581
if (this._userKeyboardLayout.keyboardLayout) {
582
userKeyboardLayouts[0].update(this._userKeyboardLayout.keyboardLayout);
583
} else {
584
this._factory.removeKeyboardLayout(userKeyboardLayouts[0]);
585
}
586
} else {
587
if (this._userKeyboardLayout.keyboardLayout) {
588
this._factory.registerKeyboardLayout(this._userKeyboardLayout.keyboardLayout);
589
}
590
}
591
592
this.setUserKeyboardLayoutIfMatched();
593
}));
594
}
595
596
setUserKeyboardLayoutIfMatched() {
597
const keyboardConfig = this.configurationService.getValue<{ layout: string }>('keyboard');
598
const layout = keyboardConfig.layout;
599
600
if (layout && this._userKeyboardLayout.keyboardLayout) {
601
if (getKeyboardLayoutId(this._userKeyboardLayout.keyboardLayout.layout) === layout && this._factory.activeKeymap) {
602
603
if (!this._userKeyboardLayout.keyboardLayout.equal(this._factory.activeKeymap)) {
604
this._factory.setActiveKeymapInfo(this._userKeyboardLayout.keyboardLayout);
605
}
606
}
607
}
608
}
609
610
getKeyboardMapper(): IKeyboardMapper {
611
return this._factory.getKeyboardMapper();
612
}
613
614
public getCurrentKeyboardLayout(): IKeyboardLayoutInfo | null {
615
return this._factory.activeKeyboardLayout;
616
}
617
618
public getAllKeyboardLayouts(): IKeyboardLayoutInfo[] {
619
return this._factory.keyboardLayouts;
620
}
621
622
public getRawKeyboardMapping(): IKeyboardMapping | null {
623
return this._factory.activeKeyMapping;
624
}
625
626
public validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): void {
627
if (this._keyboardLayoutMode !== 'autodetect') {
628
return;
629
}
630
631
this._factory.validateCurrentKeyboardMapping(keyboardEvent);
632
}
633
}
634
635
registerSingleton(IKeyboardLayoutService, BrowserKeyboardLayoutService, InstantiationType.Delayed);
636
637
// Configuration
638
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigExtensions.Configuration);
639
const keyboardConfiguration: IConfigurationNode = {
640
'id': 'keyboard',
641
'order': 15,
642
'type': 'object',
643
'title': nls.localize('keyboardConfigurationTitle', "Keyboard"),
644
'properties': {
645
'keyboard.layout': {
646
'type': 'string',
647
'default': 'autodetect',
648
'description': nls.localize('keyboard.layout.config', "Control the keyboard layout used in web.")
649
}
650
}
651
};
652
653
configurationRegistry.registerConfiguration(keyboardConfiguration);
654
655