Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/crossAppIpc/electron-main/crossAppIpcService.ts
13394 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 * as electron from 'electron';
7
import { TimeoutTimer } from '../../../base/common/async.js';
8
import { Emitter, Event } from '../../../base/common/event.js';
9
import { Disposable } from '../../../base/common/lifecycle.js';
10
import { createDecorator } from '../../instantiation/common/instantiation.js';
11
import { ILogService } from '../../log/common/log.js';
12
13
export const ICrossAppIPCService = createDecorator<ICrossAppIPCService>('crossAppIPCService');
14
15
export interface ICrossAppIPCMessage {
16
readonly type: string;
17
readonly data?: unknown;
18
}
19
20
export interface ICrossAppIPCService {
21
readonly _serviceBrand: undefined;
22
23
/** Whether the Electron crossAppIPC API is supported in this build. */
24
readonly isSupported: boolean;
25
26
/** Whether initialize() has been called and successfully set up the IPC. */
27
readonly initialized: boolean;
28
29
/** Whether the IPC connection is active. */
30
readonly connected: boolean;
31
32
/** Whether this app is the IPC server (`true`) or client (`false`). Only meaningful when connected. */
33
readonly isServer: boolean;
34
35
/** Fires when the peer connects. The boolean indicates whether this app is the server. */
36
readonly onDidConnect: Event<boolean /* isServer */>;
37
38
/** Fires when the peer disconnects. The string is the disconnect reason. */
39
readonly onDidDisconnect: Event<string /* reason */>;
40
41
/** Fires when a message is received from the peer. */
42
readonly onDidReceiveMessage: Event<ICrossAppIPCMessage>;
43
44
/** Send a message to the peer. No-op if not connected. */
45
sendMessage(msg: ICrossAppIPCMessage): void;
46
47
/** Initialize the IPC connection. Call once during startup. */
48
initialize(): void;
49
}
50
51
/**
52
* Manages the single crossAppIPC connection for the entire application.
53
*/
54
export class CrossAppIPCService extends Disposable implements ICrossAppIPCService {
55
56
declare readonly _serviceBrand: undefined;
57
58
private ipc: Electron.CrossAppIPC | undefined;
59
private _connected = false;
60
private _isServer = false;
61
private readonly reconnectTimer = this._register(new TimeoutTimer());
62
63
private readonly _onDidConnect = this._register(new Emitter<boolean>());
64
readonly onDidConnect: Event<boolean> = this._onDidConnect.event;
65
66
private readonly _onDidDisconnect = this._register(new Emitter<string>());
67
readonly onDidDisconnect: Event<string> = this._onDidDisconnect.event;
68
69
private readonly _onDidReceiveMessage = this._register(new Emitter<ICrossAppIPCMessage>());
70
readonly onDidReceiveMessage: Event<ICrossAppIPCMessage> = this._onDidReceiveMessage.event;
71
72
get isSupported(): boolean {
73
const crossAppIPC: Electron.CrossAppIPCModule | undefined = (electron as typeof electron & { crossAppIPC?: Electron.CrossAppIPCModule }).crossAppIPC;
74
return crossAppIPC !== undefined;
75
}
76
get initialized(): boolean { return this.ipc !== undefined; }
77
get connected(): boolean { return this._connected; }
78
get isServer(): boolean { return this._isServer; }
79
80
constructor(
81
@ILogService private readonly logService: ILogService,
82
) {
83
super();
84
}
85
86
initialize(): void {
87
if (this.ipc) {
88
return; // Already initialized
89
}
90
91
const crossAppIPC: Electron.CrossAppIPCModule | undefined = (electron as typeof electron & { crossAppIPC?: Electron.CrossAppIPCModule }).crossAppIPC;
92
93
if (!crossAppIPC) {
94
this.logService.info('CrossAppIPCService: crossAppIPC not available');
95
return;
96
}
97
98
const ipc = crossAppIPC.createCrossAppIPC();
99
this.ipc = ipc;
100
101
ipc.on('connected', () => {
102
this._connected = true;
103
this._isServer = ipc.isServer;
104
this.logService.info(`CrossAppIPCService: connected (isServer=${ipc.isServer})`);
105
this._onDidConnect.fire(ipc.isServer);
106
});
107
108
ipc.on('message', (messageEvent) => {
109
this._onDidReceiveMessage.fire(messageEvent.data as ICrossAppIPCMessage);
110
});
111
112
ipc.on('disconnected', (reason) => {
113
this.logService.info(`CrossAppIPCService: disconnected (${reason})`);
114
this._connected = false;
115
this._isServer = false;
116
this._onDidDisconnect.fire(reason);
117
118
// Reconnect to wait for the peer's next launch.
119
// Delay briefly to allow the old Mach bootstrap service to be
120
// deregistered before re-creating the server endpoint (macOS).
121
if (reason === 'peer-disconnected') {
122
this.reconnectTimer.cancelAndSet(() => ipc.connect(), 1000);
123
}
124
});
125
126
ipc.connect();
127
this.logService.info('CrossAppIPCService: connecting to peer');
128
}
129
130
sendMessage(msg: ICrossAppIPCMessage): void {
131
if (this.ipc?.connected) {
132
this.ipc.postMessage(msg);
133
}
134
}
135
136
override dispose(): void {
137
this.ipc?.close();
138
super.dispose();
139
}
140
}
141
142