anybox-rs 0.11.0

one API for the most popular passthrough boxes
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
use std::any::Any;
use std::sync::mpsc::Sender;
use std::time::{Duration, Instant};

use crate::capability::{Action, Axis, Button, Snapshot};
use crate::error::{Error, Result};
use crate::event::{BoxTime, DropSource};
use crate::extension::{Extension, ExtensionReply};
use crate::key::{Key, MediaKey};

/// what the core asks a backend to do on the box. it never sends one the box's capabilities don't list
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Command {
    Move(i16, i16),
    MoveNow(i16, i16),
    Press(Button),
    Release(Button),
    Click {
        button: Button,
        hold: Option<Duration>,
    },
    Wheel(i16),
    Pan(i16),
    DiscardPendingMoves,
    KeyPress(Key),
    KeyRelease(Key),
    KeyTap {
        key: Key,
        hold: Option<Duration>,
    },
    MediaPress(MediaKey),
    MediaRelease(MediaKey),
    MediaTap {
        key: MediaKey,
        hold: Option<Duration>,
    },
    /// how much of the physical input on a slot goes through, in percent. 0 is locked, 100 is free and anything
    /// else is a scale
    Lock {
        slot: LockSlot,
        keep: i16,
    },
    KeyChord {
        kind: ChordKind,
        keys: Keys,
    },
    /// `force` makes the PC see the key up whatever the finger does, otherwise the PC gets the finger's state back
    KeyForce {
        key: Key,
        force: bool,
    },
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ChordKind {
    Press,
    Release,
    Tap,
}

/// the keys of one chord, at most `MAX_CHORD`, kept in the command so it stays `Copy`
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct Keys {
    keys: [Key; MAX_CHORD],
    len: u8,
}

/// anybox's own limit for every box, MAKCU's chord command takes 1 to 14
pub(crate) const MAX_CHORD: usize = 14;

impl Keys {
    /// none when there are more than `MAX_CHORD`
    pub(crate) fn new(keys: &[Key]) -> Option<Self> {
        let mut all = [Key(0); MAX_CHORD];
        all.get_mut(..keys.len())?.copy_from_slice(keys);
        Some(Self {
            keys: all,
            len: u8::try_from(keys.len()).ok()?,
        })
    }

    pub(crate) fn as_slice(&self) -> &[Key] {
        &self.keys[..usize::from(self.len)]
    }
}

#[derive(Debug, Default)]
pub(crate) struct Typing {
    /// the link generation the text's first request went out on. every later one may only go out on it, so after
    /// a reconnect nothing more of the text is typed
    pub(crate) generation: Option<u64>,
    /// set by the backend once any of the text went to the box, from then on the box may have typed some of it
    pub(crate) sent: bool,
}

pub(crate) const UNLOCKED: i16 = 100;
pub(crate) const LOCKED: i16 = 0;

/// what a lock sits on. an axis gets one per direction, so unlocking one way doesn't touch the other
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) enum LockSlot {
    Button(Button),
    Key(Key),
    Axis(Axis, Sign),
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) enum Sign {
    Positive,
    Negative,
}

impl LockSlot {
    pub(crate) fn action(self, keep: i16) -> Action {
        match self {
            Self::Button(_) => Action::LockButton,
            Self::Key(_) => Action::LockKey,
            Self::Axis(..) if keep == LOCKED || keep == UNLOCKED => Action::LockAxis,
            Self::Axis(..) => Action::ScaleAxis,
        }
    }
}

/// anything that can be held down. the kind is part of it, so a button and a key with the same number never
/// get mixed up
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) enum Usage {
    Button(Button),
    Key(Key),
    Media(MediaKey),
}

impl Usage {
    pub(crate) fn press(self) -> Command {
        match self {
            Self::Button(button) => Command::Press(button),
            Self::Key(key) => Command::KeyPress(key),
            Self::Media(key) => Command::MediaPress(key),
        }
    }

    pub(crate) fn release(self) -> Command {
        match self {
            Self::Button(button) => Command::Release(button),
            Self::Key(key) => Command::KeyRelease(key),
            Self::Media(key) => Command::MediaRelease(key),
        }
    }

    /// the box's own click or tap of this button or key
    pub(crate) fn tap(self, hold: Option<Duration>) -> Command {
        match self {
            Self::Button(button) => Command::Click { button, hold },
            Self::Key(key) => Command::KeyTap { key, hold },
            Self::Media(key) => Command::MediaTap { key, hold },
        }
    }
}

