diff --git a/Cargo.lock b/Cargo.lock index aebb3e94..ae594f88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -899,7 +899,7 @@ dependencies = [ [[package]] name = "cosmic-protocols" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-protocols?branch=main#ec1616b90fa6b4568709cfe2c0627b1e8cc887e0" +source = "git+https://github.com/pop-os/cosmic-protocols?branch=keymap#9f0ccdb533ee48f5eec9dcf8a3486a9a13a17279" dependencies = [ "bitflags 2.6.0", "wayland-backend", @@ -1064,7 +1064,7 @@ version = "0.19.0" source = "git+https://github.com/gfx-rs/wgpu?rev=20fda69#20fda698341efbdc870b8027d6d49f5bf3f36109" dependencies = [ "bitflags 2.6.0", - "libloading 0.8.5", + "libloading 0.7.4", "winapi", ] @@ -1199,7 +1199,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.8.5", + "libloading 0.7.4", ] [[package]] @@ -2162,7 +2162,7 @@ dependencies = [ "bitflags 2.6.0", "com", "libc", - "libloading 0.8.5", + "libloading 0.7.4", "thiserror", "widestring", "winapi", @@ -2828,7 +2828,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -5879,7 +5879,7 @@ dependencies = [ "js-sys", "khronos-egl", "libc", - "libloading 0.8.5", + "libloading 0.7.4", "log", "metal", "naga", diff --git a/Cargo.toml b/Cargo.toml index 6aa22761..ed29f123 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,8 @@ bytemuck = "1.12" calloop = {version = "0.14.1", features = ["executor"]} cosmic-comp-config = {path = "cosmic-comp-config"} cosmic-config = {git = "https://github.com/pop-os/libcosmic/", features = ["calloop", "macro"]} -cosmic-protocols = {git = "https://github.com/pop-os/cosmic-protocols", branch = "main", default-features = false, features = ["server"]} +# cosmic-protocols = {git = "https://github.com/pop-os/cosmic-protocols", branch = "main", default-features = false, features = ["server"]} +cosmic-protocols = {git = "https://github.com/pop-os/cosmic-protocols", branch = "keymap", default-features = false, features = ["server"]} cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daemon" } edid-rs = {version = "0.1"} egui = {version = "0.29.0", optional = true} diff --git a/src/main.rs b/src/main.rs index 4c9c4860..99667679 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,9 @@ use state::State; use std::{env, ffi::OsString, os::unix::process::CommandExt, process, sync::Arc}; use tracing::{error, info, warn}; -use crate::wayland::handlers::compositor::client_compositor_state; +use crate::wayland::{ + handlers::compositor::client_compositor_state, protocols::keymap::KeymapState, +}; pub mod backend; pub mod config; @@ -133,6 +135,8 @@ fn main() -> Result<()> { state::Common::refresh_focus(state); state.common.update_x11_stacking_order(); + KeymapState::refresh(state); + { let shell = state.common.shell.read().unwrap(); if shell.animations_going() { diff --git a/src/state.rs b/src/state.rs index 747754c0..961b0801 100644 --- a/src/state.rs +++ b/src/state.rs @@ -15,6 +15,7 @@ use crate::{ atspi::AtspiState, drm::WlDrmState, image_source::ImageSourceState, + keymap::KeymapState, output_configuration::OutputConfigurationState, output_power::OutputPowerState, screencopy::ScreencopyState, @@ -217,6 +218,7 @@ pub struct Common { pub viewporter_state: ViewporterState, pub kde_decoration_state: KdeDecorationState, pub xdg_decoration_state: XdgDecorationState, + pub keymap_state: KeymapState, // shell-related wayland state pub xdg_shell_state: XdgShellState, @@ -522,6 +524,7 @@ impl State { VirtualKeyboardManagerState::new::(&dh, client_is_privileged); AlphaModifierState::new::(&dh); SinglePixelBufferState::new::(&dh); + let keymap_state = KeymapState::new::(&dh, client_is_privileged); let idle_notifier_state = IdleNotifierState::::new(&dh, handle.clone()); let idle_inhibit_manager_state = IdleInhibitManagerState::new::(&dh); @@ -625,6 +628,7 @@ impl State { xwayland_state: None, xwayland_shell_state, pointer_focus_state: None, + keymap_state, atspi_state, atspi_ei: Default::default(), diff --git a/src/wayland/handlers/keymap.rs b/src/wayland/handlers/keymap.rs new file mode 100644 index 00000000..9e35c615 --- /dev/null +++ b/src/wayland/handlers/keymap.rs @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use crate::wayland::protocols::keymap::{KeymapHandler, KeymapState}; +use crate::{state::State, wayland::protocols::keymap::delegate_keymap}; + +impl KeymapHandler for State { + fn keymap_state(&mut self) -> &mut KeymapState { + &mut self.common.keymap_state + } +} + +delegate_keymap!(State); diff --git a/src/wayland/handlers/mod.rs b/src/wayland/handlers/mod.rs index 52414103..1a288abc 100644 --- a/src/wayland/handlers/mod.rs +++ b/src/wayland/handlers/mod.rs @@ -17,6 +17,7 @@ pub mod idle_notify; pub mod image_source; pub mod input_method; pub mod keyboard_shortcuts_inhibit; +pub mod keymap; pub mod layer_shell; pub mod output; pub mod output_configuration; diff --git a/src/wayland/protocols/keymap.rs b/src/wayland/protocols/keymap.rs new file mode 100644 index 00000000..36b2f360 --- /dev/null +++ b/src/wayland/protocols/keymap.rs @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use cosmic_protocols::keymap::v1::server::{ + zcosmic_keymap_manager_v1::{self, ZcosmicKeymapManagerV1}, + zcosmic_keymap_v1::{self, ZcosmicKeymapV1}, +}; +use smithay::{ + input::{ + keyboard::{KeyboardHandle, Layout}, + SeatHandler, + }, + reexports::wayland_server::{ + Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, + }, +}; +use std::mem; +use wayland_backend::server::{ClientId, GlobalId}; + +pub trait KeymapHandler { + fn keymap_state(&mut self) -> &mut KeymapState; +} + +#[derive(Debug)] +pub struct KeymapState { + pub global: GlobalId, + keymaps: Vec<(ZcosmicKeymapV1, Option)>, +} + +impl KeymapState { + pub fn new(dh: &DisplayHandle, client_filter: F) -> Self + where + D: GlobalDispatch + 'static, + F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static, + { + let global = dh.create_global::( + 1, + KeymapGlobalData { + filter: Box::new(client_filter), + }, + ); + KeymapState { + global, + keymaps: Vec::new(), + } + } + + pub fn refresh(state: &mut D) + where + D: SeatHandler + KeymapHandler + 'static, + { + let mut keymaps = mem::take(&mut state.keymap_state().keymaps); + for (keymap, last_layout) in &mut keymaps { + if let Some(data) = keymap.data::>() { + 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) { + keymap.group(active_layout.0); + *last_layout = Some(active_layout); + } + } + } + } + state.keymap_state().keymaps = keymaps; + } +} + +pub struct KeymapGlobalData { + filter: Box Fn(&'a Client) -> bool + Send + Sync>, +} + +impl GlobalDispatch for KeymapState +where + D: GlobalDispatch + + Dispatch + + 'static, +{ + fn bind( + _state: &mut D, + _handle: &DisplayHandle, + _client: &Client, + resource: New, + _global_data: &KeymapGlobalData, + data_init: &mut DataInit<'_, D>, + ) { + data_init.init(resource, ()); + } + + fn can_view(client: Client, global_data: &KeymapGlobalData) -> bool { + (global_data.filter)(&client) + } +} + +impl Dispatch for KeymapState +where + D: Dispatch, + D: Dispatch>, + D: 'static, + D: SeatHandler, + D: KeymapHandler, +{ + fn request( + state: &mut D, + _client: &Client, + _resource: &ZcosmicKeymapManagerV1, + request: zcosmic_keymap_manager_v1::Request, + _data: &(), + _dhandle: &DisplayHandle, + data_init: &mut DataInit<'_, D>, + ) { + match request { + zcosmic_keymap_manager_v1::Request::GetKeymap { keymap, keyboard } => { + let handle = KeyboardHandle::::from_resource(&keyboard); + let active_layout = handle.as_ref().map(|handle| { + handle.with_xkb_state(state, |context| { + context.xkb().lock().unwrap().active_layout() + }) + }); + let keymap = data_init.init(keymap, KeymapUserData { handle }); + if let Some(layout) = active_layout { + keymap.group(layout.0); + } + state.keymap_state().keymaps.push((keymap, active_layout)); + } + zcosmic_keymap_manager_v1::Request::Destroy => {} + _ => unreachable!(), + } + } +} + +#[doc(hidden)] +pub struct KeymapUserData { + handle: Option>, +} + +impl Dispatch, D> for KeymapState +where + D: Dispatch>, + D: 'static, + D: SeatHandler, + D: KeymapHandler, +{ + fn request( + state: &mut D, + _client: &Client, + _resource: &ZcosmicKeymapV1, + request: zcosmic_keymap_v1::Request, + data: &KeymapUserData, + _dhandle: &DisplayHandle, + _data_init: &mut DataInit<'_, D>, + ) { + match request { + zcosmic_keymap_v1::Request::SetGroup { group } => { + if let Some(handle) = data.handle.as_ref() { + handle.with_xkb_state(state, |mut context| { + context.set_layout(Layout(group)); + }); + } + } + zcosmic_keymap_v1::Request::Destroy => {} + _ => unreachable!(), + } + } + + fn destroyed( + state: &mut D, + _client: ClientId, + keymap: &ZcosmicKeymapV1, + _data: &KeymapUserData, + ) { + let keymaps = &mut state.keymap_state().keymaps; + if let Some(idx) = keymaps.iter().position(|(x, _)| x == keymap) { + keymaps.remove(idx); + } + } +} + +macro_rules! delegate_keymap { + ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => { + smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ + cosmic_protocols::keymap::v1::server::zcosmic_keymap_manager_v1::ZcosmicKeymapManagerV1: $crate::wayland::protocols::keymap::KeymapGlobalData + ] => $crate::wayland::protocols::keymap::KeymapState); + smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ + cosmic_protocols::keymap::v1::server::zcosmic_keymap_manager_v1::ZcosmicKeymapManagerV1: () + ] => $crate::wayland::protocols::keymap::KeymapState); + smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ + cosmic_protocols::keymap::v1::server::zcosmic_keymap_v1::ZcosmicKeymapV1: $crate::wayland::protocols::keymap::KeymapUserData<$ty> + ] => $crate::wayland::protocols::keymap::KeymapState); + }; +} +pub(crate) use delegate_keymap; diff --git a/src/wayland/protocols/mod.rs b/src/wayland/protocols/mod.rs index 3fe5285d..5e6923f7 100644 --- a/src/wayland/protocols/mod.rs +++ b/src/wayland/protocols/mod.rs @@ -3,6 +3,7 @@ pub mod atspi; pub mod drm; pub mod image_source; +pub mod keymap; pub mod output_configuration; pub mod output_power; pub mod overlap_notify;