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>
This commit is contained in:
steve hocktail 2026-05-12 09:53:14 -04:00 committed by GitHub
parent 2309f89701
commit 557a0775c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 72 additions and 22 deletions

View File

@ -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")]

View File

@ -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<dyn HidProvider> {
pub(super) fn initialize() -> Result<UInputProvider, Toast> {
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 {

View File

@ -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<Toast>) {
let hid_result = hid::initialize();
let hid_provider: Box<dyn HidProvider>;
let error: Option<Toast>;
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(