Create IKeyboardService

This commit is contained in:
Daniel Imms
2026-01-10 04:19:23 -08:00
parent 98ea14824a
commit ec78500f86
3 changed files with 60 additions and 18 deletions
+10 -17
View File
@@ -39,8 +39,9 @@ import { LinkProviderService } from 'browser/services/LinkProviderService';
import { MouseService } from 'browser/services/MouseService';
import { RenderService } from 'browser/services/RenderService';
import { SelectionService } from 'browser/services/SelectionService';
import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, ILinkProviderService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services';
import { ICharSizeService, ICharacterJoinerService, ICoreBrowserService, IKeyboardService, ILinkProviderService, IMouseService, IRenderService, ISelectionService, IThemeService } from 'browser/services/Services';
import { ThemeService } from 'browser/services/ThemeService';
import { KeyboardService } from 'browser/services/KeyboardService';
import { channels, color } from 'common/Color';
import { CoreTerminal } from 'common/CoreTerminal';
import * as Browser from 'common/Platform';
@@ -48,8 +49,6 @@ import { ColorRequestType, CoreMouseAction, CoreMouseButton, CoreMouseEventType,
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
import { IBuffer } from 'common/buffer/Types';
import { C0, C1_ESCAPED } from 'common/data/EscapeSequences';
import { evaluateKeyboardEvent } from 'common/input/Keyboard';
import { evaluateKeyboardEventKitty, KittyKeyboardEventType, shouldUseKittyProtocol } from 'common/input/KittyKeyboard';
import { toRgbString } from 'common/input/XParseColor';
import { DecorationService } from 'common/services/DecorationService';
import { IDecorationService } from 'common/services/Services';
@@ -82,6 +81,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
// Browser services
private _decorationService: DecorationService;
private _keyboardService: IKeyboardService;
private _linkProviderService: ILinkProviderService;
// Optional browser services
@@ -174,6 +174,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
this._decorationService = this._instantiationService.createInstance(DecorationService);
this._instantiationService.setService(IDecorationService, this._decorationService);
this._keyboardService = this._instantiationService.createInstance(KeyboardService);
this._instantiationService.setService(IKeyboardService, this._keyboardService);
this._linkProviderService = this._instantiationService.createInstance(LinkProviderService);
this._instantiationService.setService(ILinkProviderService, this._linkProviderService);
this._linkProviderService.registerLinkProvider(this._instantiationService.createInstance(OscLinkProvider));
@@ -1082,12 +1084,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
this._unprocessedDeadKey = true;
}
// Use Kitty keyboard protocol if enabled, otherwise use legacy encoding
const kittyFlags = this.coreService.kittyKeyboard.flags;
const useKitty = this.options.vtExtensions?.kittyKeyboard && shouldUseKittyProtocol(kittyFlags);
const result = useKitty
? evaluateKeyboardEventKitty(event, kittyFlags, event.repeat ? KittyKeyboardEventType.REPEAT : KittyKeyboardEventType.PRESS)
: evaluateKeyboardEvent(event, this.coreService.decPrivateModes.applicationCursorKeys, this.browser.isMac, this.options.macOptionIsMeta);
const result = this._keyboardService.evaluateKeyDown(event);
this.updateCursorStyle(event);
@@ -1117,7 +1114,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
// HACK: Process A-Z in the keypress event to fix an issue with macOS IMEs where lower case
// letters cannot be input while caps lock is on. Skip this hack when using kitty protocol
// as it needs to send proper CSI u sequences for all key events.
if (!useKitty && event.key && !event.ctrlKey && !event.altKey && !event.metaKey && event.key.length === 1) {
if (!this._keyboardService.useKitty && event.key && !event.ctrlKey && !event.altKey && !event.metaKey && event.key.length === 1) {
if (event.key.charCodeAt(0) >= 65 && event.key.charCodeAt(0) <= 90) {
return true;
}
@@ -1176,13 +1173,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
}
// Handle key release for Kitty keyboard protocol
const kittyFlags = this.coreService.kittyKeyboard.flags;
const useKitty = this.options.vtExtensions?.kittyKeyboard && shouldUseKittyProtocol(kittyFlags);
if (useKitty && (kittyFlags & 0b10)) { // REPORT_EVENT_TYPES flag
const result = evaluateKeyboardEventKitty(ev, kittyFlags, KittyKeyboardEventType.RELEASE);
if (result.key) {
this.coreService.triggerDataEvent(result.key, true);
}
const result = this._keyboardService.evaluateKeyUp(ev);
if (result?.key) {
this.coreService.triggerDataEvent(result.key, true);
}
this.updateCursorStyle(ev);
+41
View File
@@ -0,0 +1,41 @@
/**
* Copyright (c) 2025 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { IKeyboardService } from 'browser/services/Services';
import { evaluateKeyboardEvent } from 'common/input/Keyboard';
import { evaluateKeyboardEventKitty, KittyKeyboardEventType, shouldUseKittyProtocol } from 'common/input/KittyKeyboard';
import { isMac } from 'common/Platform';
import { ICoreService, IOptionsService } from 'common/services/Services';
import { IKeyboardResult } from 'common/Types';
export class KeyboardService implements IKeyboardService {
public serviceBrand: undefined;
constructor(
@ICoreService private readonly _coreService: ICoreService,
@IOptionsService private readonly _optionsService: IOptionsService
) {
}
public evaluateKeyDown(event: KeyboardEvent): IKeyboardResult {
const kittyFlags = this._coreService.kittyKeyboard.flags;
return this.useKitty
? evaluateKeyboardEventKitty(event, kittyFlags, event.repeat ? KittyKeyboardEventType.REPEAT : KittyKeyboardEventType.PRESS)
: evaluateKeyboardEvent(event, this._coreService.decPrivateModes.applicationCursorKeys, isMac, this._optionsService.rawOptions.macOptionIsMeta);
}
public evaluateKeyUp(event: KeyboardEvent): IKeyboardResult | undefined {
const kittyFlags = this._coreService.kittyKeyboard.flags;
if (this.useKitty && (kittyFlags & 0b10)) { // REPORT_EVENT_TYPES flag
return evaluateKeyboardEventKitty(event, kittyFlags, KittyKeyboardEventType.RELEASE);
}
return undefined;
}
public get useKitty(): boolean {
const kittyFlags = this._coreService.kittyKeyboard.flags;
return !!(this._optionsService.rawOptions.vtExtensions?.kittyKeyboard && shouldUseKittyProtocol(kittyFlags));
}
}
+9 -1
View File
@@ -7,7 +7,7 @@ import { IRenderDimensions, IRenderer } from 'browser/renderer/shared/Types';
import { IColorSet, ILink, ReadonlyColorSet } from 'browser/Types';
import { ISelectionRedrawRequestEvent as ISelectionRequestRedrawEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types';
import { createDecorator } from 'common/services/ServiceRegistry';
import { AllColorIndex, IDisposable } from 'common/Types';
import { AllColorIndex, IDisposable, IKeyboardResult } from 'common/Types';
import type { Event } from 'vs/base/common/event';
export const ICharSizeService = createDecorator<ICharSizeService>('CharSizeService');
@@ -156,3 +156,11 @@ export interface ILinkProviderService extends IDisposable {
export interface ILinkProvider {
provideLinks(y: number, callback: (links: ILink[] | undefined) => void): void;
}
export const IKeyboardService = createDecorator<IKeyboardService>('KeyboardService');
export interface IKeyboardService {
serviceBrand: undefined;
evaluateKeyDown(event: KeyboardEvent): IKeyboardResult;
evaluateKeyUp(event: KeyboardEvent): IKeyboardResult | undefined;
readonly useKitty: boolean;
}