Path: blob/main/src/vs/workbench/services/host/electron-browser/nativeHostService.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Emitter, Event } from '../../../../base/common/event.js';6import { IHostService } from '../browser/host.js';7import { FocusMode, INativeHostService } from '../../../../platform/native/common/native.js';8import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';9import { ILabelService, Verbosity } from '../../../../platform/label/common/label.js';10import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js';11import { IWindowOpenable, IOpenWindowOptions, isFolderToOpen, isWorkspaceToOpen, IOpenEmptyWindowOptions, IPoint, IRectangle } from '../../../../platform/window/common/window.js';12import { Disposable } from '../../../../base/common/lifecycle.js';13import { NativeHostService } from '../../../../platform/native/common/nativeHostService.js';14import { INativeWorkbenchEnvironmentService } from '../../environment/electron-browser/environmentService.js';15import { IMainProcessService } from '../../../../platform/ipc/common/mainProcessService.js';16import { disposableWindowInterval, getActiveDocument, getWindowId, getWindowsCount, hasWindow, onDidRegisterWindow } from '../../../../base/browser/dom.js';17import { memoize } from '../../../../base/common/decorators.js';18import { isAuxiliaryWindow } from '../../../../base/browser/window.js';19import { VSBuffer } from '../../../../base/common/buffer.js';2021class WorkbenchNativeHostService extends NativeHostService {2223constructor(24@INativeWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService,25@IMainProcessService mainProcessService: IMainProcessService26) {27super(environmentService.window.id, mainProcessService);28}29}3031class WorkbenchHostService extends Disposable implements IHostService {3233declare readonly _serviceBrand: undefined;3435constructor(36@INativeHostService private readonly nativeHostService: INativeHostService,37@ILabelService private readonly labelService: ILabelService,38@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService39) {40super();4142this.onDidChangeFocus = Event.latch(43Event.any(44Event.map(Event.filter(this.nativeHostService.onDidFocusMainOrAuxiliaryWindow, id => hasWindow(id), this._store), () => this.hasFocus, this._store),45Event.map(Event.filter(this.nativeHostService.onDidBlurMainOrAuxiliaryWindow, id => hasWindow(id), this._store), () => this.hasFocus, this._store),46Event.map(this.onDidChangeActiveWindow, () => this.hasFocus, this._store)47), undefined, this._store48);4950this.onDidChangeFullScreen = Event.filter(this.nativeHostService.onDidChangeWindowFullScreen, e => hasWindow(e.windowId), this._store);51}5253//#region Focus5455readonly onDidChangeFocus: Event<boolean>;5657get hasFocus(): boolean {58return getActiveDocument().hasFocus();59}6061async hadLastFocus(): Promise<boolean> {62const activeWindowId = await this.nativeHostService.getActiveWindowId();6364if (typeof activeWindowId === 'undefined') {65return false;66}6768return activeWindowId === this.nativeHostService.windowId;69}7071//#endregion7273//#region Window7475@memoize76get onDidChangeActiveWindow(): Event<number> {77const emitter = this._register(new Emitter<number>());7879// Emit via native focus tracking80this._register(Event.filter(this.nativeHostService.onDidFocusMainOrAuxiliaryWindow, id => hasWindow(id), this._store)(id => emitter.fire(id)));8182this._register(onDidRegisterWindow(({ window, disposables }) => {8384// Emit via interval: immediately when opening an auxiliary window,85// it is possible that document focus has not yet changed, so we86// poll for a while to ensure we catch the event.87disposables.add(disposableWindowInterval(window, () => {88const hasFocus = window.document.hasFocus();89if (hasFocus) {90emitter.fire(window.vscodeWindowId);91}9293return hasFocus;94}, 100, 20));95}));9697return Event.latch(emitter.event, undefined, this._store);98}99100readonly onDidChangeFullScreen: Event<{ readonly windowId: number; readonly fullscreen: boolean }>;101102openWindow(options?: IOpenEmptyWindowOptions): Promise<void>;103openWindow(toOpen: IWindowOpenable[], options?: IOpenWindowOptions): Promise<void>;104openWindow(arg1?: IOpenEmptyWindowOptions | IWindowOpenable[], arg2?: IOpenWindowOptions): Promise<void> {105if (Array.isArray(arg1)) {106return this.doOpenWindow(arg1, arg2);107}108109return this.doOpenEmptyWindow(arg1);110}111112private doOpenWindow(toOpen: IWindowOpenable[], options?: IOpenWindowOptions): Promise<void> {113const remoteAuthority = this.environmentService.remoteAuthority;114if (!!remoteAuthority) {115toOpen.forEach(openable => openable.label = openable.label || this.getRecentLabel(openable));116117if (options?.remoteAuthority === undefined) {118// set the remoteAuthority of the window the request came from.119// It will be used when the input is neither file nor vscode-remote.120options = options ? { ...options, remoteAuthority } : { remoteAuthority };121}122}123124return this.nativeHostService.openWindow(toOpen, options);125}126127private getRecentLabel(openable: IWindowOpenable): string {128if (isFolderToOpen(openable)) {129return this.labelService.getWorkspaceLabel(openable.folderUri, { verbose: Verbosity.LONG });130}131132if (isWorkspaceToOpen(openable)) {133return this.labelService.getWorkspaceLabel({ id: '', configPath: openable.workspaceUri }, { verbose: Verbosity.LONG });134}135136return this.labelService.getUriLabel(openable.fileUri, { appendWorkspaceSuffix: true });137}138139private doOpenEmptyWindow(options?: IOpenEmptyWindowOptions): Promise<void> {140const remoteAuthority = this.environmentService.remoteAuthority;141if (!!remoteAuthority && options?.remoteAuthority === undefined) {142// set the remoteAuthority of the window the request came from143options = options ? { ...options, remoteAuthority } : { remoteAuthority };144}145return this.nativeHostService.openWindow(options);146}147148toggleFullScreen(targetWindow: Window): Promise<void> {149return this.nativeHostService.toggleFullScreen({ targetWindowId: isAuxiliaryWindow(targetWindow) ? targetWindow.vscodeWindowId : undefined });150}151152async moveTop(targetWindow: Window): Promise<void> {153if (getWindowsCount() <= 1) {154return; // does not apply when only one window is opened155}156157return this.nativeHostService.moveWindowTop(isAuxiliaryWindow(targetWindow) ? { targetWindowId: targetWindow.vscodeWindowId } : undefined);158}159160getCursorScreenPoint(): Promise<{ readonly point: IPoint; readonly display: IRectangle }> {161return this.nativeHostService.getCursorScreenPoint();162}163164//#endregion165166//#region Lifecycle167168focus(targetWindow: Window, options?: { mode?: FocusMode }): Promise<void> {169return this.nativeHostService.focusWindow({170mode: options?.mode,171targetWindowId: getWindowId(targetWindow)172});173}174175restart(): Promise<void> {176return this.nativeHostService.relaunch();177}178179reload(options?: { disableExtensions?: boolean }): Promise<void> {180return this.nativeHostService.reload(options);181}182183close(): Promise<void> {184return this.nativeHostService.closeWindow();185}186187async withExpectedShutdown<T>(expectedShutdownTask: () => Promise<T>): Promise<T> {188return await expectedShutdownTask();189}190191//#endregion192193//#region Screenshots194195getScreenshot(rect?: IRectangle): Promise<VSBuffer | undefined> {196return this.nativeHostService.getScreenshot(rect);197}198199//#endregion200201//#region Native Handle202203private _nativeWindowHandleCache = new Map<number, Promise<VSBuffer | undefined>>();204async getNativeWindowHandle(windowId: number): Promise<VSBuffer | undefined> {205if (!this._nativeWindowHandleCache.has(windowId)) {206this._nativeWindowHandleCache.set(windowId, this.nativeHostService.getNativeWindowHandle(windowId));207}208return this._nativeWindowHandleCache.get(windowId)!;209}210211//#endregion212}213214registerSingleton(IHostService, WorkbenchHostService, InstantiationType.Delayed);215registerSingleton(INativeHostService, WorkbenchNativeHostService, InstantiationType.Delayed);216217218