Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows, macOS and Linux transparency #52707

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c8410a6
Add WIP transparency implementation
sylveon Jun 20, 2018
f2a6835
Move windows-swca to dev dependencies
sylveon Jun 20, 2018
80e2134
Make Linux transparency functional, add macOS vibrancy
sylveon Jun 21, 2018
7b0758a
Add Windows 10 check
sylveon Jun 21, 2018
49b2ae6
Simplify Windows transparency check
sylveon Jun 21, 2018
57dd9bd
Add relauncher contributions, Windows 7 blur behind and version gate …
sylveon Jun 22, 2018
e0425aa
Add enumDescriptions for window.compositionAttribute
sylveon Jun 22, 2018
bcc6279
Fix various rendering bugs
sylveon Jun 23, 2018
b948bb0
Merge branch 'master' of https://github.com/Microsoft/vscode
sylveon Jun 23, 2018
005b173
Use app.getPath instead of getUserDataPath
sylveon Jun 23, 2018
05d04d3
Merge branch 'master' into swca
sylveon Aug 31, 2018
759338d
Add missing comma, fix build issue
sylveon Sep 7, 2018
cabf2a0
Fix build errors
sylveon Oct 1, 2018
c28bb4a
Merge branch 'master' of https://github.com/Microsoft/vscode into swca
sylveon Oct 1, 2018
e2c723f
Merge branch 'master' into swca
sylveon Oct 26, 2018
f7761cf
Fix build error
sylveon Nov 3, 2018
702ebd4
Merge branch 'master' into swca
sylveon Dec 1, 2018
2c30a0a
Fix hygiene checks
sylveon Dec 2, 2018
ba9fc59
Merge branch 'master' into swca
sylveon Dec 4, 2018
6649a25
Merge branch 'master' of https://github.com/Microsoft/vscode into swca
sylveon Jan 24, 2019
759afda
Merge branch 'master' of https://github.com/Microsoft/vscode into swca
sylveon Jan 25, 2019
4453508
Update windows-swca to latest version, drop windows 7 support
sylveon Mar 3, 2019
7e22533
Merge branch 'master' of https://github.com/Microsoft/vscode into swca
sylveon Mar 3, 2019
36b1ce2
Merge branch 'master' of https://github.com/Microsoft/vscode into swca
sylveon Apr 17, 2019
24f56c3
Just send 0 to SetWindowCompositionAttribute
sylveon Apr 17, 2019
c29d4c8
Fix build errors
sylveon Apr 17, 2019
23b9813
Merge branch 'master' of https://github.com/Microsoft/vscode into swca
sylveon Jul 2, 2019
dd3a6ac
Update acrylic effect description
sylveon Jul 2, 2019
cc027c4
Add hot reload of vibrancy and composition attribute
sylveon Jul 2, 2019
77f386e
Rename setWindowsCompositionAttribute
sylveon Jul 2, 2019
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
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,10 @@
"url": "https://github.com/Microsoft/vscode/issues"
},
"optionalDependencies": {
"windows-blurbehind": "1.0.0",
"windows-foreground-love": "0.1.0",
"windows-mutex": "^0.2.0",
"windows-process-tree": "0.2.2"
"windows-process-tree": "0.2.2",
"windows-swca": "1.1.1"
}
}
15 changes: 15 additions & 0 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,21 @@ global.getOpenUrls = function () {
return openUrls;
};

// Linux: disable hardware acceleration when transparent window is enabled
try {
if (process.platform === 'linux') {
const configFile = path.join(app.getPath('userData'), 'User', 'settings.json');
if (fs.existsSync(configFile)) {
const config = JSON.parse(stripComments(fs.readFileSync(configFile, 'utf8')));
if (config['window.transparent']) {
app.commandLine.appendSwitch('enable-transparent-visuals');
app.disableHardwareAcceleration();
}
}
}
} catch (err) {
console.error(err);
}

let nlsConfiguration = undefined;
let userDefinedLocale = getUserDefinedLocale();
Expand Down
61 changes: 58 additions & 3 deletions src/vs/code/electron-main/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import * as path from 'path';
import * as objects from 'vs/base/common/objects';
import * as os from 'os';
import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import { IStateService } from 'vs/platform/state/common/state';
Expand Down Expand Up @@ -116,6 +117,11 @@ export class CodeWindow implements ICodeWindow {
this.registerListeners();
}

private setTransparentInfo(options: Electron.BrowserWindowConstructorOptions): void {
options.backgroundColor = '#00000000';
this.stateService.setItem(CodeWindow.themeBackgroundStorageKey, 'transparent');
}

