Skip to main content

anybox/
device.rs

1use std::borrow::Cow;
2use std::collections::{HashMap, HashSet};
3use std::fmt;
4use std::sync::mpsc::{self, Receiver, Sender};
5use std::sync::{Arc, Mutex, MutexGuard, PoisonError, RwLock, Weak};
6use std::thread::{self, JoinHandle};
7use std::time::{Duration, Instant};
8
9use crate::backend::{
10    Backend, Command, Input, InputKinds, LOCKED, LockSlot, Sign, Signal, UNLOCKED, Usage,
11};
12use crate::capability::{Action, Axis, Button, Direction, Execution, Quirk, Snapshot};
13use crate::click::{Click, ClickTiming, Holds, Timing};
14use crate::error::{Error, Result};
15use crate::event::{
16    DropSource, Event, EventKinds, Events, Listeners, QUEUE_CAPACITY, Queue, Stamp,
17};
18use crate::key::{Key, MediaKey};
19
20/// how long drop waits for the backend and the link thread before it gives up on them
21const SHUTDOWN_DEADLINE: Duration = Duration::from_secs(1);
22
23/// one connected box. every call checks what the box can do first, so nothing is faked and nothing is
24/// silently dropped. dropping it lets go of every button and key you were holding and closes the port
25pub struct Device {
26    core: Arc<Core>,
27    link: Mutex<Option<(JoinHandle<()>, Receiver<()>)>>,
28    /// held for the whole shutdown, so a second `close` waits for the first one to finish
29    closed: Mutex<bool>,
30}
31
32struct Core {
33    backend: Box<dyn Backend>,
34    snapshot: RwLock<Arc<Snapshot>>,
35    output: Mutex<Output>,
36    receivers: Mutex<Receivers>,
37    click_timing: ClickTiming,
38    holds: Mutex<Holds>,
39    opened: Instant,
40    signals: Sender<Signal>,
41    port: String,
42    /// when each stream action was last refused, see `publish`
43    refused: Mutex<HashMap<Action, Instant>>,
44}
45
46/// what was sent to the box, kept under one lock with the sending so the record never disagrees with the box
47struct Output {
48    held: HashSet<Usage>,
49    /// held buttons and keys where the app was told it failed, like a press that ran out of time or a tap timed
50    /// on the PC that couldn't let go. a box that keeps held buttons on its own would press these again after a
51    /// reconnect, so they get let go
52    unsure: HashSet<Usage>,
53    /// what anybox locked or scaled, with how much of the physical input each slot keeps. it all gets unlocked on
54    /// drop, and on a box that doesn't keep locks itself it gets sent again after a disconnect or a reset
55    locked: HashMap<LockSlot, i16>,
56    link_up: bool,
57    /// set once drop or close starts, and after that nothing from the link brings the device back
58    closing: bool,
59    /// goes up on every disconnect, reconnect and box reset, so a tap timed on the PC can tell its press was
60    /// already let go
61    session: u64,
62}
63
64struct Receivers {
65    next_id: u64,
66    entries: Vec<Entry>,
67}
68
69/// one receiver and the kinds of input it asked for
70struct Entry {
71    id: u64,
72    kinds: EventKinds,
73    queue: Weak<Queue>,
74}
75
76impl Receivers {
77    /// every kind some receiver wants, which is what the box streams
78    fn union(&self) -> EventKinds {
79        self.entries
80            .iter()
81            .fold(EventKinds::NONE, |all, entry| all | entry.kinds)
82    }
83}
84
85pub(crate) struct Start {
86    pub(crate) backend: Box<dyn Backend>,
87    pub(crate) snapshot: Snapshot,
88    pub(crate) signals: Sender<Signal>,
89    pub(crate) incoming: Receiver<Signal>,
90    pub(crate) click_timing: ClickTiming,
91    pub(crate) holds: Holds,
92    pub(crate) port: String,
93}
94
95impl Device {
96    pub(crate) fn start(start: Start) -> Result<Self> {
97        let core = Arc::new(Core {
98            snapshot: RwLock::new(prepare(start.snapshot, start.click_timing, &start.port)),
99            backend: start.backend,
100            output: Mutex::new(Output {
101                held: HashSet::new(),
102                locked: HashMap::new(),
103                unsure: HashSet::new(),
104                link_up: true,
105                closing: false,
106                session: 0,
107            }),
108            receivers: Mutex::new(Receivers {
109                next_id: 0,
110                entries: Vec::new(),
111            }),
112            click_timing: start.click_timing,
113            holds: Mutex::new(start.holds),
114            opened: Instant::now(),
115            signals: start.signals,
116            port: start.port.clone(),
117            refused: Mutex::new(HashMap::new()),
118        });
119        let link = spawn_link(Arc::downgrade(&core), start.incoming)?;
120        Ok(Self {
121            core,
122            link: Mutex::new(Some(link)),
123            closed: Mutex::new(false),
124        })
125    }
126
127    /// who the box is and what it can do, both read from the current connection. it can change after
128    /// `Reconnected`
129    pub fn snapshot(&self) -> Arc<Snapshot> {
130        self.core.snapshot()
131    }
132
133    /// moves by counts, going through whatever smoothing the box has on
134    pub fn move_by(&self, dx: i16, dy: i16) -> Result<()> {
135        self.core
136            .run(Action::Move, Timing::Any, Command::Move(dx, dy))
137    }
138
139    /// moves by counts on the box's next report, skipping its smoothing for this move
140    pub fn move_now(&self, dx: i16, dy: i16) -> Result<()> {
141        self.core
142            .run(Action::MoveNow, Timing::Any, Command::MoveNow(dx, dy))
143    }
144
145    pub fn press(&self, button: Button) -> Result<()> {
146        self.core
147            .run(Action::Press, Timing::Any, Command::Press(button))
148    }
149
150    pub fn release(&self, button: Button) -> Result<()> {
151        self.core
152            .run(Action::Release, Timing::Any, Command::Release(button))
153    }
154
155    /// the box's own click, or a click timed on the PC with the device's `ClickTiming` if the box doesn't have one
156    pub fn click(&self, button: Button) -> Result<()> {
157        self.click_with(button, Click::default())
158    }
159
160    pub fn click_with(&self, button: Button, click: Click) -> Result<()> {
161        self.core.tap(Usage::Button(button), click)
162    }
163
164    /// holds a key down until `release_key`
165    pub fn press_key(&self, key: Key) -> Result<()> {
166        self.core
167            .run(Action::KeyPress, Timing::Any, Command::KeyPress(key))
168    }
169
170    pub fn release_key(&self, key: Key) -> Result<()> {
171        self.core
172            .run(Action::KeyRelease, Timing::Any, Command::KeyRelease(key))
173    }
174
175    /// presses and lets go, timed by the box, or on the PC with the device's `ClickTiming` if the box can't
176    pub fn tap_key(&self, key: Key) -> Result<()> {
177        self.tap_key_with(key, Click::default())
178    }
179
180    pub fn tap_key_with(&self, key: Key, tap: Click) -> Result<()> {
181        self.core.tap(Usage::Key(key), tap)
182    }
183
184    /// holds a media key down until `release_media`
185    pub fn press_media(&self, key: MediaKey) -> Result<()> {
186        self.core
187            .run(Action::MediaPress, Timing::Any, Command::MediaPress(key))
188    }
189
190    pub fn release_media(&self, key: MediaKey) -> Result<()> {
191        self.core.run(
192            Action::MediaRelease,
193            Timing::Any,
194            Command::MediaRelease(key),
195        )
196    }
197
198    pub fn tap_media(&self, key: MediaKey) -> Result<()> {
199        self.tap_media_with(key, Click::default())
200    }
201
202    pub fn tap_media_with(&self, key: MediaKey, tap: Click) -> Result<()> {
203        self.core.tap(Usage::Media(key), tap)
204    }
205
206    /// keeps the physical button from reaching the PC until `unlock_button`. your own presses still go through and
207    /// its events still come, so you can take the button over
208    pub fn lock_button(&self, button: Button) -> Result<()> {
209        self.core
210            .lock(Action::LockButton, &[LockSlot::Button(button)], LOCKED)
211    }
212
213    pub fn unlock_button(&self, button: Button) -> Result<()> {
214        self.core
215            .lock(Action::LockButton, &[LockSlot::Button(button)], UNLOCKED)
216    }
217
218    /// keeps the physical key from reaching the PC until `unlock_key`
219    pub fn lock_key(&self, key: Key) -> Result<()> {
220        self.core
221            .lock(Action::LockKey, &[LockSlot::Key(key)], LOCKED)
222    }
223
224    pub fn unlock_key(&self, key: Key) -> Result<()> {
225        self.core
226            .lock(Action::LockKey, &[LockSlot::Key(key)], UNLOCKED)
227    }
228
229    /// keeps physical motion one way or both ways along an axis from reaching the PC until `unlock_axis`. your own
230    /// moves still go through
231    pub fn lock_axis(&self, axis: Axis, direction: Direction) -> Result<()> {
232        self.core
233            .lock(Action::LockAxis, &slots(axis, direction), LOCKED)
234    }
235
236    /// unlocks that way along the axis and leaves the other way as it was. it also undoes a scale there
237    pub fn unlock_axis(&self, axis: Axis, direction: Direction) -> Result<()> {
238        self.core
239            .lock(Action::LockAxis, &slots(axis, direction), UNLOCKED)
240    }
241
242    /// keeps `percent` of the physical motion one way or both ways along an axis, from -255 to 255. 100 leaves it
243    /// alone, 0 blocks it and -100 inverts it. `unlock_axis` puts it back to 100
244    pub fn scale_axis(&self, axis: Axis, direction: Direction, percent: i16) -> Result<()> {
245        self.core
246            .lock(Action::ScaleAxis, &slots(axis, direction), percent)
247    }
248
249    pub fn wheel(&self, notches: i16) -> Result<()> {
250        self.core
251            .run(Action::Wheel, Timing::Any, Command::Wheel(notches))
252    }
253
254    /// horizontal scroll, positive goes right
255    pub fn pan(&self, notches: i16) -> Result<()> {
256        self.core
257            .run(Action::Pan, Timing::Any, Command::Pan(notches))
258    }
259
260    /// throws away the moves the box took but hasn't sent yet. while the link is down it fails with `Disconnected`,
261    /// and on a Medius with riding on, the moves it holds back are then only bounded by its riding window
262    pub fn discard_pending_moves(&self) -> Result<()> {
263        self.core.run(
264            Action::DiscardPendingMoves,
265            Timing::Any,
266            Command::DiscardPendingMoves,
267        )
268    }
269
270    /// a new receiver for all the input the box can stream, plus the connection events. the box only streams while
271    /// at least one receiver is alive. just know that a receiver of keys sees every key typed on the keyboard behind
272    /// the box, passwords included
273    pub fn events(&self) -> Events {
274        self.receiver(EventKinds::ALL, false)
275    }
276
277    /// a receiver for only these kinds of input, plus the connection events. the box streams what any receiver
278    /// wants, so a receiver for buttons alone doesn't make it send motion. a kind this box can't stream gets one
279    /// `Rejected` for it right away. on Medius each kind is its own subscription, so the order between two kinds
280    /// is only as good as their box time
281    pub fn events_of(&self, kinds: EventKinds) -> Events {
282        // `ALL` means everything this box has, so it never gets refusals for what it doesn't
283        self.receiver(kinds, !kinds.contains(EventKinds::ALL))
284    }
285
286    fn receiver(&self, kinds: EventKinds, report_unsupported: bool) -> Events {
287        let listeners: Weak<dyn Listeners> = Arc::downgrade(&self.core) as Weak<Core>;
288        let (queue, id) = self.core.add_receiver(kinds, report_unsupported);
289        Events::new(queue, listeners, id)
290    }
291
292    /// the medius crate's own device for this box, for everything only Medius does (clips, rendering, raw catch
293    /// etc), and none on other boxes. it's also where you get the polling rate (`query_rate()`) and the identity the
294    /// box shows the PC (`device_info()`)
295    ///
296    /// to tell a mouse from a keyboard, check the capabilities and not the crate's `DeviceKind`, which calls a mouse
297    /// with a full key bitmap a keyboard
298    ///
299    /// a clone you keep holds the port open after this device is gone. anybox doesn't know about anything you press
300    /// or lock through it, and its `reset()` lets go of and unlocks everything, including what you did through this
301    /// device. `events()` comes back by itself after a `Dropped`, though a button held through the reset can show a
302    /// second press with no release in between. anybox's stream takes up to four of the box's 32 catch entries, so
303    /// if your own subscriptions fill the table the receiver gets a `Rejected` instead. once you've freed some,
304    /// any receiver coming or going asks again. a reboot into a download target keeps the box down
305    /// until you replug it
306    #[cfg(feature = "medius")]
307    pub fn medius(&self) -> Option<medius::Device> {
308        if lock(&self.core.output).closing {
309            return None;
310        }
311        self.core
312            .backend
313            .as_any()
314            .downcast_ref::<crate::family::medius::MediusBackend>()
315            .and_then(crate::family::medius::MediusBackend::device)
316    }
317
318    /// what drop does, but you get to see whether letting go of what you held and unlocking worked, and you choose
319    /// the thread that waits for it (up to about 2 seconds when the box doesn't answer). it works on a shared
320    /// device too. after it every command fails with `Error::Closed` and the receivers end, `events()` gives one
321    /// that's already ended, `snapshot()` still works and `medius()` gives none. a second `close` waits for the
322    /// first one and then says `Closed`. that's not the `Closed` an ended receiver returns
323    pub fn close(&self) -> Result<()> {
324        let mut closed = lock(&self.closed);
325        if *closed {
326            return Err(Error::Closed);
327        }
328        *closed = true;
329        self.shutdown()
330    }
331
332    fn shutdown(&self) -> Result<()> {
333        let released = self.core.release_everything();
334        self.core.backend.shutdown(SHUTDOWN_DEADLINE);
335        let _ = self.core.signals.send(Signal::Stop);
336        if let Some((handle, done)) = lock(&self.link).take() {
337            // a link thread stuck in a dead backend gets left behind, otherwise it would hang the caller
338            if done.recv_timeout(SHUTDOWN_DEADLINE).is_ok() {
339                let _ = handle.join();
340            }
341        }
342        self.core.close_receivers();
343        released
344    }
345}
346
347impl Drop for Device {
348    fn drop(&mut self) {
349        let mut closed = lock(&self.closed);
350        if !*closed {
351            *closed = true;
352            let _ = self.shutdown();
353        }
354    }
355}
356
357impl fmt::Debug for Device {
358    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
359        f.debug_struct("Device")
360            .field("info", &self.snapshot().info)
361            .finish_non_exhaustive()
362    }
363}
364
365fn prepare(snapshot: Snapshot, click_timing: ClickTiming, port: &str) -> Arc<Snapshot> {
366    let Snapshot {
367        mut info,
368        capabilities,
369    } = snapshot;
370    // a backend's snapshot doesn't know the path, the core does and keeps it on every new snapshot
371    info.port = port.to_owned();
372    Arc::new(Snapshot::new(
373        info,
374        capabilities.with_host_taps(click_timing),
375    ))
376}
377
378fn keeps_locked(snapshot: &Snapshot) -> bool {
379    [Action::LockButton, Action::LockAxis, Action::LockKey]
380        .into_iter()
381        .any(|lock| {
382            snapshot
383                .capabilities
384                .get(lock)
385                .quirks
386                .contains(&Quirk::KeepsLocked)
387        })
388}
389
390/// the slots for one way, or both ways, along an axis
391fn slots(axis: Axis, direction: Direction) -> Vec<LockSlot> {
392    let signs: &[Sign] = match direction {
393        Direction::Both => &[Sign::Positive, Sign::Negative],
394        Direction::Positive => &[Sign::Positive],
395        Direction::Negative => &[Sign::Negative],
396    };
397    signs
398        .iter()
399        .map(|&sign| LockSlot::Axis(axis, sign))
400        .collect()
401}
402
403/// a box keeps held buttons and keys for all of them or none, so the quirk sits on whichever press it has
404fn keeps_held(snapshot: &Snapshot) -> bool {
405    [Action::Press, Action::KeyPress, Action::MediaPress]
406        .into_iter()
407        .any(|press| {
408            snapshot
409                .capabilities
410                .get(press)
411                .quirks
412                .contains(&Quirk::KeepsHeld)
413        })
414}
415
416fn max_keys_held(snapshot: &Snapshot) -> Option<u8> {
417    snapshot
418        .capabilities
419        .get(Action::KeyPress)
420        .quirks
421        .iter()
422        .find_map(|quirk| match quirk {
423            Quirk::MaxKeysHeld(max) => Some(*max),
424            _ => None,
425        })
426}
427
428/// the action of the tap itself, then the actions of its press and its release
429fn tap_actions(usage: Usage) -> (Action, Action, Action) {
430    match usage {
431        Usage::Button(_) => (Action::Click, Action::Press, Action::Release),
432        Usage::Key(_) => (Action::KeyTap, Action::KeyPress, Action::KeyRelease),
433        Usage::Media(_) => (Action::MediaTap, Action::MediaPress, Action::MediaRelease),
434    }
435}
436
437fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
438    mutex.lock().unwrap_or_else(PoisonError::into_inner)
439}
440
441fn stream_kinds(snapshot: &Snapshot) -> InputKinds {
442    let capabilities = &snapshot.capabilities;
443    InputKinds {
444        buttons: capabilities.get(Action::ButtonEvents).is_supported(),
445        motion: capabilities.get(Action::MotionEvents).is_supported(),
446        keys: capabilities.get(Action::KeyEvents).is_supported(),
447        media: capabilities.get(Action::MediaEvents).is_supported(),
448    }
449}
450
451impl Core {
452    fn snapshot(&self) -> Arc<Snapshot> {
453        self.snapshot
454            .read()
455            .unwrap_or_else(PoisonError::into_inner)
456            .clone()
457    }
458
459    fn unsupported(
460        snapshot: &Snapshot,
461        action: Action,
462        reason: impl Into<Cow<'static, str>>,
463    ) -> Error {
464        Error::Unsupported {
465            action,
466            family: snapshot.info.family,
467            reason: reason.into(),
468        }
469    }
470
471    /// checks a command against what the box can do. it runs under the output lock, and the link thread swaps
472    /// snapshots under that same lock, so the snapshot checked here is the one the command goes out with
473    fn gate(snapshot: &Snapshot, action: Action, timing: Timing, command: Command) -> Result<()> {
474        let capability = snapshot.capabilities.get(action);
475        match &capability.execution {
476            Execution::Unsupported { reason } => {
477                return Err(Self::unsupported(snapshot, action, reason.clone()));
478            }
479            Execution::Host if timing == Timing::NativeOnly => {
480                return Err(Self::unsupported(
481                    snapshot,
482                    action,
483                    "the box can't do it by itself and you asked for native timing only",
484                ));
485            }
486            _ => {}
487        }
488        match command {
489            Command::Press(button) | Command::Release(button) | Command::Click { button, .. }
490                if !snapshot.capabilities.buttons().contains(&button) =>
491            {
492                Err(Error::InvalidArgument {
493                    action,
494                    detail: format!("the box has no {button} button").into(),
495                })
496            }
497            Command::KeyPress(key) | Command::KeyRelease(key) | Command::KeyTap { key, .. }
498                if !key.is_valid() =>
499            {
500                Err(Error::InvalidArgument {
501                    action,
502                    detail: format!("{key} isn't a key, keys go from 0x04 to 0xE7").into(),
503                })
504            }
505            Command::MediaPress(key)
506            | Command::MediaRelease(key)
507            | Command::MediaTap { key, .. }
508                if key.0 == 0 =>
509            {
510                Err(Error::InvalidArgument {
511                    action,
512                    detail: "media key 0 isn't a key".into(),
513                })
514            }
515            Command::Click {
516                hold: Some(hold), ..
517            }
518            | Command::KeyTap {
519                hold: Some(hold), ..
520            }
521            | Command::MediaTap {
522                hold: Some(hold), ..
523            } => {
524                for quirk in &capability.quirks {
525                    match quirk {
526                        Quirk::FixedClickTiming => {
527                            return Err(Self::unsupported(
528                                snapshot,
529                                action,
530                                "it has its own timing and doesn't take a hold, so press and release it yourself if you need one",
531                            ));
532                        }
533                        Quirk::MaxHold(max) if hold > *max => {
534                            return Err(Error::InvalidArgument {
535                                action,
536                                detail: format!("the box holds it down for at most {max:?}").into(),
537                            });
538                        }
539                        _ => {}
540                    }
541                }
542                Ok(())
543            }
544            Command::Wheel(notches) => {
545                for quirk in &capability.quirks {
546                    if let Quirk::NotchesPerCommand(max) = quirk
547                        && notches.unsigned_abs() > *max
548                    {
549                        return Err(Self::unsupported(
550                            snapshot,
551                            action,
552                            match max {
553                                1 => {
554                                    "it takes one wheel notch per command, send them one at a time"
555                                        .into()
556                                }
557                                _ => format!(
558                                    "it takes at most {max} wheel notches per command, so split it up"
559                                ),
560                            },
561                        ));
562                    }
563                }
564                Ok(())
565            }
566            Command::Lock { slot, keep } => {
567                let detail = match slot {
568                    LockSlot::Button(button)
569                        if !snapshot.capabilities.buttons().contains(&button) =>
570                    {
571                        Some(format!("the box has no {button} button"))
572                    }
573                    LockSlot::Key(key) if !key.is_valid() => {
574                        Some(format!("{key} isn't a key, keys go from 0x04 to 0xE7"))
575                    }
576                    LockSlot::Axis(axis, _) if !snapshot.capabilities.axes().contains(&axis) => {
577                        Some(format!("the box can't lock the {axis} axis"))
578                    }
579                    LockSlot::Button(_) | LockSlot::Key(_) | LockSlot::Axis(..)
580                        if !(-255..=255).contains(&keep) =>
581                    {
582                        Some(format!(
583                            "{keep} is out of range, a scale goes from -255 to 255"
584                        ))
585                    }
586                    LockSlot::Button(_) | LockSlot::Key(_) | LockSlot::Axis(..) => None,
587                };
588                match detail {
589                    Some(detail) => Err(Error::InvalidArgument {
590                        action,
591                        detail: detail.into(),
592                    }),
593                    None => Ok(()),
594                }
595            }
596            Command::Move(..)
597            | Command::MoveNow(..)
598            | Command::Press(_)
599            | Command::Release(_)
600            | Command::Click { hold: None, .. }
601            | Command::Pan(_)
602            | Command::DiscardPendingMoves
603            | Command::KeyPress(_)
604            | Command::KeyRelease(_)
605            | Command::KeyTap { hold: None, .. }
606            | Command::MediaPress(_)
607            | Command::MediaRelease(_)
608            | Command::MediaTap { hold: None, .. } => Ok(()),
609        }
610    }
611
612    /// refuses a key that would go past what the box can hold down at once, counting what anybox already holds
613    fn check_rollover(
614        output: &Output,
615        snapshot: &Snapshot,
616        action: Action,
617        command: Command,
618    ) -> Result<()> {
619        let (Command::KeyPress(key) | Command::KeyTap { key, .. }) = command else {
620            return Ok(());
621        };
622        let Some(max) = max_keys_held(snapshot) else {
623            return Ok(());
624        };
625        if key.is_modifier() || output.held.contains(&Usage::Key(key)) {
626            return Ok(());
627        }
628        let held = output
629            .held
630            .iter()
631            .filter(|usage| matches!(usage, Usage::Key(held) if !held.is_modifier()))
632            .count();
633        if held >= usize::from(max) {
634            return Err(Self::unsupported(
635                snapshot,
636                action,
637                format!("this box holds at most {max} keys at once, let one go first"),
638            ));
639        }
640        Ok(())
641    }
642
643    fn open_output(&self) -> Result<(MutexGuard<'_, Output>, Arc<Snapshot>)> {
644        let output = lock(&self.output);
645        if output.closing {
646            return Err(Error::Closed);
647        }
648        if !output.link_up {
649            return Err(Error::Disconnected);
650        }
651        let snapshot = self.snapshot();
652        Ok((output, snapshot))
653    }
654
655    fn send_locked(
656        &self,
657        output: &mut Output,
658        snapshot: &Snapshot,
659        action: Action,
660        timing: Timing,
661        command: Command,
662    ) -> Result<()> {
663        let held = command.holds();
664        // letting go of something held always goes out, even when the box can't do it any more, otherwise a
665        // keyboard that got pulled out would leave a key anybox can never release
666        let releasing_held = matches!(held, Some((usage, false)) if output.held.contains(&usage));
667        if !releasing_held {
668            Self::gate(snapshot, action, timing, command)?;
669        }
670        Self::check_rollover(output, snapshot, action, command)?;
671        let sent = self.backend.send(command);
672        match (held, &sent) {
673            // a press that ran out of time may still have gone out, and releasing something the box doesn't hold
674            // does nothing, so it counts as held and the cleanup lets go of it
675            (Some((usage, true)), Ok(())) => {
676                output.held.insert(usage);
677                output.unsure.remove(&usage);
678            }
679            (Some((usage, true)), Err(Error::Disconnected)) => {
680                output.held.insert(usage);
681                output.unsure.insert(usage);
682            }
683            (Some((usage, false)), Ok(())) => {
684                output.held.remove(&usage);
685                output.unsure.remove(&usage);
686            }
687            _ => {}
688        }
689        sent
690    }
691
692    fn run(&self, action: Action, timing: Timing, command: Command) -> Result<()> {
693        let (mut output, snapshot) = self.open_output()?;
694        self.send_locked(&mut output, &snapshot, action, timing, command)
695    }
696
697    /// locks, unlocks or scales each slot. unlocking a slot anybox locked always goes out, same as releasing
698    /// something held. every slot gets checked before any of them goes out, so a refused call changes nothing
699    fn lock(&self, action: Action, slots: &[LockSlot], keep: i16) -> Result<()> {
700        let (mut output, snapshot) = self.open_output()?;
701        for &slot in slots {
702            let unlocking_held = keep == UNLOCKED && output.locked.contains_key(&slot);
703            if !unlocking_held {
704                Self::gate(&snapshot, action, Timing::Any, Command::Lock { slot, keep })?;
705            }
706        }
707        let mut first = Ok(());
708        for &slot in slots {
709            let sent = self.backend.send(Command::Lock { slot, keep });
710            if keep == UNLOCKED {
711                if sent.is_ok() {
712                    output.locked.remove(&slot);
713                }
714            } else if matches!(sent, Ok(()) | Err(Error::Disconnected)) {
715                // a lock that ran out of time may still have gone out, and unlocking a slot that isn't locked does
716                // nothing, so it counts as locked
717                output.locked.insert(slot, keep);
718            }
719            if first.is_ok() {
720                first = sent;
721            }
722        }
723        first
724    }
725
726    /// a click or a key or media tap. it's the box's own if it has one, otherwise anybox presses, waits and
727    /// releases from the PC
728    fn tap(&self, usage: Usage, tap: Click) -> Result<()> {
729        let (action, press, release) = tap_actions(usage);
730        let (mut output, snapshot) = self.open_output()?;
731        if snapshot.capabilities.get(action).execution != Execution::Host {
732            return self.send_locked(
733                &mut output,
734                &snapshot,
735                action,
736                tap.timing,
737                usage.tap(tap.hold),
738            );
739        }
740        if tap.timing == Timing::NativeOnly {
741            return Err(Self::unsupported(
742                &snapshot,
743                action,
744                "the box can't time it by itself and you asked for native timing only",
745            ));
746        }
747        let hold = match tap.hold {
748            Some(hold) => hold,
749            None => lock(&self.holds).pick(self.click_timing).ok_or_else(|| {
750                Self::unsupported(
751                    &snapshot,
752                    action,
753                    "the box can't time it by itself and ClickTiming is off, so give it a hold",
754                )
755            })?,
756        };
757        // checked as the tap first, so a bad key or a full box gives an error about the tap and not about its press
758        Self::gate(&snapshot, action, tap.timing, usage.tap(None))?;
759        Self::check_rollover(&output, &snapshot, action, usage.press())?;
760        self.send_locked(&mut output, &snapshot, press, Timing::Any, usage.press())?;
761        let session = output.session;
762        drop(output);
763        thread::sleep(hold);
764        let (mut output, snapshot) = match self.open_output() {
765            Ok(open) => open,
766            Err(error) => {
767                lock(&self.output).unsure.insert(usage);
768                return Err(error);
769            }
770        };
771        if output.session != session && !keeps_held(&snapshot) {
772            // the box disconnected or reset during the hold, so this press was already let go, and the button or
773            // key may belong to a new press by now
774            return Err(Error::Interrupted { action });
775        }
776        let released = self.send_locked(
777            &mut output,
778            &snapshot,
779            release,
780            Timing::Any,
781            usage.release(),
782        );
783        if released.is_err() {
784            output.unsure.insert(usage);
785        }
786        released
787    }
788
789    fn add_receiver(&self, kinds: EventKinds, report_unsupported: bool) -> (Arc<Queue>, u64) {
790        let output = lock(&self.output);
791        let queue = Arc::new(Queue::new(QUEUE_CAPACITY));
792        let id = {
793            let mut receivers = lock(&self.receivers);
794            let id = receivers.next_id;
795            receivers.next_id += 1;
796            if output.closing {
797                queue.close();
798                return (queue, id);
799            }
800            receivers.entries.push(Entry {
801                id,
802                kinds,
803                queue: Arc::downgrade(&queue),
804            });
805            id
806        };
807        let snapshot = self.snapshot();
808        if report_unsupported {
809            let streamable = EventKinds::from_input(stream_kinds(&snapshot));
810            for (_, action) in (kinds & !streamable).each() {
811                queue.push(Event::Rejected { action, count: 1 });
812            }
813        }
814        let refused = self.apply_streams(&output, &snapshot);
815        drop(output);
816        self.publish_all(refused);
817        (queue, id)
818    }
819
820    /// to each receiver that wants this kind of event. connection events and drops from the box go to everyone
821    fn publish(&self, event: Event) {
822        // a MAKCU reports one refused stream switch twice, the refusal itself and the switch that then failed, in
823        // either order, so a second one within a second is the same refusal. ponytail: a real second refusal that
824        // close is merged too, which only costs one count
825        if let Event::Rejected { action, .. } = &event
826            && EventKinds::of_action(*action).is_some()
827        {
828            let now = Instant::now();
829            let mut refused = lock(&self.refused);
830            if refused
831                .get(action)
832                .is_some_and(|at| now.duration_since(*at) < Duration::from_secs(1))
833            {
834                return;
835            }
836            refused.insert(*action, now);
837        }
838        let kind = EventKinds::of(&event);
839        let mut receivers = lock(&self.receivers);
840        receivers
841            .entries
842            .retain(|entry| match entry.queue.upgrade() {
843                Some(queue) => {
844                    if kind.is_none_or(|kind| !(entry.kinds & kind).is_empty()) {
845                        queue.push(event.clone());
846                    }
847                    true
848                }
849                None => false,
850            });
851    }
852
853    fn publish_all(&self, events: Vec<Event>) {
854        for event in events {
855            self.publish(event);
856        }
857    }
858
859    /// only to the receivers that want one of these kinds
860    fn publish_to(&self, kinds: EventKinds, event: Event) {
861        let receivers = lock(&self.receivers);
862        for entry in &receivers.entries {
863            if !(entry.kinds & kinds).is_empty()
864                && let Some(queue) = entry.queue.upgrade()
865            {
866                queue.push(event.clone());
867            }
868        }
869    }
870
871    fn close_receivers(&self) {
872        for entry in lock(&self.receivers).entries.drain(..) {
873            if let Some(queue) = entry.queue.upgrade() {
874                queue.close();
875            }
876        }
877    }
878
879    /// streams what the receivers want and the box can, the empty set too, so nothing streams that nobody wants.
880    /// called with the output lock held, which is what orders it against reconnects and the other receivers. it
881    /// returns the refusals to publish once the output lock is let go, `Disconnected` isn't one because the
882    /// reconnect asks again
883    fn apply_streams(&self, output: &Output, snapshot: &Snapshot) -> Vec<Event> {
884        if output.closing || !output.link_up {
885            return Vec::new();
886        }
887        let union = lock(&self.receivers).union();
888        let wanted = union & EventKinds::from_input(stream_kinds(snapshot));
889        match self.backend.set_streams(wanted.to_input()) {
890            Err(failure) if !matches!(failure.error, Error::Disconnected) => {
891                (EventKinds::from_input(failure.failed) & wanted)
892                    .each()
893                    .map(|(_, action)| Event::Rejected { action, count: 1 })
894                    .collect()
895            }
896            _ => Vec::new(),
897        }
898    }
899
900    /// after a reconnect or a reset this lets go of everything that's held. a box that keeps held buttons and keys
901    /// on its own only gets the ones the app was told failed. a release that fails stays in the record, so drop
902    /// tries it again
903    fn release_held(&self, output: &mut Output, snapshot: &Snapshot) -> bool {
904        let usages = if keeps_held(snapshot) {
905            &output.unsure
906        } else {
907            &output.held
908        };
909        let usages: Vec<Usage> = usages.iter().copied().collect();
910        let mut all_released = true;
911        for usage in usages {
912            if self.backend.send(usage.release()).is_ok() {
913                output.held.remove(&usage);
914                output.unsure.remove(&usage);
915            } else {
916                all_released = false;
917            }
918        }
919        all_released
920    }
921
922    fn release_everything(&self) -> Result<()> {
923        let mut output = lock(&self.output);
924        output.closing = true;
925        output.link_up = false;
926        output.unsure.clear();
927        let held: Vec<Usage> = output.held.drain().collect();
928        let released = if held.is_empty() {
929            Ok(())
930        } else {
931            self.backend.release_all(&held)
932        };
933        let locked: Vec<LockSlot> = output.locked.drain().map(|(slot, _)| slot).collect();
934        let unlocked = if locked.is_empty() {
935            Ok(())
936        } else {
937            self.backend.unlock_all(&locked)
938        };
939        released.and(unlocked)
940    }
941
942    /// sends the app's locks again after a reconnect or a reset, on a box that doesn't keep them itself. a lock the
943    /// box can't do right now waits for a snapshot that can, and if sending one fails the backend fixes it itself
944    fn relock(&self, output: &Output, snapshot: &Snapshot) {
945        if keeps_locked(snapshot) {
946            return;
947        }
948        for (&slot, &keep) in &output.locked {
949            let command = Command::Lock { slot, keep };
950            if Self::gate(snapshot, command.action(), Timing::Any, command).is_ok() {
951                let _ = self.backend.send(command);
952            }
953        }
954    }
955
956    fn handle(&self, signal: Signal) {
957        match signal {
958            Signal::Input {
959                input,
960                arrived,
961                box_time,
962                read,
963            } => {
964                let mut at = Stamp::new(arrived, arrived.saturating_duration_since(self.opened));
965                at.box_time = box_time;
966                if let Some(read) = read {
967                    at.received = read.at;
968                    at.received_since_open = read.at.saturating_duration_since(self.opened);
969                    at.received_qpc = read.qpc;
970                }
971                self.publish(match input {
972                    Input::Button { button, pressed } => Event::Button {
973                        button,
974                        pressed,
975                        at,
976                    },
977                    Input::Motion { dx, dy, wheel, pan } => Event::Motion {
978                        dx,
979                        dy,
980                        wheel,
981                        pan,
982                        at,
983                    },
984                    Input::Key { key, pressed } => Event::Key { key, pressed, at },
985                    Input::Media { key, pressed } => Event::Media { key, pressed, at },
986                });
987            }
988            Signal::Dropped(count, source, kind) => match kind {
989                Some(kind) => self.publish_to(
990                    EventKinds::from_input(kind.kinds()),
991                    Event::Dropped { count, source },
992                ),
993                None => self.publish(Event::Dropped { count, source }),
994            },
995            Signal::StreamLost(kind) => {
996                let output = lock(&self.output);
997                if output.closing {
998                    return;
999                }
1000                let lost = EventKinds::from_input(kind.kinds());
1001                // the box doesn't say how many it lost, so this means at least one, and only to the receivers of
1002                // that kind
1003                self.publish_to(
1004                    lost,
1005                    Event::Dropped {
1006                        count: 1,
1007                        source: DropSource::Device,
1008                    },
1009                );
1010                // only restart it while someone still wants it, checked under the same lock a dropped receiver takes,
1011                // so a stream nobody wants stays off. and only the lost one, because the others are still running
1012                if output.link_up && lock(&self.receivers).union().contains(lost) {
1013                    let restarted = self.backend.restart_stream(kind);
1014                    drop(output);
1015                    if let Err(error) = restarted
1016                        && !matches!(error, Error::Disconnected)
1017                    {
1018                        for (_, action) in lost.each() {
1019                            self.publish(Event::Rejected { action, count: 1 });
1020                        }
1021                    }
1022                }
1023            }
1024            Signal::Refused(action) => self.publish(Event::Rejected { action, count: 1 }),
1025            Signal::Disconnected => {
1026                let mut output = lock(&self.output);
1027                if output.closing {
1028                    return;
1029                }
1030                output.link_up = false;
1031                output.session += 1;
1032                drop(output);
1033                self.publish(Event::Disconnected);
1034            }
1035            Signal::Reconnected(snapshot) => {
1036                let mut output = lock(&self.output);
1037                if output.closing {
1038                    return;
1039                }
1040                output.session += 1;
1041                let snapshot = prepare(*snapshot, self.click_timing, &self.port);
1042                *self
1043                    .snapshot
1044                    .write()
1045                    .unwrap_or_else(PoisonError::into_inner) = snapshot.clone();
1046                if !self.release_held(&mut output, &snapshot) {
1047                    // a release failing right after the box came back means it's gone again, so stay down until
1048                    // the next reconnect gets a clean slate
1049                    let was_up = output.link_up;
1050                    output.link_up = false;
1051                    drop(output);
1052                    if was_up {
1053                        self.publish(Event::Disconnected);
1054                    }
1055                    return;
1056                }
1057                self.relock(&output, &snapshot);
1058                output.link_up = true;
1059                let refused = self.apply_streams(&output, &snapshot);
1060                drop(output);
1061                self.publish(Event::Reconnected);
1062                self.publish_all(refused);
1063            }
1064            Signal::Changed(snapshot) => {
1065                let output = lock(&self.output);
1066                if output.closing {
1067                    return;
1068                }
1069                *self
1070                    .snapshot
1071                    .write()
1072                    .unwrap_or_else(PoisonError::into_inner) =
1073                    prepare(*snapshot, self.click_timing, &self.port);
1074                drop(output);
1075            }
1076            Signal::SessionReset => {
1077                let mut output = lock(&self.output);
1078                if output.closing {
1079                    return;
1080                }
1081                output.session += 1;
1082                let snapshot = self.snapshot();
1083                self.release_held(&mut output, &snapshot);
1084                self.relock(&output, &snapshot);
1085                let refused = self.apply_streams(&output, &snapshot);
1086                drop(output);
1087                self.publish(Event::SessionReset);
1088                self.publish_all(refused);
1089            }
1090            #[cfg(test)]
1091            Signal::Barrier(done) => {
1092                let _ = done.send(());
1093            }
1094            Signal::Stop => {}
1095        }
1096    }
1097}
1098
1099impl Listeners for Core {
1100    fn receiver_dropped(&self, id: u64) {
1101        let output = lock(&self.output);
1102        lock(&self.receivers).entries.retain(|entry| entry.id != id);
1103        let refused = self.apply_streams(&output, &self.snapshot());
1104        drop(output);
1105        self.publish_all(refused);
1106    }
1107}
1108
1109fn spawn_link(
1110    core: Weak<Core>,
1111    incoming: Receiver<Signal>,
1112) -> Result<(JoinHandle<()>, Receiver<()>)> {
1113    let (done, finished) = mpsc::channel();
1114    let handle = thread::Builder::new()
1115        .name("anybox-link".into())
1116        .spawn(move || {
1117            while let Ok(signal) = incoming.recv() {
1118                if matches!(signal, Signal::Stop) {
1119                    break;
1120                }
1121                match core.upgrade() {
1122                    Some(core) => core.handle(signal),
1123                    None => break,
1124                }
1125            }
1126            let _ = done.send(());
1127        })?;
1128    Ok((handle, finished))
1129}
1130
1131#[cfg(test)]
1132impl Device {
1133    /// only the first part of what drop does, so a test can send link events into a device that's closing
1134    pub(crate) fn start_closing(&self) {
1135        let _ = self.core.release_everything();
1136    }
1137
1138    pub(crate) fn backend_for_tests(&self) -> &dyn std::any::Any {
1139        self.core.backend.as_any()
1140    }
1141
1142    pub(crate) fn held(&self) -> HashSet<Usage> {
1143        lock(&self.core.output).held.clone()
1144    }
1145
1146    /// waits until the link thread handled everything that was signaled before this call
1147    pub(crate) fn settle(&self) {
1148        let (done, wait) = mpsc::channel();
1149        let _ = self.core.signals.send(Signal::Barrier(done));
1150        let _ = wait.recv_timeout(Duration::from_secs(5));
1151    }
1152}
1153
1154#[cfg(test)]
1155mod tests;