SIGUSR1 to trigger fcitx layout change

This commit is contained in:
galister
2025-12-15 16:13:45 +09:00
parent 12e5749bda
commit 1077d2606d
9 changed files with 656 additions and 121 deletions

View File

@@ -50,6 +50,7 @@ pub struct GuiPanel<S> {
pub max_size: Vec2,
pub gui_scale: f32,
pub on_notify: Option<OnNotifyFunc<S>>,
pub initialized: bool,
interaction_transform: Option<Affine2>,
context: WguiContext,
timestep: Timestep,
@@ -165,6 +166,7 @@ impl<S: 'static> GuiPanel<S> {
interaction_transform: None,
on_notify: None,
gui_scale: params.gui_scale,
initialized: false,
})
}
@@ -194,6 +196,7 @@ impl<S: 'static> GuiPanel<S> {
on_notify: None,
interaction_transform: None,
gui_scale: params.gui_scale,
initialized: false,
})
}
@@ -230,6 +233,7 @@ impl<S: 'static> OverlayBackend for GuiPanel<S> {
self.layout.content_size.x as _,
self.layout.content_size.y as _,
]));
self.initialized = true;
}
Ok(())
}

View File

@@ -36,7 +36,10 @@ use std::{
sync::atomic::{AtomicBool, AtomicUsize, Ordering},
};
use anyhow::Context;
use clap::Parser;
use libc::{SIGINT, SIGTERM, SIGUSR1};
use signal_hook::iterator::Signals;
use sysinfo::Pid;
use tracing::level_filters::LevelFilter;
use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt};
@@ -45,6 +48,7 @@ use crate::subsystem::dbus::DbusConnector;
pub static FRAME_COUNTER: AtomicUsize = AtomicUsize::new(0);
pub static RUNNING: AtomicBool = AtomicBool::new(true);
pub static KEYMAP_CHANGE: AtomicBool = AtomicBool::new(false);
/// The lightweight desktop overlay for OpenVR and OpenXR
#[derive(Default, Parser, Debug)]
@@ -114,11 +118,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
return Ok(());
}
let _ = ctrlc::set_handler({
|| {
RUNNING.store(false, Ordering::Relaxed);
}
});
setup_signal_hooks()?;
auto_run(args);
@@ -200,6 +200,27 @@ const fn args_get_openxr(args: &Args) -> bool {
ret
}
fn setup_signal_hooks() -> anyhow::Result<()> {
let mut signals = Signals::new([SIGINT, SIGTERM, SIGUSR1])?;
std::thread::spawn(move || {
for signal in signals.forever() {
match signal {
SIGUSR1 => {
log::info!("SIGUSR1 received (keymap changed)");
KEYMAP_CHANGE.store(true, Ordering::Relaxed);
continue;
}
_ => {
RUNNING.store(false, Ordering::Relaxed);
break;
}
}
}
});
Ok(())
}
fn logging_init(args: &mut Args) {
let log_file_path = args
.log_to

View File

@@ -2,22 +2,24 @@ use std::{
cell::Cell,
collections::HashMap,
process::{Child, Command},
sync::atomic::Ordering,
};
use crate::{
KEYMAP_CHANGE,
backend::input::{HoverResult, PointerHit},
gui::panel::GuiPanel,
overlays::keyboard::{builder::create_keyboard_panel, layout::AltModifier},
state::AppState,
subsystem::hid::{
ALT, CTRL, KeyModifier, META, SHIFT, SUPER, VirtualKey, WheelDelta, XkbKeymap,
wayland::WlKeymapMonitor,
},
windowing::{
backend::{FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender},
window::OverlayWindowConfig,
},
};
use anyhow::Context;
use glam::{Affine3A, Quat, Vec3, vec3};
use slotmap::{SlotMap, new_key_type};
use wgui::{
@@ -66,7 +68,6 @@ pub fn create_keyboard(
active_keymap: KeyboardPanelKey::default(),
default_state,
layout,
wkm: WlKeymapMonitor::new()?,
};
backend.active_keymap = backend.add_new_keymap(keymap.as_ref(), app)?;
@@ -99,7 +100,6 @@ struct KeyboardBackend {
active_keymap: KeyboardPanelKey,
default_state: KeyboardState,
layout: layout::Layout,
wkm: WlKeymapMonitor,
}
impl KeyboardBackend {
@@ -167,10 +167,28 @@ impl OverlayBackend for KeyboardBackend {
self.panel().init(app)
}
fn should_render(&mut self, app: &mut AppState) -> anyhow::Result<ShouldRender> {
if let Some(keymap) = self.wkm.check() {
while KEYMAP_CHANGE.swap(false, Ordering::Relaxed) {
let keymap: XkbKeymap;
if let Ok(fcitx_layout) = app
.dbus
.fcitx_keymap()
.context("Could not fetch Fcitx5 keymap")
.inspect_err(|e| log::warn!("{e:?}"))
&& fcitx_layout.starts_with("keyboard-")
&& let Some(fcitx_keymap) = XkbKeymap::from_layout_str(&fcitx_layout[9..])
{
keymap = fcitx_keymap;
} else {
break;
}
app.hid_provider.keymap_changed(&keymap);
if self.switch_keymap(&keymap, app)? {
return Ok(match self.panel().should_render(app)? {
let panel = self.panel();
if !panel.initialized {
panel.init(app)?;
}
return Ok(match panel.should_render(app)? {
ShouldRender::Should | ShouldRender::Can => ShouldRender::Should,
ShouldRender::Unable => ShouldRender::Unable,
});

View File

@@ -0,0 +1,548 @@
// This code was autogenerated with `dbus-codegen-rust -g -m None -d org.fcitx.Fcitx5 -p /controller`, see https://github.com/diwic/dbus-rs
use dbus;
#[allow(unused_imports)]
use dbus::arg;
use dbus::blocking;
pub trait OrgFreedesktopDBusIntrospectable {
fn introspect(&self) -> Result<String, dbus::Error>;
}
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>>
OrgFreedesktopDBusIntrospectable for blocking::Proxy<'a, C>
{
fn introspect(&self) -> Result<String, dbus::Error> {
self.method_call("org.freedesktop.DBus.Introspectable", "Introspect", ())
.and_then(|r: (String,)| Ok(r.0))
}
}
pub trait OrgFcitxFcitxController1 {
fn activate(&self) -> Result<(), dbus::Error>;
fn add_input_method_group(&self, arg0: &str) -> Result<(), dbus::Error>;
fn addon_for_im(&self, arg0: &str) -> Result<String, dbus::Error>;
fn available_input_methods(
&self,
) -> Result<Vec<(String, String, String, String, String, String, bool)>, dbus::Error>;
fn available_keyboard_layouts(
&self,
) -> Result<
Vec<(
String,
String,
Vec<String>,
Vec<(String, String, Vec<String>)>,
)>,
dbus::Error,
>;
fn can_restart(&self) -> Result<bool, dbus::Error>;
fn check_update(&self) -> Result<bool, dbus::Error>;
fn configure(&self) -> Result<(), dbus::Error>;
fn configure_addon(&self, arg0: &str) -> Result<(), dbus::Error>;
fn configure_im(&self, arg0: &str) -> Result<(), dbus::Error>;
fn current_input_method(&self) -> Result<String, dbus::Error>;
fn current_input_method_group(&self) -> Result<String, dbus::Error>;
fn current_input_method_info(
&self,
) -> Result<
(
String,
String,
String,
String,
String,
String,
String,
bool,
String,
arg::PropMap,
),
dbus::Error,
>;
fn current_ui(&self) -> Result<String, dbus::Error>;
fn deactivate(&self) -> Result<(), dbus::Error>;
fn debug_info(&self) -> Result<String, dbus::Error>;
fn exit(&self) -> Result<(), dbus::Error>;
fn full_input_method_group_info(
&self,
arg0: &str,
) -> Result<
(
String,
String,
String,
arg::PropMap,
Vec<(
String,
String,
String,
String,
String,
String,
String,
bool,
String,
arg::PropMap,
)>,
),
dbus::Error,
>;
fn get_addons(&self) -> Result<Vec<(String, String, String, i32, bool, bool)>, dbus::Error>;
fn get_addons_v2(
&self,
) -> Result<
Vec<(
String,
String,
String,
i32,
bool,
bool,
bool,
Vec<String>,
Vec<String>,
)>,
dbus::Error,
>;
fn get_config<R0: for<'b> arg::Get<'b> + 'static>(
&self,
arg0: &str,
) -> Result<
(
R0,
Vec<(
String,
Vec<(
String,
String,
String,
arg::Variant<Box<dyn arg::RefArg + 'static>>,
arg::PropMap,
)>,
)>,
),
dbus::Error,
>;
fn input_method_group_info(
&self,
arg0: &str,
) -> Result<(String, Vec<(String, String)>), dbus::Error>;
fn input_method_groups(&self) -> Result<Vec<String>, dbus::Error>;
fn open_wayland_connection(&self, arg0: &str) -> Result<(), dbus::Error>;
fn open_wayland_connection_socket(&self, arg0: arg::OwnedFd) -> Result<(), dbus::Error>;
fn open_x11_connection(&self, arg0: &str) -> Result<(), dbus::Error>;
fn refresh(&self) -> Result<(), dbus::Error>;
fn reload_addon_config(&self, arg0: &str) -> Result<(), dbus::Error>;
fn reload_config(&self) -> Result<(), dbus::Error>;
fn remove_input_method_group(&self, arg0: &str) -> Result<(), dbus::Error>;
fn reopen_wayland_connection_socket(
&self,
arg0: &str,
arg1: arg::OwnedFd,
) -> Result<(), dbus::Error>;
fn reset_imlist(&self) -> Result<(), dbus::Error>;
fn restart(&self) -> Result<(), dbus::Error>;
fn save(&self) -> Result<(), dbus::Error>;
fn set_addons_state(&self, arg0: Vec<(&str, bool)>) -> Result<(), dbus::Error>;
fn set_config<I1: arg::Arg + arg::Append>(
&self,
arg0: &str,
arg1: I1,
) -> Result<(), dbus::Error>;
fn set_current_im(&self, arg0: &str) -> Result<(), dbus::Error>;
fn set_input_method_group_info(
&self,
arg0: &str,
arg1: &str,
arg2: Vec<(&str, &str)>,
) -> Result<(), dbus::Error>;
fn set_log_rule(&self, arg0: &str) -> Result<(), dbus::Error>;
fn state(&self) -> Result<i32, dbus::Error>;
fn switch_input_method_group(&self, arg0: &str) -> Result<(), dbus::Error>;
fn toggle(&self) -> Result<(), dbus::Error>;
}
#[derive(Debug)]
pub struct OrgFcitxFcitxController1InputMethodGroupsChanged {}
impl arg::AppendAll for OrgFcitxFcitxController1InputMethodGroupsChanged {
fn append(&self, _: &mut arg::IterAppend) {}
}
impl arg::ReadAll for OrgFcitxFcitxController1InputMethodGroupsChanged {
fn read(_: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> {
Ok(OrgFcitxFcitxController1InputMethodGroupsChanged {})
}
}
impl dbus::message::SignalArgs for OrgFcitxFcitxController1InputMethodGroupsChanged {
const NAME: &'static str = "InputMethodGroupsChanged";
const INTERFACE: &'static str = "org.fcitx.Fcitx.Controller1";
}
impl<'a, T: blocking::BlockingSender, C: ::std::ops::Deref<Target = T>> OrgFcitxFcitxController1
for blocking::Proxy<'a, C>
{
fn activate(&self) -> Result<(), dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "Activate", ())
}
fn add_input_method_group(&self, arg0: &str) -> Result<(), dbus::Error> {
self.method_call(
"org.fcitx.Fcitx.Controller1",
"AddInputMethodGroup",
(arg0,),
)
}
fn addon_for_im(&self, arg0: &str) -> Result<String, dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "AddonForIM", (arg0,))
.and_then(|r: (String,)| Ok(r.0))
}
fn available_input_methods(
&self,
) -> Result<Vec<(String, String, String, String, String, String, bool)>, dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "AvailableInputMethods", ())
.and_then(|r: (Vec<(String, String, String, String, String, String, bool)>,)| Ok(r.0))
}
fn available_keyboard_layouts(
&self,
) -> Result<
Vec<(
String,
String,
Vec<String>,
Vec<(String, String, Vec<String>)>,
)>,
dbus::Error,
> {
self.method_call(
"org.fcitx.Fcitx.Controller1",
"AvailableKeyboardLayouts",
(),
)
.and_then(
|r: (
Vec<(
String,
String,
Vec<String>,
Vec<(String, String, Vec<String>)>,
)>,
)| Ok(r.0),
)
}
fn can_restart(&self) -> Result<bool, dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "CanRestart", ())
.and_then(|r: (bool,)| Ok(r.0))
}
fn check_update(&self) -> Result<bool, dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "CheckUpdate", ())
.and_then(|r: (bool,)| Ok(r.0))
}
fn configure(&self) -> Result<(), dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "Configure", ())
}
fn configure_addon(&self, arg0: &str) -> Result<(), dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "ConfigureAddon", (arg0,))
}
fn configure_im(&self, arg0: &str) -> Result<(), dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "ConfigureIM", (arg0,))
}
fn current_input_method(&self) -> Result<String, dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "CurrentInputMethod", ())
.and_then(|r: (String,)| Ok(r.0))
}
fn current_input_method_group(&self) -> Result<String, dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "CurrentInputMethodGroup", ())
.and_then(|r: (String,)| Ok(r.0))
}
fn current_input_method_info(
&self,
) -> Result<
(
String,
String,
String,
String,
String,
String,
String,
bool,
String,
arg::PropMap,
),
dbus::Error,
> {
self.method_call("org.fcitx.Fcitx.Controller1", "CurrentInputMethodInfo", ())
}
fn current_ui(&self) -> Result<String, dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "CurrentUI", ())
.and_then(|r: (String,)| Ok(r.0))
}
fn deactivate(&self) -> Result<(), dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "Deactivate", ())
}
fn debug_info(&self) -> Result<String, dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "DebugInfo", ())
.and_then(|r: (String,)| Ok(r.0))
}
fn exit(&self) -> Result<(), dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "Exit", ())
}
fn full_input_method_group_info(
&self,
arg0: &str,
) -> Result<
(
String,
String,
String,
arg::PropMap,
Vec<(
String,
String,
String,
String,
String,
String,
String,
bool,
String,
arg::PropMap,
)>,
),
dbus::Error,
> {
self.method_call(
"org.fcitx.Fcitx.Controller1",
"FullInputMethodGroupInfo",
(arg0,),
)
}
fn get_addons(&self) -> Result<Vec<(String, String, String, i32, bool, bool)>, dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "GetAddons", ())
.and_then(|r: (Vec<(String, String, String, i32, bool, bool)>,)| Ok(r.0))
}
fn get_addons_v2(
&self,
) -> Result<
Vec<(
String,
String,
String,
i32,
bool,
bool,
bool,
Vec<String>,
Vec<String>,
)>,
dbus::Error,
> {
self.method_call("org.fcitx.Fcitx.Controller1", "GetAddonsV2", ())
.and_then(
|r: (
Vec<(
String,
String,
String,
i32,
bool,
bool,
bool,
Vec<String>,
Vec<String>,
)>,
)| Ok(r.0),
)
}
fn get_config<R0: for<'b> arg::Get<'b> + 'static>(
&self,
arg0: &str,
) -> Result<
(
R0,
Vec<(
String,
Vec<(
String,
String,
String,
arg::Variant<Box<dyn arg::RefArg + 'static>>,
arg::PropMap,
)>,
)>,
),
dbus::Error,
> {
self.method_call("org.fcitx.Fcitx.Controller1", "GetConfig", (arg0,))
.and_then(
|r: (
arg::Variant<R0>,
Vec<(
String,
Vec<(
String,
String,
String,
arg::Variant<Box<dyn arg::RefArg + 'static>>,
arg::PropMap,
)>,
)>,
)| Ok(((r.0).0, r.1)),
)
}
fn input_method_group_info(
&self,
arg0: &str,
) -> Result<(String, Vec<(String, String)>), dbus::Error> {
self.method_call(
"org.fcitx.Fcitx.Controller1",
"InputMethodGroupInfo",
(arg0,),
)
}
fn input_method_groups(&self) -> Result<Vec<String>, dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "InputMethodGroups", ())
.and_then(|r: (Vec<String>,)| Ok(r.0))
}
fn open_wayland_connection(&self, arg0: &str) -> Result<(), dbus::Error> {
self.method_call(
"org.fcitx.Fcitx.Controller1",
"OpenWaylandConnection",
(arg0,),
)
}
fn open_wayland_connection_socket(&self, arg0: arg::OwnedFd) -> Result<(), dbus::Error> {
self.method_call(
"org.fcitx.Fcitx.Controller1",
"OpenWaylandConnectionSocket",
(arg0,),
)
}
fn open_x11_connection(&self, arg0: &str) -> Result<(), dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "OpenX11Connection", (arg0,))
}
fn refresh(&self) -> Result<(), dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "Refresh", ())
}
fn reload_addon_config(&self, arg0: &str) -> Result<(), dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "ReloadAddonConfig", (arg0,))
}
fn reload_config(&self) -> Result<(), dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "ReloadConfig", ())
}
fn remove_input_method_group(&self, arg0: &str) -> Result<(), dbus::Error> {
self.method_call(
"org.fcitx.Fcitx.Controller1",
"RemoveInputMethodGroup",
(arg0,),
)
}
fn reopen_wayland_connection_socket(
&self,
arg0: &str,
arg1: arg::OwnedFd,
) -> Result<(), dbus::Error> {
self.method_call(
"org.fcitx.Fcitx.Controller1",
"ReopenWaylandConnectionSocket",
(arg0, arg1),
)
}
fn reset_imlist(&self) -> Result<(), dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "ResetIMList", ())
}
fn restart(&self) -> Result<(), dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "Restart", ())
}
fn save(&self) -> Result<(), dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "Save", ())
}
fn set_addons_state(&self, arg0: Vec<(&str, bool)>) -> Result<(), dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "SetAddonsState", (arg0,))
}
fn set_config<I1: arg::Arg + arg::Append>(
&self,
arg0: &str,
arg1: I1,
) -> Result<(), dbus::Error> {
self.method_call(
"org.fcitx.Fcitx.Controller1",
"SetConfig",
(arg0, arg::Variant(arg1)),
)
}
fn set_current_im(&self, arg0: &str) -> Result<(), dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "SetCurrentIM", (arg0,))
}
fn set_input_method_group_info(
&self,
arg0: &str,
arg1: &str,
arg2: Vec<(&str, &str)>,
) -> Result<(), dbus::Error> {
self.method_call(
"org.fcitx.Fcitx.Controller1",
"SetInputMethodGroupInfo",
(arg0, arg1, arg2),
)
}
fn set_log_rule(&self, arg0: &str) -> Result<(), dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "SetLogRule", (arg0,))
}
fn state(&self) -> Result<i32, dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "State", ())
.and_then(|r: (i32,)| Ok(r.0))
}
fn switch_input_method_group(&self, arg0: &str) -> Result<(), dbus::Error> {
self.method_call(
"org.fcitx.Fcitx.Controller1",
"SwitchInputMethodGroup",
(arg0,),
)
}
fn toggle(&self) -> Result<(), dbus::Error> {
self.method_call("org.fcitx.Fcitx.Controller1", "Toggle", ())
}
}

