Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
export const enum DispatchConfig {
|
||||
Code,
|
||||
KeyCode
|
||||
}
|
||||
|
||||
export function getDispatchConfig(configurationService: IConfigurationService): DispatchConfig {
|
||||
const keyboard = configurationService.getValue('keyboard');
|
||||
const r = (keyboard ? (<any>keyboard).dispatch : null);
|
||||
return (r === 'keyCode' ? DispatchConfig.KeyCode : DispatchConfig.Code);
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { Queue } from 'vs/base/common/async';
|
||||
import * as json from 'vs/base/common/json';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import { setProperty } from 'vs/base/common/jsonEdit';
|
||||
import { Edit } from 'vs/base/common/jsonFormatter';
|
||||
import { Disposable, IReference } from 'vs/base/common/lifecycle';
|
||||
import { isArray } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
|
||||
export const IKeybindingEditingService = createDecorator<IKeybindingEditingService>('keybindingEditingService');
|
||||
|
||||
export interface IKeybindingEditingService {
|
||||
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
editKeybinding(keybindingItem: ResolvedKeybindingItem, key: string, when: string | undefined): Promise<void>;
|
||||
|
||||
removeKeybinding(keybindingItem: ResolvedKeybindingItem): Promise<void>;
|
||||
|
||||
resetKeybinding(keybindingItem: ResolvedKeybindingItem): Promise<void>;
|
||||
}
|
||||
|
||||
export class KeybindingsEditingService extends Disposable implements IKeybindingEditingService {
|
||||
|
||||
public _serviceBrand: undefined;
|
||||
private queue: Queue<void>;
|
||||
|
||||
private resource: URI = this.environmentService.keybindingsResource;
|
||||
|
||||
constructor(
|
||||
@ITextModelService private readonly textModelResolverService: ITextModelService,
|
||||
@ITextFileService private readonly textFileService: ITextFileService,
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService
|
||||
) {
|
||||
super();
|
||||
this.queue = new Queue<void>();
|
||||
}
|
||||
|
||||
editKeybinding(keybindingItem: ResolvedKeybindingItem, key: string, when: string | undefined): Promise<void> {
|
||||
return this.queue.queue(() => this.doEditKeybinding(keybindingItem, key, when)); // queue up writes to prevent race conditions
|
||||
}
|
||||
|
||||
resetKeybinding(keybindingItem: ResolvedKeybindingItem): Promise<void> {
|
||||
return this.queue.queue(() => this.doResetKeybinding(keybindingItem)); // queue up writes to prevent race conditions
|
||||
}
|
||||
|
||||
removeKeybinding(keybindingItem: ResolvedKeybindingItem): Promise<void> {
|
||||
return this.queue.queue(() => this.doRemoveKeybinding(keybindingItem)); // queue up writes to prevent race conditions
|
||||
}
|
||||
|
||||
private doEditKeybinding(keybindingItem: ResolvedKeybindingItem, key: string, when: string | undefined): Promise<void> {
|
||||
return this.resolveAndValidate()
|
||||
.then(reference => {
|
||||
const model = reference.object.textEditorModel;
|
||||
const userKeybindingEntries = <IUserFriendlyKeybinding[]>json.parse(model.getValue());
|
||||
const userKeybindingEntryIndex = this.findUserKeybindingEntryIndex(keybindingItem, userKeybindingEntries);
|
||||
this.updateKeybinding(keybindingItem, key, when, model, userKeybindingEntryIndex);
|
||||
if (keybindingItem.isDefault && keybindingItem.resolvedKeybinding) {
|
||||
this.removeDefaultKeybinding(keybindingItem, model);
|
||||
}
|
||||
return this.save().finally(() => reference.dispose());
|
||||
});
|
||||
}
|
||||
|
||||
private doRemoveKeybinding(keybindingItem: ResolvedKeybindingItem): Promise<void> {
|
||||
return this.resolveAndValidate()
|
||||
.then(reference => {
|
||||
const model = reference.object.textEditorModel;
|
||||
if (keybindingItem.isDefault) {
|
||||
this.removeDefaultKeybinding(keybindingItem, model);
|
||||
} else {
|
||||
this.removeUserKeybinding(keybindingItem, model);
|
||||
}
|
||||
return this.save().finally(() => reference.dispose());
|
||||
});
|
||||
}
|
||||
|
||||
private doResetKeybinding(keybindingItem: ResolvedKeybindingItem): Promise<void> {
|
||||
return this.resolveAndValidate()
|
||||
.then(reference => {
|
||||
const model = reference.object.textEditorModel;
|
||||
if (!keybindingItem.isDefault) {
|
||||
this.removeUserKeybinding(keybindingItem, model);
|
||||
this.removeUnassignedDefaultKeybinding(keybindingItem, model);
|
||||
}
|
||||
return this.save().finally(() => reference.dispose());
|
||||
});
|
||||
}
|
||||
|
||||
private save(): Promise<any> {
|
||||
return this.textFileService.save(this.resource);
|
||||
}
|
||||
|
||||
private updateKeybinding(keybindingItem: ResolvedKeybindingItem, newKey: string, when: string | undefined, model: ITextModel, userKeybindingEntryIndex: number): void {
|
||||
const { tabSize, insertSpaces } = model.getOptions();
|
||||
const eol = model.getEOL();
|
||||
if (userKeybindingEntryIndex !== -1) {
|
||||
// Update the keybinding with new key
|
||||
this.applyEditsToBuffer(setProperty(model.getValue(), [userKeybindingEntryIndex, 'key'], newKey, { tabSize, insertSpaces, eol })[0], model);
|
||||
const edits = setProperty(model.getValue(), [userKeybindingEntryIndex, 'when'], when, { tabSize, insertSpaces, eol });
|
||||
if (edits.length > 0) {
|
||||
this.applyEditsToBuffer(edits[0], model);
|
||||
}
|
||||
} else {
|
||||
// Add the new keybinding with new key
|
||||
this.applyEditsToBuffer(setProperty(model.getValue(), [-1], this.asObject(newKey, keybindingItem.command, when, false), { tabSize, insertSpaces, eol })[0], model);
|
||||
}
|
||||
}
|
||||
|
||||
private removeUserKeybinding(keybindingItem: ResolvedKeybindingItem, model: ITextModel): void {
|
||||
const { tabSize, insertSpaces } = model.getOptions();
|
||||
const eol = model.getEOL();
|
||||
const userKeybindingEntries = <IUserFriendlyKeybinding[]>json.parse(model.getValue());
|
||||
const userKeybindingEntryIndex = this.findUserKeybindingEntryIndex(keybindingItem, userKeybindingEntries);
|
||||
if (userKeybindingEntryIndex !== -1) {
|
||||
this.applyEditsToBuffer(setProperty(model.getValue(), [userKeybindingEntryIndex], undefined, { tabSize, insertSpaces, eol })[0], model);
|
||||
}
|
||||
}
|
||||
|
||||
private removeDefaultKeybinding(keybindingItem: ResolvedKeybindingItem, model: ITextModel): void {
|
||||
const { tabSize, insertSpaces } = model.getOptions();
|
||||
const eol = model.getEOL();
|
||||
const key = keybindingItem.resolvedKeybinding ? keybindingItem.resolvedKeybinding.getUserSettingsLabel() : null;
|
||||
if (key) {
|
||||
const entry: IUserFriendlyKeybinding = this.asObject(key, keybindingItem.command, keybindingItem.when ? keybindingItem.when.serialize() : undefined, true);
|
||||
const userKeybindingEntries = <IUserFriendlyKeybinding[]>json.parse(model.getValue());
|
||||
if (userKeybindingEntries.every(e => !this.areSame(e, entry))) {
|
||||
this.applyEditsToBuffer(setProperty(model.getValue(), [-1], entry, { tabSize, insertSpaces, eol })[0], model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private removeUnassignedDefaultKeybinding(keybindingItem: ResolvedKeybindingItem, model: ITextModel): void {
|
||||
const { tabSize, insertSpaces } = model.getOptions();
|
||||
const eol = model.getEOL();
|
||||
const userKeybindingEntries = <IUserFriendlyKeybinding[]>json.parse(model.getValue());
|
||||
const indices = this.findUnassignedDefaultKeybindingEntryIndex(keybindingItem, userKeybindingEntries).reverse();
|
||||
for (const index of indices) {
|
||||
this.applyEditsToBuffer(setProperty(model.getValue(), [index], undefined, { tabSize, insertSpaces, eol })[0], model);
|
||||
}
|
||||
}
|
||||
|
||||
private findUserKeybindingEntryIndex(keybindingItem: ResolvedKeybindingItem, userKeybindingEntries: IUserFriendlyKeybinding[]): number {
|
||||
for (let index = 0; index < userKeybindingEntries.length; index++) {
|
||||
const keybinding = userKeybindingEntries[index];
|
||||
if (keybinding.command === keybindingItem.command) {
|
||||
if (!keybinding.when && !keybindingItem.when) {
|
||||
return index;
|
||||
}
|
||||
if (keybinding.when && keybindingItem.when) {
|
||||
const contextKeyExpr = ContextKeyExpr.deserialize(keybinding.when);
|
||||
if (contextKeyExpr && contextKeyExpr.serialize() === keybindingItem.when.serialize()) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private findUnassignedDefaultKeybindingEntryIndex(keybindingItem: ResolvedKeybindingItem, userKeybindingEntries: IUserFriendlyKeybinding[]): number[] {
|
||||
const indices: number[] = [];
|
||||
for (let index = 0; index < userKeybindingEntries.length; index++) {
|
||||
if (userKeybindingEntries[index].command === `-${keybindingItem.command}`) {
|
||||
indices.push(index);
|
||||
}
|
||||
}
|
||||
return indices;
|
||||
}
|
||||
|
||||
private asObject(key: string, command: string | null, when: string | undefined, negate: boolean): any {
|
||||
const object: any = { key };
|
||||
if (command) {
|
||||
object['command'] = negate ? `-${command}` : command;
|
||||
}
|
||||
if (when) {
|
||||
object['when'] = when;
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
private areSame(a: IUserFriendlyKeybinding, b: IUserFriendlyKeybinding): boolean {
|
||||
if (a.command !== b.command) {
|
||||
return false;
|
||||
}
|
||||
if (a.key !== b.key) {
|
||||
return false;
|
||||
}
|
||||
const whenA = ContextKeyExpr.deserialize(a.when);
|
||||
const whenB = ContextKeyExpr.deserialize(b.when);
|
||||
if ((whenA && !whenB) || (!whenA && whenB)) {
|
||||
return false;
|
||||
}
|
||||
if (whenA && whenB && !whenA.equals(whenB)) {
|
||||
return false;
|
||||
}
|
||||
if (!objects.equals(a.args, b.args)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private applyEditsToBuffer(edit: Edit, model: ITextModel): void {
|
||||
const startPosition = model.getPositionAt(edit.offset);
|
||||
const endPosition = model.getPositionAt(edit.offset + edit.length);
|
||||
const range = new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column);
|
||||
let currentText = model.getValueInRange(range);
|
||||
const editOperation = currentText ? EditOperation.replace(range, edit.content) : EditOperation.insert(startPosition, edit.content);
|
||||
model.pushEditOperations([new Selection(startPosition.lineNumber, startPosition.column, startPosition.lineNumber, startPosition.column)], [editOperation], () => []);
|
||||
}
|
||||
|
||||
private resolveModelReference(): Promise<IReference<IResolvedTextEditorModel>> {
|
||||
return this.fileService.exists(this.resource)
|
||||
.then(exists => {
|
||||
const EOL = this.configurationService.getValue<{ eol: string }>('files', { overrideIdentifier: 'json' })['eol'];
|
||||
const result: Promise<any> = exists ? Promise.resolve(null) : this.textFileService.write(this.resource, this.getEmptyContent(EOL), { encoding: 'utf8' });
|
||||
return result.then(() => this.textModelResolverService.createModelReference(this.resource));
|
||||
});
|
||||
}
|
||||
|
||||
private resolveAndValidate(): Promise<IReference<IResolvedTextEditorModel>> {
|
||||
|
||||
// Target cannot be dirty if not writing into buffer
|
||||
if (this.textFileService.isDirty(this.resource)) {
|
||||
return Promise.reject(new Error(localize('errorKeybindingsFileDirty', "Unable to write because the keybindings configuration file is dirty. Please save it first and then try again.")));
|
||||
}
|
||||
|
||||
return this.resolveModelReference()
|
||||
.then(reference => {
|
||||
const model = reference.object.textEditorModel;
|
||||
const EOL = model.getEOL();
|
||||
if (model.getValue()) {
|
||||
const parsed = this.parse(model);
|
||||
if (parsed.parseErrors.length) {
|
||||
reference.dispose();
|
||||
return Promise.reject<any>(new Error(localize('parseErrors', "Unable to write to the keybindings configuration file. Please open it to correct errors/warnings in the file and try again.")));
|
||||
}
|
||||
if (parsed.result) {
|
||||
if (!isArray(parsed.result)) {
|
||||
reference.dispose();
|
||||
return Promise.reject<any>(new Error(localize('errorInvalidConfiguration', "Unable to write to the keybindings configuration file. It has an object which is not of type Array. Please open the file to clean up and try again.")));
|
||||
}
|
||||
} else {
|
||||
const content = EOL + '[]';
|
||||
this.applyEditsToBuffer({ content, length: content.length, offset: model.getValue().length }, model);
|
||||
}
|
||||
} else {
|
||||
const content = this.getEmptyContent(EOL);
|
||||
this.applyEditsToBuffer({ content, length: content.length, offset: 0 }, model);
|
||||
}
|
||||
return reference;
|
||||
});
|
||||
}
|
||||
|
||||
private parse(model: ITextModel): { result: IUserFriendlyKeybinding[], parseErrors: json.ParseError[] } {
|
||||
const parseErrors: json.ParseError[] = [];
|
||||
const result = json.parse(model.getValue(), parseErrors, { allowTrailingComma: true, allowEmptyContent: true });
|
||||
return { result, parseErrors };
|
||||
}
|
||||
|
||||
private getEmptyContent(EOL: string): string {
|
||||
return '// ' + localize('emptyKeybindingsHeader', "Place your key bindings in this file to override the defaults") + EOL + '[]';
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IKeybindingEditingService, KeybindingsEditingService, true);
|
||||
@@ -0,0 +1,85 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { SimpleKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { KeybindingParser } from 'vs/base/common/keybindingParser';
|
||||
import { ScanCodeBinding } from 'vs/base/common/scanCode';
|
||||
import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
|
||||
|
||||
export interface IUserKeybindingItem {
|
||||
parts: (SimpleKeybinding | ScanCodeBinding)[];
|
||||
command: string | null;
|
||||
commandArgs?: any;
|
||||
when: ContextKeyExpression | undefined;
|
||||
}
|
||||
|
||||
export class KeybindingIO {
|
||||
|
||||
public static writeKeybindingItem(out: OutputBuilder, item: ResolvedKeybindingItem): void {
|
||||
if (!item.resolvedKeybinding) {
|
||||
return;
|
||||
}
|
||||
let quotedSerializedKeybinding = JSON.stringify(item.resolvedKeybinding.getUserSettingsLabel());
|
||||
out.write(`{ "key": ${rightPaddedString(quotedSerializedKeybinding + ',', 25)} "command": `);
|
||||
|
||||
let quotedSerializedWhen = item.when ? JSON.stringify(item.when.serialize()) : '';
|
||||
let quotedSerializeCommand = JSON.stringify(item.command);
|
||||
if (quotedSerializedWhen.length > 0) {
|
||||
out.write(`${quotedSerializeCommand},`);
|
||||
out.writeLine();
|
||||
out.write(` "when": ${quotedSerializedWhen}`);
|
||||
} else {
|
||||
out.write(`${quotedSerializeCommand}`);
|
||||
}
|
||||
if (item.commandArgs) {
|
||||
out.write(',');
|
||||
out.writeLine();
|
||||
out.write(` "args": ${JSON.stringify(item.commandArgs)}`);
|
||||
}
|
||||
out.write(' }');
|
||||
}
|
||||
|
||||
public static readUserKeybindingItem(input: IUserFriendlyKeybinding): IUserKeybindingItem {
|
||||
const parts = (typeof input.key === 'string' ? KeybindingParser.parseUserBinding(input.key) : []);
|
||||
const when = (typeof input.when === 'string' ? ContextKeyExpr.deserialize(input.when) : undefined);
|
||||
const command = (typeof input.command === 'string' ? input.command : null);
|
||||
const commandArgs = (typeof input.args !== 'undefined' ? input.args : undefined);
|
||||
return {
|
||||
parts: parts,
|
||||
command: command,
|
||||
commandArgs: commandArgs,
|
||||
when: when
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function rightPaddedString(str: string, minChars: number): string {
|
||||
if (str.length < minChars) {
|
||||
return str + (new Array(minChars - str.length).join(' '));
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
export class OutputBuilder {
|
||||
|
||||
private _lines: string[] = [];
|
||||
private _currentLine: string = '';
|
||||
|
||||
write(str: string): void {
|
||||
this._currentLine += str;
|
||||
}
|
||||
|
||||
writeLine(str: string = ''): void {
|
||||
this._lines.push(this._currentLine + str);
|
||||
this._currentLine = '';
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
this.writeLine();
|
||||
return this._lines.join('\n');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Keybinding, ResolvedKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { ScanCodeBinding } from 'vs/base/common/scanCode';
|
||||
import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding';
|
||||
|
||||
export interface IKeyboardMapper {
|
||||
dumpDebugInfo(): string;
|
||||
resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[];
|
||||
resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding;
|
||||
resolveUserBinding(firstPart: (SimpleKeybinding | ScanCodeBinding)[]): ResolvedKeybinding[];
|
||||
}
|
||||
|
||||
export class CachedKeyboardMapper implements IKeyboardMapper {
|
||||
|
||||
private _actual: IKeyboardMapper;
|
||||
private _cache: Map<string, ResolvedKeybinding[]>;
|
||||
|
||||
constructor(actual: IKeyboardMapper) {
|
||||
this._actual = actual;
|
||||
this._cache = new Map<string, ResolvedKeybinding[]>();
|
||||
}
|
||||
|
||||
public dumpDebugInfo(): string {
|
||||
return this._actual.dumpDebugInfo();
|
||||
}
|
||||
|
||||
public resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[] {
|
||||
const hashCode = keybinding.getHashCode();
|
||||
const resolved = this._cache.get(hashCode);
|
||||
if (!resolved) {
|
||||
const r = this._actual.resolveKeybinding(keybinding);
|
||||
this._cache.set(hashCode, r);
|
||||
return r;
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding {
|
||||
return this._actual.resolveKeyboardEvent(keyboardEvent);
|
||||
}
|
||||
|
||||
public resolveUserBinding(parts: (SimpleKeybinding | ScanCodeBinding)[]): ResolvedKeybinding[] {
|
||||
return this._actual.resolveUserBinding(parts);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { isWindows, isLinux } from 'vs/base/common/platform';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { DispatchConfig } from 'vs/workbench/services/keybinding/common/dispatchConfig';
|
||||
import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper';
|
||||
import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding';
|
||||
|
||||
|
||||
export interface IWindowsKeyMapping {
|
||||
vkey: string;
|
||||
value: string;
|
||||
withShift: string;
|
||||
withAltGr: string;
|
||||
withShiftAltGr: string;
|
||||
}
|
||||
export interface IWindowsKeyboardMapping {
|
||||
[code: string]: IWindowsKeyMapping;
|
||||
}
|
||||
export interface ILinuxKeyMapping {
|
||||
value: string;
|
||||
withShift: string;
|
||||
withAltGr: string;
|
||||
withShiftAltGr: string;
|
||||
}
|
||||
export interface ILinuxKeyboardMapping {
|
||||
[code: string]: ILinuxKeyMapping;
|
||||
}
|
||||
export interface IMacKeyMapping {
|
||||
value: string;
|
||||
withShift: string;
|
||||
withAltGr: string;
|
||||
withShiftAltGr: string;
|
||||
valueIsDeadKey: boolean;
|
||||
withShiftIsDeadKey: boolean;
|
||||
withAltGrIsDeadKey: boolean;
|
||||
withShiftAltGrIsDeadKey: boolean;
|
||||
}
|
||||
export interface IMacKeyboardMapping {
|
||||
[code: string]: IMacKeyMapping;
|
||||
}
|
||||
|
||||
export type IKeyboardMapping = IWindowsKeyboardMapping | ILinuxKeyboardMapping | IMacKeyboardMapping;
|
||||
|
||||
/* __GDPR__FRAGMENT__
|
||||
"IKeyboardLayoutInfo" : {
|
||||
"name" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"id": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"text": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
export interface IWindowsKeyboardLayoutInfo {
|
||||
name: string;
|
||||
id: string;
|
||||
text: string;
|
||||
}
|
||||
|
||||
/* __GDPR__FRAGMENT__
|
||||
"IKeyboardLayoutInfo" : {
|
||||
"model" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"layout": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"variant": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"options": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"rules": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
export interface ILinuxKeyboardLayoutInfo {
|
||||
model: string;
|
||||
layout: string;
|
||||
variant: string;
|
||||
options: string;
|
||||
rules: string;
|
||||
}
|
||||
|
||||
/* __GDPR__FRAGMENT__
|
||||
"IKeyboardLayoutInfo" : {
|
||||
"id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"lang": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"localizedName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
|
||||
}
|
||||
*/
|
||||
export interface IMacKeyboardLayoutInfo {
|
||||
id: string;
|
||||
lang: string;
|
||||
localizedName?: string;
|
||||
}
|
||||
|
||||
export type IKeyboardLayoutInfo = (IWindowsKeyboardLayoutInfo | ILinuxKeyboardLayoutInfo | IMacKeyboardLayoutInfo) & { isUserKeyboardLayout?: boolean; isUSStandard?: true };
|
||||
|
||||
export const IKeymapService = createDecorator<IKeymapService>('keymapService');
|
||||
|
||||
export interface IKeymapService {
|
||||
readonly _serviceBrand: undefined;
|
||||
onDidChangeKeyboardMapper: Event<void>;
|
||||
getKeyboardMapper(dispatchConfig: DispatchConfig): IKeyboardMapper;
|
||||
getCurrentKeyboardLayout(): IKeyboardLayoutInfo | null;
|
||||
getAllKeyboardLayouts(): IKeyboardLayoutInfo[];
|
||||
getRawKeyboardMapping(): IKeyboardMapping | null;
|
||||
validateCurrentKeyboardMapping(keyboardEvent: IKeyboardEvent): void;
|
||||
}
|
||||
|
||||
export function areKeyboardLayoutsEqual(a: IKeyboardLayoutInfo | null, b: IKeyboardLayoutInfo | null): boolean {
|
||||
if (!a || !b) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((<IWindowsKeyboardLayoutInfo>a).name && (<IWindowsKeyboardLayoutInfo>b).name && (<IWindowsKeyboardLayoutInfo>a).name === (<IWindowsKeyboardLayoutInfo>b).name) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((<IMacKeyboardLayoutInfo>a).id && (<IMacKeyboardLayoutInfo>b).id && (<IMacKeyboardLayoutInfo>a).id === (<IMacKeyboardLayoutInfo>b).id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((<ILinuxKeyboardLayoutInfo>a).model &&
|
||||
(<ILinuxKeyboardLayoutInfo>b).model &&
|
||||
(<ILinuxKeyboardLayoutInfo>a).model === (<ILinuxKeyboardLayoutInfo>b).model &&
|
||||
(<ILinuxKeyboardLayoutInfo>a).layout === (<ILinuxKeyboardLayoutInfo>b).layout
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function parseKeyboardLayoutDescription(layout: IKeyboardLayoutInfo | null): { label: string, description: string } {
|
||||
if (!layout) {
|
||||
return { label: '', description: '' };
|
||||
}
|
||||
|
||||
if ((<IWindowsKeyboardLayoutInfo>layout).name) {
|
||||
// windows
|
||||
let windowsLayout = <IWindowsKeyboardLayoutInfo>layout;
|
||||
return {
|
||||
label: windowsLayout.text,
|
||||
description: ''
|
||||
};
|
||||
}
|
||||
|
||||
if ((<IMacKeyboardLayoutInfo>layout).id) {
|
||||
let macLayout = <IMacKeyboardLayoutInfo>layout;
|
||||
if (macLayout.localizedName) {
|
||||
return {
|
||||
label: macLayout.localizedName,
|
||||
description: ''
|
||||
};
|
||||
}
|
||||
|
||||
if (/^com\.apple\.keylayout\./.test(macLayout.id)) {
|
||||
return {
|
||||
label: macLayout.id.replace(/^com\.apple\.keylayout\./, '').replace(/-/, ' '),
|
||||
description: ''
|
||||
};
|
||||
}
|
||||
if (/^.*inputmethod\./.test(macLayout.id)) {
|
||||
return {
|
||||
label: macLayout.id.replace(/^.*inputmethod\./, '').replace(/[-\.]/, ' '),
|
||||
description: `Input Method (${macLayout.lang})`
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
label: macLayout.lang,
|
||||
description: ''
|
||||
};
|
||||
}
|
||||
|
||||
let linuxLayout = <ILinuxKeyboardLayoutInfo>layout;
|
||||
|
||||
return {
|
||||
label: linuxLayout.layout,
|
||||
description: ''
|
||||
};
|
||||
}
|
||||
|
||||
export function getKeyboardLayoutId(layout: IKeyboardLayoutInfo): string {
|
||||
if ((<IWindowsKeyboardLayoutInfo>layout).name) {
|
||||
return (<IWindowsKeyboardLayoutInfo>layout).name;
|
||||
}
|
||||
|
||||
if ((<IMacKeyboardLayoutInfo>layout).id) {
|
||||
return (<IMacKeyboardLayoutInfo>layout).id;
|
||||
}
|
||||
|
||||
return (<ILinuxKeyboardLayoutInfo>layout).layout;
|
||||
}
|
||||
|
||||
function deserializeMapping(serializedMapping: ISerializedMapping) {
|
||||
let mapping = serializedMapping;
|
||||
|
||||
let ret: { [key: string]: any } = {};
|
||||
for (let key in mapping) {
|
||||
let result: (string | number)[] = mapping[key];
|
||||
if (result.length) {
|
||||
let value = result[0];
|
||||
let withShift = result[1];
|
||||
let withAltGr = result[2];
|
||||
let withShiftAltGr = result[3];
|
||||
let mask = Number(result[4]);
|
||||
let vkey = result.length === 6 ? result[5] : undefined;
|
||||
ret[key] = {
|
||||
'value': value,
|
||||
'vkey': vkey,
|
||||
'withShift': withShift,
|
||||
'withAltGr': withAltGr,
|
||||
'withShiftAltGr': withShiftAltGr,
|
||||
'valueIsDeadKey': (mask & 1) > 0,
|
||||
'withShiftIsDeadKey': (mask & 2) > 0,
|
||||
'withAltGrIsDeadKey': (mask & 4) > 0,
|
||||
'withShiftAltGrIsDeadKey': (mask & 8) > 0
|
||||
};
|
||||
} else {
|
||||
ret[key] = {
|
||||
'value': '',
|
||||
'valueIsDeadKey': false,
|
||||
'withShift': '',
|
||||
'withShiftIsDeadKey': false,
|
||||
'withAltGr': '',
|
||||
'withAltGrIsDeadKey': false,
|
||||
'withShiftAltGr': '',
|
||||
'withShiftAltGrIsDeadKey': false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export interface IRawMixedKeyboardMapping {
|
||||
[key: string]: {
|
||||
value: string,
|
||||
withShift: string;
|
||||
withAltGr: string;
|
||||
withShiftAltGr: string;
|
||||
valueIsDeadKey?: boolean;
|
||||
withShiftIsDeadKey?: boolean;
|
||||
withAltGrIsDeadKey?: boolean;
|
||||
withShiftAltGrIsDeadKey?: boolean;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
interface ISerializedMapping {
|
||||
[key: string]: (string | number)[];
|
||||
}
|
||||
|
||||
export interface IKeymapInfo {
|
||||
layout: IKeyboardLayoutInfo;
|
||||
secondaryLayouts: IKeyboardLayoutInfo[];
|
||||
mapping: ISerializedMapping;
|
||||
isUserKeyboardLayout?: boolean;
|
||||
}
|
||||
|
||||
export class KeymapInfo {
|
||||
mapping: IRawMixedKeyboardMapping;
|
||||
isUserKeyboardLayout: boolean;
|
||||
|
||||
constructor(public layout: IKeyboardLayoutInfo, public secondaryLayouts: IKeyboardLayoutInfo[], keyboardMapping: ISerializedMapping, isUserKeyboardLayout?: boolean) {
|
||||
this.mapping = deserializeMapping(keyboardMapping);
|
||||
this.isUserKeyboardLayout = !!isUserKeyboardLayout;
|
||||
this.layout.isUserKeyboardLayout = !!isUserKeyboardLayout;
|
||||
}
|
||||
|
||||
static createKeyboardLayoutFromDebugInfo(layout: IKeyboardLayoutInfo, value: IRawMixedKeyboardMapping, isUserKeyboardLayout?: boolean): KeymapInfo {
|
||||
let keyboardLayoutInfo = new KeymapInfo(layout, [], {}, true);
|
||||
keyboardLayoutInfo.mapping = value;
|
||||
return keyboardLayoutInfo;
|
||||
}
|
||||
|
||||
update(other: KeymapInfo) {
|
||||
this.layout = other.layout;
|
||||
this.secondaryLayouts = other.secondaryLayouts;
|
||||
this.mapping = other.mapping;
|
||||
this.isUserKeyboardLayout = other.isUserKeyboardLayout;
|
||||
this.layout.isUserKeyboardLayout = other.isUserKeyboardLayout;
|
||||
}
|
||||
|
||||
getScore(other: IRawMixedKeyboardMapping): number {
|
||||
let score = 0;
|
||||
for (let key in other) {
|
||||
if (isWindows && (key === 'Backslash' || key === 'KeyQ')) {
|
||||
// keymap from Chromium is probably wrong.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isLinux && (key === 'Backspace' || key === 'Escape')) {
|
||||
// native keymap doesn't align with keyboard event
|
||||
continue;
|
||||
}
|
||||
|
||||
let currentMapping = this.mapping[key];
|
||||
|
||||
if (currentMapping === undefined) {
|
||||
score -= 1;
|
||||
}
|
||||
|
||||
let otherMapping = other[key];
|
||||
|
||||
if (currentMapping && otherMapping && currentMapping.value !== otherMapping.value) {
|
||||
score -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
equal(other: KeymapInfo): boolean {
|
||||
if (this.isUserKeyboardLayout !== other.isUserKeyboardLayout) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getKeyboardLayoutId(this.layout) !== getKeyboardLayoutId(other.layout)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.fuzzyEqual(other.mapping);
|
||||
}
|
||||
|
||||
fuzzyEqual(other: IRawMixedKeyboardMapping): boolean {
|
||||
for (let key in other) {
|
||||
if (isWindows && (key === 'Backslash' || key === 'KeyQ')) {
|
||||
// keymap from Chromium is probably wrong.
|
||||
continue;
|
||||
}
|
||||
if (this.mapping[key] === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let currentMapping = this.mapping[key];
|
||||
let otherMapping = other[key];
|
||||
|
||||
if (currentMapping.value !== otherMapping.value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ChordKeybinding, KeyCode, Keybinding, ResolvedKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { OperatingSystem } from 'vs/base/common/platform';
|
||||
import { IMMUTABLE_CODE_TO_KEY_CODE, ScanCode, ScanCodeBinding } from 'vs/base/common/scanCode';
|
||||
import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding';
|
||||
import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper';
|
||||
import { removeElementsAfterNulls } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
|
||||
|
||||
/**
|
||||
* A keyboard mapper to be used when reading the keymap from the OS fails.
|
||||
*/
|
||||
export class MacLinuxFallbackKeyboardMapper implements IKeyboardMapper {
|
||||
|
||||
/**
|
||||
* OS (can be Linux or Macintosh)
|
||||
*/
|
||||
private readonly _OS: OperatingSystem;
|
||||
|
||||
constructor(OS: OperatingSystem) {
|
||||
this._OS = OS;
|
||||
}
|
||||
|
||||
public dumpDebugInfo(): string {
|
||||
return 'FallbackKeyboardMapper dispatching on keyCode';
|
||||
}
|
||||
|
||||
public resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[] {
|
||||
return [new USLayoutResolvedKeybinding(keybinding, this._OS)];
|
||||
}
|
||||
|
||||
public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding {
|
||||
let keybinding = new SimpleKeybinding(
|
||||
keyboardEvent.ctrlKey,
|
||||
keyboardEvent.shiftKey,
|
||||
keyboardEvent.altKey,
|
||||
keyboardEvent.metaKey,
|
||||
keyboardEvent.keyCode
|
||||
);
|
||||
return new USLayoutResolvedKeybinding(keybinding.toChord(), this._OS);
|
||||
}
|
||||
|
||||
private _scanCodeToKeyCode(scanCode: ScanCode): KeyCode {
|
||||
const immutableKeyCode = IMMUTABLE_CODE_TO_KEY_CODE[scanCode];
|
||||
if (immutableKeyCode !== -1) {
|
||||
return immutableKeyCode;
|
||||
}
|
||||
|
||||
switch (scanCode) {
|
||||
case ScanCode.KeyA: return KeyCode.KEY_A;
|
||||
case ScanCode.KeyB: return KeyCode.KEY_B;
|
||||
case ScanCode.KeyC: return KeyCode.KEY_C;
|
||||
case ScanCode.KeyD: return KeyCode.KEY_D;
|
||||
case ScanCode.KeyE: return KeyCode.KEY_E;
|
||||
case ScanCode.KeyF: return KeyCode.KEY_F;
|
||||
case ScanCode.KeyG: return KeyCode.KEY_G;
|
||||
case ScanCode.KeyH: return KeyCode.KEY_H;
|
||||
case ScanCode.KeyI: return KeyCode.KEY_I;
|
||||
case ScanCode.KeyJ: return KeyCode.KEY_J;
|
||||
case ScanCode.KeyK: return KeyCode.KEY_K;
|
||||
case ScanCode.KeyL: return KeyCode.KEY_L;
|
||||
case ScanCode.KeyM: return KeyCode.KEY_M;
|
||||
case ScanCode.KeyN: return KeyCode.KEY_N;
|
||||
case ScanCode.KeyO: return KeyCode.KEY_O;
|
||||
case ScanCode.KeyP: return KeyCode.KEY_P;
|
||||
case ScanCode.KeyQ: return KeyCode.KEY_Q;
|
||||
case ScanCode.KeyR: return KeyCode.KEY_R;
|
||||
case ScanCode.KeyS: return KeyCode.KEY_S;
|
||||
case ScanCode.KeyT: return KeyCode.KEY_T;
|
||||
case ScanCode.KeyU: return KeyCode.KEY_U;
|
||||
case ScanCode.KeyV: return KeyCode.KEY_V;
|
||||
case ScanCode.KeyW: return KeyCode.KEY_W;
|
||||
case ScanCode.KeyX: return KeyCode.KEY_X;
|
||||
case ScanCode.KeyY: return KeyCode.KEY_Y;
|
||||
case ScanCode.KeyZ: return KeyCode.KEY_Z;
|
||||
case ScanCode.Digit1: return KeyCode.KEY_1;
|
||||
case ScanCode.Digit2: return KeyCode.KEY_2;
|
||||
case ScanCode.Digit3: return KeyCode.KEY_3;
|
||||
case ScanCode.Digit4: return KeyCode.KEY_4;
|
||||
case ScanCode.Digit5: return KeyCode.KEY_5;
|
||||
case ScanCode.Digit6: return KeyCode.KEY_6;
|
||||
case ScanCode.Digit7: return KeyCode.KEY_7;
|
||||
case ScanCode.Digit8: return KeyCode.KEY_8;
|
||||
case ScanCode.Digit9: return KeyCode.KEY_9;
|
||||
case ScanCode.Digit0: return KeyCode.KEY_0;
|
||||
case ScanCode.Minus: return KeyCode.US_MINUS;
|
||||
case ScanCode.Equal: return KeyCode.US_EQUAL;
|
||||
case ScanCode.BracketLeft: return KeyCode.US_OPEN_SQUARE_BRACKET;
|
||||
case ScanCode.BracketRight: return KeyCode.US_CLOSE_SQUARE_BRACKET;
|
||||
case ScanCode.Backslash: return KeyCode.US_BACKSLASH;
|
||||
case ScanCode.IntlHash: return KeyCode.Unknown; // missing
|
||||
case ScanCode.Semicolon: return KeyCode.US_SEMICOLON;
|
||||
case ScanCode.Quote: return KeyCode.US_QUOTE;
|
||||
case ScanCode.Backquote: return KeyCode.US_BACKTICK;
|
||||
case ScanCode.Comma: return KeyCode.US_COMMA;
|
||||
case ScanCode.Period: return KeyCode.US_DOT;
|
||||
case ScanCode.Slash: return KeyCode.US_SLASH;
|
||||
case ScanCode.IntlBackslash: return KeyCode.OEM_102;
|
||||
}
|
||||
return KeyCode.Unknown;
|
||||
}
|
||||
|
||||
private _resolveSimpleUserBinding(binding: SimpleKeybinding | ScanCodeBinding | null): SimpleKeybinding | null {
|
||||
if (!binding) {
|
||||
return null;
|
||||
}
|
||||
if (binding instanceof SimpleKeybinding) {
|
||||
return binding;
|
||||
}
|
||||
const keyCode = this._scanCodeToKeyCode(binding.scanCode);
|
||||
if (keyCode === KeyCode.Unknown) {
|
||||
return null;
|
||||
}
|
||||
return new SimpleKeybinding(binding.ctrlKey, binding.shiftKey, binding.altKey, binding.metaKey, keyCode);
|
||||
}
|
||||
|
||||
public resolveUserBinding(input: (SimpleKeybinding | ScanCodeBinding)[]): ResolvedKeybinding[] {
|
||||
const parts: SimpleKeybinding[] = removeElementsAfterNulls(input.map(keybinding => this._resolveSimpleUserBinding(keybinding)));
|
||||
if (parts.length > 0) {
|
||||
return [new USLayoutResolvedKeybinding(new ChordKeybinding(parts), this._OS)];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,684 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { KeyCode, KeyCodeUtils, Keybinding, ResolvedKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { UILabelProvider } from 'vs/base/common/keybindingLabels';
|
||||
import { OperatingSystem } from 'vs/base/common/platform';
|
||||
import { IMMUTABLE_CODE_TO_KEY_CODE, ScanCode, ScanCodeBinding, ScanCodeUtils } from 'vs/base/common/scanCode';
|
||||
import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IKeyboardMapper } from 'vs/workbench/services/keybinding/common/keyboardMapper';
|
||||
import { BaseResolvedKeybinding } from 'vs/platform/keybinding/common/baseResolvedKeybinding';
|
||||
import { removeElementsAfterNulls } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
|
||||
|
||||
export interface IWindowsKeyMapping {
|
||||
vkey: string;
|
||||
value: string;
|
||||
withShift: string;
|
||||
withAltGr: string;
|
||||
withShiftAltGr: string;
|
||||
}
|
||||
|
||||
function windowsKeyMappingEquals(a: IWindowsKeyMapping, b: IWindowsKeyMapping): boolean {
|
||||
if (!a && !b) {
|
||||
return true;
|
||||
}
|
||||
if (!a || !b) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
a.vkey === b.vkey
|
||||
&& a.value === b.value
|
||||
&& a.withShift === b.withShift
|
||||
&& a.withAltGr === b.withAltGr
|
||||
&& a.withShiftAltGr === b.withShiftAltGr
|
||||
);
|
||||
}
|
||||
|
||||
export interface IWindowsKeyboardMapping {
|
||||
[scanCode: string]: IWindowsKeyMapping;
|
||||
}
|
||||
|
||||
export function windowsKeyboardMappingEquals(a: IWindowsKeyboardMapping | null, b: IWindowsKeyboardMapping | null): boolean {
|
||||
if (!a && !b) {
|
||||
return true;
|
||||
}
|
||||
if (!a || !b) {
|
||||
return false;
|
||||
}
|
||||
for (let scanCode = 0; scanCode < ScanCode.MAX_VALUE; scanCode++) {
|
||||
const strScanCode = ScanCodeUtils.toString(scanCode);
|
||||
const aEntry = a[strScanCode];
|
||||
const bEntry = b[strScanCode];
|
||||
if (!windowsKeyMappingEquals(aEntry, bEntry)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const LOG = false;
|
||||
function log(str: string): void {
|
||||
if (LOG) {
|
||||
console.info(str);
|
||||
}
|
||||
}
|
||||
|
||||
const NATIVE_KEY_CODE_TO_KEY_CODE: { [nativeKeyCode: string]: KeyCode; } = _getNativeMap();
|
||||
|
||||
export interface IScanCodeMapping {
|
||||
scanCode: ScanCode;
|
||||
keyCode: KeyCode;
|
||||
value: string;
|
||||
withShift: string;
|
||||
withAltGr: string;
|
||||
withShiftAltGr: string;
|
||||
}
|
||||
|
||||
export class WindowsNativeResolvedKeybinding extends BaseResolvedKeybinding<SimpleKeybinding> {
|
||||
|
||||
private readonly _mapper: WindowsKeyboardMapper;
|
||||
|
||||
constructor(mapper: WindowsKeyboardMapper, parts: SimpleKeybinding[]) {
|
||||
super(OperatingSystem.Windows, parts);
|
||||
this._mapper = mapper;
|
||||
}
|
||||
|
||||
protected _getLabel(keybinding: SimpleKeybinding): string | null {
|
||||
if (keybinding.isDuplicateModifierCase()) {
|
||||
return '';
|
||||
}
|
||||
return this._mapper.getUILabelForKeyCode(keybinding.keyCode);
|
||||
}
|
||||
|
||||
private _getUSLabelForKeybinding(keybinding: SimpleKeybinding): string | null {
|
||||
if (keybinding.isDuplicateModifierCase()) {
|
||||
return '';
|
||||
}
|
||||
return KeyCodeUtils.toString(keybinding.keyCode);
|
||||
}
|
||||
|
||||
public getUSLabel(): string | null {
|
||||
return UILabelProvider.toLabel(this._os, this._parts, (keybinding) => this._getUSLabelForKeybinding(keybinding));
|
||||
}
|
||||
|
||||
protected _getAriaLabel(keybinding: SimpleKeybinding): string | null {
|
||||
if (keybinding.isDuplicateModifierCase()) {
|
||||
return '';
|
||||
}
|
||||
return this._mapper.getAriaLabelForKeyCode(keybinding.keyCode);
|
||||
}
|
||||
|
||||
private _keyCodeToElectronAccelerator(keyCode: KeyCode): string | null {
|
||||
if (keyCode >= KeyCode.NUMPAD_0 && keyCode <= KeyCode.NUMPAD_DIVIDE) {
|
||||
// Electron cannot handle numpad keys
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (keyCode) {
|
||||
case KeyCode.UpArrow:
|
||||
return 'Up';
|
||||
case KeyCode.DownArrow:
|
||||
return 'Down';
|
||||
case KeyCode.LeftArrow:
|
||||
return 'Left';
|
||||
case KeyCode.RightArrow:
|
||||
return 'Right';
|
||||
}
|
||||
|
||||
// electron menus always do the correct rendering on Windows
|
||||
return KeyCodeUtils.toString(keyCode);
|
||||
}
|
||||
|
||||
protected _getElectronAccelerator(keybinding: SimpleKeybinding): string | null {
|
||||
if (keybinding.isDuplicateModifierCase()) {
|
||||
return null;
|
||||
}
|
||||
return this._keyCodeToElectronAccelerator(keybinding.keyCode);
|
||||
}
|
||||
|
||||
protected _getUserSettingsLabel(keybinding: SimpleKeybinding): string | null {
|
||||
if (keybinding.isDuplicateModifierCase()) {
|
||||
return '';
|
||||
}
|
||||
const result = this._mapper.getUserSettingsLabelForKeyCode(keybinding.keyCode);
|
||||
return (result ? result.toLowerCase() : result);
|
||||
}
|
||||
|
||||
protected _isWYSIWYG(keybinding: SimpleKeybinding): boolean {
|
||||
return this.__isWYSIWYG(keybinding.keyCode);
|
||||
}
|
||||
|
||||
private __isWYSIWYG(keyCode: KeyCode): boolean {
|
||||
if (
|
||||
keyCode === KeyCode.LeftArrow
|
||||
|| keyCode === KeyCode.UpArrow
|
||||
|| keyCode === KeyCode.RightArrow
|
||||
|| keyCode === KeyCode.DownArrow
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
const ariaLabel = this._mapper.getAriaLabelForKeyCode(keyCode);
|
||||
const userSettingsLabel = this._mapper.getUserSettingsLabelForKeyCode(keyCode);
|
||||
return (ariaLabel === userSettingsLabel);
|
||||
}
|
||||
|
||||
protected _getDispatchPart(keybinding: SimpleKeybinding): string | null {
|
||||
if (keybinding.isModifierKey()) {
|
||||
return null;
|
||||
}
|
||||
let result = '';
|
||||
|
||||
if (keybinding.ctrlKey) {
|
||||
result += 'ctrl+';
|
||||
}
|
||||
if (keybinding.shiftKey) {
|
||||
result += 'shift+';
|
||||
}
|
||||
if (keybinding.altKey) {
|
||||
result += 'alt+';
|
||||
}
|
||||
if (keybinding.metaKey) {
|
||||
result += 'meta+';
|
||||
}
|
||||
result += KeyCodeUtils.toString(keybinding.keyCode);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static getProducedCharCode(kb: ScanCodeBinding, mapping: IScanCodeMapping): string | null {
|
||||
if (!mapping) {
|
||||
return null;
|
||||
}
|
||||
if (kb.ctrlKey && kb.shiftKey && kb.altKey) {
|
||||
return mapping.withShiftAltGr;
|
||||
}
|
||||
if (kb.ctrlKey && kb.altKey) {
|
||||
return mapping.withAltGr;
|
||||
}
|
||||
if (kb.shiftKey) {
|
||||
return mapping.withShift;
|
||||
}
|
||||
return mapping.value;
|
||||
}
|
||||
|
||||
public static getProducedChar(kb: ScanCodeBinding, mapping: IScanCodeMapping): string {
|
||||
const char = this.getProducedCharCode(kb, mapping);
|
||||
if (char === null || char.length === 0) {
|
||||
return ' --- ';
|
||||
}
|
||||
return ' ' + char + ' ';
|
||||
}
|
||||
}
|
||||
|
||||
export class WindowsKeyboardMapper implements IKeyboardMapper {
|
||||
|
||||
public readonly isUSStandard: boolean;
|
||||
private readonly _codeInfo: IScanCodeMapping[];
|
||||
private readonly _scanCodeToKeyCode: KeyCode[];
|
||||
private readonly _keyCodeToLabel: Array<string | null> = [];
|
||||
private readonly _keyCodeExists: boolean[];
|
||||
|
||||
constructor(isUSStandard: boolean, rawMappings: IWindowsKeyboardMapping) {
|
||||
this.isUSStandard = isUSStandard;
|
||||
this._scanCodeToKeyCode = [];
|
||||
this._keyCodeToLabel = [];
|
||||
this._keyCodeExists = [];
|
||||
this._keyCodeToLabel[KeyCode.Unknown] = KeyCodeUtils.toString(KeyCode.Unknown);
|
||||
|
||||
for (let scanCode = ScanCode.None; scanCode < ScanCode.MAX_VALUE; scanCode++) {
|
||||
const immutableKeyCode = IMMUTABLE_CODE_TO_KEY_CODE[scanCode];
|
||||
if (immutableKeyCode !== -1) {
|
||||
this._scanCodeToKeyCode[scanCode] = immutableKeyCode;
|
||||
this._keyCodeToLabel[immutableKeyCode] = KeyCodeUtils.toString(immutableKeyCode);
|
||||
this._keyCodeExists[immutableKeyCode] = true;
|
||||
}
|
||||
}
|
||||
|
||||
let producesLetter: boolean[] = [];
|
||||
let producesLetters = false;
|
||||
|
||||
this._codeInfo = [];
|
||||
for (let strCode in rawMappings) {
|
||||
if (rawMappings.hasOwnProperty(strCode)) {
|
||||
const scanCode = ScanCodeUtils.toEnum(strCode);
|
||||
if (scanCode === ScanCode.None) {
|
||||
log(`Unknown scanCode ${strCode} in mapping.`);
|
||||
continue;
|
||||
}
|
||||
const rawMapping = rawMappings[strCode];
|
||||
|
||||
const immutableKeyCode = IMMUTABLE_CODE_TO_KEY_CODE[scanCode];
|
||||
if (immutableKeyCode !== -1) {
|
||||
const keyCode = NATIVE_KEY_CODE_TO_KEY_CODE[rawMapping.vkey] || KeyCode.Unknown;
|
||||
if (keyCode === KeyCode.Unknown || immutableKeyCode === keyCode) {
|
||||
continue;
|
||||
}
|
||||
if (scanCode !== ScanCode.NumpadComma) {
|
||||
// Looks like ScanCode.NumpadComma doesn't always map to KeyCode.NUMPAD_SEPARATOR
|
||||
// e.g. on POR - PTB
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const value = rawMapping.value;
|
||||
const withShift = rawMapping.withShift;
|
||||
const withAltGr = rawMapping.withAltGr;
|
||||
const withShiftAltGr = rawMapping.withShiftAltGr;
|
||||
const keyCode = NATIVE_KEY_CODE_TO_KEY_CODE[rawMapping.vkey] || KeyCode.Unknown;
|
||||
|
||||
const mapping: IScanCodeMapping = {
|
||||
scanCode: scanCode,
|
||||
keyCode: keyCode,
|
||||
value: value,
|
||||
withShift: withShift,
|
||||
withAltGr: withAltGr,
|
||||
withShiftAltGr: withShiftAltGr,
|
||||
};
|
||||
this._codeInfo[scanCode] = mapping;
|
||||
this._scanCodeToKeyCode[scanCode] = keyCode;
|
||||
|
||||
if (keyCode === KeyCode.Unknown) {
|
||||
continue;
|
||||
}
|
||||
this._keyCodeExists[keyCode] = true;
|
||||
|
||||
if (value.length === 0) {
|
||||
// This key does not produce strings
|
||||
this._keyCodeToLabel[keyCode] = null;
|
||||
}
|
||||
|
||||
else if (value.length > 1) {
|
||||
// This key produces a letter representable with multiple UTF-16 code units.
|
||||
this._keyCodeToLabel[keyCode] = value;
|
||||
}
|
||||
|
||||
else {
|
||||
const charCode = value.charCodeAt(0);
|
||||
|
||||
if (charCode >= CharCode.a && charCode <= CharCode.z) {
|
||||
const upperCaseValue = CharCode.A + (charCode - CharCode.a);
|
||||
producesLetter[upperCaseValue] = true;
|
||||
producesLetters = true;
|
||||
this._keyCodeToLabel[keyCode] = String.fromCharCode(CharCode.A + (charCode - CharCode.a));
|
||||
}
|
||||
|
||||
else if (charCode >= CharCode.A && charCode <= CharCode.Z) {
|
||||
producesLetter[charCode] = true;
|
||||
producesLetters = true;
|
||||
this._keyCodeToLabel[keyCode] = value;
|
||||
}
|
||||
|
||||
else {
|
||||
this._keyCodeToLabel[keyCode] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle keyboard layouts where latin characters are not produced e.g. Cyrillic
|
||||
const _registerLetterIfMissing = (charCode: CharCode, keyCode: KeyCode): void => {
|
||||
if (!producesLetter[charCode]) {
|
||||
this._keyCodeToLabel[keyCode] = String.fromCharCode(charCode);
|
||||
}
|
||||
};
|
||||
_registerLetterIfMissing(CharCode.A, KeyCode.KEY_A);
|
||||
_registerLetterIfMissing(CharCode.B, KeyCode.KEY_B);
|
||||
_registerLetterIfMissing(CharCode.C, KeyCode.KEY_C);
|
||||
_registerLetterIfMissing(CharCode.D, KeyCode.KEY_D);
|
||||
_registerLetterIfMissing(CharCode.E, KeyCode.KEY_E);
|
||||
_registerLetterIfMissing(CharCode.F, KeyCode.KEY_F);
|
||||
_registerLetterIfMissing(CharCode.G, KeyCode.KEY_G);
|
||||
_registerLetterIfMissing(CharCode.H, KeyCode.KEY_H);
|
||||
_registerLetterIfMissing(CharCode.I, KeyCode.KEY_I);
|
||||
_registerLetterIfMissing(CharCode.J, KeyCode.KEY_J);
|
||||
_registerLetterIfMissing(CharCode.K, KeyCode.KEY_K);
|
||||
_registerLetterIfMissing(CharCode.L, KeyCode.KEY_L);
|
||||
_registerLetterIfMissing(CharCode.M, KeyCode.KEY_M);
|
||||
_registerLetterIfMissing(CharCode.N, KeyCode.KEY_N);
|
||||
_registerLetterIfMissing(CharCode.O, KeyCode.KEY_O);
|
||||
_registerLetterIfMissing(CharCode.P, KeyCode.KEY_P);
|
||||
_registerLetterIfMissing(CharCode.Q, KeyCode.KEY_Q);
|
||||
_registerLetterIfMissing(CharCode.R, KeyCode.KEY_R);
|
||||
_registerLetterIfMissing(CharCode.S, KeyCode.KEY_S);
|
||||
_registerLetterIfMissing(CharCode.T, KeyCode.KEY_T);
|
||||
_registerLetterIfMissing(CharCode.U, KeyCode.KEY_U);
|
||||
_registerLetterIfMissing(CharCode.V, KeyCode.KEY_V);
|
||||
_registerLetterIfMissing(CharCode.W, KeyCode.KEY_W);
|
||||
_registerLetterIfMissing(CharCode.X, KeyCode.KEY_X);
|
||||
_registerLetterIfMissing(CharCode.Y, KeyCode.KEY_Y);
|
||||
_registerLetterIfMissing(CharCode.Z, KeyCode.KEY_Z);
|
||||
|
||||
if (!producesLetters) {
|
||||
// Since this keyboard layout produces no latin letters at all, most of the UI will use the
|
||||
// US kb layout equivalent for UI labels, so also try to render other keys with the US labels
|
||||
// for consistency...
|
||||
const _registerLabel = (keyCode: KeyCode, charCode: CharCode): void => {
|
||||
// const existingLabel = this._keyCodeToLabel[keyCode];
|
||||
// const existingCharCode = (existingLabel ? existingLabel.charCodeAt(0) : CharCode.Null);
|
||||
// if (existingCharCode < 32 || existingCharCode > 126) {
|
||||
this._keyCodeToLabel[keyCode] = String.fromCharCode(charCode);
|
||||
// }
|
||||
};
|
||||
_registerLabel(KeyCode.US_SEMICOLON, CharCode.Semicolon);
|
||||
_registerLabel(KeyCode.US_EQUAL, CharCode.Equals);
|
||||
_registerLabel(KeyCode.US_COMMA, CharCode.Comma);
|
||||
_registerLabel(KeyCode.US_MINUS, CharCode.Dash);
|
||||
_registerLabel(KeyCode.US_DOT, CharCode.Period);
|
||||
_registerLabel(KeyCode.US_SLASH, CharCode.Slash);
|
||||
_registerLabel(KeyCode.US_BACKTICK, CharCode.BackTick);
|
||||
_registerLabel(KeyCode.US_OPEN_SQUARE_BRACKET, CharCode.OpenSquareBracket);
|
||||
_registerLabel(KeyCode.US_BACKSLASH, CharCode.Backslash);
|
||||
_registerLabel(KeyCode.US_CLOSE_SQUARE_BRACKET, CharCode.CloseSquareBracket);
|
||||
_registerLabel(KeyCode.US_QUOTE, CharCode.SingleQuote);
|
||||
}
|
||||
}
|
||||
|
||||
public dumpDebugInfo(): string {
|
||||
let result: string[] = [];
|
||||
|
||||
let immutableSamples = [
|
||||
ScanCode.ArrowUp,
|
||||
ScanCode.Numpad0
|
||||
];
|
||||
|
||||
let cnt = 0;
|
||||
result.push(`-----------------------------------------------------------------------------------------------------------------------------------------`);
|
||||
for (let scanCode = ScanCode.None; scanCode < ScanCode.MAX_VALUE; scanCode++) {
|
||||
if (IMMUTABLE_CODE_TO_KEY_CODE[scanCode] !== -1) {
|
||||
if (immutableSamples.indexOf(scanCode) === -1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (cnt % 6 === 0) {
|
||||
result.push(`| HW Code combination | Key | KeyCode combination | UI label | User settings | WYSIWYG |`);
|
||||
result.push(`-----------------------------------------------------------------------------------------------------------------------------------------`);
|
||||
}
|
||||
cnt++;
|
||||
|
||||
const mapping = this._codeInfo[scanCode];
|
||||
const strCode = ScanCodeUtils.toString(scanCode);
|
||||
|
||||
const mods = [0b000, 0b010, 0b101, 0b111];
|
||||
for (const mod of mods) {
|
||||
const ctrlKey = (mod & 0b001) ? true : false;
|
||||
const shiftKey = (mod & 0b010) ? true : false;
|
||||
const altKey = (mod & 0b100) ? true : false;
|
||||
const scanCodeBinding = new ScanCodeBinding(ctrlKey, shiftKey, altKey, false, scanCode);
|
||||
const kb = this._resolveSimpleUserBinding(scanCodeBinding);
|
||||
const strKeyCode = (kb ? KeyCodeUtils.toString(kb.keyCode) : null);
|
||||
const resolvedKb = (kb ? new WindowsNativeResolvedKeybinding(this, [kb]) : null);
|
||||
|
||||
const outScanCode = `${ctrlKey ? 'Ctrl+' : ''}${shiftKey ? 'Shift+' : ''}${altKey ? 'Alt+' : ''}${strCode}`;
|
||||
const ariaLabel = (resolvedKb ? resolvedKb.getAriaLabel() : null);
|
||||
const outUILabel = (ariaLabel ? ariaLabel.replace(/Control\+/, 'Ctrl+') : null);
|
||||
const outUserSettings = (resolvedKb ? resolvedKb.getUserSettingsLabel() : null);
|
||||
const outKey = WindowsNativeResolvedKeybinding.getProducedChar(scanCodeBinding, mapping);
|
||||
const outKb = (strKeyCode ? `${ctrlKey ? 'Ctrl+' : ''}${shiftKey ? 'Shift+' : ''}${altKey ? 'Alt+' : ''}${strKeyCode}` : null);
|
||||
const isWYSIWYG = (resolvedKb ? resolvedKb.isWYSIWYG() : false);
|
||||
const outWYSIWYG = (isWYSIWYG ? ' ' : ' NO ');
|
||||
result.push(`| ${this._leftPad(outScanCode, 30)} | ${outKey} | ${this._leftPad(outKb, 25)} | ${this._leftPad(outUILabel, 25)} | ${this._leftPad(outUserSettings, 25)} | ${outWYSIWYG} |`);
|
||||
}
|
||||
result.push(`-----------------------------------------------------------------------------------------------------------------------------------------`);
|
||||
}
|
||||
|
||||
|
||||
return result.join('\n');
|
||||
}
|
||||
|
||||
private _leftPad(str: string | null, cnt: number): string {
|
||||
if (str === null) {
|
||||
str = 'null';
|
||||
}
|
||||
while (str.length < cnt) {
|
||||
str = ' ' + str;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public getUILabelForKeyCode(keyCode: KeyCode): string {
|
||||
return this._getLabelForKeyCode(keyCode);
|
||||
}
|
||||
|
||||
public getAriaLabelForKeyCode(keyCode: KeyCode): string {
|
||||
return this._getLabelForKeyCode(keyCode);
|
||||
}
|
||||
|
||||
public getUserSettingsLabelForKeyCode(keyCode: KeyCode): string {
|
||||
if (this.isUSStandard) {
|
||||
return KeyCodeUtils.toUserSettingsUS(keyCode);
|
||||
}
|
||||
return KeyCodeUtils.toUserSettingsGeneral(keyCode);
|
||||
}
|
||||
|
||||
private _getLabelForKeyCode(keyCode: KeyCode): string {
|
||||
return this._keyCodeToLabel[keyCode] || KeyCodeUtils.toString(KeyCode.Unknown);
|
||||
}
|
||||
|
||||
public resolveKeybinding(keybinding: Keybinding): WindowsNativeResolvedKeybinding[] {
|
||||
const parts = keybinding.parts;
|
||||
for (let i = 0, len = parts.length; i < len; i++) {
|
||||
const part = parts[i];
|
||||
if (!this._keyCodeExists[part.keyCode]) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return [new WindowsNativeResolvedKeybinding(this, parts)];
|
||||
}
|
||||
|
||||
public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): WindowsNativeResolvedKeybinding {
|
||||
const keybinding = new SimpleKeybinding(keyboardEvent.ctrlKey, keyboardEvent.shiftKey, keyboardEvent.altKey, keyboardEvent.metaKey, keyboardEvent.keyCode);
|
||||
return new WindowsNativeResolvedKeybinding(this, [keybinding]);
|
||||
}
|
||||
|
||||
private _resolveSimpleUserBinding(binding: SimpleKeybinding | ScanCodeBinding | null): SimpleKeybinding | null {
|
||||
if (!binding) {
|
||||
return null;
|
||||
}
|
||||
if (binding instanceof SimpleKeybinding) {
|
||||
if (!this._keyCodeExists[binding.keyCode]) {
|
||||
return null;
|
||||
}
|
||||
return binding;
|
||||
}
|
||||
const keyCode = this._scanCodeToKeyCode[binding.scanCode] || KeyCode.Unknown;
|
||||
if (keyCode === KeyCode.Unknown || !this._keyCodeExists[keyCode]) {
|
||||
return null;
|
||||
}
|
||||
return new SimpleKeybinding(binding.ctrlKey, binding.shiftKey, binding.altKey, binding.metaKey, keyCode);
|
||||
}
|
||||
|
||||
public resolveUserBinding(input: (SimpleKeybinding | ScanCodeBinding)[]): ResolvedKeybinding[] {
|
||||
const parts: SimpleKeybinding[] = removeElementsAfterNulls(input.map(keybinding => this._resolveSimpleUserBinding(keybinding)));
|
||||
if (parts.length > 0) {
|
||||
return [new WindowsNativeResolvedKeybinding(this, parts)];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
|
||||
// See https://github.com/microsoft/node-native-keymap/blob/master/deps/chromium/keyboard_codes_win.h
|
||||
function _getNativeMap() {
|
||||
return {
|
||||
VK_BACK: KeyCode.Backspace,
|
||||
VK_TAB: KeyCode.Tab,
|
||||
VK_CLEAR: KeyCode.Unknown, // MISSING
|
||||
VK_RETURN: KeyCode.Enter,
|
||||
VK_SHIFT: KeyCode.Shift,
|
||||
VK_CONTROL: KeyCode.Ctrl,
|
||||
VK_MENU: KeyCode.Alt,
|
||||
VK_PAUSE: KeyCode.PauseBreak,
|
||||
VK_CAPITAL: KeyCode.CapsLock,
|
||||
VK_KANA: KeyCode.Unknown, // MISSING
|
||||
VK_HANGUL: KeyCode.Unknown, // MISSING
|
||||
VK_JUNJA: KeyCode.Unknown, // MISSING
|
||||
VK_FINAL: KeyCode.Unknown, // MISSING
|
||||
VK_HANJA: KeyCode.Unknown, // MISSING
|
||||
VK_KANJI: KeyCode.Unknown, // MISSING
|
||||
VK_ESCAPE: KeyCode.Escape,
|
||||
VK_CONVERT: KeyCode.Unknown, // MISSING
|
||||
VK_NONCONVERT: KeyCode.Unknown, // MISSING
|
||||
VK_ACCEPT: KeyCode.Unknown, // MISSING
|
||||
VK_MODECHANGE: KeyCode.Unknown, // MISSING
|
||||
VK_SPACE: KeyCode.Space,
|
||||
VK_PRIOR: KeyCode.PageUp,
|
||||
VK_NEXT: KeyCode.PageDown,
|
||||
VK_END: KeyCode.End,
|
||||
VK_HOME: KeyCode.Home,
|
||||
VK_LEFT: KeyCode.LeftArrow,
|
||||
VK_UP: KeyCode.UpArrow,
|
||||
VK_RIGHT: KeyCode.RightArrow,
|
||||
VK_DOWN: KeyCode.DownArrow,
|
||||
VK_SELECT: KeyCode.Unknown, // MISSING
|
||||
VK_PRINT: KeyCode.Unknown, // MISSING
|
||||
VK_EXECUTE: KeyCode.Unknown, // MISSING
|
||||
VK_SNAPSHOT: KeyCode.Unknown, // MISSING
|
||||
VK_INSERT: KeyCode.Insert,
|
||||
VK_DELETE: KeyCode.Delete,
|
||||
VK_HELP: KeyCode.Unknown, // MISSING
|
||||
|
||||
VK_0: KeyCode.KEY_0,
|
||||
VK_1: KeyCode.KEY_1,
|
||||
VK_2: KeyCode.KEY_2,
|
||||
VK_3: KeyCode.KEY_3,
|
||||
VK_4: KeyCode.KEY_4,
|
||||
VK_5: KeyCode.KEY_5,
|
||||
VK_6: KeyCode.KEY_6,
|
||||
VK_7: KeyCode.KEY_7,
|
||||
VK_8: KeyCode.KEY_8,
|
||||
VK_9: KeyCode.KEY_9,
|
||||
VK_A: KeyCode.KEY_A,
|
||||
VK_B: KeyCode.KEY_B,
|
||||
VK_C: KeyCode.KEY_C,
|
||||
VK_D: KeyCode.KEY_D,
|
||||
VK_E: KeyCode.KEY_E,
|
||||
VK_F: KeyCode.KEY_F,
|
||||
VK_G: KeyCode.KEY_G,
|
||||
VK_H: KeyCode.KEY_H,
|
||||
VK_I: KeyCode.KEY_I,
|
||||
VK_J: KeyCode.KEY_J,
|
||||
VK_K: KeyCode.KEY_K,
|
||||
VK_L: KeyCode.KEY_L,
|
||||
VK_M: KeyCode.KEY_M,
|
||||
VK_N: KeyCode.KEY_N,
|
||||
VK_O: KeyCode.KEY_O,
|
||||
VK_P: KeyCode.KEY_P,
|
||||
VK_Q: KeyCode.KEY_Q,
|
||||
VK_R: KeyCode.KEY_R,
|
||||
VK_S: KeyCode.KEY_S,
|
||||
VK_T: KeyCode.KEY_T,
|
||||
VK_U: KeyCode.KEY_U,
|
||||
VK_V: KeyCode.KEY_V,
|
||||
VK_W: KeyCode.KEY_W,
|
||||
VK_X: KeyCode.KEY_X,
|
||||
VK_Y: KeyCode.KEY_Y,
|
||||
VK_Z: KeyCode.KEY_Z,
|
||||
|
||||
VK_LWIN: KeyCode.Meta,
|
||||
VK_COMMAND: KeyCode.Meta,
|
||||
VK_RWIN: KeyCode.Meta,
|
||||
VK_APPS: KeyCode.Unknown, // MISSING
|
||||
VK_SLEEP: KeyCode.Unknown, // MISSING
|
||||
VK_NUMPAD0: KeyCode.NUMPAD_0,
|
||||
VK_NUMPAD1: KeyCode.NUMPAD_1,
|
||||
VK_NUMPAD2: KeyCode.NUMPAD_2,
|
||||
VK_NUMPAD3: KeyCode.NUMPAD_3,
|
||||
VK_NUMPAD4: KeyCode.NUMPAD_4,
|
||||
VK_NUMPAD5: KeyCode.NUMPAD_5,
|
||||
VK_NUMPAD6: KeyCode.NUMPAD_6,
|
||||
VK_NUMPAD7: KeyCode.NUMPAD_7,
|
||||
VK_NUMPAD8: KeyCode.NUMPAD_8,
|
||||
VK_NUMPAD9: KeyCode.NUMPAD_9,
|
||||
VK_MULTIPLY: KeyCode.NUMPAD_MULTIPLY,
|
||||
VK_ADD: KeyCode.NUMPAD_ADD,
|
||||
VK_SEPARATOR: KeyCode.NUMPAD_SEPARATOR,
|
||||
VK_SUBTRACT: KeyCode.NUMPAD_SUBTRACT,
|
||||
VK_DECIMAL: KeyCode.NUMPAD_DECIMAL,
|
||||
VK_DIVIDE: KeyCode.NUMPAD_DIVIDE,
|
||||
VK_F1: KeyCode.F1,
|
||||
VK_F2: KeyCode.F2,
|
||||
VK_F3: KeyCode.F3,
|
||||
VK_F4: KeyCode.F4,
|
||||
VK_F5: KeyCode.F5,
|
||||
VK_F6: KeyCode.F6,
|
||||
VK_F7: KeyCode.F7,
|
||||
VK_F8: KeyCode.F8,
|
||||
VK_F9: KeyCode.F9,
|
||||
VK_F10: KeyCode.F10,
|
||||
VK_F11: KeyCode.F11,
|
||||
VK_F12: KeyCode.F12,
|
||||
VK_F13: KeyCode.F13,
|
||||
VK_F14: KeyCode.F14,
|
||||
VK_F15: KeyCode.F15,
|
||||
VK_F16: KeyCode.F16,
|
||||
VK_F17: KeyCode.F17,
|
||||
VK_F18: KeyCode.F18,
|
||||
VK_F19: KeyCode.F19,
|
||||
VK_F20: KeyCode.Unknown, // MISSING
|
||||
VK_F21: KeyCode.Unknown, // MISSING
|
||||
VK_F22: KeyCode.Unknown, // MISSING
|
||||
VK_F23: KeyCode.Unknown, // MISSING
|
||||
VK_F24: KeyCode.Unknown, // MISSING
|
||||
VK_NUMLOCK: KeyCode.NumLock,
|
||||
VK_SCROLL: KeyCode.ScrollLock,
|
||||
VK_LSHIFT: KeyCode.Shift,
|
||||
VK_RSHIFT: KeyCode.Shift,
|
||||
VK_LCONTROL: KeyCode.Ctrl,
|
||||
VK_RCONTROL: KeyCode.Ctrl,
|
||||
VK_LMENU: KeyCode.Unknown, // MISSING
|
||||
VK_RMENU: KeyCode.Unknown, // MISSING
|
||||
VK_BROWSER_BACK: KeyCode.Unknown, // MISSING
|
||||
VK_BROWSER_FORWARD: KeyCode.Unknown, // MISSING
|
||||
VK_BROWSER_REFRESH: KeyCode.Unknown, // MISSING
|
||||
VK_BROWSER_STOP: KeyCode.Unknown, // MISSING
|
||||
VK_BROWSER_SEARCH: KeyCode.Unknown, // MISSING
|
||||
VK_BROWSER_FAVORITES: KeyCode.Unknown, // MISSING
|
||||
VK_BROWSER_HOME: KeyCode.Unknown, // MISSING
|
||||
VK_VOLUME_MUTE: KeyCode.Unknown, // MISSING
|
||||
VK_VOLUME_DOWN: KeyCode.Unknown, // MISSING
|
||||
VK_VOLUME_UP: KeyCode.Unknown, // MISSING
|
||||
VK_MEDIA_NEXT_TRACK: KeyCode.Unknown, // MISSING
|
||||
VK_MEDIA_PREV_TRACK: KeyCode.Unknown, // MISSING
|
||||
VK_MEDIA_STOP: KeyCode.Unknown, // MISSING
|
||||
VK_MEDIA_PLAY_PAUSE: KeyCode.Unknown, // MISSING
|
||||
VK_MEDIA_LAUNCH_MAIL: KeyCode.Unknown, // MISSING
|
||||
VK_MEDIA_LAUNCH_MEDIA_SELECT: KeyCode.Unknown, // MISSING
|
||||
VK_MEDIA_LAUNCH_APP1: KeyCode.Unknown, // MISSING
|
||||
VK_MEDIA_LAUNCH_APP2: KeyCode.Unknown, // MISSING
|
||||
VK_OEM_1: KeyCode.US_SEMICOLON,
|
||||
VK_OEM_PLUS: KeyCode.US_EQUAL,
|
||||
VK_OEM_COMMA: KeyCode.US_COMMA,
|
||||
VK_OEM_MINUS: KeyCode.US_MINUS,
|
||||
VK_OEM_PERIOD: KeyCode.US_DOT,
|
||||
VK_OEM_2: KeyCode.US_SLASH,
|
||||
VK_OEM_3: KeyCode.US_BACKTICK,
|
||||
VK_ABNT_C1: KeyCode.ABNT_C1,
|
||||
VK_ABNT_C2: KeyCode.ABNT_C2,
|
||||
VK_OEM_4: KeyCode.US_OPEN_SQUARE_BRACKET,
|
||||
VK_OEM_5: KeyCode.US_BACKSLASH,
|
||||
VK_OEM_6: KeyCode.US_CLOSE_SQUARE_BRACKET,
|
||||
VK_OEM_7: KeyCode.US_QUOTE,
|
||||
VK_OEM_8: KeyCode.OEM_8,
|
||||
VK_OEM_102: KeyCode.OEM_102,
|
||||
VK_PROCESSKEY: KeyCode.Unknown, // MISSING
|
||||
VK_PACKET: KeyCode.Unknown, // MISSING
|
||||
VK_DBE_SBCSCHAR: KeyCode.Unknown, // MISSING
|
||||
VK_DBE_DBCSCHAR: KeyCode.Unknown, // MISSING
|
||||
VK_ATTN: KeyCode.Unknown, // MISSING
|
||||
VK_CRSEL: KeyCode.Unknown, // MISSING
|
||||
VK_EXSEL: KeyCode.Unknown, // MISSING
|
||||
VK_EREOF: KeyCode.Unknown, // MISSING
|
||||
VK_PLAY: KeyCode.Unknown, // MISSING
|
||||
VK_ZOOM: KeyCode.Unknown, // MISSING
|
||||
VK_NONAME: KeyCode.Unknown, // MISSING
|
||||
VK_PA1: KeyCode.Unknown, // MISSING
|
||||
VK_OEM_CLEAR: KeyCode.Unknown, // MISSING
|
||||
VK_UNKNOWN: KeyCode.Unknown,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user