diff --git a/README.md b/README.md index 63888d53..277e1373 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,8 @@ const { // jitsiMeetWindow - The BrowserWindow instance of the window where Jitsi Meet is loaded. // appName - Application name which will be displayed inside the content sharing tracking window // i.e. [appName] is sharing your screen. -setupScreenSharingMain(mainWindow, appName); +// osxBundleId - Mac Application bundleId for which screen capturer permissions will be reset if user denied them. +setupScreenSharingMain(mainWindow, appName, osxBundleId); ``` diff --git a/package-lock.json b/package-lock.json index 6b33a190..78bf5668 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "jitsi-meet-electron-utils", - "version": "2.0.3", + "version": "2.0.4", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -690,6 +690,20 @@ "safer-buffer": "^2.1.0" } }, + "electron-is-dev": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-1.2.0.tgz", + "integrity": "sha512-R1oD5gMBPS7PVU8gJwH6CtT0e6VSoD0+SzSnYpNm+dBkcijgA+K7VAMHDfnRq/lkKPZArpzplTW6jfiMYosdzw==" + }, + "electron-util": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/electron-util/-/electron-util-0.13.1.tgz", + "integrity": "sha512-CvOuAyQPaPtnDp7SspwnT1yTb1yynw6yp4LrZCfEJ7TG/kJFiZW9RqMHlCEFWMn3QNoMkNhGVeCvWJV5NsYyuQ==", + "requires": { + "electron-is-dev": "^1.1.0", + "new-github-issue-url": "^0.2.1" + } + }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", @@ -908,6 +922,73 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "execa": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-2.1.0.tgz", + "integrity": "sha512-Y/URAVapfbYy2Xp/gb6A0E7iR8xeqOCXsuuaoMn7A5PzrXUK84E1gyiEfq0wQd/GHA6GsoHWwhNq8anb0mleIw==", + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^3.0.0", + "onetime": "^5.1.0", + "p-finally": "^2.0.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz", + "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "execspawn": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/execspawn/-/execspawn-1.0.1.tgz", @@ -1085,6 +1166,14 @@ "wide-align": "^1.1.0" } }, + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "requires": { + "pump": "^3.0.0" + } + }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -1479,6 +1568,11 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -1493,8 +1587,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isstream": { "version": "0.1.2", @@ -1666,6 +1759,31 @@ "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", "dev": true }, + "mac-screen-capture-permissions": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mac-screen-capture-permissions/-/mac-screen-capture-permissions-1.1.0.tgz", + "integrity": "sha512-jMRumlB3FScui/7yW+5FqqbuO7CQ0XOJVT5oTsb7W9eRQDhCIpJpIF0XxLVXwq2DIOp0fYsz1LFiBjnyDYULyQ==", + "requires": { + "electron-util": "^0.13.0", + "execa": "^2.0.4", + "macos-version": "^5.2.0" + } + }, + "macos-version": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/macos-version/-/macos-version-5.2.0.tgz", + "integrity": "sha512-egt1bqVE1evUjCup2QN2F0g42AuVcumdM31xNbABz+uXquYPzWP4OrqDm+HpCfM+6t4JzWrzABQW+MZM+FW+Jg==", + "requires": { + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, "memory-stream": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/memory-stream/-/memory-stream-0.0.3.tgz", @@ -1701,6 +1819,11 @@ } } }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, "mime-db": { "version": "1.40.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", @@ -1867,6 +1990,11 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "new-github-issue-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/new-github-issue-url/-/new-github-issue-url-0.2.1.tgz", + "integrity": "sha512-md4cGoxuT4T4d/HDOXbrUHkTKrp/vp+m3aOA7XXVYwNsUNMK49g3SQicTSeV5GIz/5QVGAeYRAOlyp9OvlgsYA==" + }, "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", @@ -2021,6 +2149,21 @@ "which": "^1.2.10" } }, + "npm-run-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-3.1.0.tgz", + "integrity": "sha512-Dbl4A/VfiVGLgQv29URL9xshU8XDY1GeLy+fsaZ1AA8JDSfjvr5P5+pzRbWqRSBxk6/DW7MIh8lTM/PaGnP2kg==", + "requires": { + "path-key": "^3.0.0" + }, + "dependencies": { + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + } + } + }, "npm-which": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/npm-which/-/npm-which-3.0.1.tgz", @@ -2167,6 +2310,11 @@ "os-tmpdir": "^1.0.0" } }, + "p-finally": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", + "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==" + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -2646,6 +2794,11 @@ "ansi-regex": "^2.0.0" } }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", diff --git a/package.json b/package.json index cd73f1de..0adea80f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jitsi-meet-electron-utils", - "version": "2.0.3", + "version": "2.0.4", "description": "Utilities for jitsi-meet-electron project", "main": "index.js", "scripts": { @@ -26,6 +26,7 @@ "license": "Apache-2.0", "gypfile": true, "dependencies": { + "mac-screen-capture-permissions": "^1.1.0", "nan": "^2.14.0", "postis": "^2.2.0", "prebuild-install": "^5.3.0", diff --git a/screensharing/main.js b/screensharing/main.js index 8eb2f394..85da9fff 100644 --- a/screensharing/main.js +++ b/screensharing/main.js @@ -2,6 +2,7 @@ const electron = require('electron'); const { SCREEN_SHARE_EVENTS_CHANNEL, SCREEN_SHARE_EVENTS, TRACKER_SIZE } = require('./constants'); +const { isMac } = require('./utils'); /** * Main process component that sets up electron specific screen sharing functionality, like screen sharing @@ -17,11 +18,15 @@ class ScreenShareMainHook { * @param {string} identity - Name of the application doing screen sharing, will be displayed in the * screen sharing tracker window text i.e. {identity} is sharing your screen. */ - constructor(jitsiMeetWindow, identity) { + constructor(jitsiMeetWindow, identity, osxBundleId) { this._jitsiMeetWindow = jitsiMeetWindow; this._identity = identity; this._onScreenSharingEvent = this._onScreenSharingEvent.bind(this); + if (osxBundleId && isMac()) { + this._verifyScreenCapturePermissions(osxBundleId); + } + // Listen for events coming in from the main render window and the screen share tracker window. electron.ipcMain.on(SCREEN_SHARE_EVENTS_CHANNEL, this._onScreenSharingEvent); @@ -72,8 +77,8 @@ class ScreenShareMainHook { this._screenShareTracker = new electron.BrowserWindow({ height: TRACKER_SIZE.height, width: TRACKER_SIZE.width, - x:(display.workArea.width - TRACKER_SIZE.width) / 2, - y:display.workArea.height - TRACKER_SIZE.height - 5, + x: (display.workArea.width - TRACKER_SIZE.width) / 2, + y: display.workArea.height - TRACKER_SIZE.height - 5, transparent: true, minimizable: true, maximizable: false, @@ -99,6 +104,27 @@ class ScreenShareMainHook { this._screenShareTracker .loadURL(`file://${__dirname}/screenSharingTracker.html?sharingIdentity=${this._identity}`); } + + /** + * Verifies whether app has already asked for capture permissions. + * If it did but the user denied, resets permissions for the app + * + * @param {string} bundleId- OSX Application BundleId + */ + _verifyScreenCapturePermissions(bundleId) { + const { + hasPromptedForPermission, + hasScreenCapturePermission, + resetPermissions, + } = require('mac-screen-capture-permissions'); + + const hasPermission = hasScreenCapturePermission(); + const promptedAlready = hasPromptedForPermission(); + + if (promptedAlready && !hasPermission) { + resetPermissions({ bundleId }); + } + } } /** @@ -107,7 +133,8 @@ class ScreenShareMainHook { * @param {BrowserWindow} jitsiMeetWindow - the BrowserWindow object which displays Jitsi Meet * @param {string} identity - Name of the application doing screen sharing, will be displayed in the * screen sharing tracker window text i.e. {identity} is sharing your screen. + * @param {string} bundleId- OSX Application BundleId */ -module.exports = function setupScreenSharingMain(jitsiMeetWindow, identity) { - return new ScreenShareMainHook(jitsiMeetWindow, identity); +module.exports = function setupScreenSharingMain(jitsiMeetWindow, identity, osxBundleId) { + return new ScreenShareMainHook(jitsiMeetWindow, identity, osxBundleId); }; diff --git a/screensharing/utils.js b/screensharing/utils.js new file mode 100644 index 00000000..e5dc4cde --- /dev/null +++ b/screensharing/utils.js @@ -0,0 +1,7 @@ +/* global process */ + +const isMac = () => process.platform === 'darwin'; + +module.exports = { + isMac, +};