https://github.com/pop-os/cosmic-protocols/pull/28 Allows clients like `cosmic-applet-input-sources` to show which layout group is active and update it. Currently this is implemented by update the `xkb_config` to change the layout order.
185 lines
5.7 KiB
Rust
185 lines
5.7 KiB
Rust
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
use cosmic_protocols::keyboard_layout::v1::server::{
|
|
zcosmic_keyboard_layout_manager_v1::{self, ZcosmicKeyboardLayoutManagerV1},
|
|
zcosmic_keyboard_layout_v1::{self, ZcosmicKeyboardLayoutV1},
|
|
};
|
|
use smithay::{
|
|
input::{
|
|
SeatHandler,
|
|
keyboard::{KeyboardHandle, Layout},
|
|
},
|
|
reexports::wayland_server::{
|
|
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource,
|
|
},
|
|
wayland::{Dispatch2, GlobalDispatch2},
|
|
};
|
|
use std::mem;
|
|
use wayland_backend::server::{ClientId, GlobalId};
|
|
|
|
pub trait KeyboardLayoutHandler {
|
|
fn keyboard_layout_state(&mut self) -> &mut KeyboardLayoutState;
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct KeyboardLayoutState {
|
|
pub global: GlobalId,
|
|
keyboard_layouts: Vec<(ZcosmicKeyboardLayoutV1, Option<Layout>)>,
|
|
}
|
|
|
|
impl KeyboardLayoutState {
|
|
pub fn new<D, F>(dh: &DisplayHandle, client_filter: F) -> Self
|
|
where
|
|
D: GlobalDispatch<ZcosmicKeyboardLayoutManagerV1, LayoutGlobalData> + 'static,
|
|
F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static,
|
|
{
|
|
let global = dh.create_global::<D, ZcosmicKeyboardLayoutManagerV1, _>(
|
|
1,
|
|
LayoutGlobalData {
|
|
filter: Box::new(client_filter),
|
|
},
|
|
);
|
|
KeyboardLayoutState {
|
|
global,
|
|
keyboard_layouts: Vec::new(),
|
|
}
|
|
}
|
|
|
|
pub fn refresh<D>(state: &mut D)
|
|
where
|
|
D: SeatHandler + KeyboardLayoutHandler + 'static,
|
|
{
|
|
let mut keyboard_layouts = mem::take(&mut state.keyboard_layout_state().keyboard_layouts);
|
|
for (keyboard_layout, last_layout) in &mut keyboard_layouts {
|
|
if let Some(data) = keyboard_layout.data::<LayoutUserData<D>>() {
|
|
if let Some(handle) = &data.handle {
|
|
let active_layout = handle.with_xkb_state(state, |context| {
|
|
context.xkb().lock().unwrap().active_layout()
|
|
});
|
|
if *last_layout != Some(active_layout) {
|
|
keyboard_layout.group(active_layout.0);
|
|
*last_layout = Some(active_layout);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
state.keyboard_layout_state().keyboard_layouts = keyboard_layouts;
|
|
}
|
|
}
|
|
|
|
pub struct LayoutGlobalData {
|
|
filter: Box<dyn for<'a> Fn(&'a Client) -> bool + Send + Sync>,
|
|
}
|
|
|
|
struct LayoutManagerData;
|
|
|
|
impl<D> GlobalDispatch2<ZcosmicKeyboardLayoutManagerV1, D> for LayoutGlobalData
|
|
where
|
|
D: Dispatch<ZcosmicKeyboardLayoutManagerV1, LayoutManagerData> + 'static,
|
|
{
|
|
fn bind(
|
|
&self,
|
|
_state: &mut D,
|
|
_handle: &DisplayHandle,
|
|
_client: &Client,
|
|
resource: New<ZcosmicKeyboardLayoutManagerV1>,
|
|
data_init: &mut DataInit<'_, D>,
|
|
) {
|
|
data_init.init(resource, LayoutManagerData);
|
|
}
|
|
|
|
fn can_view(&self, client: &Client) -> bool {
|
|
(self.filter)(client)
|
|
}
|
|
}
|
|
|
|
impl<D> Dispatch2<ZcosmicKeyboardLayoutManagerV1, D> for LayoutManagerData
|
|
where
|
|
D: Dispatch<ZcosmicKeyboardLayoutV1, LayoutUserData<D>>,
|
|
D: 'static,
|
|
D: SeatHandler,
|
|
D: KeyboardLayoutHandler,
|
|
{
|
|
fn request(
|
|
&self,
|
|
state: &mut D,
|
|
_client: &Client,
|
|
_resource: &ZcosmicKeyboardLayoutManagerV1,
|
|
request: zcosmic_keyboard_layout_manager_v1::Request,
|
|
_dhandle: &DisplayHandle,
|
|
data_init: &mut DataInit<'_, D>,
|
|
) {
|
|
match request {
|
|
zcosmic_keyboard_layout_manager_v1::Request::GetKeyboardLayout {
|
|
keyboard_layout,
|
|
keyboard,
|
|
} => {
|
|
let handle = KeyboardHandle::<D>::from_resource(&keyboard);
|
|
let active_layout = handle.as_ref().map(|handle| {
|
|
handle.with_xkb_state(state, |context| {
|
|
context.xkb().lock().unwrap().active_layout()
|
|
})
|
|
});
|
|
let keyboard_layout = data_init.init(keyboard_layout, LayoutUserData { handle });
|
|
if let Some(layout) = active_layout {
|
|
keyboard_layout.group(layout.0);
|
|
}
|
|
state
|
|
.keyboard_layout_state()
|
|
.keyboard_layouts
|
|
.push((keyboard_layout, active_layout));
|
|
}
|
|
zcosmic_keyboard_layout_manager_v1::Request::Destroy => {}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[doc(hidden)]
|
|
struct LayoutUserData<D: SeatHandler> {
|
|
handle: Option<KeyboardHandle<D>>,
|
|
}
|
|
|
|
impl<D> Dispatch2<ZcosmicKeyboardLayoutV1, D> for LayoutUserData<D>
|
|
where
|
|
D: 'static,
|
|
D: SeatHandler,
|
|
D: KeyboardLayoutHandler,
|
|
{
|
|
fn request(
|
|
&self,
|
|
state: &mut D,
|
|
_client: &Client,
|
|
_resource: &ZcosmicKeyboardLayoutV1,
|
|
request: zcosmic_keyboard_layout_v1::Request,
|
|
_dhandle: &DisplayHandle,
|
|
_data_init: &mut DataInit<'_, D>,
|
|
) {
|
|
match request {
|
|
zcosmic_keyboard_layout_v1::Request::SetGroup { group } => {
|
|
if let Some(handle) = self.handle.as_ref() {
|
|
handle.with_xkb_state(state, |mut context| {
|
|
context.set_layout(Layout(group));
|
|
});
|
|
}
|
|
}
|
|
zcosmic_keyboard_layout_v1::Request::Destroy => {}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn destroyed(
|
|
&self,
|
|
state: &mut D,
|
|
_client: ClientId,
|
|
keyboard_layout: &ZcosmicKeyboardLayoutV1,
|
|
) {
|
|
let keyboard_layouts = &mut state.keyboard_layout_state().keyboard_layouts;
|
|
if let Some(idx) = keyboard_layouts
|
|
.iter()
|
|
.position(|(x, _)| x == keyboard_layout)
|
|
{
|
|
keyboard_layouts.remove(idx);
|
|
}
|
|
}
|
|
}
|