mirror of
https://gitlab.steamos.cloud/holo/steamos-manager.git
synced 2025-07-15 10:46:41 -04:00
Merge branch 'endrift/screenreader' into 'master'
Screenreader improvements See merge request holo/steamos-manager!15
This commit is contained in:
commit
6a562e2d31
4 changed files with 677 additions and 181 deletions
|
@ -28,6 +28,7 @@ mod process;
|
||||||
mod sls;
|
mod sls;
|
||||||
mod systemd;
|
mod systemd;
|
||||||
mod udev;
|
mod udev;
|
||||||
|
mod uinput;
|
||||||
|
|
||||||
pub mod cec;
|
pub mod cec;
|
||||||
pub mod daemon;
|
pub mod daemon;
|
||||||
|
|
|
@ -8,29 +8,16 @@
|
||||||
use ::sysinfo::System;
|
use ::sysinfo::System;
|
||||||
use anyhow::{anyhow, bail, ensure, Result};
|
use anyhow::{anyhow, bail, ensure, Result};
|
||||||
use gio::{prelude::SettingsExt, Settings};
|
use gio::{prelude::SettingsExt, Settings};
|
||||||
#[cfg(test)]
|
use input_linux::Key;
|
||||||
use input_linux::InputEvent;
|
|
||||||
#[cfg(not(test))]
|
|
||||||
use input_linux::{EventKind, InputId, UInputHandle};
|
|
||||||
use input_linux::{EventTime, Key, KeyEvent, KeyState, SynchronizeEvent};
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
#[cfg(not(test))]
|
|
||||||
use nix::fcntl::{fcntl, FcntlArg, OFlag};
|
|
||||||
use nix::sys::signal;
|
use nix::sys::signal;
|
||||||
use nix::unistd::Pid;
|
use nix::unistd::Pid;
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
use serde_json::{Map, Value};
|
use serde_json::{Map, Value};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
#[cfg(test)]
|
|
||||||
use std::collections::VecDeque;
|
|
||||||
#[cfg(not(test))]
|
|
||||||
use std::fs::OpenOptions;
|
|
||||||
use std::io::ErrorKind;
|
use std::io::ErrorKind;
|
||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
#[cfg(not(test))]
|
|
||||||
use std::os::fd::OwnedFd;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::SystemTime;
|
|
||||||
use strum::{Display, EnumString};
|
use strum::{Display, EnumString};
|
||||||
use tokio::fs::{read_to_string, write};
|
use tokio::fs::{read_to_string, write};
|
||||||
use tracing::{debug, error, info, trace, warn};
|
use tracing::{debug, error, info, trace, warn};
|
||||||
|
@ -41,6 +28,7 @@ use zbus::Connection;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use crate::path;
|
use crate::path;
|
||||||
use crate::systemd::SystemdUnit;
|
use crate::systemd::SystemdUnit;
|
||||||
|
use crate::uinput::UInputDevice;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
const TEST_ORCA_SETTINGS: &str = "data/test-orca-settings.conf";
|
const TEST_ORCA_SETTINGS: &str = "data/test-orca-settings.conf";
|
||||||
|
@ -94,117 +82,6 @@ pub enum ScreenReaderAction {
|
||||||
ToggleMode = 9,
|
ToggleMode = 9,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct UInputDevice {
|
|
||||||
#[cfg(not(test))]
|
|
||||||
handle: UInputHandle<OwnedFd>,
|
|
||||||
#[cfg(test)]
|
|
||||||
queue: VecDeque<InputEvent>,
|
|
||||||
name: String,
|
|
||||||
open: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UInputDevice {
|
|
||||||
#[cfg(not(test))]
|
|
||||||
pub(crate) fn new() -> Result<UInputDevice> {
|
|
||||||
let fd = OpenOptions::new()
|
|
||||||
.write(true)
|
|
||||||
.create(false)
|
|
||||||
.open("/dev/uinput")?
|
|
||||||
.into();
|
|
||||||
|
|
||||||
let mut flags = OFlag::from_bits_retain(fcntl(&fd, FcntlArg::F_GETFL)?);
|
|
||||||
flags.set(OFlag::O_NONBLOCK, true);
|
|
||||||
fcntl(&fd, FcntlArg::F_SETFL(flags))?;
|
|
||||||
|
|
||||||
Ok(UInputDevice {
|
|
||||||
handle: UInputHandle::new(fd),
|
|
||||||
name: String::new(),
|
|
||||||
open: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub(crate) fn new() -> Result<UInputDevice> {
|
|
||||||
Ok(UInputDevice {
|
|
||||||
queue: VecDeque::new(),
|
|
||||||
name: String::new(),
|
|
||||||
open: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn set_name(&mut self, name: String) -> Result<()> {
|
|
||||||
ensure!(!self.open, "Cannot change name after opening");
|
|
||||||
self.name = name;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
pub(crate) fn open(&mut self) -> Result<()> {
|
|
||||||
ensure!(!self.open, "Cannot reopen uinput handle");
|
|
||||||
|
|
||||||
self.handle.set_evbit(EventKind::Key)?;
|
|
||||||
self.handle.set_keybit(Key::A)?;
|
|
||||||
self.handle.set_keybit(Key::H)?;
|
|
||||||
self.handle.set_keybit(Key::M)?;
|
|
||||||
self.handle.set_keybit(Key::Insert)?;
|
|
||||||
self.handle.set_keybit(Key::LeftCtrl)?;
|
|
||||||
self.handle.set_keybit(Key::LeftShift)?;
|
|
||||||
self.handle.set_keybit(Key::Down)?;
|
|
||||||
self.handle.set_keybit(Key::Left)?;
|
|
||||||
self.handle.set_keybit(Key::Right)?;
|
|
||||||
self.handle.set_keybit(Key::Up)?;
|
|
||||||
|
|
||||||
let input_id = InputId {
|
|
||||||
bustype: input_linux::sys::BUS_VIRTUAL,
|
|
||||||
vendor: 0x28DE,
|
|
||||||
product: 0,
|
|
||||||
version: 0,
|
|
||||||
};
|
|
||||||
self.handle
|
|
||||||
.create(&input_id, self.name.as_bytes(), 0, &[])?;
|
|
||||||
self.open = true;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub(crate) fn open(&mut self) -> Result<()> {
|
|
||||||
ensure!(!self.open, "Cannot reopen uinput handle");
|
|
||||||
self.open = true;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn system_time() -> Result<EventTime> {
|
|
||||||
let duration = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
|
|
||||||
Ok(EventTime::new(
|
|
||||||
duration.as_secs().try_into()?,
|
|
||||||
duration.subsec_micros().into(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_key_event(&mut self, key: Key, value: KeyState) -> Result<()> {
|
|
||||||
let tv = UInputDevice::system_time().unwrap_or_else(|err| {
|
|
||||||
warn!("System time error: {err}");
|
|
||||||
EventTime::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
let ev = KeyEvent::new(tv, key, value);
|
|
||||||
let syn = SynchronizeEvent::report(tv);
|
|
||||||
#[cfg(not(test))]
|
|
||||||
self.handle.write(&[*ev.as_ref(), *syn.as_ref()])?;
|
|
||||||
#[cfg(test)]
|
|
||||||
self.queue.extend(&[*ev.as_ref(), *syn.as_ref()]);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn key_down(&mut self, key: Key) -> Result<()> {
|
|
||||||
self.send_key_event(key, KeyState::PRESSED)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn key_up(&mut self, key: Key) -> Result<()> {
|
|
||||||
self.send_key_event(key, KeyState::RELEASED)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct OrcaManager<'dbus> {
|
pub(crate) struct OrcaManager<'dbus> {
|
||||||
orca_unit: SystemdUnit<'dbus>,
|
orca_unit: SystemdUnit<'dbus>,
|
||||||
rate: f64,
|
rate: f64,
|
||||||
|
@ -234,7 +111,18 @@ impl<'dbus> OrcaManager<'dbus> {
|
||||||
let a11ysettings = Settings::new(A11Y_SETTING);
|
let a11ysettings = Settings::new(A11Y_SETTING);
|
||||||
manager.enabled = a11ysettings.boolean(SCREEN_READER_SETTING);
|
manager.enabled = a11ysettings.boolean(SCREEN_READER_SETTING);
|
||||||
manager.keyboard.set_name(KEYBOARD_NAME.to_string())?;
|
manager.keyboard.set_name(KEYBOARD_NAME.to_string())?;
|
||||||
manager.keyboard.open()?;
|
manager.keyboard.open(&[
|
||||||
|
Key::A,
|
||||||
|
Key::H,
|
||||||
|
Key::M,
|
||||||
|
Key::Insert,
|
||||||
|
Key::LeftCtrl,
|
||||||
|
Key::LeftShift,
|
||||||
|
Key::Down,
|
||||||
|
Key::Left,
|
||||||
|
Key::Right,
|
||||||
|
Key::Up,
|
||||||
|
])?;
|
||||||
|
|
||||||
Ok(manager)
|
Ok(manager)
|
||||||
}
|
}
|
||||||
|
@ -258,10 +146,7 @@ impl<'dbus> OrcaManager<'dbus> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set_enabled(&mut self, enable: bool) -> Result<()> {
|
pub async fn set_enabled(&mut self, enable: bool) -> Result<()> {
|
||||||
if self.enabled == enable {
|
if enable != self.enabled {
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
{
|
{
|
||||||
let a11ysettings = Settings::new(A11Y_SETTING);
|
let a11ysettings = Settings::new(A11Y_SETTING);
|
||||||
|
@ -275,6 +160,7 @@ impl<'dbus> OrcaManager<'dbus> {
|
||||||
_ => return Err(e),
|
_ => return Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if enable {
|
if enable {
|
||||||
self.restart_orca().await?;
|
self.restart_orca().await?;
|
||||||
} else {
|
} else {
|
||||||
|
@ -328,25 +214,24 @@ impl<'dbus> OrcaManager<'dbus> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set_mode(&mut self, mode: ScreenReaderMode) -> Result<()> {
|
pub async fn set_mode(&mut self, mode: ScreenReaderMode) -> Result<()> {
|
||||||
|
if self.mode == mode {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
// Use insert+A twice to switch to focus mode sticky
|
// Use insert+A twice to switch to focus mode sticky
|
||||||
// Use insert+A three times to switch to browse mode sticky
|
// Use insert+A three times to switch to browse mode sticky
|
||||||
match mode {
|
match mode {
|
||||||
ScreenReaderMode::Focus => {
|
ScreenReaderMode::Focus => {
|
||||||
self.keyboard.key_down(Key::Insert)?;
|
self.keyboard.key_down(Key::Insert)?;
|
||||||
self.keyboard.key_down(Key::A)?;
|
self.keyboard.key_press(Key::A)?;
|
||||||
self.keyboard.key_up(Key::A)?;
|
self.keyboard.key_press(Key::A)?;
|
||||||
self.keyboard.key_down(Key::A)?;
|
|
||||||
self.keyboard.key_up(Key::A)?;
|
|
||||||
self.keyboard.key_up(Key::Insert)?;
|
self.keyboard.key_up(Key::Insert)?;
|
||||||
}
|
}
|
||||||
ScreenReaderMode::Browse => {
|
ScreenReaderMode::Browse => {
|
||||||
self.keyboard.key_down(Key::Insert)?;
|
self.keyboard.key_down(Key::Insert)?;
|
||||||
self.keyboard.key_down(Key::A)?;
|
self.keyboard.key_press(Key::A)?;
|
||||||
self.keyboard.key_up(Key::A)?;
|
self.keyboard.key_press(Key::A)?;
|
||||||
self.keyboard.key_down(Key::A)?;
|
self.keyboard.key_press(Key::A)?;
|
||||||
self.keyboard.key_up(Key::A)?;
|
|
||||||
self.keyboard.key_down(Key::A)?;
|
|
||||||
self.keyboard.key_up(Key::A)?;
|
|
||||||
self.keyboard.key_up(Key::Insert)?;
|
self.keyboard.key_up(Key::Insert)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -369,48 +254,39 @@ impl<'dbus> OrcaManager<'dbus> {
|
||||||
}
|
}
|
||||||
ScreenReaderAction::ReadNextWord => {
|
ScreenReaderAction::ReadNextWord => {
|
||||||
self.keyboard.key_down(Key::LeftCtrl)?;
|
self.keyboard.key_down(Key::LeftCtrl)?;
|
||||||
self.keyboard.key_down(Key::Right)?;
|
self.keyboard.key_press(Key::Right)?;
|
||||||
self.keyboard.key_up(Key::Right)?;
|
|
||||||
self.keyboard.key_up(Key::LeftCtrl)?;
|
self.keyboard.key_up(Key::LeftCtrl)?;
|
||||||
}
|
}
|
||||||
ScreenReaderAction::ReadPreviousWord => {
|
ScreenReaderAction::ReadPreviousWord => {
|
||||||
self.keyboard.key_down(Key::LeftCtrl)?;
|
self.keyboard.key_down(Key::LeftCtrl)?;
|
||||||
self.keyboard.key_down(Key::Left)?;
|
self.keyboard.key_press(Key::Left)?;
|
||||||
self.keyboard.key_up(Key::Left)?;
|
|
||||||
self.keyboard.key_up(Key::LeftCtrl)?;
|
self.keyboard.key_up(Key::LeftCtrl)?;
|
||||||
}
|
}
|
||||||
ScreenReaderAction::ReadNextItem => {
|
ScreenReaderAction::ReadNextItem => {
|
||||||
self.keyboard.key_down(Key::Down)?;
|
self.keyboard.key_press(Key::Down)?;
|
||||||
self.keyboard.key_up(Key::Down)?;
|
|
||||||
}
|
}
|
||||||
ScreenReaderAction::ReadPreviousItem => {
|
ScreenReaderAction::ReadPreviousItem => {
|
||||||
self.keyboard.key_down(Key::Up)?;
|
self.keyboard.key_press(Key::Up)?;
|
||||||
self.keyboard.key_up(Key::Up)?;
|
|
||||||
}
|
}
|
||||||
ScreenReaderAction::MoveToNextLandmark => {
|
ScreenReaderAction::MoveToNextLandmark => {
|
||||||
self.keyboard.key_down(Key::M)?;
|
self.keyboard.key_press(Key::M)?;
|
||||||
self.keyboard.key_up(Key::M)?;
|
|
||||||
}
|
}
|
||||||
ScreenReaderAction::MoveToPreviousLandmark => {
|
ScreenReaderAction::MoveToPreviousLandmark => {
|
||||||
self.keyboard.key_down(Key::LeftShift)?;
|
self.keyboard.key_down(Key::LeftShift)?;
|
||||||
self.keyboard.key_down(Key::M)?;
|
self.keyboard.key_press(Key::M)?;
|
||||||
self.keyboard.key_up(Key::M)?;
|
|
||||||
self.keyboard.key_up(Key::LeftShift)?;
|
self.keyboard.key_up(Key::LeftShift)?;
|
||||||
}
|
}
|
||||||
ScreenReaderAction::MoveToNextHeading => {
|
ScreenReaderAction::MoveToNextHeading => {
|
||||||
self.keyboard.key_down(Key::H)?;
|
self.keyboard.key_press(Key::H)?;
|
||||||
self.keyboard.key_up(Key::H)?;
|
|
||||||
}
|
}
|
||||||
ScreenReaderAction::MoveToPreviousHeading => {
|
ScreenReaderAction::MoveToPreviousHeading => {
|
||||||
self.keyboard.key_down(Key::LeftShift)?;
|
self.keyboard.key_down(Key::LeftShift)?;
|
||||||
self.keyboard.key_down(Key::H)?;
|
self.keyboard.key_press(Key::H)?;
|
||||||
self.keyboard.key_up(Key::H)?;
|
|
||||||
self.keyboard.key_up(Key::LeftShift)?;
|
self.keyboard.key_up(Key::LeftShift)?;
|
||||||
}
|
}
|
||||||
ScreenReaderAction::ToggleMode => {
|
ScreenReaderAction::ToggleMode => {
|
||||||
self.keyboard.key_down(Key::Insert)?;
|
self.keyboard.key_down(Key::Insert)?;
|
||||||
self.keyboard.key_down(Key::A)?;
|
self.keyboard.key_press(Key::A)?;
|
||||||
self.keyboard.key_up(Key::A)?;
|
|
||||||
self.keyboard.key_up(Key::Insert)?;
|
self.keyboard.key_up(Key::Insert)?;
|
||||||
// TODO: I guess we should emit that the mode changed here...
|
// TODO: I guess we should emit that the mode changed here...
|
||||||
match self.mode {
|
match self.mode {
|
||||||
|
@ -567,13 +443,11 @@ impl<'dbus> OrcaManager<'dbus> {
|
||||||
|
|
||||||
async fn restart_orca(&self) -> Result<()> {
|
async fn restart_orca(&self) -> Result<()> {
|
||||||
trace!("Restarting orca...");
|
trace!("Restarting orca...");
|
||||||
self.orca_unit.enable().await?;
|
|
||||||
self.orca_unit.restart().await
|
self.orca_unit.restart().await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn stop_orca(&self) -> Result<()> {
|
async fn stop_orca(&self) -> Result<()> {
|
||||||
trace!("Stopping orca...");
|
trace!("Stopping orca...");
|
||||||
self.orca_unit.disable().await?;
|
|
||||||
self.orca_unit.stop().await
|
self.orca_unit.stop().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -581,9 +455,9 @@ impl<'dbus> OrcaManager<'dbus> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::systemd::test::{MockManager, MockUnit};
|
use crate::systemd::test::MockUnit;
|
||||||
use crate::systemd::EnableState;
|
|
||||||
use crate::testing;
|
use crate::testing;
|
||||||
|
use input_linux::{Key, KeyState};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::fs::{copy, remove_file};
|
use tokio::fs::{copy, remove_file};
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
@ -604,10 +478,6 @@ mod test {
|
||||||
.at("/org/freedesktop/systemd1/unit/orca_2eservice", unit)
|
.at("/org/freedesktop/systemd1/unit/orca_2eservice", unit)
|
||||||
.await
|
.await
|
||||||
.expect("at");
|
.expect("at");
|
||||||
object_server
|
|
||||||
.at("/org/freedesktop/systemd1", MockManager::default())
|
|
||||||
.await
|
|
||||||
.expect("at");
|
|
||||||
|
|
||||||
sleep(Duration::from_millis(10)).await;
|
sleep(Duration::from_millis(10)).await;
|
||||||
|
|
||||||
|
@ -621,12 +491,10 @@ mod test {
|
||||||
manager.set_enabled(true).await.unwrap();
|
manager.set_enabled(true).await.unwrap();
|
||||||
assert_eq!(manager.enabled(), true);
|
assert_eq!(manager.enabled(), true);
|
||||||
assert_eq!(unit.active().await.unwrap(), true);
|
assert_eq!(unit.active().await.unwrap(), true);
|
||||||
assert_eq!(unit.enabled().await.unwrap(), EnableState::Enabled);
|
|
||||||
|
|
||||||
manager.set_enabled(false).await.unwrap();
|
manager.set_enabled(false).await.unwrap();
|
||||||
assert_eq!(manager.enabled(), false);
|
assert_eq!(manager.enabled(), false);
|
||||||
assert_eq!(unit.active().await.unwrap(), false);
|
assert_eq!(unit.active().await.unwrap(), false);
|
||||||
assert_eq!(unit.enabled().await.unwrap(), EnableState::Disabled);
|
|
||||||
|
|
||||||
copy(TEST_ORCA_SETTINGS, h.test.path().join(ORCA_SETTINGS))
|
copy(TEST_ORCA_SETTINGS, h.test.path().join(ORCA_SETTINGS))
|
||||||
.await
|
.await
|
||||||
|
@ -636,12 +504,10 @@ mod test {
|
||||||
manager.set_enabled(true).await.unwrap();
|
manager.set_enabled(true).await.unwrap();
|
||||||
assert_eq!(manager.enabled(), true);
|
assert_eq!(manager.enabled(), true);
|
||||||
assert_eq!(unit.active().await.unwrap(), true);
|
assert_eq!(unit.active().await.unwrap(), true);
|
||||||
assert_eq!(unit.enabled().await.unwrap(), EnableState::Enabled);
|
|
||||||
|
|
||||||
manager.set_enabled(false).await.unwrap();
|
manager.set_enabled(false).await.unwrap();
|
||||||
assert_eq!(manager.enabled(), false);
|
assert_eq!(manager.enabled(), false);
|
||||||
assert_eq!(unit.active().await.unwrap(), false);
|
assert_eq!(unit.active().await.unwrap(), false);
|
||||||
assert_eq!(unit.enabled().await.unwrap(), EnableState::Disabled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
@ -730,4 +596,459 @@ mod test {
|
||||||
assert_eq!(manager.volume(), 5.0);
|
assert_eq!(manager.volume(), 5.0);
|
||||||
assert!(nofile_result.is_err());
|
assert!(nofile_result.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_read_next_word() {
|
||||||
|
let mut h = testing::start();
|
||||||
|
copy(TEST_ORCA_SETTINGS, h.test.path().join(ORCA_SETTINGS))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let mut manager = OrcaManager::new(&h.new_dbus().await.expect("new_dbus"))
|
||||||
|
.await
|
||||||
|
.expect("OrcaManager::new");
|
||||||
|
manager
|
||||||
|
.trigger_action(ScreenReaderAction::ReadNextWord, 0)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::LeftCtrl, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::Right, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::Right, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::LeftCtrl, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager.keyboard.expect_empty().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_read_previous_word() {
|
||||||
|
let mut h = testing::start();
|
||||||
|
copy(TEST_ORCA_SETTINGS, h.test.path().join(ORCA_SETTINGS))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let mut manager = OrcaManager::new(&h.new_dbus().await.expect("new_dbus"))
|
||||||
|
.await
|
||||||
|
.expect("OrcaManager::new");
|
||||||
|
manager
|
||||||
|
.trigger_action(ScreenReaderAction::ReadPreviousWord, 0)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::LeftCtrl, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::Left, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::Left, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::LeftCtrl, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager.keyboard.expect_empty().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_read_next_item() {
|
||||||
|
let mut h = testing::start();
|
||||||
|
copy(TEST_ORCA_SETTINGS, h.test.path().join(ORCA_SETTINGS))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let mut manager = OrcaManager::new(&h.new_dbus().await.expect("new_dbus"))
|
||||||
|
.await
|
||||||
|
.expect("OrcaManager::new");
|
||||||
|
manager
|
||||||
|
.trigger_action(ScreenReaderAction::ReadNextItem, 0)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::Down, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::Down, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager.keyboard.expect_empty().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_read_previous_item() {
|
||||||
|
let mut h = testing::start();
|
||||||
|
copy(TEST_ORCA_SETTINGS, h.test.path().join(ORCA_SETTINGS))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let mut manager = OrcaManager::new(&h.new_dbus().await.expect("new_dbus"))
|
||||||
|
.await
|
||||||
|
.expect("OrcaManager::new");
|
||||||
|
manager
|
||||||
|
.trigger_action(ScreenReaderAction::ReadPreviousItem, 0)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::Up, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::Up, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager.keyboard.expect_empty().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_move_to_next_landmark() {
|
||||||
|
let mut h = testing::start();
|
||||||
|
copy(TEST_ORCA_SETTINGS, h.test.path().join(ORCA_SETTINGS))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let mut manager = OrcaManager::new(&h.new_dbus().await.expect("new_dbus"))
|
||||||
|
.await
|
||||||
|
.expect("OrcaManager::new");
|
||||||
|
manager
|
||||||
|
.trigger_action(ScreenReaderAction::MoveToNextLandmark, 0)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::M, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::M, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager.keyboard.expect_empty().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_move_to_previous_landmark() {
|
||||||
|
let mut h = testing::start();
|
||||||
|
copy(TEST_ORCA_SETTINGS, h.test.path().join(ORCA_SETTINGS))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let mut manager = OrcaManager::new(&h.new_dbus().await.expect("new_dbus"))
|
||||||
|
.await
|
||||||
|
.expect("OrcaManager::new");
|
||||||
|
manager
|
||||||
|
.trigger_action(ScreenReaderAction::MoveToPreviousLandmark, 0)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::LeftShift, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::M, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::M, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::LeftShift, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager.keyboard.expect_empty().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_move_to_next_heading() {
|
||||||
|
let mut h = testing::start();
|
||||||
|
copy(TEST_ORCA_SETTINGS, h.test.path().join(ORCA_SETTINGS))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let mut manager = OrcaManager::new(&h.new_dbus().await.expect("new_dbus"))
|
||||||
|
.await
|
||||||
|
.expect("OrcaManager::new");
|
||||||
|
manager
|
||||||
|
.trigger_action(ScreenReaderAction::MoveToNextHeading, 0)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::H, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::H, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager.keyboard.expect_empty().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_move_to_previous_heading() {
|
||||||
|
let mut h = testing::start();
|
||||||
|
copy(TEST_ORCA_SETTINGS, h.test.path().join(ORCA_SETTINGS))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let mut manager = OrcaManager::new(&h.new_dbus().await.expect("new_dbus"))
|
||||||
|
.await
|
||||||
|
.expect("OrcaManager::new");
|
||||||
|
manager
|
||||||
|
.trigger_action(ScreenReaderAction::MoveToPreviousHeading, 0)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::LeftShift, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::H, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::H, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::LeftShift, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager.keyboard.expect_empty().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_toggle_mode_to_focus() {
|
||||||
|
let mut h = testing::start();
|
||||||
|
copy(TEST_ORCA_SETTINGS, h.test.path().join(ORCA_SETTINGS))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let mut manager = OrcaManager::new(&h.new_dbus().await.expect("new_dbus"))
|
||||||
|
.await
|
||||||
|
.expect("OrcaManager::new");
|
||||||
|
manager.mode = ScreenReaderMode::Browse;
|
||||||
|
manager
|
||||||
|
.trigger_action(ScreenReaderAction::ToggleMode, 0)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::Insert, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::A, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::A, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::Insert, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager.keyboard.expect_empty().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(manager.mode, ScreenReaderMode::Focus);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_toggle_mode_to_browse() {
|
||||||
|
let mut h = testing::start();
|
||||||
|
copy(TEST_ORCA_SETTINGS, h.test.path().join(ORCA_SETTINGS))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let mut manager = OrcaManager::new(&h.new_dbus().await.expect("new_dbus"))
|
||||||
|
.await
|
||||||
|
.expect("OrcaManager::new");
|
||||||
|
manager.mode = ScreenReaderMode::Focus;
|
||||||
|
manager
|
||||||
|
.trigger_action(ScreenReaderAction::ToggleMode, 0)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::Insert, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::A, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::A, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::Insert, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager.keyboard.expect_empty().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(manager.mode, ScreenReaderMode::Browse);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_set_mode_to_focus() {
|
||||||
|
let mut h = testing::start();
|
||||||
|
copy(TEST_ORCA_SETTINGS, h.test.path().join(ORCA_SETTINGS))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let mut manager = OrcaManager::new(&h.new_dbus().await.expect("new_dbus"))
|
||||||
|
.await
|
||||||
|
.expect("OrcaManager::new");
|
||||||
|
manager.mode = ScreenReaderMode::Browse;
|
||||||
|
manager.set_mode(ScreenReaderMode::Focus).await.unwrap();
|
||||||
|
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::Insert, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::A, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::A, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::A, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::A, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::Insert, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager.keyboard.expect_empty().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(manager.mode, ScreenReaderMode::Focus);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_set_mode_to_browse() {
|
||||||
|
let mut h = testing::start();
|
||||||
|
copy(TEST_ORCA_SETTINGS, h.test.path().join(ORCA_SETTINGS))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let mut manager = OrcaManager::new(&h.new_dbus().await.expect("new_dbus"))
|
||||||
|
.await
|
||||||
|
.expect("OrcaManager::new");
|
||||||
|
manager.mode = ScreenReaderMode::Focus;
|
||||||
|
manager.set_mode(ScreenReaderMode::Browse).await.unwrap();
|
||||||
|
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::Insert, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::A, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::A, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::A, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::A, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::A, KeyState::PRESSED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::A, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager
|
||||||
|
.keyboard
|
||||||
|
.expect_key(Key::Insert, KeyState::RELEASED)
|
||||||
|
.unwrap();
|
||||||
|
manager.keyboard.expect_sync().unwrap();
|
||||||
|
manager.keyboard.expect_empty().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(manager.mode, ScreenReaderMode::Browse);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_set_mode_same() {
|
||||||
|
let mut h = testing::start();
|
||||||
|
copy(TEST_ORCA_SETTINGS, h.test.path().join(ORCA_SETTINGS))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let mut manager = OrcaManager::new(&h.new_dbus().await.expect("new_dbus"))
|
||||||
|
.await
|
||||||
|
.expect("OrcaManager::new");
|
||||||
|
manager.mode = ScreenReaderMode::Browse;
|
||||||
|
manager.set_mode(ScreenReaderMode::Browse).await.unwrap();
|
||||||
|
manager.keyboard.expect_empty().unwrap();
|
||||||
|
assert_eq!(manager.mode, ScreenReaderMode::Browse);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,6 +128,7 @@ impl<'dbus> SystemdUnit<'dbus> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
pub async fn enable(&self) -> Result<bool> {
|
pub async fn enable(&self) -> Result<bool> {
|
||||||
let manager = SystemdManagerProxy::new(&self.connection).await?;
|
let manager = SystemdManagerProxy::new(&self.connection).await?;
|
||||||
let (_, res) = manager
|
let (_, res) = manager
|
||||||
|
@ -136,6 +137,7 @@ impl<'dbus> SystemdUnit<'dbus> {
|
||||||
Ok(!res.is_empty())
|
Ok(!res.is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
pub async fn disable(&self) -> Result<bool> {
|
pub async fn disable(&self) -> Result<bool> {
|
||||||
let manager = SystemdManagerProxy::new(&self.connection).await?;
|
let manager = SystemdManagerProxy::new(&self.connection).await?;
|
||||||
let res = manager
|
let res = manager
|
||||||
|
|
172
src/uinput.rs
Normal file
172
src/uinput.rs
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2025 Collabora Ltd.
|
||||||
|
* Copyright © 2025 Valve Software
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
use anyhow::{ensure, Result};
|
||||||
|
#[cfg(test)]
|
||||||
|
use input_linux::InputEvent;
|
||||||
|
use input_linux::{EventKind, EventTime, Key, KeyEvent, KeyState, SynchronizeEvent};
|
||||||
|
#[cfg(not(test))]
|
||||||
|
use input_linux::{InputId, UInputHandle};
|
||||||
|
#[cfg(not(test))]
|
||||||
|
use nix::fcntl::{fcntl, FcntlArg, OFlag};
|
||||||
|
#[cfg(test)]
|
||||||
|
use std::collections::HashSet;
|
||||||
|
#[cfg(test)]
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
#[cfg(not(test))]
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
#[cfg(not(test))]
|
||||||
|
use std::os::fd::OwnedFd;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
|
pub(crate) struct UInputDevice {
|
||||||
|
#[cfg(not(test))]
|
||||||
|
handle: UInputHandle<OwnedFd>,
|
||||||
|
#[cfg(test)]
|
||||||
|
queue: VecDeque<InputEvent>,
|
||||||
|
#[cfg(test)]
|
||||||
|
keybits: HashSet<Key>,
|
||||||
|
name: String,
|
||||||
|
open: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UInputDevice {
|
||||||
|
#[cfg(not(test))]
|
||||||
|
pub(crate) fn new() -> Result<UInputDevice> {
|
||||||
|
let fd = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create(false)
|
||||||
|
.open("/dev/uinput")?
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let mut flags = OFlag::from_bits_retain(fcntl(&fd, FcntlArg::F_GETFL)?);
|
||||||
|
flags.set(OFlag::O_NONBLOCK, true);
|
||||||
|
fcntl(&fd, FcntlArg::F_SETFL(flags))?;
|
||||||
|
|
||||||
|
Ok(UInputDevice {
|
||||||
|
handle: UInputHandle::new(fd),
|
||||||
|
name: String::new(),
|
||||||
|
open: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn new() -> Result<UInputDevice> {
|
||||||
|
Ok(UInputDevice {
|
||||||
|
queue: VecDeque::new(),
|
||||||
|
keybits: HashSet::new(),
|
||||||
|
name: String::new(),
|
||||||
|
open: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_name(&mut self, name: String) -> Result<()> {
|
||||||
|
ensure!(!self.open, "Cannot change name after opening");
|
||||||
|
self.name = name;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
|
pub(crate) fn open(&mut self, keybits: &[Key]) -> Result<()> {
|
||||||
|
ensure!(!self.open, "Cannot reopen uinput handle");
|
||||||
|
|
||||||
|
self.handle.set_evbit(EventKind::Key)?;
|
||||||
|
for key in keybits.into_iter().copied() {
|
||||||
|
self.handle.set_keybit(key)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let input_id = InputId {
|
||||||
|
bustype: input_linux::sys::BUS_VIRTUAL,
|
||||||
|
vendor: 0x28DE,
|
||||||
|
product: 0,
|
||||||
|
version: 0,
|
||||||
|
};
|
||||||
|
self.handle
|
||||||
|
.create(&input_id, self.name.as_bytes(), 0, &[])?;
|
||||||
|
self.open = true;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn open(&mut self, keybits: &[Key]) -> Result<()> {
|
||||||
|
ensure!(!self.open, "Cannot reopen uinput handle");
|
||||||
|
self.open = true;
|
||||||
|
self.keybits = HashSet::from_iter(keybits.into_iter().copied());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn system_time() -> Result<EventTime> {
|
||||||
|
let duration = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
|
||||||
|
Ok(EventTime::new(
|
||||||
|
duration.as_secs().try_into()?,
|
||||||
|
duration.subsec_micros().into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_key_event(&mut self, key: Key, value: KeyState) -> Result<()> {
|
||||||
|
let tv = UInputDevice::system_time().unwrap_or_else(|err| {
|
||||||
|
warn!("System time error: {err}");
|
||||||
|
EventTime::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
let ev = KeyEvent::new(tv, key, value);
|
||||||
|
let syn = SynchronizeEvent::report(tv);
|
||||||
|
#[cfg(not(test))]
|
||||||
|
self.handle.write(&[*ev.as_ref(), *syn.as_ref()])?;
|
||||||
|
#[cfg(test)]
|
||||||
|
{
|
||||||
|
ensure!(self.keybits.contains(&key), "Key not in keybits");
|
||||||
|
self.queue.extend(&[*ev.as_ref(), *syn.as_ref()]);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn key_down(&mut self, key: Key) -> Result<()> {
|
||||||
|
self.send_key_event(key, KeyState::PRESSED)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn key_up(&mut self, key: Key) -> Result<()> {
|
||||||
|
self.send_key_event(key, KeyState::RELEASED)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn key_press(&mut self, key: Key) -> Result<()> {
|
||||||
|
self.send_key_event(key, KeyState::PRESSED)?;
|
||||||
|
self.send_key_event(key, KeyState::RELEASED)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn expect_sync(&mut self) -> Result<()> {
|
||||||
|
let event = self.queue.pop_front().unwrap();
|
||||||
|
ensure!(
|
||||||
|
event.kind == EventKind::Synchronize,
|
||||||
|
"event.kind is {:?}",
|
||||||
|
event.kind
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn expect_key(&mut self, key: Key, state: KeyState) -> Result<()> {
|
||||||
|
let event = self.queue.pop_front().unwrap();
|
||||||
|
ensure!(
|
||||||
|
event.kind == EventKind::Key,
|
||||||
|
"event.kind is {:?}",
|
||||||
|
event.kind
|
||||||
|
);
|
||||||
|
ensure!(event.code == key as u16, "event.code is {}", event.code);
|
||||||
|
ensure!(event.value == state.value, "event.value is {}", event.value);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn expect_empty(&mut self) -> Result<()> {
|
||||||
|
ensure!(self.queue.is_empty(), "queue not empty");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue