wayland: Remove atspi protocol

If we want to use the `org.freedesktop.a11y.KeyboardMonitor` protocol on
Pop!_OS, there is no need to support the Cosmic-specific protocol that
requires an `at-spi2-core` patch.
This commit is contained in:
Ian Douglas Scott 2025-11-14 14:45:36 -08:00 committed by Ian Douglas Scott
parent f065143d3e
commit a3904af03c
9 changed files with 8 additions and 519 deletions

11
Cargo.lock generated
View file

@ -853,7 +853,6 @@ dependencies = [
"profiling",
"rand 0.9.2",
"regex",
"reis",
"ron",
"rust-embed",
"rustix 1.1.2",
@ -4511,16 +4510,6 @@ version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "reis"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00939c5c526a1b4054ef8d9d96b3f92227f08ca355965e986741b556eda6d289"
dependencies = [
"calloop 0.14.3",
"rustix 0.38.44",
]
[[package]]
name = "renderdoc-sys"
version = "1.1.0"

View file

@ -82,7 +82,6 @@ zbus = "5.12.0"
profiling = { version = "1.0" }
rustix = { version = "1.1.2", features = ["process"] }
rand = "0.9.2"
reis = { version = "0.5", features = ["calloop"] }
# CLI arguments
clap_lex = "0.7"
parking_lot = "0.12.5"

View file

@ -805,7 +805,6 @@ fn config_changed(config: cosmic_config::Config, keys: Vec<String>, state: &mut
}
}
}
state.common.atspi_ei.update_keymap(value.clone());
state.common.config.cosmic_conf.xkb_config = value;
}
"keyboard_config" => {

View file

@ -1592,9 +1592,6 @@ impl State {
.unwrap_or(false)
});
self.common
.atspi_ei
.input(modifiers, &handle, event.state(), event.time() * 1000);
self.common
.a11y_keyboard_monitor_state
.key_event(modifiers, &handle, event.state());
@ -1762,12 +1759,7 @@ impl State {
}
if event.state() == KeyState::Released {
let mut removed = self
.common
.atspi_ei
.active_virtual_mods
.remove(&event.key_code());
removed |= self
let removed = self
.common
.a11y_keyboard_monitor_state
.remove_active_virtual_mod(handle.modified_sym());
@ -1797,27 +1789,20 @@ impl State {
);
}
} else if event.state() == KeyState::Pressed
&& (self
.common
.atspi_ei
.virtual_mods
.contains(&event.key_code())
|| self
&& self
.common
.a11y_keyboard_monitor_state
.has_virtual_mod(handle.modified_sym()))
.has_virtual_mod(handle.modified_sym())
{
self.common
.atspi_ei
.active_virtual_mods
.insert(event.key_code());
self.common
.a11y_keyboard_monitor_state
.add_active_virtual_mod(handle.modified_sym());
tracing::debug!(
"active virtual mods: {:?}",
self.common.atspi_ei.active_virtual_mods
self.common
.a11y_keyboard_monitor_state
.active_virtual_mods()
);
seat.supressed_keys().add(&handle, None);
@ -1848,12 +1833,7 @@ impl State {
return FilterResult::Intercept(None);
}
if self.common.atspi_ei.has_keyboard_grab()
|| self.common.a11y_keyboard_monitor_state.has_keyboard_grab()
|| self
.common
.atspi_ei
.has_key_grab(modifiers.serialized.layout_effective, event.key_code())
if self.common.a11y_keyboard_monitor_state.has_keyboard_grab()
|| self
.common
.a11y_keyboard_monitor_state

View file

@ -16,7 +16,6 @@ use crate::{
handlers::{data_device::get_dnd_icon, screencopy::SessionHolder},
protocols::{
a11y::A11yState,
atspi::AtspiState,
corner_radius::CornerRadiusState,
drm::WlDrmState,
image_capture_source::ImageCaptureSourceState,
@ -271,9 +270,6 @@ pub struct Common {
pub xwayland_shell_state: XWaylandShellState,
pub pointer_focus_state: Option<PointerFocusState>,
pub atspi_state: AtspiState,
pub atspi_ei: crate::wayland::handlers::atspi::AtspiEiState,
#[cfg(feature = "systemd")]
pub inhibit_lid_fd: Option<OwnedFd>,
}
@ -708,9 +704,6 @@ impl State {
let a11y_keyboard_monitor_state = A11yKeyboardMonitorState::new(&async_executor);
// TODO: Restrict to only specific client?
let atspi_state = AtspiState::new::<State, _>(dh, client_has_no_security_context);
State {
common: Common {
config,
@ -774,9 +767,6 @@ impl State {
xwayland_shell_state,
pointer_focus_state: None,
atspi_state,
atspi_ei: Default::default(),
#[cfg(feature = "systemd")]
inhibit_lid_fd: None,
},

View file

@ -1,304 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-only
use cosmic_comp_config::XkbConfig;
use cosmic_protocols::atspi::v1::server::cosmic_atspi_manager_v1::CosmicAtspiManagerV1;
use reis::{
calloop::{EisRequestSource, EisRequestSourceEvent},
eis::{self, device::DeviceType},
request::{Connection, Device, DeviceCapability, EisRequest, Seat},
};
use smithay::{
backend::input::{KeyState, Keycode},
input::keyboard::ModifiersState,
utils::SealedFile,
};
use std::{
collections::{HashMap, HashSet},
ffi::CString,
mem,
os::unix::{io::AsFd, net::UnixStream},
};
use xkbcommon::xkb;
use crate::{
state::State,
wayland::protocols::atspi::{AtspiHandler, delegate_atspi},
};
#[derive(PartialEq, Debug)]
pub struct AtspiKeyGrab {
pub mods: u32,
pub virtual_mods: HashSet<Keycode>,
pub key: Keycode,
}
#[derive(Debug, Default)]
struct AtspiClient {
key_grabs: Vec<AtspiKeyGrab>,
has_keyboard_grab: bool,
// TODO: purge old instances
keyboards: Vec<(Connection, Device, eis::Keyboard)>,
}
impl AtspiClient {
fn add_keyboard(
&mut self,
connection: &Connection,
seat: &Seat,
keymap: &xkb::Keymap,
modifiers: &ModifiersState,
) {
let keymap_text = keymap.get_as_string(xkb::KEYMAP_FORMAT_TEXT_V1);
let name = c"eis-keymap";
let file = SealedFile::with_content(name, &CString::new(keymap_text).unwrap()).unwrap();
let device = seat.add_device(
Some("keyboard"),
DeviceType::Virtual,
&[DeviceCapability::Keyboard],
|device| {
let keyboard = device.interface::<eis::Keyboard>().unwrap();
keyboard.keymap(
eis::keyboard::KeymapType::Xkb,
file.size() as u32 - 1,
file.as_fd(),
);
},
);
device.resumed();
let keyboard = device.interface::<eis::Keyboard>().unwrap();
connection.with_next_serial(|serial| {
keyboard.modifiers(
serial,
modifiers.serialized.depressed,
modifiers.serialized.locked,
modifiers.serialized.latched,
modifiers.serialized.layout_effective,
)
});
device.start_emulating(0);
self.keyboards.push((connection.clone(), device, keyboard));
}
}
#[derive(Debug, Default)]
pub struct AtspiEiState {
modifiers: ModifiersState,
clients: HashMap<CosmicAtspiManagerV1, AtspiClient>,
pub virtual_mods: HashSet<Keycode>,
pub active_virtual_mods: HashSet<Keycode>,
}
impl AtspiEiState {
pub fn input(
&mut self,
modifiers: &smithay::input::keyboard::ModifiersState,
keysym: &smithay::input::keyboard::KeysymHandle,
state: KeyState,
time: u64,
) {
let state = match state {
KeyState::Pressed => eis::keyboard::KeyState::Press,
KeyState::Released => eis::keyboard::KeyState::Released,
};
if &self.modifiers != modifiers {
self.modifiers = *modifiers;
for client in self.clients.values() {
for (connection, _, keyboard) in &client.keyboards {
connection.with_next_serial(|serial| {
keyboard.modifiers(
serial,
modifiers.serialized.depressed,
modifiers.serialized.locked,
modifiers.serialized.latched,
modifiers.serialized.layout_effective,
)
});
}
}
}
for client in self.clients.values() {
for (connection, device, keyboard) in &client.keyboards {
keyboard.key(keysym.raw_code().raw() - 8, state);
device.frame(time);
let _ = connection.flush();
}
}
}
pub fn has_keyboard_grab(&self) -> bool {
self.clients.values().any(|client| client.has_keyboard_grab)
}
/// Key grab exists for mods, key, with active virtual mods
pub fn has_key_grab(&self, mods: u32, key: Keycode) -> bool {
self.clients
.values()
.flat_map(|client| &client.key_grabs)
.any(|grab| {
grab.mods == mods
&& grab.virtual_mods == self.active_virtual_mods
&& grab.key == key
})
}
fn update_virtual_mods(&mut self) {
self.virtual_mods.clear();
self.virtual_mods.extend(
self.clients
.values()
.flat_map(|client| &client.key_grabs)
.flat_map(|grab| &grab.virtual_mods),
);
}
pub fn update_keymap(&mut self, xkb_config: XkbConfig) {
let keymap = keymap_or_default(xkb_config);
for client in self.clients.values_mut() {
let old_keyboards = mem::take(&mut client.keyboards);
for (connection, device, _keyboard) in old_keyboards {
device.remove();
client.add_keyboard(&connection, device.seat(), &keymap, &self.modifiers);
let _ = connection.flush();
}
}
}
}
impl AtspiHandler for State {
fn client_connected(&mut self, manager: &CosmicAtspiManagerV1, socket: UnixStream) {
self.common
.atspi_ei
.clients
.insert(manager.clone(), AtspiClient::default());
let context = eis::Context::new(socket).unwrap();
let source = EisRequestSource::new(context, 0);
let manager = manager.clone();
self.common
.event_loop_handle
.insert_source(source, move |event, connected_state, state| {
Ok(handle_event(&manager, event, connected_state, state))
})
.unwrap();
}
fn client_disconnected(&mut self, manager: &CosmicAtspiManagerV1) {
self.common.atspi_ei.clients.remove(manager);
self.common.atspi_ei.update_virtual_mods();
}
fn add_key_grab(
&mut self,
manager: &CosmicAtspiManagerV1,
mods: u32,
virtual_mods: Vec<Keycode>,
key: Keycode,
) {
let grab = AtspiKeyGrab {
mods,
virtual_mods: virtual_mods.into_iter().collect(),
key,
};
let client = self.common.atspi_ei.clients.get_mut(manager).unwrap();
client.key_grabs.push(grab);
self.common.atspi_ei.update_virtual_mods();
}
fn remove_key_grab(
&mut self,
manager: &CosmicAtspiManagerV1,
mods: u32,
virtual_mods: Vec<Keycode>,
key: Keycode,
) {
let grab = AtspiKeyGrab {
mods,
virtual_mods: virtual_mods.into_iter().collect(),
key,
};
let client = self.common.atspi_ei.clients.get_mut(manager).unwrap();
if let Some(idx) = client.key_grabs.iter().position(|x| *x == grab) {
client.key_grabs.remove(idx);
}
self.common.atspi_ei.update_virtual_mods();
}
fn grab_keyboard(&mut self, manager: &CosmicAtspiManagerV1) {
let client = self.common.atspi_ei.clients.get_mut(manager).unwrap();
client.has_keyboard_grab = true;
}
fn ungrab_keyboard(&mut self, manager: &CosmicAtspiManagerV1) {
let client = self.common.atspi_ei.clients.get_mut(manager).unwrap();
client.has_keyboard_grab = false;
}
}
fn handle_event(
manager: &CosmicAtspiManagerV1,
event: Result<EisRequestSourceEvent, reis::Error>,
connection: &Connection,
state: &mut State,
) -> calloop::PostAction {
let Some(client) = state.common.atspi_ei.clients.get_mut(manager) else {
return calloop::PostAction::Remove;
};
match event {
Ok(EisRequestSourceEvent::Connected) => {
if connection.context_type() != reis::ei::handshake::ContextType::Receiver {
return calloop::PostAction::Remove;
}
let _seat = connection.add_seat(Some("default"), &[DeviceCapability::Keyboard]);
}
Ok(EisRequestSourceEvent::Request(EisRequest::Disconnect)) => {
return calloop::PostAction::Remove;
}
Ok(EisRequestSourceEvent::Request(EisRequest::Bind(request))) => {
if connection.has_interface("ei_keyboard")
&& request.capabilities & (2 << DeviceCapability::Keyboard as u64) != 0
{
let keymap = keymap_or_default(state.common.config.xkb_config());
client.add_keyboard(
connection,
&request.seat,
&keymap,
&state.common.atspi_ei.modifiers,
);
}
}
Ok(EisRequestSourceEvent::Request(_request)) => {
// seat / keyboard / device release?
}
Ok(EisRequestSourceEvent::InvalidObject(_)) => {}
Err(_) => {
// TODO
}
}
let _ = connection.flush();
calloop::PostAction::Continue
}
// TODO: use keymap of seat?
fn keymap_or_default(xkb_config: XkbConfig) -> xkb::Keymap {
keymap(xkb_config).unwrap_or_else(|| keymap(XkbConfig::default()).unwrap())
}
fn keymap(xkb_config: XkbConfig) -> Option<xkb::Keymap> {
let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
xkb::Keymap::new_from_names(
&context,
&xkb_config.rules,
&xkb_config.model,
&xkb_config.layout,
&xkb_config.variant,
xkb_config.options.clone(),
xkb::KEYMAP_COMPILE_NO_FLAGS,
)
}
delegate_atspi!(State);

View file

@ -2,7 +2,6 @@
pub mod a11y;
pub mod alpha_modifier;
pub mod atspi;
pub mod buffer;
pub mod compositor;
pub mod corner_radius;

View file

@ -1,162 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-only
use cosmic_protocols::atspi::v1::server::cosmic_atspi_manager_v1;
use smithay::{
backend::input::Keycode,
reexports::wayland_server::{
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, backend::GlobalId,
},
};
use std::os::unix::{io::AsFd, net::UnixStream};
use wayland_backend::server::ClientId;
pub trait AtspiHandler {
fn client_connected(
&mut self,
manager: &cosmic_atspi_manager_v1::CosmicAtspiManagerV1,
key_event_socket: UnixStream,
);
fn client_disconnected(&mut self, manager: &cosmic_atspi_manager_v1::CosmicAtspiManagerV1);
fn add_key_grab(
&mut self,
manager: &cosmic_atspi_manager_v1::CosmicAtspiManagerV1,
mods: u32,
virtual_mods: Vec<Keycode>,
key: Keycode,
);
fn remove_key_grab(
&mut self,
manager: &cosmic_atspi_manager_v1::CosmicAtspiManagerV1,
mods: u32,
virtual_mods: Vec<Keycode>,
key: Keycode,
);
fn grab_keyboard(&mut self, manager: &cosmic_atspi_manager_v1::CosmicAtspiManagerV1);
fn ungrab_keyboard(&mut self, manager: &cosmic_atspi_manager_v1::CosmicAtspiManagerV1);
}
#[derive(Debug)]
pub struct AtspiState {
global: GlobalId,
}
impl AtspiState {
pub fn new<D, F>(dh: &DisplayHandle, client_filter: F) -> AtspiState
where
D: GlobalDispatch<cosmic_atspi_manager_v1::CosmicAtspiManagerV1, AtspiGlobalData> + 'static,
F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static,
{
let global = dh.create_global::<D, cosmic_atspi_manager_v1::CosmicAtspiManagerV1, _>(
1,
AtspiGlobalData {
filter: Box::new(client_filter),
},
);
AtspiState { global }
}
pub fn global_id(&self) -> GlobalId {
self.global.clone()
}
}
pub struct AtspiGlobalData {
filter: Box<dyn for<'a> Fn(&'a Client) -> bool + Send + Sync>,
}
impl<D> GlobalDispatch<cosmic_atspi_manager_v1::CosmicAtspiManagerV1, AtspiGlobalData, D>
for AtspiState
where
D: GlobalDispatch<cosmic_atspi_manager_v1::CosmicAtspiManagerV1, AtspiGlobalData>
+ Dispatch<cosmic_atspi_manager_v1::CosmicAtspiManagerV1, ()>
+ AtspiHandler
+ 'static,
{
fn bind(
state: &mut D,
_dh: &DisplayHandle,
_client: &Client,
resource: New<cosmic_atspi_manager_v1::CosmicAtspiManagerV1>,
_global_data: &AtspiGlobalData,
data_init: &mut DataInit<'_, D>,
) {
let instance = data_init.init(resource, ());
let (client_socket, server_socket) = UnixStream::pair().unwrap();
state.client_connected(&instance, server_socket);
instance.key_events_eis(client_socket.as_fd());
}
fn can_view(client: Client, global_data: &AtspiGlobalData) -> bool {
(global_data.filter)(&client)
}
}
impl<D> Dispatch<cosmic_atspi_manager_v1::CosmicAtspiManagerV1, (), D> for AtspiState
where
D: Dispatch<cosmic_atspi_manager_v1::CosmicAtspiManagerV1, ()> + AtspiHandler + 'static,
{
fn request(
state: &mut D,
_client: &Client,
manager: &cosmic_atspi_manager_v1::CosmicAtspiManagerV1,
request: cosmic_atspi_manager_v1::Request,
_data: &(),
_dh: &DisplayHandle,
_data_init: &mut DataInit<'_, D>,
) {
match request {
cosmic_atspi_manager_v1::Request::AddKeyGrab {
mods,
virtual_mods,
key,
} => {
let virtual_mods = virtual_mods
.chunks_exact(4)
.map(|x| (u32::from_ne_bytes(<[u8; 4]>::try_from(x).unwrap()) + 8).into())
.collect();
state.add_key_grab(manager, mods, virtual_mods, (key + 8).into());
}
cosmic_atspi_manager_v1::Request::RemoveKeyGrab {
mods,
virtual_mods,
key,
} => {
let virtual_mods = virtual_mods
.chunks_exact(4)
.map(|x| (u32::from_ne_bytes(<[u8; 4]>::try_from(x).unwrap()) + 8).into())
.collect();
state.remove_key_grab(manager, mods, virtual_mods, (key + 8).into());
}
cosmic_atspi_manager_v1::Request::GrabKeyboard => {
state.grab_keyboard(manager);
}
cosmic_atspi_manager_v1::Request::UngrabKeyboard => {
state.ungrab_keyboard(manager);
}
cosmic_atspi_manager_v1::Request::Destroy => {}
_ => unreachable!(),
}
}
fn destroyed(
state: &mut D,
_client: ClientId,
manager: &cosmic_atspi_manager_v1::CosmicAtspiManagerV1,
_data: &(),
) {
state.client_disconnected(manager);
}
}
macro_rules! delegate_atspi {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
cosmic_protocols::atspi::v1::server::cosmic_atspi_manager_v1::CosmicAtspiManagerV1: $crate::wayland::protocols::atspi::AtspiGlobalData
] => $crate::wayland::protocols::atspi::AtspiState);
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
cosmic_protocols::atspi::v1::server::cosmic_atspi_manager_v1::CosmicAtspiManagerV1: ()
] => $crate::wayland::protocols::atspi::AtspiState);
};
}
pub(crate) use delegate_atspi;

View file

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