wayland: Implement cosmic-a11y-v1

This commit is contained in:
Victoria Brekenfeld 2025-02-17 20:30:26 +01:00 committed by Victoria Brekenfeld
parent 7af3650b83
commit c45a58c16c
8 changed files with 223 additions and 22 deletions

14
Cargo.lock generated
View file

@ -786,7 +786,7 @@ dependencies = [
[[package]]
name = "cosmic-client-toolkit"
version = "0.1.0"
source = "git+https://github.com/pop-os/cosmic-protocols?rev=178eb0b#178eb0b14a0e5c192f64f6dee6c40341a8e5ee51"
source = "git+https://github.com/pop-os//cosmic-protocols?rev=ed2a481#ed2a48143cd6dc63274aa04461de3d341e50b16b"
dependencies = [
"cosmic-protocols",
"libc",
@ -906,7 +906,7 @@ dependencies = [
[[package]]
name = "cosmic-protocols"
version = "0.1.0"
source = "git+https://github.com/pop-os/cosmic-protocols?rev=178eb0b#178eb0b14a0e5c192f64f6dee6c40341a8e5ee51"
source = "git+https://github.com/pop-os//cosmic-protocols?rev=ed2a481#ed2a48143cd6dc63274aa04461de3d341e50b16b"
dependencies = [
"bitflags 2.7.0",
"wayland-backend",
@ -1466,7 +1466,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -2911,7 +2911,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [
"cfg-if",
"windows-targets 0.48.5",
"windows-targets 0.52.6",
]
[[package]]
@ -4385,7 +4385,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -4985,7 +4985,7 @@ dependencies = [
"getrandom",
"once_cell",
"rustix",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -5278,7 +5278,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69fff37da548239c3bf9e64a12193d261e8b22b660991c6fd2df057c168f435f"
dependencies = [
"cc",
"windows-targets 0.48.5",
"windows-targets 0.52.6",
]
[[package]]

View file

@ -17,7 +17,7 @@ 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", rev = "178eb0b", default-features = false, features = ["server"]}
cosmic-protocols = {git = "https://github.com/pop-os/cosmic-protocols", rev = "ed2a481", default-features = false, features = ["server"]}
cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daemon" }
libdisplay-info = "0.2.0"
egui = {version = "0.30.0", optional = true}
@ -115,4 +115,8 @@ inherits = "release"
lto = "fat"
[patch.crates-io]
smithay = { git = "https://github.com/smithay/smithay.git", rev = "f93476c" }
smithay = { git = "https://github.com/smithay/smithay.git", rev = "f93476c" }
[patch."https://github.com/pop-os/cosmic-protocols"]
cosmic-protocols = { git = "https://github.com/pop-os//cosmic-protocols", rev = "ed2a481" }
cosmic-client-toolkit = { git = "https://github.com/pop-os//cosmic-protocols", rev = "ed2a481" }

View file

@ -2034,6 +2034,14 @@ impl Shell {
1.
};
let toggled = previous_level != level && (previous_level == 1.0 || level == 1.0);
if toggled {
let value = previous_level == 1.0;
let _ = loop_handle.insert_idle(move |state| {
state.common.a11y_state.set_screen_magnifier(value);
});
}
self.zoom_state = Some(ZoomState {
seat: seat.clone(),
level,

View file

@ -11,18 +11,21 @@ use crate::{
input::{gestures::GestureState, PointerFocusState},
shell::{grabs::SeatMoveGrabState, CosmicSurface, SeatExt, Shell},
utils::prelude::OutputExt,
wayland::handlers::screencopy::SessionHolder,
wayland::protocols::{
atspi::AtspiState,
drm::WlDrmState,
image_source::ImageSourceState,
output_configuration::OutputConfigurationState,
output_power::OutputPowerState,
overlap_notify::OverlapNotifyState,
screencopy::ScreencopyState,
toplevel_info::ToplevelInfoState,
toplevel_management::{ManagementCapabilities, ToplevelManagementState},
workspace::{WorkspaceClientState, WorkspaceState, WorkspaceUpdateGuard},
wayland::{
handlers::screencopy::SessionHolder,
protocols::{
a11y::A11yState,
atspi::AtspiState,
drm::WlDrmState,
image_source::ImageSourceState,
output_configuration::OutputConfigurationState,
output_power::OutputPowerState,
overlap_notify::OverlapNotifyState,
screencopy::ScreencopyState,
toplevel_info::ToplevelInfoState,
toplevel_management::{ManagementCapabilities, ToplevelManagementState},
workspace::{WorkspaceClientState, WorkspaceState, WorkspaceUpdateGuard},
},
},
xwayland::XWaylandState,
};
@ -223,6 +226,7 @@ pub struct Common {
pub kde_decoration_state: KdeDecorationState,
pub xdg_decoration_state: XdgDecorationState,
pub overlap_notify_state: OverlapNotifyState,
pub a11y_state: A11yState,
// shell-related wayland state
pub xdg_shell_state: XdgShellState,
@ -581,8 +585,10 @@ impl State {
tracing::warn!(?err, "Failed to initialize dbus handlers");
}
let a11y_state = A11yState::new::<State, _>(dh, client_is_privileged);
// TODO: Restrict to only specific client?
let atspi_state = AtspiState::new::<State, _>(dh, client_is_privileged);
let atspi_state = AtspiState::new::<State, _>(dh, |_| true);
State {
common: Common {
@ -638,6 +644,7 @@ impl State {
xdg_activation_state,
xdg_foreign_state,
workspace_state,
a11y_state,
xwayland_scale: None,
xwayland_state: None,
xwayland_shell_state,

View file

@ -0,0 +1,38 @@
use crate::{
state::State,
wayland::protocols::a11y::{delegate_a11y, A11yHandler, A11yState},
};
impl A11yHandler for State {
fn a11y_state(&mut self) -> &mut A11yState {
&mut self.common.a11y_state
}
fn request_screen_magnifier(&mut self, enabled: bool) {
let mut shell = self.common.shell.write().unwrap();
if shell
.zoom_state()
.is_some_and(|state| state.current_level() != 1.0)
!= enabled
{
let seat = shell.seats.last_active().clone();
let level = if enabled {
self.common.config.dynamic_conf.zoom_state().last_level
} else {
1.0
};
let zoom_config = &self.common.config.cosmic_conf.accessibility_zoom;
shell.trigger_zoom(
&seat,
level,
zoom_config,
true,
&self.common.event_loop_handle,
);
}
}
}
delegate_a11y!(State);

View file

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
pub mod a11y;
pub mod alpha_modifier;
pub mod atspi;
pub mod buffer;

View file

@ -0,0 +1,142 @@
// SPDX-License-Identifier: GPL-3.0-only
use cosmic_protocols::a11y::v1::server::cosmic_a11y_manager_v1;
use smithay::reexports::wayland_server::{
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New,
};
use wayland_backend::server::GlobalId;
pub trait A11yHandler {
fn a11y_state(&mut self) -> &mut A11yState;
fn request_screen_magnifier(&mut self, enabled: bool);
}
#[derive(Debug)]
pub struct A11yState {
global: GlobalId,
instances: Vec<cosmic_a11y_manager_v1::CosmicA11yManagerV1>,
magnifier_state: bool,
}
impl A11yState {
pub fn new<D, F>(dh: &DisplayHandle, client_filter: F) -> A11yState
where
D: GlobalDispatch<cosmic_a11y_manager_v1::CosmicA11yManagerV1, A11yGlobalData> + 'static,
F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static,
{
let global = dh.create_global::<D, cosmic_a11y_manager_v1::CosmicA11yManagerV1, _>(
1,
A11yGlobalData {
filter: Box::new(client_filter),
},
);
A11yState {
global,
instances: Vec::new(),
magnifier_state: false,
}
}
pub fn global_id(&self) -> GlobalId {
self.global.clone()
}
pub fn set_screen_magnifier(&mut self, enabled: bool) {
self.magnifier_state = enabled;
for instance in &self.instances {
instance.magnifier(if enabled {
cosmic_a11y_manager_v1::ActiveState::Enabled
} else {
cosmic_a11y_manager_v1::ActiveState::Disabled
});
}
}
}
pub struct A11yGlobalData {
filter: Box<dyn for<'a> Fn(&'a Client) -> bool + Send + Sync>,
}
impl<D> GlobalDispatch<cosmic_a11y_manager_v1::CosmicA11yManagerV1, A11yGlobalData, D> for A11yState
where
D: GlobalDispatch<cosmic_a11y_manager_v1::CosmicA11yManagerV1, A11yGlobalData>
+ Dispatch<cosmic_a11y_manager_v1::CosmicA11yManagerV1, ()>
+ A11yHandler
+ 'static,
{
fn bind(
state: &mut D,
_handle: &DisplayHandle,
_client: &Client,
resource: New<cosmic_a11y_manager_v1::CosmicA11yManagerV1>,
_global_data: &A11yGlobalData,
data_init: &mut DataInit<'_, D>,
) {
let instance = data_init.init(resource, ());
let state = state.a11y_state();
instance.magnifier(if state.magnifier_state {
cosmic_a11y_manager_v1::ActiveState::Enabled
} else {
cosmic_a11y_manager_v1::ActiveState::Disabled
});
state.instances.push(instance);
}
fn can_view(client: Client, global_data: &A11yGlobalData) -> bool {
(global_data.filter)(&client)
}
}
impl<D> Dispatch<cosmic_a11y_manager_v1::CosmicA11yManagerV1, (), D> for A11yState
where
D: Dispatch<cosmic_a11y_manager_v1::CosmicA11yManagerV1, ()> + A11yHandler + 'static,
{
fn request(
state: &mut D,
_client: &Client,
_resource: &cosmic_a11y_manager_v1::CosmicA11yManagerV1,
request: <cosmic_a11y_manager_v1::CosmicA11yManagerV1 as smithay::reexports::wayland_server::Resource>::Request,
_data: &(),
_dhandle: &DisplayHandle,
_data_init: &mut DataInit<'_, D>,
) {
match request {
cosmic_a11y_manager_v1::Request::SetMagnifier { active } => {
state.request_screen_magnifier(
active
.into_result()
.unwrap_or(cosmic_a11y_manager_v1::ActiveState::Disabled)
== cosmic_a11y_manager_v1::ActiveState::Enabled,
);
}
_ => unreachable!(),
}
}
fn destroyed(
state: &mut D,
_client: wayland_backend::server::ClientId,
resource: &cosmic_a11y_manager_v1::CosmicA11yManagerV1,
_data: &(),
) {
state.a11y_state().instances.retain(|i| i != resource);
}
}
macro_rules! delegate_a11y {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
cosmic_protocols::a11y::v1::server::cosmic_a11y_manager_v1::CosmicA11yManagerV1: $crate::wayland::protocols::a11y::A11yGlobalData
] => $crate::wayland::protocols::a11y::A11yState);
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
cosmic_protocols::a11y::v1::server::cosmic_a11y_manager_v1::CosmicA11yManagerV1: ()
] => $crate::wayland::protocols::a11y::A11yState);
};
}
pub(crate) use delegate_a11y;

View file

@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only
pub mod a11y;
pub mod atspi;
pub mod drm;
pub mod image_source;