Skip to main content

dioxus_cameras/
devices.rs

1use std::time::Duration;
2
3use cameras::Device;
4use dioxus::prelude::*;
5
6use crate::channel::Channel;
7
8const POLL_INTERVAL: Duration = Duration::from_millis(50);
9
10/// Handle returned by [`use_devices`].
11#[derive(Copy, Clone, PartialEq)]
12pub struct UseDevices {
13    /// The current list of cameras reported by [`cameras::devices`].
14    ///
15    /// Updated asynchronously by `refresh`, the UI thread is never blocked
16    /// by platform device enumeration.
17    pub devices: Signal<Vec<Device>>,
18    /// Flips from `false` to `true` once the first refresh completes.
19    ///
20    /// Lets the UI distinguish "we haven't scanned yet" (show a loading
21    /// state) from "we scanned and found no cameras" (show an empty state).
22    /// Remains `true` for the rest of the component's lifetime.
23    pub ready: Signal<bool>,
24    /// Callback that rescans the platform for cameras and updates `devices`.
25    ///
26    /// Runs `cameras::devices()` on a worker thread. Errors are swallowed;
27    /// the signal stays at its previous value.
28    pub refresh: Callback<()>,
29}
30
31/// Hook that keeps a [`Signal<Vec<Device>>`] populated with the current camera
32/// list.
33///
34/// Refreshes once on mount. Call `refresh.call(())` (e.g. from a "Refresh"
35/// button's `onclick`) to rescan on demand. Enumeration runs on a worker
36/// thread so the UI thread never stalls while the platform scans hardware.
37pub fn use_devices() -> UseDevices {
38    let mut devices = use_signal(Vec::<Device>::new);
39    let mut ready = use_signal(|| false);
40    let channel = use_hook(Channel::<Vec<Device>>::new);
41
42    let poll_channel = channel.clone();
43    use_hook(move || {
44        spawn(async move {
45            loop {
46                futures_timer::Delay::new(POLL_INTERVAL).await;
47                if let Some(latest) = poll_channel.drain().into_iter().last() {
48                    devices.set(latest);
49                    if !*ready.peek() {
50                        ready.set(true);
51                    }
52                }
53            }
54        })
55    });
56
57    let refresh_tx = channel.sender.clone();
58    let refresh = use_callback(move |()| {
59        let tx = refresh_tx.clone();
60        let _ = std::thread::Builder::new()
61            .name("cameras-devices".into())
62            .spawn(move || {
63                if let Ok(list) = cameras::devices() {
64                    let _ = tx.send(list);
65                }
66            });
67    });
68
69    use_effect(move || refresh.call(()));
70
71    UseDevices {
72        devices,
73        ready,
74        refresh,
75    }
76}