diff --git a/src/main/constants.ts b/src/main/constants.ts index 01bdd80b..2e955d92 100644 --- a/src/main/constants.ts +++ b/src/main/constants.ts @@ -74,3 +74,7 @@ export const enum MessageBoxChoice { Default, Cancel } + +export const isWayland = + process.platform === "linux" && (process.env.XDG_SESSION_TYPE === "wayland" || !!process.env.WAYLAND_DISPLAY); +export const isLinux = process.platform === "linux"; diff --git a/src/main/keybinds.ts b/src/main/keybinds.ts new file mode 100644 index 00000000..7c400814 --- /dev/null +++ b/src/main/keybinds.ts @@ -0,0 +1,78 @@ +/* + * Vesktop, a desktop app aiming to give you a snappier Discord Experience + * Copyright (c) 2024 Vendicated and Vencord contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { spawnSync } from "node:child_process"; +import { constants, existsSync, open, unlinkSync } from "node:fs"; +import { join } from "node:path"; + +import { Socket } from "net"; +import { IpcEvents } from "shared/IpcEvents"; + +import { mainWin } from "./mainWindow"; + +const xdgRuntimeDir = process.env.XDG_RUNTIME_DIR || process.env.TMP || "/tmp"; +const socketFile = join(xdgRuntimeDir, "vesktop-ipc"); + +const Actions = new Set([IpcEvents.TOGGLE_SELF_DEAF, IpcEvents.TOGGLE_SELF_MUTE]); + +function createFIFO() { + if (existsSync(socketFile)) { + try { + unlinkSync(socketFile); + } catch (err) { + console.error("Failed to remove existing mkfifo file:", err); + return false; + } + } + + try { + spawnSync("mkfifo", [socketFile]); + } catch (err) { + console.error("Failed to create mkfifo while initializing keybinds:", err); + return false; + } + return true; +} + +function openFIFO() { + try { + open(socketFile, constants.O_RDONLY | constants.O_NONBLOCK, (err, fd) => { + if (err) { + console.error("Error opening pipe while initializing keybinds:", err); + return; + } + + const pipe = new Socket({ fd }); + pipe.on("data", data => { + const action = data.toString().trim(); + if (Actions.has(action as IpcEvents)) { + mainWin.webContents.send(action); + } + }); + + pipe.on("end", () => { + pipe.destroy(); + openFIFO(); + }); + }); + } catch (err) { + console.error("Can't open socket file.", err); + } +} + +function cleanup() { + try { + unlinkSync(socketFile); + } catch (err) {} +} + +process.on("exit", cleanup); + +export function initKeybinds() { + if (createFIFO()) { + openFIFO(); + } +} diff --git a/src/main/mainWindow.ts b/src/main/mainWindow.ts index c4159f57..a7c2a16f 100644 --- a/src/main/mainWindow.ts +++ b/src/main/mainWindow.ts @@ -31,6 +31,7 @@ import { DATA_DIR, DEFAULT_HEIGHT, DEFAULT_WIDTH, + isLinux, MessageBoxChoice, MIN_HEIGHT, MIN_WIDTH, @@ -38,6 +39,7 @@ import { } from "./constants"; import { darwinURL } from "./index"; import { sendRendererCommand } from "./ipcCommands"; +import { initKeybinds } from "./keybinds"; import { Settings, State, VencordSettings } from "./settings"; import { createSplashWindow } from "./splash"; import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally"; @@ -535,4 +537,5 @@ export async function createWindows() { }); initArRPC(); + if (isLinux) initKeybinds(); } diff --git a/src/main/screenShare.ts b/src/main/screenShare.ts index 5ba93a8c..c22febea 100644 --- a/src/main/screenShare.ts +++ b/src/main/screenShare.ts @@ -8,11 +8,9 @@ import { desktopCapturer, session, Streams } from "electron"; import type { StreamPick } from "renderer/components/ScreenSharePicker"; import { IpcEvents } from "shared/IpcEvents"; +import { isWayland } from "./constants"; import { handle } from "./utils/ipcWrappers"; -const isWayland = - process.platform === "linux" && (process.env.XDG_SESSION_TYPE === "wayland" || !!process.env.WAYLAND_DISPLAY); - export function registerScreenShareHandler() { handle(IpcEvents.CAPTURER_GET_LARGE_THUMBNAIL, async (_, id: string) => { const sources = await desktopCapturer.getSources({ diff --git a/src/preload/VesktopNative.ts b/src/preload/VesktopNative.ts index 69884cd7..58a093ce 100644 --- a/src/preload/VesktopNative.ts +++ b/src/preload/VesktopNative.ts @@ -75,6 +75,14 @@ export const VesktopNative = { copyImage: (imageBuffer: Uint8Array, imageSrc: string) => invoke(IpcEvents.CLIPBOARD_COPY_IMAGE, imageBuffer, imageSrc) }, + voice: { + onToggleSelfMute: (listener: (...args: any[]) => void) => { + ipcRenderer.on(IpcEvents.TOGGLE_SELF_MUTE, listener); + }, + onToggleSelfDeaf: (listener: (...args: any[]) => void) => { + ipcRenderer.on(IpcEvents.TOGGLE_SELF_DEAF, listener); + } + }, debug: { launchGpu: () => invoke(IpcEvents.DEBUG_LAUNCH_GPU), launchWebrtcInternals: () => invoke(IpcEvents.DEBUG_LAUNCH_WEBRTC_INTERNALS) diff --git a/src/renderer/index.ts b/src/renderer/index.ts index 0f863cc2..6296d0a8 100644 --- a/src/renderer/index.ts +++ b/src/renderer/index.ts @@ -13,6 +13,8 @@ import "./arrpc"; export * as Components from "./components"; +import { findByPropsLazy } from "@vencord/types/webpack"; + import SettingsUi from "./components/settings/Settings"; import { VesktopLogger } from "./logger"; import { Settings } from "./settings"; @@ -31,3 +33,8 @@ customSettingsSections.push(() => ({ element: SettingsUi, className: "vc-vesktop-settings" })); + +const VoiceActions = findByPropsLazy("toggleSelfMute"); + +VesktopNative.voice.onToggleSelfMute(() => VoiceActions.toggleSelfMute()); +VesktopNative.voice.onToggleSelfDeaf(() => VoiceActions.toggleSelfDeaf()); diff --git a/src/shared/IpcEvents.ts b/src/shared/IpcEvents.ts index dd05e3ba..a7aea4dd 100644 --- a/src/shared/IpcEvents.ts +++ b/src/shared/IpcEvents.ts @@ -51,6 +51,8 @@ export const enum IpcEvents { ARRPC_ACTIVITY = "VCD_ARRPC_ACTIVITY", CLIPBOARD_COPY_IMAGE = "VCD_CLIPBOARD_COPY_IMAGE", + TOGGLE_SELF_MUTE = "VCD_TOGGLE_SELF_MUTE", + TOGGLE_SELF_DEAF = "VCD_TOGGLE_SELF_DEAF", DEBUG_LAUNCH_GPU = "VCD_DEBUG_LAUNCH_GPU", DEBUG_LAUNCH_WEBRTC_INTERNALS = "VCD_DEBUG_LAUNCH_WEBRTC",