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
//! Watch screen regions and receive typed events over async streams when
//! patterns match — OCR, template matching, and pixel conditions on live
//! captures.
//!
//! # Concepts
//!
//! - A [`Session`] owns one capture stream (a real display/window via
//! [`Target`], or a scripted [`FakeSource`] for headless use). Each new
//! frame lands in a latest-frame slot — newest wins, stale frames drop.
//! - A **watcher** ([`Session::watch`]) evaluates one [`Region`] at its own
//! [`Rate`]: cheap **gates** run first (an implicit pixels-changed gate,
//! plus any you add), then the main [`Detector`].
//! - **Emission modes** decide when detector output becomes an [`Event`]:
//!
//! | builder call | fires |
//! |---|---|
//! | default / `.on_change()` | [`EventKind::Changed`] on output transitions |
//! | `.on_threshold(pred)` | [`EventKind::ThresholdCrossed`] when the predicate flips |
//! | `.template(png, thr)` | [`EventKind::TemplateAppeared`] / `TemplateVanished` |
//! | `.on_every_match()` | [`EventKind::Matched`] every matching tick |
//!
//! - Heavy detectors (OCR — anything with [`Detector::is_heavy`]) run on the
//! blocking thread pool; the async scheduler never stalls on inference.
//!
//! # Quickstart (headless)
//!
//! ```
//! use visual_cortex::{Brightness, EventKind, FakeSource, Frame, Rate, Region, Session};
//!
//! # fn main() -> visual_cortex::Result<()> {
//! let rt = tokio::runtime::Runtime::new().expect("tokio runtime");
//! rt.block_on(async {
//! let session = Session::builder()
//! .source(FakeSource::new([Frame::solid(64, 64, [200, 200, 200, 255])]))
//! .build()
//! .await?;
//!
//! let mut events = session
//! .watch("bright")
//! .region(Region::Full)
//! .rate(Rate::hz(50.0))
//! .detector(Brightness)
//! .subscribe()?;
//!
//! let ev = events.next().await.expect("stream open");
//! assert!(matches!(ev.kind, EventKind::Changed { .. }));
//! Ok(())
//! })
//! # }
//! ```
//!
//! Real capture (macOS): swap the source for
//! `.target(Target::display(0))`. OCR: add the `visual-cortex-ocr-onnx`
//! crate and use `.ocr_text(PaddleOcr::new().await.map_err(Error::engine)?)`.
//! See the [guide](https://github.com/benjamin-small/visual-cortex/blob/main/docs/guide.md)
//! for the full walk-through.
//!
//! # macOS permissions
//!
//! Real capture needs the Screen Recording permission, granted to the
//! *process that runs your binary* (your terminal/IDE). The first request
//! after a fresh grant may still report `PermissionDenied` — macOS applies
//! the grant per process launch, so re-run once after granting.
pub use ;
pub use ;
pub use ;
pub use EventStream;
pub use WatcherBuilder;
// Re-export the building blocks so consumers only need `visual_cortex`.
pub use ;
pub use ;
pub use ;