forked from matrix/element-web
Jitsi Push-to-Talk
This commit is contained in:
parent
588030141b
commit
6e71fa5902
|
@ -8,7 +8,27 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"auto-launch": "^5.0.1",
|
"auto-launch": "^5.0.1",
|
||||||
"electron-window-state": "^4.1.0",
|
"electron-window-state": "^4.1.0",
|
||||||
|
"iohook": "^0.2.4",
|
||||||
"minimist": "^1.2.0",
|
"minimist": "^1.2.0",
|
||||||
"png-to-ico": "^1.0.2"
|
"png-to-ico": "^1.0.2"
|
||||||
|
},
|
||||||
|
"iohook": {
|
||||||
|
"targets": [
|
||||||
|
"node-64",
|
||||||
|
"electron-64"
|
||||||
|
],
|
||||||
|
"platforms": [
|
||||||
|
"win32",
|
||||||
|
"darwin",
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"arches": [
|
||||||
|
"x64",
|
||||||
|
"ia32"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"cmake-js": {
|
||||||
|
"runtime": "electron",
|
||||||
|
"runtimeVersion": "3.0.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ const argv = require('minimist')(process.argv);
|
||||||
const {app, ipcMain, powerSaveBlocker, BrowserWindow, Menu, autoUpdater, protocol} = require('electron');
|
const {app, ipcMain, powerSaveBlocker, BrowserWindow, Menu, autoUpdater, protocol} = require('electron');
|
||||||
const AutoLaunch = require('auto-launch');
|
const AutoLaunch = require('auto-launch');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const ioHook = require('iohook');
|
||||||
|
|
||||||
const tray = require('./tray');
|
const tray = require('./tray');
|
||||||
const vectorMenu = require('./vectormenu');
|
const vectorMenu = require('./vectormenu');
|
||||||
|
@ -340,6 +341,13 @@ app.on('ready', () => {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
mainWindow.on('blur', () => {
|
||||||
|
// Stop recording keypresses if Riot loses focus
|
||||||
|
// Used for Push-To-Talk, keypress recording only triggered when setting
|
||||||
|
// a global shortcut in Settings
|
||||||
|
mainWindow.webContents.send('window-blurred');
|
||||||
|
stopListeningKeys();
|
||||||
|
});
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
// Handle forward/backward mouse buttons in Windows
|
// Handle forward/backward mouse buttons in Windows
|
||||||
|
@ -382,6 +390,92 @@ app.on('second-instance', (ev, commandLine, workingDirectory) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Counter for keybindings we have registered
|
||||||
|
let ioHookTasks = 0;
|
||||||
|
|
||||||
|
// Limit for amount of keybindings that can be
|
||||||
|
// registered at once.
|
||||||
|
const keybindingRegistrationLimit = 1;
|
||||||
|
|
||||||
|
// Fires when a global keybinding is being registered
|
||||||
|
ipcMain.on('register-keybinding', function(ev, keybinding) {
|
||||||
|
// Prevent registering more than the defined limit
|
||||||
|
if (ioHookTasks >= keybindingRegistrationLimit) {
|
||||||
|
ioHookTasks = keybindingRegistrationLimit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start listening for global keyboard shortcuts
|
||||||
|
if (ioHookTasks <= 0) {
|
||||||
|
ioHookTasks = 0;
|
||||||
|
ioHook.start();
|
||||||
|
}
|
||||||
|
ioHookTasks++;
|
||||||
|
|
||||||
|
ioHook.registerShortcut(keybinding.code, () => {
|
||||||
|
ev.sender.send('keybinding-pressed', keybinding.name);
|
||||||
|
}, () => {
|
||||||
|
ev.sender.send('keybinding-released', keybinding.name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fires when a global keybinding is being unregistered
|
||||||
|
ipcMain.on('unregister-keybinding', function(ev, keybindingCode) {
|
||||||
|
// Stop listening for global keyboard shortcuts if we're
|
||||||
|
// unregistering the last one
|
||||||
|
if (ioHookTasks <= 1) {
|
||||||
|
ioHook.stop();
|
||||||
|
}
|
||||||
|
ioHookTasks--;
|
||||||
|
|
||||||
|
ioHook.unregisterShortcutByKeys(keybindingCode);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Tell renderer process what key was pressed
|
||||||
|
// iohook has its own encoding for keys, so we can't just use a
|
||||||
|
// listener in the renderer process to register iohook shortcuts
|
||||||
|
let renderProcessID = null;
|
||||||
|
const reportKeyEvent = function(keyEvent) {
|
||||||
|
// "this" is the renderer process because we call this method with .bind()
|
||||||
|
renderProcessID.sender.send('keypress', {
|
||||||
|
keydown: keyEvent.type == 'keydown',
|
||||||
|
keycode: keyEvent.keycode,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fires when listening on all keys
|
||||||
|
// !!Security note: Ensure iohook is only allowed to listen to keybindings
|
||||||
|
// when the browser window is in focus, else an XSS could lead to keylogging
|
||||||
|
// Currently, this is achieved by leveraging browserWindow to act on focus loss
|
||||||
|
ipcMain.on('start-listening-keys', function(ev, keybindingCode) {
|
||||||
|
// Start recording keypresses
|
||||||
|
if (ioHookTasks <= 0) {
|
||||||
|
ioHookTasks = 0;
|
||||||
|
ioHook.start();
|
||||||
|
}
|
||||||
|
ioHookTasks++;
|
||||||
|
|
||||||
|
renderProcessID = ev;
|
||||||
|
ioHook.on('keydown', reportKeyEvent);
|
||||||
|
ioHook.on('keyup', reportKeyEvent);
|
||||||
|
});
|
||||||
|
|
||||||
|
const stopListeningKeys = () => {
|
||||||
|
// Stop recording keypresses
|
||||||
|
ioHook.off('keydown', reportKeyEvent);
|
||||||
|
ioHook.off('keyup', reportKeyEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
ipcMain.on('stop-listening-keys', () => {
|
||||||
|
if (ioHookTasks <= 1) {
|
||||||
|
ioHookTasks = 1;
|
||||||
|
ioHook.stop();
|
||||||
|
}
|
||||||
|
ioHookTasks--;
|
||||||
|
|
||||||
|
stopListeningKeys();
|
||||||
|
});
|
||||||
|
|
||||||
// Set the App User Model ID to match what the squirrel
|
// Set the App User Model ID to match what the squirrel
|
||||||
// installer uses for the shortcut icon.
|
// installer uses for the shortcut icon.
|
||||||
// This makes notifications work on windows 8.1 (and is
|
// This makes notifications work on windows 8.1 (and is
|
||||||
|
|
|
@ -6627,12 +6627,14 @@
|
||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
|
@ -6647,17 +6649,20 @@
|
||||||
"code-point-at": {
|
"code-point-at": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"console-control-strings": {
|
"console-control-strings": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"core-util-is": {
|
"core-util-is": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
|
@ -6774,7 +6779,8 @@
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"ini": {
|
"ini": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
|
@ -6786,6 +6792,7 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"number-is-nan": "^1.0.0"
|
"number-is-nan": "^1.0.0"
|
||||||
}
|
}
|
||||||
|
@ -6800,6 +6807,7 @@
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
|
@ -6807,12 +6815,14 @@
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"minipass": {
|
"minipass": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.4",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "^5.1.1",
|
"safe-buffer": "^5.1.1",
|
||||||
"yallist": "^3.0.0"
|
"yallist": "^3.0.0"
|
||||||
|
@ -6831,6 +6841,7 @@
|
||||||
"version": "0.5.1",
|
"version": "0.5.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"minimist": "0.0.8"
|
"minimist": "0.0.8"
|
||||||
}
|
}
|
||||||
|
@ -6911,7 +6922,8 @@
|
||||||
"number-is-nan": {
|
"number-is-nan": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
|
@ -6923,6 +6935,7 @@
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
|
@ -7044,6 +7057,7 @@
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"bundled": true,
|
"bundled": true,
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"code-point-at": "^1.0.0",
|
"code-point-at": "^1.0.0",
|
||||||
"is-fullwidth-code-point": "^1.0.0",
|
"is-fullwidth-code-point": "^1.0.0",
|
||||||
|
|
|
@ -17,5 +17,6 @@
|
||||||
"Explore rooms": "Explore rooms",
|
"Explore rooms": "Explore rooms",
|
||||||
"Room Directory": "Room Directory",
|
"Room Directory": "Room Directory",
|
||||||
"Search the room directory": "Search the room directory",
|
"Search the room directory": "Search the room directory",
|
||||||
"Get started with some tips from Riot Bot!": "Get started with some tips from Riot Bot!"
|
"Get started with some tips from Riot Bot!": "Get started with some tips from Riot Bot!",
|
||||||
|
"Push-to-Talk": "Push-to-Talk"
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import Promise from 'bluebird';
|
||||||
import rageshake from 'matrix-react-sdk/lib/rageshake/rageshake';
|
import rageshake from 'matrix-react-sdk/lib/rageshake/rageshake';
|
||||||
|
|
||||||
const ipcRenderer = window.ipcRenderer;
|
const ipcRenderer = window.ipcRenderer;
|
||||||
|
var globalKeybindings = {};
|
||||||
|
|
||||||
function platformFriendlyName(): string {
|
function platformFriendlyName(): string {
|
||||||
// used to use window.process but the same info is available here
|
// used to use window.process but the same info is available here
|
||||||
|
@ -99,6 +100,25 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||||
|
|
||||||
this.startUpdateCheck = this.startUpdateCheck.bind(this);
|
this.startUpdateCheck = this.startUpdateCheck.bind(this);
|
||||||
this.stopUpdateCheck = this.stopUpdateCheck.bind(this);
|
this.stopUpdateCheck = this.stopUpdateCheck.bind(this);
|
||||||
|
|
||||||
|
ipcRenderer.on('keybinding-pressed', (event, keybindName) => {
|
||||||
|
// Prevent holding down a shortcut meaning multiple presses
|
||||||
|
if (globalKeybindings[keybindName].pressed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
globalKeybindings[keybindName].pressed = true;
|
||||||
|
|
||||||
|
// Run the callback
|
||||||
|
globalKeybindings[keybindName].callback();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('keybinding-released', (event, keybindName) => {
|
||||||
|
// Keybinding is no longer pressed
|
||||||
|
globalKeybindings[keybindName].pressed = false;
|
||||||
|
|
||||||
|
// Run the callback
|
||||||
|
globalKeybindings[keybindName].releaseCallback();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async onUpdateDownloaded(ev, updateInfo) {
|
async onUpdateDownloaded(ev, updateInfo) {
|
||||||
|
@ -198,6 +218,26 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||||
ipcRenderer.send('check_updates');
|
ipcRenderer.send('check_updates');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addGlobalKeybinding(keybindName: string, keybindCode: string, callback: () => void, releaseCallback: () => void) {
|
||||||
|
// Add a keybinding that works even when the app is minimized
|
||||||
|
const keybinding = {name: keybindName, code: keybindCode};
|
||||||
|
|
||||||
|
ipcRenderer.send('register-keybinding', keybinding);
|
||||||
|
globalKeybindings[keybindName] = {callback, releaseCallback};
|
||||||
|
|
||||||
|
console.warn("Adding global keybinding:", keybindName, "with code:", keybindCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeGlobalKeybinding(keybindName: string, keybindCode: string) {
|
||||||
|
// Unbind a global keybinding
|
||||||
|
ipcRenderer.send('unregister-keybinding', keybindCode);
|
||||||
|
|
||||||
|
// Remove the callback
|
||||||
|
delete globalKeybindings[keybindName];
|
||||||
|
|
||||||
|
console.warn("Removing global keybinding:", keybindName);
|
||||||
|
}
|
||||||
|
|
||||||
installUpdate() {
|
installUpdate() {
|
||||||
// IPC to the main process to install the update, since quitAndInstall
|
// IPC to the main process to install the update, since quitAndInstall
|
||||||
// doesn't fire the before-quit event so the main process needs to know
|
// doesn't fire the before-quit event so the main process needs to know
|
||||||
|
@ -232,7 +272,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||||
const ipcCallId = ++this._nextIpcCallId;
|
const ipcCallId = ++this._nextIpcCallId;
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this._pendingIpcCalls[ipcCallId] = {resolve, reject};
|
this._pendingIpcCalls[ipcCallId] = {resolve, reject};
|
||||||
window.ipcRenderer.send('ipcCall', {id: ipcCallId, name, args});
|
ipcRenderer.send('ipcCall', {id: ipcCallId, name, args});
|
||||||
// Maybe add a timeout to these? Probably not necessary.
|
// Maybe add a timeout to these? Probably not necessary.
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue