diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/dbus/freedesktop_locale1.rs | 144 | ||||
| -rw-r--r-- | src/dbus/mod.rs | 18 | ||||
| -rw-r--r-- | src/niri.rs | 38 |
3 files changed, 199 insertions, 1 deletions
diff --git a/src/dbus/freedesktop_locale1.rs b/src/dbus/freedesktop_locale1.rs new file mode 100644 index 00000000..3f7c2484 --- /dev/null +++ b/src/dbus/freedesktop_locale1.rs @@ -0,0 +1,144 @@ +use futures_util::StreamExt; +use niri_config::Xkb; +use zbus::names::InterfaceName; +use zbus::{fdo, zvariant}; + +pub enum Locale1ToNiri { + XkbChanged(Xkb), +} + +pub fn start( + to_niri: calloop::channel::Sender<Locale1ToNiri>, +) -> anyhow::Result<zbus::blocking::Connection> { + let conn = zbus::blocking::Connection::system()?; + + let async_conn = conn.inner().clone(); + let future = async move { + let proxy = fdo::PropertiesProxy::new( + &async_conn, + "org.freedesktop.locale1", + "/org/freedesktop/locale1", + ) + .await; + let proxy = match proxy { + Ok(x) => x, + Err(err) => { + warn!("error creating PropertiesProxy: {err:?}"); + return; + } + }; + + let mut props_changed = match proxy.receive_properties_changed().await { + Ok(x) => x, + Err(err) => { + warn!("error subscribing to PropertiesChanged: {err:?}"); + return; + } + }; + + let props = proxy + .get_all(InterfaceName::try_from("org.freedesktop.locale1").unwrap()) + .await; + let mut props = match props { + Ok(x) => x, + Err(err) => { + warn!("error receiving initial properties: {err:?}"); + return; + } + }; + + trace!("initial properties: {props:?}"); + + let mut get = |name| { + props + .remove(name) + .and_then(|x| String::try_from(x).ok()) + .unwrap_or_default() + }; + + let mut xkb = Xkb { + rules: String::new(), + model: get("X11Model"), + layout: get("X11Layout"), + variant: get("X11Variant"), + options: match get("X11Options") { + x if x.is_empty() => None, + x => Some(x), + }, + file: None, + }; + + // Send the initial properties. + if let Err(err) = to_niri.send(Locale1ToNiri::XkbChanged(xkb.clone())) { + warn!("error sending message to niri: {err:?}"); + return; + }; + + while let Some(changed) = props_changed.next().await { + let args = match changed.args() { + Ok(args) => args, + Err(err) => { + warn!("error parsing locale1 PropertiesChanged args: {err:?}"); + return; + } + }; + + let mut changed = false; + for (name, value) in args.changed_properties() { + trace!("changed property: {name} => {value:?}"); + + let value = zvariant::Str::try_from(value).unwrap_or_default(); + let value = value.as_str(); + + match *name { + "X11Model" => { + if xkb.model != value { + xkb.model = String::from(value); + changed = true; + } + } + "X11Layout" => { + if xkb.layout != value { + xkb.layout = String::from(value); + changed = true; + } + } + "X11Variant" => { + if xkb.variant != value { + xkb.variant = String::from(value); + changed = true; + } + } + "X11Options" => { + let value = match value { + "" => None, + x => Some(x), + }; + if xkb.options.as_deref() != value { + xkb.options = value.map(String::from); + changed = true; + } + } + _ => (), + } + } + + if !changed { + continue; + } + + if let Err(err) = to_niri.send(Locale1ToNiri::XkbChanged(xkb.clone())) { + warn!("error sending message to niri: {err:?}"); + return; + }; + } + }; + + let task = conn + .inner() + .executor() + .spawn(future, "monitor locale1 property changes"); + task.detach(); + + Ok(conn) +} diff --git a/src/dbus/mod.rs b/src/dbus/mod.rs index 09d973ad..aaa7c1ae 100644 --- a/src/dbus/mod.rs +++ b/src/dbus/mod.rs @@ -3,6 +3,7 @@ use zbus::object_server::Interface; use crate::niri::State; +pub mod freedesktop_locale1; pub mod freedesktop_screensaver; pub mod gnome_shell_introspect; pub mod gnome_shell_screenshot; @@ -32,6 +33,7 @@ pub struct DBusServers { pub conn_introspect: Option<Connection>, #[cfg(feature = "xdp-gnome-screencast")] pub conn_screen_cast: Option<Connection>, + pub conn_locale1: Option<Connection>, } impl DBusServers { @@ -125,6 +127,22 @@ impl DBusServers { } } + let (to_niri, from_locale1) = calloop::channel::channel(); + niri.event_loop + .insert_source(from_locale1, move |event, _, state| match event { + calloop::channel::Event::Msg(msg) => state.on_locale1_msg(msg), + calloop::channel::Event::Closed => (), + }) + .unwrap(); + match freedesktop_locale1::start(to_niri) { + Ok(conn) => { + dbus.conn_locale1 = Some(conn); + } + Err(err) => { + warn!("error starting locale1 watcher: {err:?}"); + } + } + niri.dbus = Some(dbus); } } diff --git a/src/niri.rs b/src/niri.rs index 60365e0b..03fe6d4f 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -15,7 +15,7 @@ use anyhow::{bail, ensure, Context}; use calloop::futures::Scheduler; use niri_config::{ Config, FloatOrInt, Key, Modifiers, OutputName, PreviewRender, TrackLayout, - WarpMouseToFocusMode, WorkspaceReference, + WarpMouseToFocusMode, WorkspaceReference, Xkb, }; use smithay::backend::allocator::Fourcc; use smithay::backend::input::Keycode; @@ -115,6 +115,8 @@ use crate::backend::tty::SurfaceDmabufFeedback; use crate::backend::{Backend, Headless, RenderResult, Tty, Winit}; use crate::cursor::{CursorManager, CursorTextureCache, RenderCursor, XCursor}; #[cfg(feature = "dbus")] +use crate::dbus::freedesktop_locale1::Locale1ToNiri; +#[cfg(feature = "dbus")] use crate::dbus::gnome_shell_introspect::{self, IntrospectToNiri, NiriToIntrospect}; #[cfg(feature = "dbus")] use crate::dbus::gnome_shell_screenshot::{NiriToScreenshot, ScreenshotToNiri}; @@ -319,6 +321,9 @@ pub struct Niri { pub is_fdo_idle_inhibited: Arc<AtomicBool>, pub keyboard_shortcuts_inhibiting_surfaces: HashMap<WlSurface, KeyboardShortcutsInhibitor>, + /// Most recent XKB settings from org.freedesktop.locale1. + pub xkb_from_locale1: Option<Xkb>, + pub cursor_manager: CursorManager, pub cursor_texture_cache: CursorTextureCache, pub cursor_shape_manager_state: CursorShapeManagerState, @@ -1490,6 +1495,12 @@ impl State { } if set_xkb_config { + // If xkb is unset in the niri config, use settings from locale1. + if xkb == Xkb::default() { + trace!("using xkb from locale1"); + xkb = self.niri.xkb_from_locale1.clone().unwrap_or_default(); + } + let keyboard = self.niri.seat.get_keyboard().unwrap(); if let Err(err) = keyboard.set_xkb_config(self, xkb.to_xkb_config()) { warn!("error updating xkb config: {err:?}"); @@ -2226,6 +2237,30 @@ impl State { warn!("error sending windows to introspect: {err:?}"); } } + + #[cfg(feature = "dbus")] + pub fn on_locale1_msg(&mut self, msg: Locale1ToNiri) { + let Locale1ToNiri::XkbChanged(xkb) = msg; + + trace!("locale1 xkb settings changed: {xkb:?}"); + let xkb = self.niri.xkb_from_locale1.insert(xkb); + + { + let config = self.niri.config.borrow(); + if config.input.keyboard.xkb != Xkb::default() { + trace!("ignoring locale1 xkb change because niri config has xkb settings"); + return; + } + } + + let xkb = xkb.clone(); + let keyboard = self.niri.seat.get_keyboard().unwrap(); + if let Err(err) = keyboard.set_xkb_config(self, xkb.to_xkb_config()) { + warn!("error updating xkb config: {err:?}"); + } + + self.ipc_keyboard_layouts_changed(); + } } impl Niri { @@ -2583,6 +2618,7 @@ impl Niri { idle_inhibiting_surfaces: HashSet::new(), is_fdo_idle_inhibited: Arc::new(AtomicBool::new(false)), keyboard_shortcuts_inhibiting_surfaces: HashMap::new(), + xkb_from_locale1: None, cursor_manager, cursor_texture_cache: Default::default(), cursor_shape_manager_state, |
