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
//! The "Lookit!" crate checks for new devices in a cross-platform asynchronous
//! manner. Returns the `RawFd` equivalent for the target platform.
//!
//! - Linux: inotify on /dev/*
//! - Web: JavaScript event listeners
//! - Others: TODO
//!
//! ## Getting Started
//! ```rust, no_run
//! use lookit::Searcher;
//! use pasts::prelude::*;
//!
//! #[async_main::async_main]
//! async fn main(_spawner: impl async_main::Spawn) {
//! let mut searcher = Searcher::with_camera();
//! loop {
//! let file = searcher.next().await;
//! dbg!(file);
//! }
//! }
//! ```
//!
//! ## Implementation
//! Input
//! - inotify => /dev/input/event*
//! - `window.addEventListener("gamepadconnected", function(e) { });`
//!
//! Audio
//! - inotify => /dev/snd/pcm*
//! - `navigator.mediaDevices.getUserMedia(constraints).then(function(s) {
//! }).catch(function(denied_err) {})` // only one speakers connection ever
//!
//! MIDI
//! - inotify => /dev/snd/midi*, if no /dev/snd then /dev/midi*
//! - <https://developer.mozilla.org/en-US/docs/Web/API/MIDIAccess>
//!
//! Camera
//! - inotify => /dev/video*
//! - `navigator.mediaDevices.getUserMedia(constraints).then(function(s) {
//! }).catch(function(denied_err) {})`
#![warn(
anonymous_parameters,
missing_copy_implementations,
missing_debug_implementations,
missing_docs,
nonstandard_style,
rust_2018_idioms,
single_use_lifetimes,
trivial_casts,
trivial_numeric_casts,
unreachable_pub,
unused_extern_crates,
unused_qualifications,
variant_size_differences
)]
#[cfg_attr(target_os = "linux", path = "linux.rs")]
#[cfg_attr(not(target_os = "linux"), path = "mock.rs")]
mod platform;
use std::{cell::Cell, fmt};
use pasts::prelude::*;
use smelling_salts::Device;
/// Device kinds
enum Kind {
Input(),
Audio(),
Midi(),
Camera(),
}
enum Events {
Read(),
Write(),
All(),
}
/// Platform implementation
struct Platform;
/// Interface should be implemented for each `Platform`
trait Interface {
type Searcher: Notify<Event = Found> + Send + Unpin;
/// Create a searcher for a specific type of device
fn searcher(kind: Kind) -> Option<Self::Searcher>;
/// Try to watch a found device for both read+write events
fn open(found: Found, events: Events) -> Result<Device, Found>;
}
/// Lookit [`Notify`]. Lets you know when a device is [`Found`].
pub struct Searcher(Cell<Option<<Platform as Interface>::Searcher>>);
impl fmt::Debug for Searcher {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Searcher").finish_non_exhaustive()
}
}
impl Searcher {
/// Create new future checking for input devices.
pub fn with_input() -> Self {
Self(Platform::searcher(Kind::Input()).into())
}
/// Create new future checking for audio devices (speakers, microphones).
pub fn with_audio() -> Self {
Self(Platform::searcher(Kind::Audio()).into())
}
/// Create new future checking for MIDI devices.
pub fn with_midi() -> Self {
Self(Platform::searcher(Kind::Midi()).into())
}
/// Create new future checking for camera devices.
pub fn with_camera() -> Self {
Self(Platform::searcher(Kind::Camera()).into())
}
}
impl Notify for Searcher {
type Event = Found;
fn poll_next(mut self: Pin<&mut Self>, task: &mut Task<'_>) -> Poll<Found> {
let Some(ref mut notifier) = self.0.get_mut() else { return Pending };
Pin::new(notifier).poll_next(task)
}
}
/// Device found by the [`Searcher`] notifier.
pub struct Found(Cell<String>);
impl fmt::Debug for Found {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let path = self.0.take();
f.debug_struct("Found").field("path", &path).finish()?;
self.0.set(path);
Ok(())
}
}
impl Found {
/// Connect to device (input + output)
pub fn connect(self) -> Result<Device, Found> {
Platform::open(self, Events::All())
}
/// Connect to device (input only)
pub fn connect_input(self) -> Result<Device, Found> {
Platform::open(self, Events::Read())
}
/// Connect to device (output only)
pub fn connect_output(self) -> Result<Device, Found> {
Platform::open(self, Events::Write())
}
}