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}