private createBrowserWindow(config: IWindowCreationOptions): void {

// Load window state
Expand All @@ -129,7 +135,7 @@ export class CodeWindow implements ICodeWindow {
backgroundColor = '#171717'; // https://github.com/electron/electron/issues/5150
}

const options: Electron.BrowserWindowConstructorOptions = {
let options: Electron.BrowserWindowConstructorOptions = {
width: this.windowState.width,
height: this.windowState.height,
x: this.windowState.x,
Expand All @@ -145,11 +151,17 @@ export class CodeWindow implements ICodeWindow {
}
};

const windowConfig = this.configurationService.getValue<IWindowSettings>('window');

if (isLinux) {
options.icon = path.join(this.environmentService.appRoot, 'resources/linux/code.png'); // Windows and Mac are better off using the embedded icon(s)
}

const windowConfig = this.configurationService.getValue<IWindowSettings>('window');
// Make sure hardware acceleration is actually disabled.
options.transparent = windowConfig && windowConfig.transparent && app.getGPUFeatureStatus().gpu_compositing !== 'enabled';
if (options.transparent) {
this.setTransparentInfo(options);
}
}

if (isMacintosh) {
options.acceptFirstMouse = true; // enabled by default
Expand All @@ -173,6 +185,11 @@ export class CodeWindow implements ICodeWindow {
if (isDev) {
useCustomTitleStyle = false; // not enabled when developing due to https://github.com/electron/electron/issues/3647
}

if (windowConfig && windowConfig.vibrancy && windowConfig.vibrancy !== 'none') {
this.setTransparentInfo(options);
options.vibrancy = windowConfig.vibrancy;
}
} else {
useCustomTitleStyle = windowConfig && windowConfig.titleBarStyle === 'custom'; // Must be specified on Windows/Linux
}
Expand All @@ -189,6 +206,14 @@ export class CodeWindow implements ICodeWindow {
}
}

const isWin10 = isWindows && parseFloat(os.release()) >= 10;
const needsWinTransparency =
isWindows && windowConfig && windowConfig.compositionAttribute && windowConfig.compositionAttribute !== 'none' &&
((isWin10 && useCustomTitleStyle) || (!isWin10 && windowConfig.compositionAttribute === 'blur'));
if (needsWinTransparency) {
this.setTransparentInfo(options);
}

// Create the browser window.
this._win = new BrowserWindow(options);
this._id = this._win.id;
Expand All @@ -197,6 +222,36 @@ export class CodeWindow implements ICodeWindow {
this._win.setSheetOffset(22); // offset dialogs by the height of the custom title bar if we have any
}

if (needsWinTransparency && isWin10) {
const { SetWindowCompositionAttribute, AccentState } = require.__$__nodeRequire('windows-swca') as any;

let attribValue = AccentState.ACCENT_DISABLED;
let color = 0x00000000;
switch (windowConfig.compositionAttribute) {
case 'acrylic':
// Fluent/acrylic flag was introduced in Windows 10 build 17063 (between FCU and April 2018 update)
if (parseInt(os.release().split('.')[2]) >= 17063) {
attribValue = AccentState.ACCENT_ENABLE_FLUENT;
color = 0x01000000; // using a small alpha because acrylic bugs out at full transparency.
}
break;

case 'blur':
attribValue = AccentState.ACCENT_ENABLE_BLURBEHIND;
break;

case 'transparent':
attribValue = AccentState.ACCENT_ENABLE_TRANSPARENTGRADIENT;
break;
}

SetWindowCompositionAttribute(this._win, attribValue, color);
} else if (needsWinTransparency) {
const { DwmEnableBlurBehindWindow } = require.__$__nodeRequire('windows-blurbehind') as any;

DwmEnableBlurBehindWindow(this._win, true);
}

if (isFullscreenOrMaximized) {
this._win.maximize();

Expand Down
3 changes: 2 additions & 1 deletion src/vs/editor/browser/viewParts/minimap/minimap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,7 @@ class MinimapBuffers {
const backgroundR = background.r;
const backgroundG = background.g;
const backgroundB = background.b;
const backgroundA = background.a;

let result = new Uint8ClampedArray(WIDTH * HEIGHT * 4);
let offset = 0;
Expand All @@ -425,7 +426,7 @@ class MinimapBuffers {
result[offset] = backgroundR;
result[offset + 1] = backgroundG;
result[offset + 2] = backgroundB;
result[offset + 3] = 255;
result[offset + 3] = backgroundA;
offset += 4;
}
}
Expand Down
30 changes: 30 additions & 0 deletions src/vs/editor/common/view/minimapCharRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,12 @@ export class MinimapCharRenderer {
const backgroundR = backgroundColor.r;
const backgroundG = backgroundColor.g;
const backgroundB = backgroundColor.b;
const backgroundA = backgroundColor.a;

const deltaR = color.r - backgroundR;
const deltaG = color.g - backgroundG;
const deltaB = color.b - backgroundB;
const deltaA = color.a - backgroundA;

const dest = target.data;
const sourceOffset = chIndex * Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH;
Expand All @@ -149,12 +151,14 @@ export class MinimapCharRenderer {
dest[destOffset + 0] = backgroundR + deltaR * c;
dest[destOffset + 1] = backgroundG + deltaG * c;
dest[destOffset + 2] = backgroundB + deltaB * c;
dest[destOffset + 3] = backgroundA + deltaA * c;
}
{
const c = x2CharData[sourceOffset + 1] / 255;
dest[destOffset + 4] = backgroundR + deltaR * c;
dest[destOffset + 5] = backgroundG + deltaG * c;
dest[destOffset + 6] = backgroundB + deltaB * c;
dest[destOffset + 7] = backgroundA + deltaA * c;
}

destOffset += outWidth;
Expand All @@ -163,12 +167,14 @@ export class MinimapCharRenderer {
dest[destOffset + 0] = backgroundR + deltaR * c;
dest[destOffset + 1] = backgroundG + deltaG * c;
dest[destOffset + 2] = backgroundB + deltaB * c;
dest[destOffset + 3] = backgroundA + deltaA * c;
}
{
const c = x2CharData[sourceOffset + 3] / 255;
dest[destOffset + 4] = backgroundR + deltaR * c;
dest[destOffset + 5] = backgroundG + deltaG * c;
dest[destOffset + 6] = backgroundB + deltaB * c;
dest[destOffset + 7] = backgroundA + deltaA * c;
}

destOffset += outWidth;
Expand All @@ -177,12 +183,14 @@ export class MinimapCharRenderer {
dest[destOffset + 0] = backgroundR + deltaR * c;
dest[destOffset + 1] = backgroundG + deltaG * c;
dest[destOffset + 2] = backgroundB + deltaB * c;
dest[destOffset + 3] = backgroundA + deltaA * c;
}
{
const c = x2CharData[sourceOffset + 5] / 255;
dest[destOffset + 4] = backgroundR + deltaR * c;
dest[destOffset + 5] = backgroundG + deltaG * c;
dest[destOffset + 6] = backgroundB + deltaB * c;
dest[destOffset + 7] = backgroundA + deltaA * c;
}

destOffset += outWidth;
Expand All @@ -191,12 +199,14 @@ export class MinimapCharRenderer {
dest[destOffset + 0] = backgroundR + deltaR * c;
dest[destOffset + 1] = backgroundG + deltaG * c;
dest[destOffset + 2] = backgroundB + deltaB * c;
dest[destOffset + 3] = backgroundA + deltaA * c;
}
{
const c = x2CharData[sourceOffset + 7] / 255;
dest[destOffset + 4] = backgroundR + deltaR * c;
dest[destOffset + 5] = backgroundG + deltaG * c;
dest[destOffset + 6] = backgroundB + deltaB * c;
dest[destOffset + 7] = backgroundA + deltaA * c;
}
}

Expand All @@ -213,10 +223,12 @@ export class MinimapCharRenderer {
const backgroundR = backgroundColor.r;
const backgroundG = backgroundColor.g;
const backgroundB = backgroundColor.b;
const backgroundA = backgroundColor.a;

const deltaR = color.r - backgroundR;
const deltaG = color.g - backgroundG;
const deltaB = color.b - backgroundB;
const deltaA = color.a - backgroundA;

const dest = target.data;
const sourceOffset = chIndex * Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH;
Expand All @@ -226,6 +238,7 @@ export class MinimapCharRenderer {
dest[destOffset + 0] = backgroundR + deltaR * c;
dest[destOffset + 1] = backgroundG + deltaG * c;
dest[destOffset + 2] = backgroundB + deltaB * c;
dest[destOffset + 3] = backgroundA + deltaA * c;
}

destOffset += outWidth;
Expand All @@ -234,6 +247,7 @@ export class MinimapCharRenderer {
dest[destOffset + 0] = backgroundR + deltaR * c;
dest[destOffset + 1] = backgroundG + deltaG * c;
dest[destOffset + 2] = backgroundB + deltaB * c;
dest[destOffset + 3] = backgroundA + deltaA * c;
}
}

Expand All @@ -250,62 +264,73 @@ export class MinimapCharRenderer {
const backgroundR = backgroundColor.r;
const backgroundG = backgroundColor.g;
const backgroundB = backgroundColor.b;
const backgroundA = backgroundColor.a;

const deltaR = color.r - backgroundR;
const deltaG = color.g - backgroundG;
const deltaB = color.b - backgroundB;
const deltaA = color.a - backgroundA;

const colorR = backgroundR + deltaR * c;
const colorG = backgroundG + deltaG * c;
const colorB = backgroundB + deltaB * c;
const colorA = backgroundA + deltaA * c;

const dest = target.data;
let destOffset = dy * outWidth + dx * Constants.RGBA_CHANNELS_CNT;
{
dest[destOffset + 0] = colorR;
dest[destOffset + 1] = colorG;
dest[destOffset + 2] = colorB;
dest[destOffset + 3] = colorA;
}
{
dest[destOffset + 4] = colorR;
dest[destOffset + 5] = colorG;
dest[destOffset + 6] = colorB;
dest[destOffset + 7] = colorA;
}

destOffset += outWidth;
{
dest[destOffset + 0] = colorR;
dest[destOffset + 1] = colorG;
dest[destOffset + 2] = colorB;
dest[destOffset + 3] = colorA;
}
{
dest[destOffset + 4] = colorR;
dest[destOffset + 5] = colorG;
dest[destOffset + 6] = colorB;
dest[destOffset + 7] = colorA;
}

destOffset += outWidth;
{
dest[destOffset + 0] = colorR;
dest[destOffset + 1] = colorG;
dest[destOffset + 2] = colorB;
dest[destOffset + 3] = colorA;
}
{
dest[destOffset + 4] = colorR;
dest[destOffset + 5] = colorG;
dest[destOffset + 6] = colorB;
dest[destOffset + 7] = colorA;
}

destOffset += outWidth;
{
dest[destOffset + 0] = colorR;
dest[destOffset + 1] = colorG;
dest[destOffset + 2] = colorB;
dest[destOffset + 3] = colorA;
}
{
dest[destOffset + 4] = colorR;
dest[destOffset + 5] = colorG;
dest[destOffset + 6] = colorB;
dest[destOffset + 7] = colorA;
}
}

Expand All @@ -322,14 +347,17 @@ export class MinimapCharRenderer {
const backgroundR = backgroundColor.r;
const backgroundG = backgroundColor.g;
const backgroundB = backgroundColor.b;
const backgroundA = backgroundColor.a;

const deltaR = color.r - backgroundR;
const deltaG = color.g - backgroundG;
const deltaB = color.b - backgroundB;
const deltaA = color.a - backgroundA;

const colorR = backgroundR + deltaR * c;
const colorG = backgroundG + deltaG * c;
const colorB = backgroundB + deltaB * c;
const colorA = backgroundA + deltaA * c;

const dest = target.data;

Expand All @@ -338,13 +366,15 @@ export class MinimapCharRenderer {
dest[destOffset + 0] = colorR;
dest[destOffset + 1] = colorG;
dest[destOffset + 2] = colorB;
dest[destOffset + 3] = colorA;
}

destOffset += outWidth;
{
dest[destOffset + 0] = colorR;
dest[destOffset + 1] = colorG;
dest[destOffset + 2] = colorB;
dest[destOffset + 3] = colorA;
}
}
}
3 changes: 3 additions & 0 deletions src/vs/platform/windows/common/windows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,9 @@ export interface IWindowSettings {
closeWhenEmpty: boolean;
smoothScrollingWorkaround: boolean;
clickThroughInactive: boolean;
transparent: boolean;
compositionAttribute: 'none' | 'transparent' | 'blur' | 'acrylic';
vibrancy: 'none' | 'appearance-based' | 'light' | 'dark' | 'titlebar' | 'medium-light' | 'ultra-dark';
}

export enum OpenContext {
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/browser/parts/editor/tabsTitleControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1109,7 +1109,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {

// Fade out styles via linear gradient (when tabs are set to shrink)
if (theme.type !== 'hc') {
const workbenchBackground = WORKBENCH_BACKGROUND(theme);
const workbenchBackground = theme.getColor(WORKBENCH_BACKGROUND);
const editorBackgroundColor = theme.getColor(editorBackground);
const editorGroupHeaderTabsBackground = theme.getColor(EDITOR_GROUP_HEADER_TABS_BACKGROUND);
const editorDragAndDropBackground = theme.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND);
Expand Down
Loading