diff --git a/src/vs/code/common/windows.ts b/src/vs/code/common/windows.ts new file mode 100644 index 00000000000..28df548ebcc --- /dev/null +++ b/src/vs/code/common/windows.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import Event from 'vs/base/common/event'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IDisposable } from 'vs/base/common/lifecycle'; + +export const IWindowEventService = createDecorator('windowEventService'); + +export interface IWindowEventService { + _serviceBrand: any; + + onNewWindowOpen: Event; + onWindowFocus: Event; +} + +export class ActiveWindowManager implements IDisposable { + + private disposables: IDisposable[] = []; + private _activeWindowId: number; + + constructor(@IWindowEventService private windowService: IWindowEventService) { + this.disposables.push(this.windowService.onNewWindowOpen(windowId => this.setActiveWindow(windowId))); + this.disposables.push(this.windowService.onWindowFocus(windowId => this.setActiveWindow(windowId))); + } + + private setActiveWindow(windowId: number) { + this._activeWindowId = windowId; + } + + public get activeWindowId(): string { + return `window:${ this._activeWindowId }`; + } + + public dispose() { + for (const disposable of this.disposables) { + disposable.dispose(); + } + } +} \ No newline at end of file diff --git a/src/vs/code/common/windowsIpc.ts b/src/vs/code/common/windowsIpc.ts new file mode 100644 index 00000000000..feec4bbd2be --- /dev/null +++ b/src/vs/code/common/windowsIpc.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TPromise } from 'vs/base/common/winjs.base'; +import { IChannel, eventToCall, eventFromCall } from 'vs/base/parts/ipc/common/ipc'; +import { IWindowEventService } from './windows'; +import Event, { buffer } from 'vs/base/common/event'; + +export interface IWindowsChannel extends IChannel { + call(command: 'event:onNewWindowOpen'): TPromise; + call(command: 'event:onWindowFocus'): TPromise; + call(command: string, arg: any): any; +} + +export class WindowsChannel implements IWindowsChannel { + + onNewWindowOpen: Event; + onWindowFocus: Event; + + constructor(private service: IWindowEventService) { + this.onNewWindowOpen = buffer(service.onNewWindowOpen, true); + this.onWindowFocus = buffer(service.onWindowFocus, true); + } + + call(command: string, args: any): any { + switch (command) { + case 'event:onNewWindowOpen': + return eventToCall(this.onNewWindowOpen); + case 'event:onWindowFocus': + return eventToCall(this.onWindowFocus); + } + return TPromise.wrapError('invalid command'); + } +} + +export class WindowEventChannelClient implements IWindowEventService { + + _serviceBrand: any; + + constructor(private channel: IWindowsChannel) { } + + private _onNewWindowOpen: Event = eventFromCall(this.channel, 'event:onNewWindowOpen'); + get onNewWindowOpen(): Event { + return this._onNewWindowOpen; + } + + private _onWindowFocus: Event = eventFromCall(this.channel, 'event:onWindowFocus'); + get onWindowFocus(): Event { + return this._onWindowFocus; + } +} \ No newline at end of file diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 05f6d5b0195..c9a317dd98e 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -13,7 +13,9 @@ import * as platform from 'vs/base/common/platform'; import { parseMainProcessArgv, ParsedArgs } from 'vs/platform/environment/node/argv'; import { mkdirp } from 'vs/base/node/pfs'; import { IProcessEnvironment, IEnvService, EnvService } from 'vs/code/electron-main/env'; -import { IWindowsService, WindowsManager } from 'vs/code/electron-main/windows'; +import { IWindowsService, WindowsManager, WindowEventService } from 'vs/code/electron-main/windows'; +import { IWindowEventService } from 'vs/code/common/windows'; +import { WindowsChannel } from 'vs/code/common/windowsIpc'; import { ILifecycleService, LifecycleService } from 'vs/code/electron-main/lifecycle'; import { VSCodeMenu } from 'vs/code/electron-main/menus'; import { IUpdateService, UpdateManager } from 'vs/code/electron-main/update-manager'; @@ -68,6 +70,7 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: IProce const envService = accessor.get(IEnvService); const environmentService = accessor.get(IEnvironmentService); const windowsService = accessor.get(IWindowsService); + const windowEventService = accessor.get(IWindowEventService); const lifecycleService = accessor.get(ILifecycleService); const updateService = accessor.get(IUpdateService); const configurationService = accessor.get(IConfigurationService) as ConfigurationService; @@ -131,6 +134,10 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: IProce let sharedProcessDisposable; spawnSharedProcess(initData, options).done(disposable => { sharedProcessDisposable = disposable; + const sharedProcessConnect = connect(environmentService.sharedIPCHandle, 'main'); + sharedProcessConnect.done(client => { + client.registerChannel('windowEvent', new WindowsChannel(windowEventService)); + }); }); // Make sure we associate the program with the app user model id @@ -408,6 +415,7 @@ function start(): void { services.set(IEnvService, new SyncDescriptor(EnvService)); services.set(ILogService, new SyncDescriptor(MainLogService)); services.set(IWindowsService, new SyncDescriptor(WindowsManager)); + services.set(IWindowEventService, new SyncDescriptor(WindowEventService)); services.set(ILifecycleService, new SyncDescriptor(LifecycleService)); services.set(IStorageService, new SyncDescriptor(StorageService)); services.set(IConfigurationService, new SyncDescriptor(ConfigurationService)); diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index cb1a9f9b8e6..ea64a23dd33 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -22,7 +22,9 @@ import { ILifecycleService } from 'vs/code/electron-main/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IUpdateService, IUpdate } from 'vs/code/electron-main/update-manager'; import { ILogService } from 'vs/code/electron-main/log'; +import { IWindowEventService } from 'vs/code/common/windows'; import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import CommonEvent, { Emitter } from 'vs/base/common/event'; const EventTypes = { OPEN: 'open', @@ -90,6 +92,8 @@ export interface IWindowsService { onOpen(clb: (path: IPath) => void): () => void; onReady(clb: (win: VSCodeWindow) => void): () => void; onClose(clb: (id: number) => void): () => void; + onNewWindowOpen: CommonEvent; + onWindowFocus: CommonEvent; // methods ready(initialUserEnv: IProcessEnvironment): void; @@ -115,6 +119,21 @@ export interface IWindowsService { clearRecentPathsList(): void; } +export class WindowEventService implements IWindowEventService { + + _serviceBrand: any; + + constructor(@IWindowsService private windowsService: IWindowsService) { } + + public get onWindowFocus(): CommonEvent { + return this.windowsService.onWindowFocus; + } + + public get onNewWindowOpen(): CommonEvent { + return this.windowsService.onNewWindowOpen; + } +} + export class WindowsManager implements IWindowsService { _serviceBrand: any; @@ -131,6 +150,12 @@ export class WindowsManager implements IWindowsService { private initialUserEnv: IProcessEnvironment; private windowsState: IWindowsState; + private _onFocus = new Emitter(); + onWindowFocus: CommonEvent = this._onFocus.event; + + private _onNewWindow = new Emitter(); + onNewWindowOpen: CommonEvent = this._onNewWindow.event; + constructor( @IInstantiationService private instantiationService: IInstantiationService, @ILogService private logService: ILogService, @@ -938,7 +963,9 @@ export class WindowsManager implements IWindowsService { vscodeWindow.win.on('unresponsive', () => this.onWindowError(vscodeWindow, WindowError.UNRESPONSIVE)); vscodeWindow.win.on('close', () => this.onBeforeWindowClose(vscodeWindow)); vscodeWindow.win.on('closed', () => this.onWindowClosed(vscodeWindow)); + vscodeWindow.win.on('focus', () => this._onFocus.fire(vscodeWindow.id)); + this._onNewWindow.fire(vscodeWindow.id); // Lifecycle this.lifecycleService.registerWindow(vscodeWindow); } diff --git a/src/vs/code/node/sharedProcessMain.ts b/src/vs/code/node/sharedProcessMain.ts index c5984bfab2a..36e367654ae 100644 --- a/src/vs/code/node/sharedProcessMain.ts +++ b/src/vs/code/node/sharedProcessMain.ts @@ -30,6 +30,8 @@ import { TelemetryAppenderChannel } from 'vs/platform/telemetry/common/telemetry import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender'; import { ISharedProcessInitData } from './sharedProcess'; +import { WindowEventChannelClient } from 'vs/code/common/windowsIpc'; +import { IWindowEventService } from 'vs/code/common/windows'; function quit(err?: Error) { if (err) { @@ -62,6 +64,13 @@ function main(server: Server, initData: ISharedProcessInitData): void { services.set(IConfigurationService, new SyncDescriptor(ConfigurationService)); services.set(IRequestService, new SyncDescriptor(RequestService)); + const windowEventService:IWindowEventService = new WindowEventChannelClient(server.getChannel('windowEvent', { + routeCall: (command: any, arg: any) => { + return 'main'; + } + })); + services.set(IWindowEventService, windowEventService); + const instantiationService = new InstantiationService(services); instantiationService.invokeFunction(accessor => {