From 557a0775c3daf501ded176ad99e5536dd965b78e Mon Sep 17 00:00:00 2001 From: steve hocktail Date: Tue, 12 May 2026 09:53:14 -0400 Subject: [PATCH] add a toast for uinput errors (#494) * add a toast for uinput errors this makes it more obvious if missing uinput permissions are the reason your keyboard and mouse aren't working. * uinput toaster part 2: the cargo formattening * increase uinput error toast's timeout to 30 seconds --------- Co-authored-by: galister <22305755+galister@users.noreply.github.com> --- wayvr/src/state.rs | 12 ++++++-- wayvr/src/subsystem/hid/mod.rs | 50 +++++++++++++++++++++++++--------- wayvr/src/subsystem/input.rs | 32 ++++++++++++++++++---- 3 files changed, 72 insertions(+), 22 deletions(-) diff --git a/wayvr/src/state.rs b/wayvr/src/state.rs index a2546111..478fb261 100644 --- a/wayvr/src/state.rs +++ b/wayvr/src/state.rs @@ -90,7 +90,7 @@ impl AppState { .log_err("Could not initialize WayVR Server") .ok(); - let mut hid_provider = HidWrapper::new(); + let (hid_provider, mut hid_error) = HidWrapper::new(); #[cfg(feature = "osc")] let osc_sender = crate::subsystem::osc::OscSender::new(session.config.osc_out_port).ok(); @@ -154,8 +154,9 @@ impl AppState { desktop_finder.refresh(); let lang_provider = WayVRLangProvider::from_config(&session.config); + let executor = Rc::new(smol::LocalExecutor::new()); - Ok(Self { + let mut app_state = Self { session, tasks, gfx, @@ -188,7 +189,12 @@ impl AppState { #[cfg(feature = "openxr")] monado_state: None, - }) + }; + + if let Some(error_toast) = hid_error { + error_toast.submit(&mut app_state); + } + Ok(app_state) } #[cfg(feature = "openxr")] diff --git a/wayvr/src/subsystem/hid/mod.rs b/wayvr/src/subsystem/hid/mod.rs index ed446d12..b3d5e611 100644 --- a/wayvr/src/subsystem/hid/mod.rs +++ b/wayvr/src/subsystem/hid/mod.rs @@ -11,8 +11,11 @@ use std::mem::transmute; use std::sync::LazyLock; use std::{fs::File, sync::atomic::AtomicBool}; use strum::{EnumIter, EnumString, IntoEnumIterator}; +use wlx_common::overlays::ToastTopic; use xkbcommon::xkb; +use crate::overlays::toast::Toast; + #[cfg(feature = "wayland")] pub mod wayland; @@ -21,27 +24,48 @@ mod x11; pub static USE_UINPUT: AtomicBool = AtomicBool::new(true); -pub(super) fn initialize() -> Box { +pub(super) fn initialize() -> Result { if !USE_UINPUT.load(std::sync::atomic::Ordering::Relaxed) { - log::info!("Uinput disabled by user."); - return Box::new(DummyProvider {}); + const UINPUT_DISABLED: &str = "Uinput disabled by user."; + log::info!("{UINPUT_DISABLED}"); + return Err(Toast::new( + ToastTopic::System, + String::with_capacity(0), + String::from(UINPUT_DISABLED), + ) + .with_timeout(5.0)); } if let Some(uinput) = UInputProvider::try_new() { log::info!("Initialized uinput."); - return Box::new(uinput); + return Ok(uinput); } - log::error!("Could not create uinput provider. Keyboard/Mouse input will not work!"); - log::error!("Check if the uinput kernel module is loaded: lsmod | grep uinput"); - log::error!( - " - If not loaded, follow your distro's instructions to load the uinput kernel module." - ); - log::error!("Check if you're in input group, run: id -nG"); + const CHECK_UINPUT_MESSAGE: &str = + "Could not create uinput provider. Keyboard/Mouse input will not work! + +Check if the uinput kernel module is loaded: lsmod | grep uinput + - If not loaded, follow your distro's instructions to load the uinput kernel module. + +Check if you're in input group, run: id -nG"; + let mut full_uinput_error = String::from(CHECK_UINPUT_MESSAGE); if let Ok(user) = std::env::var("USER") { - log::error!(" - To add yourself to the input group, run: sudo usermod -aG input {user}"); - log::error!(" - After adding yourself to the input group, you will need to reboot."); + let check_group_message = format!( + " - To add yourself to the input group, run: sudo usermod -aG input {user} + - After adding yourself to the input group, you will need to reboot." + ); + full_uinput_error.push_str(&check_group_message); } - Box::new(DummyProvider {}) + for error_line in full_uinput_error.lines() { + if !error_line.is_empty() { + log::error!("{error_line}"); + } + } + Err(Toast::new( + ToastTopic::Error, + String::with_capacity(0), + full_uinput_error, + ) + .with_timeout(30.0)) } pub struct WheelDelta { diff --git a/wayvr/src/subsystem/input.rs b/wayvr/src/subsystem/input.rs index c51f4876..51716405 100644 --- a/wayvr/src/subsystem/input.rs +++ b/wayvr/src/subsystem/input.rs @@ -1,6 +1,10 @@ use super::hid::{self, HidProvider, VirtualKey}; -use crate::{backend::wayvr::WvrServerState, subsystem::hid::XkbKeymap}; +use crate::{ + backend::wayvr::WvrServerState, + overlays::toast::Toast, + subsystem::hid::{DummyProvider, XkbKeymap}, +}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum KeyboardFocus { @@ -15,12 +19,28 @@ pub struct HidWrapper { } impl HidWrapper { - pub fn new() -> Self { - Self { - keyboard_focus: KeyboardFocus::PhysicalScreen, - inner: hid::initialize(), - keymap: None, + pub fn new() -> (Self, Option) { + let hid_result = hid::initialize(); + let hid_provider: Box; + let error: Option; + match hid_result { + Ok(uinput) => { + hid_provider = Box::new(uinput); + error = None; + } + Err(toast) => { + hid_provider = Box::new(DummyProvider {}); + error = Some(toast); + } } + ( + Self { + keyboard_focus: KeyboardFocus::PhysicalScreen, + inner: hid_provider, + keymap: None, + }, + error, + ) } pub fn send_key_routed(