From 73cba6dda37a15203142d01ecd1b2803c4c91ab9 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 10 Jan 2026 04:38:06 -0800 Subject: [PATCH] Clean up, add sgr extension to api --- .../client/components/window/optionsWindow.ts | 3 +- src/browser/services/KeyboardService.ts | 4 +- src/common/InputHandler.ts | 200 +++++++++--------- src/common/input/KittyKeyboard.ts | 1 - 4 files changed, 105 insertions(+), 103 deletions(-) diff --git a/demo/client/components/window/optionsWindow.ts b/demo/client/components/window/optionsWindow.ts index a84d7f75..18f37b58 100644 --- a/demo/client/components/window/optionsWindow.ts +++ b/demo/client/components/window/optionsWindow.ts @@ -124,7 +124,8 @@ export class OptionsWindow extends BaseWindow implements IControlWindow { 'windowsPty', ]; const nestedBooleanOptions: { label: string, parent: string, prop: string }[] = [ - { label: 'vtExtensions.kittyKeyboard', parent: 'vtExtensions', prop: 'kittyKeyboard' } + { label: 'vtExtensions.kittyKeyboard', parent: 'vtExtensions', prop: 'kittyKeyboard' }, + { label: 'vtExtensions.kittySgrBoldFaintControl', parent: 'vtExtensions', prop: 'kittySgrBoldFaintControl' } ]; const stringOptions: { [key: string]: string[] | null } = { cursorStyle: ['block', 'underline', 'bar'], diff --git a/src/browser/services/KeyboardService.ts b/src/browser/services/KeyboardService.ts index c1603300..da2368a4 100644 --- a/src/browser/services/KeyboardService.ts +++ b/src/browser/services/KeyboardService.ts @@ -5,7 +5,7 @@ import { IKeyboardService } from 'browser/services/Services'; import { evaluateKeyboardEvent } from 'common/input/Keyboard'; -import { evaluateKeyboardEventKitty, KittyKeyboardEventType, shouldUseKittyProtocol } from 'common/input/KittyKeyboard'; +import { evaluateKeyboardEventKitty, KittyKeyboardEventType, KittyKeyboardFlags, shouldUseKittyProtocol } from 'common/input/KittyKeyboard'; import { isMac } from 'common/Platform'; import { ICoreService, IOptionsService } from 'common/services/Services'; import { IKeyboardResult } from 'common/Types'; @@ -28,7 +28,7 @@ export class KeyboardService implements IKeyboardService { public evaluateKeyUp(event: KeyboardEvent): IKeyboardResult | undefined { const kittyFlags = this._coreService.kittyKeyboard.flags; - if (this.useKitty && (kittyFlags & 0b10)) { // REPORT_EVENT_TYPES flag + if (this.useKitty && (kittyFlags & KittyKeyboardFlags.REPORT_EVENT_TYPES)) { return evaluateKeyboardEventKitty(event, kittyFlags, KittyKeyboardEventType.RELEASE); } return undefined; diff --git a/src/common/InputHandler.ts b/src/common/InputHandler.ts index 17baef11..85f6f725 100644 --- a/src/common/InputHandler.ts +++ b/src/common/InputHandler.ts @@ -3002,105 +3002,6 @@ export class InputHandler extends Disposable implements IInputHandler { return true; } - - /** - * CSI = flags ; mode u - * Set Kitty keyboard protocol flags. - * mode: 1=set, 2=set-only-specified, 3=reset-only-specified - * - * @vt: #Y CSI KKBDSET "Kitty Keyboard Set" "CSI = Ps ; Pm u" "Set Kitty keyboard protocol flags." - */ - public kittyKeyboardSet(params: IParams): boolean { - if (!this._optionsService.rawOptions.vtExtensions?.kittyKeyboard) { - return true; - } - const flags = params.params[0] || 0; - const mode = params.params[1] || 1; - const state = this._coreService.kittyKeyboard; - - switch (mode) { - case 1: // Set all flags - state.flags = flags; - break; - case 2: // Set only specified flags (OR) - state.flags |= flags; - break; - case 3: // Reset only specified flags (AND NOT) - state.flags &= ~flags; - break; - } - return true; - } - - /** - * CSI ? u - * Query Kitty keyboard protocol flags. - * Terminal responds with CSI ? flags u - * - * @vt: #Y CSI KKBDQUERY "Kitty Keyboard Query" "CSI ? u" "Query Kitty keyboard protocol flags." - */ - public kittyKeyboardQuery(params: IParams): boolean { - if (!this._optionsService.rawOptions.vtExtensions?.kittyKeyboard) { - return true; - } - const flags = this._coreService.kittyKeyboard.flags; - this._coreService.triggerDataEvent(`${C0.ESC}[?${flags}u`); - return true; - } - - /** - * CSI > flags u - * Push Kitty keyboard flags onto stack and set new flags. - * - * @vt: #Y CSI KKBDPUSH "Kitty Keyboard Push" "CSI > Ps u" "Push keyboard flags to stack and set new flags." - */ - public kittyKeyboardPush(params: IParams): boolean { - if (!this._optionsService.rawOptions.vtExtensions?.kittyKeyboard) { - return true; - } - const flags = params.params[0] || 0; - const state = this._coreService.kittyKeyboard; - const isAlt = this._bufferService.buffer === this._bufferService.buffers.alt; - const stack = isAlt ? state.altStack : state.mainStack; - - // Evict oldest entry if stack is full (DoS protection, limit of 16) - if (stack.length >= 16) { - stack.shift(); - } - - // Push current flags onto stack and set new flags - stack.push(state.flags); - state.flags = flags; - return true; - } - - /** - * CSI < count u - * Pop Kitty keyboard flags from stack. - * - * @vt: #Y CSI KKBDPOP "Kitty Keyboard Pop" "CSI < Ps u" "Pop keyboard flags from stack." - */ - public kittyKeyboardPop(params: IParams): boolean { - if (!this._optionsService.rawOptions.vtExtensions?.kittyKeyboard) { - return true; - } - const count = Math.max(1, params.params[0] || 1); - const state = this._coreService.kittyKeyboard; - const isAlt = this._bufferService.buffer === this._bufferService.buffers.alt; - const stack = isAlt ? state.altStack : state.mainStack; - - // Pop specified number of entries from stack - for (let i = 0; i < count && stack.length > 0; i++) { - state.flags = stack.pop()!; - } - // If stack is empty after popping, reset to 0 - if (stack.length === 0 && count > 0) { - state.flags = 0; - } - return true; - } - - /** * OSC 2; ST (set window title) * Proxy to set window title. @@ -3612,6 +3513,107 @@ export class InputHandler extends Disposable implements IInputHandler { public markRangeDirty(y1: number, y2: number): void { this._dirtyRowTracker.markRangeDirty(y1, y2); } + + // #region Kitty keyboard + + /** + * CSI = flags ; mode u + * Set Kitty keyboard protocol flags. + * mode: 1=set, 2=set-only-specified, 3=reset-only-specified + * + * @vt: #Y CSI KKBDSET "Kitty Keyboard Set" "CSI = Ps ; Pm u" "Set Kitty keyboard protocol flags." + */ + public kittyKeyboardSet(params: IParams): boolean { + if (!this._optionsService.rawOptions.vtExtensions?.kittyKeyboard) { + return true; + } + const flags = params.params[0] || 0; + const mode = params.params[1] || 1; + const state = this._coreService.kittyKeyboard; + + switch (mode) { + case 1: // Set all flags + state.flags = flags; + break; + case 2: // Set only specified flags (OR) + state.flags |= flags; + break; + case 3: // Reset only specified flags (AND NOT) + state.flags &= ~flags; + break; + } + return true; + } + + /** + * CSI ? u + * Query Kitty keyboard protocol flags. + * Terminal responds with CSI ? flags u + * + * @vt: #Y CSI KKBDQUERY "Kitty Keyboard Query" "CSI ? u" "Query Kitty keyboard protocol flags." + */ + public kittyKeyboardQuery(params: IParams): boolean { + if (!this._optionsService.rawOptions.vtExtensions?.kittyKeyboard) { + return true; + } + const flags = this._coreService.kittyKeyboard.flags; + this._coreService.triggerDataEvent(`${C0.ESC}[?${flags}u`); + return true; + } + + /** + * CSI > flags u + * Push Kitty keyboard flags onto stack and set new flags. + * + * @vt: #Y CSI KKBDPUSH "Kitty Keyboard Push" "CSI > Ps u" "Push keyboard flags to stack and set new flags." + */ + public kittyKeyboardPush(params: IParams): boolean { + if (!this._optionsService.rawOptions.vtExtensions?.kittyKeyboard) { + return true; + } + const flags = params.params[0] || 0; + const state = this._coreService.kittyKeyboard; + const isAlt = this._bufferService.buffer === this._bufferService.buffers.alt; + const stack = isAlt ? state.altStack : state.mainStack; + + // Evict oldest entry if stack is full (DoS protection, limit of 16) + if (stack.length >= 16) { + stack.shift(); + } + + // Push current flags onto stack and set new flags + stack.push(state.flags); + state.flags = flags; + return true; + } + + /** + * CSI < count u + * Pop Kitty keyboard flags from stack. + * + * @vt: #Y CSI KKBDPOP "Kitty Keyboard Pop" "CSI < Ps u" "Pop keyboard flags from stack." + */ + public kittyKeyboardPop(params: IParams): boolean { + if (!this._optionsService.rawOptions.vtExtensions?.kittyKeyboard) { + return true; + } + const count = Math.max(1, params.params[0] || 1); + const state = this._coreService.kittyKeyboard; + const isAlt = this._bufferService.buffer === this._bufferService.buffers.alt; + const stack = isAlt ? state.altStack : state.mainStack; + + // Pop specified number of entries from stack + for (let i = 0; i < count && stack.length > 0; i++) { + state.flags = stack.pop()!; + } + // If stack is empty after popping, reset to 0 + if (stack.length === 0 && count > 0) { + state.flags = 0; + } + return true; + } + + // #endregion } export interface IDirtyRowTracker { diff --git a/src/common/input/KittyKeyboard.ts b/src/common/input/KittyKeyboard.ts index 98cf3ded..0dfd0b56 100644 --- a/src/common/input/KittyKeyboard.ts +++ b/src/common/input/KittyKeyboard.ts @@ -206,7 +206,6 @@ function encodeModifiers(ev: IKeyboardEvent): number { if (ev.altKey) mods |= KittyKeyboardModifiers.ALT; if (ev.ctrlKey) mods |= KittyKeyboardModifiers.CTRL; if (ev.metaKey) mods |= KittyKeyboardModifiers.SUPER; - // Note: getModifierState would be needed for CAPS_LOCK/NUM_LOCK but not in IKeyboardEvent return mods > 0 ? mods + 1 : 0; }