authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-01 14:09:53-07:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2026-10-01 16:23:40-07:00
log418e0a5f2fa864f4515f34398791511859b32201
tree9e4f27a0ee2a309b2866a588504f14990b26323b
parentcd1f5d42a7bb47e3adc3b9f15bdb47d635ccdb68
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU

fix: without libxkbcommon, Snowbound says what to install instead of crashing

winit unwraps its keyboard context, so a missing libxkbcommon (or, on X11, libxkbcommon-x11) panicked at the first key or at start. Snowbound checks first and stops with the package to install. Assisted-by: claude-opus-5.5

1 files changed, 20 insertions(+), 6 deletions(-)

crates/snowbound/src/linux.rs+20-6
......@@ -4,6 +4,7 @@
44
55use canvas::date::DateField;
66use std::{
7 error::Error,
78 ffi::CStr,
89 path::PathBuf,
910 process::Command,
......@@ -15,7 +16,6 @@ use std::{
1516};
1617use ui::{Flags, Spec, Ui, px};
1718use winit::{
18 error::EventLoopError,
1919 event_loop::{EventLoop, EventLoopProxy},
2020 platform::{
2121 wayland::{EventLoopExtWayland, WindowAttributesExtWayland},
......@@ -41,17 +41,31 @@ static QUIT: OnceLock<EventLoopProxy<crate::UserEvent>> = OnceLock::new();
4141/// KWin leaves windows square.
4242static BREEZE_RADIUS: AtomicU32 = AtomicU32::new(0);
4343
44pub fn event_loop(headless: bool) -> Result<EventLoop<crate::UserEvent>, EventLoopError> {
44pub fn event_loop(headless: bool) -> Result<EventLoop<crate::UserEvent>, Box<dyn Error>> {
4545 // Month and day names and date orders follow the user's locale; other categories stay C.
4646 unsafe { libc::setlocale(libc::LC_TIME, c"".as_ptr()) };
4747 let mut builder = EventLoop::with_user_event();
4848 // winit takes Wayland wherever a compositor is named, even without libwayland-client.
4949 let named = |names: &[&str]| names.iter().any(|name| std::env::var_os(name).is_some());
50 if named(&["WAYLAND_DISPLAY", "WAYLAND_SOCKET"])
51 && named(&["DISPLAY"])
52 && !crate::loader::loads(c"libwayland-client.so.0")
53 {
50 let mut x11 = !named(&["WAYLAND_DISPLAY", "WAYLAND_SOCKET"]);
51 if !x11 && named(&["DISPLAY"]) && !crate::loader::loads(c"libwayland-client.so.0") {
5452 builder.with_x11();
53 x11 = true;
54 }
55 // winit panics on the keyboard without them.
56 let keyboard: &[(&CStr, &str)] = if x11 {
57 &[
58 (c"libxkbcommon.so.0", "libxkbcommon"),
59 (c"libxkbcommon-x11.so.0", "libxkbcommon-x11"),
60 ]
61 } else {
62 &[(c"libxkbcommon.so.0", "libxkbcommon")]
63 };
64 if let Some((_, package)) = keyboard
65 .iter()
66 .find(|(library, _)| !crate::loader::loads(library))
67 {
68 return Err(format!("Install {package} to start Snowbound.").into());
5569 }
5670 let event_loop = builder.build()?;
5771 if !headless {