Started implementing proof of concept for keybind support on Wayland

This commit is contained in:
HannahPadd
2026-03-18 21:17:21 +01:00
parent faa3da362e
commit 810c7e5327
9 changed files with 64 additions and 195 deletions

View File

@@ -3,7 +3,6 @@ import { useMemo } from 'react';
import { NavLink, useLocation, useMatch } from 'react-router-dom';
import { Typography } from '@/components/commons/Typography';
import { useVRCConfig } from '@/hooks/vrc-config';
import { platform } from '@tauri-apps/plugin-os';
export function SettingsLink({
to,
@@ -42,7 +41,6 @@ export function SettingsLink({
export function SettingsSidebar() {
const { state: vrcConfigState } = useVRCConfig();
const currentPlatform = platform();
return (
<div className="flex flex-col px-5 py-5 gap-3 overflow-y-auto bg-background-70 rounded-lg h-full">
@@ -76,12 +74,11 @@ export function SettingsSidebar() {
id="settings-sidebar-gesture_control"
/>
{
currentPlatform == 'windows' || currentPlatform == 'linux' ?
<SettingsLink
to="/settings/keybinds"
scrollTo="keybinds"
id="settings-sidebar-keybinds"
/> : <></>
<SettingsLink
to="/settings/keybinds"
scrollTo="keybinds"
id="settings-sidebar-keybinds"
/>
}
</div>
</div>

View File

@@ -54,6 +54,7 @@ allprojects {
// You can declare any Maven/Ivy/file repository here.
mavenCentral()
maven(url = "https://jitpack.io")
mavenLocal()
}
}
@@ -79,6 +80,8 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
implementation("com.mayakapps.kache:kache:2.1.1")
implementation("dev.hannahpadd:dbusglobalshortcutskotlin:0.1.0")
api("com.github.loucass003:EspflashKotlin:v0.11.0")
// Allow the use of reflection

View File

@@ -2,23 +2,21 @@ package dev.slimevr
import com.melloware.jintellitype.HotkeyListener
import com.melloware.jintellitype.JIntellitype
import dev.hannah.portals.PortalManager
import dev.hannah.portals.globalShortcuts.ShortcutTuple
import dev.slimevr.config.KeybindingsConfig
import dev.slimevr.tracking.trackers.TrackerUtils
import io.eiren.util.OperatingSystem
import io.eiren.util.OperatingSystem.Companion.currentPlatform
import io.eiren.util.ann.AWTThread
import io.eiren.util.logging.LogManager
import org.freedesktop.dbus.types.Variant
class Keybinding @AWTThread constructor(val server: VRServer) : HotkeyListener {
val config: KeybindingsConfig = server.configManager.vrConfig.keybindings
init {
if (currentPlatform != OperatingSystem.WINDOWS) {
LogManager
.info(
"[Keybinding] Currently only supported on Windows. Keybindings will be disabled.",
)
} else {
if (currentPlatform == OperatingSystem.WINDOWS) {
try {
if (JIntellitype.getInstance() != null) {
JIntellitype.getInstance().addHotKeyListener(this)
@@ -55,6 +53,55 @@ class Keybinding @AWTThread constructor(val server: VRServer) : HotkeyListener {
)
}
}
if (currentPlatform == OperatingSystem.LINUX) {
val portalManager = PortalManager(SLIMEVR_IDENTIFIER)
val shortcutsList = mutableListOf(
ShortcutTuple("FULL_RESET", mapOf("description" to Variant("Full Reset"), "trigger_description" to Variant("CTRL+ALT+SHIFT+Y"))),
ShortcutTuple("YAW_RESET", mapOf("description" to Variant("Yaw Reset"), "trigger_description" to Variant("CTRL+ALT+SHIFT+U"))),
ShortcutTuple("MOUNTING_RESET", mapOf("description" to Variant("Mounting Reset"), "trigger_description" to Variant("CTRL+ALT+SHIFT+I"))),
ShortcutTuple("FEET_MOUNTING_RESET", mapOf("description" to Variant("Feet Mounting Reset"), "trigger_description" to Variant("CTRL+ALT+SHIFT+P"))),
ShortcutTuple("PAUSE_TRACKING", mapOf("description" to Variant("Pause Tracking"), "trigger_description" to Variant("CTRL+ALT+SHIFT+O"))))
val globalShortcutsHandler = portalManager.globalShortcutsRequest(shortcutsList)
Runtime.getRuntime().addShutdownHook(Thread {
println("Closing connection")
globalShortcutsHandler.close()
})
globalShortcutsHandler.onShortcutActivated = { shortcutId ->
when (shortcutId) {
"FULL_RESET" -> {
println("Full reset triggered")
server.scheduleResetTrackersFull(RESET_SOURCE_NAME, config.fullResetDelay.toLong())
}
"YAW_RESET" -> {
println("Yaw reset triggered")
server.scheduleResetTrackersYaw(RESET_SOURCE_NAME, config.yawResetDelay.toLong())
}
"MOUNTING_RESET" -> {
println("Mounting reset triggered")
server.scheduleResetTrackersMounting(
RESET_SOURCE_NAME,
config.mountingResetDelay.toLong(),
)
}
"FEET_MOUNTING_RESET" -> {
println("Feet mounting reset triggered")
server.scheduleResetTrackersMounting(
RESET_SOURCE_NAME,
config.feetMountingResetDelay.toLong(),
TrackerUtils.feetsBodyParts,
)
}
"PAUSE_TRACKING" -> {
println("Pause tracking triggered")
server.scheduleTogglePauseTracking(
RESET_SOURCE_NAME,
config.pauseTrackingDelay.toLong(),
)
}
}
}
}
}
@AWTThread

View File

@@ -1,31 +0,0 @@
package dev.slimevr.keybinding
import org.freedesktop.dbus.DBusPath
import org.freedesktop.dbus.annotations.DBusInterfaceName
import org.freedesktop.dbus.interfaces.DBusInterface
import org.freedesktop.dbus.messages.DBusSignal
import org.freedesktop.dbus.types.Variant
@DBusInterfaceName("org.freedesktop.portal.GlobalKeybinds")
interface GlobalKeybinds : DBusInterface {
// Creates a session for the shortcuts
fun CreateSession(options: Map<String, Variant<*>>): DBusPath
fun BindShortcuts(
sessionHandle: DBusPath,
shortcuts: Array<Shortcut>,
parentWindow: String,
options: Map<String, Variant<*>>
): DBusPath
class Activated(
path: String,
val sessionHandle: DBusPath,
val shortcutId: String,
val timestamp: Long,
val options: Map<String, Variant<*>>
) : DBusSignal(path, sessionHandle, shortcutId, timestamp, options)
}
data class Shortcut(val id: String, val properties: Map<String, Variant<*>>)

View File

@@ -1,148 +0,0 @@
package dev.slimevr.keybinding
import com.melloware.jintellitype.HotkeyListener
import com.melloware.jintellitype.JIntellitype
import dev.slimevr.VRServer
import dev.slimevr.config.KeybindingsConfig
import dev.slimevr.tracking.trackers.TrackerUtils
import io.eiren.util.OperatingSystem
import io.eiren.util.ann.AWTThread
import io.eiren.util.logging.LogManager
class Keybinding @AWTThread constructor(val server: VRServer) : HotkeyListener {
val config: KeybindingsConfig = server.configManager.vrConfig.keybindings
init {
if (OperatingSystem.Companion.currentPlatform != OperatingSystem.WINDOWS) {
LogManager
.info(
"[Keybinding] Currently only supported on Windows. Keybindings will be disabled.",
)
/*
try {
val connection = DBusConnectionBuilder.forSessionBus().build()
// 1. Get the Portal object
val portal = connection.getRemoteObject(
"org.freedesktop.portal.Desktop",
"/org/freedesktop/portal/desktop",
GlobalKeybinds::class.java
)
// 2. Setup the Signal Listener
connection.addSigHandler(GlobalKeybinds.Activated::class.java) { signal ->
when (signal.shortcutId) {
"my_cool_action" -> println("🚀 Hotkey Triggered!")
}
}
// 3. Create Session & Bind (Simplified)
// Note: In a real app, you'd handle the ObjectPath callbacks
// for CreateSession before calling BindShortcuts.
val shortcuts = arrayOf(
Shortcut(
"my_cool_action",
mapOf("description" to Variant("Open My App"))
)
)
// This triggers the OS permissions popup
portal.BindShortcuts(, shortcuts, "", emptyMap())
} catch (e: Error) {
println("Dbus error: ${e}")
}
*/
} else {
try {
if (JIntellitype.getInstance() != null) {
JIntellitype.getInstance().addHotKeyListener(this)
val fullResetBinding = config.fullResetBinding
JIntellitype.getInstance()
.registerHotKey(FULL_RESET, fullResetBinding)
LogManager.info("[Keybinding] Bound full reset to $fullResetBinding")
val yawResetBinding = config.yawResetBinding
JIntellitype.getInstance()
.registerHotKey(YAW_RESET, yawResetBinding)
LogManager.info("[Keybinding] Bound yaw reset to $yawResetBinding")
val mountingResetBinding = config.mountingResetBinding
JIntellitype.getInstance()
.registerHotKey(MOUNTING_RESET, mountingResetBinding)
LogManager.info("[Keybinding] Bound reset mounting to $mountingResetBinding")
val feetMountingResetBinding = config.feetMountingResetBinding
JIntellitype.getInstance()
.registerHotKey(FEET_MOUNTING_RESET, feetMountingResetBinding)
LogManager.info("[Keybinding] Bound feet reset mounting to $feetMountingResetBinding")
val pauseTrackingBinding = config.pauseTrackingBinding
JIntellitype.getInstance()
.registerHotKey(PAUSE_TRACKING, pauseTrackingBinding)
LogManager.info("[Keybinding] Bound pause tracking to $pauseTrackingBinding")
}
} catch (e: Throwable) {
LogManager
.warning(
"[Keybinding] JIntellitype initialization failed. Keybindings will be disabled. Try restarting your computer.",
)
}
}
}
@AWTThread
override fun onHotKey(identifier: Int) {
when (identifier) {
FULL_RESET -> server.scheduleResetTrackersFull(
RESET_SOURCE_NAME,
(config.fullResetDelay * 1000).toLong()
)
YAW_RESET -> server.scheduleResetTrackersYaw(
RESET_SOURCE_NAME,
(config.yawResetDelay * 1000).toLong()
)
MOUNTING_RESET -> server.scheduleResetTrackersMounting(
RESET_SOURCE_NAME,
(config.mountingResetDelay * 1000).toLong()
)
FEET_MOUNTING_RESET -> server.scheduleResetTrackersMounting(
RESET_SOURCE_NAME,
(config.feetMountingResetDelay * 1000).toLong(),
TrackerUtils.feetsBodyParts,
)
PAUSE_TRACKING ->
server
.scheduleTogglePauseTracking(
RESET_SOURCE_NAME,
(config.pauseTrackingDelay * 1000).toLong(),
)
}
}
enum class KeybindName {
FULL_RESET,
YAW_RESET,
MOUNTING_RESET,
PAUSE_TRACKING
}
companion object {
private const val RESET_SOURCE_NAME = "Keybinding"
private const val FULL_RESET = 1
private const val YAW_RESET = 2
private const val MOUNTING_RESET = 3
private const val FEET_MOUNTING_RESET = 4
private const val PAUSE_TRACKING = 5
}
}

View File

@@ -1,7 +1,6 @@
package dev.slimevr.protocol.rpc.keybinds
import com.google.flatbuffers.FlatBufferBuilder
import dev.slimevr.keybinding.Keybinding
import dev.slimevr.keybind.KeybindListener
import dev.slimevr.protocol.GenericConnection
import dev.slimevr.protocol.rpc.RPCHandler

View File

@@ -51,6 +51,7 @@ allprojects {
mavenCentral()
maven(url = "https://jitpack.io")
maven(url = "https://oss.sonatype.org/content/repositories/snapshots")
mavenLocal()
}
}

View File

@@ -2,7 +2,7 @@
package dev.slimevr.desktop
import dev.slimevr.keybinding.Keybinding
import dev.slimevr.Keybinding
import dev.slimevr.SLIMEVR_IDENTIFIER
import dev.slimevr.VRServer
import dev.slimevr.bridge.Bridge

View File

@@ -14,6 +14,7 @@ pluginManagement {
gradlePluginPortal()
google()
mavenCentral()
mavenLocal()
}
val kotlinVersion: String by settings