1#![forbid(future_incompatible)]
2#![deny(bad_style, missing_docs)]
3#![doc = include_str!("../README.md")]
4
5use hidg_core::{check_read, check_write, open};
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 os::unix::io::{AsRawFd, RawFd},
23 pin::Pin,
24 task::{Context, Poll},
25};
26
27use async_io::Async;
28use async_std::{
29 io::{Read, ReadExt, Write, WriteExt},
30 path::Path,
31 task::spawn_blocking as asyncify,
32};
33
34#[doc(hidden)]
35pub struct File {
36 inner: Async<std::fs::File>,
38}
39
40impl File {
41 pub fn from_file(file: std::fs::File) -> Result<Self> {
42 Ok(Self {
43 inner: Async::new(file)?,
44 })
45 }
46}
47
48impl AsRawFd for File {
49 fn as_raw_fd(&self) -> RawFd {
50 self.inner.as_raw_fd()
51 }
52}
53
54impl Read for File {
55 fn poll_read(
56 self: Pin<&mut Self>,
57 cx: &mut Context<'_>,
58 buf: &mut [u8],
59 ) -> Poll<Result<usize>> {
60 use std::io::Read;
61
62 match self.inner.poll_readable(cx) {
63 Poll::Ready(x) => x,
64 Poll::Pending => return Poll::Pending,
65 }?;
66
67 Poll::Ready(self.inner.get_ref().read(buf))
68 }
69}
70
71impl Write for File {
72 fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
73 use std::io::Write;
74
75 match self.inner.poll_writable(cx) {
76 Poll::Ready(x) => x,
77 Poll::Pending => return Poll::Pending,
78 }?;
79
80 Poll::Ready(self.inner.get_ref().write(buf))
81 }
82
83 fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<()>> {
84 Poll::Ready(Ok(()))
85 }
86
87 fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<()>> {
88 Poll::Ready(Ok(()))
89 }
90}
91
92pub struct Device<C: Class> {
94 file: File,
95 _class: PhantomData<C>,
96}
97
98impl<C: Class> Device<C> {
99 pub async fn open(path: impl AsRef<Path>) -> Result<Self> {
101 let path = path.as_ref().to_owned();
102 let file = asyncify(move || open(path, false)).await?;
103 let file = File::from_file(file)?;
104 Ok(Self {
105 file,
106 _class: PhantomData,
107 })
108 }
109
110 pub async fn input(&mut self, input: &C::Input) -> Result<()>
112 where
113 C::Input: AsRef<[u8]>,
114 {
115 let raw = input.as_ref();
116 let len = self.file.write(raw).await?;
117
118 check_write(len, raw.len())
119 }
120
121 pub async fn output(&mut self, output: &mut C::Output) -> Result<()>
123 where
124 C::Output: AsMut<[u8]>,
125 {
126 let raw = output.as_mut();
127 let len = self.file.read(raw).await?;
128
129 check_read(len, raw.len())?;
130
131 Ok(())
132 }
133}