aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/dbus/freedesktop_locale1.rs144
-rw-r--r--src/dbus/mod.rs18
-rw-r--r--src/niri.rs38
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,