cosmic-comp/src/dbus/a11y_keyboard_monitor.rs

331 lines
10 KiB
Rust
Raw Normal View History

// https://gitlab.gnome.org/GNOME/mutter/-/blob/main/data/dbus-interfaces/org.freedesktop.a11y.xml
use futures_executor::ThreadPool;
use smithay::{
backend::input::KeyState,
input::keyboard::{KeysymHandle, ModifiersState},
};
use std::{
collections::{HashMap, HashSet},
sync::{Arc, Mutex, OnceLock},
};
use tracing::debug;
use xkbcommon::xkb::Keysym;
use zbus::{
message::Header,
names::{UniqueName, WellKnownName},
object_server::SignalEmitter,
};
use super::name_owners::NameOwners;
static ALLOWED_NAMES: &'static [WellKnownName] = &[WellKnownName::from_static_str_unchecked(
"org.gnome.Orca.KeyboardMonitor",
)];
// As defined in at-spi2-core
const ATSPI_DEVICE_A11Y_MANAGER_VIRTUAL_MOD_START: u32 = 15;
#[derive(PartialEq, Eq, Debug)]
struct KeyGrab {
pub mods: u32,
pub virtual_mods: HashSet<Keysym>,
pub key: Keysym,
}
impl KeyGrab {
fn new(virtual_mods: &[Keysym], key: Keysym, raw_mods: u32) -> Self {
let mods = raw_mods & ((1 << ATSPI_DEVICE_A11Y_MANAGER_VIRTUAL_MOD_START) - 1);
let virtual_mods = virtual_mods
.iter()
.copied()
.enumerate()
.filter(|(i, _)| {
raw_mods & (1 << (ATSPI_DEVICE_A11Y_MANAGER_VIRTUAL_MOD_START + *i as u32)) != 0
})
.map(|(_, x)| x)
.collect();
Self {
mods,
virtual_mods,
key,
}
}
}
#[derive(Debug, Default)]
struct Client {
grabbed: bool,
watched: bool,
virtual_mods: HashSet<Keysym>,
key_grabs: Vec<KeyGrab>,
}
#[derive(Debug, Default)]
struct Clients(HashMap<UniqueName<'static>, Client>);
impl Clients {
fn get(&mut self, name: &UniqueName<'_>) -> &mut Client {
self.0.entry(name.to_owned()).or_default()
}
}
#[derive(Debug)]
pub struct A11yKeyboardMonitorState {
executor: ThreadPool,
clients: Arc<Mutex<Clients>>,
active_virtual_mods: HashSet<Keysym>,
conn: Arc<OnceLock<zbus::Connection>>,
name_owners: Arc<OnceLock<NameOwners>>,
}
impl A11yKeyboardMonitorState {
pub fn new(executor: &ThreadPool) -> Self {
let clients = Arc::new(Mutex::new(Clients::default()));
let clients_clone = clients.clone();
let conn_cell = Arc::new(OnceLock::new());
let conn_cell_clone = conn_cell.clone();
let name_owners_cell = Arc::new(OnceLock::new());
let name_owners_cell_clone = name_owners_cell.clone();
let executor_clone = executor.clone();
executor.spawn_ok(async move {
match serve(clients_clone, &executor_clone).await {
Ok((conn, name_owners)) => {
conn_cell_clone.set(conn).unwrap();
name_owners_cell_clone.set(name_owners).unwrap();
}
Err(err) => {
tracing::error!("Failed to serve `org.freedesktop.a11y.Manager`: {err}");
}
}
});
Self {
executor: executor.clone(),
clients,
active_virtual_mods: HashSet::new(),
conn: conn_cell,
name_owners: name_owners_cell,
}
}
pub fn has_virtual_mod(&self, keysym: Keysym) -> bool {
self.clients
.lock()
.unwrap()
.0
.values()
.any(|client| client.virtual_mods.contains(&keysym))
}
pub fn add_active_virtual_mod(&mut self, keysym: Keysym) {
self.active_virtual_mods.insert(keysym);
}
pub fn remove_active_virtual_mod(&mut self, keysym: Keysym) -> bool {
self.active_virtual_mods.remove(&keysym)
}
pub fn active_virtual_mods(&self) -> &HashSet<Keysym> {
&self.active_virtual_mods
}
pub fn has_keyboard_grab(&self) -> bool {
self.clients
.lock()
.unwrap()
.0
.values()
.any(|client| client.grabbed)
}
/// Key grab exists for mods, key, with active virtual mods
pub fn has_key_grab(&self, modifiers: &ModifiersState, key: Keysym) -> bool {
self.clients
.lock()
.unwrap()
.0
.values()
.flat_map(|client| &client.key_grabs)
.any(|grab| {
grab.mods == modifiers.serialized.depressed
&& grab.virtual_mods == self.active_virtual_mods
&& grab.key == key
})
}
pub fn key_event(&self, modifiers: &ModifiersState, keysym: &KeysymHandle, state: KeyState) {
let Some(conn) = self.conn.get() else {
return;
};
let clients = self.clients.lock().unwrap();
for (unique_name, client) in clients.0.iter() {
if !client.watched && !self.has_key_grab(modifiers, keysym.modified_sym()) {
continue;
}
let mut signal_context =
SignalEmitter::new(conn, "/org/freedesktop/a11y/Manager").unwrap();
// Instead of sending signal to all clients, send only to authorized
// clients with registed watches.
signal_context = signal_context.set_destination(unique_name.clone().into());
let released = match state {
KeyState::Pressed => false,
KeyState::Released => true,
};
let unichar = {
let xkb = keysym.xkb().lock().unwrap();
unsafe { xkb.state() }.key_get_utf32(keysym.raw_code())
};
let future = KeyboardMonitor::key_event(
signal_context,
released,
modifiers.serialized.depressed,
keysym.modified_sym().raw(),
unichar,
keysym.raw_code().raw() as u16,
);
self.executor.spawn_ok(async {
let _ = future.await;
});
}
}
pub fn refresh(&mut self) {
// Remove clients and associated grabs when unique names are no longer
// present on bus, or no longer hold approved name on bus.
if let Some(name_owners) = self.name_owners.get() {
self.clients
.lock()
.unwrap()
.0
.retain(|k, _| name_owners.check_owner_no_poll(k, ALLOWED_NAMES))
}
}
}
struct KeyboardMonitor {
clients: Arc<Mutex<Clients>>,
name_owners: NameOwners,
}
impl KeyboardMonitor {
async fn check_sender_allowed(&self, sender: &UniqueName<'_>) -> zbus::fdo::Result<()> {
if self.name_owners.check_owner(sender, ALLOWED_NAMES).await {
Ok(())
} else {
Err(zbus::fdo::Error::AccessDenied("Access denied".to_string()))
}
}
}
#[zbus::interface(name = "org.freedesktop.a11y.KeyboardMonitor")]
impl KeyboardMonitor {
async fn grab_keyboard(&mut self, #[zbus(header)] header: Header<'_>) -> zbus::fdo::Result<()> {
if let Some(sender) = header.sender() {
self.check_sender_allowed(sender).await?;
let mut clients = self.clients.lock().unwrap();
clients.get(sender).grabbed = true;
debug!("grab keyboard by {}", sender);
}
Ok(())
}
async fn ungrab_keyboard(
&mut self,
#[zbus(header)] header: Header<'_>,
) -> zbus::fdo::Result<()> {
if let Some(sender) = header.sender() {
self.check_sender_allowed(sender).await?;
let mut clients = self.clients.lock().unwrap();
clients.get(sender).grabbed = false;
debug!("ungrab keyboard by {}", sender);
}
Ok(())
}
async fn watch_keyboard(
&mut self,
#[zbus(header)] header: Header<'_>,
) -> zbus::fdo::Result<()> {
if let Some(sender) = header.sender() {
self.check_sender_allowed(sender).await?;
let mut clients = self.clients.lock().unwrap();
clients.get(sender).watched = true;
debug!("watch keyboard by {}", sender);
}
Ok(())
}
async fn unwatch_keyboard(
&mut self,
#[zbus(header)] header: Header<'_>,
) -> zbus::fdo::Result<()> {
if let Some(sender) = header.sender() {
self.check_sender_allowed(sender).await?;
let mut clients = self.clients.lock().unwrap();
clients.get(sender).watched = false;
debug!("unwatch keyboard by {}", sender);
}
Ok(())
}
async fn set_key_grabs(
&self,
#[zbus(header)] header: Header<'_>,
virtual_mods: Vec<u32>,
keystrokes: Vec<(u32, u32)>,
) -> zbus::fdo::Result<()> {
let virtual_mods = virtual_mods
.into_iter()
.map(Keysym::from)
.collect::<Vec<_>>();
let key_grabs = keystrokes
.into_iter()
.map(|(k, mods)| KeyGrab::new(&virtual_mods, Keysym::from(k), mods))
.collect::<Vec<_>>();
if let Some(sender) = header.sender() {
self.check_sender_allowed(sender).await?;
let mut clients = self.clients.lock().unwrap();
let client = clients.get(sender);
debug!(
"key grabs set by {}: {:?}",
sender,
(&virtual_mods, &key_grabs)
);
client.virtual_mods = virtual_mods.into_iter().collect::<HashSet<_>>();
client.key_grabs = key_grabs;
}
Ok(())
}
#[zbus(signal)]
async fn key_event(
ctx: SignalEmitter<'_>,
released: bool,
state: u32,
keysym: u32,
unichar: u32,
keycode: u16,
) -> zbus::Result<()>;
}
async fn serve(
clients: Arc<Mutex<Clients>>,
executor: &ThreadPool,
) -> zbus::Result<(zbus::Connection, NameOwners)> {
let conn = zbus::Connection::session().await?;
let name_owners = NameOwners::new(&conn, executor).await?;
let keyboard_monitor = KeyboardMonitor {
clients,
name_owners: name_owners.clone(),
};
conn.object_server()
.at("/org/freedesktop/a11y/Manager", keyboard_monitor)
.await?;
conn.request_name("org.freedesktop.a11y.Manager").await?;
Ok((conn, name_owners))
}