Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP Add cosmic-keymap-unstable-v1 protocol #660

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
6 changes: 5 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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() {
Expand Down
4 changes: 4 additions & 0 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::{
atspi::AtspiState,
drm::WlDrmState,
image_source::ImageSourceState,
keymap::KeymapState,
output_configuration::OutputConfigurationState,
output_power::OutputPowerState,
screencopy::ScreencopyState,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -522,6 +524,7 @@ impl State {
VirtualKeyboardManagerState::new::<State, _>(&dh, client_is_privileged);
AlphaModifierState::new::<Self>(&dh);
SinglePixelBufferState::new::<Self>(&dh);
let keymap_state = KeymapState::new::<State, _>(&dh, client_is_privileged);

let idle_notifier_state = IdleNotifierState::<Self>::new(&dh, handle.clone());
let idle_inhibit_manager_state = IdleInhibitManagerState::new::<State>(&dh);
Expand Down Expand Up @@ -625,6 +628,7 @@ impl State {
xwayland_state: None,
xwayland_shell_state,
pointer_focus_state: None,
keymap_state,

atspi_state,
atspi_ei: Default::default(),
Expand Down
12 changes: 12 additions & 0 deletions src/wayland/handlers/keymap.rs
Original file line number Diff line number Diff line change
@@ -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);
1 change: 1 addition & 0 deletions src/wayland/handlers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
192 changes: 192 additions & 0 deletions src/wayland/protocols/keymap.rs
Original file line number Diff line number Diff line change
@@ -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<Layout>)>,
}

impl KeymapState {
pub fn new<D, F>(dh: &DisplayHandle, client_filter: F) -> Self
where
D: GlobalDispatch<ZcosmicKeymapManagerV1, KeymapGlobalData> + 'static,
F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static,
{
let global = dh.create_global::<D, ZcosmicKeymapManagerV1, _>(
1,
KeymapGlobalData {
filter: Box::new(client_filter),
},
);
KeymapState {
global,
keymaps: Vec::new(),
}
}

pub fn refresh<D>(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::<KeymapUserData<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) {
keymap.group(active_layout.0);
*last_layout = Some(active_layout);
}
}
}
}
state.keymap_state().keymaps = keymaps;
}
}

pub struct KeymapGlobalData {
filter: Box<dyn for<'a> Fn(&'a Client) -> bool + Send + Sync>,
}

impl<D> GlobalDispatch<ZcosmicKeymapManagerV1, KeymapGlobalData, D> for KeymapState
where
D: GlobalDispatch<ZcosmicKeymapManagerV1, KeymapGlobalData>
+ Dispatch<ZcosmicKeymapManagerV1, ()>
+ 'static,
{
fn bind(
_state: &mut D,
_handle: &DisplayHandle,
_client: &Client,
resource: New<ZcosmicKeymapManagerV1>,
_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<D> Dispatch<ZcosmicKeymapManagerV1, (), D> for KeymapState
where
D: Dispatch<ZcosmicKeymapManagerV1, ()>,
D: Dispatch<ZcosmicKeymapV1, KeymapUserData<D>>,
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::<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 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<D: SeatHandler> {
handle: Option<KeyboardHandle<D>>,
}

impl<D> Dispatch<ZcosmicKeymapV1, KeymapUserData<D>, D> for KeymapState
where
D: Dispatch<ZcosmicKeymapV1, KeymapUserData<D>>,
D: 'static,
D: SeatHandler,
D: KeymapHandler,
{
fn request(
state: &mut D,
_client: &Client,
_resource: &ZcosmicKeymapV1,
request: zcosmic_keymap_v1::Request,
data: &KeymapUserData<D>,
_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<D>,
) {
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;
1 change: 1 addition & 0 deletions src/wayland/protocols/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading