feat(keybinds): basic implementation
This commit is contained in:
parent
6993b2a7d4
commit
073aa6621f
8 changed files with 192 additions and 5 deletions
|
@ -16,6 +16,7 @@ import { registerMediaPermissionsHandler } from "./mediaPermissions";
|
|||
import { registerScreenShareHandler } from "./screenShare";
|
||||
import { Settings, State } from "./settings";
|
||||
import { isDeckGameMode } from "./utils/steamOS";
|
||||
import { registerKeybindsHandler } from "./keybinds";
|
||||
|
||||
if (IS_DEV) {
|
||||
require("source-map-support").install();
|
||||
|
@ -62,6 +63,7 @@ function init() {
|
|||
|
||||
registerScreenShareHandler();
|
||||
registerMediaPermissionsHandler();
|
||||
registerKeybindsHandler();
|
||||
|
||||
bootstrap();
|
||||
|
||||
|
|
71
src/main/keybinds.ts
Normal file
71
src/main/keybinds.ts
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { globalShortcut } from "electron";
|
||||
|
||||
import { IpcEvents } from "../shared/IpcEvents";
|
||||
import { handle } from "./utils/ipcWrappers";
|
||||
|
||||
// mapping the discord ids to the electron accelerators
|
||||
const registeredKeybinds = new Map<string, string>();
|
||||
|
||||
export function registerKeybindsHandler() {
|
||||
handle(IpcEvents.KEYBIND_REGISTER, async (_, id: string, shortcut: number[][], callback: () => void) => {
|
||||
const accelerator = discordShortcutToElectronAccelerator(shortcut);
|
||||
globalShortcut.register(accelerator, callback);
|
||||
registeredKeybinds.set(id, accelerator);
|
||||
console.log("Registered keybind", id, shortcut, accelerator);
|
||||
});
|
||||
handle(IpcEvents.KEYBIND_UNREGISTER, async (_, id: string) => {
|
||||
const keybind = registeredKeybinds.get(id);
|
||||
if (keybind) {
|
||||
globalShortcut.unregister(keybind);
|
||||
registeredKeybinds.delete(id);
|
||||
console.log("Unregistered keybind", id, keybind);
|
||||
} else {
|
||||
console.warn("Tried to unregister keybind", id, "but it was not registered");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function discordShortcutToElectronAccelerator(shortcut: number[][]): string {
|
||||
// discords shortcuts are an array of an array of numbers, where each number is a key code
|
||||
// electron expects strings like "Control+Shift+B"
|
||||
|
||||
let accelerator = "";
|
||||
|
||||
// for some reason array[0] is always 0 and array[2] is always 4 TODO: investigate what these are for
|
||||
const keyCodes = shortcut.map(keybind => keybind[1]);
|
||||
|
||||
// convert modifier keys to strings
|
||||
// 16 = shift, 17 = control, 18 = alt
|
||||
for (const keyCode of keyCodes) {
|
||||
switch (keyCode) {
|
||||
case 16:
|
||||
accelerator += "Shift+";
|
||||
break;
|
||||
case 17:
|
||||
accelerator += "Control+";
|
||||
break;
|
||||
case 18:
|
||||
accelerator += "Alt+";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// convert other keys to strings
|
||||
// numbers are from 48 to 57
|
||||
// letters are from 65 to 90
|
||||
for (const keyCode of keyCodes) {
|
||||
if ((keyCode >= 48 && keyCode <= 57) || (keyCode >= 65 && keyCode <= 90)) {
|
||||
// String.fromCharCode only works for numbers and letters so only those are support as of now
|
||||
// TODO: improve this method to add support for more keys
|
||||
accelerator += String.fromCharCode(keyCode);
|
||||
}
|
||||
}
|
||||
|
||||
return accelerator;
|
||||
}
|
|
@ -454,20 +454,20 @@ export async function createWindows() {
|
|||
splash.destroy();
|
||||
|
||||
if (!startMinimized) {
|
||||
mainWin!.show();
|
||||
if (State.store.maximized && !isDeckGameMode) mainWin!.maximize();
|
||||
mainWin.show();
|
||||
if (State.store.maximized && !isDeckGameMode) mainWin.maximize();
|
||||
}
|
||||
|
||||
if (isDeckGameMode) {
|
||||
// always use entire display
|
||||
mainWin!.setFullScreen(true);
|
||||
mainWin.setFullScreen(true);
|
||||
|
||||
askToApplySteamLayout(mainWin);
|
||||
}
|
||||
|
||||
mainWin.once("show", () => {
|
||||
if (State.store.maximized && !mainWin!.isMaximized() && !isDeckGameMode) {
|
||||
mainWin!.maximize();
|
||||
if (State.store.maximized && !mainWin.isMaximized() && !isDeckGameMode) {
|
||||
mainWin.maximize();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -59,6 +59,11 @@ export const VesktopNative = {
|
|||
capturer: {
|
||||
getLargeThumbnail: (id: string) => invoke<string>(IpcEvents.CAPTURER_GET_LARGE_THUMBNAIL, id)
|
||||
},
|
||||
keybinds: {
|
||||
register: (id: string, shortcut: number[][], callback: () => void) =>
|
||||
invoke<void>(IpcEvents.KEYBIND_REGISTER, id, shortcut, callback),
|
||||
unregister: (id: string) => invoke<void>(IpcEvents.KEYBIND_UNREGISTER, id)
|
||||
},
|
||||
/** only available on Linux. */
|
||||
virtmic: {
|
||||
list: () =>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
// TODO: Possibly auto generate glob if we have more patches in the future
|
||||
import "./enableNotificationsByDefault";
|
||||
import "./keybinds";
|
||||
import "./platformClass";
|
||||
import "./screenShareAudio";
|
||||
import "./spellCheck";
|
||||
|
|
82
src/renderer/patches/keybinds.tsx
Normal file
82
src/renderer/patches/keybinds.tsx
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { findByPropsLazy, findStoreLazy, onceReady } from "@vencord/types/webpack";
|
||||
import { FluxDispatcher } from "@vencord/types/webpack/common";
|
||||
|
||||
import { Keybind, KeybindsStoreType } from "../../shared/keybinds";
|
||||
import { addPatch } from "./shared";
|
||||
|
||||
const KeybindsStore: KeybindsStoreType = findStoreLazy("KeybindsStore");
|
||||
const MuteActions: {
|
||||
toggleSelfMute: () => void;
|
||||
toggleSelfDeaf: () => void;
|
||||
} = findByPropsLazy("MuteActions");
|
||||
|
||||
// Re-enable Discord's keybind editor
|
||||
addPatch({
|
||||
patches: [
|
||||
{
|
||||
// maybe one day they will fix the typo "broswer"
|
||||
find: "Messages.KEYBIND_IN_BROSWER_NOTICE",
|
||||
replacement: [
|
||||
{
|
||||
match: /(\i)\.isPlatformEmbedded/,
|
||||
replace: "true"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
onceReady.then(() => {
|
||||
// register keybinds on load
|
||||
toggleAllKeybinds(true);
|
||||
|
||||
// we only need this event as this gets fired when the keybinds page is opened/closed and this is the only place where we need to adjust our keybinds
|
||||
FluxDispatcher.subscribe("KEYBINDS_ENABLE_ALL_KEYBINDS", ({ enable }: { enable: boolean }) => {
|
||||
console.log("keybinds enable all keybinds", enable);
|
||||
toggleAllKeybinds(enable);
|
||||
});
|
||||
});
|
||||
|
||||
function shouldAllowKeybind(keybind: Keybind) {
|
||||
return keybind.enabled && !keybind.managed && keybind.shortcut && keybind.action !== "UNASSIGNED";
|
||||
}
|
||||
|
||||
function getKeybindHandler(action: Keybind["action"]) {
|
||||
return () => {
|
||||
// execute the action
|
||||
switch (action) {
|
||||
case "TOGGLE_MUTE":
|
||||
MuteActions.toggleSelfMute();
|
||||
break;
|
||||
case "TOGGLE_DEAFEN":
|
||||
MuteActions.toggleSelfDeaf();
|
||||
break;
|
||||
default:
|
||||
console.warn("Unknown keybind action", action);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function toggleAllKeybinds(enable: boolean) {
|
||||
const keybinds = KeybindsStore.getState();
|
||||
for (const keybind of Object.values(keybinds)) {
|
||||
const { id, shortcut, action } = keybind;
|
||||
if (!shouldAllowKeybind(keybind)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
VesktopNative.keybinds.register(id, shortcut, getKeybindHandler(action));
|
||||
console.log("keybind registered", action);
|
||||
} else {
|
||||
VesktopNative.keybinds.unregister(id);
|
||||
console.log("keybind unregistered", action);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -38,6 +38,9 @@ export const enum IpcEvents {
|
|||
|
||||
CAPTURER_GET_LARGE_THUMBNAIL = "VCD_CAPTURER_GET_LARGE_THUMBNAIL",
|
||||
|
||||
KEYBIND_UNREGISTER = "VCD_KEYBIND_UNREGISTER",
|
||||
KEYBIND_REGISTER = "VCD_KEYBIND_REGISTER",
|
||||
|
||||
AUTOSTART_ENABLED = "VCD_AUTOSTART_ENABLED",
|
||||
ENABLE_AUTOSTART = "VCD_ENABLE_AUTOSTART",
|
||||
DISABLE_AUTOSTART = "VCD_DISABLE_AUTOSTART",
|
||||
|
|
23
src/shared/keybinds.d.ts
vendored
Normal file
23
src/shared/keybinds.d.ts
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { Constants } from "discord-types/other";
|
||||
|
||||
type ValueOf<T> = T[keyof T];
|
||||
export type GlobalKeybindAction = ValueOf<Constants["GlobalKeybindActions"]>;
|
||||
|
||||
export interface Keybind {
|
||||
id: string;
|
||||
enabled: boolean;
|
||||
action: GlobalKeybindAction;
|
||||
shortcut: number[][];
|
||||
managed: boolean;
|
||||
params: any;
|
||||
}
|
||||
|
||||
export interface KeybindsStoreType {
|
||||
getState(): Record<number, Keybind>;
|
||||
}
|
Reference in a new issue