/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, CrashReporterStartOptions, crashReporter, Menu, BrowserWindow, app } from 'electron'; import { INativeOpenInWindowOptions } from 'vs/platform/windows/node/window'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IOpenedWindow, OpenContext, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs'; import { isMacintosh, IProcessEnvironment } from 'vs/base/common/platform'; import { IElectronService } from 'vs/platform/electron/node/electron'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { AddFirstParameterToFunctions } from 'vs/base/common/types'; export class ElectronMainService implements AddFirstParameterToFunctions /* only methods, not events */, number /* window ID */> { _serviceBrand: undefined; constructor( @IWindowsMainService private readonly windowsMainService: IWindowsMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IEnvironmentService private readonly environmentService: IEnvironmentService ) { } //#region Events readonly onWindowOpen: Event = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-created', (_, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); readonly onWindowMaximize: Event = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-maximize', (_, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); readonly onWindowUnmaximize: Event = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-unmaximize', (_, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); readonly onWindowBlur: Event = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-blur', (_, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); readonly onWindowFocus: Event = Event.any( Event.map(Event.filter(Event.map(this.windowsMainService.onWindowsCountChanged, () => this.windowsMainService.getLastActiveWindow()), window => !!window), window => window!.id), Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-focus', (_, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)) ); //#endregion //#region Window async getWindows(): Promise { const windows = this.windowsMainService.getWindows(); return windows.map(window => ({ id: window.id, workspace: window.openedWorkspace, folderUri: window.openedFolderUri, title: window.win.getTitle(), filename: window.getRepresentedFilename() })); } async getWindowCount(windowId: number): Promise { return this.windowsMainService.getWindowCount(); } async getActiveWindowId(windowId: number): Promise { const activeWindow = this.windowsMainService.getFocusedWindow() || this.windowsMainService.getLastActiveWindow(); if (activeWindow) { return activeWindow.id; } return undefined; } async openEmptyWindow(windowId: number, options?: IOpenEmptyWindowOptions): Promise { this.windowsMainService.openEmptyWindow(OpenContext.API, options); } async openInWindow(windowId: number, toOpen: IWindowOpenable[], options: INativeOpenInWindowOptions = Object.create(null)): Promise { if (toOpen.length > 0) { this.windowsMainService.open({ context: OpenContext.API, contextWindowId: windowId, urisToOpen: toOpen, cli: this.environmentService.args, forceNewWindow: options.forceNewWindow, forceReuseWindow: options.forceReuseWindow, diffMode: options.diffMode, addMode: options.addMode, gotoLineMode: options.gotoLineMode, noRecentEntry: options.noRecentEntry, waitMarkerFileURI: options.waitMarkerFileURI }); } } async toggleFullScreen(windowId: number): Promise { const window = this.windowsMainService.getWindowById(windowId); if (window) { window.toggleFullScreen(); } } async handleTitleDoubleClick(windowId: number): Promise { const window = this.windowsMainService.getWindowById(windowId); if (window) { window.handleTitleDoubleClick(); } } async isMaximized(windowId: number): Promise { const window = this.windowsMainService.getWindowById(windowId); if (window) { return window.win.isMaximized(); } return false; } async maximizeWindow(windowId: number): Promise { const window = this.windowsMainService.getWindowById(windowId); if (window) { window.win.maximize(); } } async unmaximizeWindow(windowId: number): Promise { const window = this.windowsMainService.getWindowById(windowId); if (window) { window.win.unmaximize(); } } async minimizeWindow(windowId: number): Promise { const window = this.windowsMainService.getWindowById(windowId); if (window) { window.win.minimize(); } } async isWindowFocused(windowId: number): Promise { const window = this.windowsMainService.getWindowById(windowId); if (window) { return window.win.isFocused(); } return false; } async focusWindow(windowId: number, options?: { windowId?: number; }): Promise { if (options && typeof options.windowId === 'number') { windowId = options.windowId; } const window = this.windowsMainService.getWindowById(windowId); if (window) { if (isMacintosh) { window.win.show(); } else { window.win.focus(); } } } //#endregion //#region Dialog async showMessageBox(windowId: number, options: MessageBoxOptions): Promise { return this.windowsMainService.showMessageBox(options, this.windowsMainService.getWindowById(windowId)); } async showSaveDialog(windowId: number, options: SaveDialogOptions): Promise { return this.windowsMainService.showSaveDialog(options, this.windowsMainService.getWindowById(windowId)); } async showOpenDialog(windowId: number, options: OpenDialogOptions): Promise { return this.windowsMainService.showOpenDialog(options, this.windowsMainService.getWindowById(windowId)); } async pickFileFolderAndOpen(windowId: number, options: INativeOpenDialogOptions): Promise { return this.windowsMainService.pickFileFolderAndOpen(options, this.windowsMainService.getWindowById(windowId)); } async pickFileAndOpen(windowId: number, options: INativeOpenDialogOptions): Promise { return this.windowsMainService.pickFileAndOpen(options, this.windowsMainService.getWindowById(windowId)); } async pickFolderAndOpen(windowId: number, options: INativeOpenDialogOptions): Promise { return this.windowsMainService.pickFolderAndOpen(options, this.windowsMainService.getWindowById(windowId)); } async pickWorkspaceAndOpen(windowId: number, options: INativeOpenDialogOptions): Promise { return this.windowsMainService.pickWorkspaceAndOpen(options, this.windowsMainService.getWindowById(windowId)); } //#endregion //#region OS async showItemInFolder(windowId: number, path: string): Promise { shell.showItemInFolder(path); } async setRepresentedFilename(windowId: number, path: string): Promise { const window = this.windowsMainService.getWindowById(windowId); if (window) { window.setRepresentedFilename(path); } } async setDocumentEdited(windowId: number, edited: boolean): Promise { const window = this.windowsMainService.getWindowById(windowId); if (window) { window.win.setDocumentEdited(edited); } } async openExternal(windowId: number, url: string): Promise { return this.windowsMainService.openExternal(url); } async updateTouchBar(windowId: number, items: ISerializableCommandAction[][]): Promise { const window = this.windowsMainService.getWindowById(windowId); if (window) { window.updateTouchBar(items); } } //#endregion //#region macOS Touchbar async newWindowTab(): Promise { this.windowsMainService.openNewTabbedWindow(OpenContext.API); } async showPreviousWindowTab(): Promise { Menu.sendActionToFirstResponder('selectPreviousTab:'); } async showNextWindowTab(): Promise { Menu.sendActionToFirstResponder('selectNextTab:'); } async moveWindowTabToNewWindow(): Promise { Menu.sendActionToFirstResponder('moveTabToNewWindow:'); } async mergeAllWindowTabs(): Promise { Menu.sendActionToFirstResponder('mergeAllWindows:'); } async toggleWindowTabsBar(): Promise { Menu.sendActionToFirstResponder('toggleTabBar:'); } //#endregion //#region Lifecycle async relaunch(windowId: number, options?: { addArgs?: string[], removeArgs?: string[] }): Promise { return this.lifecycleMainService.relaunch(options); } async reload(windowId: number): Promise { const window = this.windowsMainService.getWindowById(windowId); if (window) { return this.windowsMainService.reload(window); } } async closeWorkpsace(windowId: number): Promise { const window = this.windowsMainService.getWindowById(windowId); if (window) { return this.windowsMainService.closeWorkspace(window); } } async closeWindow(windowId: number): Promise { const window = this.windowsMainService.getWindowById(windowId); if (window) { return window.win.close(); } } async quit(windowId: number): Promise { return this.windowsMainService.quit(); } //#endregion //#region Connectivity async resolveProxy(windowId: number, url: string): Promise { return new Promise(resolve => { const window = this.windowsMainService.getWindowById(windowId); if (window && window.win && window.win.webContents && window.win.webContents.session) { window.win.webContents.session.resolveProxy(url, proxy => resolve(proxy)); } else { resolve(); } }); } //#endregion //#region Development async openDevTools(windowId: number, options?: OpenDevToolsOptions): Promise { const window = this.windowsMainService.getWindowById(windowId); if (window) { window.win.webContents.openDevTools(options); } } async toggleDevTools(windowId: number): Promise { const window = this.windowsMainService.getWindowById(windowId); if (window) { const contents = window.win.webContents; if (isMacintosh && window.hasHiddenTitleBarStyle() && !window.isFullScreen() && !contents.isDevToolsOpened()) { contents.openDevTools({ mode: 'undocked' }); // due to https://github.com/electron/electron/issues/3647 } else { contents.toggleDevTools(); } } } async startCrashReporter(windowId: number, options: CrashReporterStartOptions): Promise { crashReporter.start(options); } //#endregion //#region Debug // TODO@Isidor move into debug IPC channel (https://github.com/microsoft/vscode/issues/81060) async openExtensionDevelopmentHostWindow(windowId: number, args: ParsedArgs, env: IProcessEnvironment): Promise { const extDevPaths = args.extensionDevelopmentPath; if (extDevPaths) { this.windowsMainService.openExtensionDevelopmentHostWindow(extDevPaths, { context: OpenContext.API, cli: args, userEnv: Object.keys(env).length > 0 ? env : undefined }); } } //#endregion }