Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindowsMainService.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { BrowserWindow, BrowserWindowConstructorOptions, HandlerDetails, WebContents, app } from 'electron';
7
import { Emitter, Event } from '../../../base/common/event.js';
8
import { Disposable, DisposableStore, toDisposable } from '../../../base/common/lifecycle.js';
9
import { FileAccess } from '../../../base/common/network.js';
10
import { validatedIpcMain } from '../../../base/parts/ipc/electron-main/ipcMain.js';
11
import { AuxiliaryWindow, IAuxiliaryWindow } from './auxiliaryWindow.js';
12
import { IAuxiliaryWindowsMainService } from './auxiliaryWindows.js';
13
import { IInstantiationService } from '../../instantiation/common/instantiation.js';
14
import { ILogService } from '../../log/common/log.js';
15
import { IWindowState, WindowMode, defaultAuxWindowState } from '../../window/electron-main/window.js';
16
import { IDefaultBrowserWindowOptionsOverrides, WindowStateValidator, defaultBrowserWindowOptions, getLastFocused } from '../../windows/electron-main/windows.js';
17
18
export class AuxiliaryWindowsMainService extends Disposable implements IAuxiliaryWindowsMainService {
19
20
declare readonly _serviceBrand: undefined;
21
22
private readonly _onDidMaximizeWindow = this._register(new Emitter<IAuxiliaryWindow>());
23
readonly onDidMaximizeWindow = this._onDidMaximizeWindow.event;
24
25
private readonly _onDidUnmaximizeWindow = this._register(new Emitter<IAuxiliaryWindow>());
26
readonly onDidUnmaximizeWindow = this._onDidUnmaximizeWindow.event;
27
28
private readonly _onDidChangeFullScreen = this._register(new Emitter<{ window: IAuxiliaryWindow; fullscreen: boolean }>());
29
readonly onDidChangeFullScreen = this._onDidChangeFullScreen.event;
30
31
private readonly _onDidChangeAlwaysOnTop = this._register(new Emitter<{ window: IAuxiliaryWindow; alwaysOnTop: boolean }>());
32
readonly onDidChangeAlwaysOnTop = this._onDidChangeAlwaysOnTop.event;
33
34
private readonly _onDidTriggerSystemContextMenu = this._register(new Emitter<{ window: IAuxiliaryWindow; x: number; y: number }>());
35
readonly onDidTriggerSystemContextMenu = this._onDidTriggerSystemContextMenu.event;
36
37
private readonly windows = new Map<number /* webContents ID */, AuxiliaryWindow>();
38
39
constructor(
40
@IInstantiationService private readonly instantiationService: IInstantiationService,
41
@ILogService private readonly logService: ILogService
42
) {
43
super();
44
45
this.registerListeners();
46
}
47
48
private registerListeners(): void {
49
50
// We have to ensure that an auxiliary window gets to know its
51
// containing `BrowserWindow` so that it can apply listeners to it
52
// Unfortunately we cannot rely on static `BrowserWindow` methods
53
// because we might call the methods too early before the window
54
// is created.
55
56
app.on('browser-window-created', (_event, browserWindow) => {
57
58
// This is an auxiliary window, try to claim it
59
const auxiliaryWindow = this.getWindowByWebContents(browserWindow.webContents);
60
if (auxiliaryWindow) {
61
this.logService.trace('[aux window] app.on("browser-window-created"): Trying to claim auxiliary window');
62
63
auxiliaryWindow.tryClaimWindow();
64
}
65
66
// This is a main window, listen to child windows getting created to claim it
67
else {
68
const disposables = new DisposableStore();
69
disposables.add(Event.fromNodeEventEmitter(browserWindow.webContents, 'did-create-window', (browserWindow, details) => ({ browserWindow, details }))(({ browserWindow, details }) => {
70
const auxiliaryWindow = this.getWindowByWebContents(browserWindow.webContents);
71
if (auxiliaryWindow) {
72
this.logService.trace('[aux window] window.on("did-create-window"): Trying to claim auxiliary window');
73
74
auxiliaryWindow.tryClaimWindow(details.options);
75
}
76
}));
77
disposables.add(Event.fromNodeEventEmitter(browserWindow, 'closed')(() => disposables.dispose()));
78
}
79
});
80
81
validatedIpcMain.handle('vscode:registerAuxiliaryWindow', async (event, mainWindowId: number) => {
82
const auxiliaryWindow = this.getWindowByWebContents(event.sender);
83
if (auxiliaryWindow) {
84
this.logService.trace('[aux window] vscode:registerAuxiliaryWindow: Registering auxiliary window to main window');
85
86
auxiliaryWindow.parentId = mainWindowId;
87
}
88
89
return event.sender.id;
90
});
91
}
92
93
createWindow(details: HandlerDetails): BrowserWindowConstructorOptions {
94
const { state, overrides } = this.computeWindowStateAndOverrides(details);
95
return this.instantiationService.invokeFunction(defaultBrowserWindowOptions, state, overrides, {
96
preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-browser/preload-aux.js').fsPath
97
});
98
}
99
100
private computeWindowStateAndOverrides(details: HandlerDetails): { readonly state: IWindowState; readonly overrides: IDefaultBrowserWindowOptionsOverrides } {
101
const windowState: IWindowState = {};
102
const overrides: IDefaultBrowserWindowOptionsOverrides = {};
103
104
const features = details.features.split(','); // for example: popup=yes,left=270,top=14.5,width=1024,height=768
105
for (const feature of features) {
106
const [key, value] = feature.split('=');
107
switch (key) {
108
case 'width':
109
windowState.width = parseInt(value, 10);
110
break;
111
case 'height':
112
windowState.height = parseInt(value, 10);
113
break;
114
case 'left':
115
windowState.x = parseInt(value, 10);
116
break;
117
case 'top':
118
windowState.y = parseInt(value, 10);
119
break;
120
case 'window-maximized':
121
windowState.mode = WindowMode.Maximized;
122
break;
123
case 'window-fullscreen':
124
windowState.mode = WindowMode.Fullscreen;
125
break;
126
case 'window-disable-fullscreen':
127
overrides.disableFullscreen = true;
128
break;
129
case 'window-native-titlebar':
130
overrides.forceNativeTitlebar = true;
131
break;
132
case 'window-always-on-top':
133
overrides.alwaysOnTop = true;
134
break;
135
}
136
}
137
138
const state = WindowStateValidator.validateWindowState(this.logService, windowState) ?? defaultAuxWindowState();
139
140
this.logService.trace('[aux window] using window state', state);
141
142
return { state, overrides };
143
}
144
145
registerWindow(webContents: WebContents): void {
146
const disposables = new DisposableStore();
147
148
const auxiliaryWindow = this.instantiationService.createInstance(AuxiliaryWindow, webContents);
149
150
this.windows.set(auxiliaryWindow.id, auxiliaryWindow);
151
disposables.add(toDisposable(() => this.windows.delete(auxiliaryWindow.id)));
152
153
disposables.add(auxiliaryWindow.onDidMaximize(() => this._onDidMaximizeWindow.fire(auxiliaryWindow)));
154
disposables.add(auxiliaryWindow.onDidUnmaximize(() => this._onDidUnmaximizeWindow.fire(auxiliaryWindow)));
155
disposables.add(auxiliaryWindow.onDidEnterFullScreen(() => this._onDidChangeFullScreen.fire({ window: auxiliaryWindow, fullscreen: true })));
156
disposables.add(auxiliaryWindow.onDidLeaveFullScreen(() => this._onDidChangeFullScreen.fire({ window: auxiliaryWindow, fullscreen: false })));
157
disposables.add(auxiliaryWindow.onDidChangeAlwaysOnTop(alwaysOnTop => this._onDidChangeAlwaysOnTop.fire({ window: auxiliaryWindow, alwaysOnTop })));
158
disposables.add(auxiliaryWindow.onDidTriggerSystemContextMenu(({ x, y }) => this._onDidTriggerSystemContextMenu.fire({ window: auxiliaryWindow, x, y })));
159
160
Event.once(auxiliaryWindow.onDidClose)(() => disposables.dispose());
161
}
162
163
getWindowByWebContents(webContents: WebContents): AuxiliaryWindow | undefined {
164
const window = this.windows.get(webContents.id);
165
166
return window?.matches(webContents) ? window : undefined;
167
}
168
169
getFocusedWindow(): IAuxiliaryWindow | undefined {
170
const window = BrowserWindow.getFocusedWindow();
171
if (window) {
172
return this.getWindowByWebContents(window.webContents);
173
}
174
175
return undefined;
176
}
177
178
getLastActiveWindow(): IAuxiliaryWindow | undefined {
179
return getLastFocused(Array.from(this.windows.values()));
180
}
181
182
getWindows(): readonly IAuxiliaryWindow[] {
183
return Array.from(this.windows.values());
184
}
185
}
186
187