Add cosmic-keyboard-layout-unstable-v1 protocol
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.
This commit is contained in:
parent
031fdc389a
commit
29320bdab5
7 changed files with 215 additions and 11 deletions
20
Cargo.lock
generated
20
Cargo.lock
generated
|
|
@ -788,7 +788,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cosmic-client-toolkit"
|
name = "cosmic-client-toolkit"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#160b086abe03cd34a8a375d7fbe47b24308d1f38"
|
source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#0d0b0b573fe4836ede4c237dadd365033cd28296"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.0",
|
||||||
"cosmic-protocols",
|
"cosmic-protocols",
|
||||||
|
|
@ -918,7 +918,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cosmic-protocols"
|
name = "cosmic-protocols"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#160b086abe03cd34a8a375d7fbe47b24308d1f38"
|
source = "git+https://github.com/pop-os//cosmic-protocols?branch=main#0d0b0b573fe4836ede4c237dadd365033cd28296"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.0",
|
"bitflags 2.11.0",
|
||||||
"wayland-backend",
|
"wayland-backend",
|
||||||
|
|
@ -1204,7 +1204,7 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"option-ext",
|
"option-ext",
|
||||||
"redox_users",
|
"redox_users",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1518,7 +1518,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2688,7 +2688,7 @@ dependencies = [
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"portable-atomic-util",
|
"portable-atomic-util",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2811,7 +2811,7 @@ version = "1.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "770919970f7d2f74fea948900d35e2ef64f44129e8ae4015f59de1f0aca7c2a5"
|
checksum = "770919970f7d2f74fea948900d35e2ef64f44129e8ae4015f59de1f0aca7c2a5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -3421,7 +3421,7 @@ version = "0.50.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -4502,7 +4502,7 @@ dependencies = [
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys 0.12.1",
|
"linux-raw-sys 0.12.1",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -5094,7 +5094,7 @@ dependencies = [
|
||||||
"getrandom 0.4.1",
|
"getrandom 0.4.1",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix 1.1.4",
|
"rustix 1.1.4",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -6113,7 +6113,7 @@ version = "0.1.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,9 @@ use std::{
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, info, warn};
|
||||||
use wayland::protocols::overlap_notify::OverlapNotifyState;
|
use wayland::protocols::{
|
||||||
|
keyboard_layout::KeyboardLayoutState, overlap_notify::OverlapNotifyState,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::wayland::handlers::compositor::client_compositor_state;
|
use crate::wayland::handlers::compositor::client_compositor_state;
|
||||||
|
|
||||||
|
|
@ -331,5 +333,6 @@ fn refresh(state: &mut State) {
|
||||||
state::Common::refresh_focus(state);
|
state::Common::refresh_focus(state);
|
||||||
OverlapNotifyState::refresh(state);
|
OverlapNotifyState::refresh(state);
|
||||||
state.common.update_x11_stacking_order();
|
state.common.update_x11_stacking_order();
|
||||||
|
KeyboardLayoutState::refresh(state);
|
||||||
state.last_refresh = LastRefresh::At(Instant::now());
|
state.last_refresh = LastRefresh::At(Instant::now());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ use crate::{
|
||||||
corner_radius::CornerRadiusState,
|
corner_radius::CornerRadiusState,
|
||||||
drm::WlDrmState,
|
drm::WlDrmState,
|
||||||
image_capture_source::CosmicImageCaptureSourceState,
|
image_capture_source::CosmicImageCaptureSourceState,
|
||||||
|
keyboard_layout::KeyboardLayoutState,
|
||||||
output_configuration::OutputConfigurationState,
|
output_configuration::OutputConfigurationState,
|
||||||
output_power::OutputPowerState,
|
output_power::OutputPowerState,
|
||||||
overlap_notify::OverlapNotifyState,
|
overlap_notify::OverlapNotifyState,
|
||||||
|
|
@ -279,6 +280,7 @@ pub struct Common {
|
||||||
pub overlap_notify_state: OverlapNotifyState,
|
pub overlap_notify_state: OverlapNotifyState,
|
||||||
pub a11y_state: A11yState,
|
pub a11y_state: A11yState,
|
||||||
pub dbus_state: DBusState,
|
pub dbus_state: DBusState,
|
||||||
|
pub keyboard_layout_state: KeyboardLayoutState,
|
||||||
|
|
||||||
// shell-related wayland state
|
// shell-related wayland state
|
||||||
pub xdg_shell_state: XdgShellState,
|
pub xdg_shell_state: XdgShellState,
|
||||||
|
|
@ -682,6 +684,7 @@ impl State {
|
||||||
AlphaModifierState::new::<Self>(dh);
|
AlphaModifierState::new::<Self>(dh);
|
||||||
SinglePixelBufferState::new::<Self>(dh);
|
SinglePixelBufferState::new::<Self>(dh);
|
||||||
FixesState::new::<Self>(dh);
|
FixesState::new::<Self>(dh);
|
||||||
|
let keyboard_layout_state = KeyboardLayoutState::new::<State, _>(&dh, client_not_sandboxed);
|
||||||
|
|
||||||
let idle_notifier_state = IdleNotifierState::<Self>::new(dh, handle.clone());
|
let idle_notifier_state = IdleNotifierState::<Self>::new(dh, handle.clone());
|
||||||
let idle_inhibit_manager_state = IdleInhibitManagerState::new::<State>(dh);
|
let idle_inhibit_manager_state = IdleInhibitManagerState::new::<State>(dh);
|
||||||
|
|
@ -792,6 +795,7 @@ impl State {
|
||||||
xwayland_shell_state,
|
xwayland_shell_state,
|
||||||
pointer_focus_state: None,
|
pointer_focus_state: None,
|
||||||
dbus_state,
|
dbus_state,
|
||||||
|
keyboard_layout_state,
|
||||||
|
|
||||||
#[cfg(feature = "logind")]
|
#[cfg(feature = "logind")]
|
||||||
inhibit_lid_fd: None,
|
inhibit_lid_fd: None,
|
||||||
|
|
|
||||||
10
src/wayland/handlers/keyboard_layout.rs
Normal file
10
src/wayland/handlers/keyboard_layout.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
use crate::state::State;
|
||||||
|
use crate::wayland::protocols::keyboard_layout::{KeyboardLayoutHandler, KeyboardLayoutState};
|
||||||
|
|
||||||
|
impl KeyboardLayoutHandler for State {
|
||||||
|
fn keyboard_layout_state(&mut self) -> &mut KeyboardLayoutState {
|
||||||
|
&mut self.common.keyboard_layout_state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,6 +18,7 @@ pub mod idle_notify;
|
||||||
pub mod image_capture_source;
|
pub mod image_capture_source;
|
||||||
pub mod image_copy_capture;
|
pub mod image_copy_capture;
|
||||||
pub mod input_method;
|
pub mod input_method;
|
||||||
|
pub mod keyboard_layout;
|
||||||
pub mod keyboard_shortcuts_inhibit;
|
pub mod keyboard_shortcuts_inhibit;
|
||||||
pub mod layer_shell;
|
pub mod layer_shell;
|
||||||
pub mod output;
|
pub mod output;
|
||||||
|
|
|
||||||
185
src/wayland/protocols/keyboard_layout.rs
Normal file
185
src/wayland/protocols/keyboard_layout.rs
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ pub mod a11y;
|
||||||
pub mod corner_radius;
|
pub mod corner_radius;
|
||||||
pub mod drm;
|
pub mod drm;
|
||||||
pub mod image_capture_source;
|
pub mod image_capture_source;
|
||||||
|
pub mod keyboard_layout;
|
||||||
pub mod output_configuration;
|
pub mod output_configuration;
|
||||||
pub mod output_power;
|
pub mod output_power;
|
||||||
pub mod overlap_notify;
|
pub mod overlap_notify;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue