Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/vs/platform/terminal/common/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export const enum TerminalSettingId {
EnableMultiLinePasteWarning = 'terminal.integrated.enableMultiLinePasteWarning',
DrawBoldTextInBrightColors = 'terminal.integrated.drawBoldTextInBrightColors',
FontFamily = 'terminal.integrated.fontFamily',
FontRendering = 'terminal.integrated.fontRendering',
FontSize = 'terminal.integrated.fontSize',
LetterSpacing = 'terminal.integrated.letterSpacing',
LineHeight = 'terminal.integrated.lineHeight',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
.xterm.terminal-font-rendering-crisp {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import type { Terminal } from '@xterm/xterm';
import { isMacintosh } from '../../../../../base/common/platform.js';
import type { ITerminalConfiguration } from '../../common/terminal.js';
import './terminalFontRendering.css';

const enum CssClasses {
Crisp = 'terminal-font-rendering-crisp'
}

export function updateTerminalFontRendering(terminal: Terminal, fontRendering: ITerminalConfiguration['fontRendering']): void {
const element = terminal.element;
if (!element) {
return;
}

const crisp = isMacintosh && fontRendering === 'crisp';
if (element.classList.contains(CssClasses.Crisp) === crisp) {
return;
}
element.classList.toggle(CssClasses.Crisp, crisp);

// The atlas canvas inherits this policy, but cached glyphs retain their previous pixels.
terminal.clearTextureAtlas();
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import { isNumber } from '../../../../../base/common/types.js';
import { clamp } from '../../../../../base/common/numbers.js';
import { LayoutSettings } from '../../../../services/layout/browser/layoutService.js';
import { ILifecycleService } from '../../../../services/lifecycle/common/lifecycle.js';
import { updateTerminalFontRendering } from './terminalFontRendering.js';

const enum RenderConstants {
SmoothScrollDuration = 125
Expand Down Expand Up @@ -505,6 +506,8 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach
this.raw.open(container);
}

updateTerminalFontRendering(this.raw, this._terminalConfigurationService.config.fontRendering);

// TODO: Move before open so the DOM renderer doesn't initialize
if (options.enableGpu) {
if (this._shouldLoadWebgl()) {
Expand Down Expand Up @@ -589,6 +592,7 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach

updateConfig(): void {
const config = this._terminalConfigurationService.config;
updateTerminalFontRendering(this.raw, config.fontRendering);
this.raw.options.altClickMovesCursor = config.altClickMovesCursor;
this._setCursorBlink(config.cursorBlinking);
this._setTextBlinking(config.textBlinking);
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/contrib/terminal/common/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ export interface ITerminalConfiguration {
drawBoldTextInBrightColors: boolean;
fastScrollSensitivity: number;
fontFamily: string;
fontRendering: 'inherit' | 'crisp';
fontWeight: FontWeight;
fontWeightBold: FontWeight;
minimumContrastRatio: number;
Expand Down
11 changes: 11 additions & 0 deletions src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,17 @@ const terminalConfiguration: IStringDictionary<IConfigurationPropertySchema> = {
markdownDescription: localize('terminal.integrated.fontFamily', "Controls the font family of the terminal. Defaults to {0}'s value.", '`#editor.fontFamily#`'),
type: 'string',
},
[TerminalSettingId.FontRendering]: {
markdownDescription: localize('terminal.integrated.fontRendering', "Controls the font rendering style of terminal text on macOS without changing theme colors, font size, or font weight."),
type: 'string',
enum: ['inherit', 'crisp'],
markdownEnumDescriptions: [
localize('terminal.integrated.fontRendering.inherit', "Use the workbench font smoothing configured by {0}.", '`#workbench.fontAliasing#`'),
localize('terminal.integrated.fontRendering.crisp', "Use pixel-level antialiasing on high-DPI displays. Text may appear lighter and more defined. On other displays, inherit the workbench font smoothing.")
],
default: 'inherit',
included: isMacintosh
},
[TerminalSettingId.FontLigaturesEnabled]: {
markdownDescription: localize('terminal.integrated.fontLigatures.enabled', "Controls whether font ligatures are enabled in the terminal. Ligatures will only work if the configured {0} supports them.", `\`#${TerminalSettingId.FontFamily}#\``),
type: 'boolean',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { timeout } from '../../../../../../base/common/async.js';
import { Color, RGBA } from '../../../../../../base/common/color.js';
import { Emitter } from '../../../../../../base/common/event.js';
import { toDisposable } from '../../../../../../base/common/lifecycle.js';
import { isMacintosh } from '../../../../../../base/common/platform.js';
import { mock } from '../../../../../../base/test/common/mock.js';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js';
import { IEditorOptions } from '../../../../../../editor/common/config/editorOptions.js';
Expand All @@ -28,6 +29,7 @@ import { registerColors, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_C
import { workbenchInstantiationService } from '../../../../../test/browser/workbenchTestServices.js';
import { TestLifecycleService } from '../../../../../test/common/workbenchTestServices.js';
import { TestWebglAddon, TestXtermAddonImporter } from './xtermTestUtils.js';
import { stub } from 'sinon';

registerColors();

Expand All @@ -53,6 +55,7 @@ export class TestViewDescriptorService implements Partial<IViewDescriptorService

const defaultTerminalConfig: Partial<ITerminalConfiguration> = {
fontFamily: 'monospace',
fontRendering: 'inherit',
fontWeight: 'normal',
fontWeightBold: 'normal',
gpuAcceleration: 'off',
Expand Down Expand Up @@ -125,6 +128,86 @@ suite('XtermTerminal', () => {
strictEqual(xterm.raw.rows, 30);
});

suite('fontRendering', () => {
async function setFontRendering(fontRendering: 'inherit' | 'crisp'): Promise<void> {
await configurationService.setUserConfiguration('terminal.integrated', {
...defaultTerminalConfig,
fontRendering
});
configurationService.onDidChangeConfigurationEmitter.fire(new class extends mock<IConfigurationChangeEvent>() {
override affectsConfiguration(section: string): boolean {
return section.startsWith('terminal.integrated');
}
});
}

function attach(terminal: XtermTerminal = xterm): HTMLElement {
const container = document.createElement('div');
document.body.appendChild(container);
store.add(toDisposable(() => container.remove()));
terminal.attachToElement(container, { enableGpu: false });
return container;
}

test('inherits the workbench font policy by default', () => {
attach();
strictEqual(xterm.raw.element!.classList.contains('terminal-font-rendering-crisp'), false);
});

test('applies the configured policy when the terminal is opened', async () => {
await setFontRendering('crisp');
attach();
strictEqual(xterm.raw.element!.classList.contains('terminal-font-rendering-crisp'), isMacintosh);
});

test('refreshes the atlas after changing the policy without changing font options', async () => {
attach();
const initialOptions = {
fontFamily: xterm.raw.options.fontFamily,
fontSize: xterm.raw.options.fontSize,
fontWeight: xterm.raw.options.fontWeight,
theme: xterm.raw.options.theme
};
const statesAtRedraw: boolean[] = [];
const listener = stub(xterm.raw, 'clearTextureAtlas').callsFake(() => {
statesAtRedraw.push(xterm.raw.element!.classList.contains('terminal-font-rendering-crisp'));
});
store.add(toDisposable(() => listener.restore()));
await setFontRendering('crisp');
await setFontRendering('crisp');
await setFontRendering('inherit');
deepStrictEqual({
statesAtRedraw,
fontFamily: xterm.raw.options.fontFamily,
fontSize: xterm.raw.options.fontSize,
fontWeight: xterm.raw.options.fontWeight,
theme: xterm.raw.options.theme
}, {
statesAtRedraw: isMacintosh ? [true, false] : [],
...initialOptions
});
});

test('applies updates forwarded to detached terminals', async () => {
const terminal = store.add(instantiationService.createInstance(XtermTerminal, undefined, XTermBaseCtor, {
cols: 80,
rows: 30,
xtermColorProvider: { getBackgroundColor: () => undefined },
capabilities: store.add(new TerminalCapabilityStore()),
disableShellIntegrationReporting: true,
xtermAddonImporter: new TestXtermAddonImporter(),
detached: true
}, undefined));
attach(terminal);
await setFontRendering('crisp');
terminal.updateConfig();
strictEqual(terminal.raw.element!.classList.contains('terminal-font-rendering-crisp'), isMacintosh);
await setFontRendering('inherit');
terminal.updateConfig();
strictEqual(terminal.raw.element!.classList.contains('terminal-font-rendering-crisp'), false);
});
});

test('detached terminals do not register decoration shutdown listeners', () => {
const listenerCountAfterRegularXterm = listenerCount(onWillShutdown);
for (let index = 0; index < 50; index++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { terminalStrings } from '../../../terminal/common/terminalStrings.js';
import { TerminalStickyScrollSettingId } from '../common/terminalStickyScrollConfiguration.js';
import { terminalStickyScrollBackground, terminalStickyScrollHoverBackground } from './terminalStickyScrollColorRegistry.js';
import { XtermAddonImporter } from '../../../terminal/browser/xterm/xtermAddonImporter.js';
import { updateTerminalFontRendering } from '../../../terminal/browser/xterm/terminalFontRendering.js';

const enum OverlayState {
/** Initial state/disabled by the alt buffer. */
Expand Down Expand Up @@ -422,6 +423,7 @@ export class TerminalStickyScrollOverlay extends Disposable {
}

this._stickyScrollOverlay.open(this._element);
updateTerminalFontRendering(this._stickyScrollOverlay, this._terminalConfigurationService.config.fontRendering);

// Prevent tab key from being handled by the xterm overlay to allow natural tab navigation
this._stickyScrollOverlay.attachCustomKeyEventHandler((event: KeyboardEvent) => {
Expand Down Expand Up @@ -476,6 +478,7 @@ export class TerminalStickyScrollOverlay extends Disposable {
}
this._stickyScrollOverlay.resize(this._xterm.raw.cols, this._stickyScrollOverlay.rows);
this._stickyScrollOverlay.options = this._getOptions();
updateTerminalFontRendering(this._stickyScrollOverlay, this._terminalConfigurationService.config.fontRendering);
this._refreshGpuAcceleration();
}

Expand Down