View File

@@ -9,8 +9,11 @@ use dbus::{
message::MatchRule,
};
use crate::subsystem::dbus::notifications::OrgFreedesktopNotifications;
use crate::subsystem::dbus::{
fcitx5::OrgFcitxFcitxController1, notifications::OrgFreedesktopNotifications,
};
mod fcitx5;
mod notifications;
pub type DbusReceiveCallback = Box<dyn FnMut(Message, &Connection) -> bool + Send>;
@@ -74,6 +77,20 @@ impl DbusConnector {
Ok(())
}
pub fn fcitx_keymap(&mut self) -> anyhow::Result<String> {
let connection = Connection::new_session()?;
let proxy = connection.with_proxy(
"org.fcitx.Fcitx5",
"/controller",
Duration::from_millis(500),
);
let result = proxy
.current_input_method()
.context("Could not get D-Bus response");
result
}
pub fn notify_send(
&mut self,
summary: &str,
@@ -83,16 +100,12 @@ impl DbusConnector {
replaces_id: u32,
transient: bool,
) -> anyhow::Result<u32> {
let connection = self
.connection
.take()
.context("Not connected")
.or_else(|_| Connection::new_session())?;
let connection = Connection::new_session()?;
let proxy = connection.with_proxy(
"org.freedesktop.Notifications",
"/org/freedesktop/Notifications",
Duration::from_millis(1000),
Duration::from_millis(500),
);
let mut hints = PropMap::new();
@@ -109,25 +122,19 @@ impl DbusConnector {
hints,
timeout,
)?;
self.connection = Some(connection);
Ok(retval)
}
pub fn notify_close(&mut self, id: u32) -> anyhow::Result<()> {
let connection = self
.connection
.take()
.context("Not connected")
.or_else(|_| Connection::new_session())?;
let connection = Connection::new_session()?;
let proxy = connection.with_proxy(
"org.freedesktop.Notifications",
"/org/freedesktop/Notifications",
Duration::from_millis(1000),
Duration::from_millis(500),
);
proxy.close_notification(id)?;
self.connection = Some(connection);
Ok(())
}
}

