diff --git a/index.js b/index.js index 9f08f64..6c550d2 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; @@ -30,66 +31,96 @@ function onReady () { 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()}}) - }) - - 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) - }) + 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()}}) + }) + 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()}}) + }) - powerMonitor.on('user-did-become-active', function() { - client.write(consts.targetIds.app, consts.eventNames.powerEventUserDidBecomeActive) - }) + // 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) + }); + 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]}, + ], + }); + }); + }); - powerMonitor.on('user-did-resign-active', function() { - client.write(consts.targetIds.app, consts.eventNames.powerEventUserDidResignActive) - }) + 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 + }) + ipcMain.handle(consts.eventNames.desktopCapturerGetSources, async () => { + const res = await electron.desktopCapturer.getSources({ types: ['window', 'screen'] }) + return res + }) + ipcMain.handle(consts.eventNames.webContentsMediaSourceID, async () => { + return getLastWindow().getMediaSourceId() + }) - // 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) - }); + 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){ @@ -366,15 +397,16 @@ function onReady () { // start begins listening to go-astilectron. function start(address = process.argv[2]) { - client.init(address); - rl = readline.createInterface({ input: client.socket }); + client.init(address, function () { + rl = readline.createInterface({ input: client.socket }); + if (app.isReady()) { + onReady(); + } else { + app.on("ready", onReady); + } + }); app.on("before-quit", beforeQuit); - if (app.isReady()) { - onReady(); - } else { - app.on("ready", onReady); - } app.on("window-all-closed", app.quit); } @@ -473,6 +505,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") { @@ -511,6 +544,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('enter-full-screen', () => { client.write(json.targetID, consts.eventNames.windowEventEnterFullScreen, {windowOptions: {fullscreen: true}})} ) elements[json.targetID].on('focus', () => { client.write(json.targetID, consts.eventNames.windowEventFocus) }) @@ -621,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); 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(); diff --git a/src/client.js b/src/client.js index 37f4537..7a7ab33 100644 --- a/src/client.js +++ b/src/client.js @@ -6,13 +6,16 @@ const url = require("url"); // Client can read/write messages from a TCP server class Client { // 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() {}); + this.socket.connect(u.port, u.hostname, onConnect); this.socket.on("close", function() { - process.exit(); - }); + // Socket closed, try to reconnect + setTimeout(() => { + this.init(addr, onConnect); + }, 200) + }.bind(this)); this.socket.on("error", function(err) { // Prevent Unhandled Exception resulting from TCP Error console.error(err); diff --git a/src/consts.js b/src/consts.js index 6a5712c..63395ef 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", @@ -143,7 +146,9 @@ 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", + webContentsMediaSourceID: "webContents.mediaSourceID", }, targetIds: { app: 'app',