@launchmenu/core
Version:
An environment for visual keyboard controlled applets
366 lines • 30.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GlobalKeyHandler = void 0;
const electron_1 = require("electron");
const electronAcceleratorKeyMapping_1 = require("./electronAcceleratorKeyMapping");
const primaryGlobalShortcutKeys_1 = require("./primaryGlobalShortcutKeys");
const node_global_key_listener_1 = require("node-global-key-listener");
const nodeGlobalKeyListenerMapping_1 = require("./nodeGlobalKeyListenerMapping");
const keys_1 = require("../keyIdentifiers/keys");
const model_react_1 = require("model-react");
const isPlatform_1 = require("../../utils/platform/isPlatform");
/** A class that can be used for registering keyboard shortcuts. Should be used as a singleton obtained from LaunchMenu */
class GlobalKeyHandler {
/**
* Creates a new instance of the global key handler
* @param useElectronListener A data retriever to determine whether to force use electron's listener
*/
constructor(useElectronListener = () => true) {
this.keyListeners = [];
this.electronListeners = {};
// Track whether the handler is disposed in order to not register new handlers when it is
this.silentError = true; // TODO: May want to change this in the future and/or make it configurable (but it currently happens on every reload, so silent error is preferable)
this.disposed = false;
this.currentUseElectronListener = false;
this.shortcutListeners = [];
try {
this.advancedManager = new node_global_key_listener_1.GlobalKeyboardListener();
}
catch (e) {
console.error(e);
}
this.useElectronListener = useElectronListener;
this.useElectronListenerObserver = this.setupShortcutMethodObserver();
}
/**
* Adds a global key listeners that listens to all events
* @param callback The key press callback
* @returns A function that can be invoked to remove the listener
*/
addListener(callback) {
var _a;
if (this.disposed) {
if (this.silentError)
return () => { };
throw new Error("Handler already disposed");
}
if (!this.advancedManager)
throw new Error("Global key listeners are not supported on this platform");
this.keyListeners.push(callback);
// If this is the first listener, add it to key hook
if (this.keyListeners.length == 1) {
this.invokeListeners = (event, held) => {
if (this.currentUseElectronListener)
return;
const ev = this.convertKeyEvent(event, held);
if (!ev)
return;
let stopPropagation = false;
let stopImmediatePropagation = false;
for (let listener of this.keyListeners) {
const res = listener(ev);
if (typeof res == "object") {
if (res.stopPropagation)
stopPropagation = true;
if (res.stopImmediatePropagation) {
stopImmediatePropagation = true;
break;
}
}
else if (res)
stopPropagation = true;
}
return {
stopImmediatePropagation,
stopPropagation,
};
};
(_a = this.advancedManager) === null || _a === void 0 ? void 0 : _a.addListener(this.invokeListeners);
}
// Return a function to remove the listener
return () => {
var _a;
const index = this.keyListeners.indexOf(callback);
if (index != -1)
this.keyListeners.splice(index, 1);
// Remove the key hook listener if no listeners remain
if (this.keyListeners.length == 0 && this.invokeListeners) {
(_a = this.advancedManager) === null || _a === void 0 ? void 0 : _a.removeListener(this.invokeListeners);
}
};
}
/**
* Converts a global key event to the format as used by LM
* @param event The event to convert
* @param held The keys that are currently held
* @returns The LM event
*/
convertKeyEvent(event, held) {
if (!event.name)
return undefined;
const key = nodeGlobalKeyListenerMapping_1.nodeGlobalKeyListenerMapping[event.name];
if (!key)
return undefined;
return {
key,
rawcode: event.name,
type: event.state == "UP" ? "keyup" : "keydown",
altKey: held["LEFT ALT"] ? "left" : held["RIGHT ALT"] ? "right" : undefined,
ctrlKey: held["LEFT CTRL"]
? "left"
: held["RIGHT CTRL"]
? "right"
: undefined,
metaKey: held["LEFT META"]
? "left"
: held["RIGHT META"]
? "right"
: undefined,
shiftKey: held["LEFT SHIFT"]
? "left"
: held["RIGHT SHIFT"]
? "right"
: undefined,
};
}
/**
* Checks whether global key listeners are supported on the current OS/environment
* @param hook The hook to subscribe to changes
* @returns Whether listeners are supported
*/
areListenersSupported(hook) {
return (!this.useElectronListener(hook) &&
!!this.advancedManager &&
(!isPlatform_1.isPlatform("mac") ||
electron_1.remote.systemPreferences.isTrustedAccessibilityClient(false)));
}
/**
* Sets up an observer that takes care of moving the shortcut listeners if the setting changed
*/
setupShortcutMethodObserver() {
return new model_react_1.Observer(h => !this.areListenersSupported(h)).listen(useElectron => {
if (this.currentUseElectronListener != useElectron) {
this.currentUseElectronListener = useElectron;
// Dispose all the old listeners
const allListeners = this.shortcutListeners;
this.shortcutListeners = [];
allListeners.forEach(({ dispose }) => dispose());
// Unregister all listeners
allListeners.forEach(bundle => {
const newDispose = this.addShortcut(bundle.shortcut, bundle.callback);
// Make sure that the original dispose method can still be used (since this was returned from the original addShortcut callback)
bundle.dispose = newDispose;
});
// Add or remove the global listener
if (this.advancedManager && this.invokeListeners) {
if (useElectron) {
this.advancedManager.removeListener(this.invokeListeners);
this.advancedManager.kill();
}
else {
this.advancedManager.addListener(this.invokeListeners);
}
}
}
}, true);
}
/**
* Adds a global shortcut
* @param shortcut The keypattern to listen for
* @param callback The callback to trigger when the event is fired
* @returns A function that can be invoked to remove the shortcut
*/
addShortcut(shortcut, callback) {
if (this.disposed) {
if (this.silentError)
return () => { };
throw new Error("Handler already disposed");
}
const invalid = this.isShortcutInvalid(shortcut);
if (invalid)
throw invalid[0].error;
// Use one of the two shortcut methods
let dispose;
if (!this.areListenersSupported())
dispose = this.addElectronShortcut(shortcut, callback);
else
dispose = this.addCustomShortcut(shortcut, callback);
// Setup a callback to dispose all data associated to a shortcut
const fullDispose = () => {
const index = this.shortcutListeners.indexOf(bundle);
if (index != -1)
this.shortcutListeners.splice(index, 1);
dispose();
};
const bundle = { shortcut, callback, dispose: fullDispose };
this.shortcutListeners.push(bundle);
// Note that bundle's fullDispose method can be changed throughout its lifetime, the bundle object mutates (to support dynamic `useElectronListener`)
return () => bundle.dispose();
}
/**
* Checks whether the given keypattern is valid as a global shortcut or not
* @param shortcut The key pattern to check
* @returns False if the pattern is valid, or the patterns and errors if invalid
*/
isShortcutInvalid(shortcut) {
const invalid = this.getElectronAccelerators(shortcut)
.map((res, index) => ({ pattern: shortcut.patterns[index], error: res }))
.filter((res) => typeof res.error != "string");
return invalid.length > 0 ? invalid : false;
}
/**
* Adds a global shortcut using the node-global-key-listener package
* @param shortcut The key pattern to listen for
* @param callback The callback to trigger when the event is fired
* @returns A function that can be invoked to remove the shortcut
*/
addCustomShortcut(shortcut, callback) {
// Create a format that's faster to compare with
const keys = {};
shortcut.patterns.forEach(({ pattern }, i) => {
const primaryKey = pattern.find(key => ![
"altLeft",
"altRight",
"controlLeft",
"controlRight",
"shiftLeft",
"shiftRight",
"metaLeft",
"metaRight",
"alt",
"ctrl",
"shift",
"meta",
].includes(key));
function getState(pattern, left, right, either) {
const includesLeft = pattern.includes(left);
const includesRight = pattern.includes(right);
const includesEither = pattern.includes(either);
return [
...(includesLeft || includesEither ? ["left"] : []),
...(includesRight || includesEither ? ["right"] : []),
...(!includesLeft && !includesRight && !includesEither
? [undefined]
: []),
];
}
if (primaryKey) {
Object.entries(keys_1.keyIdMapping)
.filter(([id, name]) => id == primaryKey || name == primaryKey)
.forEach(([id]) => {
var _a;
if (!keys[id])
keys[id] = [];
(_a = keys[id]) === null || _a === void 0 ? void 0 : _a.push({
altKey: getState(pattern, "altLeft", "altRight", "alt"),
ctrlKey: getState(pattern, "controlLeft", "controlRight", "ctrl"),
shiftKey: getState(pattern, "shiftLeft", "shiftRight", "shift"),
metaKey: getState(pattern, "metaLeft", "metaRight", "meta"),
});
});
}
});
// Create the listener
const listener = event => {
if (event.type == "keyup")
return;
const modifiers = keys[event.key];
if (modifiers != undefined) {
const matches = modifiers.some(modifiers => modifiers.altKey.includes(event.altKey) &&
modifiers.ctrlKey.includes(event.ctrlKey) &&
modifiers.shiftKey.includes(event.shiftKey) &&
modifiers.metaKey.includes(event.metaKey));
if (matches) {
setTimeout(callback); // Prevents reaching timeout (apart from when node is already busy while triggering the shortcut :/)
return true;
}
}
};
return this.addListener(listener);
}
/**
* Adds a global shortcut using electron's shortcut system
* @param shortcut The key pattern to listen for
* @param callback The callback to trigger when the event is fired
* @returns A function that can be invoked to remove the shortcut
*/
addElectronShortcut(shortcut, callback) {
const accelerators = this.getElectronAccelerators(shortcut).filter((n) => typeof n == "string");
// Register each accelerator
accelerators.forEach(accelerator => {
if (!this.electronListeners[accelerator]) {
const listeners = [];
this.electronListeners[accelerator] = listeners;
const invoker = () => listeners.forEach(listener => listener());
electron_1.remote.globalShortcut.register(accelerator, invoker);
}
this.electronListeners[accelerator].push(callback);
});
// Return a function to remove the listeners
return () => {
accelerators.forEach(accelerator => {
const listeners = this.electronListeners[accelerator];
if (!listeners)
return;
const index = listeners.indexOf(callback);
if (index != -1) {
listeners.splice(index, 1);
if (listeners.length == 0) {
electron_1.remote.globalShortcut.unregister(accelerator);
delete this.electronListeners[accelerator];
}
}
});
};
}
/**
* Retrieves the electron accelerator string if valid, or an error object otherwise
* @param shortcut The key pattern shortcut
* @returns The accelerator string or error object
*/
getElectronAccelerators(shortcut) {
return shortcut.patterns.map(({ allowExtra, pattern, type }) => {
var _a;
// Check the extra pattern event data
if ((_a = allowExtra === null || allowExtra === void 0 ? void 0 : allowExtra.length) !== null && _a !== void 0 ? _a : 0 > 0)
return new Error("Global shortcuts can't have extra keys");
if (type != "down")
return new Error("Global shortcuts can only listen for key up events");
// Check the pattern itself
const { error } = pattern.reduce(({ error, charCount }, key) => {
if (error)
return { error, charCount };
if (primaryGlobalShortcutKeys_1.primaryGlobalShortcutKeys.includes(key)) {
charCount += 1;
if (charCount > 1)
error = new Error("Global shortcuts can only contain 1 primary key");
}
return { error, charCount };
}, { error: null, charCount: 0 });
if (error)
return error;
// Obtain the shortcut
return pattern
.map(key => { var _a; return (_a = electronAcceleratorKeyMapping_1.electronAcceleratorKeyMapping[key]) !== null && _a !== void 0 ? _a : key; })
.join("+");
});
}
/**
* Disposes all listeners
*/
destroy() {
this.disposed = true;
this.useElectronListenerObserver.destroy();
if (this.advancedManager) {
if (this.invokeListeners)
this.advancedManager.removeListener(this.invokeListeners);
this.keyListeners = [];
this.advancedManager.kill();
}
for (let shortcut in this.electronListeners)
electron_1.remote.globalShortcut.unregister(shortcut);
this.electronListeners = {};
}
}
exports.GlobalKeyHandler = GlobalKeyHandler;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiR2xvYmFsS2V5SGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9rZXlIYW5kbGVyL2dsb2JhbEtleUhhbmRsZXIvR2xvYmFsS2V5SGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSx1Q0FBZ0M7QUFHaEMsbUZBQThFO0FBQzlFLDJFQUFzRTtBQUN0RSx1RUFLa0M7QUFFbEMsaUZBQTRFO0FBRzVFLGlEQUFpRTtBQUVqRSw2Q0FBZ0U7QUFDaEUsZ0VBQTJEO0FBRTNELDBIQUEwSDtBQUMxSCxNQUFhLGdCQUFnQjtJQXFCekI7OztPQUdHO0lBQ0gsWUFBbUIsc0JBQStDLEdBQUcsRUFBRSxDQUFDLElBQUk7UUFyQmxFLGlCQUFZLEdBQXlCLEVBQUUsQ0FBQztRQUN4QyxzQkFBaUIsR0FBbUMsRUFBRSxDQUFDO1FBRWpFLHlGQUF5RjtRQUMvRSxnQkFBVyxHQUFHLElBQUksQ0FBQyxDQUFDLG9KQUFvSjtRQUN4SyxhQUFRLEdBQUcsS0FBSyxDQUFDO1FBS2pCLCtCQUEwQixHQUFZLEtBQUssQ0FBQztRQUM1QyxzQkFBaUIsR0FJckIsRUFBRSxDQUFDO1FBT0wsSUFBSTtZQUNBLElBQUksQ0FBQyxlQUFlLEdBQUcsSUFBSSxpREFBc0IsRUFBRSxDQUFDO1NBQ3ZEO1FBQUMsT0FBTyxDQUFDLEVBQUU7WUFDUixPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1NBQ3BCO1FBQ0QsSUFBSSxDQUFDLG1CQUFtQixHQUFHLG1CQUFtQixDQUFDO1FBQy9DLElBQUksQ0FBQywyQkFBMkIsR0FBRyxJQUFJLENBQUMsMkJBQTJCLEVBQUUsQ0FBQztJQUMxRSxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLFdBQVcsQ0FBQyxRQUE0Qjs7UUFDM0MsSUFBSSxJQUFJLENBQUMsUUFBUSxFQUFFO1lBQ2YsSUFBSSxJQUFJLENBQUMsV0FBVztnQkFBRSxPQUFPLEdBQUcsRUFBRSxHQUFFLENBQUMsQ0FBQztZ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