mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Align behavior closer with kitty
This commit is contained in:
@@ -610,6 +610,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
// Listen for mouse events and translate
|
||||
// them into terminal mouse protocols.
|
||||
this.bindMouse();
|
||||
|
||||
// Emit kitty keyboard protocol support notification (31 = all flags supported)
|
||||
this.coreService.triggerDataEvent(`${C0.ESC}[>31u`);
|
||||
}
|
||||
|
||||
private _createRenderer(): IRenderer {
|
||||
|
||||
@@ -2012,6 +2012,8 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
this._coreService.isCursorInitialized = true;
|
||||
this._onRequestRefreshRows.fire(undefined);
|
||||
this._onRequestSyncScrollBar.fire();
|
||||
// Emit kitty keyboard protocol support notification
|
||||
this._coreService.triggerDataEvent(`${C0.ESC}[>31u`);
|
||||
break;
|
||||
case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste)
|
||||
this._coreService.decPrivateModes.bracketedPasteMode = true;
|
||||
|
||||
@@ -98,12 +98,12 @@ describe('KittyKeyboard', () => {
|
||||
|
||||
it('should include event type for repeat events', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'a' }), flags, KittyKeyboardEventType.REPEAT);
|
||||
assert.strictEqual(result.key, '\x1b[97;:2u');
|
||||
assert.strictEqual(result.key, '\x1b[97;1:2u');
|
||||
});
|
||||
|
||||
it('should include event type for release events', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'a' }), flags, KittyKeyboardEventType.RELEASE);
|
||||
assert.strictEqual(result.key, '\x1b[97;:3u');
|
||||
assert.strictEqual(result.key, '\x1b[97;1:3u');
|
||||
});
|
||||
|
||||
it('should include modifiers and event type', () => {
|
||||
@@ -157,6 +157,12 @@ describe('KittyKeyboard', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'Control', code: 'ControlRight', ctrlKey: true }), flags);
|
||||
assert.strictEqual(result.key, '\x1b[57448;5u');
|
||||
});
|
||||
|
||||
it('should not report modifier-only keys without REPORT_EVENT_TYPES', () => {
|
||||
const flagsNoEventTypes = KittyKeyboardFlags.DISAMBIGUATE_ESCAPE_CODES;
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'Shift', code: 'ShiftLeft', shiftKey: true }), flagsNoEventTypes);
|
||||
assert.strictEqual(result.key, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('release events without REPORT_EVENT_TYPES', () => {
|
||||
@@ -167,5 +173,46 @@ describe('KittyKeyboard', () => {
|
||||
assert.strictEqual(result.key, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with REPORT_ASSOCIATED_TEXT flag', () => {
|
||||
const flags = KittyKeyboardFlags.REPORT_ALL_KEYS_AS_ESCAPE_CODES | KittyKeyboardFlags.REPORT_ASSOCIATED_TEXT;
|
||||
|
||||
it('should include text codepoint for regular keys', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'a' }), flags);
|
||||
assert.strictEqual(result.key, '\x1b[97;;97u');
|
||||
});
|
||||
|
||||
it('should include text codepoint even when same as keycode', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'b' }), flags);
|
||||
assert.strictEqual(result.key, '\x1b[98;;98u');
|
||||
});
|
||||
|
||||
it('should include modifier and text codepoint together', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'a', shiftKey: true }), flags);
|
||||
assert.strictEqual(result.key, '\x1b[97;2;97u');
|
||||
});
|
||||
|
||||
it('should include text on repeat events', () => {
|
||||
const flagsWithEvents = flags | KittyKeyboardFlags.REPORT_EVENT_TYPES;
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'a' }), flagsWithEvents, KittyKeyboardEventType.REPEAT);
|
||||
assert.strictEqual(result.key, '\x1b[97;;97u');
|
||||
});
|
||||
|
||||
it('should not include text on release events', () => {
|
||||
const flagsWithEvents = flags | KittyKeyboardFlags.REPORT_EVENT_TYPES;
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'a' }), flagsWithEvents, KittyKeyboardEventType.RELEASE);
|
||||
assert.strictEqual(result.key, '\x1b[97;1:3u');
|
||||
});
|
||||
|
||||
it('should not include text for functional keys', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'Escape' }), flags);
|
||||
assert.strictEqual(result.key, '\x1b[27u');
|
||||
});
|
||||
|
||||
it('should not include text for modifier keys', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'Shift', code: 'ShiftLeft', shiftKey: true }), flags);
|
||||
assert.strictEqual(result.key, '\x1b[57441;2u');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -268,16 +268,12 @@ export function evaluateKeyboardEventKitty(
|
||||
// When reporting event types, use CSI u for all keys
|
||||
useCsiU = true;
|
||||
} else if (flags & KittyKeyboardFlags.DISAMBIGUATE_ESCAPE_CODES) {
|
||||
// Use CSI u for:
|
||||
// - Modifier-only keys when reporting event types
|
||||
// - Keys that would be ambiguous in legacy encoding
|
||||
// - Escape key
|
||||
// - Backspace
|
||||
// - Tab (when shifted)
|
||||
// - Enter
|
||||
if (isMod && (flags & KittyKeyboardFlags.REPORT_EVENT_TYPES)) {
|
||||
useCsiU = true;
|
||||
} else if (keyCode === 27 || keyCode === 127 || keyCode === 13) {
|
||||
// Modifier-only keys are never reported without REPORT_EVENT_TYPES
|
||||
if (isMod) {
|
||||
return result;
|
||||
}
|
||||
// Use CSI u for keys that would be ambiguous in legacy encoding
|
||||
if (keyCode === 27 || keyCode === 127 || keyCode === 13) {
|
||||
// Escape, Backspace, Enter
|
||||
useCsiU = true;
|
||||
} else if (keyCode === 9 && ev.shiftKey) {
|
||||
@@ -303,27 +299,30 @@ export function evaluateKeyboardEventKitty(
|
||||
// Format: CSI <keycode>[:<shifted>][:<base>] ; <modifiers>[:<event>] u
|
||||
let seq = C0.ESC + '[' + keyCode;
|
||||
|
||||
// Add modifiers and event type
|
||||
if (modifiers > 0 || (reportEventTypes && eventType !== KittyKeyboardEventType.PRESS)) {
|
||||
// Check if we need associated text (press and repeat events, not release)
|
||||
const reportAssociatedText = !!(flags & KittyKeyboardFlags.REPORT_ASSOCIATED_TEXT) &&
|
||||
eventType !== KittyKeyboardEventType.RELEASE && ev.key.length === 1 && !isFunc && !isMod;
|
||||
const textCode = reportAssociatedText ? ev.key.codePointAt(0) : undefined;
|
||||
|
||||
// When text is present, don't include event type marker (even for repeat)
|
||||
// Release events always need event type marker
|
||||
const needsEventType = reportEventTypes && eventType !== KittyKeyboardEventType.PRESS && textCode === undefined;
|
||||
if (modifiers > 0 || needsEventType || textCode !== undefined) {
|
||||
seq += ';';
|
||||
// Use 1 as base when event type needed but no modifiers (kitty format: 1 + modifier_bits)
|
||||
if (modifiers > 0) {
|
||||
seq += modifiers;
|
||||
} else if (needsEventType) {
|
||||
seq += '1';
|
||||
}
|
||||
if (reportEventTypes && eventType !== KittyKeyboardEventType.PRESS) {
|
||||
if (needsEventType) {
|
||||
seq += ':' + eventType;
|
||||
}
|
||||
}
|
||||
|
||||
// Add associated text if requested and available
|
||||
if ((flags & KittyKeyboardFlags.REPORT_ASSOCIATED_TEXT) && ev.key.length === 1 && !isFunc && !isMod) {
|
||||
const textCode = ev.key.codePointAt(0);
|
||||
if (textCode !== undefined && textCode !== keyCode) {
|
||||
// Append text as ; text u
|
||||
if (!seq.includes(';')) {
|
||||
seq += ';';
|
||||
}
|
||||
seq += ';' + textCode;
|
||||
}
|
||||
// Add associated text if requested
|
||||
if (textCode !== undefined) {
|
||||
seq += ';' + textCode;
|
||||
}
|
||||
|
||||
seq += 'u';
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
|
||||
import { IBuffer } from 'common/buffer/Types';
|
||||
import { CoreTerminal } from 'common/CoreTerminal';
|
||||
import { C0 } from 'common/data/EscapeSequences';
|
||||
import { IMarker, ITerminalOptions } from 'common/Types';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
|
||||
@@ -54,6 +55,9 @@ export class Terminal extends CoreTerminal {
|
||||
this._register(Event.forward(this._inputHandler.onA11yChar, this._onA11yCharEmitter));
|
||||
this._register(Event.forward(this._inputHandler.onA11yTab, this._onA11yTabEmitter));
|
||||
this._register(Event.forward(Event.map(this._inputHandler.onRequestRefreshRows, e => ({ start: e?.start ?? 0, end: e?.end ?? this.rows - 1 })), this._onRender));
|
||||
|
||||
// Emit kitty keyboard protocol support notification (31 = all flags supported)
|
||||
this.coreService.triggerDataEvent(`${C0.ESC}[>31u`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user