Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/host/electron-browser/nativeHostService.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 { Emitter, Event } from '../../../../base/common/event.js';
7
import { IHostService } from '../browser/host.js';
8
import { FocusMode, INativeHostService } from '../../../../platform/native/common/native.js';
9
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
10
import { ILabelService, Verbosity } from '../../../../platform/label/common/label.js';
11
import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js';
12
import { IWindowOpenable, IOpenWindowOptions, isFolderToOpen, isWorkspaceToOpen, IOpenEmptyWindowOptions, IPoint, IRectangle } from '../../../../platform/window/common/window.js';
13
import { Disposable } from '../../../../base/common/lifecycle.js';
14
import { NativeHostService } from '../../../../platform/native/common/nativeHostService.js';
15
import { INativeWorkbenchEnvironmentService } from '../../environment/electron-browser/environmentService.js';
16
import { IMainProcessService } from '../../../../platform/ipc/common/mainProcessService.js';
17
import { disposableWindowInterval, getActiveDocument, getWindowId, getWindowsCount, hasWindow, onDidRegisterWindow } from '../../../../base/browser/dom.js';
18
import { memoize } from '../../../../base/common/decorators.js';
19
import { isAuxiliaryWindow } from '../../../../base/browser/window.js';
20
import { VSBuffer } from '../../../../base/common/buffer.js';
21
22
class WorkbenchNativeHostService extends NativeHostService {
23
24
constructor(
25
@INativeWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService,
26
@IMainProcessService mainProcessService: IMainProcessService
27
) {
28
super(environmentService.window.id, mainProcessService);
29
}
30
}
31
32
class WorkbenchHostService extends Disposable implements IHostService {
33
34
declare readonly _serviceBrand: undefined;
35
36
constructor(
37
@INativeHostService private readonly nativeHostService: INativeHostService,
38
@ILabelService private readonly labelService: ILabelService,
39
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
40
) {
41
super();
42
43
this.onDidChangeFocus = Event.latch(
44
Event.any(
45
Event.map(Event.filter(this.nativeHostService.onDidFocusMainOrAuxiliaryWindow, id => hasWindow(id), this._store), () => this.hasFocus, this._store),
46
Event.map(Event.filter(this.nativeHostService.onDidBlurMainOrAuxiliaryWindow, id => hasWindow(id), this._store), () => this.hasFocus, this._store),
47
Event.map(this.onDidChangeActiveWindow, () => this.hasFocus, this._store)
48
), undefined, this._store
49
);
50
51
this.onDidChangeFullScreen = Event.filter(this.nativeHostService.onDidChangeWindowFullScreen, e => hasWindow(e.windowId), this._store);
52
}
53
54
//#region Focus
55
56
readonly onDidChangeFocus: Event<boolean>;
57
58
get hasFocus(): boolean {
59
return getActiveDocument().hasFocus();
60
}
61
62
async hadLastFocus(): Promise<boolean> {
63
const activeWindowId = await this.nativeHostService.getActiveWindowId();
64
65
if (typeof activeWindowId === 'undefined') {
66
return false;
67
}
68
69
return activeWindowId === this.nativeHostService.windowId;
70
}
71
72
//#endregion
73
74
//#region Window
75
76
@memoize
77
get onDidChangeActiveWindow(): Event<number> {
78
const emitter = this._register(new Emitter<number>());
79
80
// Emit via native focus tracking
81
this._register(Event.filter(this.nativeHostService.onDidFocusMainOrAuxiliaryWindow, id => hasWindow(id), this._store)(id => emitter.fire(id)));
82
83
this._register(onDidRegisterWindow(({ window, disposables }) => {
84
85
// Emit via interval: immediately when opening an auxiliary window,
86
// it is possible that document focus has not yet changed, so we
87
// poll for a while to ensure we catch the event.
88
disposables.add(disposableWindowInterval(window, () => {
89
const hasFocus = window.document.hasFocus();
90
if (hasFocus) {
91
emitter.fire(window.vscodeWindowId);
92
}
93
94
return hasFocus;
95
}, 100, 20));
96
}));
97
98
return Event.latch(emitter.event, undefined, this._store);
99
}
100
101
readonly onDidChangeFullScreen: Event<{ readonly windowId: number; readonly fullscreen: boolean }>;
102
103
openWindow(options?: IOpenEmptyWindowOptions): Promise<void>;
104
openWindow(toOpen: IWindowOpenable[], options?: IOpenWindowOptions): Promise<void>;
105
openWindow(arg1?: IOpenEmptyWindowOptions | IWindowOpenable[], arg2?: IOpenWindowOptions): Promise<void> {
106
if (Array.isArray(arg1)) {
107
return this.doOpenWindow(arg1, arg2);
108
}
109
110
return this.doOpenEmptyWindow(arg1);
111
}
112
113
private doOpenWindow(toOpen: IWindowOpenable[], options?: IOpenWindowOptions): Promise<void> {
114
const remoteAuthority = this.environmentService.remoteAuthority;
115
if (!!remoteAuthority) {
116
toOpen.forEach(openable => openable.label = openable.label || this.getRecentLabel(openable));
117
118
if (options?.remoteAuthority === undefined) {
119
// set the remoteAuthority of the window the request came from.
120
// It will be used when the input is neither file nor vscode-remote.
121
options = options ? { ...options, remoteAuthority } : { remoteAuthority };
122
}
123
}
124
125
return this.nativeHostService.openWindow(toOpen, options);
126
}
127
128
private getRecentLabel(openable: IWindowOpenable): string {
129
if (isFolderToOpen(openable)) {
130
return this.labelService.getWorkspaceLabel(openable.folderUri, { verbose: Verbosity.LONG });
131
}
132
133
if (isWorkspaceToOpen(openable)) {
134
return this.labelService.getWorkspaceLabel({ id: '', configPath: openable.workspaceUri }, { verbose: Verbosity.LONG });
135
}
136
137
return this.labelService.getUriLabel(openable.fileUri, { appendWorkspaceSuffix: true });
138
}
139
140
private doOpenEmptyWindow(options?: IOpenEmptyWindowOptions): Promise<void> {
141
const remoteAuthority = this.environmentService.remoteAuthority;
142
if (!!remoteAuthority && options?.remoteAuthority === undefined) {
143
// set the remoteAuthority of the window the request came from
144
options = options ? { ...options, remoteAuthority } : { remoteAuthority };
145
}
146
return this.nativeHostService.openWindow(options);
147
}
148
149
toggleFullScreen(targetWindow: Window): Promise<void> {
150
return this.nativeHostService.toggleFullScreen({ targetWindowId: isAuxiliaryWindow(targetWindow) ? targetWindow.vscodeWindowId : undefined });
151
}
152
153
async moveTop(targetWindow: Window): Promise<void> {
154
if (getWindowsCount() <= 1) {
155
return; // does not apply when only one window is opened
156
}
157
158
return this.nativeHostService.moveWindowTop(isAuxiliaryWindow(targetWindow) ? { targetWindowId: targetWindow.vscodeWindowId } : undefined);
159
}
160
161
getCursorScreenPoint(): Promise<{ readonly point: IPoint; readonly display: IRectangle }> {
162
return this.nativeHostService.getCursorScreenPoint();
163
}
164
165
//#endregion
166
167
//#region Lifecycle
168
169
focus(targetWindow: Window, options?: { mode?: FocusMode }): Promise<void> {
170
return this.nativeHostService.focusWindow({
171
mode: options?.mode,
172
targetWindowId: getWindowId(targetWindow)
173
});
174
}
175
176
restart(): Promise<void> {
177
return this.nativeHostService.relaunch();
178
}
179
180
reload(options?: { disableExtensions?: boolean }): Promise<void> {
181
return this.nativeHostService.reload(options);
182
}
183
184
close(): Promise<void> {
185
return this.nativeHostService.closeWindow();
186
}
187
188
async withExpectedShutdown<T>(expectedShutdownTask: () => Promise<T>): Promise<T> {
189
return await expectedShutdownTask();
190
}
191
192
//#endregion
193
194
//#region Screenshots
195
196
getScreenshot(rect?: IRectangle): Promise<VSBuffer | undefined> {
197
return this.nativeHostService.getScreenshot(rect);
198
}
199
200
//#endregion
201
202
//#region Native Handle
203
204
private _nativeWindowHandleCache = new Map<number, Promise<VSBuffer | undefined>>();
205
async getNativeWindowHandle(windowId: number): Promise<VSBuffer | undefined> {
206
if (!this._nativeWindowHandleCache.has(windowId)) {
207
this._nativeWindowHandleCache.set(windowId, this.nativeHostService.getNativeWindowHandle(windowId));
208
}
209
return this._nativeWindowHandleCache.get(windowId)!;
210
}
211
212
//#endregion
213
}
214
215
registerSingleton(IHostService, WorkbenchHostService, InstantiationType.Delayed);
216
registerSingleton(INativeHostService, WorkbenchNativeHostService, InstantiationType.Delayed);
217
218