Skip to content

Commit cfffaac

Browse files
committed
WIP org.freedesktop.a11y.Manager dbus protocol
unused code url unichar store set grabs KeyGrab key grabs virtual_mods Fix deadlock
1 parent b5d5479 commit cfffaac

File tree

6 files changed

+302
-2
lines changed

6 files changed

+302
-2
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ rand = "0.9.0"
6565
reis = { version = "0.5", features = ["calloop"] }
6666
# CLI arguments
6767
clap_lex = "0.7"
68+
futures-executor = "0.3.31"
6869

6970
[dependencies.id_tree]
7071
branch = "feature/copy_clone"

src/dbus/a11y_keyboard_monitor.rs

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
// https://gitlab.gnome.org/GNOME/mutter/-/blob/main/data/dbus-interfaces/org.freedesktop.a11y.xml
2+
//
3+
// TODO: Restrict protocol acccess?
4+
// TODO remove client when not connected to server
5+
6+
use futures_executor::ThreadPool;
7+
use smithay::backend::input::KeyState;
8+
use std::collections::HashMap;
9+
use std::collections::HashSet;
10+
use std::sync::OnceLock;
11+
use std::sync::{Arc, Mutex};
12+
use xkbcommon::xkb::{self, Keysym};
13+
use zbus::message::Header;
14+
use zbus::names::UniqueName;
15+
use zbus::object_server::SignalEmitter;
16+
17+
// As defined in at-spi2-core
18+
const ATSPI_DEVICE_A11Y_MANAGER_VIRTUAL_MOD_START: u32 = 15;
19+
20+
#[derive(PartialEq, Eq, Debug)]
21+
struct KeyGrab {
22+
pub mods: u32,
23+
pub virtual_mods: HashSet<Keysym>,
24+
pub key: Keysym,
25+
}
26+
27+
impl KeyGrab {
28+
fn new(virtual_mods: &[Keysym], key: Keysym, raw_mods: u32) -> Self {
29+
let mods = raw_mods & ((1 << ATSPI_DEVICE_A11Y_MANAGER_VIRTUAL_MOD_START) - 1);
30+
let virtual_mods = virtual_mods
31+
.iter()
32+
.copied()
33+
.enumerate()
34+
.filter(|(i, _)| {
35+
raw_mods & (1 << (ATSPI_DEVICE_A11Y_MANAGER_VIRTUAL_MOD_START + *i as u32)) != 0
36+
})
37+
.map(|(_, x)| x)
38+
.collect();
39+
Self {
40+
mods,
41+
virtual_mods,
42+
key,
43+
}
44+
}
45+
}
46+
47+
#[derive(Debug, Default)]
48+
struct Client {
49+
grabbed: bool,
50+
watched: bool,
51+
virtual_mods: HashSet<Keysym>,
52+
key_grabs: Vec<KeyGrab>,
53+
}
54+
55+
#[derive(Debug, Default)]
56+
struct Clients(HashMap<UniqueName<'static>, Client>);
57+
58+
impl Clients {
59+
fn get(&mut self, name: &UniqueName<'_>) -> &mut Client {
60+
self.0.entry(name.to_owned()).or_default()
61+
}
62+
63+
fn remove(&mut self, name: &UniqueName<'_>) -> bool {
64+
self.0.remove(&name.to_owned()).is_some()
65+
}
66+
}
67+
68+
#[derive(Debug)]
69+
pub struct A11yKeyboardMonitorState {
70+
executor: ThreadPool,
71+
clients: Arc<Mutex<Clients>>,
72+
active_virtual_mods: HashSet<Keysym>,
73+
conn: Arc<OnceLock<zbus::Connection>>,
74+
}
75+
76+
impl A11yKeyboardMonitorState {
77+
pub fn new() -> Self {
78+
let clients = Arc::new(Mutex::new(Clients::default()));
79+
let clients_clone = clients.clone();
80+
let executor = ThreadPool::builder().pool_size(1).create().unwrap();
81+
let conn_cell = Arc::new(OnceLock::new());
82+
let conn_cell_clone = conn_cell.clone();
83+
executor.spawn_ok(async move {
84+
match serve(clients_clone).await {
85+
Ok(conn) => {
86+
conn_cell_clone.set(conn).unwrap();
87+
}
88+
Err(err) => {
89+
tracing::error!("Failed to serve `org.freedesktop.a11y.Manager`: {err}");
90+
}
91+
}
92+
});
93+
Self {
94+
clients,
95+
executor,
96+
active_virtual_mods: HashSet::new(),
97+
conn: conn_cell,
98+
}
99+
}
100+
101+
pub fn has_virtual_mod(&self, keysym: Keysym) -> bool {
102+
self.clients
103+
.lock()
104+
.unwrap()
105+
.0
106+
.values()
107+
.any(|client| client.virtual_mods.contains(&keysym))
108+
}
109+
110+
pub fn add_active_virtual_mod(&mut self, keysym: Keysym) {
111+
self.active_virtual_mods.insert(keysym);
112+
}
113+
114+
pub fn remove_active_virtual_mod(&mut self, keysym: Keysym) -> bool {
115+
self.active_virtual_mods.remove(&keysym)
116+
}
117+
118+
pub fn has_keyboard_grab(&self) -> bool {
119+
self.clients
120+
.lock()
121+
.unwrap()
122+
.0
123+
.values()
124+
.any(|client| client.grabbed)
125+
}
126+
127+
/// Key grab exists for mods, key, with active virtual mods
128+
pub fn has_key_grab(&self, mods: u32, key: Keysym) -> bool {
129+
self.clients
130+
.lock()
131+
.unwrap()
132+
.0
133+
.values()
134+
.flat_map(|client| &client.key_grabs)
135+
.any(|grab| {
136+
grab.mods == mods
137+
&& grab.virtual_mods == self.active_virtual_mods
138+
&& grab.key == key
139+
})
140+
}
141+
142+
pub fn key_event(
143+
&self,
144+
modifiers: &smithay::input::keyboard::ModifiersState,
145+
keysym: &smithay::input::keyboard::KeysymHandle,
146+
state: smithay::backend::input::KeyState,
147+
) {
148+
let Some(conn) = self.conn.get() else {
149+
return;
150+
};
151+
152+
// Test if any client is watching key input
153+
if !self
154+
.clients
155+
.lock()
156+
.unwrap()
157+
.0
158+
.values()
159+
.any(|client| client.watched)
160+
{
161+
return;
162+
}
163+
164+
let signal_context = SignalEmitter::new(conn, "/org/freedesktop/a11y/Manager").unwrap();
165+
let released = match state {
166+
KeyState::Pressed => false,
167+
KeyState::Released => true,
168+
};
169+
let unichar = {
170+
let xkb = keysym.xkb().lock().unwrap();
171+
unsafe { xkb.state() }.key_get_utf32(keysym.raw_code())
172+
};
173+
let future = KeyboardMonitor::key_event(
174+
signal_context,
175+
released,
176+
modifiers.serialized.layout_effective,
177+
keysym.modified_sym().raw(),
178+
unichar,
179+
keysym.raw_code().raw() as u16,
180+
);
181+
self.executor.spawn_ok(async {
182+
future.await;
183+
});
184+
}
185+
}
186+
187+
struct KeyboardMonitor {
188+
clients: Arc<Mutex<Clients>>,
189+
}
190+
191+
#[zbus::interface(name = "org.freedesktop.a11y.KeyboardMonitor")]
192+
impl KeyboardMonitor {
193+
fn grab_keyboard(&mut self, #[zbus(header)] header: Header<'_>) {
194+
if let Some(sender) = header.sender() {
195+
let mut clients = self.clients.lock().unwrap();
196+
clients.get(sender).grabbed = true;
197+
eprintln!("grab keyboard by {}", sender);
198+
}
199+
}
200+
201+
fn ungrab_keyboard(&mut self, #[zbus(header)] header: Header<'_>) {
202+
if let Some(sender) = header.sender() {
203+
let mut clients = self.clients.lock().unwrap();
204+
clients.get(sender).grabbed = false;
205+
eprintln!("ungrab keyboard by {}", sender);
206+
}
207+
}
208+
209+
fn watch_keyboard(&mut self, #[zbus(header)] header: Header<'_>) {
210+
if let Some(sender) = header.sender() {
211+
let mut clients = self.clients.lock().unwrap();
212+
clients.get(sender).watched = true;
213+
eprintln!("watch keyboard by {}", sender);
214+
}
215+
}
216+
217+
fn unwatch_keyboard(&mut self, #[zbus(header)] header: Header<'_>) {
218+
if let Some(sender) = header.sender() {
219+
let mut clients = self.clients.lock().unwrap();
220+
clients.get(sender).watched = false;
221+
eprintln!("unwatch keyboard by {}", sender);
222+
}
223+
}
224+
225+
fn set_key_grabs(
226+
&self,
227+
#[zbus(header)] header: Header<'_>,
228+
virtual_mods: Vec<u32>,
229+
keystrokes: Vec<(u32, u32)>,
230+
) {
231+
let virtual_mods = virtual_mods
232+
.into_iter()
233+
.map(Keysym::from)
234+
.collect::<Vec<_>>();
235+
let key_grabs = keystrokes
236+
.into_iter()
237+
.map(|(k, mods)| KeyGrab::new(&virtual_mods, Keysym::from(k), mods))
238+
.collect::<Vec<_>>();
239+
240+
if let Some(sender) = header.sender() {
241+
let mut clients = self.clients.lock().unwrap();
242+
let client = clients.get(sender);
243+
eprintln!(
244+
"key grabs set by {}: {:?}",
245+
sender,
246+
(&virtual_mods, &key_grabs)
247+
);
248+
client.virtual_mods = virtual_mods.into_iter().collect::<HashSet<_>>();
249+
client.key_grabs = key_grabs;
250+
}
251+
}
252+
253+
// TODO signal
254+
#[zbus(signal)]
255+
async fn key_event(
256+
ctx: SignalEmitter<'_>,
257+
released: bool,
258+
state: u32,
259+
keysym: u32,
260+
unichar: u32,
261+
keycode: u16,
262+
) -> zbus::Result<()>;
263+
}
264+
265+
async fn serve(clients: Arc<Mutex<Clients>>) -> zbus::Result<zbus::Connection> {
266+
let keyboard_monitor = KeyboardMonitor { clients };
267+
zbus::connection::Builder::session()?
268+
.name("org.freedesktop.a11y.Manager")?
269+
.serve_at("/org/freedesktop/a11y/Manager", keyboard_monitor)?
270+
.build()
271+
.await
272+
}

src/dbus/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use calloop::{InsertError, LoopHandle, RegistrationToken};
44
use std::collections::HashMap;
55
use zbus::blocking::{fdo::DBusProxy, Connection};
66

7+
pub mod a11y_keyboard_monitor;
78
mod power;
89

910
pub fn init(evlh: &LoopHandle<'static, State>) -> Result<Vec<RegistrationToken>> {

src/input/mod.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,12 +1557,16 @@ impl State {
15571557
.unwrap_or(false)
15581558
});
15591559

1560+
// TODO update keyboard monitor
15601561
self.common.atspi_ei.input(
15611562
modifiers,
15621563
&handle,
15631564
event.state(),
15641565
event.time() as u64 * 1000,
15651566
);
1567+
self.common
1568+
.a11y_keyboard_monitor_state
1569+
.key_event(modifiers, &handle, event.state());
15661570

15671571
// Leave move overview mode, if any modifier was released
15681572
if let Some(Trigger::KeyboardMove(action_modifiers)) =
@@ -1725,11 +1729,15 @@ impl State {
17251729
}
17261730

17271731
if event.state() == KeyState::Released {
1728-
let removed = self
1732+
let mut removed = self
17291733
.common
17301734
.atspi_ei
17311735
.active_virtual_mods
17321736
.remove(&event.key_code());
1737+
removed |= self
1738+
.common
1739+
.a11y_keyboard_monitor_state
1740+
.remove_active_virtual_mod(handle.modified_sym());
17331741
// If `Caps_Lock` is a virtual modifier, and is in locked state, clear it
17341742
if removed && handle.modified_sym() == Keysym::Caps_Lock {
17351743
if (modifiers.serialized.locked & 2) != 0 {
@@ -1755,16 +1763,23 @@ impl State {
17551763
}
17561764
}
17571765
} else if event.state() == KeyState::Pressed
1758-
&& self
1766+
&& (self
17591767
.common
17601768
.atspi_ei
17611769
.virtual_mods
17621770
.contains(&event.key_code())
1771+
|| self
1772+
.common
1773+
.a11y_keyboard_monitor_state
1774+
.has_virtual_mod(handle.modified_sym()))
17631775
{
17641776
self.common
17651777
.atspi_ei
17661778
.active_virtual_mods
17671779
.insert(event.key_code());
1780+
self.common
1781+
.a11y_keyboard_monitor_state
1782+
.add_active_virtual_mod(handle.modified_sym());
17681783

17691784
tracing::debug!(
17701785
"active virtual mods: {:?}",
@@ -1800,10 +1815,15 @@ impl State {
18001815
}
18011816

18021817
if self.common.atspi_ei.has_keyboard_grab()
1818+
|| self.common.a11y_keyboard_monitor_state.has_keyboard_grab()
18031819
|| self
18041820
.common
18051821
.atspi_ei
18061822
.has_key_grab(modifiers.serialized.layout_effective, event.key_code())
1823+
|| self
1824+
.common
1825+
.a11y_keyboard_monitor_state
1826+
.has_key_grab(modifiers.serialized.layout_effective, handle.modified_sym())
18071827
{
18081828
return FilterResult::Intercept(None);
18091829
}

0 commit comments

Comments
 (0)