Detailed changes
@@ -47,6 +47,10 @@ impl LinuxClient for HeadlessClient {
f(&mut self.0.borrow_mut().common)
}
+ fn keyboard_layout(&self) -> String {
+ "unknown".to_string()
+ }
+
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
vec![]
}
@@ -46,6 +46,7 @@ const FILE_PICKER_PORTAL_MISSING: &str =
pub trait LinuxClient {
fn compositor_name(&self) -> &'static str;
fn with_common<R>(&self, f: impl FnOnce(&mut LinuxCommon) -> R) -> R;
+ fn keyboard_layout(&self) -> String;
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>>;
#[allow(unused)]
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>>;
@@ -76,6 +77,7 @@ pub(crate) struct PlatformHandlers {
pub(crate) app_menu_action: Option<Box<dyn FnMut(&dyn Action)>>,
pub(crate) will_open_app_menu: Option<Box<dyn FnMut()>>,
pub(crate) validate_app_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>,
+ pub(crate) keyboard_layout_change: Option<Box<dyn FnMut()>>,
}
pub(crate) struct LinuxCommon {
@@ -133,11 +135,11 @@ impl<P: LinuxClient + 'static> Platform for P {
}
fn keyboard_layout(&self) -> String {
- "unknown".into()
+ self.keyboard_layout()
}
- fn on_keyboard_layout_change(&self, _callback: Box<dyn FnMut()>) {
- // todo(linux)
+ fn on_keyboard_layout_change(&self, callback: Box<dyn FnMut()>) {
+ self.with_common(|common| common.callbacks.keyboard_layout_change = Some(callback));
}
fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
@@ -588,6 +588,19 @@ impl WaylandClient {
}
impl LinuxClient for WaylandClient {
+ fn keyboard_layout(&self) -> String {
+ let state = self.0.borrow();
+ if let Some(keymap_state) = &state.keymap_state {
+ let layout_idx = keymap_state.serialize_layout(xkbcommon::xkb::STATE_LAYOUT_EFFECTIVE);
+ keymap_state
+ .get_keymap()
+ .layout_get_name(layout_idx)
+ .to_string()
+ } else {
+ "unknown".to_string()
+ }
+ }
+
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
self.0
.borrow()
@@ -1139,6 +1152,13 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
};
state.keymap_state = Some(xkb::State::new(&keymap));
state.compose_state = get_xkb_compose_state(&xkb_context);
+
+ if let Some(mut callback) = state.common.callbacks.keyboard_layout_change.take() {
+ drop(state);
+ callback();
+ state = client.borrow_mut();
+ state.common.callbacks.keyboard_layout_change = Some(callback);
+ }
}
wl_keyboard::Event::Enter { surface, .. } => {
state.keyboard_focused_window = get_window(&mut state, &surface.id());
@@ -1176,9 +1196,21 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
let focused_window = state.keyboard_focused_window.clone();
let keymap_state = state.keymap_state.as_mut().unwrap();
+ let old_layout =
+ keymap_state.serialize_layout(xkbcommon::xkb::STATE_LAYOUT_EFFECTIVE);
keymap_state.update_mask(mods_depressed, mods_latched, mods_locked, 0, 0, group);
state.modifiers = Modifiers::from_xkb(keymap_state);
+ if group != old_layout {
+ if let Some(mut callback) = state.common.callbacks.keyboard_layout_change.take()
+ {
+ drop(state);
+ callback();
+ state = client.borrow_mut();
+ state.common.callbacks.keyboard_layout_change = Some(callback);
+ }
+ }
+
let Some(focused_window) = focused_window else {
return;
};
@@ -37,7 +37,7 @@ use x11rb::{
};
use xim::{x11rb::X11rbClient, AttributeName, Client, InputStyle};
use xkbc::x11::ffi::{XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION};
-use xkbcommon::xkb::{self as xkbc, LayoutIndex, ModMask};
+use xkbcommon::xkb::{self as xkbc, LayoutIndex, ModMask, STATE_LAYOUT_EFFECTIVE};
use super::{
button_or_scroll_from_event_detail, get_valuator_axis_index, modifiers_from_state,
@@ -840,6 +840,8 @@ impl X11Client {
}
Event::XkbStateNotify(event) => {
let mut state = self.0.borrow_mut();
+ let old_layout = state.xkb.serialize_layout(STATE_LAYOUT_EFFECTIVE);
+ let new_layout = u32::from(event.group);
state.xkb.update_mask(
event.base_mods.into(),
event.latched_mods.into(),
@@ -853,6 +855,17 @@ impl X11Client {
latched_layout: event.latched_group as u32,
locked_layout: event.locked_group.into(),
};
+
+ if new_layout != old_layout {
+ if let Some(mut callback) = state.common.callbacks.keyboard_layout_change.take()
+ {
+ drop(state);
+ callback();
+ state = self.0.borrow_mut();
+ state.common.callbacks.keyboard_layout_change = Some(callback);
+ }
+ }
+
let modifiers = Modifiers::from_xkb(&state.xkb);
if state.modifiers == modifiers {
drop(state);
@@ -1265,6 +1278,16 @@ impl LinuxClient for X11Client {
f(&mut self.0.borrow_mut().common)
}
+ fn keyboard_layout(&self) -> String {
+ let state = self.0.borrow();
+ let layout_idx = state.xkb.serialize_layout(STATE_LAYOUT_EFFECTIVE);
+ state
+ .xkb
+ .get_keymap()
+ .layout_get_name(layout_idx)
+ .to_string()
+ }
+
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
let state = self.0.borrow();
let setup = state.xcb_connection.setup();