impl Command {
    /// the action the capability check looks up for this command
    pub(crate) fn action(self) -> Action {
        match self {
            Self::Move(..) => Action::Move,
            Self::MoveNow(..) => Action::MoveNow,
            Self::Press(_) => Action::Press,
            Self::Release(_) => Action::Release,
            Self::Click { .. } => Action::Click,
            Self::Wheel(_) => Action::Wheel,
            Self::Pan(_) => Action::Pan,
            Self::DiscardPendingMoves => Action::DiscardPendingMoves,
            Self::KeyPress(_) => Action::KeyPress,
            Self::KeyRelease(_) => Action::KeyRelease,
            Self::KeyTap { .. } => Action::KeyTap,
            Self::MediaPress(_) => Action::MediaPress,
            Self::MediaRelease(_) => Action::MediaRelease,
            Self::MediaTap { .. } => Action::MediaTap,
            Self::Lock { slot, keep } => slot.action(keep),
            Self::KeyChord { .. } => Action::KeyChord,
            Self::KeyForce { .. } => Action::ForceReleaseKey,
        }
    }

    /// what this command holds down or lets go of, if anything
    pub(crate) fn holds(self) -> Option<(Usage, bool)> {
        match self {
            Self::Press(button) => Some((Usage::Button(button), true)),
            Self::Release(button) => Some((Usage::Button(button), false)),
            Self::KeyPress(key) => Some((Usage::Key(key), true)),
            Self::KeyRelease(key) => Some((Usage::Key(key), false)),
            Self::MediaPress(key) => Some((Usage::Media(key), true)),
            Self::MediaRelease(key) => Some((Usage::Media(key), false)),
            Self::Move(..)
            | Self::MoveNow(..)
            | Self::Click { .. }
            | Self::Wheel(_)
            | Self::Pan(_)
            | Self::DiscardPendingMoves
            | Self::KeyTap { .. }
            | Self::MediaTap { .. }
            | Self::Lock { .. }
            | Self::KeyChord { .. }
            | Self::KeyForce { .. } => None,
        }
    }
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) struct InputKinds {
    pub(crate) buttons: bool,
    pub(crate) motion: bool,
    pub(crate) keys: bool,
    pub(crate) media: bool,
}

/// an input stream the box can switch off by itself
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum InputKind {
    Buttons,
    Motion,
    Keys,
    Media,
}

impl InputKind {
    pub(crate) fn kinds(self) -> InputKinds {
        let mut kinds = InputKinds::default();
        match self {
            Self::Buttons => kinds.buttons = true,
            Self::Motion => kinds.motion = true,
            Self::Keys => kinds.keys = true,
            Self::Media => kinds.media = true,
        }
        kinds
    }
}

/// the kinds that didn't switch the way they were asked to, and why. `Disconnected` means the reconnect asks again
#[derive(Debug)]
pub(crate) struct StreamFailure {
    pub(crate) failed: InputKinds,
    pub(crate) error: crate::error::Error,
}

/// one family's connection to one box. every method gives up and returns `Disconnected` after `WRITE_DEADLINE`
/// per request it makes (one per button or key for `release_all`, a switch and its confirmation per stream for
/// `set_streams`), because the core calls them while it holds the output lock and drop waits on that lock
pub(crate) trait Backend: Send + Sync + 'static {
    /// has to check the command against what the box can do right now, because a reconnect can change that after the
    /// core already said yes
    fn send(&self, command: Command) -> Result<()>;
    /// switches each kind on or off to match `kinds`, and leaves a kind that's already right alone
    fn set_streams(&self, kinds: InputKinds) -> std::result::Result<(), StreamFailure>;
    /// switches back on the one stream the box switched off, the others are left alone
    fn restart_stream(&self, kind: InputKind) -> Result<()>;
    fn release_all(&self, held: &[Usage]) -> Result<()>;
    /// types one piece of printable ASCII and returns once the box is done with it, all of it within
    /// `WRITE_DEADLINE`. a box that doesn't type answers `Unsupported`, the core never gets that far on one
    fn type_piece(&self, piece: &[u8], typing: &mut Typing) -> Result<()>;
    /// unlocks each slot, and once one comes back `Disconnected` it doesn't wait on the rest. Medius still queues
    /// them, MAKCU leaves them to its last check at shutdown, which only gets to them if the box answers in time
    fn unlock_all(&self, slots: &[LockSlot]) -> Result<()>;
    /// gives back each key `force_release_key` held up, on drop and close. every one goes out even after one fails,
    /// a box that queues its writes overrides this to stop waiting once one ran out of time
    fn restore_all(&self, keys: &[Key]) -> Result<()> {
        let mut first = Ok(());
        for &key in keys {
            let restored = self.send(Command::KeyForce { key, force: false });
            if first.is_ok() {
                first = restored;
            }
        }
        first
    }
    fn shutdown(&self, deadline: Duration);
    /// lets explicit close report a failed final write, while drop can still give it a best effort
    fn close(&self, deadline: Duration) -> Result<()> {
        self.shutdown(deadline);
        Ok(())
    }
    /// one call only this family has. the core checks the capability first, so a backend without extensions
    /// never gets here, this answer is for one that does by mistake
    fn extension(&self, request: Extension) -> Result<ExtensionReply> {
        Err(Error::Unsupported {
            action: request.action(),
            family: crate::extension::family(request),
            reason: "this backend has no calls of its own".into(),
        })
    }
    fn as_any(&self) -> &dyn Any;
}