View File

@@ -578,6 +578,12 @@ pub struct XkbKeymap {
}
impl XkbKeymap {
pub fn from_layout_str(layout: &str) -> Option<Self> {
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
xkb::Keymap::new_from_names(&context, "", "", layout, "", None, xkb::COMPILE_NO_FLAGS)
.map(|inner| XkbKeymap { inner })
}
pub fn label_for_key(&self, key: VirtualKey, modifier: KeyModifier) -> String {
let mut state = xkb::State::new(&self.inner);
if modifier > 0

View File

@@ -1,5 +1,4 @@
use anyhow::Context;
use wayland_client::{EventQueue, globals::GlobalList};
use wlx_capture::wayland::wayland_client::{
Connection, Dispatch, Proxy, QueueHandle,
globals::{GlobalListContents, registry_queue_init},
@@ -13,62 +12,6 @@ use xkbcommon::xkb;
use super::XkbKeymap;
pub struct WlKeymapMonitor {
connection: Connection,
globals: GlobalList,
queue: EventQueue<MonitorState>,
state: MonitorState,
}
impl WlKeymapMonitor {
pub fn new() -> anyhow::Result<Self> {
let connection = Connection::connect_to_env()?;
let (globals, mut queue) = registry_queue_init::<MonitorState>(&connection)?;
let qh = queue.handle();
let seat: WlSeat = globals
.bind(&qh, 4..=9, ())
.unwrap_or_else(|_| panic!("{}", WlSeat::interface().name));
let mut state = MonitorState {
seat,
keyboard: None,
keymap: None,
};
// this gets us the wl_seat
let _ = queue.blocking_dispatch(&mut state);
// this gets us the wl_keyboard
let _ = queue.blocking_dispatch(&mut state);
Ok(Self {
connection,
globals,
queue,
state,
})
}
pub fn check(&mut self) -> Option<XkbKeymap> {
let Some(read_guard) = self.connection.prepare_read() else {
return None;
};
read_guard
.read()
.context("could not read wayland events")
.inspect_err(|e| log::warn!("{e:?}"))
.ok()?;
self.queue.dispatch_pending(&mut self.state).ok()?;
self.take_keymap()
}
pub fn take_keymap(&mut self) -> Option<XkbKeymap> {
self.state.keymap.take()
}
}
struct MonitorState {
seat: WlSeat,
keyboard: Option<WlKeyboard>,