From 06cee0a104b1655b8ad3da8d8309ee489b044a19 Mon Sep 17 00:00:00 2001 From: Guillaume Lesniak Date: Fri, 2 Jul 2021 09:11:06 +0200 Subject: [PATCH 01/13] Allow TCP client to reconnect in case of lost connection --- index.js | 10 ++++++++++ src/client.js | 15 ++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 76ade12..d017fbb 100644 --- a/index.js +++ b/index.js @@ -14,6 +14,7 @@ let elements = {}; let windowOptions = {}; let menus = {}; let quittingApp = false; +let didReady = false; // Single instance let lastWindowId = null; @@ -27,6 +28,8 @@ const beforeQuit = () => { // App is ready function onReady () { // Init + didReady = true; + const screen = electron.screen Menu.setApplicationMenu(null) @@ -307,6 +310,13 @@ function onReady () { // start begins listening to go-astilectron. function start(address = process.argv[2]) { client.init(address); + client.connectionListener = function () { + if (didReady) { + // Re-signal ready and recreate a new readline interface on the new socket + rl = readline.createInterface({ input: client.socket }); + onReady(); + } + } rl = readline.createInterface({ input: client.socket }); app.on("before-quit", beforeQuit); diff --git a/src/client.js b/src/client.js index 37f4537..0afc50e 100644 --- a/src/client.js +++ b/src/client.js @@ -5,14 +5,23 @@ const url = require("url"); // Client can read/write messages from a TCP server class Client { + connectionListener = null; + // init initializes the Client init(addr) { let u = url.parse("tcp://" + addr, false, false); this.socket = new net.Socket(); - this.socket.connect(u.port, u.hostname, function() {}); + this.socket.connect(u.port, u.hostname, function() { + if (this.connectionListener) { + this.connectionListener() + } + }.bind(this)); this.socket.on("close", function() { - process.exit(); - }); + // Socket closed, try to reconnect + setTimeout(() => { + this.init(addr); + }, 200) + }.bind(this)); this.socket.on("error", function(err) { // Prevent Unhandled Exception resulting from TCP Error console.error(err); From 41401ed5e71d31d7d318254775c79a26a9c5574a Mon Sep 17 00:00:00 2001 From: Guillaume Lesniak Date: Wed, 7 Jul 2021 17:31:59 +0200 Subject: [PATCH 02/13] Force-quit electron after window closes --- index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/index.js b/index.js index d017fbb..8910c51 100644 --- a/index.js +++ b/index.js @@ -451,6 +451,9 @@ function windowCreateFinish(json) { elements[json.targetID].on('closed', () => { client.write(json.targetID, consts.eventNames.windowEventClosed) delete elements[json.targetID] + setTimeout(() => { + app.quit() + }, 1000) }) elements[json.targetID].on('focus', () => { client.write(json.targetID, consts.eventNames.windowEventFocus) }) elements[json.targetID].on('hide', () => { client.write(json.targetID, consts.eventNames.windowEventHide) }) From 6a01e9bebd87f8a8760455126812173c8d4ea124 Mon Sep 17 00:00:00 2001 From: Guillaume Lesniak Date: Wed, 28 Jul 2021 10:04:58 +0200 Subject: [PATCH 03/13] index: Fix double IPC calls --- index.js | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/index.js b/index.js index 8910c51..5126ac0 100644 --- a/index.js +++ b/index.js @@ -28,33 +28,33 @@ const beforeQuit = () => { // App is ready function onReady () { // Init - didReady = true; - const screen = electron.screen Menu.setApplicationMenu(null) - // Listen to screen events - screen.on('display-added', function() { - client.write(consts.targetIds.app, consts.eventNames.displayEventAdded, {displays: {all: screen.getAllDisplays(), primary: screen.getPrimaryDisplay()}}) - }) - screen.on('display-metrics-changed', function() { - client.write(consts.targetIds.app, consts.eventNames.displayEventMetricsChanged, {displays: {all: screen.getAllDisplays(), primary: screen.getPrimaryDisplay()}}) - }) - screen.on('display-removed', function() { - client.write(consts.targetIds.app, consts.eventNames.displayEventRemoved, {displays: {all: screen.getAllDisplays(), primary: screen.getPrimaryDisplay()}}) - }) + if (!didReady) { + // Listen to screen events + screen.on('display-added', function() { + client.write(consts.targetIds.app, consts.eventNames.displayEventAdded, {displays: {all: screen.getAllDisplays(), primary: screen.getPrimaryDisplay()}}) + }) + screen.on('display-metrics-changed', function() { + client.write(consts.targetIds.app, consts.eventNames.displayEventMetricsChanged, {displays: {all: screen.getAllDisplays(), primary: screen.getPrimaryDisplay()}}) + }) + screen.on('display-removed', function() { + client.write(consts.targetIds.app, consts.eventNames.displayEventRemoved, {displays: {all: screen.getAllDisplays(), primary: screen.getPrimaryDisplay()}}) + }) - // Listen on main ipcMain - ipcMain.on(consts.eventNames.ipcEventMessage, (event, arg) => { - let payload = {message: arg.message}; - if (typeof arg.callbackId !== "undefined") payload.callbackId = arg.callbackId; - client.write(arg.targetID, consts.eventNames.windowEventMessage, payload) - }); - ipcMain.on(consts.eventNames.ipcEventMessageCallback, (event, arg) => { - let payload = {message: arg.message}; - if (typeof arg.callbackId !== "undefined") payload.callbackId = arg.callbackId; - client.write(arg.targetID, consts.eventNames.windowEventMessageCallback, payload) - }); + // Listen on main ipcMain + ipcMain.on(consts.eventNames.ipcEventMessage, (event, arg) => { + let payload = {message: arg.message}; + if (typeof arg.callbackId !== "undefined") payload.callbackId = arg.callbackId; + client.write(arg.targetID, consts.eventNames.windowEventMessage, payload) + }); + ipcMain.on(consts.eventNames.ipcEventMessageCallback, (event, arg) => { + let payload = {message: arg.message}; + if (typeof arg.callbackId !== "undefined") payload.callbackId = arg.callbackId; + client.write(arg.targetID, consts.eventNames.windowEventMessageCallback, payload) + }); + } // Read from client rl.on('line', function(line){ @@ -305,6 +305,8 @@ function onReady () { notification: Notification.isSupported() } }) + + didReady = true; }; // start begins listening to go-astilectron. From f7cc8c210ed35bfd95be15ae6e73243dd78a1d36 Mon Sep 17 00:00:00 2001 From: Guillaume Lesniak Date: Wed, 28 Jul 2021 12:28:03 +0200 Subject: [PATCH 04/13] Make requested changes for TCP reconnect --- index.js | 5 ++--- src/client.js | 12 +++--------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index 5126ac0..64a87eb 100644 --- a/index.js +++ b/index.js @@ -311,14 +311,13 @@ function onReady () { // start begins listening to go-astilectron. function start(address = process.argv[2]) { - client.init(address); - client.connectionListener = function () { + client.init(address, function () { if (didReady) { // Re-signal ready and recreate a new readline interface on the new socket rl = readline.createInterface({ input: client.socket }); onReady(); } - } + }); rl = readline.createInterface({ input: client.socket }); app.on("before-quit", beforeQuit); diff --git a/src/client.js b/src/client.js index 0afc50e..7a7ab33 100644 --- a/src/client.js +++ b/src/client.js @@ -5,21 +5,15 @@ const url = require("url"); // Client can read/write messages from a TCP server class Client { - connectionListener = null; - // init initializes the Client - init(addr) { + init(addr, onConnect) { let u = url.parse("tcp://" + addr, false, false); this.socket = new net.Socket(); - this.socket.connect(u.port, u.hostname, function() { - if (this.connectionListener) { - this.connectionListener() - } - }.bind(this)); + this.socket.connect(u.port, u.hostname, onConnect); this.socket.on("close", function() { // Socket closed, try to reconnect setTimeout(() => { - this.init(addr); + this.init(addr, onConnect); }, 200) }.bind(this)); this.socket.on("error", function(err) { From 8bddfe2bec98a3973302f9994fb3acc7516a8ace Mon Sep 17 00:00:00 2001 From: Guillaume Lesniak Date: Wed, 28 Jul 2021 12:50:51 +0200 Subject: [PATCH 05/13] Don't initialize readline twice on start --- index.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/index.js b/index.js index 64a87eb..cff67ff 100644 --- a/index.js +++ b/index.js @@ -32,6 +32,7 @@ function onReady () { Menu.setApplicationMenu(null) if (!didReady) { + didReady = true // Listen to screen events screen.on('display-added', function() { client.write(consts.targetIds.app, consts.eventNames.displayEventAdded, {displays: {all: screen.getAllDisplays(), primary: screen.getPrimaryDisplay()}}) @@ -305,27 +306,20 @@ function onReady () { notification: Notification.isSupported() } }) - - didReady = true; }; // start begins listening to go-astilectron. function start(address = process.argv[2]) { client.init(address, function () { - if (didReady) { - // Re-signal ready and recreate a new readline interface on the new socket - rl = readline.createInterface({ input: client.socket }); + rl = readline.createInterface({ input: client.socket }); + if (app.isReady()) { onReady(); + } else { + app.on("ready", onReady); } }); - rl = readline.createInterface({ input: client.socket }); app.on("before-quit", beforeQuit); - if (app.isReady()) { - onReady(); - } else { - app.on("ready", onReady); - } app.on("window-all-closed", app.quit); } From b25865e02d0eed11c7d9330c0013bef54477557e Mon Sep 17 00:00:00 2001 From: Guillaume Lesniak Date: Mon, 6 Nov 2023 11:35:06 +0100 Subject: [PATCH 06/13] Fix mismerge --- index.js | 88 ++++++++++++++++++++++++-------------------------------- 1 file changed, 38 insertions(+), 50 deletions(-) diff --git a/index.js b/index.js index 07f2d5a..348c471 100644 --- a/index.js +++ b/index.js @@ -55,57 +55,45 @@ function onReady () { if (typeof arg.callbackId !== "undefined") payload.callbackId = arg.callbackId; client.write(arg.targetID, consts.eventNames.windowEventMessageCallback, payload) }); - } - const powerMonitor = electron.powerMonitor - // Listen to power events - powerMonitor.on('suspend', function() { - client.write(consts.targetIds.app, consts.eventNames.powerEventSuspend) - }) - - powerMonitor.on('resume', function() { - client.write(consts.targetIds.app, consts.eventNames.powerEventResume) - }) - - powerMonitor.on('on-ac', function() { - client.write(consts.targetIds.app, consts.eventNames.powerEventOnAC) - }) - - powerMonitor.on('on-battery', function () { - client.write(consts.targetIds.app, consts.eventNames.powerEventOnBattery) - }) - - powerMonitor.on('shutdown', function() { - client.write(consts.targetIds.app, consts.eventNames.powerEventShutdown) - }) - - powerMonitor.on('lock-screen', function() { - client.write(consts.targetIds.app, consts.eventNames.powerEventLockScreen) - }) - - powerMonitor.on('unlock-screen', function() { - client.write(consts.targetIds.app, consts.eventNames.powerEventUnlockScreen) - }) - - powerMonitor.on('user-did-become-active', function() { - client.write(consts.targetIds.app, consts.eventNames.powerEventUserDidBecomeActive) - }) - - powerMonitor.on('user-did-resign-active', function() { - client.write(consts.targetIds.app, consts.eventNames.powerEventUserDidResignActive) - }) - // Listen on main ipcMain - ipcMain.on(consts.eventNames.ipcEventMessage, (event, arg) => { - let payload = {message: arg.message}; - if (typeof arg.callbackId !== "undefined") payload.callbackId = arg.callbackId; - client.write(arg.targetID, consts.eventNames.windowEventMessage, payload) - }); - ipcMain.on(consts.eventNames.ipcEventMessageCallback, (event, arg) => { - let payload = {message: arg.message}; - if (typeof arg.callbackId !== "undefined") payload.callbackId = arg.callbackId; - client.write(arg.targetID, consts.eventNames.windowEventMessageCallback, payload) - }); ->>>>>>> 8cab200d3589a850ee07212f2de65e61b0229cff + const powerMonitor = electron.powerMonitor + // Listen to power events + powerMonitor.on('suspend', function() { + client.write(consts.targetIds.app, consts.eventNames.powerEventSuspend) + }) + + powerMonitor.on('resume', function() { + client.write(consts.targetIds.app, consts.eventNames.powerEventResume) + }) + + powerMonitor.on('on-ac', function() { + client.write(consts.targetIds.app, consts.eventNames.powerEventOnAC) + }) + + powerMonitor.on('on-battery', function () { + client.write(consts.targetIds.app, consts.eventNames.powerEventOnBattery) + }) + + powerMonitor.on('shutdown', function() { + client.write(consts.targetIds.app, consts.eventNames.powerEventShutdown) + }) + + powerMonitor.on('lock-screen', function() { + client.write(consts.targetIds.app, consts.eventNames.powerEventLockScreen) + }) + + powerMonitor.on('unlock-screen', function() { + client.write(consts.targetIds.app, consts.eventNames.powerEventUnlockScreen) + }) + + powerMonitor.on('user-did-become-active', function() { + client.write(consts.targetIds.app, consts.eventNames.powerEventUserDidBecomeActive) + }) + + powerMonitor.on('user-did-resign-active', function() { + client.write(consts.targetIds.app, consts.eventNames.powerEventUserDidResignActive) + }) + } // Read from client rl.on('line', function(line){ From 3c44da51f221df49e42ebc0e5f5d9618f5642b64 Mon Sep 17 00:00:00 2001 From: Guillaume Lesniak Date: Mon, 6 Nov 2023 13:52:19 +0100 Subject: [PATCH 07/13] Add IPC handlers for file dialogs --- index.js | 21 +++++++++++++++++++++ src/consts.js | 3 +++ 2 files changed, 24 insertions(+) diff --git a/index.js b/index.js index 348c471..437b60a 100644 --- a/index.js +++ b/index.js @@ -55,6 +55,26 @@ function onReady () { if (typeof arg.callbackId !== "undefined") payload.callbackId = arg.callbackId; client.write(arg.targetID, consts.eventNames.windowEventMessageCallback, payload) }); + ipcMain.handle(consts.eventNames.sessionCmdSetWillDownloadExtensions, (event, possibleExtensions) => { + BrowserWindow.getFocusedWindow().webContents.session.on('will-download', (event, item) => { + let extension = item.getFilename().split('.').pop() + let extensionLabel = possibleExtensions[extension] ?? `Fichier ${extension}` + item.setSaveDialogOptions({ + filters: [ + {name: extensionLabel, extensions: [extension]}, + ], + }); + }); + }); + + ipcMain.handle(consts.eventNames.dialogShowOpen, async (event, options) => { + const res = await dialog.showOpenDialog(options) + return res + }) + ipcMain.handle(consts.eventNames.dialogShowSave, async (event, options) => { + const res = await dialog.showSaveDialog(options) + return res + }) const powerMonitor = electron.powerMonitor // Listen to power events @@ -478,6 +498,7 @@ function windowCreate(json) { } json.windowOptions.webPreferences.contextIsolation = false json.windowOptions.webPreferences.nodeIntegration = true + json.windowOptions.webPreferences.enableRemoteModule = true elements[json.targetID] = new BrowserWindow(json.windowOptions) windowOptions[json.targetID] = json.windowOptions if (typeof json.windowOptions.proxy !== "undefined") { diff --git a/src/consts.js b/src/consts.js index 6a5712c..1acd4be 100644 --- a/src/consts.js +++ b/src/consts.js @@ -12,6 +12,8 @@ module.exports = { displayEventAdded: "display.event.added", displayEventMetricsChanged: "display.event.metrics.changed", displayEventRemoved: "display.event.removed", + dialogShowOpen: "dialog.show.open", + dialogShowSave: "dialog.show.save", dockCmdBounce: "dock.cmd.bounce", dockCmdBounceDownloads: "dock.cmd.bounce.downloads", dockCmdCancelBounce: "dock.cmd.cancel.bounce", @@ -64,6 +66,7 @@ module.exports = { sessionCmdClearCache: "session.cmd.clear.cache", sessionCmdFlushStorage: "session.cmd.flush.storage", sessionCmdLoadExtension: "session.cmd.load.extension", + sessionCmdSetWillDownloadExtensions: "session.cmd.set-will-download-extensions", sessionEventClearedCache: "session.event.cleared.cache", sessionEventWillDownload: "session.event.will.download", sessionEventFlushedStorage: "session.event.flushed.storage", From ead0313494a27aeea443282aed4e125a11ff204c Mon Sep 17 00:00:00 2001 From: antoine Date: Tue, 26 Dec 2023 09:49:33 +0100 Subject: [PATCH 08/13] get desktop capter get sources event --- index.js | 4 ++++ src/consts.js | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 437b60a..2e6ccc2 100644 --- a/index.js +++ b/index.js @@ -75,6 +75,10 @@ function onReady () { const res = await dialog.showSaveDialog(options) return res }) + ipcMain.handle(consts.eventNames.desktopCapturerGetSources, async () => { + const res = await electron.desktopCapturer.getSources({ types: ['window', 'screen'] }) + return res + }) const powerMonitor = electron.powerMonitor // Listen to power events diff --git a/src/consts.js b/src/consts.js index 1acd4be..d9f1bad 100644 --- a/src/consts.js +++ b/src/consts.js @@ -146,7 +146,8 @@ module.exports = { windowEventUnresponsive: "window.event.unresponsive", windowEventWebContentsExecutedJavaScript: "window.event.web.contents.executed.javascript", windowEventWillMove: "window.event.will.move", - windowEventUpdatedCustomOptions: "window.event.updated.custom.options" + windowEventUpdatedCustomOptions: "window.event.updated.custom.options", + desktopCapturerGetSources: "desktopCapturer.sources", }, targetIds: { app: 'app', From 08a1c0f4fa87fc25cf1e8a83d8eac7a647595ab9 Mon Sep 17 00:00:00 2001 From: antoine Date: Tue, 26 Dec 2023 16:02:18 +0100 Subject: [PATCH 09/13] add handler to get webContent media source id --- index.js | 3 +++ src/consts.js | 1 + 2 files changed, 4 insertions(+) diff --git a/index.js b/index.js index 2e6ccc2..f5bf638 100644 --- a/index.js +++ b/index.js @@ -79,6 +79,9 @@ function onReady () { const res = await electron.desktopCapturer.getSources({ types: ['window', 'screen'] }) return res }) + ipcMain.handle(consts.eventNames.webContentsMediaSourceID, async () => { + return electron.webContents.getMediaSourceId() + }) const powerMonitor = electron.powerMonitor // Listen to power events diff --git a/src/consts.js b/src/consts.js index d9f1bad..63395ef 100644 --- a/src/consts.js +++ b/src/consts.js @@ -148,6 +148,7 @@ module.exports = { windowEventWillMove: "window.event.will.move", windowEventUpdatedCustomOptions: "window.event.updated.custom.options", desktopCapturerGetSources: "desktopCapturer.sources", + webContentsMediaSourceID: "webContents.mediaSourceID", }, targetIds: { app: 'app', From f7654354ca76216886f3f69a0b84cc3078d60af7 Mon Sep 17 00:00:00 2001 From: antoine Date: Wed, 27 Dec 2023 11:34:36 +0100 Subject: [PATCH 10/13] fixed webcontents: use from BrowserWindow --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index f5bf638..9196ddc 100644 --- a/index.js +++ b/index.js @@ -80,7 +80,7 @@ function onReady () { return res }) ipcMain.handle(consts.eventNames.webContentsMediaSourceID, async () => { - return electron.webContents.getMediaSourceId() + return BrowserWindow.getFocusedWindow().webContents.getMediaSourceId() }) const powerMonitor = electron.powerMonitor From 6d8efb4fe8baad5ca156bd15f56a7b200bb2daa0 Mon Sep 17 00:00:00 2001 From: antoine Date: Wed, 27 Dec 2023 13:20:33 +0100 Subject: [PATCH 11/13] using getLastWindow to get source id --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 9196ddc..b25a202 100644 --- a/index.js +++ b/index.js @@ -80,7 +80,7 @@ function onReady () { return res }) ipcMain.handle(consts.eventNames.webContentsMediaSourceID, async () => { - return BrowserWindow.getFocusedWindow().webContents.getMediaSourceId() + return getLastWindow().getMediaSourceId() }) const powerMonitor = electron.powerMonitor From 99ab211835c6a19015a41fe484646df591c80b6f Mon Sep 17 00:00:00 2001 From: Guillaume Lesniak Date: Fri, 16 Feb 2024 17:35:05 +0100 Subject: [PATCH 12/13] Handle unhandled rejections gracefully, to allow parent process to die peacefully --- main.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/main.js b/main.js index 6eaa17c..154453a 100644 --- a/main.js +++ b/main.js @@ -3,6 +3,15 @@ const { app } = require("electron"); const { start, getLastWindow, client, consts } = require("./index"); +process + .on('unhandledRejection', (reason, p) => { + //console.error(reason, 'Unhandled Rejection at Promise', p); + }) + .on('uncaughtException', err => { + //console.error(err, 'Uncaught Exception thrown'); + //process.exit(1); + }); + // edge case when the program is launched without arguments if (process.argv.length == 1) { app.requestSingleInstanceLock(); From baa93f3ac7b12a943ef9dfea7ca51991836ab80f Mon Sep 17 00:00:00 2001 From: Guillaume Lesniak Date: Tue, 5 Aug 2025 14:19:34 +0200 Subject: [PATCH 13/13] fix(index): Reload the app in case of renderer process crash --- index.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/index.js b/index.js index b25a202..6c550d2 100644 --- a/index.js +++ b/index.js @@ -657,6 +657,15 @@ function windowCreateFinish(json) { url: url }) }) + elements[json.targetID].webContents.on('render-process-gone', (event, details) => { + if (details.reason !== 'clean-exit') { + console.error('Renderer process crashed. Reloading the window...'); + // Using a small delay before reloading can help prevent potential race conditions + setTimeout(() => { + elements[json.targetID].reload(); + }, 500); + } + }) if (typeof json.windowOptions.appDetails !== "undefined" && process.platform === "win32"){ elements[json.targetID].setThumbarButtons([]); elements[json.targetID].setAppDetails(json.windowOptions.appDetails);