From 073aa6621f386c51cd7217b4a4c358324d9f2d54 Mon Sep 17 00:00:00 2001 From: D3SOX Date: Thu, 1 Feb 2024 16:00:11 +0100 Subject: [PATCH] feat(keybinds): basic implementation --- src/main/index.ts | 2 + src/main/keybinds.ts | 71 ++++++++++++++++++++++++++ src/main/mainWindow.ts | 10 ++-- src/preload/VesktopNative.ts | 5 ++ src/renderer/patches/index.ts | 1 + src/renderer/patches/keybinds.tsx | 82 +++++++++++++++++++++++++++++++ src/shared/IpcEvents.ts | 3 ++ src/shared/keybinds.d.ts | 23 +++++++++ 8 files changed, 192 insertions(+), 5 deletions(-) create mode 100644 src/main/keybinds.ts create mode 100644 src/renderer/patches/keybinds.tsx create mode 100644 src/shared/keybinds.d.ts diff --git a/src/main/index.ts b/src/main/index.ts index 9d6a52c..12f50d4 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -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(); diff --git a/src/main/keybinds.ts b/src/main/keybinds.ts new file mode 100644 index 0000000..a296f02 --- /dev/null +++ b/src/main/keybinds.ts @@ -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(); + +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; +} diff --git a/src/main/mainWindow.ts b/src/main/mainWindow.ts index 62a2559..966ca84 100644 --- a/src/main/mainWindow.ts +++ b/src/main/mainWindow.ts @@ -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(); } }); }); diff --git a/src/preload/VesktopNative.ts b/src/preload/VesktopNative.ts index 625b8c1..ddbef0c 100644 --- a/src/preload/VesktopNative.ts +++ b/src/preload/VesktopNative.ts @@ -59,6 +59,11 @@ export const VesktopNative = { capturer: { getLargeThumbnail: (id: string) => invoke(IpcEvents.CAPTURER_GET_LARGE_THUMBNAIL, id) }, + keybinds: { + register: (id: string, shortcut: number[][], callback: () => void) => + invoke(IpcEvents.KEYBIND_REGISTER, id, shortcut, callback), + unregister: (id: string) => invoke(IpcEvents.KEYBIND_UNREGISTER, id) + }, /** only available on Linux. */ virtmic: { list: () => diff --git a/src/renderer/patches/index.ts b/src/renderer/patches/index.ts index 7d4c4b3..3c7bfb5 100644 --- a/src/renderer/patches/index.ts +++ b/src/renderer/patches/index.ts @@ -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"; diff --git a/src/renderer/patches/keybinds.tsx b/src/renderer/patches/keybinds.tsx new file mode 100644 index 0000000..b0c6ffd --- /dev/null +++ b/src/renderer/patches/keybinds.tsx @@ -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); + } + } +} diff --git a/src/shared/IpcEvents.ts b/src/shared/IpcEvents.ts index df64403..6850405 100644 --- a/src/shared/IpcEvents.ts +++ b/src/shared/IpcEvents.ts @@ -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", diff --git a/src/shared/keybinds.d.ts b/src/shared/keybinds.d.ts new file mode 100644 index 0000000..fe807c4 --- /dev/null +++ b/src/shared/keybinds.d.ts @@ -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[keyof T]; +export type GlobalKeybindAction = ValueOf; + +export interface Keybind { + id: string; + enabled: boolean; + action: GlobalKeybindAction; + shortcut: number[][]; + managed: boolean; + params: any; +} + +export interface KeybindsStoreType { + getState(): Record; +}