poc keyboard layout change on wayland

This commit is contained in:
galister
2025-12-14 21:19:03 +09:00
parent c732424e7d
commit 9c71917b2a
3 changed files with 86 additions and 40 deletions

View File

@@ -10,7 +10,8 @@ use crate::{
overlays::keyboard::{builder::create_keyboard_panel, layout::AltModifier}, overlays::keyboard::{builder::create_keyboard_panel, layout::AltModifier},
state::AppState, state::AppState,
subsystem::hid::{ subsystem::hid::{
ALT, CTRL, KeyModifier, META, SHIFT, SUPER, VirtualKey, WheelDelta, XkbKeymap, wayland::WlKeymapMonitor, KeyModifier, VirtualKey, WheelDelta, XkbKeymap, ALT, CTRL, META,
SHIFT, SUPER,
}, },
windowing::{ windowing::{
backend::{FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender}, backend::{FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender},
@@ -18,8 +19,8 @@ use crate::{
}, },
}; };
use dbus::message::MatchRule; use dbus::message::MatchRule;
use glam::{Affine3A, Quat, Vec3, vec3}; use glam::{vec3, Affine3A, Quat, Vec3};
use slotmap::{SlotMap, new_key_type}; use slotmap::{new_key_type, SlotMap};
use wgui::{ use wgui::{
drawing, drawing,
event::{InternalStateChangeEvent, MouseButton, MouseButtonIndex}, event::{InternalStateChangeEvent, MouseButton, MouseButtonIndex},
@@ -62,10 +63,10 @@ pub fn create_keyboard(
active_keymap: KeyboardPanelKey::default(), active_keymap: KeyboardPanelKey::default(),
default_state, default_state,
layout, layout,
wkm: WlKeymapMonitor::new()?,
}; };
backend.active_keymap = backend.add_new_keymap(keymap.as_ref(), app)?; backend.active_keymap = backend.add_new_keymap(keymap.as_ref(), app)?;
backend.watch_dbus(app);
Ok(OverlayWindowConfig { Ok(OverlayWindowConfig {
name: KEYBOARD_NAME.into(), name: KEYBOARD_NAME.into(),
@@ -95,6 +96,7 @@ struct KeyboardBackend {
active_keymap: KeyboardPanelKey, active_keymap: KeyboardPanelKey,
default_state: KeyboardState, default_state: KeyboardState,
layout: layout::Layout, layout: layout::Layout,
wkm: WlKeymapMonitor,
} }
impl KeyboardBackend { impl KeyboardBackend {
@@ -114,43 +116,22 @@ impl KeyboardBackend {
Ok(id) Ok(id)
} }
fn watch_dbus(&mut self, app: &mut AppState) { fn switch_keymap(&mut self, keymap: &XkbKeymap, app: &mut AppState) -> anyhow::Result<bool> {
let rules = [
MatchRule::new()
.with_member("CurrentInputMethod")
.with_interface("org.fcitx.Fcitx.Controller1")
.with_path("/controller")
.with_sender("org.fcitx.Fcitx5"),
MatchRule::new_signal("org.kde.KeyboardLayouts", "layoutChanged").with_path("/Layouts"),
];
for rule in rules {
let _ = app.dbus.add_match(
rule,
Box::new(move |(), _, msg| {
log::warn!("new keymap: {msg:?}");
true
}),
);
}
}
fn switch_keymap(&mut self, keymap: &XkbKeymap, app: &mut AppState) -> anyhow::Result<()> {
let Some(layout_name) = keymap.inner.layouts().next() else { let Some(layout_name) = keymap.inner.layouts().next() else {
log::error!("XKB keymap without a layout!"); log::error!("XKB keymap without a layout!");
return Ok(()); return Ok(false);
}; };
if let Some(new_key) = self.keymap_ids.get(layout_name) { if let Some(new_key) = self.keymap_ids.get(layout_name) {
if self.active_keymap.eq(new_key) { if self.active_keymap.eq(new_key) {
return Ok(()); return Ok(false);
} }
self.internal_switch_keymap(*new_key); self.internal_switch_keymap(*new_key);
} else { } else {
let new_key = self.add_new_keymap(Some(keymap), app)?; let new_key = self.add_new_keymap(Some(keymap), app)?;
self.internal_switch_keymap(new_key); self.internal_switch_keymap(new_key);
} }
Ok(()) Ok(true)
} }
fn internal_switch_keymap(&mut self, new_key: KeyboardPanelKey) { fn internal_switch_keymap(&mut self, new_key: KeyboardPanelKey) {
@@ -179,6 +160,14 @@ impl OverlayBackend for KeyboardBackend {
self.panel().init(app) self.panel().init(app)
} }
fn should_render(&mut self, app: &mut AppState) -> anyhow::Result<ShouldRender> { fn should_render(&mut self, app: &mut AppState) -> anyhow::Result<ShouldRender> {
if let Some(keymap) = self.wkm.check() {
if self.switch_keymap(&keymap, app)? {
return Ok(match self.panel().should_render(app)? {
ShouldRender::Should | ShouldRender::Can => ShouldRender::Should,
ShouldRender::Unable => ShouldRender::Unable,
});
}
}
self.panel().should_render(app) self.panel().should_render(app)
} }
fn render(&mut self, app: &mut AppState, rdr: &mut RenderResources) -> anyhow::Result<()> { fn render(&mut self, app: &mut AppState, rdr: &mut RenderResources) -> anyhow::Result<()> {

View File

@@ -1,5 +1,5 @@
use glam::Vec2; use glam::Vec2;
use idmap::{IdMap, idmap}; use idmap::{idmap, IdMap};
use idmap_derive::IntegerId; use idmap_derive::IntegerId;
use input_linux::{ use input_linux::{
AbsoluteAxis, AbsoluteInfo, AbsoluteInfoSetup, EventKind, InputId, Key, RelativeAxis, AbsoluteAxis, AbsoluteInfo, AbsoluteInfoSetup, EventKind, InputId, Key, RelativeAxis,
@@ -14,7 +14,7 @@ use strum::{EnumIter, EnumString, IntoEnumIterator};
use xkbcommon::xkb; use xkbcommon::xkb;
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
mod wayland; pub mod wayland;
#[cfg(feature = "x11")] #[cfg(feature = "x11")]
mod x11; mod x11;

View File

@@ -1,24 +1,81 @@
use anyhow::Context; use anyhow::Context;
use wayland_client::{globals::GlobalList, EventQueue};
use wlx_capture::wayland::wayland_client::{ use wlx_capture::wayland::wayland_client::{
Connection, Dispatch, Proxy, QueueHandle, globals::{registry_queue_init, GlobalListContents},
globals::{GlobalListContents, registry_queue_init},
protocol::{ protocol::{
wl_keyboard::{self, WlKeyboard}, wl_keyboard::{self, WlKeyboard},
wl_registry::WlRegistry, wl_registry::WlRegistry,
wl_seat::{self, Capability, WlSeat}, wl_seat::{self, Capability, WlSeat},
}, },
Connection, Dispatch, Proxy, QueueHandle,
}; };
use xkbcommon::xkb; use xkbcommon::xkb;
use super::XkbKeymap; use super::XkbKeymap;
struct WlKeymapHandler { 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, seat: WlSeat,
keyboard: Option<WlKeyboard>, keyboard: Option<WlKeyboard>,
keymap: Option<XkbKeymap>, keymap: Option<XkbKeymap>,
} }
impl Drop for WlKeymapHandler { impl Drop for MonitorState {
fn drop(&mut self) { fn drop(&mut self) {
if let Some(keyboard) = &self.keyboard { if let Some(keyboard) = &self.keyboard {
keyboard.release(); keyboard.release();
@@ -29,13 +86,13 @@ impl Drop for WlKeymapHandler {
pub fn get_keymap_wl() -> anyhow::Result<XkbKeymap> { pub fn get_keymap_wl() -> anyhow::Result<XkbKeymap> {
let connection = Connection::connect_to_env()?; let connection = Connection::connect_to_env()?;
let (globals, mut queue) = registry_queue_init::<WlKeymapHandler>(&connection)?; let (globals, mut queue) = registry_queue_init::<MonitorState>(&connection)?;
let qh = queue.handle(); let qh = queue.handle();
let seat: WlSeat = globals let seat: WlSeat = globals
.bind(&qh, 4..=9, ()) .bind(&qh, 4..=9, ())
.unwrap_or_else(|_| panic!("{}", WlSeat::interface().name)); .unwrap_or_else(|_| panic!("{}", WlSeat::interface().name));
let mut me = WlKeymapHandler { let mut me = MonitorState {
seat, seat,
keyboard: None, keyboard: None,
keymap: None, keymap: None,
@@ -50,7 +107,7 @@ pub fn get_keymap_wl() -> anyhow::Result<XkbKeymap> {
me.keymap.take().context("could not load keymap") me.keymap.take().context("could not load keymap")
} }
impl Dispatch<WlRegistry, GlobalListContents> for WlKeymapHandler { impl Dispatch<WlRegistry, GlobalListContents> for MonitorState {
fn event( fn event(
_state: &mut Self, _state: &mut Self,
_proxy: &WlRegistry, _proxy: &WlRegistry,
@@ -62,7 +119,7 @@ impl Dispatch<WlRegistry, GlobalListContents> for WlKeymapHandler {
} }
} }
impl Dispatch<WlSeat, ()> for WlKeymapHandler { impl Dispatch<WlSeat, ()> for MonitorState {
fn event( fn event(
state: &mut Self, state: &mut Self,
proxy: &WlSeat, proxy: &WlSeat,
@@ -88,7 +145,7 @@ impl Dispatch<WlSeat, ()> for WlKeymapHandler {
} }
} }
impl Dispatch<WlKeyboard, ()> for WlKeymapHandler { impl Dispatch<WlKeyboard, ()> for MonitorState {
fn event( fn event(
state: &mut Self, state: &mut Self,
_proxy: &WlKeyboard, _proxy: &WlKeyboard,