Add cosmic_atspi_v1 protocol
Used to provide a backend for `AtspiDevice` in `at-spi2-core`, so Orca keybindings can work.
This commit is contained in:
parent
eb64fdaf8f
commit
90883c6ab1
9 changed files with 561 additions and 3 deletions
15
Cargo.lock
generated
15
Cargo.lock
generated
|
|
@ -833,6 +833,7 @@ dependencies = [
|
||||||
"profiling",
|
"profiling",
|
||||||
"rand",
|
"rand",
|
||||||
"regex",
|
"regex",
|
||||||
|
"reis",
|
||||||
"ron",
|
"ron",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
"rustix",
|
"rustix",
|
||||||
|
|
@ -854,7 +855,7 @@ dependencies = [
|
||||||
"xcursor",
|
"xcursor",
|
||||||
"xdg",
|
"xdg",
|
||||||
"xdg-user",
|
"xdg-user",
|
||||||
"xkbcommon 0.7.0",
|
"xkbcommon 0.8.0",
|
||||||
"zbus",
|
"zbus",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -898,7 +899,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cosmic-protocols"
|
name = "cosmic-protocols"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/pop-os/cosmic-protocols?branch=main#91aeb55052a8e6e15a7ddd53e039a9350f16fa69"
|
source = "git+https://github.com/pop-os/cosmic-protocols?branch=main#ec1616b90fa6b4568709cfe2c0627b1e8cc887e0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"wayland-backend",
|
"wayland-backend",
|
||||||
|
|
@ -4250,6 +4251,16 @@ version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "reis"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "827073dbe443c57fd72ae05491c6b94213218627ac6ac169850673b0cb7034f1"
|
||||||
|
dependencies = [
|
||||||
|
"calloop 0.14.1",
|
||||||
|
"rustix",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "renderdoc-sys"
|
name = "renderdoc-sys"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
|
|
||||||
|
|
@ -54,12 +54,13 @@ wayland-scanner = "0.31.1"
|
||||||
xcursor = "0.3.3"
|
xcursor = "0.3.3"
|
||||||
xdg = "^2.1"
|
xdg = "^2.1"
|
||||||
xdg-user = "0.2.1"
|
xdg-user = "0.2.1"
|
||||||
xkbcommon = "0.7"
|
xkbcommon = "0.8"
|
||||||
zbus = "4.4.0"
|
zbus = "4.4.0"
|
||||||
profiling = { version = "1.0" }
|
profiling = { version = "1.0" }
|
||||||
rustix = { version = "0.38.32", features = ["process"] }
|
rustix = { version = "0.38.32", features = ["process"] }
|
||||||
smallvec = "1.13.2"
|
smallvec = "1.13.2"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
|
reis = { version = "0.4", features = ["calloop"] }
|
||||||
drm-ffi = "0.8.0"
|
drm-ffi = "0.8.0"
|
||||||
|
|
||||||
[dependencies.id_tree]
|
[dependencies.id_tree]
|
||||||
|
|
|
||||||
|
|
@ -640,6 +640,7 @@ 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;
|
state.common.config.cosmic_conf.xkb_config = value;
|
||||||
}
|
}
|
||||||
"input_default" => {
|
"input_default" => {
|
||||||
|
|
|
||||||
|
|
@ -1464,6 +1464,13 @@ impl State {
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
self.common.atspi_ei.input(
|
||||||
|
modifiers,
|
||||||
|
&handle,
|
||||||
|
event.state(),
|
||||||
|
event.time() as u64 * 1000,
|
||||||
|
);
|
||||||
|
|
||||||
// Leave move overview mode, if any modifier was released
|
// Leave move overview mode, if any modifier was released
|
||||||
if let Some(Trigger::KeyboardMove(action_modifiers)) =
|
if let Some(Trigger::KeyboardMove(action_modifiers)) =
|
||||||
shell.overview_mode().0.active_trigger()
|
shell.overview_mode().0.active_trigger()
|
||||||
|
|
@ -1625,6 +1632,57 @@ impl State {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if event.state() == KeyState::Released {
|
||||||
|
let removed = self
|
||||||
|
.common
|
||||||
|
.atspi_ei
|
||||||
|
.active_virtual_mods
|
||||||
|
.remove(&event.key_code());
|
||||||
|
// If `Caps_Lock` is a virtual modifier, and is in locked state, clear it
|
||||||
|
if removed && handle.modified_sym() == Keysym::Caps_Lock {
|
||||||
|
if (modifiers.serialized.locked & 2) != 0 {
|
||||||
|
let serial = SERIAL_COUNTER.next_serial();
|
||||||
|
let time = self.common.clock.now().as_millis();
|
||||||
|
keyboard.input(
|
||||||
|
self,
|
||||||
|
event.key_code(),
|
||||||
|
KeyState::Pressed,
|
||||||
|
serial,
|
||||||
|
time,
|
||||||
|
|_, _, _| FilterResult::<()>::Forward,
|
||||||
|
);
|
||||||
|
let serial = SERIAL_COUNTER.next_serial();
|
||||||
|
keyboard.input(
|
||||||
|
self,
|
||||||
|
event.key_code(),
|
||||||
|
KeyState::Released,
|
||||||
|
serial,
|
||||||
|
time,
|
||||||
|
|_, _, _| FilterResult::<()>::Forward,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if event.state() == KeyState::Pressed
|
||||||
|
&& self
|
||||||
|
.common
|
||||||
|
.atspi_ei
|
||||||
|
.virtual_mods
|
||||||
|
.contains(&event.key_code())
|
||||||
|
{
|
||||||
|
self.common
|
||||||
|
.atspi_ei
|
||||||
|
.active_virtual_mods
|
||||||
|
.insert(event.key_code());
|
||||||
|
|
||||||
|
tracing::debug!(
|
||||||
|
"active virtual mods: {:?}",
|
||||||
|
self.common.atspi_ei.active_virtual_mods
|
||||||
|
);
|
||||||
|
seat.supressed_keys().add(&handle, None);
|
||||||
|
|
||||||
|
return FilterResult::Intercept(None);
|
||||||
|
}
|
||||||
|
|
||||||
// Skip released events for initially surpressed keys
|
// Skip released events for initially surpressed keys
|
||||||
if event.state() == KeyState::Released {
|
if event.state() == KeyState::Released {
|
||||||
if let Some(tokens) = seat.supressed_keys().filter(&handle) {
|
if let Some(tokens) = seat.supressed_keys().filter(&handle) {
|
||||||
|
|
@ -1649,6 +1707,15 @@ impl State {
|
||||||
return FilterResult::Intercept(None);
|
return FilterResult::Intercept(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.common.atspi_ei.has_keyboard_grab()
|
||||||
|
|| self
|
||||||
|
.common
|
||||||
|
.atspi_ei
|
||||||
|
.has_key_grab(modifiers.serialized.layout_effective, event.key_code())
|
||||||
|
{
|
||||||
|
return FilterResult::Intercept(None);
|
||||||
|
}
|
||||||
|
|
||||||
// handle the rest of the global shortcuts
|
// handle the rest of the global shortcuts
|
||||||
let mut clear_queue = true;
|
let mut clear_queue = true;
|
||||||
if !shortcuts_inhibited {
|
if !shortcuts_inhibited {
|
||||||
|
|
|
||||||
10
src/state.rs
10
src/state.rs
|
|
@ -12,6 +12,7 @@ use crate::{
|
||||||
shell::{grabs::SeatMoveGrabState, CosmicSurface, SeatExt, Shell},
|
shell::{grabs::SeatMoveGrabState, CosmicSurface, SeatExt, Shell},
|
||||||
utils::prelude::OutputExt,
|
utils::prelude::OutputExt,
|
||||||
wayland::protocols::{
|
wayland::protocols::{
|
||||||
|
atspi::AtspiState,
|
||||||
drm::WlDrmState,
|
drm::WlDrmState,
|
||||||
image_source::ImageSourceState,
|
image_source::ImageSourceState,
|
||||||
output_configuration::OutputConfigurationState,
|
output_configuration::OutputConfigurationState,
|
||||||
|
|
@ -229,6 +230,9 @@ pub struct Common {
|
||||||
pub xwayland_state: Option<XWaylandState>,
|
pub xwayland_state: Option<XWaylandState>,
|
||||||
pub xwayland_shell_state: XWaylandShellState,
|
pub xwayland_shell_state: XWaylandShellState,
|
||||||
pub pointer_focus_state: Option<PointerFocusState>,
|
pub pointer_focus_state: Option<PointerFocusState>,
|
||||||
|
|
||||||
|
pub atspi_state: AtspiState,
|
||||||
|
pub atspi_ei: crate::wayland::handlers::atspi::AtspiEiState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -559,6 +563,9 @@ impl State {
|
||||||
tracing::warn!(?err, "Failed to initialize dbus handlers");
|
tracing::warn!(?err, "Failed to initialize dbus handlers");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Restrict to only specific client?
|
||||||
|
let atspi_state = AtspiState::new::<State, _>(dh, client_is_privileged);
|
||||||
|
|
||||||
State {
|
State {
|
||||||
common: Common {
|
common: Common {
|
||||||
config,
|
config,
|
||||||
|
|
@ -615,6 +622,9 @@ impl State {
|
||||||
xwayland_state: None,
|
xwayland_state: None,
|
||||||
xwayland_shell_state,
|
xwayland_shell_state,
|
||||||
pointer_focus_state: None,
|
pointer_focus_state: None,
|
||||||
|
|
||||||
|
atspi_state,
|
||||||
|
atspi_ei: Default::default(),
|
||||||
},
|
},
|
||||||
backend: BackendData::Unset,
|
backend: BackendData::Unset,
|
||||||
ready: Once::new(),
|
ready: Once::new(),
|
||||||
|
|
|
||||||
304
src/wayland/handlers/atspi.rs
Normal file
304
src/wayland/handlers/atspi.rs
Normal file
|
|
@ -0,0 +1,304 @@
|
||||||
|
// 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::{CStr, CString},
|
||||||
|
mem,
|
||||||
|
os::unix::{io::AsFd, net::UnixStream},
|
||||||
|
};
|
||||||
|
use xkbcommon::xkb;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
state::State,
|
||||||
|
wayland::protocols::atspi::{delegate_atspi, AtspiHandler},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[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 = CStr::from_bytes_with_nul(b"eis-keymap\0").unwrap();
|
||||||
|
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);
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
pub mod alpha_modifier;
|
pub mod alpha_modifier;
|
||||||
|
pub mod atspi;
|
||||||
pub mod buffer;
|
pub mod buffer;
|
||||||
pub mod compositor;
|
pub mod compositor;
|
||||||
pub mod data_control;
|
pub mod data_control;
|
||||||
|
|
|
||||||
162
src/wayland/protocols/atspi.rs
Normal file
162
src/wayland/protocols/atspi.rs
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
// 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::{
|
||||||
|
backend::GlobalId, Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
pub mod atspi;
|
||||||
pub mod drm;
|
pub mod drm;
|
||||||
pub mod image_source;
|
pub mod image_source;
|
||||||
pub mod output_configuration;
|
pub mod output_configuration;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue