1#![forbid(future_incompatible)]
2#![deny(bad_style, missing_docs)]
3#![doc = include_str!("../README.md")]
4
5use hidg_core::{check_read, check_write, AsDevicePath};
6
7pub use hidg_core::{Class, Error, Result, StateChange, ValueChange};
8
9#[cfg(feature = "keyboard")]
10pub use hidg_core::{
11 Key, KeyStateChanges, Keyboard, KeyboardInput, KeyboardOutput, Led, LedStateChanges, Leds,
12 Modifiers,
13};
14
15#[cfg(feature = "mouse")]
16pub use hidg_core::{
17 Button, Buttons, Mouse, MouseInput, MouseInputChange, MouseInputChanges, MouseOutput,
18};
19
20use core::marker::PhantomData;
21use std::{
22 fs::File,
23 io::{Read, Write},
24};
25
26use tokio::{fs::OpenOptions, io::unix::AsyncFd};
27
28pub struct Device<C: Class> {
30 file: AsyncFd<File>,
31 _class: PhantomData<C>,
32}
33
34impl<C: Class> Device<C> {
35 pub async fn open(device: impl AsDevicePath) -> Result<Self> {
37 let path = device.as_device_path();
38 let file = OpenOptions::new()
39 .read(true)
40 .write(true)
41 .custom_flags(libc::O_NONBLOCK)
42 .open(path)
43 .await?
44 .into_std()
45 .await;
46 let file = AsyncFd::new(file)?;
47 Ok(Self {
48 file,
49 _class: PhantomData,
50 })
51 }
52
53 pub async fn input(&mut self, input: &C::Input) -> Result<()>
55 where
56 C::Input: AsRef<[u8]>,
57 {
58 let _ = self.file.writable().await?;
59 let raw = input.as_ref();
60 let len = self.file.get_ref().write(raw)?;
61
62 check_write(len, raw.len())
63 }
64
65 pub async fn output(&mut self, output: &mut C::Output) -> Result<()>
67 where
68 C::Output: AsMut<[u8]>,
69 {
70 let _ = self.file.readable().await?;
71 let raw = output.as_mut();
72 let len = self.file.get_ref().read(raw)?;
73
74 check_read(len, raw.len())?;
75
76 Ok(())
77 }
78}