/// how long a send may hold the output lock on a port that stopped answering
pub(crate) const WRITE_DEADLINE: Duration = Duration::from_millis(500);

#[derive(Debug)]
pub(crate) enum Input {
    Button {
        button: Button,
        pressed: bool,
    },
    Motion {
        dx: i32,
        dy: i32,
        wheel: i32,
        pan: i32,
    },
    Key {
        key: Key,
        pressed: bool,
    },
    Media {
        key: MediaKey,
        pressed: bool,
    },
}

#[derive(Debug)]
pub(crate) enum Signal {
    Input {
        input: Input,
        arrived: Instant,
        box_time: Option<BoxTime>,
        /// none where the box's own library reads the port, so anybox never sees the moment
        read: Option<crate::transport::os::Read>,
    },
    /// with a kind when only that kind's stream lost them, like one Medius subscription overflowing
    Dropped(u64, DropSource, Option<InputKind>),
    /// the box switched one input stream off by itself and threw away what it had queued
    StreamLost(InputKind),
    /// the box refused a command after its call already returned
    Refused(Action),
    SessionReset,
    Disconnected,
    Reconnected(Box<Snapshot>),
    /// still the same box, just with other details (a new key limit, or a device that came or went on a box that
    /// doesn't lose anything over it), so nothing gets let go. only the streams follow what it can do now
    Changed(Box<Snapshot>),
    /// lets a test wait until everything sent before it got handled
    #[cfg(test)]
    Barrier(std::sync::mpsc::Sender<()>),
    /// events that only go out to the receivers, with none of what the real ones change in the device, so a
    /// test can show every kind without a disconnect actually disconnecting
    #[cfg(feature = "mock")]
    Examples(Vec<crate::event::Event>),
    Stop,
}

/// how a backend reports to the core. it only queues the report, so a backend thread never waits on the output
/// lock
#[derive(Clone, Debug)]
pub(crate) struct Sink(pub(crate) Sender<Signal>);

impl Sink {
    pub(crate) fn input(
        &self,
        input: Input,
        arrived: Instant,
        box_time: Option<BoxTime>,
        read: Option<crate::transport::os::Read>,
    ) {
        self.send(Signal::Input {
            input,
            arrived,
            box_time,
            read,
        });
    }

    pub(crate) fn dropped(&self, count: u64, source: DropSource) {
        self.send(Signal::Dropped(count, source, None));
    }

    /// events one kind's stream lost on the PC side, so only that kind's receivers hear about it
    pub(crate) fn lost(&self, count: u64, kind: InputKind) {
        self.send(Signal::Dropped(count, DropSource::Host, Some(kind)));
    }

    pub(crate) fn stream_lost(&self, kind: InputKind) {
        self.send(Signal::StreamLost(kind));
    }

    pub(crate) fn refused(&self, action: Action) {
        self.send(Signal::Refused(action));
    }

    pub(crate) fn session_reset(&self) {
        self.send(Signal::SessionReset);
    }

    pub(crate) fn disconnected(&self) {
        self.send(Signal::Disconnected);
    }

    pub(crate) fn reconnected(&self, snapshot: Snapshot) {
        self.send(Signal::Reconnected(Box::new(snapshot)));
    }

    pub(crate) fn changed(&self, snapshot: Snapshot) {
        self.send(Signal::Changed(Box::new(snapshot)));
    }

    fn send(&self, signal: Signal) {
        // this only fails once the core is gone, and then there's nobody left to tell
        let _ = self.0.send(signal);
    }
}