mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge branch 'master' into patch-1
This commit is contained in:
@@ -354,7 +354,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
}
|
||||
|
||||
private _updateCursorBlink(): void {
|
||||
if (this._terminal.options.cursorBlink) {
|
||||
if (this._coreService.decPrivateModes.cursorBlink ?? this._terminal.options.cursorBlink) {
|
||||
this._cursorBlinkStateManager.value = new CursorBlinkStateManager(() => {
|
||||
this._requestRedrawCursor();
|
||||
}, this._coreBrowserService);
|
||||
@@ -387,6 +387,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
let j: number;
|
||||
start = clamp(start, terminal.rows - 1, 0);
|
||||
end = clamp(end, terminal.rows - 1, 0);
|
||||
const cursorStyle = this._coreService.decPrivateModes.cursorStyle ?? terminal.options.cursorStyle ?? 'block';
|
||||
|
||||
const cursorY = this._terminal.buffer.active.baseY + this._terminal.buffer.active.cursorY;
|
||||
const viewportRelativeCursorY = cursorY - terminal.buffer.ydisp;
|
||||
@@ -450,8 +451,7 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
x: cursorX,
|
||||
y: viewportRelativeCursorY,
|
||||
width: cell.getWidth(),
|
||||
style: this._coreBrowserService.isFocused ?
|
||||
(terminal.options.cursorStyle || 'block') : terminal.options.cursorInactiveStyle,
|
||||
style: this._coreBrowserService.isFocused ? cursorStyle : terminal.options.cursorInactiveStyle,
|
||||
cursorWidth: terminal.options.cursorWidth,
|
||||
dpr: this._devicePixelRatio
|
||||
};
|
||||
@@ -459,9 +459,10 @@ export class WebglRenderer extends Disposable implements IRenderer {
|
||||
}
|
||||
if (x >= cursorX && x <= lastCursorX &&
|
||||
((this._coreBrowserService.isFocused &&
|
||||
(terminal.options.cursorStyle || 'block') === 'block') ||
|
||||
cursorStyle === 'block') ||
|
||||
(this._coreBrowserService.isFocused === false &&
|
||||
terminal.options.cursorInactiveStyle === 'block'))) {
|
||||
terminal.options.cursorInactiveStyle === 'block'))
|
||||
) {
|
||||
this._cellColorResolver.result.fg =
|
||||
Attributes.CM_RGB | (this._themeService.colors.cursorAccent.rgba >> 8 & Attributes.RGB_MASK);
|
||||
this._cellColorResolver.result.bg =
|
||||
|
||||
+22
-21
@@ -16,7 +16,7 @@ if ('WebAssembly' in window) {
|
||||
ImageAddon = imageAddon.ImageAddon;
|
||||
}
|
||||
|
||||
import { Terminal, ITerminalOptions, type IDisposable } from '@xterm/xterm';
|
||||
import { Terminal, ITerminalOptions, type IDisposable, type ITheme } from '@xterm/xterm';
|
||||
import { AttachAddon } from '@xterm/addon-attach';
|
||||
import { ClipboardAddon } from '@xterm/addon-clipboard';
|
||||
import { FitAddon } from '@xterm/addon-fit';
|
||||
@@ -131,7 +131,7 @@ const xtermjsTheme = {
|
||||
brightCyan: '#72F0FF',
|
||||
white: '#F8F8F8',
|
||||
brightWhite: '#FFFFFF'
|
||||
};
|
||||
} satisfies ITheme;
|
||||
function setPadding(): void {
|
||||
term.element.style.padding = parseInt(paddingElement.value, 10).toString() + 'px';
|
||||
addons.fit.instance.fit();
|
||||
@@ -1266,9 +1266,9 @@ function addVtButtons(): void {
|
||||
|
||||
const element = document.createElement('button');
|
||||
element.textContent = name;
|
||||
writeCsi.split('');
|
||||
const prefix = writeCsi.length === 2 ? writeCsi[0] : '';
|
||||
const suffix = writeCsi[writeCsi.length - 1];
|
||||
const writeCsiSplit = writeCsi.split('|');
|
||||
const prefix = writeCsiSplit.length === 2 ? writeCsiSplit[0] : '';
|
||||
const suffix = writeCsiSplit[writeCsiSplit.length - 1];
|
||||
element.addEventListener(`click`, () => term.write(csi(`${prefix}${inputs.map(e => e.value).join(';')}${suffix}`)));
|
||||
|
||||
const desc = document.createElement('span');
|
||||
@@ -1281,22 +1281,23 @@ function addVtButtons(): void {
|
||||
}
|
||||
const vtFragment = document.createDocumentFragment();
|
||||
const buttonSpecs: { [key: string]: { label: string, description: string, paramCount?: number }} = {
|
||||
A: { label: 'CUU ↑', description: 'Cursor Up Ps Times' },
|
||||
B: { label: 'CUD ↓', description: 'Cursor Down Ps Times' },
|
||||
C: { label: 'CUF →', description: 'Cursor Forward Ps Times' },
|
||||
D: { label: 'CUB ←', description: 'Cursor Backward Ps Times' },
|
||||
E: { label: 'CNL', description: 'Cursor Next Line Ps Times' },
|
||||
F: { label: 'CPL', description: 'Cursor Preceding Line Ps Times' },
|
||||
G: { label: 'CHA', description: 'Cursor Character Absolute' },
|
||||
H: { label: 'CUP', description: 'Cursor Position [row;column]', paramCount: 2 },
|
||||
I: { label: 'CHT', description: 'Cursor Forward Tabulation Ps tab stops' },
|
||||
J: { label: 'ED', description: 'Erase in Display' },
|
||||
'?J': { label: 'DECSED', description: 'Erase in Display' },
|
||||
K: { label: 'EL', description: 'Erase in Line' },
|
||||
'?K': { label: 'DECSEL', description: 'Erase in Line' },
|
||||
L: { label: 'IL', description: 'Insert Ps Line(s)' },
|
||||
M: { label: 'DL', description: 'Delete Ps Line(s)' },
|
||||
P: { label: 'DCH', description: 'Delete Ps Character(s)' }
|
||||
A: { label: 'CUU ↑', description: 'Cursor Up Ps Times' },
|
||||
B: { label: 'CUD ↓', description: 'Cursor Down Ps Times' },
|
||||
C: { label: 'CUF →', description: 'Cursor Forward Ps Times' },
|
||||
D: { label: 'CUB ←', description: 'Cursor Backward Ps Times' },
|
||||
E: { label: 'CNL', description: 'Cursor Next Line Ps Times' },
|
||||
F: { label: 'CPL', description: 'Cursor Preceding Line Ps Times' },
|
||||
G: { label: 'CHA', description: 'Cursor Character Absolute' },
|
||||
H: { label: 'CUP', description: 'Cursor Position [row;column]', paramCount: 2 },
|
||||
I: { label: 'CHT', description: 'Cursor Forward Tabulation Ps tab stops' },
|
||||
J: { label: 'ED', description: 'Erase in Display' },
|
||||
'?|J': { label: 'DECSED', description: 'Erase in Display' },
|
||||
K: { label: 'EL', description: 'Erase in Line' },
|
||||
'?|K': { label: 'DECSEL', description: 'Erase in Line' },
|
||||
L: { label: 'IL', description: 'Insert Ps Line(s)' },
|
||||
M: { label: 'DL', description: 'Delete Ps Line(s)' },
|
||||
P: { label: 'DCH', description: 'Delete Ps Character(s)' },
|
||||
' q': { label: 'DECSCUSR', description: 'Set Cursor Style', paramCount: 1 }
|
||||
};
|
||||
for (const s of Object.keys(buttonSpecs)) {
|
||||
const spec = buttonSpecs[s];
|
||||
|
||||
@@ -437,7 +437,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
this.screenElement.appendChild(this._helperContainer);
|
||||
fragment.appendChild(this.screenElement);
|
||||
|
||||
this.textarea = this._document.createElement('textarea');
|
||||
const textarea = this.textarea = this._document.createElement('textarea');
|
||||
this.textarea.classList.add('xterm-helper-textarea');
|
||||
this.textarea.setAttribute('aria-label', Strings.promptLabel.get());
|
||||
if (!Browser.isChromeOS) {
|
||||
@@ -449,6 +449,8 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
this.textarea.setAttribute('autocapitalize', 'off');
|
||||
this.textarea.setAttribute('spellcheck', 'false');
|
||||
this.textarea.tabIndex = 0;
|
||||
this._register(this.optionsService.onSpecificOptionChange('disableStdin', () => textarea.readOnly = this.optionsService.rawOptions.disableStdin));
|
||||
this.textarea.readOnly = this.optionsService.rawOptions.disableStdin;
|
||||
|
||||
// Register the core browser service before the generic textarea handlers are registered so it
|
||||
// handles them first. Otherwise the renderers may use the wrong focus state.
|
||||
|
||||
@@ -13,7 +13,7 @@ import { ICharSizeService, ICoreBrowserService, IThemeService } from 'browser/se
|
||||
import { ILinkifier2, ILinkifierEvent, ITerminal, ReadonlyColorSet } from 'browser/Types';
|
||||
import { color } from 'common/Color';
|
||||
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IBufferService, IInstantiationService, IOptionsService } from 'common/services/Services';
|
||||
import { IBufferService, ICoreService, IInstantiationService, IOptionsService } from 'common/services/Services';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
@ICharSizeService private readonly _charSizeService: ICharSizeService,
|
||||
@IOptionsService private readonly _optionsService: IOptionsService,
|
||||
@IBufferService private readonly _bufferService: IBufferService,
|
||||
@ICoreService private readonly _coreService: ICoreService,
|
||||
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService,
|
||||
@IThemeService private readonly _themeService: IThemeService
|
||||
) {
|
||||
@@ -161,6 +162,10 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
// Base CSS
|
||||
let styles =
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} {` +
|
||||
// Disabling pointer events circumvents a browser behavior that prevents `click` events from
|
||||
// being delivered if the target element is replaced during the click. This happened due to
|
||||
// refresh() being called during the mousedown handler to start a selection.
|
||||
` pointer-events: none;` +
|
||||
` color: ${colors.foreground.css};` +
|
||||
` font-family: ${this._optionsService.rawOptions.fontFamily};` +
|
||||
` font-size: ${this._optionsService.rawOptions.fontSize}px;` +
|
||||
@@ -437,8 +442,8 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
const buffer = this._bufferService.buffer;
|
||||
const cursorAbsoluteY = buffer.ybase + buffer.y;
|
||||
const cursorX = Math.min(buffer.x, this._bufferService.cols - 1);
|
||||
const cursorBlink = this._optionsService.rawOptions.cursorBlink;
|
||||
const cursorStyle = this._optionsService.rawOptions.cursorStyle;
|
||||
const cursorBlink = this._coreService.decPrivateModes.cursorBlink ?? this._optionsService.rawOptions.cursorBlink;
|
||||
const cursorStyle = this._coreService.decPrivateModes.cursorStyle ?? this._optionsService.rawOptions.cursorStyle;
|
||||
const cursorInactiveStyle = this._optionsService.rawOptions.cursorInactiveStyle;
|
||||
|
||||
for (let y = start; y <= end; y++) {
|
||||
|
||||
@@ -82,8 +82,8 @@ export class ThemeService extends Disposable implements IThemeService {
|
||||
const colors = this._colors;
|
||||
colors.foreground = parseColor(theme.foreground, DEFAULT_FOREGROUND);
|
||||
colors.background = parseColor(theme.background, DEFAULT_BACKGROUND);
|
||||
colors.cursor = parseColor(theme.cursor, DEFAULT_CURSOR);
|
||||
colors.cursorAccent = parseColor(theme.cursorAccent, DEFAULT_CURSOR_ACCENT);
|
||||
colors.cursor = color.blend(colors.background, parseColor(theme.cursor, DEFAULT_CURSOR));
|
||||
colors.cursorAccent = color.blend(colors.background, parseColor(theme.cursorAccent, DEFAULT_CURSOR_ACCENT));
|
||||
colors.selectionBackgroundTransparent = parseColor(theme.selectionBackground, DEFAULT_SELECTION);
|
||||
colors.selectionBackgroundOpaque = color.blend(colors.background, colors.selectionBackgroundTransparent);
|
||||
colors.selectionInactiveBackgroundTransparent = parseColor(theme.selectionInactiveBackground, colors.selectionBackgroundTransparent);
|
||||
|
||||
@@ -202,38 +202,38 @@ describe('InputHandler', () => {
|
||||
describe('setCursorStyle', () => {
|
||||
it('should call Terminal.setOption with correct params', () => {
|
||||
inputHandler.setCursorStyle(Params.fromArray([0]));
|
||||
assert.equal(optionsService.options['cursorStyle'], 'block');
|
||||
assert.equal(optionsService.options['cursorBlink'], true);
|
||||
assert.equal(coreService.decPrivateModes.cursorStyle, undefined);
|
||||
assert.equal(coreService.decPrivateModes.cursorBlink, undefined);
|
||||
|
||||
optionsService.options = clone(DEFAULT_OPTIONS);
|
||||
inputHandler.setCursorStyle(Params.fromArray([1]));
|
||||
assert.equal(optionsService.options['cursorStyle'], 'block');
|
||||
assert.equal(optionsService.options['cursorBlink'], true);
|
||||
assert.equal(coreService.decPrivateModes.cursorStyle, 'block');
|
||||
assert.equal(coreService.decPrivateModes.cursorBlink, true);
|
||||
|
||||
optionsService.options = clone(DEFAULT_OPTIONS);
|
||||
inputHandler.setCursorStyle(Params.fromArray([2]));
|
||||
assert.equal(optionsService.options['cursorStyle'], 'block');
|
||||
assert.equal(optionsService.options['cursorBlink'], false);
|
||||
assert.equal(coreService.decPrivateModes.cursorStyle, 'block');
|
||||
assert.equal(coreService.decPrivateModes.cursorBlink, false);
|
||||
|
||||
optionsService.options = clone(DEFAULT_OPTIONS);
|
||||
inputHandler.setCursorStyle(Params.fromArray([3]));
|
||||
assert.equal(optionsService.options['cursorStyle'], 'underline');
|
||||
assert.equal(optionsService.options['cursorBlink'], true);
|
||||
assert.equal(coreService.decPrivateModes.cursorStyle, 'underline');
|
||||
assert.equal(coreService.decPrivateModes.cursorBlink, true);
|
||||
|
||||
optionsService.options = clone(DEFAULT_OPTIONS);
|
||||
inputHandler.setCursorStyle(Params.fromArray([4]));
|
||||
assert.equal(optionsService.options['cursorStyle'], 'underline');
|
||||
assert.equal(optionsService.options['cursorBlink'], false);
|
||||
assert.equal(coreService.decPrivateModes.cursorStyle, 'underline');
|
||||
assert.equal(coreService.decPrivateModes.cursorBlink, false);
|
||||
|
||||
optionsService.options = clone(DEFAULT_OPTIONS);
|
||||
inputHandler.setCursorStyle(Params.fromArray([5]));
|
||||
assert.equal(optionsService.options['cursorStyle'], 'bar');
|
||||
assert.equal(optionsService.options['cursorBlink'], true);
|
||||
assert.equal(coreService.decPrivateModes.cursorStyle, 'bar');
|
||||
assert.equal(coreService.decPrivateModes.cursorBlink, true);
|
||||
|
||||
optionsService.options = clone(DEFAULT_OPTIONS);
|
||||
inputHandler.setCursorStyle(Params.fromArray([6]));
|
||||
assert.equal(optionsService.options['cursorStyle'], 'bar');
|
||||
assert.equal(optionsService.options['cursorBlink'], false);
|
||||
assert.equal(coreService.decPrivateModes.cursorStyle, 'bar');
|
||||
assert.equal(coreService.decPrivateModes.cursorBlink, false);
|
||||
});
|
||||
});
|
||||
describe('setMode', () => {
|
||||
|
||||
+29
-23
@@ -2714,7 +2714,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
|
||||
/**
|
||||
* CSI Ps SP q Set cursor style (DECSCUSR, VT520).
|
||||
* Ps = 0 -> blinking block.
|
||||
* Ps = 0 -> reset to option.
|
||||
* Ps = 1 -> blinking block (default).
|
||||
* Ps = 2 -> steady block.
|
||||
* Ps = 3 -> blinking underline.
|
||||
@@ -2724,31 +2724,37 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
*
|
||||
* @vt: #Y CSI DECSCUSR "Set Cursor Style" "CSI Ps SP q" "Set cursor style."
|
||||
* Supported cursor styles:
|
||||
* - empty, 0 or 1: steady block
|
||||
* - 2: blink block
|
||||
* - 3: steady underline
|
||||
* - 4: blink underline
|
||||
* - 5: steady bar
|
||||
* - 6: blink bar
|
||||
* - 0: reset to option
|
||||
* - empty, 1: blinking block
|
||||
* - 2: steady block
|
||||
* - 3: blinking underline
|
||||
* - 4: steady underline
|
||||
* - 5: blinking bar
|
||||
* - 6: steady bar
|
||||
*/
|
||||
public setCursorStyle(params: IParams): boolean {
|
||||
const param = params.params[0] || 1;
|
||||
switch (param) {
|
||||
case 1:
|
||||
case 2:
|
||||
this._optionsService.options.cursorStyle = 'block';
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
this._optionsService.options.cursorStyle = 'underline';
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
this._optionsService.options.cursorStyle = 'bar';
|
||||
break;
|
||||
const param = params.length === 0 ? 1 : params.params[0];
|
||||
if (param === 0) {
|
||||
this._coreService.decPrivateModes.cursorStyle = undefined;
|
||||
this._coreService.decPrivateModes.cursorBlink = undefined;
|
||||
} else {
|
||||
switch (param) {
|
||||
case 1:
|
||||
case 2:
|
||||
this._coreService.decPrivateModes.cursorStyle = 'block';
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
this._coreService.decPrivateModes.cursorStyle = 'underline';
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
this._coreService.decPrivateModes.cursorStyle = 'bar';
|
||||
break;
|
||||
}
|
||||
const isBlinking = param % 2 === 1;
|
||||
this._coreService.decPrivateModes.cursorBlink = isBlinking;
|
||||
}
|
||||
const isBlinking = param % 2 === 1;
|
||||
this._optionsService.options.cursorBlink = isBlinking;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -89,6 +89,8 @@ export class MockCoreService implements ICoreService {
|
||||
applicationCursorKeys: false,
|
||||
applicationKeypad: false,
|
||||
bracketedPasteMode: false,
|
||||
cursorBlink: undefined,
|
||||
cursorStyle: undefined,
|
||||
origin: false,
|
||||
reverseWraparound: false,
|
||||
sendFocus: false,
|
||||
|
||||
@@ -268,6 +268,8 @@ export interface IDecPrivateModes {
|
||||
applicationCursorKeys: boolean;
|
||||
applicationKeypad: boolean;
|
||||
bracketedPasteMode: boolean;
|
||||
cursorBlink: boolean | undefined;
|
||||
cursorStyle: CursorStyle | undefined;
|
||||
origin: boolean;
|
||||
reverseWraparound: boolean;
|
||||
sendFocus: boolean;
|
||||
|
||||
@@ -17,6 +17,8 @@ const DEFAULT_DEC_PRIVATE_MODES: IDecPrivateModes = Object.freeze({
|
||||
applicationCursorKeys: false,
|
||||
applicationKeypad: false,
|
||||
bracketedPasteMode: false,
|
||||
cursorBlink: undefined,
|
||||
cursorStyle: undefined,
|
||||
origin: false,
|
||||
reverseWraparound: false,
|
||||
sendFocus: false,
|
||||
|
||||
@@ -67,7 +67,7 @@ export class InstantiationService implements IInstantiationService {
|
||||
for (const dependency of serviceDependencies) {
|
||||
const service = this._services.get(dependency.id);
|
||||
if (!service) {
|
||||
throw new Error(`[createInstance] ${ctor.name} depends on UNKNOWN service ${dependency.id}.`);
|
||||
throw new Error(`[createInstance] ${ctor.name} depends on UNKNOWN service ${dependency.id._id}.`);
|
||||
}
|
||||
serviceArgs.push(service);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ export function createDecorator<T>(id: string): IServiceIdentifier<T> {
|
||||
storeServiceDependency(decorator, target, index);
|
||||
};
|
||||
|
||||
decorator.toString = () => id;
|
||||
decorator._id = id;
|
||||
|
||||
serviceRegistry.set(id, decorator);
|
||||
return decorator;
|
||||
|
||||
@@ -124,6 +124,7 @@ export interface ICharsetService {
|
||||
export interface IServiceIdentifier<T> {
|
||||
(...args: any[]): void;
|
||||
type: T;
|
||||
_id: string;
|
||||
}
|
||||
|
||||
export interface IBrandedService {
|
||||
|
||||
@@ -1248,6 +1248,24 @@ export function injectSharedRendererTests(ctx: ISharedRendererTestContext): void
|
||||
await ctx.value.proxy.scrollLines(-2);
|
||||
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [0, 0, 0, 255]);
|
||||
});
|
||||
test('#5241 cursor with alpha should blend color with background color', async () => {
|
||||
const theme: ITheme = {
|
||||
cursor: '#FF000080'
|
||||
};
|
||||
await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`);
|
||||
await ctx.value.proxy.focus();
|
||||
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [128, 0, 0, 255]);
|
||||
});
|
||||
test('#5241 cursorAccent with alpha should blend color with background color', async () => {
|
||||
const theme: ITheme = {
|
||||
cursorAccent: '#FF000080'
|
||||
};
|
||||
await ctx.value.page.evaluate(`window.term.options.theme = ${JSON.stringify(theme)};`);
|
||||
await ctx.value.proxy.focus();
|
||||
await ctx.value.proxy.write('■');
|
||||
await ctx.value.proxy.write('\x1b[1D');
|
||||
await pollFor(ctx.value.page, () => getCellColor(ctx.value, 1, 1), [128, 0, 0, 255]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user