Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global shortcuts support #371

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { checkUpdates } from "updater/main";

import { DATA_DIR } from "./constants";
import { createFirstLaunchTour } from "./firstLaunch";
import { registerKeybindsHandler } from "./keybinds";
import { createWindows, mainWin } from "./mainWindow";
import { registerMediaPermissionsHandler } from "./mediaPermissions";
import { registerScreenShareHandler } from "./screenShare";
Expand Down Expand Up @@ -62,6 +63,7 @@ function init() {

registerScreenShareHandler();
registerMediaPermissionsHandler();
registerKeybindsHandler();

bootstrap();

Expand Down
65 changes: 65 additions & 0 deletions src/main/keybinds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* 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";

// saving the registered keybinds to unregister them when needed
const registeredKeybinds: string[] = [];

export function registerKeybindsHandler() {
handle(IpcEvents.KEYBIND_REGISTER, async (_, id: string, shortcut: number[][], callback: () => void) => {
const accelerator = discordShortcutToElectronAccelerator(shortcut);
globalShortcut.register(accelerator, callback);
registeredKeybinds.push(accelerator);
console.log("Registered keybind", id, shortcut, accelerator);
});
handle(IpcEvents.KEYBIND_UNREGISTER, async () => {
registeredKeybinds.forEach(keybind => globalShortcut.unregister(keybind));
console.log("Unregistered keybinds");
});
}

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;
}
10 changes: 5 additions & 5 deletions src/main/mainWindow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
});
});
Expand Down
5 changes: 5 additions & 0 deletions src/preload/VesktopNative.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: () => invoke<void>(IpcEvents.KEYBIND_UNREGISTER)
},
/** only available on Linux. */
virtmic: {
list: () =>
Expand Down
1 change: 1 addition & 0 deletions src/renderer/patches/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
82 changes: 82 additions & 0 deletions src/renderer/patches/keybinds.tsx
Original file line number Diff line number Diff line change
@@ -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: [
{
// eslint-disable-next-line no-useless-escape
match: /\i\.isPlatformEmbedded/,
replace: "true"
}
]
}
]
});

onceReady.then(() => {
// register keybinds on load
registerAllKeybinds();

// 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);
if (enable) {
registerAllKeybinds();
} else {
VesktopNative.keybinds.unregister();
}
});
});

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 registerAllKeybinds() {
const keybinds = KeybindsStore.getState();
for (const keybind of Object.values(keybinds)) {
if (!shouldAllowKeybind(keybind)) {
continue;
}

const { id, shortcut, action } = keybind;
VesktopNative.keybinds.register(id, shortcut, getKeybindHandler(action));
console.log("keybind registered", action);
}
}
3 changes: 3 additions & 0 deletions src/shared/IpcEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
23 changes: 23 additions & 0 deletions src/shared/keybinds.d.ts
Original file line number Diff line number Diff line change
@@ -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>;
}
Loading