poc keyboard layout change on wayland
This commit is contained in:
@@ -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<()> {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user