diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..fa8629f9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "rust-analyzer.check.features": ["dxgi", "dx11", "bitmap", "metal","iosurface"], + "rust-analyzer.cargo.features": ["dxgi", "dx11", "bitmap", "metal","iosurface"], +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..dbb44832 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,68 @@ +[package] +name = "crabgrab" +version = "0.1.0" +edition = "2021" + +[features] +iosurface = [] +metal = ["dep:metal"] +dxgi = [] +dx11 = ["dxgi"] +bitmap = ["dep:bytemuck", "dep:half"] +wgpu = [] + +[target.'cfg(target_os = "windows")'.features] +bitmap = ["dx11"] + +[dependencies] +futures = "0.3" +parking_lot = "0.12" +half = { version = "2.4", optional = true } +bytemuck = { version = "1.15", optional = true } + +[target.'cfg(target_os = "macos")'.dependencies] +cocoa-foundation = "0.1.0" +block = "0.1.6" +objc = "0.2.7" +cocoa = "0.24" +libc = "~0.2.33" +objc2 = "0.3.0-beta.5" +metal = { version = "0.27", optional = true } +lazy_static = "1.4" +core-graphics-types = "*" +mach2 = "*" + +[target.'cfg(target_os = "windows")'.dependencies] +windows = { version = "0.52", features = [ + "Win32_Foundation", + "Win32_System_Threading", + "Win32_UI_WindowsAndMessaging", + "Win32_Graphics_Gdi", + "Graphics_Capture", + "Graphics_DirectX_Direct3D11", + "Win32_Graphics", + "Win32_Graphics_Direct3D_Fxc", + "Win32_Graphics_Direct3D", + "Win32_Graphics_Direct3D10", + "Win32_Graphics_Direct3D11", + "Win32_Graphics_Dwm", + "Win32_Graphics_Dxgi_Common", + "Win32_Graphics_Dxgi", + "Win32_Graphics_Gdi", + "Win32_UI_HiDpi", + "Win32_Graphics_Hlsl", + "Win32_Media_Audio", + "Win32_System_ProcessStatus", + "Win32_System_WinRT_Direct3D11", + "Win32_System_WinRT_Graphics_Capture", + "Win32_System_WinRT", + "Win32_System_Com", + "Win32_System_Com_StructuredStorage", + "Win32_System_Variant", + "Foundation", + "Security_Authorization_AppCapabilityAccess" +] } + +[dev-dependencies] +tokio = { version = "*", features = ["rt", "macros", "rt-multi-thread"] } +winit = "*" diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..960ed073 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,12 @@ + + + + CrabGrab documentation + + + MacOS Documentation +
+
+ Windows Documentation + + diff --git a/docs/macos_docs/crabgrab/all.html b/docs/macos_docs/crabgrab/all.html new file mode 100644 index 00000000..449a0af4 --- /dev/null +++ b/docs/macos_docs/crabgrab/all.html @@ -0,0 +1 @@ +List of all items in this crate

List of all items

Structs

Enums

Traits

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capturable_content/enum.CapturableContentError.html b/docs/macos_docs/crabgrab/capturable_content/enum.CapturableContentError.html new file mode 100644 index 00000000..8e430c22 --- /dev/null +++ b/docs/macos_docs/crabgrab/capturable_content/enum.CapturableContentError.html @@ -0,0 +1,17 @@ +CapturableContentError in crabgrab::capturable_content - Rust
pub enum CapturableContentError {
+    Other(String),
+}
Expand description

Represents an error that occured when enumerating capturable content

+

Variants§

§

Other(String)

Trait Implementations§

source§

impl Clone for CapturableContentError

source§

fn clone(&self) -> CapturableContentError

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for CapturableContentError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for CapturableContentError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for CapturableContentError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capturable_content/index.html b/docs/macos_docs/crabgrab/capturable_content/index.html new file mode 100644 index 00000000..03174b1c --- /dev/null +++ b/docs/macos_docs/crabgrab/capturable_content/index.html @@ -0,0 +1 @@ +crabgrab::capturable_content - Rust

Structs

Enums

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capturable_content/sidebar-items.js b/docs/macos_docs/crabgrab/capturable_content/sidebar-items.js new file mode 100644 index 00000000..8e201021 --- /dev/null +++ b/docs/macos_docs/crabgrab/capturable_content/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["CapturableContentError"],"struct":["CapturableApplication","CapturableContent","CapturableContentFilter","CapturableDisplay","CapturableDisplayIterator","CapturableWindow","CapturableWindowFilter","CapturableWindowIterator"]}; \ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capturable_content/struct.CapturableApplication.html b/docs/macos_docs/crabgrab/capturable_content/struct.CapturableApplication.html new file mode 100644 index 00000000..3dbf844e --- /dev/null +++ b/docs/macos_docs/crabgrab/capturable_content/struct.CapturableApplication.html @@ -0,0 +1,14 @@ +CapturableApplication in crabgrab::capturable_content - Rust
pub struct CapturableApplication { /* private fields */ }

Implementations§

source§

impl CapturableApplication

source

pub fn identifier(&self) -> String

Gets the “identifier” of the application

+

On Macos, this is the application bundle, and on windows, this is the application file name

+

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capturable_content/struct.CapturableContent.html b/docs/macos_docs/crabgrab/capturable_content/struct.CapturableContent.html new file mode 100644 index 00000000..3f4290b9 --- /dev/null +++ b/docs/macos_docs/crabgrab/capturable_content/struct.CapturableContent.html @@ -0,0 +1,19 @@ +CapturableContent in crabgrab::capturable_content - Rust
pub struct CapturableContent { /* private fields */ }

Implementations§

source§

impl CapturableContent

source

pub async fn new( + filter: CapturableContentFilter +) -> Result<Self, CapturableContentError>

Requests capturable content from the OS

+

Note that the returned capturable content may be stale - for example, a window enumerated in this capturable content +may have been closed before it is used to open a stream, and creating a stream for that window will result in an error.

+
source

pub fn windows<'a>(&'a self) -> CapturableWindowIterator<'a>

Get an iterator over the capturable windows

+
source

pub fn displays<'a>(&'a self) -> CapturableDisplayIterator<'a>

Get an iterator over the capturable displays

+

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capturable_content/struct.CapturableContentFilter.html b/docs/macos_docs/crabgrab/capturable_content/struct.CapturableContentFilter.html new file mode 100644 index 00000000..a644cb9e --- /dev/null +++ b/docs/macos_docs/crabgrab/capturable_content/struct.CapturableContentFilter.html @@ -0,0 +1,19 @@ +CapturableContentFilter in crabgrab::capturable_content - Rust
pub struct CapturableContentFilter {
+    pub windows: Option<CapturableWindowFilter>,
+    pub displays: bool,
+}
Expand description

Selects the kind of capturable content to enumerate

+

Fields§

§windows: Option<CapturableWindowFilter>

What kind of capturable windows, if Some, to enumerate

+
§displays: bool

Whether to enumerate capturable displays

+

Implementations§

source§

impl CapturableContentFilter

source

pub fn is_empty(&self) -> bool

Whether this filter allows any capturable content

+

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capturable_content/struct.CapturableDisplay.html b/docs/macos_docs/crabgrab/capturable_content/struct.CapturableDisplay.html new file mode 100644 index 00000000..68165267 --- /dev/null +++ b/docs/macos_docs/crabgrab/capturable_content/struct.CapturableDisplay.html @@ -0,0 +1,16 @@ +CapturableDisplay in crabgrab::capturable_content - Rust
pub struct CapturableDisplay { /* private fields */ }
Expand description

Represents a capturable display

+

Implementations§

source§

impl CapturableDisplay

source

pub fn rect(&self) -> Rect

Gets the virtual screen rectangle of this display

+

Note: Currently on windows, this is only evaluated at the time of display enumeration

+

Trait Implementations§

source§

impl Clone for CapturableDisplay

source§

fn clone(&self) -> CapturableDisplay

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for CapturableDisplay

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capturable_content/struct.CapturableDisplayIterator.html b/docs/macos_docs/crabgrab/capturable_content/struct.CapturableDisplayIterator.html new file mode 100644 index 00000000..99e4402f --- /dev/null +++ b/docs/macos_docs/crabgrab/capturable_content/struct.CapturableDisplayIterator.html @@ -0,0 +1,191 @@ +CapturableDisplayIterator in crabgrab::capturable_content - Rust
pub struct CapturableDisplayIterator<'content> { /* private fields */ }
Expand description

An iterator over capturable displays

+

Trait Implementations§

source§

impl ExactSizeIterator for CapturableDisplayIterator<'_>

source§

fn len(&self) -> usize

Returns the exact remaining length of the iterator. Read more
source§

fn is_empty(&self) -> bool

🔬This is a nightly-only experimental API. (exact_size_is_empty)
Returns true if the iterator is empty. Read more
source§

impl Iterator for CapturableDisplayIterator<'_>

§

type Item = CapturableDisplay

The type of the elements being iterated over.
source§

fn next(&mut self) -> Option<Self::Item>

Advances the iterator and returns the next value. Read more
source§

fn size_hint(&self) -> (usize, Option<usize>)

Returns the bounds on the remaining length of the iterator. Read more
source§

fn next_chunk<const N: usize>( + &mut self +) -> Result<[Self::Item; N], IntoIter<Self::Item, N>>where + Self: Sized,

🔬This is a nightly-only experimental API. (iter_next_chunk)
Advances the iterator and returns an array containing the next N values. Read more
1.0.0 · source§

fn count(self) -> usizewhere + Self: Sized,

Consumes the iterator, counting the number of iterations and returning it. Read more
1.0.0 · source§

fn last(self) -> Option<Self::Item>where + Self: Sized,

Consumes the iterator, returning the last element. Read more
source§

fn advance_by(&mut self, n: usize) -> Result<(), NonZeroUsize>

🔬This is a nightly-only experimental API. (iter_advance_by)
Advances the iterator by n elements. Read more
1.0.0 · source§

fn nth(&mut self, n: usize) -> Option<Self::Item>

Returns the nth element of the iterator. Read more
1.28.0 · source§

fn step_by(self, step: usize) -> StepBy<Self>where + Self: Sized,

Creates an iterator starting at the same point, but stepping by +the given amount at each iteration. Read more
1.0.0 · source§

fn chain<U>(self, other: U) -> Chain<Self, <U as IntoIterator>::IntoIter>where + Self: Sized, + U: IntoIterator<Item = Self::Item>,

Takes two iterators and creates a new iterator over both in sequence. Read more
1.0.0 · source§

fn zip<U>(self, other: U) -> Zip<Self, <U as IntoIterator>::IntoIter>where + Self: Sized, + U: IntoIterator,

‘Zips up’ two iterators into a single iterator of pairs. Read more
source§

fn intersperse_with<G>(self, separator: G) -> IntersperseWith<Self, G>where + Self: Sized, + G: FnMut() -> Self::Item,

🔬This is a nightly-only experimental API. (iter_intersperse)
Creates a new iterator which places an item generated by separator +between adjacent items of the original iterator. Read more
1.0.0 · source§

fn map<B, F>(self, f: F) -> Map<Self, F>where + Self: Sized, + F: FnMut(Self::Item) -> B,

Takes a closure and creates an iterator which calls that closure on each +element. Read more
1.21.0 · source§

fn for_each<F>(self, f: F)where + Self: Sized, + F: FnMut(Self::Item),

Calls a closure on each element of an iterator. Read more
1.0.0 · source§

fn filter<P>(self, predicate: P) -> Filter<Self, P>where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Creates an iterator which uses a closure to determine if an element +should be yielded. Read more
1.0.0 · source§

fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F>where + Self: Sized, + F: FnMut(Self::Item) -> Option<B>,

Creates an iterator that both filters and maps. Read more
1.0.0 · source§

fn enumerate(self) -> Enumerate<Self>where + Self: Sized,

Creates an iterator which gives the current iteration count as well as +the next value. Read more
1.0.0 · source§

fn peekable(self) -> Peekable<Self>where + Self: Sized,

Creates an iterator which can use the peek and peek_mut methods +to look at the next element of the iterator without consuming it. See +their documentation for more information. Read more
1.0.0 · source§

fn skip_while<P>(self, predicate: P) -> SkipWhile<Self, P>where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Creates an iterator that skips elements based on a predicate. Read more
1.0.0 · source§

fn take_while<P>(self, predicate: P) -> TakeWhile<Self, P>where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Creates an iterator that yields elements based on a predicate. Read more
1.57.0 · source§

fn map_while<B, P>(self, predicate: P) -> MapWhile<Self, P>where + Self: Sized, + P: FnMut(Self::Item) -> Option<B>,

Creates an iterator that both yields elements based on a predicate and maps. Read more
1.0.0 · source§

fn skip(self, n: usize) -> Skip<Self>where + Self: Sized,

Creates an iterator that skips the first n elements. Read more
1.0.0 · source§

fn take(self, n: usize) -> Take<Self>where + Self: Sized,

Creates an iterator that yields the first n elements, or fewer +if the underlying iterator ends sooner. Read more
1.0.0 · source§

fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F>where + Self: Sized, + F: FnMut(&mut St, Self::Item) -> Option<B>,

An iterator adapter which, like fold, holds internal state, but +unlike fold, produces a new iterator. Read more
1.0.0 · source§

fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F>where + Self: Sized, + U: IntoIterator, + F: FnMut(Self::Item) -> U,

Creates an iterator that works like map, but flattens nested structure. Read more
source§

fn map_windows<F, R, const N: usize>(self, f: F) -> MapWindows<Self, F, N>where + Self: Sized, + F: FnMut(&[Self::Item; N]) -> R,

🔬This is a nightly-only experimental API. (iter_map_windows)
Calls the given function f for each contiguous window of size N over +self and returns an iterator over the outputs of f. Like slice::windows(), +the windows during mapping overlap as well. Read more
1.0.0 · source§

fn fuse(self) -> Fuse<Self>where + Self: Sized,

Creates an iterator which ends after the first None. Read more
1.0.0 · source§

fn inspect<F>(self, f: F) -> Inspect<Self, F>where + Self: Sized, + F: FnMut(&Self::Item),

Does something with each element of an iterator, passing the value on. Read more
1.0.0 · source§

fn by_ref(&mut self) -> &mut Selfwhere + Self: Sized,

Borrows an iterator, rather than consuming it. Read more
1.0.0 · source§

fn collect<B>(self) -> Bwhere + B: FromIterator<Self::Item>, + Self: Sized,

Transforms an iterator into a collection. Read more
source§

fn collect_into<E>(self, collection: &mut E) -> &mut Ewhere + E: Extend<Self::Item>, + Self: Sized,

🔬This is a nightly-only experimental API. (iter_collect_into)
Collects all the items from an iterator into a collection. Read more
1.0.0 · source§

fn partition<B, F>(self, f: F) -> (B, B)where + Self: Sized, + B: Default + Extend<Self::Item>, + F: FnMut(&Self::Item) -> bool,

Consumes an iterator, creating two collections from it. Read more
source§

fn is_partitioned<P>(self, predicate: P) -> boolwhere + Self: Sized, + P: FnMut(Self::Item) -> bool,

🔬This is a nightly-only experimental API. (iter_is_partitioned)
Checks if the elements of this iterator are partitioned according to the given predicate, +such that all those that return true precede all those that return false. Read more
1.27.0 · source§

fn try_fold<B, F, R>(&mut self, init: B, f: F) -> Rwhere + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try<Output = B>,

An iterator method that applies a function as long as it returns +successfully, producing a single, final value. Read more
1.27.0 · source§

fn try_for_each<F, R>(&mut self, f: F) -> Rwhere + Self: Sized, + F: FnMut(Self::Item) -> R, + R: Try<Output = ()>,

An iterator method that applies a fallible function to each item in the +iterator, stopping at the first error and returning that error. Read more
1.0.0 · source§

fn fold<B, F>(self, init: B, f: F) -> Bwhere + Self: Sized, + F: FnMut(B, Self::Item) -> B,

Folds every element into an accumulator by applying an operation, +returning the final result. Read more
1.51.0 · source§

fn reduce<F>(self, f: F) -> Option<Self::Item>where + Self: Sized, + F: FnMut(Self::Item, Self::Item) -> Self::Item,

Reduces the elements to a single one, by repeatedly applying a reducing +operation. Read more
source§

fn try_reduce<F, R>( + &mut self, + f: F +) -> <<R as Try>::Residual as Residual<Option<<R as Try>::Output>>>::TryTypewhere + Self: Sized, + F: FnMut(Self::Item, Self::Item) -> R, + R: Try<Output = Self::Item>, + <R as Try>::Residual: Residual<Option<Self::Item>>,

🔬This is a nightly-only experimental API. (iterator_try_reduce)
Reduces the elements to a single one by repeatedly applying a reducing operation. If the +closure returns a failure, the failure is propagated back to the caller immediately. Read more
1.0.0 · source§

fn all<F>(&mut self, f: F) -> boolwhere + Self: Sized, + F: FnMut(Self::Item) -> bool,

Tests if every element of the iterator matches a predicate. Read more
1.0.0 · source§

fn any<F>(&mut self, f: F) -> boolwhere + Self: Sized, + F: FnMut(Self::Item) -> bool,

Tests if any element of the iterator matches a predicate. Read more
1.0.0 · source§

fn find<P>(&mut self, predicate: P) -> Option<Self::Item>where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Searches for an element of an iterator that satisfies a predicate. Read more
1.30.0 · source§

fn find_map<B, F>(&mut self, f: F) -> Option<B>where + Self: Sized, + F: FnMut(Self::Item) -> Option<B>,

Applies function to the elements of iterator and returns +the first non-none result. Read more
source§

fn try_find<F, R>( + &mut self, + f: F +) -> <<R as Try>::Residual as Residual<Option<Self::Item>>>::TryTypewhere + Self: Sized, + F: FnMut(&Self::Item) -> R, + R: Try<Output = bool>, + <R as Try>::Residual: Residual<Option<Self::Item>>,

🔬This is a nightly-only experimental API. (try_find)
Applies function to the elements of iterator and returns +the first true result or the first error. Read more
1.0.0 · source§

fn position<P>(&mut self, predicate: P) -> Option<usize>where + Self: Sized, + P: FnMut(Self::Item) -> bool,

Searches for an element in an iterator, returning its index. Read more
1.6.0 · source§

fn max_by_key<B, F>(self, f: F) -> Option<Self::Item>where + B: Ord, + Self: Sized, + F: FnMut(&Self::Item) -> B,

Returns the element that gives the maximum value from the +specified function. Read more
1.15.0 · source§

fn max_by<F>(self, compare: F) -> Option<Self::Item>where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering,

Returns the element that gives the maximum value with respect to the +specified comparison function. Read more
1.6.0 · source§

fn min_by_key<B, F>(self, f: F) -> Option<Self::Item>where + B: Ord, + Self: Sized, + F: FnMut(&Self::Item) -> B,

Returns the element that gives the minimum value from the +specified function. Read more
1.15.0 · source§

fn min_by<F>(self, compare: F) -> Option<Self::Item>where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering,

Returns the element that gives the minimum value with respect to the +specified comparison function. Read more
1.0.0 · source§

fn unzip<A, B, FromA, FromB>(self) -> (FromA, FromB)where + FromA: Default + Extend<A>, + FromB: Default + Extend<B>, + Self: Sized + Iterator<Item = (A, B)>,

Converts an iterator of pairs into a pair of containers. Read more
1.36.0 · source§

fn copied<'a, T>(self) -> Copied<Self>where + T: 'a + Copy, + Self: Sized + Iterator<Item = &'a T>,

Creates an iterator which copies all of its elements. Read more
1.0.0 · source§

fn cloned<'a, T>(self) -> Cloned<Self>where + T: 'a + Clone, + Self: Sized + Iterator<Item = &'a T>,

Creates an iterator which clones all of its elements. Read more
source§

fn array_chunks<const N: usize>(self) -> ArrayChunks<Self, N>where + Self: Sized,

🔬This is a nightly-only experimental API. (iter_array_chunks)
Returns an iterator over N elements of the iterator at a time. Read more
1.11.0 · source§

fn sum<S>(self) -> Swhere + Self: Sized, + S: Sum<Self::Item>,

Sums the elements of an iterator. Read more
1.11.0 · source§

fn product<P>(self) -> Pwhere + Self: Sized, + P: Product<Self::Item>,

Iterates over the entire iterator, multiplying all the elements Read more
source§

fn cmp_by<I, F>(self, other: I, cmp: F) -> Orderingwhere + Self: Sized, + I: IntoIterator, + F: FnMut(Self::Item, <I as IntoIterator>::Item) -> Ordering,

🔬This is a nightly-only experimental API. (iter_order_by)
Lexicographically compares the elements of this Iterator with those +of another with respect to the specified comparison function. Read more
1.5.0 · source§

fn partial_cmp<I>(self, other: I) -> Option<Ordering>where + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Lexicographically compares the PartialOrd elements of +this Iterator with those of another. The comparison works like short-circuit +evaluation, returning a result without comparing the remaining elements. +As soon as an order can be determined, the evaluation stops and a result is returned. Read more
source§

fn partial_cmp_by<I, F>(self, other: I, partial_cmp: F) -> Option<Ordering>where + Self: Sized, + I: IntoIterator, + F: FnMut(Self::Item, <I as IntoIterator>::Item) -> Option<Ordering>,

🔬This is a nightly-only experimental API. (iter_order_by)
Lexicographically compares the elements of this Iterator with those +of another with respect to the specified comparison function. Read more
1.5.0 · source§

fn eq<I>(self, other: I) -> boolwhere + I: IntoIterator, + Self::Item: PartialEq<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are equal to those of +another. Read more
source§

fn eq_by<I, F>(self, other: I, eq: F) -> boolwhere + Self: Sized, + I: IntoIterator, + F: FnMut(Self::Item, <I as IntoIterator>::Item) -> bool,

🔬This is a nightly-only experimental API. (iter_order_by)
Determines if the elements of this Iterator are equal to those of +another with respect to the specified equality function. Read more
1.5.0 · source§

fn ne<I>(self, other: I) -> boolwhere + I: IntoIterator, + Self::Item: PartialEq<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are not equal to those of +another. Read more
1.5.0 · source§

fn lt<I>(self, other: I) -> boolwhere + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are lexicographically +less than those of another. Read more
1.5.0 · source§

fn le<I>(self, other: I) -> boolwhere + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are lexicographically +less or equal to those of another. Read more
1.5.0 · source§

fn gt<I>(self, other: I) -> boolwhere + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are lexicographically +greater than those of another. Read more
1.5.0 · source§

fn ge<I>(self, other: I) -> boolwhere + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are lexicographically +greater than or equal to those of another. Read more
source§

fn is_sorted_by<F>(self, compare: F) -> boolwhere + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Option<Ordering>,

🔬This is a nightly-only experimental API. (is_sorted)
Checks if the elements of this iterator are sorted using the given comparator function. Read more
source§

fn is_sorted_by_key<F, K>(self, f: F) -> boolwhere + Self: Sized, + F: FnMut(Self::Item) -> K, + K: PartialOrd,

🔬This is a nightly-only experimental API. (is_sorted)
Checks if the elements of this iterator are sorted using the given key extraction +function. Read more

Auto Trait Implementations§

§

impl<'content> RefUnwindSafe for CapturableDisplayIterator<'content>

§

impl<'content> !Send for CapturableDisplayIterator<'content>

§

impl<'content> !Sync for CapturableDisplayIterator<'content>

§

impl<'content> Unpin for CapturableDisplayIterator<'content>

§

impl<'content> UnwindSafe for CapturableDisplayIterator<'content>

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<I> IntoIterator for Iwhere + I: Iterator,

§

type Item = <I as Iterator>::Item

The type of the elements being iterated over.
§

type IntoIter = I

Which kind of iterator are we turning this into?
const: unstable · source§

fn into_iter(self) -> I

Creates an iterator from a value. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capturable_content/struct.CapturableWindow.html b/docs/macos_docs/crabgrab/capturable_content/struct.CapturableWindow.html new file mode 100644 index 00000000..e5184117 --- /dev/null +++ b/docs/macos_docs/crabgrab/capturable_content/struct.CapturableWindow.html @@ -0,0 +1,17 @@ +CapturableWindow in crabgrab::capturable_content - Rust
pub struct CapturableWindow { /* private fields */ }
Expand description

Represents a capturable application window

+

Implementations§

source§

impl CapturableWindow

source

pub fn title(&self) -> String

Gets the title of the window

+
source

pub fn rect(&self) -> Rect

Gets the virtual screen rectangle of the window

+
source

pub fn application(&self) -> CapturableApplication

Gets the application that owns this window

+

Trait Implementations§

source§

impl Clone for CapturableWindow

source§

fn clone(&self) -> CapturableWindow

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for CapturableWindow

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capturable_content/struct.CapturableWindowFilter.html b/docs/macos_docs/crabgrab/capturable_content/struct.CapturableWindowFilter.html new file mode 100644 index 00000000..079d8dbd --- /dev/null +++ b/docs/macos_docs/crabgrab/capturable_content/struct.CapturableWindowFilter.html @@ -0,0 +1,18 @@ +CapturableWindowFilter in crabgrab::capturable_content - Rust
pub struct CapturableWindowFilter {
+    pub desktop_windows: bool,
+    pub onscreen_only: bool,
+}
Expand description

Selects the kind of windows to enumerate for capture

+

Fields§

§desktop_windows: bool

Desktop windows are elements of the desktop environment, E.G. the dock on macos or the start bar on windows.

+
§onscreen_only: bool

Whether to restrict to onscreen windows

+

Trait Implementations§

source§

impl Default for CapturableWindowFilter

source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capturable_content/struct.CapturableWindowIterator.html b/docs/macos_docs/crabgrab/capturable_content/struct.CapturableWindowIterator.html new file mode 100644 index 00000000..d39b48c3 --- /dev/null +++ b/docs/macos_docs/crabgrab/capturable_content/struct.CapturableWindowIterator.html @@ -0,0 +1,191 @@ +CapturableWindowIterator in crabgrab::capturable_content - Rust
pub struct CapturableWindowIterator<'content> { /* private fields */ }
Expand description

An iterator over capturable windows

+

Trait Implementations§

source§

impl ExactSizeIterator for CapturableWindowIterator<'_>

1.0.0 · source§

fn len(&self) -> usize

Returns the exact remaining length of the iterator. Read more
source§

fn is_empty(&self) -> bool

🔬This is a nightly-only experimental API. (exact_size_is_empty)
Returns true if the iterator is empty. Read more
source§

impl Iterator for CapturableWindowIterator<'_>

§

type Item = CapturableWindow

The type of the elements being iterated over.
source§

fn next(&mut self) -> Option<Self::Item>

Advances the iterator and returns the next value. Read more
source§

fn size_hint(&self) -> (usize, Option<usize>)

Returns the bounds on the remaining length of the iterator. Read more
source§

fn next_chunk<const N: usize>( + &mut self +) -> Result<[Self::Item; N], IntoIter<Self::Item, N>>where + Self: Sized,

🔬This is a nightly-only experimental API. (iter_next_chunk)
Advances the iterator and returns an array containing the next N values. Read more
1.0.0 · source§

fn count(self) -> usizewhere + Self: Sized,

Consumes the iterator, counting the number of iterations and returning it. Read more
1.0.0 · source§

fn last(self) -> Option<Self::Item>where + Self: Sized,

Consumes the iterator, returning the last element. Read more
source§

fn advance_by(&mut self, n: usize) -> Result<(), NonZeroUsize>

🔬This is a nightly-only experimental API. (iter_advance_by)
Advances the iterator by n elements. Read more
1.0.0 · source§

fn nth(&mut self, n: usize) -> Option<Self::Item>

Returns the nth element of the iterator. Read more
1.28.0 · source§

fn step_by(self, step: usize) -> StepBy<Self>where + Self: Sized,

Creates an iterator starting at the same point, but stepping by +the given amount at each iteration. Read more
1.0.0 · source§

fn chain<U>(self, other: U) -> Chain<Self, <U as IntoIterator>::IntoIter>where + Self: Sized, + U: IntoIterator<Item = Self::Item>,

Takes two iterators and creates a new iterator over both in sequence. Read more
1.0.0 · source§

fn zip<U>(self, other: U) -> Zip<Self, <U as IntoIterator>::IntoIter>where + Self: Sized, + U: IntoIterator,

‘Zips up’ two iterators into a single iterator of pairs. Read more
source§

fn intersperse_with<G>(self, separator: G) -> IntersperseWith<Self, G>where + Self: Sized, + G: FnMut() -> Self::Item,

🔬This is a nightly-only experimental API. (iter_intersperse)
Creates a new iterator which places an item generated by separator +between adjacent items of the original iterator. Read more
1.0.0 · source§

fn map<B, F>(self, f: F) -> Map<Self, F>where + Self: Sized, + F: FnMut(Self::Item) -> B,

Takes a closure and creates an iterator which calls that closure on each +element. Read more
1.21.0 · source§

fn for_each<F>(self, f: F)where + Self: Sized, + F: FnMut(Self::Item),

Calls a closure on each element of an iterator. Read more
1.0.0 · source§

fn filter<P>(self, predicate: P) -> Filter<Self, P>where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Creates an iterator which uses a closure to determine if an element +should be yielded. Read more
1.0.0 · source§

fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F>where + Self: Sized, + F: FnMut(Self::Item) -> Option<B>,

Creates an iterator that both filters and maps. Read more
1.0.0 · source§

fn enumerate(self) -> Enumerate<Self>where + Self: Sized,

Creates an iterator which gives the current iteration count as well as +the next value. Read more
1.0.0 · source§

fn peekable(self) -> Peekable<Self>where + Self: Sized,

Creates an iterator which can use the peek and peek_mut methods +to look at the next element of the iterator without consuming it. See +their documentation for more information. Read more
1.0.0 · source§

fn skip_while<P>(self, predicate: P) -> SkipWhile<Self, P>where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Creates an iterator that skips elements based on a predicate. Read more
1.0.0 · source§

fn take_while<P>(self, predicate: P) -> TakeWhile<Self, P>where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Creates an iterator that yields elements based on a predicate. Read more
1.57.0 · source§

fn map_while<B, P>(self, predicate: P) -> MapWhile<Self, P>where + Self: Sized, + P: FnMut(Self::Item) -> Option<B>,

Creates an iterator that both yields elements based on a predicate and maps. Read more
1.0.0 · source§

fn skip(self, n: usize) -> Skip<Self>where + Self: Sized,

Creates an iterator that skips the first n elements. Read more
1.0.0 · source§

fn take(self, n: usize) -> Take<Self>where + Self: Sized,

Creates an iterator that yields the first n elements, or fewer +if the underlying iterator ends sooner. Read more
1.0.0 · source§

fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F>where + Self: Sized, + F: FnMut(&mut St, Self::Item) -> Option<B>,

An iterator adapter which, like fold, holds internal state, but +unlike fold, produces a new iterator. Read more
1.0.0 · source§

fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F>where + Self: Sized, + U: IntoIterator, + F: FnMut(Self::Item) -> U,

Creates an iterator that works like map, but flattens nested structure. Read more
source§

fn map_windows<F, R, const N: usize>(self, f: F) -> MapWindows<Self, F, N>where + Self: Sized, + F: FnMut(&[Self::Item; N]) -> R,

🔬This is a nightly-only experimental API. (iter_map_windows)
Calls the given function f for each contiguous window of size N over +self and returns an iterator over the outputs of f. Like slice::windows(), +the windows during mapping overlap as well. Read more
1.0.0 · source§

fn fuse(self) -> Fuse<Self>where + Self: Sized,

Creates an iterator which ends after the first None. Read more
1.0.0 · source§

fn inspect<F>(self, f: F) -> Inspect<Self, F>where + Self: Sized, + F: FnMut(&Self::Item),

Does something with each element of an iterator, passing the value on. Read more
1.0.0 · source§

fn by_ref(&mut self) -> &mut Selfwhere + Self: Sized,

Borrows an iterator, rather than consuming it. Read more
1.0.0 · source§

fn collect<B>(self) -> Bwhere + B: FromIterator<Self::Item>, + Self: Sized,

Transforms an iterator into a collection. Read more
source§

fn collect_into<E>(self, collection: &mut E) -> &mut Ewhere + E: Extend<Self::Item>, + Self: Sized,

🔬This is a nightly-only experimental API. (iter_collect_into)
Collects all the items from an iterator into a collection. Read more
1.0.0 · source§

fn partition<B, F>(self, f: F) -> (B, B)where + Self: Sized, + B: Default + Extend<Self::Item>, + F: FnMut(&Self::Item) -> bool,

Consumes an iterator, creating two collections from it. Read more
source§

fn is_partitioned<P>(self, predicate: P) -> boolwhere + Self: Sized, + P: FnMut(Self::Item) -> bool,

🔬This is a nightly-only experimental API. (iter_is_partitioned)
Checks if the elements of this iterator are partitioned according to the given predicate, +such that all those that return true precede all those that return false. Read more
1.27.0 · source§

fn try_fold<B, F, R>(&mut self, init: B, f: F) -> Rwhere + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try<Output = B>,

An iterator method that applies a function as long as it returns +successfully, producing a single, final value. Read more
1.27.0 · source§

fn try_for_each<F, R>(&mut self, f: F) -> Rwhere + Self: Sized, + F: FnMut(Self::Item) -> R, + R: Try<Output = ()>,

An iterator method that applies a fallible function to each item in the +iterator, stopping at the first error and returning that error. Read more
1.0.0 · source§

fn fold<B, F>(self, init: B, f: F) -> Bwhere + Self: Sized, + F: FnMut(B, Self::Item) -> B,

Folds every element into an accumulator by applying an operation, +returning the final result. Read more
1.51.0 · source§

fn reduce<F>(self, f: F) -> Option<Self::Item>where + Self: Sized, + F: FnMut(Self::Item, Self::Item) -> Self::Item,

Reduces the elements to a single one, by repeatedly applying a reducing +operation. Read more
source§

fn try_reduce<F, R>( + &mut self, + f: F +) -> <<R as Try>::Residual as Residual<Option<<R as Try>::Output>>>::TryTypewhere + Self: Sized, + F: FnMut(Self::Item, Self::Item) -> R, + R: Try<Output = Self::Item>, + <R as Try>::Residual: Residual<Option<Self::Item>>,

🔬This is a nightly-only experimental API. (iterator_try_reduce)
Reduces the elements to a single one by repeatedly applying a reducing operation. If the +closure returns a failure, the failure is propagated back to the caller immediately. Read more
1.0.0 · source§

fn all<F>(&mut self, f: F) -> boolwhere + Self: Sized, + F: FnMut(Self::Item) -> bool,

Tests if every element of the iterator matches a predicate. Read more
1.0.0 · source§

fn any<F>(&mut self, f: F) -> boolwhere + Self: Sized, + F: FnMut(Self::Item) -> bool,

Tests if any element of the iterator matches a predicate. Read more
1.0.0 · source§

fn find<P>(&mut self, predicate: P) -> Option<Self::Item>where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Searches for an element of an iterator that satisfies a predicate. Read more
1.30.0 · source§

fn find_map<B, F>(&mut self, f: F) -> Option<B>where + Self: Sized, + F: FnMut(Self::Item) -> Option<B>,

Applies function to the elements of iterator and returns +the first non-none result. Read more
source§

fn try_find<F, R>( + &mut self, + f: F +) -> <<R as Try>::Residual as Residual<Option<Self::Item>>>::TryTypewhere + Self: Sized, + F: FnMut(&Self::Item) -> R, + R: Try<Output = bool>, + <R as Try>::Residual: Residual<Option<Self::Item>>,

🔬This is a nightly-only experimental API. (try_find)
Applies function to the elements of iterator and returns +the first true result or the first error. Read more
1.0.0 · source§

fn position<P>(&mut self, predicate: P) -> Option<usize>where + Self: Sized, + P: FnMut(Self::Item) -> bool,

Searches for an element in an iterator, returning its index. Read more
1.6.0 · source§

fn max_by_key<B, F>(self, f: F) -> Option<Self::Item>where + B: Ord, + Self: Sized, + F: FnMut(&Self::Item) -> B,

Returns the element that gives the maximum value from the +specified function. Read more
1.15.0 · source§

fn max_by<F>(self, compare: F) -> Option<Self::Item>where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering,

Returns the element that gives the maximum value with respect to the +specified comparison function. Read more
1.6.0 · source§

fn min_by_key<B, F>(self, f: F) -> Option<Self::Item>where + B: Ord, + Self: Sized, + F: FnMut(&Self::Item) -> B,

Returns the element that gives the minimum value from the +specified function. Read more
1.15.0 · source§

fn min_by<F>(self, compare: F) -> Option<Self::Item>where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering,

Returns the element that gives the minimum value with respect to the +specified comparison function. Read more
1.0.0 · source§

fn unzip<A, B, FromA, FromB>(self) -> (FromA, FromB)where + FromA: Default + Extend<A>, + FromB: Default + Extend<B>, + Self: Sized + Iterator<Item = (A, B)>,

Converts an iterator of pairs into a pair of containers. Read more
1.36.0 · source§

fn copied<'a, T>(self) -> Copied<Self>where + T: 'a + Copy, + Self: Sized + Iterator<Item = &'a T>,

Creates an iterator which copies all of its elements. Read more
1.0.0 · source§

fn cloned<'a, T>(self) -> Cloned<Self>where + T: 'a + Clone, + Self: Sized + Iterator<Item = &'a T>,

Creates an iterator which clones all of its elements. Read more
source§

fn array_chunks<const N: usize>(self) -> ArrayChunks<Self, N>where + Self: Sized,

🔬This is a nightly-only experimental API. (iter_array_chunks)
Returns an iterator over N elements of the iterator at a time. Read more
1.11.0 · source§

fn sum<S>(self) -> Swhere + Self: Sized, + S: Sum<Self::Item>,

Sums the elements of an iterator. Read more
1.11.0 · source§

fn product<P>(self) -> Pwhere + Self: Sized, + P: Product<Self::Item>,

Iterates over the entire iterator, multiplying all the elements Read more
source§

fn cmp_by<I, F>(self, other: I, cmp: F) -> Orderingwhere + Self: Sized, + I: IntoIterator, + F: FnMut(Self::Item, <I as IntoIterator>::Item) -> Ordering,

🔬This is a nightly-only experimental API. (iter_order_by)
Lexicographically compares the elements of this Iterator with those +of another with respect to the specified comparison function. Read more
1.5.0 · source§

fn partial_cmp<I>(self, other: I) -> Option<Ordering>where + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Lexicographically compares the PartialOrd elements of +this Iterator with those of another. The comparison works like short-circuit +evaluation, returning a result without comparing the remaining elements. +As soon as an order can be determined, the evaluation stops and a result is returned. Read more
source§

fn partial_cmp_by<I, F>(self, other: I, partial_cmp: F) -> Option<Ordering>where + Self: Sized, + I: IntoIterator, + F: FnMut(Self::Item, <I as IntoIterator>::Item) -> Option<Ordering>,

🔬This is a nightly-only experimental API. (iter_order_by)
Lexicographically compares the elements of this Iterator with those +of another with respect to the specified comparison function. Read more
1.5.0 · source§

fn eq<I>(self, other: I) -> boolwhere + I: IntoIterator, + Self::Item: PartialEq<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are equal to those of +another. Read more
source§

fn eq_by<I, F>(self, other: I, eq: F) -> boolwhere + Self: Sized, + I: IntoIterator, + F: FnMut(Self::Item, <I as IntoIterator>::Item) -> bool,

🔬This is a nightly-only experimental API. (iter_order_by)
Determines if the elements of this Iterator are equal to those of +another with respect to the specified equality function. Read more
1.5.0 · source§

fn ne<I>(self, other: I) -> boolwhere + I: IntoIterator, + Self::Item: PartialEq<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are not equal to those of +another. Read more
1.5.0 · source§

fn lt<I>(self, other: I) -> boolwhere + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are lexicographically +less than those of another. Read more
1.5.0 · source§

fn le<I>(self, other: I) -> boolwhere + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are lexicographically +less or equal to those of another. Read more
1.5.0 · source§

fn gt<I>(self, other: I) -> boolwhere + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are lexicographically +greater than those of another. Read more
1.5.0 · source§

fn ge<I>(self, other: I) -> boolwhere + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are lexicographically +greater than or equal to those of another. Read more
source§

fn is_sorted_by<F>(self, compare: F) -> boolwhere + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Option<Ordering>,

🔬This is a nightly-only experimental API. (is_sorted)
Checks if the elements of this iterator are sorted using the given comparator function. Read more
source§

fn is_sorted_by_key<F, K>(self, f: F) -> boolwhere + Self: Sized, + F: FnMut(Self::Item) -> K, + K: PartialOrd,

🔬This is a nightly-only experimental API. (is_sorted)
Checks if the elements of this iterator are sorted using the given key extraction +function. Read more

Auto Trait Implementations§

§

impl<'content> RefUnwindSafe for CapturableWindowIterator<'content>

§

impl<'content> !Send for CapturableWindowIterator<'content>

§

impl<'content> !Sync for CapturableWindowIterator<'content>

§

impl<'content> Unpin for CapturableWindowIterator<'content>

§

impl<'content> UnwindSafe for CapturableWindowIterator<'content>

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<I> IntoIterator for Iwhere + I: Iterator,

§

type Item = <I as Iterator>::Item

The type of the elements being iterated over.
§

type IntoIter = I

Which kind of iterator are we turning this into?
const: unstable · source§

fn into_iter(self) -> I

Creates an iterator from a value. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capture_stream/enum.CaptureConfigError.html b/docs/macos_docs/crabgrab/capture_stream/enum.CaptureConfigError.html new file mode 100644 index 00000000..45cfced6 --- /dev/null +++ b/docs/macos_docs/crabgrab/capture_stream/enum.CaptureConfigError.html @@ -0,0 +1,19 @@ +CaptureConfigError in crabgrab::capture_stream - Rust
pub enum CaptureConfigError {
+    UnsupportedPixelFormat,
+    InvalidBufferCount,
+}
Expand description

Represents an error creating the capture config

+

Variants§

§

UnsupportedPixelFormat

The pixel format is unsupported by the implementation

+
§

InvalidBufferCount

The buffer count is out of the valid range for the implementation

+

Trait Implementations§

source§

impl Clone for CaptureConfigError

source§

fn clone(&self) -> CaptureConfigError

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for CaptureConfigError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capture_stream/enum.CapturePixelFormat.html b/docs/macos_docs/crabgrab/capture_stream/enum.CapturePixelFormat.html new file mode 100644 index 00000000..e53f3134 --- /dev/null +++ b/docs/macos_docs/crabgrab/capture_stream/enum.CapturePixelFormat.html @@ -0,0 +1,33 @@ +CapturePixelFormat in crabgrab::capture_stream - Rust
#[non_exhaustive]
pub enum CapturePixelFormat { + Bgra8888, + Argb2101010, + V420, + F420, +}
Expand description

The pixel format of returned video frames

+

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

Bgra8888

One plane, 4 channels, 8 bits per channel: {b: u8, g: u8, r: u8, a: u8}, full range: [0, 255]

+
§

Argb2101010

One plane, 4 channels, 10 bits per color channel, two bits for alpha: {a: u2, r: u10, g: u10, b: u10}, rgb range: [0, 1023], alpha range: [0, 3]

+
§

V420

Two planes:

+
    +
  • 1 channel, luminance, 8 bits per pixel, video range: [16, 240]
  • +
  • 2 channels, chroma (cb, cr) 8 bits bits per channel per two pixels vertically, range: [0, 255]
  • +
+
§

F420

Two planes:

+
    +
  • 1 channel, luminance, 8 bits per pixel, full range: [0, 255]
  • +
  • 2 channels, chroma (cb, cr) 8 bits bits per channel per two pixels vertically, range: [0, 255]
  • +
+

Trait Implementations§

source§

impl Clone for CapturePixelFormat

source§

fn clone(&self) -> CapturePixelFormat

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for CapturePixelFormat

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for CapturePixelFormat

source§

fn eq(&self, other: &CapturePixelFormat) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Copy for CapturePixelFormat

source§

impl Eq for CapturePixelFormat

source§

impl StructuralEq for CapturePixelFormat

source§

impl StructuralPartialEq for CapturePixelFormat

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capture_stream/enum.StreamCreateError.html b/docs/macos_docs/crabgrab/capture_stream/enum.StreamCreateError.html new file mode 100644 index 00000000..778d83fc --- /dev/null +++ b/docs/macos_docs/crabgrab/capture_stream/enum.StreamCreateError.html @@ -0,0 +1,19 @@ +StreamCreateError in crabgrab::capture_stream - Rust
pub enum StreamCreateError {
+    Other(String),
+    UnsupportedPixelFormat,
+}
Expand description

This represents an error when creating a capture stream

+

Variants§

§

Other(String)

§

UnsupportedPixelFormat

The supplied pixel format is unsupported by the implementation

+

Trait Implementations§

source§

impl Clone for StreamCreateError

source§

fn clone(&self) -> StreamCreateError

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for StreamCreateError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for StreamCreateError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for StreamCreateError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more
source§

impl Send for StreamCreateError

source§

impl Sync for StreamCreateError

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capture_stream/enum.StreamError.html b/docs/macos_docs/crabgrab/capture_stream/enum.StreamError.html new file mode 100644 index 00000000..e476e62e --- /dev/null +++ b/docs/macos_docs/crabgrab/capture_stream/enum.StreamError.html @@ -0,0 +1,17 @@ +StreamError in crabgrab::capture_stream - Rust
pub enum StreamError {
+    Other(String),
+}
Expand description

This represents an error during a stream, for example a failure to retreive a video or audio frame

+

Variants§

§

Other(String)

Trait Implementations§

source§

impl Clone for StreamError

source§

fn clone(&self) -> StreamError

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for StreamError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for StreamError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for StreamError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capture_stream/enum.StreamEvent.html b/docs/macos_docs/crabgrab/capture_stream/enum.StreamEvent.html new file mode 100644 index 00000000..744c66da --- /dev/null +++ b/docs/macos_docs/crabgrab/capture_stream/enum.StreamEvent.html @@ -0,0 +1,22 @@ +StreamEvent in crabgrab::capture_stream - Rust
pub enum StreamEvent {
+    Audio(AudioFrame),
+    Video(VideoFrame),
+    Idle,
+    End,
+}
Expand description

Represents an event in a capture stream

+

Variants§

§

Audio(AudioFrame)

This event is produced when the stream receives a new audio packet

+
§

Video(VideoFrame)

This event is produced when the stream receives a new video frame

+
§

Idle

This event is produced when the stream goes idle - IE when no new frames are expected for some time, like when a window minimizes

+
§

End

This event is produced once at the end of the stream

+

Trait Implementations§

source§

impl Debug for StreamEvent

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capture_stream/enum.StreamStopError.html b/docs/macos_docs/crabgrab/capture_stream/enum.StreamStopError.html new file mode 100644 index 00000000..9abb3a3b --- /dev/null +++ b/docs/macos_docs/crabgrab/capture_stream/enum.StreamStopError.html @@ -0,0 +1,17 @@ +StreamStopError in crabgrab::capture_stream - Rust
pub enum StreamStopError {
+    Other(String),
+    AlreadyStopped,
+}
Expand description

This represents an error while stopping a stream

+

Variants§

§

Other(String)

§

AlreadyStopped

The stream was already stopped

+

Trait Implementations§

source§

impl Debug for StreamStopError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Send for StreamStopError

source§

impl Sync for StreamStopError

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capture_stream/index.html b/docs/macos_docs/crabgrab/capture_stream/index.html new file mode 100644 index 00000000..1ca0bc0e --- /dev/null +++ b/docs/macos_docs/crabgrab/capture_stream/index.html @@ -0,0 +1 @@ +crabgrab::capture_stream - Rust

Structs

Enums

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capture_stream/sidebar-items.js b/docs/macos_docs/crabgrab/capture_stream/sidebar-items.js new file mode 100644 index 00000000..583d3409 --- /dev/null +++ b/docs/macos_docs/crabgrab/capture_stream/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["CaptureConfigError","CapturePixelFormat","StreamCreateError","StreamError","StreamEvent","StreamStopError"],"struct":["AudioCaptureConfig","CaptureConfig","CaptureStream"]}; \ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capture_stream/struct.AudioCaptureConfig.html b/docs/macos_docs/crabgrab/capture_stream/struct.AudioCaptureConfig.html new file mode 100644 index 00000000..3490c7fb --- /dev/null +++ b/docs/macos_docs/crabgrab/capture_stream/struct.AudioCaptureConfig.html @@ -0,0 +1,22 @@ +AudioCaptureConfig in crabgrab::capture_stream - Rust
pub struct AudioCaptureConfig { /* private fields */ }
Expand description

Configuration settings for audio streams

+

Implementations§

source§

impl AudioCaptureConfig

source

pub fn new() -> Self

Creates a new audio capture config with default settings:

+
    +
  • 24000 hz
  • +
  • Mono
  • +
+

Trait Implementations§

source§

impl Clone for AudioCaptureConfig

source§

fn clone(&self) -> AudioCaptureConfig

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for AudioCaptureConfig

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl MacosAudioCaptureConfigExt for AudioCaptureConfig

source§

fn set_exclude_current_process_audio( + self, + exclude_current_process_audio: bool +) -> Self

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capture_stream/struct.CaptureConfig.html b/docs/macos_docs/crabgrab/capture_stream/struct.CaptureConfig.html new file mode 100644 index 00000000..f111bf86 --- /dev/null +++ b/docs/macos_docs/crabgrab/capture_stream/struct.CaptureConfig.html @@ -0,0 +1,26 @@ +CaptureConfig in crabgrab::capture_stream - Rust
pub struct CaptureConfig { /* private fields */ }
Expand description

Configuration settings for a capture stream

+

Implementations§

source§

impl CaptureConfig

source

pub fn with_window( + window: CapturableWindow, + pixel_format: CapturePixelFormat +) -> Result<CaptureConfig, CaptureConfigError>

Create a capture configuration for a given capturable window

+
source

pub fn with_display( + display: CapturableDisplay, + pixel_format: CapturePixelFormat +) -> CaptureConfig

Create a capture configuration for a given capturable display

+
source

pub fn with_buffer_count(self, buffer_count: usize) -> Self

Configure the buffer count - the number of frames in the capture queue.

+

Higher numbers mean higher latency, but smoother performance

+
source

pub fn with_show_cursor(self, show_cursor: bool) -> Self

Configure whether the cursor is visible in the capture

+
source

pub fn set_output_size(self, output_size: Size) -> Self

Configure the output texture size - by default, this will match the captured content at the time of enumeration

+

Trait Implementations§

source§

impl Clone for CaptureConfig

source§

fn clone(&self) -> CaptureConfig

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for CaptureConfig

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl MacosCaptureConfigExt for CaptureConfig

source§

fn with_scale_to_fit(self, scale_to_fit: bool) -> Self

source§

fn with_maximum_fps(self, maximum_fps: Option<f32>) -> Self

source§

fn with_metal_device(self, metal_device: Device) -> Self

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/capture_stream/struct.CaptureStream.html b/docs/macos_docs/crabgrab/capture_stream/struct.CaptureStream.html new file mode 100644 index 00000000..8928f72a --- /dev/null +++ b/docs/macos_docs/crabgrab/capture_stream/struct.CaptureStream.html @@ -0,0 +1,22 @@ +CaptureStream in crabgrab::capture_stream - Rust
pub struct CaptureStream { /* private fields */ }
Expand description

Represents an active capture stream

+

Implementations§

source§

impl CaptureStream

source

pub fn test_access(borderless: bool) -> bool

Test whether the calling application has permission to capture content

+
source

pub async fn request_access(borderless: bool) -> bool

Prompt the user for permission to capture content

+
source

pub fn supported_pixel_formats() -> &'static [CapturePixelFormat]

Gets the implementation’s supported pixel formats

+

Note that the returned formats may not work for all capture modalities (eg, window vs display)

+
source

pub fn new( + config: CaptureConfig, + callback: impl FnMut(Result<StreamEvent, StreamError>) + Send + 'static +) -> Result<Self, StreamCreateError>

Start a new capture stream with the given stream callback

+
source

pub fn stop(&mut self) -> Result<(), StreamStopError>

Stop the capture

+

Trait Implementations§

source§

impl MetalCaptureStream for CaptureStream

source§

fn get_metal_device(&self) -> Device

Get the metal device used for frame capture

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/bitmap/enum.FrameBitmap.html b/docs/macos_docs/crabgrab/feature/bitmap/enum.FrameBitmap.html new file mode 100644 index 00000000..f3df72b9 --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/bitmap/enum.FrameBitmap.html @@ -0,0 +1,18 @@ +FrameBitmap in crabgrab::feature::bitmap - Rust
pub enum FrameBitmap {
+    BgraUnorm8x4(FrameBitmapBgraUnorm8x4),
+    RgbaUnormPacked1010102(FrameBitmapRgbaUnormPacked1010102),
+    RgbaF16x4(FrameBitmapRgbaF16x4),
+    YCbCr(FrameBitmapYCbCr),
+}
Expand description

A bitmap image

+

Variants§

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/bitmap/enum.VideoFrameBitmapError.html b/docs/macos_docs/crabgrab/feature/bitmap/enum.VideoFrameBitmapError.html new file mode 100644 index 00000000..28e7fedc --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/bitmap/enum.VideoFrameBitmapError.html @@ -0,0 +1,16 @@ +VideoFrameBitmapError in crabgrab::feature::bitmap - Rust
pub enum VideoFrameBitmapError {
+    Other(String),
+}

Variants§

§

Other(String)

Trait Implementations§

source§

impl Clone for VideoFrameBitmapError

source§

fn clone(&self) -> VideoFrameBitmapError

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for VideoFrameBitmapError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for VideoFrameBitmapError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for VideoFrameBitmapError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/bitmap/enum.VideoRange.html b/docs/macos_docs/crabgrab/feature/bitmap/enum.VideoRange.html new file mode 100644 index 00000000..d880ec94 --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/bitmap/enum.VideoRange.html @@ -0,0 +1,15 @@ +VideoRange in crabgrab::feature::bitmap - Rust
pub enum VideoRange {
+    Video,
+    Full,
+}

Variants§

§

Video

§

Full

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/bitmap/index.html b/docs/macos_docs/crabgrab/feature/bitmap/index.html new file mode 100644 index 00000000..a956c7cb --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/bitmap/index.html @@ -0,0 +1 @@ +crabgrab::feature::bitmap - Rust

Module crabgrab::feature::bitmap

source ·

Structs

Enums

Traits

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/bitmap/sidebar-items.js b/docs/macos_docs/crabgrab/feature/bitmap/sidebar-items.js new file mode 100644 index 00000000..343d6815 --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/bitmap/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["FrameBitmap","VideoFrameBitmapError","VideoRange"],"struct":["FrameBitmapBgraUnorm8x4","FrameBitmapRgbaF16x4","FrameBitmapRgbaUnormPacked1010102","FrameBitmapYCbCr"],"trait":["VideoFrameBitmap"]}; \ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/bitmap/struct.FrameBitmapBgraUnorm8x4.html b/docs/macos_docs/crabgrab/feature/bitmap/struct.FrameBitmapBgraUnorm8x4.html new file mode 100644 index 00000000..511bce9e --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/bitmap/struct.FrameBitmapBgraUnorm8x4.html @@ -0,0 +1,16 @@ +FrameBitmapBgraUnorm8x4 in crabgrab::feature::bitmap - Rust
pub struct FrameBitmapBgraUnorm8x4 {
+    pub data: Box<[[u8; 4]]>,
+    pub width: usize,
+    pub height: usize,
+}

Fields§

§data: Box<[[u8; 4]]>§width: usize§height: usize

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/bitmap/struct.FrameBitmapRgbaF16x4.html b/docs/macos_docs/crabgrab/feature/bitmap/struct.FrameBitmapRgbaF16x4.html new file mode 100644 index 00000000..b8cd0e69 --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/bitmap/struct.FrameBitmapRgbaF16x4.html @@ -0,0 +1,16 @@ +FrameBitmapRgbaF16x4 in crabgrab::feature::bitmap - Rust
pub struct FrameBitmapRgbaF16x4 {
+    pub data: Box<[[f16; 4]]>,
+    pub width: usize,
+    pub height: usize,
+}

Fields§

§data: Box<[[f16; 4]]>§width: usize§height: usize

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/bitmap/struct.FrameBitmapRgbaUnormPacked1010102.html b/docs/macos_docs/crabgrab/feature/bitmap/struct.FrameBitmapRgbaUnormPacked1010102.html new file mode 100644 index 00000000..6ca7ac99 --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/bitmap/struct.FrameBitmapRgbaUnormPacked1010102.html @@ -0,0 +1,16 @@ +FrameBitmapRgbaUnormPacked1010102 in crabgrab::feature::bitmap - Rust
pub struct FrameBitmapRgbaUnormPacked1010102 {
+    pub data: Box<[u32]>,
+    pub width: usize,
+    pub height: usize,
+}

Fields§

§data: Box<[u32]>§width: usize§height: usize

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/bitmap/struct.FrameBitmapYCbCr.html b/docs/macos_docs/crabgrab/feature/bitmap/struct.FrameBitmapYCbCr.html new file mode 100644 index 00000000..62aefa1b --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/bitmap/struct.FrameBitmapYCbCr.html @@ -0,0 +1,24 @@ +FrameBitmapYCbCr in crabgrab::feature::bitmap - Rust
pub struct FrameBitmapYCbCr {
+    pub luma_data: Box<[u8]>,
+    pub luma_width: usize,
+    pub luma_height: usize,
+    pub chroma_data: Box<[[u8; 2]]>,
+    pub chroma_width: usize,
+    pub chroma_height: usize,
+    pub range: VideoRange,
+}
Expand description

A YCbCr image, corresponding to either V420 or F420 pixel formats.

+

Dual-planar, with luma (Y) in one plane, and chroma (CbCr) in another. +Note that each plane may have a different size, as with V420 format, where +the chroma plane is 2x2 blocks, but luma is per-pixel

+

Fields§

§luma_data: Box<[u8]>§luma_width: usize§luma_height: usize§chroma_data: Box<[[u8; 2]]>§chroma_width: usize§chroma_height: usize§range: VideoRange

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/bitmap/trait.VideoFrameBitmap.html b/docs/macos_docs/crabgrab/feature/bitmap/trait.VideoFrameBitmap.html new file mode 100644 index 00000000..7363a474 --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/bitmap/trait.VideoFrameBitmap.html @@ -0,0 +1,7 @@ +VideoFrameBitmap in crabgrab::feature::bitmap - Rust
pub trait VideoFrameBitmap {
+    // Required method
+    fn get_bitmap(&self) -> Result<FrameBitmap, VideoFrameBitmapError>;
+}
Expand description

A video frame which can produce a bitmap

+

Required Methods§

source

fn get_bitmap(&self) -> Result<FrameBitmap, VideoFrameBitmapError>

Create a bitmap image from this frame. This usually involves a memory transfer from VRAM to system RAM, +and is an expensive operation.

+

Implementors§

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/index.html b/docs/macos_docs/crabgrab/feature/index.html new file mode 100644 index 00000000..dcdaa806 --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/index.html @@ -0,0 +1 @@ +crabgrab::feature - Rust

Module crabgrab::feature

source ·

Modules

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/iosurface/enum.GetIoSurfaceError.html b/docs/macos_docs/crabgrab/feature/iosurface/enum.GetIoSurfaceError.html new file mode 100644 index 00000000..f6a8c208 --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/iosurface/enum.GetIoSurfaceError.html @@ -0,0 +1,16 @@ +GetIoSurfaceError in crabgrab::feature::iosurface - Rust
pub enum GetIoSurfaceError {
+    NoImageBuffer,
+    NoIoSurface,
+}

Variants§

§

NoImageBuffer

§

NoIoSurface

Trait Implementations§

source§

impl Debug for GetIoSurfaceError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for GetIoSurfaceError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for GetIoSurfaceError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/iosurface/index.html b/docs/macos_docs/crabgrab/feature/iosurface/index.html new file mode 100644 index 00000000..b3b4730a --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/iosurface/index.html @@ -0,0 +1 @@ +crabgrab::feature::iosurface - Rust

Module crabgrab::feature::iosurface

source ·

Structs

Enums

Traits

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/iosurface/sidebar-items.js b/docs/macos_docs/crabgrab/feature/iosurface/sidebar-items.js new file mode 100644 index 00000000..60b4a779 --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/iosurface/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["GetIoSurfaceError"],"struct":["IoSurface"],"trait":["MacosIoSurfaceVideoFrame"]}; \ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/iosurface/struct.IoSurface.html b/docs/macos_docs/crabgrab/feature/iosurface/struct.IoSurface.html new file mode 100644 index 00000000..52af6adc --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/iosurface/struct.IoSurface.html @@ -0,0 +1,15 @@ +IoSurface in crabgrab::feature::iosurface - Rust
pub struct IoSurface(/* private fields */);
Expand description

A Macos IOSurface instance

+

Implementations§

source§

impl IoSurface

source

pub fn get_raw(&self) -> *const c_void

Gets the raw IOSurfaceRef

+

Trait Implementations§

source§

impl Clone for IoSurface

source§

fn clone(&self) -> Self

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Drop for IoSurface

source§

fn drop(&mut self)

Executes the destructor for this type. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/iosurface/trait.MacosIoSurfaceVideoFrame.html b/docs/macos_docs/crabgrab/feature/iosurface/trait.MacosIoSurfaceVideoFrame.html new file mode 100644 index 00000000..283c0b61 --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/iosurface/trait.MacosIoSurfaceVideoFrame.html @@ -0,0 +1,5 @@ +MacosIoSurfaceVideoFrame in crabgrab::feature::iosurface - Rust
pub trait MacosIoSurfaceVideoFrame {
+    // Required method
+    fn get_iosurface(&self) -> Result<IoSurface, GetIoSurfaceError>;
+}

Required Methods§

source

fn get_iosurface(&self) -> Result<IoSurface, GetIoSurfaceError>

Get the iosurface representing the video frame’s texture

+

Implementors§

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/metal/enum.MacosVideoFrameError.html b/docs/macos_docs/crabgrab/feature/metal/enum.MacosVideoFrameError.html new file mode 100644 index 00000000..581fdc31 --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/metal/enum.MacosVideoFrameError.html @@ -0,0 +1,20 @@ +MacosVideoFrameError in crabgrab::feature::metal - Rust
pub enum MacosVideoFrameError {
+    NoIoSurface,
+    NoImageBuffer,
+    InvalidVideoPlaneTexture,
+    Other(String),
+}
Expand description

Represents an error getting the texture from a video frame

+

Variants§

§

NoIoSurface

§

NoImageBuffer

§

InvalidVideoPlaneTexture

§

Other(String)

Trait Implementations§

source§

impl Clone for MacosVideoFrameError

source§

fn clone(&self) -> MacosVideoFrameError

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for MacosVideoFrameError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for MacosVideoFrameError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for MacosVideoFrameError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for Twhere + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/metal/enum.MetalVideoFramePlaneTexture.html b/docs/macos_docs/crabgrab/feature/metal/enum.MetalVideoFramePlaneTexture.html new file mode 100644 index 00000000..9a524302 --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/metal/enum.MetalVideoFramePlaneTexture.html @@ -0,0 +1,19 @@ +MetalVideoFramePlaneTexture in crabgrab::feature::metal - Rust
pub enum MetalVideoFramePlaneTexture {
+    Rgba,
+    Luminance,
+    Chroma,
+}

Variants§

§

Rgba

§

Luminance

§

Chroma

Trait Implementations§

source§

impl Clone for MetalVideoFramePlaneTexture

source§

fn clone(&self) -> MetalVideoFramePlaneTexture

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for MetalVideoFramePlaneTexture

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for MetalVideoFramePlaneTexture

source§

fn eq(&self, other: &MetalVideoFramePlaneTexture) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Copy for MetalVideoFramePlaneTexture

source§

impl Eq for MetalVideoFramePlaneTexture

source§

impl StructuralEq for MetalVideoFramePlaneTexture

source§

impl StructuralPartialEq for MetalVideoFramePlaneTexture

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/metal/index.html b/docs/macos_docs/crabgrab/feature/metal/index.html new file mode 100644 index 00000000..5c66bfd3 --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/metal/index.html @@ -0,0 +1 @@ +crabgrab::feature::metal - Rust

Module crabgrab::feature::metal

source ·

Enums

Traits

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/metal/sidebar-items.js b/docs/macos_docs/crabgrab/feature/metal/sidebar-items.js new file mode 100644 index 00000000..5613a99f --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/metal/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["MacosVideoFrameError","MetalVideoFramePlaneTexture"],"trait":["MetalCaptureStream","MetalVideoFrame"]}; \ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/metal/trait.MetalCaptureStream.html b/docs/macos_docs/crabgrab/feature/metal/trait.MetalCaptureStream.html new file mode 100644 index 00000000..118e2629 --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/metal/trait.MetalCaptureStream.html @@ -0,0 +1,5 @@ +MetalCaptureStream in crabgrab::feature::metal - Rust
pub trait MetalCaptureStream {
+    // Required method
+    fn get_metal_device(&self) -> Device;
+}

Required Methods§

source

fn get_metal_device(&self) -> Device

Get the metal device used for frame capture

+

Implementors§

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/metal/trait.MetalVideoFrame.html b/docs/macos_docs/crabgrab/feature/metal/trait.MetalVideoFrame.html new file mode 100644 index 00000000..37f8c31f --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/metal/trait.MetalVideoFrame.html @@ -0,0 +1,11 @@ +MetalVideoFrame in crabgrab::feature::metal - Rust
pub trait MetalVideoFrame {
+    // Required method
+    fn get_texture(
+        &self,
+        plane: MetalVideoFramePlaneTexture
+    ) -> Result<Texture, MacosVideoFrameError>;
+}

Required Methods§

source

fn get_texture( + &self, + plane: MetalVideoFramePlaneTexture +) -> Result<Texture, MacosVideoFrameError>

Get the texture for the given plane of the video frame

+

Implementors§

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/feature/sidebar-items.js b/docs/macos_docs/crabgrab/feature/sidebar-items.js new file mode 100644 index 00000000..cc0e02f1 --- /dev/null +++ b/docs/macos_docs/crabgrab/feature/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"mod":["bitmap","iosurface","metal"]}; \ No newline at end of file diff --git a/docs/macos_docs/crabgrab/frame/enum.AudioBufferError.html b/docs/macos_docs/crabgrab/frame/enum.AudioBufferError.html new file mode 100644 index 00000000..3319660d --- /dev/null +++ b/docs/macos_docs/crabgrab/frame/enum.AudioBufferError.html @@ -0,0 +1,17 @@ +AudioBufferError in crabgrab::frame - Rust
pub enum AudioBufferError {
+    UnsupportedFormat,
+    InvalidChannel,
+    Other(String),
+}
Expand description

Represents an error getting the data for an audio channel

+

Variants§

§

UnsupportedFormat

§

InvalidChannel

§

Other(String)

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/frame/enum.AudioChannelCount.html b/docs/macos_docs/crabgrab/frame/enum.AudioChannelCount.html new file mode 100644 index 00000000..62852797 --- /dev/null +++ b/docs/macos_docs/crabgrab/frame/enum.AudioChannelCount.html @@ -0,0 +1,17 @@ +AudioChannelCount in crabgrab::frame - Rust
pub enum AudioChannelCount {
+    Mono,
+    Stereo,
+}
Expand description

The number of audio channels to capture

+

Variants§

§

Mono

§

Stereo

Trait Implementations§

source§

impl Clone for AudioChannelCount

source§

fn clone(&self) -> AudioChannelCount

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for AudioChannelCount

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Copy for AudioChannelCount

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/frame/enum.AudioChannelData.html b/docs/macos_docs/crabgrab/frame/enum.AudioChannelData.html new file mode 100644 index 00000000..b9a9b4f9 --- /dev/null +++ b/docs/macos_docs/crabgrab/frame/enum.AudioChannelData.html @@ -0,0 +1,17 @@ +AudioChannelData in crabgrab::frame - Rust
pub enum AudioChannelData<'data> {
+    F32(AudioChannelDataSamples<'data, f32>),
+    I32(AudioChannelDataSamples<'data, i32>),
+    I16(AudioChannelDataSamples<'data, i16>),
+}
Expand description

Represents audio channel data in an audio frame

+

Variants§

Auto Trait Implementations§

§

impl<'data> RefUnwindSafe for AudioChannelData<'data>

§

impl<'data> !Send for AudioChannelData<'data>

§

impl<'data> !Sync for AudioChannelData<'data>

§

impl<'data> Unpin for AudioChannelData<'data>

§

impl<'data> UnwindSafe for AudioChannelData<'data>

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/frame/enum.AudioSampleRate.html b/docs/macos_docs/crabgrab/frame/enum.AudioSampleRate.html new file mode 100644 index 00000000..fe761eef --- /dev/null +++ b/docs/macos_docs/crabgrab/frame/enum.AudioSampleRate.html @@ -0,0 +1,19 @@ +AudioSampleRate in crabgrab::frame - Rust
pub enum AudioSampleRate {
+    Hz8000,
+    Hz16000,
+    Hz24000,
+    Hz48000,
+}
Expand description

The rate to capture audio samples

+

Variants§

§

Hz8000

§

Hz16000

§

Hz24000

§

Hz48000

Trait Implementations§

source§

impl Clone for AudioSampleRate

source§

fn clone(&self) -> AudioSampleRate

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for AudioSampleRate

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Copy for AudioSampleRate

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/frame/index.html b/docs/macos_docs/crabgrab/frame/index.html new file mode 100644 index 00000000..8a142e6e --- /dev/null +++ b/docs/macos_docs/crabgrab/frame/index.html @@ -0,0 +1 @@ +crabgrab::frame - Rust

Module crabgrab::frame

source ·

Structs

Enums

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/frame/sidebar-items.js b/docs/macos_docs/crabgrab/frame/sidebar-items.js new file mode 100644 index 00000000..099e8e73 --- /dev/null +++ b/docs/macos_docs/crabgrab/frame/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["AudioBufferError","AudioChannelCount","AudioChannelData","AudioSampleRate"],"struct":["AudioChannelDataSamples","AudioFrame","VideoFrame"]}; \ No newline at end of file diff --git a/docs/macos_docs/crabgrab/frame/struct.AudioChannelDataSamples.html b/docs/macos_docs/crabgrab/frame/struct.AudioChannelDataSamples.html new file mode 100644 index 00000000..12c05260 --- /dev/null +++ b/docs/macos_docs/crabgrab/frame/struct.AudioChannelDataSamples.html @@ -0,0 +1,14 @@ +AudioChannelDataSamples in crabgrab::frame - Rust
pub struct AudioChannelDataSamples<'data, T> { /* private fields */ }

Auto Trait Implementations§

§

impl<'data, T> RefUnwindSafe for AudioChannelDataSamples<'data, T>where + T: RefUnwindSafe,

§

impl<'data, T> !Send for AudioChannelDataSamples<'data, T>

§

impl<'data, T> !Sync for AudioChannelDataSamples<'data, T>

§

impl<'data, T> Unpin for AudioChannelDataSamples<'data, T>

§

impl<'data, T> UnwindSafe for AudioChannelDataSamples<'data, T>where + T: RefUnwindSafe,

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/frame/struct.AudioFrame.html b/docs/macos_docs/crabgrab/frame/struct.AudioFrame.html new file mode 100644 index 00000000..fa6304bc --- /dev/null +++ b/docs/macos_docs/crabgrab/frame/struct.AudioFrame.html @@ -0,0 +1,23 @@ +AudioFrame in crabgrab::frame - Rust

Struct crabgrab::frame::AudioFrame

source ·
pub struct AudioFrame { /* private fields */ }
Expand description

A frame of captured audio

+

Implementations§

source§

impl AudioFrame

source

pub fn sample_rate(&self) -> AudioSampleRate

Get the sample rate of the captured audio

+
source

pub fn channel_count(&self) -> AudioChannelCount

Get the channel count of the captured audio

+
source

pub fn audio_channel_buffer( + &mut self, + channel: usize +) -> Result<AudioChannelData<'_>, AudioBufferError>

Get the data buffer for the captured audio channel

+
source

pub fn duration(&self) -> Duration

Get the duration of this audio frames

+
source

pub fn origin_time(&self) -> Duration

Get the time since the start of the stream that this audio frame begins at

+
source

pub fn frame_id(&self) -> u64

Get the sequence id of this frame (monotonically increasing)

+

Note: This is separate from video frame ids

+

Trait Implementations§

source§

impl Debug for AudioFrame

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/frame/struct.VideoFrame.html b/docs/macos_docs/crabgrab/frame/struct.VideoFrame.html new file mode 100644 index 00000000..6fb72428 --- /dev/null +++ b/docs/macos_docs/crabgrab/frame/struct.VideoFrame.html @@ -0,0 +1,24 @@ +VideoFrame in crabgrab::frame - Rust

Struct crabgrab::frame::VideoFrame

source ·
pub struct VideoFrame { /* private fields */ }
Expand description

A frame of captured video

+

Implementations§

source§

impl VideoFrame

source

pub fn frame_id(&self) -> u64

Get the sequence id of this video frame (monotonically increasing)

+

Note: This is separate from audio frame ids

+
source

pub fn capture_time(&self) -> Instant

Get the Instant that this frame was delivered to the application

+
source

pub fn origin_time(&self) -> Duration

Get the time since the start of the stream that this frame was generated

+
source

pub fn size(&self) -> Size

Get the raw size of the frame

+

For planar image formats, this is the size of the largest plane

+
source

pub fn dpi(&self) -> f64

Get the dpi of the contents of the frame (accounting for capture scaling)

+

Trait Implementations§

source§

impl Debug for VideoFrame

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl MacosIoSurfaceVideoFrame for VideoFrame

source§

fn get_iosurface(&self) -> Result<IoSurface, GetIoSurfaceError>

Get the iosurface representing the video frame’s texture
source§

impl MetalVideoFrame for VideoFrame

source§

fn get_texture( + &self, + plane: MetalVideoFramePlaneTexture +) -> Result<Texture, MacosVideoFrameError>

Get the texture for the given plane of the video frame
source§

impl VideoFrameBitmap for VideoFrame

source§

fn get_bitmap(&self) -> Result<FrameBitmap, VideoFrameBitmapError>

Create a bitmap image from this frame. This usually involves a memory transfer from VRAM to system RAM, +and is an expensive operation.
source§

impl Send for VideoFrame

source§

impl Sync for VideoFrame

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/index.html b/docs/macos_docs/crabgrab/index.html new file mode 100644 index 00000000..3c05fff5 --- /dev/null +++ b/docs/macos_docs/crabgrab/index.html @@ -0,0 +1,73 @@ +crabgrab - Rust

Crate crabgrab

source ·
Expand description

A cross-platform screen/window/audio capture library

+

Feature flags

GPU Interop

+
    +
  • dx11 - enables retreiving the surface of a video frame and getting the dx11 device instance for the stream (windows only)
  • +
  • dxgi - enables retreiving the surface of a video frame and getting the dxgi device instance for the stream (windows only)
  • +
  • metal - enabels retreiving the metal textures for a video frame and getting the metal device instance for the stream (macos only)
  • +
  • iosurface - enables retreiving the iosurface for a video frame (macos only)
  • +
+

Bitmap output

+
    +
  • bitmap - enables creating raw bitmap copies of frames in system memory
  • +
+

Example

+
use std::time::Duration;
+use crabgrab::prelude::*;
+ 
+// spin up the async runtime
+let runtime = tokio::runtime::Builder::new_multi_thread().build().unwrap();
+// run our capture code in an async context
+let future = runtime.spawn(async {
+    // ensure we have priveleges to capture content
+    if !CaptureStream::test_access(false) {
+        CaptureStream::request_access(false).await;
+        println!("Approve access and run again!");
+    }
+    // create a filter for the windows we're interested in capturing
+    let window_filter = CapturableWindowFilter {
+        desktop_windows: false,
+        onscreen_only: true,
+    };
+    // create an overall content filter
+    let filter = CapturableContentFilter { windows: Some(window_filter), displays: false };
+    // get capturable content matching the filter
+    let content = CapturableContent::new(filter).await.unwrap();
+    // find the window we want to capture
+    let window = content.windows().filter(|window| {
+        let app_identifier = window.application().identifier();
+        app_identifier.to_lowercase().contains("finder") || app_identifier.to_lowercase().contains("explorer")
+    }).next();
+    match window {
+        Some(window) => {
+            println!("capturing window: {}", window.title()); 
+            // create a captuere config using the first supported pixel format
+            let config = CaptureConfig::with_window(window, CaptureStream::supported_pixel_formats()[0]).unwrap();
+            // create a capture stream with an event handler callback
+            let mut stream = CaptureStream::new(config, |stream_event| {
+                match stream_event {
+                    Ok(event) => {
+                        match event {
+                            StreamEvent::Video(frame) => {
+                                println!("Got frame: {}", frame.frame_id());
+                            },
+                            _ => {}
+                        }
+                    },
+                    Err(error) => {
+                        println!("Stream error: {:?}", error);
+                    }
+                }
+            }).unwrap();
+            // wait for a while to capture some frames
+            tokio::task::block_in_place(|| std::thread::sleep(Duration::from_millis(4000)));
+            stream.stop().unwrap();
+        },
+        None => { println!("Failed to find window"); }
+    }
+});
+// wait for the future to complete
+runtime.block_on(future).unwrap();
+// shutdown the async runtime
+runtime.shutdown_timeout(Duration::from_millis(10000));
+

Modules

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/platform/index.html b/docs/macos_docs/crabgrab/platform/index.html new file mode 100644 index 00000000..4ead115b --- /dev/null +++ b/docs/macos_docs/crabgrab/platform/index.html @@ -0,0 +1 @@ +crabgrab::platform - Rust

Module crabgrab::platform

source ·

Re-exports

  • pub use macos as platform_impl;

Modules

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/platform/macos/capture_stream/trait.MacosAudioCaptureConfigExt.html b/docs/macos_docs/crabgrab/platform/macos/capture_stream/trait.MacosAudioCaptureConfigExt.html new file mode 100644 index 00000000..8922a47e --- /dev/null +++ b/docs/macos_docs/crabgrab/platform/macos/capture_stream/trait.MacosAudioCaptureConfigExt.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../crabgrab/platform/macos/trait.MacosAudioCaptureConfigExt.html...

+ + + \ No newline at end of file diff --git a/docs/macos_docs/crabgrab/platform/macos/capture_stream/trait.MacosCaptureConfigExt.html b/docs/macos_docs/crabgrab/platform/macos/capture_stream/trait.MacosCaptureConfigExt.html new file mode 100644 index 00000000..83ac6885 --- /dev/null +++ b/docs/macos_docs/crabgrab/platform/macos/capture_stream/trait.MacosCaptureConfigExt.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../crabgrab/platform/macos/trait.MacosCaptureConfigExt.html...

+ + + \ No newline at end of file diff --git a/docs/macos_docs/crabgrab/platform/macos/index.html b/docs/macos_docs/crabgrab/platform/macos/index.html new file mode 100644 index 00000000..02be3d2d --- /dev/null +++ b/docs/macos_docs/crabgrab/platform/macos/index.html @@ -0,0 +1 @@ +crabgrab::platform::macos - Rust

Module crabgrab::platform::macos

source ·

Traits

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/platform/macos/sidebar-items.js b/docs/macos_docs/crabgrab/platform/macos/sidebar-items.js new file mode 100644 index 00000000..05f6585a --- /dev/null +++ b/docs/macos_docs/crabgrab/platform/macos/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"trait":["MacosAudioCaptureConfigExt","MacosCaptureConfigExt"]}; \ No newline at end of file diff --git a/docs/macos_docs/crabgrab/platform/macos/trait.MacosAudioCaptureConfigExt.html b/docs/macos_docs/crabgrab/platform/macos/trait.MacosAudioCaptureConfigExt.html new file mode 100644 index 00000000..6fe39d53 --- /dev/null +++ b/docs/macos_docs/crabgrab/platform/macos/trait.MacosAudioCaptureConfigExt.html @@ -0,0 +1,10 @@ +MacosAudioCaptureConfigExt in crabgrab::platform::macos - Rust
pub trait MacosAudioCaptureConfigExt {
+    // Required method
+    fn set_exclude_current_process_audio(
+        self,
+        exclude_current_process_audio: bool
+    ) -> Self;
+}

Required Methods§

source

fn set_exclude_current_process_audio( + self, + exclude_current_process_audio: bool +) -> Self

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/platform/macos/trait.MacosCaptureConfigExt.html b/docs/macos_docs/crabgrab/platform/macos/trait.MacosCaptureConfigExt.html new file mode 100644 index 00000000..0d36a50c --- /dev/null +++ b/docs/macos_docs/crabgrab/platform/macos/trait.MacosCaptureConfigExt.html @@ -0,0 +1,6 @@ +MacosCaptureConfigExt in crabgrab::platform::macos - Rust
pub trait MacosCaptureConfigExt {
+    // Required methods
+    fn with_scale_to_fit(self, scale_to_fit: bool) -> Self;
+    fn with_maximum_fps(self, maximum_fps: Option<f32>) -> Self;
+    fn with_metal_device(self, metal_device: Device) -> Self;
+}

Required Methods§

source

fn with_scale_to_fit(self, scale_to_fit: bool) -> Self

source

fn with_maximum_fps(self, maximum_fps: Option<f32>) -> Self

source

fn with_metal_device(self, metal_device: Device) -> Self

Object Safety§

This trait is not object safe.

Implementors§

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/platform/sidebar-items.js b/docs/macos_docs/crabgrab/platform/sidebar-items.js new file mode 100644 index 00000000..2433a0d3 --- /dev/null +++ b/docs/macos_docs/crabgrab/platform/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"mod":["macos"]}; \ No newline at end of file diff --git a/docs/macos_docs/crabgrab/prelude/index.html b/docs/macos_docs/crabgrab/prelude/index.html new file mode 100644 index 00000000..2067499d --- /dev/null +++ b/docs/macos_docs/crabgrab/prelude/index.html @@ -0,0 +1 @@ +crabgrab::prelude - Rust

Module crabgrab::prelude

source ·

Re-exports

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/prelude/sidebar-items.js b/docs/macos_docs/crabgrab/prelude/sidebar-items.js new file mode 100644 index 00000000..5244ce01 --- /dev/null +++ b/docs/macos_docs/crabgrab/prelude/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {}; \ No newline at end of file diff --git a/docs/macos_docs/crabgrab/sidebar-items.js b/docs/macos_docs/crabgrab/sidebar-items.js new file mode 100644 index 00000000..47415dbe --- /dev/null +++ b/docs/macos_docs/crabgrab/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"mod":["capturable_content","capture_stream","feature","frame","platform","prelude","util"]}; \ No newline at end of file diff --git a/docs/macos_docs/crabgrab/util/index.html b/docs/macos_docs/crabgrab/util/index.html new file mode 100644 index 00000000..157b5090 --- /dev/null +++ b/docs/macos_docs/crabgrab/util/index.html @@ -0,0 +1 @@ +crabgrab::util - Rust

Module crabgrab::util

source ·

Structs

  • Represents a 2d point
  • Represents an axis-aligned rectangle
  • Represents a 2d size
\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/util/sidebar-items.js b/docs/macos_docs/crabgrab/util/sidebar-items.js new file mode 100644 index 00000000..c1c0499d --- /dev/null +++ b/docs/macos_docs/crabgrab/util/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"struct":["Point","Rect","Size"]}; \ No newline at end of file diff --git a/docs/macos_docs/crabgrab/util/struct.Point.html b/docs/macos_docs/crabgrab/util/struct.Point.html new file mode 100644 index 00000000..f4a31da3 --- /dev/null +++ b/docs/macos_docs/crabgrab/util/struct.Point.html @@ -0,0 +1,20 @@ +Point in crabgrab::util - Rust

Struct crabgrab::util::Point

source ·
pub struct Point {
+    pub x: f64,
+    pub y: f64,
+}
Expand description

Represents a 2d point

+

Fields§

§x: f64§y: f64

Implementations§

source§

impl Point

source

pub const ZERO: Point = _

The point at (0, 0)

+
source

pub fn scaled(&self, scale: f64) -> Self

Scale the point uniformly by some value

+
source

pub fn scaled_2d(&self, scale: (f64, f64)) -> Self

Scale the point non-uniformly in x and y

+

Trait Implementations§

source§

impl Clone for Point

source§

fn clone(&self) -> Point

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Point

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Copy for Point

Auto Trait Implementations§

§

impl RefUnwindSafe for Point

§

impl Send for Point

§

impl Sync for Point

§

impl Unpin for Point

§

impl UnwindSafe for Point

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/util/struct.Rect.html b/docs/macos_docs/crabgrab/util/struct.Rect.html new file mode 100644 index 00000000..e9b581b1 --- /dev/null +++ b/docs/macos_docs/crabgrab/util/struct.Rect.html @@ -0,0 +1,19 @@ +Rect in crabgrab::util - Rust

Struct crabgrab::util::Rect

source ·
pub struct Rect {
+    pub origin: Point,
+    pub size: Size,
+}
Expand description

Represents an axis-aligned rectangle

+

Fields§

§origin: Point§size: Size

Implementations§

source§

impl Rect

source

pub fn scaled(&self, scale: f64) -> Self

Scale the rectangle uniformly

+
source

pub fn scaled_2d(&self, scale: (f64, f64)) -> Self

Scale the rectangle non-uniformly in x and y

+

Trait Implementations§

source§

impl Clone for Rect

source§

fn clone(&self) -> Rect

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Rect

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Copy for Rect

Auto Trait Implementations§

§

impl RefUnwindSafe for Rect

§

impl Send for Rect

§

impl Sync for Rect

§

impl Unpin for Rect

§

impl UnwindSafe for Rect

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crabgrab/util/struct.Size.html b/docs/macos_docs/crabgrab/util/struct.Size.html new file mode 100644 index 00000000..fa8f1efd --- /dev/null +++ b/docs/macos_docs/crabgrab/util/struct.Size.html @@ -0,0 +1,19 @@ +Size in crabgrab::util - Rust

Struct crabgrab::util::Size

source ·
pub struct Size {
+    pub width: f64,
+    pub height: f64,
+}
Expand description

Represents a 2d size

+

Fields§

§width: f64§height: f64

Implementations§

source§

impl Size

source

pub fn scaled(&self, scale: f64) -> Self

scale the size uniformly by some value

+
source

pub fn scaled_2d(&self, scale: (f64, f64)) -> Self

scale the size non-uniformly in x and y

+

Trait Implementations§

source§

impl Clone for Size

source§

fn clone(&self) -> Size

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Size

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Copy for Size

Auto Trait Implementations§

§

impl RefUnwindSafe for Size

§

impl Send for Size

§

impl Sync for Size

§

impl Unpin for Size

§

impl UnwindSafe for Size

Blanket Implementations§

source§

impl<T> Any for Twhere + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for Twhere + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for Twhere + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for Twhere + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> AutoreleaseSafe for Twhere + T: ?Sized,

\ No newline at end of file diff --git a/docs/macos_docs/crates.js b/docs/macos_docs/crates.js new file mode 100644 index 00000000..08fe8925 --- /dev/null +++ b/docs/macos_docs/crates.js @@ -0,0 +1 @@ +window.ALL_CRATES = ["crabgrab"]; \ No newline at end of file diff --git a/docs/macos_docs/help.html b/docs/macos_docs/help.html new file mode 100644 index 00000000..4f50e53d --- /dev/null +++ b/docs/macos_docs/help.html @@ -0,0 +1 @@ +Help

Rustdoc help

Back
\ No newline at end of file diff --git a/docs/macos_docs/search-index.js b/docs/macos_docs/search-index.js new file mode 100644 index 00000000..5784d386 --- /dev/null +++ b/docs/macos_docs/search-index.js @@ -0,0 +1,5 @@ +var searchIndex = JSON.parse('{\ +"crabgrab":{"doc":"A cross-platform screen/window/audio capture library","t":"AAAAAAADDEDDDDDDNLLLLLLLLLLLLLLLLLLLLLLLLLLLLMLMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMNNNDNDEEDNNNNNNNEEEENNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLAAANEDDDDNNNNNIEENLLLLLLLLLLLLLLLMMMLLMMMLLLLLLLLLLKMMMLLLLLLLMMMMLLLLLLLLLLLLLLLLLLLLLLLLMMMEDINNLLLLLLLLLLLLLKLLLLLLLLLLLLNNNEIIENNNNLLLLLLLLLLLLLLLLKKLLLLLLLLLLLLEEEDDENNNNNNNNNNNNDLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLACIIKKKKDDDSLLLLLLLLLLLLLLLLLLMLLLMLLLLLLMLLLLLLLLLLLLMMM","n":["capturable_content","capture_stream","feature","frame","platform","prelude","util","CapturableApplication","CapturableContent","CapturableContentError","CapturableContentFilter","CapturableDisplay","CapturableDisplayIterator","CapturableWindow","CapturableWindowFilter","CapturableWindowIterator","Other","application","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","cause","clone","clone","clone","clone_into","clone_into","clone_into","default","description","desktop_windows","displays","displays","fmt","fmt","fmt","fmt","from","from","from","from","from","from","from","from","from","identifier","into","into","into","into","into","into","into","into","into","into_iter","into_iter","is_empty","len","new","next","next","onscreen_only","rect","rect","size_hint","size_hint","source","title","to_owned","to_owned","to_owned","to_string","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","windows","windows","AlreadyStopped","Argb2101010","Audio","AudioCaptureConfig","Bgra8888","CaptureConfig","CaptureConfigError","CapturePixelFormat","CaptureStream","End","F420","Idle","InvalidBufferCount","Other","Other","Other","StreamCreateError","StreamError","StreamEvent","StreamStopError","UnsupportedPixelFormat","UnsupportedPixelFormat","V420","Video","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","cause","cause","clone","clone","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","description","description","eq","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","from","from","from","from","from","from","from","from","from","get_metal_device","into","into","into","into","into","into","into","into","into","new","new","request_access","set_exclude_current_process_audio","set_output_size","source","source","stop","supported_pixel_formats","test_access","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_string","to_string","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","with_buffer_count","with_display","with_maximum_fps","with_metal_device","with_scale_to_fit","with_show_cursor","with_window","bitmap","iosurface","metal","BgraUnorm8x4","FrameBitmap","FrameBitmapBgraUnorm8x4","FrameBitmapRgbaF16x4","FrameBitmapRgbaUnormPacked1010102","FrameBitmapYCbCr","Full","Other","RgbaF16x4","RgbaUnormPacked1010102","Video","VideoFrameBitmap","VideoFrameBitmapError","VideoRange","YCbCr","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","cause","chroma_data","chroma_height","chroma_width","clone","clone_into","data","data","data","description","fmt","fmt","from","from","from","from","from","from","from","get_bitmap","height","height","height","into","into","into","into","into","into","into","luma_data","luma_height","luma_width","range","source","to_owned","to_string","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","type_id","width","width","width","GetIoSurfaceError","IoSurface","MacosIoSurfaceVideoFrame","NoImageBuffer","NoIoSurface","borrow","borrow","borrow_mut","borrow_mut","cause","clone","clone_into","description","drop","fmt","fmt","from","from","get_iosurface","get_raw","into","into","source","to_owned","to_string","try_from","try_from","try_into","try_into","type_id","type_id","Chroma","InvalidVideoPlaneTexture","Luminance","MacosVideoFrameError","MetalCaptureStream","MetalVideoFrame","MetalVideoFramePlaneTexture","NoImageBuffer","NoIoSurface","Other","Rgba","borrow","borrow","borrow_mut","borrow_mut","cause","clone","clone","clone_into","clone_into","description","eq","fmt","fmt","fmt","from","from","get_metal_device","get_texture","into","into","source","to_owned","to_owned","to_string","try_from","try_from","try_into","try_into","type_id","type_id","AudioBufferError","AudioChannelCount","AudioChannelData","AudioChannelDataSamples","AudioFrame","AudioSampleRate","F32","Hz16000","Hz24000","Hz48000","Hz8000","I16","I32","InvalidChannel","Mono","Other","Stereo","UnsupportedFormat","VideoFrame","audio_channel_buffer","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","capture_time","channel_count","clone","clone","clone_into","clone_into","dpi","duration","fmt","fmt","fmt","fmt","frame_id","frame_id","from","from","from","from","from","from","from","get_bitmap","get_iosurface","get_texture","into","into","into","into","into","into","into","origin_time","origin_time","sample_rate","size","to_owned","to_owned","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","type_id","macos","platform_impl","MacosAudioCaptureConfigExt","MacosCaptureConfigExt","set_exclude_current_process_audio","with_maximum_fps","with_metal_device","with_scale_to_fit","Point","Rect","Size","ZERO","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","clone","clone","clone","clone_into","clone_into","clone_into","fmt","fmt","fmt","from","from","from","height","into","into","into","origin","scaled","scaled","scaled","scaled_2d","scaled_2d","scaled_2d","size","to_owned","to_owned","to_owned","try_from","try_from","try_from","try_into","try_into","try_into","type_id","type_id","type_id","width","x","y"],"q":[[0,"crabgrab"],[7,"crabgrab::capturable_content"],[118,"crabgrab::capture_stream"],[258,"crabgrab::feature"],[261,"crabgrab::feature::bitmap"],[351,"crabgrab::feature::iosurface"],[382,"crabgrab::feature::metal"],[423,"crabgrab::frame"],[515,"crabgrab::platform"],[517,"crabgrab::platform::macos"],[523,"crabgrab::util"],[572,"core::error"],[573,"core::option"],[574,"core::fmt"],[575,"core::fmt"],[576,"core::result"],[577,"core::any"],[578,"metal::device"],[579,"core::ops::function"],[580,"core::marker"],[581,"std::os::raw"],[582,"metal::texture"],[583,"std::time"],[584,"core::time"]],"d":["","","","","","","","","","Represents an error that occured when enumerating …","Selects the kind of capturable content to enumerate","Represents a capturable display","An iterator over capturable displays","Represents a capturable application window","Selects the kind of windows to enumerate for capture","An iterator over capturable windows","","Gets the application that owns this window","","","","","","","","","","","","","","","","","","","","","","","","","","","","Desktop windows are elements of the desktop environment, …","Get an iterator over the capturable displays","Whether to enumerate capturable displays","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Gets the “identifier” of the application","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","","","Whether this filter allows any capturable content","","Requests capturable content from the OS","","","Whether to restrict to onscreen windows","Gets the virtual screen rectangle of the window","Gets the virtual screen rectangle of this display","","","","Gets the title of the window","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Get an iterator over the capturable windows","What kind of capturable windows, if Some, to enumerate","The stream was already stopped","One plane, 4 channels, 10 bits per color channel, two bits …","This event is produced when the stream receives a new …","Configuration settings for audio streams","One plane, 4 channels, 8 bits per channel: {b: u8, g: u8, …","Configuration settings for a capture stream","Represents an error creating the capture config","The pixel format of returned video frames","Represents an active capture stream","This event is produced once at the end of the stream","Two planes:","This event is produced when the stream goes idle - IE when …","The buffer count is out of the valid range for the …","","","","This represents an error when creating a capture stream","This represents an error during a stream, for example a …","Represents an event in a capture stream","This represents an error while stopping a stream","The supplied pixel format is unsupported by the …","The pixel format is unsupported by the implementation","Two planes:","This event is produced when the stream receives a new …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Start a new capture stream with the given stream callback","Creates a new audio capture config with default settings:","Prompt the user for permission to capture content","","Configure the output texture size - by default, this will …","","","Stop the capture","Gets the implementation’s supported pixel formats","Test whether the calling application has permission to …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Configure the buffer count - the number of frames in the …","Create a capture configuration for a given capturable …","","","","Configure whether the cursor is visible in the capture","Create a capture configuration for a given capturable …","","","","","A bitmap image","","","","A YCbCr image, corresponding to either V420 or F420 pixel …","","","","","","A video frame which can produce a bitmap","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Create a bitmap image from this frame. This usually …","","","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","A Macos IOSurface instance","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Get the iosurface representing the video frame’s texture","Gets the raw IOSurfaceRef","Calls U::from(self).","Calls U::from(self).","","","","","","","","","","","","","Represents an error getting the texture from a video frame","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Get the metal device used for frame capture","Get the texture for the given plane of the video frame","Calls U::from(self).","Calls U::from(self).","","","","","","","","","","","Represents an error getting the data for an audio channel","The number of audio channels to capture","Represents audio channel data in an audio frame","","A frame of captured audio","The rate to capture audio samples","","","","","","","","","","","","","A frame of captured video","Get the data buffer for the captured audio channel","","","","","","","","","","","","","","","Get the Instant that this frame was delivered to the …","Get the channel count of the captured audio","","","","","Get the dpi of the contents of the frame (accounting for …","Get the duration of this audio frames","","","","","Get the sequence id of this frame (monotonically …","Get the sequence id of this video frame (monotonically …","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Get the time since the start of the stream that this audio …","Get the time since the start of the stream that this frame …","Get the sample rate of the captured audio","Get the raw size of the frame","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Represents a 2d point","Represents an axis-aligned rectangle","Represents a 2d size","The point at (0, 0)","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","","scale the size uniformly by some value","Scale the point uniformly by some value","Scale the rectangle uniformly","scale the size non-uniformly in x and y","Scale the point non-uniformly in x and y","Scale the rectangle non-uniformly in x and y","","","","","","","","","","","","","","","",""],"i":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,1,8,15,10,19,11,2,3,1,6,8,15,10,19,11,2,3,1,6,3,3,1,6,3,1,6,8,3,8,10,15,3,3,1,6,8,15,10,19,11,2,3,1,6,2,8,15,10,19,11,2,3,1,6,19,11,15,11,10,19,11,8,1,6,19,11,3,1,3,1,6,3,8,15,10,19,11,2,3,1,6,8,15,10,19,11,2,3,1,6,8,15,10,19,11,2,3,1,6,10,15,29,25,28,0,25,0,0,0,0,28,25,28,27,22,23,29,0,0,0,0,23,27,25,28,30,28,22,23,29,24,25,26,27,30,28,22,23,29,24,25,26,27,22,23,22,23,24,25,26,27,22,23,24,25,26,27,22,23,25,28,22,22,23,23,29,24,25,26,27,30,28,22,23,29,24,25,26,27,30,30,28,22,23,29,24,25,26,27,30,24,30,24,26,22,23,30,30,30,22,23,24,25,26,27,22,23,30,28,22,23,29,24,25,26,27,30,28,22,23,29,24,25,26,27,30,28,22,23,29,24,25,26,27,26,26,26,26,26,26,26,0,0,0,38,0,0,0,0,0,56,37,38,38,56,0,0,0,38,57,58,59,56,60,38,37,57,58,59,56,60,38,37,37,60,60,60,37,37,57,58,59,37,37,37,57,58,59,56,60,38,37,61,57,58,59,57,58,59,56,60,38,37,60,60,60,60,37,37,37,57,58,59,56,60,38,37,57,58,59,56,60,38,37,57,58,59,56,60,38,37,57,58,59,0,0,0,39,39,40,39,40,39,39,40,40,39,40,39,39,40,39,62,40,40,39,39,40,39,40,39,40,39,40,39,43,42,43,0,0,0,0,42,42,42,43,43,42,43,42,42,43,42,43,42,42,43,43,42,42,43,42,63,64,43,42,42,43,42,42,43,42,43,42,43,42,0,0,0,0,0,0,46,51,51,51,51,46,46,47,50,47,50,47,0,45,46,65,47,45,48,51,50,46,65,47,45,48,51,50,48,45,51,50,51,50,48,45,45,48,51,50,45,48,46,65,47,45,48,51,50,48,48,48,46,65,47,45,48,51,50,45,48,45,48,51,50,46,65,47,45,48,51,50,46,65,47,45,48,51,50,46,65,47,45,48,51,50,0,0,0,0,66,67,67,67,0,0,0,55,34,55,20,34,55,20,34,55,20,34,55,20,34,55,20,34,55,20,34,34,55,20,20,34,55,20,34,55,20,20,34,55,20,34,55,20,34,55,20,34,55,20,34,55,55],"f":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[1,2],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[3,[[5,[4]]]],[3,3],[1,1],[6,6],[[-1,-2],7,[],[]],[[-1,-2],7,[],[]],[[-1,-2],7,[],[]],[[],8],[3,9],0,[10,11],0,[[3,12],13],[[3,12],13],[[1,12],13],[[6,12],13],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[2,14],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[15,16],[11,17],[15,[[18,[10,3]]]],[19,5],[11,5],0,[1,20],[6,20],[19,[[7,[17,[5,[17]]]]]],[11,[[7,[17,[5,[17]]]]]],[3,[[5,[4]]]],[1,14],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,14,[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[10,19],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[22,[[5,[4]]]],[23,[[5,[4]]]],[22,22],[23,23],[24,24],[25,25],[26,26],[27,27],[[-1,-2],7,[],[]],[[-1,-2],7,[],[]],[[-1,-2],7,[],[]],[[-1,-2],7,[],[]],[[-1,-2],7,[],[]],[[-1,-2],7,[],[]],[22,9],[23,9],[[25,25],16],[[28,12],13],[[22,12],13],[[22,12],13],[[23,12],13],[[23,12],13],[[29,12],13],[[24,12],13],[[25,12],13],[[26,12],13],[[27,12],13],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[30,31],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[[26,-1],[[18,[30,23]]],[32,33]],[[],24],[16,16],[[24,16],24],[[26,34],26],[22,[[5,[4]]]],[23,[[5,[4]]]],[30,[[18,[7,29]]]],[[],[[35,[25]]]],[16,16],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,14,[]],[-1,14,[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[[26,17],26],[[6,25],26],[[26,[5,[36]]],26],[[26,31],26],[[26,16],26],[[26,16],26],[[1,25],[[18,[26,27]]]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[37,[[5,[4]]]],0,0,0,[37,37],[[-1,-2],7,[],[]],0,0,0,[37,9],[[37,12],13],[[37,12],13],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,[[18,[38,37]]],[]],0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,0,0,0,[37,[[5,[4]]]],[-1,-2,[],[]],[-1,14,[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[39,[[5,[4]]]],[40,40],[[-1,-2],7,[],[]],[39,9],[40,7],[[39,12],13],[[39,12],13],[-1,-1,[]],[-1,-1,[]],[-1,[[18,[40,39]]],[]],[40,41],[-1,-2,[],[]],[-1,-2,[],[]],[39,[[5,[4]]]],[-1,-2,[],[]],[-1,14,[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,21,[]],[-1,21,[]],0,0,0,0,0,0,0,0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[42,[[5,[4]]]],[43,43],[42,42],[[-1,-2],7,[],[]],[[-1,-2],7,[],[]],[42,9],[[43,43],16],[[43,12],13],[[42,12],13],[[42,12],13],[-1,-1,[]],[-1,-1,[]],[-1,31,[]],[[-1,43],[[18,[44,42]]],[]],[-1,-2,[],[]],[-1,-2,[],[]],[42,[[5,[4]]]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,14,[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,21,[]],[-1,21,[]],0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,[[45,17],[[18,[46,47]]]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[48,49],[45,50],[51,51],[50,50],[[-1,-2],7,[],[]],[[-1,-2],7,[],[]],[48,52],[45,53],[[45,12],13],[[48,12],13],[[51,12],13],[[50,12],13],[45,54],[48,54],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],[48,[[18,[38,37]]]],[48,[[18,[40,39]]]],[[48,43],[[18,[44,42]]]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[45,53],[48,53],[45,51],[48,34],[-1,-2,[],[]],[-1,-2,[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],0,0,0,0,[[-1,16],-1,[]],[[-1,[5,[36]]],-1,[]],[[-1,31],-1,[]],[[-1,16],-1,[]],0,0,0,0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[34,34],[55,55],[20,20],[[-1,-2],7,[],[]],[[-1,-2],7,[],[]],[[-1,-2],7,[],[]],[[34,12],13],[[55,12],13],[[20,12],13],[-1,-1,[]],[-1,-1,[]],[-1,-1,[]],0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],0,[[34,52],34],[[55,52],55],[[20,52],20],[[34,[7,[52,52]]],34],[[55,[7,[52,52]]],55],[[20,[7,[52,52]]],20],0,[-1,-2,[],[]],[-1,-2,[],[]],[-1,-2,[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,[[18,[-2]]],[],[]],[-1,21,[]],[-1,21,[]],[-1,21,[]],0,0,0],"c":[],"p":[[3,"CapturableWindow",7],[3,"CapturableApplication",7],[4,"CapturableContentError",7],[8,"Error",572],[4,"Option",573],[3,"CapturableDisplay",7],[15,"tuple"],[3,"CapturableWindowFilter",7],[15,"str"],[3,"CapturableContent",7],[3,"CapturableDisplayIterator",7],[3,"Formatter",574],[6,"Result",574],[3,"String",575],[3,"CapturableContentFilter",7],[15,"bool"],[15,"usize"],[4,"Result",576],[3,"CapturableWindowIterator",7],[3,"Rect",523],[3,"TypeId",577],[4,"StreamError",118],[4,"StreamCreateError",118],[3,"AudioCaptureConfig",118],[4,"CapturePixelFormat",118],[3,"CaptureConfig",118],[4,"CaptureConfigError",118],[4,"StreamEvent",118],[4,"StreamStopError",118],[3,"CaptureStream",118],[3,"Device",578],[8,"FnMut",579],[8,"Send",580],[3,"Size",523],[15,"slice"],[15,"f32"],[4,"VideoFrameBitmapError",261],[4,"FrameBitmap",261],[4,"GetIoSurfaceError",351],[3,"IoSurface",351],[6,"c_void",581],[4,"MacosVideoFrameError",382],[4,"MetalVideoFramePlaneTexture",382],[3,"Texture",582],[3,"AudioFrame",423],[4,"AudioChannelData",423],[4,"AudioBufferError",423],[3,"VideoFrame",423],[3,"Instant",583],[4,"AudioChannelCount",423],[4,"AudioSampleRate",423],[15,"f64"],[3,"Duration",584],[15,"u64"],[3,"Point",523],[4,"VideoRange",261],[3,"FrameBitmapBgraUnorm8x4",261],[3,"FrameBitmapRgbaUnormPacked1010102",261],[3,"FrameBitmapRgbaF16x4",261],[3,"FrameBitmapYCbCr",261],[8,"VideoFrameBitmap",261],[8,"MacosIoSurfaceVideoFrame",351],[8,"MetalCaptureStream",382],[8,"MetalVideoFrame",382],[3,"AudioChannelDataSamples",423],[8,"MacosAudioCaptureConfigExt",517],[8,"MacosCaptureConfigExt",517]],"b":[[48,"impl-Debug-for-CapturableContentError"],[49,"impl-Display-for-CapturableContentError"],[178,"impl-Display-for-StreamError"],[179,"impl-Debug-for-StreamError"],[180,"impl-Display-for-StreamCreateError"],[181,"impl-Debug-for-StreamCreateError"],[300,"impl-Debug-for-VideoFrameBitmapError"],[301,"impl-Display-for-VideoFrameBitmapError"],[365,"impl-Display-for-GetIoSurfaceError"],[366,"impl-Debug-for-GetIoSurfaceError"],[405,"impl-Display-for-MacosVideoFrameError"],[406,"impl-Debug-for-MacosVideoFrameError"]]}\ +}'); +if (typeof window !== 'undefined' && window.initSearch) {window.initSearch(searchIndex)}; +if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex}; diff --git a/docs/macos_docs/settings.html b/docs/macos_docs/settings.html new file mode 100644 index 00000000..62a52ce8 --- /dev/null +++ b/docs/macos_docs/settings.html @@ -0,0 +1 @@ +Settings

Rustdoc settings

Back
\ No newline at end of file diff --git a/docs/macos_docs/src-files.js b/docs/macos_docs/src-files.js new file mode 100644 index 00000000..17f91a8c --- /dev/null +++ b/docs/macos_docs/src-files.js @@ -0,0 +1,4 @@ +var srcIndex = JSON.parse('{\ +"crabgrab":["",[["feature",[["bitmap",[],["mod.rs"]],["iosurface",[],["mod.rs"]],["metal",[],["mod.rs"]]],["mod.rs"]],["platform",[["macos",[],["capturable_content.rs","capture_stream.rs","frame.rs","mod.rs","objc_wrap.rs"]]],["mod.rs"]]],["capturable_content.rs","capture_stream.rs","frame.rs","lib.rs","prelude.rs","util.rs"]]\ +}'); +createSrcSidebar(); diff --git a/docs/macos_docs/src/crabgrab/capturable_content.rs.html b/docs/macos_docs/src/crabgrab/capturable_content.rs.html new file mode 100644 index 00000000..dfcf6382 --- /dev/null +++ b/docs/macos_docs/src/crabgrab/capturable_content.rs.html @@ -0,0 +1,411 @@ +capturable_content.rs - source
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
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+
use std::{error::Error, fmt::{Debug, Display}};
+
+use crate::{platform::platform_impl::{ImplCapturableApplication, ImplCapturableContent, ImplCapturableDisplay, ImplCapturableWindow}, util::Rect};
+
+/// Represents an error that occured when enumerating capturable content
+#[derive(Debug, Clone)]
+pub enum CapturableContentError {
+    Other(String)
+}
+
+impl Display for CapturableContentError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Other(message) => f.write_fmt(format_args!("CapturableContentError::Other(\"{}\")", message))
+        }
+    }
+}
+
+impl Error for CapturableContentError {
+    fn source(&self) -> Option<&(dyn Error + 'static)> {
+        None
+    }
+
+    fn description(&self) -> &str {
+        "description() is deprecated; use Display"
+    }
+
+    fn cause(&self) -> Option<&dyn Error> {
+        self.source()
+    }
+}
+
+/// Selects the kind of windows to enumerate for capture
+pub struct CapturableWindowFilter {
+    /// Desktop windows are elements of the desktop environment, E.G. the dock on macos or the start bar on windows.
+    pub desktop_windows: bool,
+    /// Whether to restrict to onscreen windows
+    pub onscreen_only: bool,
+}
+
+impl Default for CapturableWindowFilter {
+    fn default() -> Self {
+        Self { desktop_windows: false, onscreen_only: true }
+    }
+}
+
+/// Selects the kind of capturable content to enumerate
+pub struct CapturableContentFilter {
+    /// What kind of capturable windows, if Some, to enumerate
+    pub windows: Option<CapturableWindowFilter>,
+    /// Whether to enumerate capturable displays
+    pub displays: bool,
+}
+
+impl CapturableContentFilter {
+    /// Whether this filter allows any capturable content
+    pub fn is_empty(&self) -> bool {
+        !(
+            self.windows.is_some() ||
+            self.displays
+        )
+    }
+}
+
+pub struct CapturableContent {
+    impl_capturable_content: ImplCapturableContent
+}
+
+/// An iterator over capturable windows
+pub struct CapturableWindowIterator<'content> {
+    content: &'content CapturableContent,
+    i: usize
+}
+
+impl Iterator for CapturableWindowIterator<'_> {
+    type Item = CapturableWindow;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.i < self.content.impl_capturable_content.windows.len() {
+            let i = self.i;
+            self.i += 1;
+            Some(CapturableWindow { impl_capturable_window: ImplCapturableWindow::from_impl(self.content.impl_capturable_content.windows[i].clone()) })
+        } else {
+            None
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.i, Some(self.content.impl_capturable_content.windows.len()))
+    }
+}
+
+impl ExactSizeIterator for CapturableWindowIterator<'_> {
+}
+
+/// An iterator over capturable displays
+pub struct CapturableDisplayIterator<'content> {
+    content: &'content CapturableContent,
+    i: usize
+}
+
+impl Iterator for CapturableDisplayIterator<'_> {
+    type Item = CapturableDisplay;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.i < self.content.impl_capturable_content.displays.len() {
+            let i = self.i;
+            self.i += 1;
+            Some(CapturableDisplay { impl_capturable_display: ImplCapturableDisplay::from_impl(self.content.impl_capturable_content.displays[i].clone()) })
+        } else {
+            None
+        }
+    }
+    
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.i, Some(self.content.impl_capturable_content.displays.len()))
+    }
+}
+
+impl ExactSizeIterator for CapturableDisplayIterator<'_> {
+    fn len(&self) -> usize {
+        self.content.impl_capturable_content.displays.len()
+    }
+}
+
+impl CapturableContent {
+    /// Requests capturable content from the OS
+    /// 
+    /// Note that the returned capturable content may be stale - for example, a window enumerated in this capturable content
+    /// may have been closed before it is used to open a stream, and creating a stream for that window will result in an error.
+    pub async fn new(filter: CapturableContentFilter) -> Result<Self, CapturableContentError> {
+        Ok(Self {
+            impl_capturable_content: ImplCapturableContent::new(filter).await?
+        })
+    }
+
+    /// Get an iterator over the capturable windows
+    pub fn windows<'a>(&'a self) -> CapturableWindowIterator<'a> {
+        CapturableWindowIterator { content: self, i: 0 }
+    }
+
+    /// Get an iterator over the capturable displays
+    pub fn displays<'a>(&'a self) -> CapturableDisplayIterator<'a> {
+        CapturableDisplayIterator { content: self, i: 0 }
+    }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) enum Capturable {
+    Window(CapturableWindow),
+    Display(CapturableDisplay),
+}
+
+/// Represents a capturable application window
+#[derive(Debug, Clone)]
+pub struct CapturableWindow {
+    pub(crate) impl_capturable_window: ImplCapturableWindow
+}
+
+impl CapturableWindow {
+    /// Gets the title of the window
+    pub fn title(&self) -> String {
+        self.impl_capturable_window.title()
+    }
+
+    /// Gets the virtual screen rectangle of the window
+    pub fn rect(&self) -> Rect {
+        self.impl_capturable_window.rect()
+    }
+
+    /// Gets the application that owns this window
+    pub fn application(&self) -> CapturableApplication {
+        CapturableApplication {
+            impl_capturable_application: self.impl_capturable_window.application()
+        }
+    }
+}
+
+/// Represents a capturable display
+#[derive(Debug, Clone)]
+pub struct CapturableDisplay {
+    pub(crate) impl_capturable_display: ImplCapturableDisplay
+}
+
+impl CapturableDisplay {
+    /// Gets the virtual screen rectangle of this display
+    /// 
+    /// Note: Currently on windows, this is only evaluated at the time of display enumeration
+    pub fn rect(&self) -> Rect {
+        self.impl_capturable_display.rect()
+    }
+}
+
+pub struct CapturableApplication {
+    impl_capturable_application: ImplCapturableApplication
+}
+
+impl CapturableApplication {
+    /// Gets the "identifier" of the application
+    /// 
+    /// On Macos, this is the application bundle, and on windows, this is the application file name
+    pub fn identifier(&self) -> String {
+        self.impl_capturable_application.identifier()
+    }
+}
+
\ No newline at end of file diff --git a/docs/macos_docs/src/crabgrab/capture_stream.rs.html b/docs/macos_docs/src/crabgrab/capture_stream.rs.html new file mode 100644 index 00000000..766accfb --- /dev/null +++ b/docs/macos_docs/src/crabgrab/capture_stream.rs.html @@ -0,0 +1,531 @@ +capture_stream.rs - source
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
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+
use std::fmt::Debug;
+use std::{error::Error, fmt::Display};
+
+use crate::platform::platform_impl::{ImplAudioCaptureConfig, ImplCaptureConfig, ImplCaptureStream};
+use crate::capturable_content::Capturable;
+use crate::prelude::{AudioChannelCount, AudioFrame, AudioSampleRate, CapturableDisplay, CapturableWindow, VideoFrame};
+use crate::util::{Point, Rect, Size};
+
+/// Represents an event in a capture stream
+#[derive(Debug)]
+pub enum StreamEvent {
+    /// This event is produced when the stream receives a new audio packet
+    Audio(AudioFrame),
+    /// This event is produced when the stream receives a new video frame
+    Video(VideoFrame),
+    /// This event is produced when the stream goes idle - IE when no new frames are expected for some time, like when a window minimizes
+    Idle,
+    /// This event is produced once at the end of the stream
+    End,
+}
+
+/// This represents an error during a stream, for example a failure to retreive a video or audio frame
+#[derive(Debug, Clone)]
+pub enum StreamError {
+    Other(String),
+}
+
+impl Display for StreamError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Other(message) => f.write_fmt(format_args!("StreamError::Other(\"{}\")", message))
+        }
+    }
+}
+
+impl Error for StreamError {
+    fn source(&self) -> Option<&(dyn Error + 'static)> {
+        None
+    }
+
+    fn description(&self) -> &str {
+        "description() is deprecated; use Display"
+    }
+
+    fn cause(&self) -> Option<&dyn Error> {
+        self.source()
+    }
+}
+
+/// This represents an error when creating a capture stream
+#[derive(Debug, Clone)]
+pub enum StreamCreateError {
+    Other(String),
+    /// The supplied pixel format is unsupported by the implementation
+    UnsupportedPixelFormat,
+    //GpuLost,
+}
+
+unsafe impl Send for StreamCreateError {}
+unsafe impl Sync for StreamCreateError {}
+
+/// This represents an error while stopping a stream
+#[derive(Debug)]
+pub enum StreamStopError {
+    Other(String),
+    /// The stream was already stopped
+    AlreadyStopped,
+    //GpuLost,
+}
+
+unsafe impl Send for StreamStopError {}
+unsafe impl Sync for StreamStopError {}
+
+impl Display for StreamCreateError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Other(message) => f.write_fmt(format_args!("StreamCreateError::Other(\"{}\")", message)),
+            Self::UnsupportedPixelFormat => f.write_fmt(format_args!("SteamCreateError::UnsupportedPixelFormat")),
+        }
+    }
+}
+
+impl Error for StreamCreateError {
+    fn source(&self) -> Option<&(dyn Error + 'static)> {
+        None
+    }
+
+    fn description(&self) -> &str {
+        "description() is deprecated; use Display"
+    }
+
+    fn cause(&self) -> Option<&dyn Error> {
+        self.source()
+    }
+}
+
+/// Configuration settings for audio streams
+#[derive(Clone, Debug)]
+pub struct AudioCaptureConfig {
+    pub(crate)  sample_rate: AudioSampleRate, 
+    pub(crate)  channel_count: AudioChannelCount,
+    pub(crate)  impl_capture_audio_config: ImplAudioCaptureConfig,
+}
+
+impl AudioCaptureConfig {
+    /// Creates a new audio capture config with default settings:
+    /// * 24000 hz
+    /// * Mono
+    pub fn new() -> Self {
+        Self {
+            sample_rate: AudioSampleRate::Hz24000,
+            channel_count: AudioChannelCount::Mono,
+            impl_capture_audio_config: ImplAudioCaptureConfig::new()
+        }
+    }
+}
+
+/// The pixel format of returned video frames
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[non_exhaustive]
+pub enum CapturePixelFormat {
+    /// One plane, 4 channels, 8 bits per channel: {b: u8, g: u8, r: u8, a: u8}, full range: [0, 255]
+    Bgra8888,
+    /// One plane, 4 channels, 10 bits per color channel, two bits for alpha: {a: u2, r: u10, g: u10, b: u10}, rgb range: [0, 1023], alpha range: [0, 3]
+    Argb2101010,
+    /// Two planes:
+    /// * 1 channel, luminance, 8 bits per pixel, video range: [16, 240]
+    /// * 2 channels, chroma (cb, cr) 8 bits bits per channel per two pixels vertically, range: [0, 255]
+    V420,
+    /// Two planes:
+    /// * 1 channel, luminance, 8 bits per pixel, full range: [0, 255]
+    /// * 2 channels, chroma (cb, cr) 8 bits bits per channel per two pixels vertically, range: [0, 255]
+    F420,
+}
+
+/// Configuration settings for a capture stream
+#[derive(Clone, Debug)]
+pub struct CaptureConfig {
+    pub(crate) target: Capturable,
+    pub(crate) source_rect: Rect,
+    pub(crate) output_size: Size,
+    pub(crate) show_cursor: bool,
+    pub(crate) pixel_format: CapturePixelFormat,
+    pub(crate) capture_audio: Option<AudioCaptureConfig>,
+    pub(crate) impl_capture_config: ImplCaptureConfig,
+    pub(crate) buffer_count: usize,
+}
+
+/// Represents an error creating the capture config
+#[derive(Debug, Clone)]
+pub enum CaptureConfigError {
+    /// The pixel format is unsupported by the implementation
+    UnsupportedPixelFormat,
+    /// The buffer count is out of the valid range for the implementation
+    InvalidBufferCount,
+}
+
+impl CaptureConfig {
+    /// Create a capture configuration for a given capturable window
+    pub fn with_window(window: CapturableWindow, pixel_format: CapturePixelFormat) -> Result<CaptureConfig, CaptureConfigError> {
+        let rect = window.rect();
+        Ok(CaptureConfig {
+            target: Capturable::Window(window),
+            pixel_format,
+            source_rect: Rect {
+                origin: Point {
+                    x: 0.0,
+                    y: 0.0,
+                },
+                size: rect.size
+            },
+            output_size: rect.size,
+            show_cursor: false,
+            impl_capture_config: ImplCaptureConfig::new(),
+            capture_audio: None,
+            buffer_count: 3,
+        })
+    }
+
+    /// Create a capture configuration for a given capturable display
+    pub fn with_display(display: CapturableDisplay, pixel_format: CapturePixelFormat) -> CaptureConfig {
+        let rect = display.rect();
+        CaptureConfig {
+            target: Capturable::Display(display),
+            pixel_format,
+            source_rect: Rect {
+                origin: Point {
+                    x: 0.0,
+                    y: 0.0,
+                },
+                size: rect.size
+            },
+            output_size: rect.size,
+            show_cursor: false,
+            impl_capture_config: ImplCaptureConfig::new(),
+            capture_audio: None,
+            buffer_count: 3,
+        }
+    }
+
+    /// Configure the buffer count - the number of frames in the capture queue.
+    /// 
+    /// Higher numbers mean higher latency, but smoother performance
+    pub fn with_buffer_count(self, buffer_count: usize) -> Self {
+        Self {
+            buffer_count,
+            ..self
+        }
+    }
+
+    /// Configure whether the cursor is visible in the capture
+    pub fn with_show_cursor(self, show_cursor: bool) -> Self {
+        Self {
+            show_cursor,
+            ..self
+        }
+    }
+
+    /// Configure the output texture size - by default, this will match the captured content at the time of enumeration
+    pub fn set_output_size(self, output_size: Size) -> Self {
+        Self {
+            output_size,
+            ..self
+        }
+    }
+}
+
+/// Represents an active capture stream
+pub struct CaptureStream {
+    pub(crate) impl_capture_stream: ImplCaptureStream,
+}
+
+impl CaptureStream {
+    /// Test whether the calling application has permission to capture content
+    pub fn test_access(borderless: bool) -> bool {
+        ImplCaptureStream::check_access(borderless)
+    }
+
+    /// Prompt the user for permission to capture content
+    pub async fn request_access(borderless: bool) -> bool {
+        ImplCaptureStream::request_access(borderless).await
+    }
+
+    /// Gets the implementation's supported pixel formats
+    /// 
+    /// Note that the returned formats may not work for all capture modalities (eg, window vs display)
+    pub fn supported_pixel_formats() -> &'static [CapturePixelFormat] {
+        ImplCaptureStream::supported_pixel_formats()
+    }
+
+    /// Start a new capture stream with the given stream callback
+    pub fn new(config: CaptureConfig, callback: impl FnMut(Result<StreamEvent, StreamError>) + Send + 'static) -> Result<Self, StreamCreateError> {
+        let boxed_callback = Box::new(callback);
+        Ok(Self {
+            impl_capture_stream: ImplCaptureStream::new(config, boxed_callback)?
+        })
+    }
+
+    /// Stop the capture
+    pub fn stop(&mut self) -> Result<(), StreamStopError> {
+        self.impl_capture_stream.stop()
+    }
+}
+
+
+
\ No newline at end of file diff --git a/docs/macos_docs/src/crabgrab/feature/bitmap/mod.rs.html b/docs/macos_docs/src/crabgrab/feature/bitmap/mod.rs.html new file mode 100644 index 00000000..bce0a422 --- /dev/null +++ b/docs/macos_docs/src/crabgrab/feature/bitmap/mod.rs.html @@ -0,0 +1,545 @@ +mod.rs - source
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
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+
#![cfg(feature = "bitmap")]
+
+use std::error::Error;
+use std::fmt::Display;
+
+use half::f16;
+
+use crate::prelude::VideoFrame;
+#[cfg(target_os = "macos")]
+use crate::platform::macos::frame::MacosVideoFrame;
+#[cfg(target_os = "macos")]
+use crate::platform::platform_impl::objc_wrap::CVPixelFormat;
+
+#[cfg(target_os = "windows")]
+use crate::feature::dx11::{WindowsDx11VideoFrame, WindowsDx11VideoFrameError};
+#[cfg(target_os = "windows")]
+use windows::Win32::Graphics::Direct3D11::ID3D11Texture2D;
+#[cfg(target_os = "windows")]
+use windows::Graphics::DirectX::DirectXPixelFormat;
+#[cfg(target_os = "windows")]
+use windows::core::ComInterface;
+#[cfg(target_os = "windows")]
+use windows::Win32::Graphics::Direct3D11::{D3D11_BOX, D3D11_CPU_ACCESS_READ, D3D11_MAPPED_SUBRESOURCE, D3D11_MAP_READ, D3D11_TEXTURE2D_DESC, D3D11_USAGE_STAGING};
+#[cfg(target_os = "windows")]
+use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_SAMPLE_DESC};
+#[cfg(target_os = "windows")]
+use windows::Win32::System::WinRT::Direct3D11::IDirect3DDxgiInterfaceAccess;
+#[cfg(target_os = "windows")]
+use windows::Win32::Graphics::Direct3D11::{D3D11_STANDARD_MULTISAMPLE_QUALITY_LEVELS, D3D11_USAGE_DYNAMIC};
+
+pub struct FrameBitmapBgraUnorm8x4 {
+    pub data: Box<[[u8; 4]]>,
+    pub width:  usize,
+    pub height: usize,
+}
+
+pub struct FrameBitmapRgbaUnormPacked1010102 {
+    pub data: Box<[u32]>,
+    pub width:  usize,
+    pub height: usize,
+}
+
+pub struct FrameBitmapRgbaF16x4 {
+    pub data: Box<[[f16; 4]]>,
+    pub width:  usize,
+    pub height: usize,
+}
+
+pub enum VideoRange {
+    Video,
+    Full,
+}
+
+/// A YCbCr image, corresponding to either V420 or F420 pixel formats.
+/// 
+/// Dual-planar, with luma (Y) in one plane, and chroma (CbCr) in another.
+/// Note that each plane may have a different size, as with V420 format, where
+/// the chroma plane is 2x2 blocks, but luma is per-pixel
+pub struct FrameBitmapYCbCr {
+    pub luma_data: Box<[u8]>,
+    pub luma_width: usize,
+    pub luma_height: usize,
+    pub chroma_data: Box<[[u8; 2]]>,
+    pub chroma_width: usize,
+    pub chroma_height: usize,
+    pub range: VideoRange,
+}
+
+/// A bitmap image
+pub enum FrameBitmap {
+    BgraUnorm8x4(FrameBitmapBgraUnorm8x4),
+    RgbaUnormPacked1010102(FrameBitmapRgbaUnormPacked1010102),
+    RgbaF16x4(FrameBitmapRgbaF16x4),
+    YCbCr(FrameBitmapYCbCr),
+}
+
+/// A video frame which can produce a bitmap
+pub trait VideoFrameBitmap {
+    /// Create a bitmap image from this frame. This usually involves a memory transfer from VRAM to system RAM,
+    /// and is an expensive operation.
+    fn get_bitmap(&self) -> Result<FrameBitmap, VideoFrameBitmapError>;
+}
+
+#[derive(Clone, Debug)]
+pub enum VideoFrameBitmapError {
+    Other(String),
+}
+
+impl Display for VideoFrameBitmapError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Other(error) => f.write_fmt(format_args!("VideoFrameBitmapError::Other(\"{}\")", error)),
+        }
+    }
+}
+
+impl Error for VideoFrameBitmapError {
+    fn source(&self) -> Option<&(dyn Error + 'static)> {
+        None
+    }
+
+    fn description(&self) -> &str {
+        "description() is deprecated; use Display"
+    }
+
+    fn cause(&self) -> Option<&dyn Error> {
+        self.source()
+    }
+}
+
+impl VideoFrameBitmap for VideoFrame {
+    fn get_bitmap(&self) -> Result<FrameBitmap, VideoFrameBitmapError> {
+        #[cfg(target_os = "windows")]
+        {
+            let (width, height) = self.impl_video_frame.frame_size;
+            match self.get_dx11_surface() {
+                Err(WindowsDx11VideoFrameError::Other(x)) => Err(VideoFrameBitmapError::Other(x)),
+                Ok((surface, pixel_format)) => {
+                    let (pixel_size, dxgi_format) = match pixel_format {
+                        DirectXPixelFormat::B8G8R8A8UIntNormalized => (4, DXGI_FORMAT_B8G8R8A8_UNORM),
+                        DirectXPixelFormat::R10G10B10A2UIntNormalized => (4, DXGI_FORMAT_R10G10B10A2_UNORM),
+                        _ => return Err(VideoFrameBitmapError::Other("Unknown or unsupported pixel format on DXGISurface".to_string())),
+                    };
+                    
+                    unsafe {
+                        let surface_desc = surface.Description()
+                            .map_err(|_| VideoFrameBitmapError::Other("Couldn't get description of frame surface".to_string()))?;
+                        let mut new_texture_desc = D3D11_TEXTURE2D_DESC::default();
+                        new_texture_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ.0 as u32;
+                        new_texture_desc.ArraySize = 1;
+                        new_texture_desc.BindFlags = 0;
+                        new_texture_desc.Width = surface_desc.Width as u32;
+                        new_texture_desc.Height = surface_desc.Height as u32;
+                        new_texture_desc.MipLevels = 1;
+                        new_texture_desc.SampleDesc.Count = 1;
+                        new_texture_desc.SampleDesc.Quality = 0;
+                        new_texture_desc.Usage.0 = D3D11_USAGE_STAGING.0 | D3D11_USAGE_DYNAMIC.0;
+                        new_texture_desc.Format = dxgi_format;
+                        let mut staging_texture = Option::<ID3D11Texture2D>::None;
+                        let staging_tex_result = self.impl_video_frame.device.CreateTexture2D(&new_texture_desc as *const _, None, Some(&mut staging_texture as *mut _));
+                        staging_tex_result.map_err(|error| VideoFrameBitmapError::Other(format!("Failed to create texture: {}", error.to_string())))?;
+                        let dxgi_interfce_access: IDirect3DDxgiInterfaceAccess = surface.cast()
+                            .map_err(|_| VideoFrameBitmapError::Other("Couldn't create surface interface access".to_string()))?;
+                        let surface_texture: ID3D11Texture2D = dxgi_interfce_access.GetInterface()
+                            .map_err(|_| VideoFrameBitmapError::Other("Couldn't create surface texture from surface IDirect3DDxgiInterfaceAccess".to_string()))?;
+                        let device = self.impl_video_frame.device.GetImmediateContext()
+                            .map_err(|_| VideoFrameBitmapError::Other("Couldn't get immediate d3d11 context".to_string()))?;
+                        let staging_texture = staging_texture.unwrap();
+                        device.CopyResource(&staging_texture, &surface_texture);
+                        let mut mapped_resource = D3D11_MAPPED_SUBRESOURCE::default();
+                        let map_result = device.Map(&staging_texture, 0, D3D11_MAP_READ, 0, Some(&mut mapped_resource as *mut _));
+                        map_result.map_err(|_| VideoFrameBitmapError::Other("Couldn't map staging texture".to_string()))?;
+                        match pixel_format {
+                            DirectXPixelFormat::B8G8R8A8UIntNormalized => {
+                                let mut image_data = vec![[0u8; 4]; width * height];
+                                let bpr = mapped_resource.RowPitch as usize;
+                                let surface_slice = std::slice::from_raw_parts(mapped_resource.pData as *const u8, bpr * height);
+                                for y in 0..height {
+                                    let source_slice = bytemuck::cast_slice::<_, [u8; 4]>(&surface_slice[(bpr * y)..(bpr * y + 4 * width)]);
+                                    image_data[(width * y)..(width * y + width)].copy_from_slice(source_slice);
+                                }
+                                let _ = device.Unmap(&staging_texture, 0);
+                                Ok(FrameBitmap::BgraUnorm8x4(FrameBitmapBgraUnorm8x4 {
+                                    data: image_data.into_boxed_slice(),
+                                    width,
+                                    height,
+                                }))
+                            },
+                            DirectXPixelFormat::R10G10B10A2UIntNormalized => {
+                                let mut image_data = vec![0u32; width * height];
+                                let bpr = mapped_resource.RowPitch as usize;
+                                let surface_slice = std::slice::from_raw_parts(mapped_resource.pData as *const u8, bpr * height);
+                                for y in 0..height {
+                                    let source_slice = bytemuck::cast_slice::<_, u32>(&surface_slice[(bpr * y)..(bpr * y + 4 * width)]);
+                                    image_data[(width * y)..(width * y + width)].copy_from_slice(source_slice);
+                                }
+                                let _ = device.Unmap(&staging_texture, 0);
+                                Ok(FrameBitmap::RgbaUnormPacked1010102(FrameBitmapRgbaUnormPacked1010102 {
+                                    data: image_data.into_boxed_slice(),
+                                    width,
+                                    height,
+                                }))
+                            },
+                            _ => {
+                                Err(VideoFrameBitmapError::Other("Unknown or unsupported pixel format on DXGISurface".to_string()))
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        #[cfg(target_os = "macos")]
+        {
+            let iosurface = match &self.impl_video_frame {
+                MacosVideoFrame::SCStream(sc_frame) => {
+                    match sc_frame.sample_buffer.get_image_buffer().map(|image_buffer| image_buffer.get_iosurface()).flatten() {
+                        Some(iosurface) => iosurface,
+                        None => return Err(VideoFrameBitmapError::Other("Failed to get iosurface".to_string())),
+                    }
+                },
+                MacosVideoFrame::CGDisplayStream(cg_display_frame) => {
+                    cg_display_frame.io_surface.clone()
+                }
+            };
+            if let Ok(lock_gaurd) = iosurface.lock(true, false) {
+                let pixel_format = iosurface.get_pixel_format();
+                match pixel_format {
+                    Some(CVPixelFormat::BGRA8888) => {
+                        let bpr = iosurface.get_bytes_per_row();
+                        let height = iosurface.get_height();
+                        let width = iosurface.get_width();
+                        let mut image_data = vec![[0; 4]; width * height];
+                        let base_address = lock_gaurd.get_base_address().ok_or(VideoFrameBitmapError::Other("Failed to get base address of iosurface".into()))?;
+                        let iosurface_slice = unsafe { std::slice::from_raw_parts(base_address as *const u8, bpr * height) };
+                        for y in 0..height {
+                            let source_slice = bytemuck::cast_slice::<_, [u8; 4]>(&iosurface_slice[(bpr * y)..(bpr * y + 4 * width)]);
+                            image_data[(width * y)..(width * y + width)].copy_from_slice(source_slice);
+                        }
+                        Ok(FrameBitmap::BgraUnorm8x4(FrameBitmapBgraUnorm8x4 {
+                            data: image_data.into_boxed_slice(),
+                            width,
+                            height,
+                        }))
+                    },
+                    Some(CVPixelFormat::V420) |
+                    Some(CVPixelFormat::F420) => {
+
+                        let luma_bpr = iosurface.get_bytes_per_row_of_plane(0);
+                        let luma_height = iosurface.get_height_of_plane(0);
+                        let luma_width = iosurface.get_width_of_plane(0);
+
+                        let mut luma_image_data = vec![0u8; luma_width * luma_height];
+                        let luma_base_address = lock_gaurd.get_base_address_of_plane(0).ok_or(VideoFrameBitmapError::Other("Failed to get base address of iosurface".into()))?;
+                        let luma_iosurface_slice = unsafe { std::slice::from_raw_parts(luma_base_address as *const u8, luma_bpr * luma_height) };
+
+                        for y in 0..luma_height {
+                            let luma_source_slice = &luma_iosurface_slice[(luma_bpr * y)..(luma_bpr * y + luma_width)];
+                            luma_image_data[(luma_width * y)..(luma_width * y + luma_width)].copy_from_slice(luma_source_slice);                            
+                        }
+
+                        let chroma_bpr = iosurface.get_bytes_per_row_of_plane(1);
+                        let chroma_height = iosurface.get_height_of_plane(1);
+                        let chroma_width = iosurface.get_width_of_plane(1);
+                        let mut chroma_image_data = vec![[0u8; 2]; chroma_width * chroma_height];
+                        let chroma_base_address = lock_gaurd.get_base_address_of_plane(1).ok_or(VideoFrameBitmapError::Other("Failed to get base address of iosurface".into()))?;
+                        let chroma_iosurface_slice = unsafe { std::slice::from_raw_parts(chroma_base_address as *const u8, chroma_bpr * chroma_height) };
+
+                        for y in 0..chroma_height {
+                            let chroma_source_slice = bytemuck::cast_slice::<_, [u8; 2]>(&chroma_iosurface_slice[(chroma_bpr * y)..(chroma_bpr * y + 2 * chroma_width)]);
+                            chroma_image_data[(chroma_width * y)..(chroma_width * y + chroma_width)].copy_from_slice(chroma_source_slice);
+                        }
+
+                        Ok(FrameBitmap::YCbCr(FrameBitmapYCbCr {
+                            luma_data: luma_image_data.into_boxed_slice(),
+                            chroma_data: chroma_image_data.into_boxed_slice(),
+                            luma_width,
+                            luma_height,
+                            chroma_width,
+                            chroma_height,
+                            range: if pixel_format == Some(CVPixelFormat::F420) { VideoRange::Full } else { VideoRange::Video }
+                        }))
+                    },
+                    _ => Err(VideoFrameBitmapError::Other("Unknown pixel format on iosurface".to_string()))
+                }
+            } else {
+                Err(VideoFrameBitmapError::Other("Failed to lock iosurface".to_string()))
+            }
+        }
+    }
+}
+
+
+
\ No newline at end of file diff --git a/docs/macos_docs/src/crabgrab/feature/iosurface/mod.rs.html b/docs/macos_docs/src/crabgrab/feature/iosurface/mod.rs.html new file mode 100644 index 00000000..6d8bd874 --- /dev/null +++ b/docs/macos_docs/src/crabgrab/feature/iosurface/mod.rs.html @@ -0,0 +1,189 @@ +mod.rs - source
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
+
#![cfg(target_os = "macos")]
+#![cfg(feature = "iosurface")]
+
+use std::os::raw::c_void;
+
+use std::error::Error;
+use std::fmt::Display;
+
+use crate::{platform::{macos::{frame::MacosVideoFrame, objc_wrap::IOSurfaceRef}, platform_impl::objc_wrap::{IOSurfaceDecrementUseCount, IOSurfaceIncrementUseCount}}, prelude::VideoFrame};
+
+/// A Macos IOSurface instance
+pub struct IoSurface(IOSurfaceRef);
+
+impl IoSurface {
+    /// Gets the raw IOSurfaceRef
+    pub fn get_raw(&self) -> *const c_void {
+        self.0
+    }
+
+    pub(crate) fn from_ref_unretained(r: IOSurfaceRef) -> Self {
+        unsafe { IOSurfaceIncrementUseCount(r); }
+        IoSurface(r)
+    }
+}
+
+impl Clone for IoSurface {
+    fn clone(&self) -> Self {
+        unsafe { IOSurfaceIncrementUseCount(self.0); }
+        IoSurface(self.0)
+    }
+}
+
+impl Drop for IoSurface {
+    fn drop(&mut self) {
+        unsafe { IOSurfaceDecrementUseCount(self.0); }
+    }
+}
+
+pub trait MacosIoSurfaceVideoFrame {
+    /// Get the iosurface representing the video frame's texture
+    fn get_iosurface(&self) -> Result<IoSurface, GetIoSurfaceError>;
+}
+
+#[derive(Debug)]
+pub enum GetIoSurfaceError{
+    NoImageBuffer,
+    NoIoSurface
+}
+
+impl Display for GetIoSurfaceError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::NoImageBuffer => f.write_str("GetIoSurfaceError::NoImageBuffer"),
+            Self::NoIoSurface => f.write_str("GetIoSurfaceError::NoIoSurface"),
+        }
+    }
+}
+
+impl Error for GetIoSurfaceError {
+    fn source(&self) -> Option<&(dyn Error + 'static)> {
+        None
+    }
+
+    fn description(&self) -> &str {
+        "description() is deprecated; use Display"
+    }
+
+    fn cause(&self) -> Option<&dyn Error> {
+        self.source()
+    }
+}
+
+impl MacosIoSurfaceVideoFrame for VideoFrame {
+    fn get_iosurface(&self) -> Result<IoSurface, GetIoSurfaceError> {
+        match &self.impl_video_frame {
+            MacosVideoFrame::SCStream(frame) => {
+                match frame.sample_buffer.get_image_buffer() {
+                    Some(image_buffer) => {
+                        match image_buffer.get_iosurface_ptr() {
+                            Some(ptr) => {
+                                Ok(IoSurface::from_ref_unretained(ptr))
+                            },
+                            None => Err(GetIoSurfaceError::NoIoSurface)
+                        }
+                    },
+                    None => Err(GetIoSurfaceError::NoImageBuffer)
+                }
+            },
+            MacosVideoFrame::CGDisplayStream(frame) => {
+                Ok(IoSurface::from_ref_unretained(frame.io_surface.0))
+            }
+        }
+    }
+}
+
\ No newline at end of file diff --git a/docs/macos_docs/src/crabgrab/feature/metal/mod.rs.html b/docs/macos_docs/src/crabgrab/feature/metal/mod.rs.html new file mode 100644 index 00000000..83d2428e --- /dev/null +++ b/docs/macos_docs/src/crabgrab/feature/metal/mod.rs.html @@ -0,0 +1,307 @@ +mod.rs - source
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
+
#![cfg(target_os = "macos")]
+#![cfg(feature = "metal")]
+
+use metal::foreign_types::ForeignType;
+use objc::msg_send;
+use objc::runtime::Object;
+use objc::sel;
+use objc::sel_impl;
+
+use crate::platform::platform_impl::objc_wrap::CVPixelFormat;
+use crate::prelude::{CaptureStream, VideoFrame};
+
+use std::error::Error;
+use std::fmt::Display;
+
+use crate::platform::macos::frame::MacosVideoFrame;
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum MetalVideoFramePlaneTexture {
+    Rgba,
+    Luminance,
+    Chroma
+}
+
+/// Represents an error getting the texture from a video frame
+#[derive(Clone, Debug)]
+pub enum MacosVideoFrameError {
+    NoIoSurface,
+    NoImageBuffer,
+    InvalidVideoPlaneTexture,
+    Other(String)
+}
+
+
+impl Display for MacosVideoFrameError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::NoIoSurface => f.write_str("MacosVideoFrameError::NoIoSurface"),
+            Self::NoImageBuffer => f.write_str("MacosVideoFrameError::NoImageBuffer"),
+            Self::InvalidVideoPlaneTexture => f.write_str("MacosVideoFrameError::InvalidVideoPlaneTexture"),
+            Self::Other(error) => f.write_fmt(format_args!("MacosVideoFrameError::Other(\"{}\")", error)),
+        }
+    }
+}
+
+impl Error for MacosVideoFrameError {
+    fn source(&self) -> Option<&(dyn Error + 'static)> {
+        None
+    }
+
+    fn description(&self) -> &str {
+        "description() is deprecated; use Display"
+    }
+
+    fn cause(&self) -> Option<&dyn Error> {
+        self.source()
+    }
+}
+
+pub trait MetalVideoFrame {
+    /// Get the texture for the given plane of the video frame
+    fn get_texture(&self, plane: MetalVideoFramePlaneTexture) -> Result<metal::Texture, MacosVideoFrameError>;
+}
+
+#[cfg(feature="metal")]
+impl MetalVideoFrame for VideoFrame {
+    fn get_texture(&self, plane: MetalVideoFramePlaneTexture) -> Result<metal::Texture, MacosVideoFrameError> {
+        let iosurface_and_metal_device = match &self.impl_video_frame {
+            MacosVideoFrame::SCStream(frame) => {
+                match frame.sample_buffer.get_image_buffer() {
+                    Some(image_buffer) => {
+                        match image_buffer.get_iosurface() {
+                            Some(iosurface) => {
+                                Ok((iosurface, frame.metal_device.clone()))
+                            },
+                            None => Err(MacosVideoFrameError::NoIoSurface)
+                        }
+                    },
+                    None => Err(MacosVideoFrameError::NoImageBuffer)
+                }
+            },
+            MacosVideoFrame::CGDisplayStream(frame) => {
+                Ok((frame.io_surface.clone(), frame.metal_device.clone()))
+            }
+        }?;
+        let (iosurface, metal_device) = iosurface_and_metal_device;
+        let pixel_format = match iosurface.get_pixel_format() {
+            None => return Err(MacosVideoFrameError::Other("Unable to get pixel format from iosurface".to_string())),
+            Some(format) => format
+        };
+        match pixel_format {
+            CVPixelFormat::BGRA8888 => {
+                match plane {
+                    MetalVideoFramePlaneTexture::Rgba => {},
+                    _ => return Err(MacosVideoFrameError::InvalidVideoPlaneTexture),
+                }
+                unsafe {
+                    let device_ref = metal_device.as_ref();
+                    let texture_descriptor = metal::TextureDescriptor::new();
+                    texture_descriptor.set_texture_type(metal::MTLTextureType::D2);
+                    texture_descriptor.set_pixel_format(metal::MTLPixelFormat::RGBA8Unorm);
+                    texture_descriptor.set_width(iosurface.get_width() as u64);
+                    texture_descriptor.set_height(iosurface.get_height() as u64);
+                    texture_descriptor.set_sample_count(1);
+                    texture_descriptor.set_mipmap_level_count(1);
+                    texture_descriptor.set_storage_mode(metal::MTLStorageMode::Shared);
+                    let texture_ptr: *mut Object = msg_send![device_ref, newTextureWithDescriptor: texture_descriptor.as_ptr() iosurface: iosurface.0 plane: 0];
+                    if texture_ptr.is_null() {
+                        Err(MacosVideoFrameError::Other("Failed to create metal texture".to_string()))
+                    } else {
+                        Ok((metal::Texture::from_ptr(texture_ptr as *mut metal::MTLTexture)).to_owned())
+                    }
+                }
+            },
+            CVPixelFormat::V420 | CVPixelFormat::F420 => {
+                let (plane, texture_index) = match plane {
+                    MetalVideoFramePlaneTexture::Luminance => (0, metal::MTLPixelFormat::R8Uint),
+                    MetalVideoFramePlaneTexture::Chroma => (1, metal::MTLPixelFormat::RG8Uint),
+                    _ => return Err(MacosVideoFrameError::InvalidVideoPlaneTexture),
+                };
+                unsafe {
+                    let device_ref = metal_device.as_ref();
+                    let texture_descriptor = metal::TextureDescriptor::new();
+                    texture_descriptor.set_texture_type(metal::MTLTextureType::D2);
+                    texture_descriptor.set_pixel_format(texture_index);
+                    texture_descriptor.set_width(iosurface.get_width() as u64);
+                    texture_descriptor.set_height(iosurface.get_height_of_plane(plane) as u64);
+                    texture_descriptor.set_sample_count(1);
+                    texture_descriptor.set_mipmap_level_count(1);
+                    texture_descriptor.set_storage_mode(metal::MTLStorageMode::Shared);
+                    let texture_ptr: *mut Object = msg_send![device_ref, newTextureWithDescriptor: texture_descriptor.as_ptr() iosurface: iosurface.0 plane: plane];
+                    if texture_ptr.is_null() {
+                        Err(MacosVideoFrameError::Other("Failed to create metal texture".to_string()))
+                    } else {
+                        Ok((metal::Texture::from_ptr(texture_ptr as *mut metal::MTLTexture)).to_owned())
+                    }
+                }
+            },
+            _ => Err(MacosVideoFrameError::Other("Unknown pixel format on iosurface".to_string())),
+        }
+    }
+}
+
+pub trait MetalCaptureStream {
+    /// Get the metal device used for frame capture
+    fn get_metal_device(&self) -> metal::Device;
+}
+
+impl MetalCaptureStream for CaptureStream {
+    fn get_metal_device(&self) -> metal::Device {
+        self.impl_capture_stream.metal_device.clone()
+    }
+}
+
\ No newline at end of file diff --git a/docs/macos_docs/src/crabgrab/feature/mod.rs.html b/docs/macos_docs/src/crabgrab/feature/mod.rs.html new file mode 100644 index 00000000..7971dc41 --- /dev/null +++ b/docs/macos_docs/src/crabgrab/feature/mod.rs.html @@ -0,0 +1,32 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
#[cfg(feature = "metal")]
+#[cfg(target_os="macos")]
+pub mod metal;
+#[cfg(feature = "dxgi")]
+#[cfg(target_os="windows")]
+pub mod dxgi;
+#[cfg(feature = "dx11")]
+#[cfg(target_os="windows")]
+pub mod dx11;
+#[cfg(feature = "iosurface")]
+#[cfg(target_os="macos")]
+pub mod iosurface;
+#[cfg(feature = "bitmap")]
+pub mod bitmap;
+#[cfg(feature = "wgpu")]
+pub mod wgpu;
\ No newline at end of file diff --git a/docs/macos_docs/src/crabgrab/frame.rs.html b/docs/macos_docs/src/crabgrab/frame.rs.html new file mode 100644 index 00000000..3bbb7429 --- /dev/null +++ b/docs/macos_docs/src/crabgrab/frame.rs.html @@ -0,0 +1,321 @@ +frame.rs - source
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
+
#![allow(unused)]
+use std::{marker::PhantomData, time::{Duration, Instant}, fmt::Debug};
+
+use crate::{platform::platform_impl::{ImplAudioFrame, ImplVideoFrame}, util::*};
+
+/// The rate to capture audio samples
+#[derive(Copy, Clone, Debug)]
+pub enum AudioSampleRate {
+    Hz8000,
+    Hz16000,
+    Hz24000,
+    Hz48000,
+}
+
+/// The number of audio channels to capture
+#[derive(Copy, Clone, Debug)]
+pub enum AudioChannelCount {
+    Mono,
+    Stereo
+}
+
+/// Represents audio channel data in an audio frame
+pub enum AudioChannelData<'data> {
+    F32(AudioChannelDataSamples<'data, f32>),
+    I32(AudioChannelDataSamples<'data, i32>),
+    I16(AudioChannelDataSamples<'data, i16>),
+}
+
+pub struct AudioChannelDataSamples<'data, T> {
+    pub(crate) data: *const u8,
+    pub(crate) stride: usize,
+    pub(crate) length: usize,
+    pub(crate) phantom_lifetime: PhantomData<&'data T>,
+}
+
+impl<T: Copy> AudioChannelDataSamples<'_, T> {
+    fn get(&self, i: usize) -> T {
+        let ptr = self.data.wrapping_add(self.stride * i);
+        unsafe { *(ptr as *const T) }
+    }
+
+    fn length(&self) -> usize {
+        self.length
+    }
+}
+
+/// Represents an error getting the data for an audio channel
+pub enum AudioBufferError {
+    UnsupportedFormat,
+    InvalidChannel,
+    Other(String)
+}
+
+pub(crate) trait AudioCaptureFrame {
+    fn sample_rate(&self) -> AudioSampleRate;
+    fn channel_count(&self) -> AudioChannelCount;
+    fn audio_channel_buffer(&mut self, channel: usize) -> Result<AudioChannelData<'_>, AudioBufferError>;
+    fn duration(&self) -> Duration;
+    fn origin_time(&self) -> Duration;
+    fn frame_id(&self) -> u64;
+}
+
+/// A frame of captured audio
+pub struct AudioFrame {
+    pub(crate) impl_audio_frame: ImplAudioFrame,
+}
+
+impl Debug for AudioFrame {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("AudioFrame").finish()
+    }
+}
+
+impl AudioFrame {
+    /// Get the sample rate of the captured audio
+    pub fn sample_rate(&self) -> AudioSampleRate {
+        self.impl_audio_frame.sample_rate()
+    }
+
+    /// Get the channel count of the captured audio
+    pub fn channel_count(&self) -> AudioChannelCount {
+        self.impl_audio_frame.channel_count()
+    }
+
+    /// Get the data buffer for the captured audio channel
+    pub fn audio_channel_buffer(&mut self, channel: usize) -> Result<AudioChannelData<'_>, AudioBufferError> {
+        self.impl_audio_frame.audio_channel_buffer(channel)
+    }
+
+    /// Get the duration of this audio frames
+    pub fn duration(&self) -> Duration {
+        self.impl_audio_frame.duration()
+    }
+
+    /// Get the time since the start of the stream that this audio frame begins at
+    pub fn origin_time(&self) -> Duration {
+        self.impl_audio_frame.duration()
+    }
+
+    /// Get the sequence id of this frame (monotonically increasing)
+    /// 
+    /// Note: This is separate from video frame ids
+    pub fn frame_id(&self) -> u64 {
+        self.impl_audio_frame.frame_id()
+    }
+}
+
+pub(crate) trait VideoCaptureFrame {
+    fn size(&self) -> Size;
+    fn dpi(&self) -> f64;
+    fn duration(&self) -> Duration;
+    fn origin_time(&self) -> Duration;
+    fn capture_time(&self) -> Instant;
+    fn frame_id(&self) -> u64;
+}
+
+/// A frame of captured video
+pub struct VideoFrame {
+    pub(crate) impl_video_frame: ImplVideoFrame,
+}
+
+unsafe impl Send for VideoFrame {}
+unsafe impl Sync for VideoFrame {}
+
+impl VideoFrame {
+    /// Get the sequence id of this video frame (monotonically increasing)
+    /// 
+    /// Note: This is separate from audio frame ids
+    pub fn frame_id(&self) -> u64 {
+        self.impl_video_frame.frame_id()
+    }
+
+    /// Get the Instant that this frame was delivered to the application
+    pub fn capture_time(&self) -> Instant {
+        self.impl_video_frame.capture_time()
+    }
+
+    /// Get the time since the start of the stream that this frame was generated
+    pub fn origin_time(&self) -> Duration {
+        self.impl_video_frame.origin_time()
+    }
+
+    /// Get the raw size of the frame
+    /// 
+    /// For planar image formats, this is the size of the largest plane
+    pub fn size(&self) -> Size {
+        self.impl_video_frame.size()
+    }
+
+    /// Get the dpi of the contents of the frame (accounting for capture scaling)
+    pub fn dpi(&self) -> f64 {
+        self.impl_video_frame.dpi()
+    }
+}
+
+impl Debug for VideoFrame {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("VideoFrame").finish()
+    }
+}
+
\ No newline at end of file diff --git a/docs/macos_docs/src/crabgrab/lib.rs.html b/docs/macos_docs/src/crabgrab/lib.rs.html new file mode 100644 index 00000000..af9a9122 --- /dev/null +++ b/docs/macos_docs/src/crabgrab/lib.rs.html @@ -0,0 +1,176 @@ +lib.rs - source
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
+
//! A cross-platform screen/window/audio capture library
+//! 
+//! ## Feature flags
+//! 
+//! ### GPU Interop
+//! 
+//! - **`dx11`** - enables retreiving the surface of a video frame and getting the dx11 device instance for the stream (windows only)
+//! - **`dxgi`** - enables retreiving the surface of a video frame and getting the dxgi device instance for the stream (windows only)
+//! - **`metal`** - enabels retreiving the metal textures for a video frame and getting the metal device instance for the stream (macos only)
+//! - **`iosurface`** - enables retreiving the iosurface for a video frame (macos only)
+//! 
+//! ### Bitmap output
+//! 
+//! - **`bitmap`** - enables creating raw bitmap copies of frames in system memory
+//! 
+//! ## Example
+//! 
+//! ```
+//! use std::time::Duration;
+//! use crabgrab::prelude::*;
+//! 
+//! // spin up the async runtime
+//! let runtime = tokio::runtime::Builder::new_multi_thread().build().unwrap();
+//! // run our capture code in an async context
+//! let future = runtime.spawn(async {
+//!     // ensure we have priveleges to capture content
+//!     if !CaptureStream::test_access(false) {
+//!         CaptureStream::request_access(false).await;
+//!         println!("Approve access and run again!");
+//!     }
+//!     // create a filter for the windows we're interested in capturing
+//!     let window_filter = CapturableWindowFilter {
+//!         desktop_windows: false,
+//!         onscreen_only: true,
+//!     };
+//!     // create an overall content filter
+//!     let filter = CapturableContentFilter { windows: Some(window_filter), displays: false };
+//!     // get capturable content matching the filter
+//!     let content = CapturableContent::new(filter).await.unwrap();
+//!     // find the window we want to capture
+//!     let window = content.windows().filter(|window| {
+//!         let app_identifier = window.application().identifier();
+//!         app_identifier.to_lowercase().contains("finder") || app_identifier.to_lowercase().contains("explorer")
+//!     }).next();
+//!     match window {
+//!         Some(window) => {
+//!             println!("capturing window: {}", window.title()); 
+//!             // create a captuere config using the first supported pixel format
+//!             let config = CaptureConfig::with_window(window, CaptureStream::supported_pixel_formats()[0]).unwrap();
+//!             // create a capture stream with an event handler callback
+//!             let mut stream = CaptureStream::new(config, |stream_event| {
+//!                 match stream_event {
+//!                     Ok(event) => {
+//!                         match event {
+//!                             StreamEvent::Video(frame) => {
+//!                                 println!("Got frame: {}", frame.frame_id());
+//!                             },
+//!                             _ => {}
+//!                         }
+//!                     },
+//!                     Err(error) => {
+//!                         println!("Stream error: {:?}", error);
+//!                     }
+//!                 }
+//!             }).unwrap();
+//!             // wait for a while to capture some frames
+//!             tokio::task::block_in_place(|| std::thread::sleep(Duration::from_millis(4000)));
+//!             stream.stop().unwrap();
+//!         },
+//!         None => { println!("Failed to find window"); }
+//!     }
+//! });
+//! // wait for the future to complete
+//! runtime.block_on(future).unwrap();
+//! // shutdown the async runtime
+//! runtime.shutdown_timeout(Duration::from_millis(10000));
+//! ````
+//! 
+
+pub mod platform;
+pub mod feature;
+
+pub mod util;
+pub mod frame;
+pub mod capture_stream;
+pub mod capturable_content;
+
+pub mod prelude;
\ No newline at end of file diff --git a/docs/macos_docs/src/crabgrab/platform/macos/capturable_content.rs.html b/docs/macos_docs/src/crabgrab/platform/macos/capturable_content.rs.html new file mode 100644 index 00000000..3e9f5275 --- /dev/null +++ b/docs/macos_docs/src/crabgrab/platform/macos/capturable_content.rs.html @@ -0,0 +1,257 @@ +capturable_content.rs - source
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
+
use std::{cell::Cell, fmt::Debug};
+
+use futures::channel::oneshot;
+use libc::getpid;
+
+use crate::{capturable_content::{CapturableContentFilter, CapturableContentError}, util::{Rect, Point, Size}};
+
+use super::objc_wrap::{SCDisplay, SCRunningApplication, SCShareableContent, SCWindow};
+
+pub struct MacosCapturableContent {
+    pub windows: Vec<SCWindow>,
+    pub displays: Vec<SCDisplay>,
+}
+
+impl MacosCapturableContent {
+    pub async fn new(filter: CapturableContentFilter) -> Result<Self, CapturableContentError> {
+        let (exclude_desktop, onscreen_only) = filter.windows.map_or((false, true), |filter| (!filter.desktop_windows, filter.onscreen_only));
+        let (tx, rx) = oneshot::channel();
+        let tx = Cell::new(Some(tx));
+        SCShareableContent::get_shareable_content_with_completion_handler(exclude_desktop, onscreen_only, move |result| {
+            if let Some(tx) = tx.take() {
+                let _ = tx.send(result);
+            }
+        });
+
+        match rx.await {
+            Ok(Ok(content)) => {
+                let windows = content.windows();
+                let displays = content.displays();
+                Ok(Self {
+                    windows,
+                    displays,
+                })
+            },
+            Ok(Err(error)) => {
+                Err(CapturableContentError::Other(format!("SCShareableContent returned error code: {}", error.code())))
+            }
+            Err(_) => Err(CapturableContentError::Other("Failed to receive SCSharableContent result from completion handler future".into())),
+        }
+    }
+}
+
+#[derive(Clone)]
+pub struct MacosCapturableWindow {
+    pub(crate) window: SCWindow
+}
+
+impl MacosCapturableWindow {
+    pub fn from_impl(window: SCWindow) -> Self {
+        Self {
+            window
+        }
+    }
+
+    pub fn title(&self) -> String {
+        self.window.title()
+    }
+
+    pub fn rect(&self) -> Rect {
+        let frame = self.window.frame();
+        Rect {
+            origin: Point {
+                x: frame.origin.x,
+                y: frame.origin.y,
+            },
+            size: Size {
+                width: frame.size.x,
+                height: frame.size.y
+            }
+        }
+    }
+
+    pub fn application(&self) -> MacosCapturableApplication {
+        MacosCapturableApplication {
+            running_application: self.window.owning_application()
+        }
+    }
+}
+
+impl Debug for MacosCapturableWindow {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("MacosCapturableWindow").field("window", &self.window.title()).finish()
+    }
+}
+
+#[derive(Clone)]
+pub struct MacosCapturableDisplay {
+    pub(crate) display: SCDisplay
+}
+
+impl MacosCapturableDisplay {
+    pub fn from_impl(display: SCDisplay) -> Self {
+        Self {
+            display
+        }
+    }
+
+    pub fn rect(&self) -> Rect {
+        let frame = self.display.frame();
+        Rect {
+            origin: Point {
+                x: frame.origin.x,
+                y: frame.origin.y,
+            },
+            size: Size {
+                width: frame.size.x,
+                height: frame.size.y
+            }
+        }
+    }
+}
+
+impl Debug for MacosCapturableDisplay {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("MacosCapturableDisplay").field("display", &self.display.raw_id()).finish()
+    }
+}
+
+#[derive()]
+pub struct MacosCapturableApplication {
+    pub(crate) running_application: SCRunningApplication,
+}
+
+impl MacosCapturableApplication {
+    pub fn identifier(&self) -> String {
+        self.running_application.bundle_identifier()
+    }
+}
+
\ No newline at end of file diff --git a/docs/macos_docs/src/crabgrab/platform/macos/capture_stream.rs.html b/docs/macos_docs/src/crabgrab/platform/macos/capture_stream.rs.html new file mode 100644 index 00000000..660afcaa --- /dev/null +++ b/docs/macos_docs/src/crabgrab/platform/macos/capture_stream.rs.html @@ -0,0 +1,685 @@ +capture_stream.rs - source
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
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+
use std::{borrow::{Borrow, BorrowMut}, cell::{Cell, RefCell}, sync::{atomic::{self, AtomicBool, AtomicU64}, Arc}, time::{Duration, Instant}};
+
+use futures::executor::block_on;
+use objc::runtime::Object;
+use parking_lot::Mutex;
+
+use crate::{capture_stream::{CaptureConfig, StreamCreateError, StreamError, StreamEvent}, platform::platform_impl::{frame::MacosSCStreamVideoFrame, objc_wrap::NSNumber}, prelude::{AudioCaptureConfig, AudioFrame, Capturable, CaptureConfigError, CapturePixelFormat, StreamStopError, VideoFrame}, util::{Rect, Size}};
+use super::{frame::{MacosAudioFrame, MacosCGDisplayStreamVideoFrame, MacosVideoFrame}, objc_wrap::{kCFBooleanFalse, kCFBooleanTrue, kCGDisplayStreamDestinationRect, kCGDisplayStreamMinimumFrameTime, kCGDisplayStreamPreserveAspectRatio, kCGDisplayStreamQueueDepth, kCGDisplayStreamShowCursor, kCGDisplayStreamSourceRect, CFNumber, CGDisplayStream, CGDisplayStreamFrameStatus, CGPoint, CGRect, CGSize, CMSampleBuffer, CMTime, DispatchQueue, NSArray, NSDictionary, NSString, SCContentFilter, SCFrameStatus, SCStream, SCStreamCallbackError, SCStreamColorMatrix, SCStreamConfiguration, SCStreamFrameInfoStatus, SCStreamHandler, SCStreamOutputType, SCStreamPixelFormat, SCStreamSampleRate}};
+
+pub type MacosPixelFormat = SCStreamPixelFormat;
+
+impl TryFrom<CapturePixelFormat> for SCStreamPixelFormat {
+    type Error = StreamCreateError;
+
+    fn try_from(value: CapturePixelFormat) -> Result<Self, Self::Error> {
+        match value {
+            CapturePixelFormat::Bgra8888 => Ok(SCStreamPixelFormat::BGRA8888),
+            CapturePixelFormat::Argb2101010 => Ok(SCStreamPixelFormat::L10R),
+            CapturePixelFormat::F420 => Ok(SCStreamPixelFormat::F420),
+            CapturePixelFormat::V420 => Ok(SCStreamPixelFormat::V420),
+            _ => Err(StreamCreateError::UnsupportedPixelFormat)
+        }
+    }
+}
+
+enum MacosCaptureStreamInternal {
+    Window(SCStream),
+    Display(CGDisplayStream),
+}
+
+pub(crate) struct MacosCaptureStream {
+    stream: MacosCaptureStreamInternal,
+    stopped_flag: Arc<AtomicBool>,
+    shared_callback: Arc<Mutex<Box<dyn FnMut(Result<StreamEvent, StreamError>) + Send + 'static>>>,
+    #[cfg(feature = "metal")]
+    pub(crate) metal_device: metal::Device,
+}
+
+pub trait MacosCaptureConfigExt {
+    fn with_scale_to_fit(self, scale_to_fit: bool) -> Self;
+    fn with_maximum_fps(self, maximum_fps: Option<f32>) -> Self;
+    #[cfg(feature = "metal")]
+    fn with_metal_device(self, metal_device: metal::Device) -> Self;
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct MacosCaptureConfig {
+    scale_to_fit: bool,
+    maximum_fps: Option<f32>,
+    #[cfg(feature = "metal")]
+    metal_device: Option<metal::Device>,
+}
+
+impl MacosCaptureConfig {
+    pub fn new() -> Self {
+        Self {
+            scale_to_fit: true,
+            maximum_fps: None,
+            #[cfg(feature = "metal")]
+            metal_device: None,
+        }
+    }
+}
+
+impl MacosCaptureConfigExt for CaptureConfig {
+    fn with_scale_to_fit(self, scale_to_fit: bool) -> Self {
+        Self {
+            impl_capture_config: MacosCaptureConfig {
+                scale_to_fit,
+                ..self.impl_capture_config
+            },
+            ..self
+        }
+    }
+
+    fn with_maximum_fps(self, maximum_fps: Option<f32>) -> Self {
+        Self {
+            impl_capture_config: MacosCaptureConfig {
+                maximum_fps,
+                ..self.impl_capture_config
+            },
+            ..self
+        }
+    }
+
+    #[cfg(feature = "metal")]
+    fn with_metal_device(self, metal_device: metal::Device) -> Self {
+        Self {
+            impl_capture_config: MacosCaptureConfig {
+                metal_device: Some(metal_device),
+                ..self.impl_capture_config
+            },
+            ..self
+        }
+    }
+}
+
+pub trait MacosAudioCaptureConfigExt {
+    fn set_exclude_current_process_audio(self, exclude_current_process_audio: bool) -> Self;
+}
+
+#[derive(Copy, Clone, Debug)]
+pub(crate) struct MacosAudioCaptureConfig {
+    exclude_current_process_audio: bool,
+}
+
+impl MacosAudioCaptureConfig {
+    pub fn new() -> Self {
+        Self {
+            exclude_current_process_audio: false,
+        }
+    }
+}
+
+impl MacosAudioCaptureConfigExt for AudioCaptureConfig {
+    fn set_exclude_current_process_audio(self, exclude_current_process_audio: bool) -> Self {
+        Self {
+            impl_capture_audio_config: MacosAudioCaptureConfig {
+                exclude_current_process_audio,
+                ..self.impl_capture_audio_config
+            },
+            ..self
+        }
+    }
+}
+
+impl MacosCaptureStream {
+    pub fn supported_pixel_formats() -> &'static [CapturePixelFormat] {
+        &[
+            CapturePixelFormat::V420,
+            CapturePixelFormat::F420,
+            CapturePixelFormat::Bgra8888,
+            CapturePixelFormat::Argb2101010,
+        ]
+    }
+
+    pub fn check_access(_borderless: bool) -> bool {
+        return SCStream::preflight_access()
+    }
+
+    pub async fn request_access(_borderless: bool) -> bool {
+        SCStream::request_access().await
+    }
+
+    pub fn new(capture_config: CaptureConfig, mut callback: Box<impl FnMut(Result<StreamEvent, StreamError>) + Send + 'static>) -> Result<Self, StreamCreateError> {
+        let shared_callback = Arc::new(Mutex::new(callback as Box<dyn FnMut(Result<StreamEvent, StreamError>) + Send + 'static>));
+        let stream_shared_callback = shared_callback.clone();
+        match capture_config.target {
+            Capturable::Window(window) => {
+                let mut config = SCStreamConfiguration::new();
+                config.set_size(CGSize {
+                    x: window.rect().size.width * 2.0,
+                    y: window.rect().size.height * 2.0,
+                });
+                config.set_scales_to_fit(false);
+                let (pixel_format, set_color_matrix) = match capture_config.pixel_format {
+                    CapturePixelFormat::Bgra8888 =>    (SCStreamPixelFormat::BGRA8888, false),
+                    CapturePixelFormat::Argb2101010 => (SCStreamPixelFormat::L10R, false),
+                    CapturePixelFormat::V420 =>        (SCStreamPixelFormat::V420, true),
+                    CapturePixelFormat::F420 =>        (SCStreamPixelFormat::F420, true),
+                };
+                if set_color_matrix {
+                    config.set_color_matrix(SCStreamColorMatrix::ItuR709_2);
+                }
+                config.set_pixel_format(pixel_format);
+                config.set_minimum_time_interval(CMTime::new_with_seconds(capture_config.impl_capture_config.maximum_fps.map(|x| 1.0 / x).unwrap_or(1.0 / 120.0) as f64, 240));
+                config.set_source_rect(CGRect {
+                    origin: CGPoint {
+                        x: capture_config.source_rect.origin.x,
+                        y: capture_config.source_rect.origin.y,
+                    },
+                    size: CGSize {
+                        x: capture_config.source_rect.size.width,
+                        y: capture_config.source_rect.size.height
+                    }
+                });
+                config.set_size(CGSize {
+                    x: capture_config.output_size.width,
+                    y: capture_config.output_size.height,
+                });
+                config.set_queue_depth(capture_config.buffer_count as isize);
+                config.set_show_cursor(capture_config.show_cursor);
+                match capture_config.capture_audio {
+                    Some(audio_config) => {
+                        config.set_capture_audio(true);
+                        let channel_count = match audio_config.channel_count {
+                            crate::prelude::AudioChannelCount::Mono => 1,
+                            crate::prelude::AudioChannelCount::Stereo => 2,
+                        };
+                        config.set_channel_count(channel_count);
+                        config.set_exclude_current_process_audio(audio_config.impl_capture_audio_config.exclude_current_process_audio);
+                        let sample_rate = match audio_config.sample_rate {
+                            crate::prelude::AudioSampleRate::Hz8000 =>  SCStreamSampleRate::R8000,
+                            crate::prelude::AudioSampleRate::Hz16000 => SCStreamSampleRate::R16000,
+                            crate::prelude::AudioSampleRate::Hz24000 => SCStreamSampleRate::R24000,
+                            crate::prelude::AudioSampleRate::Hz48000 => SCStreamSampleRate::R48000,
+                        };
+                        config.set_sample_rate(sample_rate);
+                    },
+                    None => {
+                        config.set_capture_audio(false);
+                    }
+                }
+
+                let filter = SCContentFilter::new_with_desktop_independent_window(&window.impl_capturable_window.window);
+
+                let handler_queue = DispatchQueue::make_concurrent("com.augmend.crabgrab.window_capture".into());
+
+                let mut audio_frame_id_counter = AtomicU64::new(0);
+                let mut video_frame_id_counter = AtomicU64::new(0);
+
+                let stopped_flag = Arc::new(AtomicBool::new(false));
+                let callback_stopped_flag = stopped_flag.clone();
+
+                #[cfg(feature = "metal")]
+                let mut metal_device = match capture_config.impl_capture_config.metal_device {
+                    Some(metal_device) => metal_device,
+                    None => {
+                        match metal::Device::system_default() {
+                            Some(device) => device,
+                            None => return Err(StreamCreateError::Other("Failed to create system default metal device".into()))
+                        }
+                    }
+                };
+                #[cfg(feature = "metal")]
+                let callback_metal_device = metal_device.clone();
+                
+                let handler = SCStreamHandler::new(Box::new(move |stream_result: Result<(CMSampleBuffer, SCStreamOutputType), SCStreamCallbackError>| {
+                    let mut callback = stream_shared_callback.lock();
+                    let capture_time = Instant::now();
+                    match stream_result {
+                        Ok((sample_buffer, output_type)) => {
+                            match output_type {
+                                SCStreamOutputType::Audio => {
+                                    let frame_id = audio_frame_id_counter.fetch_add(1, atomic::Ordering::AcqRel);
+                                    // TODO...
+                                },
+                                SCStreamOutputType::Screen => {
+                                    let attachments = sample_buffer.get_sample_attachment_array();
+                                    if attachments.len() == 0 {
+                                        return;
+                                    }
+                                    let status_nsnumber_ptr = unsafe { attachments[0].get_value(SCStreamFrameInfoStatus) };
+                                    if status_nsnumber_ptr.is_null() {
+                                        return;
+                                    }
+                                    let status_i32 = unsafe { NSNumber::from_id_unretained(status_nsnumber_ptr as *mut Object).as_i32() };
+                                    let status_opt = SCFrameStatus::from_i32(status_i32);
+                                    if status_opt.is_none() {
+                                        return;
+                                    }
+                                    match status_opt.unwrap() {
+                                        SCFrameStatus::Complete => {
+                                            if callback_stopped_flag.load(atomic::Ordering::Acquire) {
+                                                return;
+                                            }
+                                            let frame_id = video_frame_id_counter.fetch_add(1, atomic::Ordering::AcqRel);
+                                            let video_frame = VideoFrame {
+                                                impl_video_frame: MacosVideoFrame::SCStream(MacosSCStreamVideoFrame {
+                                                    sample_buffer,
+                                                    capture_time,
+                                                    dictionary: RefCell::new(None),
+                                                    frame_id,
+                                                    #[cfg(feature = "metal")]
+                                                    metal_device: callback_metal_device.clone()
+                                                })
+                                            };
+                                            (callback)(Ok(StreamEvent::Video(video_frame)));
+                                        },
+                                        SCFrameStatus::Suspended |
+                                        SCFrameStatus::Idle => {
+                                            if callback_stopped_flag.load(atomic::Ordering::Acquire) {
+                                                return;
+                                            }
+                                            (callback)(Ok(StreamEvent::Idle));
+                                        },
+                                        SCFrameStatus::Stopped => {
+                                            if callback_stopped_flag.fetch_and(true, atomic::Ordering::AcqRel) {
+                                                return;
+                                            }
+                                            (callback)(Ok(StreamEvent::End));
+                                        }
+                                        _ => {}
+                                    }
+
+                                    
+                                },
+                            }
+                        },
+                        Err(err) => {
+                            let event = match err {
+                                SCStreamCallbackError::StreamStopped => {
+                                    if callback_stopped_flag.fetch_and(true, atomic::Ordering::AcqRel) {
+                                        return;
+                                    }
+                                    Ok(StreamEvent::End)
+                                },
+                                SCStreamCallbackError::SampleBufferCopyFailed => Err(StreamError::Other("Failed to copy sample buffer".into())),
+                                SCStreamCallbackError::Other(e) => Err(StreamError::Other(format!("Internal stream failure: [description: {}, reason: {}, code: {}, domain: {}]", e.description(), e.reason(), e.code(), e.domain()))),
+                            };
+                            (callback)(event);
+                        }
+                    }
+                }));
+
+                let mut sc_stream = SCStream::new(filter, config, handler_queue, handler)
+                    .map_err(|error| StreamCreateError::Other(error))?;
+
+                sc_stream.start();
+
+                Ok(MacosCaptureStream {
+                    stopped_flag,
+                    shared_callback,
+                    stream: MacosCaptureStreamInternal::Window(sc_stream),
+                    #[cfg(feature = "metal")]
+                    metal_device
+                })
+            },
+            Capturable::Display(_) => Err(StreamCreateError::Other("Macos display capture unimplemented".into()))
+        }
+
+    }
+
+    pub(crate) fn stop(&mut self) -> Result<(), StreamStopError> {
+        {
+            let mut callback = self.shared_callback.lock();
+            if !self.stopped_flag.fetch_and(true, atomic::Ordering::AcqRel) {
+                (callback)(Ok(StreamEvent::End));
+            }
+        }
+        match &mut self.stream {
+            MacosCaptureStreamInternal::Window(stream) => { stream.stop(); Ok(()) },
+            MacosCaptureStreamInternal::Display(stream) => stream.stop().map_err(|_| StreamStopError::Other("Unkown".into())),
+        }
+    }
+}
+
+impl Drop for MacosCaptureStream {
+    fn drop(&mut self) {
+        self.stop();
+    }
+}
+
\ No newline at end of file diff --git a/docs/macos_docs/src/crabgrab/platform/macos/frame.rs.html b/docs/macos_docs/src/crabgrab/platform/macos/frame.rs.html new file mode 100644 index 00000000..8706086d --- /dev/null +++ b/docs/macos_docs/src/crabgrab/platform/macos/frame.rs.html @@ -0,0 +1,389 @@ +frame.rs - source
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
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+
use std::{cell::{Ref, RefCell}, marker::PhantomData, time::{Duration, Instant}};
+
+use objc::runtime::Object;
+
+use crate::{frame::{AudioCaptureFrame, VideoCaptureFrame}, prelude::{AudioBufferError, AudioChannelCount, AudioChannelData, AudioChannelDataSamples, AudioSampleRate}, util::{Rect, Size}};
+
+use super::objc_wrap::{kAudioFormatFlagIsBigEndian, kAudioFormatFlagIsPacked, kAudioFormatFlagsCanonical, kAudioFormatNativeEndian, AVAudioFormat, AVAudioPCMBuffer, AudioBufferList, AudioStreamBasicDescription, CFDictionary, CGRect, CGRectMakeWithDictionaryRepresentation, CMBlockBuffer, CMSampleBuffer, IOSurface, NSDictionary, NSNumber, NSScreen, SCStreamFrameInfoScaleFactor, SCStreamFrameInfoScreenRect};
+
+pub(crate) struct MacosSCStreamVideoFrame {
+    pub(crate) sample_buffer: CMSampleBuffer,
+    pub(crate) capture_time: Instant,
+    pub(crate) dictionary: RefCell<Option<CFDictionary>>,
+    pub(crate) frame_id: u64,
+    #[cfg(feature = "metal")]
+    pub(crate) metal_device: metal::Device,
+}
+
+pub(crate) struct MacosCGDisplayStreamVideoFrame {
+    pub(crate) io_surface: IOSurface,
+    pub(crate) duration: Duration,
+    pub(crate) capture_time: Duration,
+    pub(crate) capture_timestamp: Instant,
+    pub(crate) frame_id: u64,
+    pub(crate) source_rect: Rect,
+    pub(crate) dest_size: Size,
+    #[cfg(feature = "metal")]
+    pub(crate) metal_device: metal::Device,
+}
+
+impl MacosSCStreamVideoFrame {
+    fn get_info_dict(&self) -> Ref<'_, CFDictionary> {
+        let needs_dict = { self.dictionary.borrow().is_none() };
+        if needs_dict {
+            let mut dict_opt_mut = self.dictionary.borrow_mut();
+            *dict_opt_mut = Some(self.sample_buffer.get_sample_attachment_array()[0].clone());
+        }
+        Ref::map(self.dictionary.borrow(), |x| x.as_ref().unwrap())
+    }
+}
+
+pub(crate) enum MacosVideoFrame {
+    SCStream(MacosSCStreamVideoFrame),
+    CGDisplayStream(MacosCGDisplayStreamVideoFrame),
+}
+
+impl VideoCaptureFrame for MacosVideoFrame {
+    fn size(&self) -> Size {
+        match self {
+            MacosVideoFrame::SCStream(sc_frame) => {
+                sc_frame.sample_buffer.get_image_buffer().map(|image_buffer| {
+                    Size {
+                        width: image_buffer.get_width() as f64,
+                        height: image_buffer.get_height() as f64,
+                    }
+                }).unwrap_or(Size { width: 0.0, height: 0.0})
+            }
+            MacosVideoFrame::CGDisplayStream(cgd_frame) => cgd_frame.dest_size
+        }
+    }
+
+    fn dpi(&self) -> f64 {
+        match self {
+            MacosVideoFrame::SCStream(sc_frame) => {
+                let info_dict = sc_frame.get_info_dict();
+                let scale_factor_ptr = unsafe { info_dict.get_value(SCStreamFrameInfoScaleFactor) };
+                let scale_factor = unsafe { NSNumber::from_id_unretained(scale_factor_ptr as *mut Object).as_f64() };
+                let screen_rect_ptr = unsafe { info_dict.get_value(SCStreamFrameInfoScreenRect) };
+                let screen_rect_dict = unsafe { NSDictionary::from_id_unretained(screen_rect_ptr as *mut Object) };
+                let frame_screen_rect = unsafe { CGRect::create_from_dictionary_representation(&screen_rect_dict) };
+                let mut dpi = 72.0f64;
+                for screen in NSScreen::screens() {
+                    let screen_rect = screen.frame();
+                    if screen_rect.contains(frame_screen_rect.origin) {
+                        dpi = screen.dpi();
+                        break;
+                    }
+                }
+                dpi
+            },
+            MacosVideoFrame::CGDisplayStream(cgd_frame) => todo!()
+        }
+    }
+
+    fn duration(&self) -> Duration {
+        match self {
+            MacosVideoFrame::SCStream(sc_frame) => std::time::Duration::from_secs_f64(sc_frame.sample_buffer.get_duration().seconds_f64()),
+            MacosVideoFrame::CGDisplayStream(cgd_frame) => cgd_frame.duration
+        }
+    }
+
+    fn origin_time(&self) -> Duration {
+        match self {
+            MacosVideoFrame::SCStream(sc_frame) => std::time::Duration::from_secs_f64(sc_frame.sample_buffer.get_presentation_timestamp().seconds_f64()),
+            MacosVideoFrame::CGDisplayStream(cgd_frame) => cgd_frame.capture_time
+        }
+    }
+
+    fn capture_time(&self) -> Instant {
+        match self {
+            MacosVideoFrame::SCStream(sc_frame) => sc_frame.capture_time,
+            MacosVideoFrame::CGDisplayStream(cgd_frame) => cgd_frame.capture_timestamp
+        }
+    }
+
+    fn frame_id(&self) -> u64 {
+        match self {
+            MacosVideoFrame::SCStream(sc_frame) => sc_frame.frame_id,
+            MacosVideoFrame::CGDisplayStream(cgd_frame) => cgd_frame.frame_id
+        }
+    }
+}
+
+pub struct MacosAudioFrame {
+    pub(crate) sample_buffer: CMSampleBuffer,
+    pub(crate) audio_format_description: AudioStreamBasicDescription,
+    pub(crate) pcm_audio_buffer: Option<AVAudioPCMBuffer>,
+    pub(crate) block_buffer: Option<CMBlockBuffer>,
+    pub(crate) buffer_list: Option<AudioBufferList>,
+    pub(crate) capture_time: Instant,
+    pub(crate) frame_id: u64,
+}
+
+impl AudioCaptureFrame for MacosAudioFrame {
+    fn sample_rate(&self) -> crate::prelude::AudioSampleRate {
+        if self.audio_format_description.sample_rate >= 15500.0 && self.audio_format_description.sample_rate <= 16500.0 {
+            AudioSampleRate::Hz16000
+        } else if self.audio_format_description.sample_rate >= 23500.0 && self.audio_format_description.sample_rate <= 24500.0 {
+            AudioSampleRate::Hz24000
+        } else if self.audio_format_description.sample_rate >= 47500.0 && self.audio_format_description.sample_rate <= 48500.0 {
+            AudioSampleRate::Hz48000
+        } else {
+            AudioSampleRate::Hz8000
+        }
+    }
+
+    fn channel_count(&self) -> crate::prelude::AudioChannelCount {
+        if self.audio_format_description.channels_per_frame == 1 {
+            AudioChannelCount::Mono
+        } else {
+            AudioChannelCount::Stereo
+        }
+    }
+
+    fn audio_channel_buffer(&mut self, channel: usize) -> Result<AudioChannelData<'_>, AudioBufferError> {
+        let pcm_audio_buffer_ref = if self.pcm_audio_buffer.is_some() {
+            self.pcm_audio_buffer.as_ref().unwrap()
+        } else {
+            if self.audio_format_description.format_flags == kAudioFormatFlagsCanonical {
+                let (audio_buffer_list, block_buffer) = match unsafe { self.sample_buffer.get_audio_buffer_list_with_block_buffer() } {
+                    Ok(x) => x,
+                    Err(()) => return Err(AudioBufferError::Other("CMSampleBuffer::get_audio_buffer_list_with_block_buffer() failed".into()))
+                };
+                self.buffer_list = Some(audio_buffer_list);
+                self.block_buffer = Some(block_buffer);
+                let audio_buffer_list = self.buffer_list.as_ref().unwrap();
+                let av_audio_format = AVAudioFormat::new_with_standard_format_sample_rate_channels(self.audio_format_description.sample_rate, self.audio_format_description.channels_per_frame);
+                if let Ok(pcm_audio_buffer) = AVAudioPCMBuffer::new_with_format_buffer_list_no_copy_deallocator(av_audio_format, audio_buffer_list as *const _) {
+                    self.pcm_audio_buffer = Some(pcm_audio_buffer);
+                    self.pcm_audio_buffer.as_ref().unwrap()
+                } else {
+                    return Err(AudioBufferError::Other("Failed to build PCM audio buffer".into()));
+                }
+            } else {
+                return Err(AudioBufferError::UnsupportedFormat);
+            }
+        };
+        if channel >= pcm_audio_buffer_ref.channel_count() {
+            return Err(AudioBufferError::InvalidChannel);
+        }
+        let stride = pcm_audio_buffer_ref.stride();
+        if let Some(f32_ptr) = pcm_audio_buffer_ref.f32_buffer(channel) {
+            let data_samples = AudioChannelDataSamples {
+                data: f32_ptr as *const u8,
+                stride,
+                length: pcm_audio_buffer_ref.frame_capacity(),
+                phantom_lifetime: PhantomData
+            };
+            return Ok(AudioChannelData::F32(data_samples));
+        }
+        return Err(AudioBufferError::Other("Failed to get audio buffer".into()))
+    }
+
+    fn duration(&self) -> std::time::Duration {
+        std::time::Duration::from_secs_f64(self.sample_buffer.get_duration().seconds_f64())
+    }
+
+    fn origin_time(&self) -> std::time::Duration {
+        std::time::Duration::from_secs_f64(self.sample_buffer.get_presentation_timestamp().seconds_f64())
+    }
+
+    fn frame_id(&self) -> u64 {
+        self.frame_id
+    }
+}
+
\ No newline at end of file diff --git a/docs/macos_docs/src/crabgrab/platform/macos/mod.rs.html b/docs/macos_docs/src/crabgrab/platform/macos/mod.rs.html new file mode 100644 index 00000000..0e0a0e19 --- /dev/null +++ b/docs/macos_docs/src/crabgrab/platform/macos/mod.rs.html @@ -0,0 +1,41 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+
#![allow(unused)]
+
+pub(crate) mod capture_stream;
+pub(crate) mod frame;
+pub(crate) mod capturable_content;
+pub(crate) mod objc_wrap;
+
+pub(crate) use capture_stream::MacosCaptureStream as ImplCaptureStream;
+pub(crate) use capture_stream::MacosAudioCaptureConfig as ImplAudioCaptureConfig;
+pub(crate) use capture_stream::MacosCaptureConfig as ImplCaptureConfig;
+pub(crate) use frame::MacosAudioFrame as ImplAudioFrame;
+pub(crate) use frame::MacosVideoFrame as ImplVideoFrame;
+pub(crate) use capturable_content::MacosCapturableContent as ImplCapturableContent;
+pub(crate) use capturable_content::MacosCapturableWindow as ImplCapturableWindow;
+pub(crate) use capturable_content::MacosCapturableDisplay as ImplCapturableDisplay;
+pub(crate) use capture_stream::MacosPixelFormat as ImplPixelFormat;
+pub(crate) use capturable_content::MacosCapturableApplication as ImplCapturableApplication;
+
+pub use capture_stream::MacosAudioCaptureConfigExt;
+pub use capture_stream::MacosCaptureConfigExt;
+
\ No newline at end of file diff --git a/docs/macos_docs/src/crabgrab/platform/macos/objc_wrap.rs.html b/docs/macos_docs/src/crabgrab/platform/macos/objc_wrap.rs.html new file mode 100644 index 00000000..f78ddf3a --- /dev/null +++ b/docs/macos_docs/src/crabgrab/platform/macos/objc_wrap.rs.html @@ -0,0 +1,5173 @@ +objc_wrap.rs - source
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
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+871
+872
+873
+874
+875
+876
+877
+878
+879
+880
+881
+882
+883
+884
+885
+886
+887
+888
+889
+890
+891
+892
+893
+894
+895
+896
+897
+898
+899
+900
+901
+902
+903
+904
+905
+906
+907
+908
+909
+910
+911
+912
+913
+914
+915
+916
+917
+918
+919
+920
+921
+922
+923
+924
+925
+926
+927
+928
+929
+930
+931
+932
+933
+934
+935
+936
+937
+938
+939
+940
+941
+942
+943
+944
+945
+946
+947
+948
+949
+950
+951
+952
+953
+954
+955
+956
+957
+958
+959
+960
+961
+962
+963
+964
+965
+966
+967
+968
+969
+970
+971
+972
+973
+974
+975
+976
+977
+978
+979
+980
+981
+982
+983
+984
+985
+986
+987
+988
+989
+990
+991
+992
+993
+994
+995
+996
+997
+998
+999
+1000
+1001
+1002
+1003
+1004
+1005
+1006
+1007
+1008
+1009
+1010
+1011
+1012
+1013
+1014
+1015
+1016
+1017
+1018
+1019
+1020
+1021
+1022
+1023
+1024
+1025
+1026
+1027
+1028
+1029
+1030
+1031
+1032
+1033
+1034
+1035
+1036
+1037
+1038
+1039
+1040
+1041
+1042
+1043
+1044
+1045
+1046
+1047
+1048
+1049
+1050
+1051
+1052
+1053
+1054
+1055
+1056
+1057
+1058
+1059
+1060
+1061
+1062
+1063
+1064
+1065
+1066
+1067
+1068
+1069
+1070
+1071
+1072
+1073
+1074
+1075
+1076
+1077
+1078
+1079
+1080
+1081
+1082
+1083
+1084
+1085
+1086
+1087
+1088
+1089
+1090
+1091
+1092
+1093
+1094
+1095
+1096
+1097
+1098
+1099
+1100
+1101
+1102
+1103
+1104
+1105
+1106
+1107
+1108
+1109
+1110
+1111
+1112
+1113
+1114
+1115
+1116
+1117
+1118
+1119
+1120
+1121
+1122
+1123
+1124
+1125
+1126
+1127
+1128
+1129
+1130
+1131
+1132
+1133
+1134
+1135
+1136
+1137
+1138
+1139
+1140
+1141
+1142
+1143
+1144
+1145
+1146
+1147
+1148
+1149
+1150
+1151
+1152
+1153
+1154
+1155
+1156
+1157
+1158
+1159
+1160
+1161
+1162
+1163
+1164
+1165
+1166
+1167
+1168
+1169
+1170
+1171
+1172
+1173
+1174
+1175
+1176
+1177
+1178
+1179
+1180
+1181
+1182
+1183
+1184
+1185
+1186
+1187
+1188
+1189
+1190
+1191
+1192
+1193
+1194
+1195
+1196
+1197
+1198
+1199
+1200
+1201
+1202
+1203
+1204
+1205
+1206
+1207
+1208
+1209
+1210
+1211
+1212
+1213
+1214
+1215
+1216
+1217
+1218
+1219
+1220
+1221
+1222
+1223
+1224
+1225
+1226
+1227
+1228
+1229
+1230
+1231
+1232
+1233
+1234
+1235
+1236
+1237
+1238
+1239
+1240
+1241
+1242
+1243
+1244
+1245
+1246
+1247
+1248
+1249
+1250
+1251
+1252
+1253
+1254
+1255
+1256
+1257
+1258
+1259
+1260
+1261
+1262
+1263
+1264
+1265
+1266
+1267
+1268
+1269
+1270
+1271
+1272
+1273
+1274
+1275
+1276
+1277
+1278
+1279
+1280
+1281
+1282
+1283
+1284
+1285
+1286
+1287
+1288
+1289
+1290
+1291
+1292
+1293
+1294
+1295
+1296
+1297
+1298
+1299
+1300
+1301
+1302
+1303
+1304
+1305
+1306
+1307
+1308
+1309
+1310
+1311
+1312
+1313
+1314
+1315
+1316
+1317
+1318
+1319
+1320
+1321
+1322
+1323
+1324
+1325
+1326
+1327
+1328
+1329
+1330
+1331
+1332
+1333
+1334
+1335
+1336
+1337
+1338
+1339
+1340
+1341
+1342
+1343
+1344
+1345
+1346
+1347
+1348
+1349
+1350
+1351
+1352
+1353
+1354
+1355
+1356
+1357
+1358
+1359
+1360
+1361
+1362
+1363
+1364
+1365
+1366
+1367
+1368
+1369
+1370
+1371
+1372
+1373
+1374
+1375
+1376
+1377
+1378
+1379
+1380
+1381
+1382
+1383
+1384
+1385
+1386
+1387
+1388
+1389
+1390
+1391
+1392
+1393
+1394
+1395
+1396
+1397
+1398
+1399
+1400
+1401
+1402
+1403
+1404
+1405
+1406
+1407
+1408
+1409
+1410
+1411
+1412
+1413
+1414
+1415
+1416
+1417
+1418
+1419
+1420
+1421
+1422
+1423
+1424
+1425
+1426
+1427
+1428
+1429
+1430
+1431
+1432
+1433
+1434
+1435
+1436
+1437
+1438
+1439
+1440
+1441
+1442
+1443
+1444
+1445
+1446
+1447
+1448
+1449
+1450
+1451
+1452
+1453
+1454
+1455
+1456
+1457
+1458
+1459
+1460
+1461
+1462
+1463
+1464
+1465
+1466
+1467
+1468
+1469
+1470
+1471
+1472
+1473
+1474
+1475
+1476
+1477
+1478
+1479
+1480
+1481
+1482
+1483
+1484
+1485
+1486
+1487
+1488
+1489
+1490
+1491
+1492
+1493
+1494
+1495
+1496
+1497
+1498
+1499
+1500
+1501
+1502
+1503
+1504
+1505
+1506
+1507
+1508
+1509
+1510
+1511
+1512
+1513
+1514
+1515
+1516
+1517
+1518
+1519
+1520
+1521
+1522
+1523
+1524
+1525
+1526
+1527
+1528
+1529
+1530
+1531
+1532
+1533
+1534
+1535
+1536
+1537
+1538
+1539
+1540
+1541
+1542
+1543
+1544
+1545
+1546
+1547
+1548
+1549
+1550
+1551
+1552
+1553
+1554
+1555
+1556
+1557
+1558
+1559
+1560
+1561
+1562
+1563
+1564
+1565
+1566
+1567
+1568
+1569
+1570
+1571
+1572
+1573
+1574
+1575
+1576
+1577
+1578
+1579
+1580
+1581
+1582
+1583
+1584
+1585
+1586
+1587
+1588
+1589
+1590
+1591
+1592
+1593
+1594
+1595
+1596
+1597
+1598
+1599
+1600
+1601
+1602
+1603
+1604
+1605
+1606
+1607
+1608
+1609
+1610
+1611
+1612
+1613
+1614
+1615
+1616
+1617
+1618
+1619
+1620
+1621
+1622
+1623
+1624
+1625
+1626
+1627
+1628
+1629
+1630
+1631
+1632
+1633
+1634
+1635
+1636
+1637
+1638
+1639
+1640
+1641
+1642
+1643
+1644
+1645
+1646
+1647
+1648
+1649
+1650
+1651
+1652
+1653
+1654
+1655
+1656
+1657
+1658
+1659
+1660
+1661
+1662
+1663
+1664
+1665
+1666
+1667
+1668
+1669
+1670
+1671
+1672
+1673
+1674
+1675
+1676
+1677
+1678
+1679
+1680
+1681
+1682
+1683
+1684
+1685
+1686
+1687
+1688
+1689
+1690
+1691
+1692
+1693
+1694
+1695
+1696
+1697
+1698
+1699
+1700
+1701
+1702
+1703
+1704
+1705
+1706
+1707
+1708
+1709
+1710
+1711
+1712
+1713
+1714
+1715
+1716
+1717
+1718
+1719
+1720
+1721
+1722
+1723
+1724
+1725
+1726
+1727
+1728
+1729
+1730
+1731
+1732
+1733
+1734
+1735
+1736
+1737
+1738
+1739
+1740
+1741
+1742
+1743
+1744
+1745
+1746
+1747
+1748
+1749
+1750
+1751
+1752
+1753
+1754
+1755
+1756
+1757
+1758
+1759
+1760
+1761
+1762
+1763
+1764
+1765
+1766
+1767
+1768
+1769
+1770
+1771
+1772
+1773
+1774
+1775
+1776
+1777
+1778
+1779
+1780
+1781
+1782
+1783
+1784
+1785
+1786
+1787
+1788
+1789
+1790
+1791
+1792
+1793
+1794
+1795
+1796
+1797
+1798
+1799
+1800
+1801
+1802
+1803
+1804
+1805
+1806
+1807
+1808
+1809
+1810
+1811
+1812
+1813
+1814
+1815
+1816
+1817
+1818
+1819
+1820
+1821
+1822
+1823
+1824
+1825
+1826
+1827
+1828
+1829
+1830
+1831
+1832
+1833
+1834
+1835
+1836
+1837
+1838
+1839
+1840
+1841
+1842
+1843
+1844
+1845
+1846
+1847
+1848
+1849
+1850
+1851
+1852
+1853
+1854
+1855
+1856
+1857
+1858
+1859
+1860
+1861
+1862
+1863
+1864
+1865
+1866
+1867
+1868
+1869
+1870
+1871
+1872
+1873
+1874
+1875
+1876
+1877
+1878
+1879
+1880
+1881
+1882
+1883
+1884
+1885
+1886
+1887
+1888
+1889
+1890
+1891
+1892
+1893
+1894
+1895
+1896
+1897
+1898
+1899
+1900
+1901
+1902
+1903
+1904
+1905
+1906
+1907
+1908
+1909
+1910
+1911
+1912
+1913
+1914
+1915
+1916
+1917
+1918
+1919
+1920
+1921
+1922
+1923
+1924
+1925
+1926
+1927
+1928
+1929
+1930
+1931
+1932
+1933
+1934
+1935
+1936
+1937
+1938
+1939
+1940
+1941
+1942
+1943
+1944
+1945
+1946
+1947
+1948
+1949
+1950
+1951
+1952
+1953
+1954
+1955
+1956
+1957
+1958
+1959
+1960
+1961
+1962
+1963
+1964
+1965
+1966
+1967
+1968
+1969
+1970
+1971
+1972
+1973
+1974
+1975
+1976
+1977
+1978
+1979
+1980
+1981
+1982
+1983
+1984
+1985
+1986
+1987
+1988
+1989
+1990
+1991
+1992
+1993
+1994
+1995
+1996
+1997
+1998
+1999
+2000
+2001
+2002
+2003
+2004
+2005
+2006
+2007
+2008
+2009
+2010
+2011
+2012
+2013
+2014
+2015
+2016
+2017
+2018
+2019
+2020
+2021
+2022
+2023
+2024
+2025
+2026
+2027
+2028
+2029
+2030
+2031
+2032
+2033
+2034
+2035
+2036
+2037
+2038
+2039
+2040
+2041
+2042
+2043
+2044
+2045
+2046
+2047
+2048
+2049
+2050
+2051
+2052
+2053
+2054
+2055
+2056
+2057
+2058
+2059
+2060
+2061
+2062
+2063
+2064
+2065
+2066
+2067
+2068
+2069
+2070
+2071
+2072
+2073
+2074
+2075
+2076
+2077
+2078
+2079
+2080
+2081
+2082
+2083
+2084
+2085
+2086
+2087
+2088
+2089
+2090
+2091
+2092
+2093
+2094
+2095
+2096
+2097
+2098
+2099
+2100
+2101
+2102
+2103
+2104
+2105
+2106
+2107
+2108
+2109
+2110
+2111
+2112
+2113
+2114
+2115
+2116
+2117
+2118
+2119
+2120
+2121
+2122
+2123
+2124
+2125
+2126
+2127
+2128
+2129
+2130
+2131
+2132
+2133
+2134
+2135
+2136
+2137
+2138
+2139
+2140
+2141
+2142
+2143
+2144
+2145
+2146
+2147
+2148
+2149
+2150
+2151
+2152
+2153
+2154
+2155
+2156
+2157
+2158
+2159
+2160
+2161
+2162
+2163
+2164
+2165
+2166
+2167
+2168
+2169
+2170
+2171
+2172
+2173
+2174
+2175
+2176
+2177
+2178
+2179
+2180
+2181
+2182
+2183
+2184
+2185
+2186
+2187
+2188
+2189
+2190
+2191
+2192
+2193
+2194
+2195
+2196
+2197
+2198
+2199
+2200
+2201
+2202
+2203
+2204
+2205
+2206
+2207
+2208
+2209
+2210
+2211
+2212
+2213
+2214
+2215
+2216
+2217
+2218
+2219
+2220
+2221
+2222
+2223
+2224
+2225
+2226
+2227
+2228
+2229
+2230
+2231
+2232
+2233
+2234
+2235
+2236
+2237
+2238
+2239
+2240
+2241
+2242
+2243
+2244
+2245
+2246
+2247
+2248
+2249
+2250
+2251
+2252
+2253
+2254
+2255
+2256
+2257
+2258
+2259
+2260
+2261
+2262
+2263
+2264
+2265
+2266
+2267
+2268
+2269
+2270
+2271
+2272
+2273
+2274
+2275
+2276
+2277
+2278
+2279
+2280
+2281
+2282
+2283
+2284
+2285
+2286
+2287
+2288
+2289
+2290
+2291
+2292
+2293
+2294
+2295
+2296
+2297
+2298
+2299
+2300
+2301
+2302
+2303
+2304
+2305
+2306
+2307
+2308
+2309
+2310
+2311
+2312
+2313
+2314
+2315
+2316
+2317
+2318
+2319
+2320
+2321
+2322
+2323
+2324
+2325
+2326
+2327
+2328
+2329
+2330
+2331
+2332
+2333
+2334
+2335
+2336
+2337
+2338
+2339
+2340
+2341
+2342
+2343
+2344
+2345
+2346
+2347
+2348
+2349
+2350
+2351
+2352
+2353
+2354
+2355
+2356
+2357
+2358
+2359
+2360
+2361
+2362
+2363
+2364
+2365
+2366
+2367
+2368
+2369
+2370
+2371
+2372
+2373
+2374
+2375
+2376
+2377
+2378
+2379
+2380
+2381
+2382
+2383
+2384
+2385
+2386
+2387
+2388
+2389
+2390
+2391
+2392
+2393
+2394
+2395
+2396
+2397
+2398
+2399
+2400
+2401
+2402
+2403
+2404
+2405
+2406
+2407
+2408
+2409
+2410
+2411
+2412
+2413
+2414
+2415
+2416
+2417
+2418
+2419
+2420
+2421
+2422
+2423
+2424
+2425
+2426
+2427
+2428
+2429
+2430
+2431
+2432
+2433
+2434
+2435
+2436
+2437
+2438
+2439
+2440
+2441
+2442
+2443
+2444
+2445
+2446
+2447
+2448
+2449
+2450
+2451
+2452
+2453
+2454
+2455
+2456
+2457
+2458
+2459
+2460
+2461
+2462
+2463
+2464
+2465
+2466
+2467
+2468
+2469
+2470
+2471
+2472
+2473
+2474
+2475
+2476
+2477
+2478
+2479
+2480
+2481
+2482
+2483
+2484
+2485
+2486
+2487
+2488
+2489
+2490
+2491
+2492
+2493
+2494
+2495
+2496
+2497
+2498
+2499
+2500
+2501
+2502
+2503
+2504
+2505
+2506
+2507
+2508
+2509
+2510
+2511
+2512
+2513
+2514
+2515
+2516
+2517
+2518
+2519
+2520
+2521
+2522
+2523
+2524
+2525
+2526
+2527
+2528
+2529
+2530
+2531
+2532
+2533
+2534
+2535
+2536
+2537
+2538
+2539
+2540
+2541
+2542
+2543
+2544
+2545
+2546
+2547
+2548
+2549
+2550
+2551
+2552
+2553
+2554
+2555
+2556
+2557
+2558
+2559
+2560
+2561
+2562
+2563
+2564
+2565
+2566
+2567
+2568
+2569
+2570
+2571
+2572
+2573
+2574
+2575
+2576
+2577
+2578
+2579
+2580
+2581
+2582
+2583
+2584
+2585
+2586
+
#![allow(unused)]
+#![allow(non_upper_case_globals)]
+
+#[link(name = "ScreenCaptureKit", kind = "framework")]
+#[link(name = "CoreGraphics", kind = "framework")]
+#[link(name = "CoreMedia", kind = "framework")]
+#[link(name = "CoreVideo", kind = "framework")]
+#[link(name = "IOSurface", kind = "framework")]
+#[link(name = "System", kind = "dylib")]
+#[link(name = "Foundation", kind = "framework")]
+#[link(name = "AppKit", kind = "framework")]
+#[link(name = "ApplicationServices", kind = "framework")]
+#[link(name = "AVFoundation", kind = "framework")]
+extern "C" {}
+
+use std::{cell::RefCell, ffi::CString, ops::{Add, Mul, Sub}, ptr::{null, null_mut}, sync::Arc, time::{Duration, Instant}};
+
+use block::{Block, ConcreteBlock, RcBlock};
+use libc::{c_void, strlen};
+use objc::{class, declare::MethodImplementation, msg_send, runtime::{objc_copyProtocolList, objc_getProtocol, Class, Object, Protocol, Sel, BOOL}, sel, sel_impl, Encode, Encoding, Message};
+use objc2::runtime::Bool;
+use mach2::mach_time::{mach_timebase_info, mach_timebase_info_data_t};
+
+use crate::{prelude::{AudioSampleRate, StreamCreateError, StreamError, StreamEvent, StreamStopError}};
+
+use lazy_static::lazy_static;
+use parking_lot::Mutex;
+
+use super::ImplPixelFormat;
+
+type CFTypeRef = *const c_void;
+type CFStringRef = CFTypeRef;
+type CMSampleBufferRef = CFTypeRef;
+type CFAllocatorRef = CFTypeRef;
+type CFDictionaryRef = CFTypeRef;
+type CMFormatDescriptionRef = CFTypeRef;
+type CMBlockBufferRef = CFTypeRef;
+type CFArrayRef = CFTypeRef;
+type OSStatus = i32;
+type CGDisplayStreamRef = CFTypeRef;
+type CGDisplayStreamUpdateRef = CFTypeRef;
+pub(crate) type IOSurfaceRef = CFTypeRef;
+type CGDictionaryRef = CFTypeRef;
+type CFBooleanRef = CFTypeRef;
+type CFNumberRef = CFTypeRef;
+type CVPixelBufferRef = CFTypeRef;
+
+#[allow(unused)]
+fn debug_objc_class(name: &str) {
+    let class_name_cstring = CString::new(name).unwrap();
+    let class = unsafe { &*objc::runtime::objc_getClass(class_name_cstring.as_ptr()) };
+    println!("instance methods: ");
+    for method in class.instance_methods().iter() {
+        print!("METHOD {}::{}(", class.name(), method.name().name());
+        for i in 0 .. method.arguments_count() {
+            if i + 1 == method.arguments_count() {
+                println!("{}) -> {}", method.argument_type(i).unwrap().as_str(), method.return_type().as_str());
+            } else {
+                print!("{}, ", method.argument_type(i).unwrap().as_str());
+            }
+        }
+    }
+    println!("instance variables: ");
+    for ivar in class.instance_variables().iter() {
+        println!("IVAR {}::{}: {}", class.name(), ivar.name(), ivar.type_encoding().as_str());
+    }
+    let metaclass = class.metaclass();
+    let metaclass_ptr = metaclass as *const _;
+    println!("metaclass ptr: {:?}", metaclass_ptr);
+    println!("class methods: ");
+    for method in metaclass.instance_methods().iter() {
+        print!("CLASS METHOD {}::{}(", class.name(), method.name().name());
+        for i in 0 .. method.arguments_count() {
+            if i + 1 == method.arguments_count() {
+                println!("{}) -> {}", method.argument_type(i).unwrap().as_str(), method.return_type().as_str());
+            } else {
+                print!("{}, ", method.argument_type(i).unwrap().as_str());
+            }
+        }
+    }
+
+    println!("class ivars: ");
+    for ivar in metaclass.instance_variables().iter() {
+        println!("CLASS IVAR {}::{}: {}", class.name(), ivar.name(), ivar.type_encoding().as_str());
+    }
+
+    println!("protocols: ");
+    for protocol in class.adopted_protocols().iter() {
+        println!("PROTOCOL {}", protocol.name());
+    }
+    println!("end");
+}
+
+pub(crate) fn debug_objc_object(obj: *mut Object) {
+    if (obj.is_null()) {
+        println!("debug_objc_object: nil");
+        return;
+    } else {
+        println!("debug_objc_object: {:?}", obj);
+    }
+    unsafe {
+        let class_ptr = objc::runtime::object_getClass(obj);
+        if class_ptr.is_null() {
+            println!(" * class: nil");
+            return;
+        }
+        let class = &*class_ptr;
+        println!(" * class: {}", class.name());
+        debug_objc_class(class.name());
+    }
+}
+
+extern "C" {
+
+    static kCFAllocatorNull: CFTypeRef;
+    static kCFAllocatorDefault: CFTypeRef;
+
+    fn CFRetain(x: CFTypeRef) -> CFTypeRef;
+    fn CFRelease(x: CFTypeRef);
+
+    pub(crate) static kCFBooleanTrue: CFBooleanRef;
+    pub(crate) static kCFBooleanFalse: CFBooleanRef;
+
+    //CFNumberRef CFNumberCreate(CFAllocatorRef allocator, CFNumberType theType, const void *valuePtr);
+    fn CFNumberCreate(allocator: CFAllocatorRef, the_type: isize, value_ptr: *const c_void) -> CFNumberRef;
+
+    fn CGColorGetConstantColor(color_name: CFStringRef) -> CGColorRef;
+
+    pub(crate) fn CGRectMakeWithDictionaryRepresentation(dictionary: CGDictionaryRef, rect: *mut CGRect) -> bool;
+
+    static kCGColorBlack: CFStringRef;
+    static kCGColorWhite: CFStringRef;
+    static kCGColorClear: CFStringRef;
+
+    fn CMTimeMake(value: i64, timescale: i32) -> CMTime;
+    fn CMTimeMakeWithEpoch(value: i64, timescale: i32, epoch: i64) -> CMTime;
+    fn CMTimeMakeWithSeconds(seconds: f64, preferred_timescale: i32) -> CMTime;
+    fn CMTimeGetSeconds(time: CMTime) -> f64;
+
+    fn CMTimeAdd(lhs: CMTime, rhs: CMTime) -> CMTime;
+    fn CMTimeSubtract(lhs: CMTime, rhs: CMTime) -> CMTime;
+    fn CMTimeMultiply(lhs: CMTime, rhs: i32) -> CMTime;
+    fn CMTimeMultiplyByFloat64(time: CMTime, multiplier: f64) -> CMTime;
+    fn CMTimeMultiplyByRatio(time: CMTime, multiplier: i32, divisor: i32) -> CMTime;
+    fn CMTimeConvertScale(time: CMTime, new_timescale: i32, rounding_method: u32) -> CMTime;
+    fn CMTimeCompare(time1: CMTime, time2: CMTime) -> i32;
+
+    static kCMTimeInvalid: CMTime;
+    static kCMTimeIndefinite: CMTime;
+    static kCMTimePositiveInfinity: CMTime;
+    static kCMTimeNegativeInfinity: CMTime;
+    static kCMTimeZero: CMTime;
+
+    fn CMSampleBufferCreateCopy(allocator: CFAllocatorRef, original: CMSampleBufferRef, new: *mut CMSampleBufferRef) -> OSStatus;
+    fn CMSampleBufferIsValid(sbuf: CMSampleBufferRef) -> Bool;
+    fn CMSampleBufferGetNumSamples(sbuf: CMSampleBufferRef) -> isize;
+    fn CMSampleBufferGetPresentationTimeStamp(sbuf: CMSampleBufferRef) -> CMTime;
+    fn CMSampleBufferGetDuration(sbuf: CMSampleBufferRef) -> CMTime;
+    fn CMSampleBufferGetFormatDescription(sbuf: CMSampleBufferRef) -> CMFormatDescriptionRef;
+    fn CMSampleBufferGetSampleAttachmentsArray(sbuf: CMSampleBufferRef, create_if_necessary: Bool) -> CFArrayRef;
+
+    fn CMFormatDescriptionGetMediaType(fdesc: CMFormatDescriptionRef) -> OSType;
+    fn CMAudioFormatDescriptionGetStreamBasicDescription(afdesc: CMFormatDescriptionRef) -> *const AudioStreamBasicDescription;
+    fn CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sbuf: CMSampleBufferRef, buffer_list_size_needed_out: *mut usize, buffer_list_out: *mut AudioBufferList, buffer_list_size: usize, block_buffer_structure_allocator: CFAllocatorRef, block_buffer_block_allocator: CFAllocatorRef, flags: u32, block_buffer_out: *mut CMBlockBufferRef) -> OSStatus;
+    fn CMSampleBufferGetImageBuffer(sbuffer: CMSampleBufferRef) -> CVPixelBufferRef;
+    fn CVPixelBufferGetIOSurface(pixel_buffer: CVPixelBufferRef) -> IOSurfaceRef;
+    fn CVPixelBufferGetWidth(pixel_buffer: CVPixelBufferRef) -> usize;
+    fn CVPixelBufferGetHeight(pixel_buffer: CVPixelBufferRef) -> usize;
+    fn CVBufferRetain(buffer: CVPixelBufferRef) -> CVPixelBufferRef;
+    fn CVBufferRelease(buffer: CVPixelBufferRef) -> CVPixelBufferRef;
+
+    fn CFArrayGetCount(array: CFArrayRef) -> i32;
+    fn CFArrayGetValueAtIndex(array: CFArrayRef, index: i32) -> CFTypeRef;
+
+    fn CFStringCreateWithBytes(allocator: CFTypeRef, bytes: *const u8, byte_count: isize, encoding: u32, contains_byte_order_marker: bool) -> CFStringRef;
+
+    fn CFDictionaryGetValue(dict: CFDictionaryRef, value: CFTypeRef) -> CFTypeRef;
+
+    fn CGPreflightScreenCaptureAccess() -> bool;
+    fn CGRequestScreenCaptureAccess() -> bool;
+
+    //CGDisplayStreamRef CGDisplayStreamCreateWithDispatchQueue(CGDirectDisplayID display, size_t outputWidth, size_t outputHeight, int32_t pixelFormat, CFDictionaryRef properties, dispatch_queue_t queue, CGDisplayStreamFrameAvailableHandler handler);
+    fn CGDisplayStreamCreateWithDispatchQueue(display_id: u32, output_width: usize, output_height: usize, pixel_format: i32, properties: CFDictionaryRef, dispatch_queue: *mut Object, handler: *const c_void) -> CGDisplayStreamRef;
+    fn CGDisplayStreamStart(stream: CGDisplayStreamRef) -> i32;
+    fn CGDisplayStreamStop(stream: CGDisplayStreamRef) -> i32;
+
+    fn CGMainDisplayID() -> u32;
+    
+    fn CGDisplayScreenSize(display: u32) -> CGSize;
+
+    fn CGRectCreateDictionaryRepresentation(rect: CGRect) -> CFDictionaryRef;
+
+    pub(crate) fn IOSurfaceIncrementUseCount(r: IOSurfaceRef);
+    pub(crate) fn IOSurfaceDecrementUseCount(r: IOSurfaceRef);
+
+    fn IOSurfaceGetPixelFormat(surface: IOSurfaceRef) -> OSType;
+    fn IOSurfaceGetPlaneCount(surface: IOSurfaceRef) -> usize;
+
+    fn IOSurfaceGetWidth(surface: IOSurfaceRef) -> usize;
+    fn IOSurfaceGetHeight(surface: IOSurfaceRef) -> usize;
+
+    fn IOSurfaceLock(surface: IOSurfaceRef, options: u32, seed: *mut u32) -> i32;
+    fn IOSurfaceUnlock(surface: IOSurfaceRef, options: u32, seed: *mut u32) -> i32;
+
+    fn IOSurfaceGetBaseAddressOfPlane(surface: IOSurfaceRef, plane: usize) -> *mut c_void;
+    fn IOSurfaceGetBaseAddress(surface: IOSurfaceRef) -> *mut c_void;
+
+    fn IOSurfaceGetBytesPerRow(surface: IOSurfaceRef) -> usize;
+    fn IOSurfaceGetBytesPerRowOfPlane(surface: IOSurfaceRef, plane: usize) -> usize;
+
+    fn IOSurfaceGetHeightOfPlane(surface: IOSurfaceRef, plane: usize) -> usize;
+    fn IOSurfaceGetWidthOfPlane(surface: IOSurfaceRef, plane: usize) -> usize;
+    
+    fn IOSurfaceGetElementWidthOfPlane(surface: IOSurfaceRef, plane: usize) -> usize;
+    fn IOSurfaceGetBytesPerElementOfPlane(surface: IOSurfaceRef, plane: usize) -> usize;
+    fn IOSurfaceGetNumberOfComponentsOfPlane(surface: IOSurfaceRef, plane: usize) -> usize;
+
+    static mut _dispatch_queue_attr_concurrent: c_void;
+
+    fn dispatch_queue_create(label: *const std::ffi::c_char, attr: DispatchQueueAttr) -> DispatchQueue;
+    fn dispatch_retain(object: *mut Object);
+    fn dispatch_release(object: *mut Object);
+
+    pub(crate) static SCStreamFrameInfoStatus       : CFStringRef;
+    pub(crate) static SCStreamFrameInfoDisplayTime  : CFStringRef;
+    pub(crate) static SCStreamFrameInfoScaleFactor  : CFStringRef;
+    pub(crate) static SCStreamFrameInfoContentScale : CFStringRef;
+    pub(crate) static SCStreamFrameInfoContentRect  : CFStringRef;
+    pub(crate) static SCStreamFrameInfoBoundingRect : CFStringRef;
+    pub(crate) static SCStreamFrameInfoScreenRect   : CFStringRef;
+    pub(crate) static SCStreamFrameInfoDirtyRects   : CFStringRef;
+
+    pub(crate) static kCGDisplayStreamSourceRect          : CFStringRef;
+    pub(crate) static kCGDisplayStreamDestinationRect     : CFStringRef;
+    pub(crate) static kCGDisplayStreamPreserveAspectRatio : CFStringRef;
+    pub(crate) static kCGDisplayStreamColorSpace          : CFStringRef;
+    pub(crate) static kCGDisplayStreamMinimumFrameTime    : CFStringRef;
+    pub(crate) static kCGDisplayStreamShowCursor          : CFStringRef;
+    pub(crate) static kCGDisplayStreamQueueDepth          : CFStringRef;
+    pub(crate) static kCGDisplayStreamYCbCrMatrix         : CFStringRef;
+
+    static kCGDisplayStreamYCbCrMatrix_ITU_R_709_2     : CFStringRef;
+    static kCGDisplayStreamYCbCrMatrix_ITU_R_601_4     : CFStringRef;
+    static kCGDisplayStreamYCbCrMatrix_SMPTE_240M_1995 : CFStringRef;
+
+    static NSDeviceSize: CFStringRef;
+}
+
+const SCSTREAM_ERROR_CODE_USER_STOPPED: isize = -3817;
+
+pub const kAudioFormatFlagIsFloat          : u32 = 1 << 0;
+pub const kAudioFormatFlagIsBigEndian     : u32 = 1 << 1;
+pub const kAudioFormatFlagIsPacked         : u32 = 1 << 3;
+pub const kAudioFormatFlagIsNonInterleaved : u32 = 1 << 5;
+#[cfg(target_endian = "big")]
+pub const kAudioFormatNativeEndian         : u32 = kAudioFormatFlagIsBigEndian;
+#[cfg(target_endian = "little")]
+pub const kAudioFormatNativeEndian         : u32 = 0;
+
+pub const kAudioFormatFlagsCanonical       : u32 = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatNativeEndian;
+
+pub const kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment: u32 = 1 << 0;
+
+pub const kCMSampleBufferError_ArrayTooSmall: i32 = -12737;
+
+pub const kCFStringEncodingUTF8: u32 = 0x08000100;
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug)]
+pub(crate) struct CGColorRef(CFTypeRef);
+
+unsafe impl Encode for CGColorRef {
+    fn encode() -> Encoding {
+        unsafe { Encoding::from_str("^{CGColor=}") }
+    }
+}
+
+#[repr(C)]
+pub(crate) struct NSString(pub(crate) *mut Object);
+
+impl NSString {
+    pub(crate) fn new(s: &str) -> Self {
+        unsafe {
+            let bytes = s.as_bytes();
+            let instance = CFStringCreateWithBytes(std::ptr::null(), bytes.as_ptr(), bytes.len() as isize, kCFStringEncodingUTF8, false);
+            NSString(instance as *mut Object)
+        }
+    }
+
+    pub(crate) fn from_ref_unretained(r: CFStringRef) -> Self {
+        unsafe { CFRetain(r); }
+        Self(r as *mut Object)
+    }
+
+    pub(crate) fn from_ref_retained(r: CFStringRef) -> Self {
+        Self(r as *mut Object)
+    }
+
+    pub(crate) fn from_id_unretained(id: *mut Object) -> Self {
+        unsafe {
+            let _: () = msg_send![id, retain];
+            Self(id)
+        }
+    }
+
+    pub(crate) fn as_string(&self) -> String {
+        unsafe {
+            let c_str: *const i8 = msg_send![self.0, UTF8String];
+            let len = strlen(c_str);
+            let bytes = std::slice::from_raw_parts(c_str as *const u8, len);
+            String::from_utf8_lossy(bytes).into_owned()
+        }
+    }
+}
+
+unsafe impl Encode for NSString {
+    fn encode() -> Encoding {
+        unsafe { Encoding::from_str("^@\"NSString\"") }
+    }
+}
+
+#[repr(C)]
+pub(crate) struct NSError(*mut Object);
+unsafe impl Send for NSError {}
+
+impl NSError {
+    pub(crate) fn from_id_unretained(id: *mut Object) -> Self {
+        unsafe { let _: () = msg_send![id, retain]; }
+        Self(id)
+    }
+
+    pub(crate) fn from_id_retained(id: *mut Object) -> Self {
+        Self(id)
+    }
+
+    pub(crate) fn code(&self) -> isize {
+        unsafe { msg_send![self.0, code] }
+    }
+
+    pub(crate) fn domain(&self) -> String {
+        unsafe {
+            let domain_cfstringref: CFStringRef = msg_send![self.0, domain];
+            NSString::from_ref_retained(domain_cfstringref).as_string()
+        }
+    }
+
+    pub fn description(&self) -> String {
+        unsafe { NSString::from_id_unretained(msg_send![self.0, localizedDescription]).as_string() }
+    }
+
+    pub fn reason(&self) -> String {
+        unsafe { NSString::from_id_unretained(msg_send![self.0, localizedFailureReason]).as_string() }
+    }
+
+    //pub(crate) fn user_info(&self) -> 
+}
+
+unsafe impl Encode for NSError {
+    fn encode() -> Encoding {
+        unsafe {
+            Encoding::from_str("@\"NSError\"")
+        }
+    }
+}
+
+impl Drop for NSError {
+    fn drop(&mut self) {
+        unsafe { let _: () = msg_send![self.0, release]; };
+    }
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub(crate) struct NSArrayRef(*mut Object);
+
+impl NSArrayRef {
+    pub(crate) fn is_null(&self) -> bool {
+        self.0.is_null()
+    }
+}
+
+unsafe impl Encode for NSArrayRef {
+    fn encode() -> objc::Encoding {
+        unsafe { Encoding::from_str("@\"NSArray\"") }
+    }
+}
+
+#[repr(C)]
+pub(crate) struct NSArray(*mut Object);
+
+impl NSArray {
+    pub(crate) fn new() -> Self {
+        unsafe {
+            let id: *mut Object = msg_send![class!(NSArray), new];
+            Self::from_id_retained(id)
+        }
+    }
+
+    pub(crate) fn new_mutable() -> Self {
+        unsafe {
+            let id: *mut Object = msg_send![class!(NSMutableArray), alloc];
+            let id: *mut Object = msg_send![id, init];
+            Self::from_id_retained(id)
+        }
+    }
+
+    pub(crate) fn from_ref(r: NSArrayRef) -> Self {
+        Self::from_id_unretained(r.0)
+    }
+
+    pub(crate) fn from_id_unretained(id: *mut Object) -> Self {
+        unsafe { let _: () = msg_send![id, retain]; }
+        Self(id)
+    }
+
+    pub(crate) fn from_id_retained(id: *mut Object) -> Self {
+        Self(id)
+    }
+
+    pub(crate) fn count(&self) -> usize {
+        unsafe { msg_send![self.0, count] }
+    }
+
+    pub(crate) fn add_object<T: 'static>(&mut self, object: T) {
+        unsafe {
+            let _: () = msg_send![self.0, addObject: object];
+        }
+    }
+
+    pub(crate) fn obj_at_index<T: 'static>(&self, i: usize) -> T {
+        unsafe { msg_send![self.0, objectAtIndex: i] }
+    }
+}
+
+impl Drop for NSArray {
+    fn drop(&mut self) {
+        unsafe { let _: () = msg_send![self.0, release]; }
+    }
+}
+
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub(crate) struct NSDictionaryEncoded(*mut Object);
+
+impl NSDictionaryEncoded {
+    pub(crate) fn is_null(&self) -> bool {
+        self.0.is_null()
+    }
+}
+
+unsafe impl Encode for NSDictionaryEncoded {
+    fn encode() -> objc::Encoding {
+        unsafe { Encoding::from_str("@\"NSDictionary\"") }
+    }
+}
+
+
+#[repr(C)]
+pub(crate) struct NSDictionary(pub(crate) *mut Object);
+
+impl NSDictionary {
+    pub(crate) fn new() -> Self {
+        unsafe {
+            let id: *mut Object = msg_send![class!(NSDictionary), new];
+            Self::from_id_retained(id)
+        }
+    }
+
+    pub(crate) fn new_mutable() -> Self {
+        unsafe {
+            let id: *mut Object = msg_send![class!(NSMutableDictionary), new];
+            Self::from_id_retained(id)
+        }
+    }
+
+    pub(crate) fn from_ref_unretained(r: CGDictionaryRef) -> Self {
+        Self::from_id_unretained(r as *mut Object)
+    }
+
+    pub(crate) fn from_encoded(e: NSDictionaryEncoded) -> Self {
+        Self::from_id_unretained(e.0)
+    }
+
+    pub(crate) fn from_id_unretained(id: *mut Object) -> Self {
+        unsafe { let _: () = msg_send![id, retain]; }
+        Self(id)
+    }
+
+    pub(crate) fn from_id_retained(id: *mut Object) -> Self {
+        Self(id)
+    }
+
+    pub(crate) fn count(&self) -> usize {
+        unsafe { msg_send![self.0, count] }
+    }
+
+    pub(crate) fn all_keys(&self) -> NSArray {
+        unsafe {
+            let keys: *mut Object = msg_send![self.0, allKeys];
+            NSArray::from_id_retained(keys)
+        }
+    }
+
+    pub(crate) fn value_for_key(&self, key: CFStringRef) -> *mut Object {
+        unsafe {
+            msg_send![self.0, valueForKey: key]
+        }
+    }
+
+    pub(crate) fn set_object_for_key(&mut self, object: *mut Object, key: *mut Object) {
+        unsafe {
+            let _: () = msg_send![self.0, setObject: object forKey: key];
+        }
+    }
+}
+
+impl Drop for NSDictionary {
+    fn drop(&mut self) {
+        unsafe { let _: () = msg_send![self.0, release]; }
+    }
+}
+
+impl Clone for NSDictionary {
+    fn clone(&self) -> Self {
+        Self::from_id_unretained(self.0)
+    }
+}
+
+type CGFloat = f64;
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug, Default)]
+pub(crate) struct CGPoint {
+    pub(crate) x: CGFloat,
+    pub(crate) y: CGFloat,
+}
+
+impl CGPoint {
+    pub(crate) const ZERO: CGPoint = CGPoint { x: 0.0, y: 0.0 };
+    pub(crate) const INF: CGPoint = CGPoint { x: std::f64::INFINITY, y: std::f64::INFINITY };
+}
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug, Default)]
+pub(crate) struct CGSize {
+    pub(crate) x: CGFloat,
+    pub(crate) y: CGFloat,
+}
+
+impl CGSize {
+    pub(crate) const ZERO: CGSize = CGSize { x: 0.0, y: 0.0 };
+}
+
+unsafe impl Encode for CGSize {
+    fn encode() -> Encoding {
+        unsafe { Encoding::from_str("{CGSize=\"width\"d\"height\"d}") }
+    }
+}
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug, Default)]
+pub(crate) struct CGRect {
+    pub(crate) origin: CGPoint,
+    pub(crate) size: CGSize,
+}
+
+impl CGRect {
+    pub(crate) const ZERO: CGRect = CGRect {
+        origin: CGPoint::ZERO,
+        size: CGSize::ZERO
+    };
+
+    pub(crate) const NULL: CGRect = CGRect {
+        origin: CGPoint::INF,
+        size: CGSize::ZERO
+    };
+
+    pub(crate) fn contains(&self, p: CGPoint) -> bool {
+        p.x >= self.origin.x &&
+        p.y >= self.origin.y &&
+        p.x <= (self.origin.x + self.size.x) &&
+        p.y <= (self.origin.y + self.size.y)
+    }
+
+    pub(crate) fn create_dicitonary_representation(&self) -> NSDictionary {
+        unsafe {
+            NSDictionary::from_ref_unretained(CGRectCreateDictionaryRepresentation(*self))
+        }
+    }
+
+    pub(crate) fn create_from_dictionary_representation(dictionary: &NSDictionary) -> Self {
+        unsafe {
+            let mut rect = CGRect::default();
+            CGRectMakeWithDictionaryRepresentation(dictionary.0 as *const c_void, &mut rect as *mut _);
+            return rect;
+        }
+    }
+}
+
+unsafe impl Encode for CGRect {
+    fn encode() -> Encoding {
+        unsafe { Encoding::from_str("{CGRect=\"origin\"{CGPoint=\"x\"d\"y\"d}\"size\"{CGSize=\"width\"d\"height\"d}}") }
+    }
+}
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub(crate) struct CGWindowID(pub(crate) u32);
+
+impl CGWindowID {
+    pub(crate) fn raw(&self) -> u32 {
+        self.0
+    }
+}
+
+unsafe impl Send for CGWindowID {}
+
+#[repr(C)]
+pub(crate) struct SCWindow(*mut Object);
+unsafe impl Send for SCWindow {}
+
+impl SCWindow {
+    pub(crate) fn from_id_unretained(id: *mut Object) -> Self {
+        unsafe { let _: () = msg_send![id, retain]; }
+        Self(id)
+    }
+
+    pub(crate) fn from_id_retained(id: *mut Object) -> Self {
+        Self(id)
+    }
+
+    pub(crate) fn id(&self) -> CGWindowID {
+        unsafe { 
+            let id: u32 = msg_send![self.0, windowID];
+            CGWindowID(id)
+        }
+    }
+
+    pub(crate) fn title(&self) -> String {
+        unsafe {
+            let title_cfstringref: CFStringRef = msg_send![self.0, title];
+            NSString::from_ref_unretained(title_cfstringref).as_string()
+        }
+    }
+
+    pub(crate) fn frame(&self) -> CGRect {
+        unsafe {
+            *(*self.0).get_ivar("_frame")
+        }
+    }
+
+    pub(crate) fn owning_application(&self) -> SCRunningApplication {
+        unsafe {
+            let scra_id: *mut Object = msg_send![self.0, owningApplication];
+            SCRunningApplication::from_id_unretained(scra_id)
+        }
+    }
+}
+
+unsafe impl Encode for SCWindow {
+    fn encode() -> Encoding {
+        unsafe { Encoding::from_str("^\"@SCWindow\"") }
+    }
+}
+
+impl Clone for SCWindow {
+    fn clone(&self) -> Self {
+        unsafe { let _: () = msg_send![self.0, retain]; }
+        SCWindow(self.0)
+    }
+}
+
+impl Drop for SCWindow {
+    fn drop(&mut self) {
+        unsafe { let _: () = msg_send![self.0, release]; }
+    }
+}
+
+#[repr(C)]
+pub(crate) struct SCDisplay(*mut Object);
+unsafe impl Send for SCDisplay {}
+
+impl SCDisplay {
+    pub(crate) fn from_id_unretained(id: *mut Object) -> Self {
+        unsafe { let _: () = msg_send![id, retain]; }
+        Self(id)
+    }
+
+    pub(crate) fn from_id_retained(id: *mut Object) -> Self {
+        Self(id)
+    }
+
+    pub(crate) fn frame(&self) -> CGRect {
+        unsafe {
+            *(*self.0).get_ivar("_frame")
+        }
+    }
+
+    pub(crate) fn raw_id(&self) -> u32 {
+        unsafe {
+            *(*self.0).get_ivar("_displayID")
+        }
+    }
+}
+
+impl Clone for SCDisplay {
+    fn clone(&self) -> Self {
+        unsafe { let _: () = msg_send![self.0, retain]; }
+        SCDisplay(self.0)
+    }
+}
+
+impl Drop for SCDisplay {
+    fn drop(&mut self) {
+        unsafe { let _: () = msg_send![self.0, release]; }
+    }
+}
+
+#[repr(C)]
+pub(crate) struct SCShareableContent(*mut Object);
+unsafe impl Send for SCShareableContent {}
+unsafe impl Sync for SCShareableContent {}
+
+impl SCShareableContent {
+    pub(crate) fn get_shareable_content_with_completion_handler(
+        excluding_desktop_windows: bool,
+        onscreen_windows_only: bool,
+        completion_handler: impl Fn(Result<SCShareableContent, NSError>) + Send + 'static,
+    ) {
+        let completion_handler = Box::new(completion_handler);
+        let handler_block = ConcreteBlock::new(move |sc_shareable_content: *mut Object, error: *mut Object| {
+            if !error.is_null() {
+                let error = NSError::from_id_retained(error);
+                (completion_handler)(Err(error));
+            } else {
+                unsafe { let _:() = msg_send![sc_shareable_content, retain]; }
+                let sc_shareable_content = SCShareableContent(sc_shareable_content);
+                (completion_handler)(Ok(sc_shareable_content));
+            }
+        }).copy();
+        unsafe {
+            let _: () = msg_send![
+                class!(SCShareableContent),
+                getShareableContentExcludingDesktopWindows: Bool::from_raw(excluding_desktop_windows)
+                onScreenWindowsOnly: Bool::from_raw(onscreen_windows_only)
+                completionHandler: handler_block
+            ];
+        }
+    }
+
+    pub(crate) fn windows(&self) -> Vec<SCWindow> {
+        let mut windows = Vec::new();
+        unsafe {
+            let windows_nsarray_ref: NSArrayRef = *(*self.0).get_ivar("_windows");
+            if !windows_nsarray_ref.is_null() {
+                let windows_ns_array = NSArray::from_ref(windows_nsarray_ref);
+                let count = windows_ns_array.count();
+                for i in 0..count {
+                    let window_id: *mut Object = windows_ns_array.obj_at_index(i);
+                    windows.push(SCWindow::from_id_unretained(window_id));
+                }
+            }
+        }
+        windows
+    }
+
+    pub(crate) fn displays(&self) -> Vec<SCDisplay> {
+        let mut displays = Vec::new();
+        unsafe {
+            let displays_ref: NSArrayRef = *(*self.0).get_ivar("_displays");
+            if !displays_ref.is_null() {
+                let displays_ns_array = NSArray::from_ref(displays_ref);
+                let count = displays_ns_array.count();
+                for i in 0..count {
+                    let display_id: *mut Object = displays_ns_array.obj_at_index(i);
+                    displays.push(SCDisplay::from_id_unretained(display_id));
+                }
+            }
+        }
+        displays
+    }
+
+    pub(crate) fn applications(&self) -> Vec<SCRunningApplication> {
+        let mut applications = Vec::new();
+        unsafe {
+            let applicaitons_ref: NSArrayRef = *(*self.0).get_ivar("_applications");
+            if !applicaitons_ref.is_null() {
+                let applications_array = NSArray::from_ref(applicaitons_ref);
+                let count = applications_array.count();
+                for i in 0..count {
+                    let application_id: *mut Object = applications_array.obj_at_index(i);
+                    applications.push(SCRunningApplication::from_id_unretained(application_id));
+                }
+            }
+        }
+        applications
+    }
+}
+
+impl Drop for SCShareableContent {
+    fn drop(&mut self) {
+        unsafe { let _: () = msg_send![self.0, release]; }
+    }
+}
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug)]
+pub(crate) struct OSType([u8; 4]);
+
+impl OSType {
+    pub fn as_i32(&self) -> i32 {
+        unsafe { std::mem::transmute(self.0) }
+    }
+
+    pub fn as_u32(&self) -> u32 {
+        unsafe { std::mem::transmute(self.0) }
+    }
+}
+
+unsafe impl Encode for OSType {
+    fn encode() -> Encoding {
+        unsafe { Encoding::from_str("I") }
+    }
+}
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug)]
+pub(crate) enum SCStreamPixelFormat {
+    BGRA8888,
+    L10R,
+    V420,
+    F420,
+}
+
+impl SCStreamPixelFormat {
+    pub(crate) fn to_ostype(&self) -> OSType {
+        match self {
+            Self::BGRA8888 => OSType(['A' as u8, 'R' as u8, 'G' as u8, 'B' as u8]),
+            Self::L10R     => OSType(['r' as u8, '0' as u8, '1' as u8, 'l' as u8]),
+            Self::V420     => OSType(['v' as u8, '0' as u8, '2' as u8, '4' as u8]),
+            Self::F420     => OSType(['f' as u8, '0' as u8, '2' as u8, '4' as u8]),
+        }
+    }
+}
+
+
+#[derive(Copy, Clone, Debug)]
+pub(crate) enum SCStreamBackgroundColor {
+    Black,
+    White,
+    Clear,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub(crate) enum SCStreamColorMatrix {
+    ItuR709_2,
+    ItuR601_4,
+    Smpte240M1995,
+}
+
+impl SCStreamColorMatrix {
+    pub(crate) fn to_cfstringref(&self) -> CFStringRef {
+        unsafe {
+            match self {
+                Self::ItuR709_2 => kCGDisplayStreamYCbCrMatrix_ITU_R_709_2,
+                Self::ItuR601_4 => kCGDisplayStreamYCbCrMatrix_ITU_R_709_2,
+                Self::Smpte240M1995 => kCGDisplayStreamYCbCrMatrix_SMPTE_240M_1995,
+            }
+        }
+    }
+}
+
+#[repr(C)]
+pub(crate) struct SCStreamConfiguration(pub(crate) *mut Object);
+unsafe impl Send for SCStreamConfiguration {}
+unsafe impl Sync for SCStreamConfiguration {}
+
+#[test]
+fn test_sc_stream_config_properties() {
+    debug_objc_class("SCStreamConfiguration");
+}
+
+impl SCStreamConfiguration {
+    pub(crate) fn new() -> Self {
+        unsafe {
+            let instance: *mut Object = msg_send![class!(SCStreamConfiguration), alloc];
+            let instance: *mut Object = msg_send![instance, init];
+            Self(instance)
+        }
+    }
+
+    pub(crate) fn set_size(&mut self, size: CGSize) {
+        let dest_rect = CGRect {
+            size,
+            origin: CGPoint::ZERO,
+        };
+        unsafe {
+            let _: () = msg_send![self.0, setDestinationRect: dest_rect];
+        }
+    }
+
+    pub(crate) fn set_source_rect(&mut self, source_rect: CGRect) {
+        unsafe {
+            let _: () = msg_send![self.0, setSourceRect: source_rect];
+        }
+    }
+
+    pub(crate) fn set_scales_to_fit(&mut self, scale_to_fit: bool) {
+        unsafe {
+            let _: () = msg_send![self.0, setScalesToFit: scale_to_fit];
+        }
+    }
+
+    pub(crate) fn set_pixel_format(&mut self, format: SCStreamPixelFormat) {
+        unsafe {
+            let old_pf: OSType = *(*self.0).get_ivar("_pixelFormat");
+            println!("old pixel format: {:?}", old_pf);
+            (*self.0).set_ivar("_pixelFormat", format.to_ostype());
+        }
+    }
+
+    pub(crate) fn set_color_matrix(&mut self, color_matrix: SCStreamColorMatrix) {
+        unsafe {
+            let _: () = msg_send![self.0, setColorMatrix: color_matrix.to_cfstringref()];
+        }
+    }
+
+    pub(crate) fn set_background_color(&mut self, bg_color: SCStreamBackgroundColor) {
+        unsafe {
+            let bg_color_name = match bg_color {
+                SCStreamBackgroundColor::Black => kCGColorBlack,
+                SCStreamBackgroundColor::White => kCGColorWhite,
+                SCStreamBackgroundColor::Clear => kCGColorClear,
+            };
+            let bg_color = CGColorGetConstantColor(bg_color_name);
+            (*self.0).set_ivar("_backgroundColor", bg_color);
+        }
+    }
+
+    pub(crate) fn set_queue_depth(&mut self, queue_depth: isize) {
+        unsafe {
+            let _: () = msg_send![self.0, setQueueDepth: queue_depth];
+        }
+    }
+
+    pub(crate) fn set_minimum_time_interval(&mut self, interval: CMTime) {
+        unsafe {
+            (*self.0).set_ivar("_minimumFrameInterval", interval);
+        }
+    }
+
+    pub(crate) fn set_sample_rate(&mut self, sample_rate: SCStreamSampleRate) {
+        unsafe {
+            (*self.0).set_ivar("_sampleRate", sample_rate.to_isize());
+        }
+    }
+
+    pub(crate) fn set_show_cursor(&mut self, show_cursor: bool) {
+        unsafe {
+            (*self.0).set_ivar("_showsCursor", show_cursor);
+        }
+    }
+
+    pub(crate) fn set_capture_audio(&mut self, capture_audio: bool) {
+        unsafe {
+            (*self.0).set_ivar("_capturesAudio", BOOL::from(capture_audio));
+        }
+    }
+
+    pub(crate) fn set_channel_count(&mut self, channel_count: isize) {
+        unsafe {
+            (*self.0).set_ivar("_channelCount", channel_count);
+        }
+    }
+
+    pub(crate) fn set_exclude_current_process_audio(&mut self, exclude_current_process_audio: bool) {
+        unsafe {
+            (*self.0).set_ivar("_excludesCurrentProcessAudio", exclude_current_process_audio);
+        }
+    }
+}
+
+impl Clone for SCStreamConfiguration {
+    fn clone(&self) -> Self {
+        unsafe { let _: () = msg_send![self.0, retain]; }
+        SCStreamConfiguration(self.0)
+    }
+}
+
+impl Drop for SCStreamConfiguration {
+    fn drop(&mut self) {
+        unsafe { let _: () = msg_send![self.0, release]; }
+    }
+}
+
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug)]
+pub(crate) struct CMTime {
+    value: i64,
+    scale: i32,
+    epoch: i64,
+    flags: u32,
+}
+
+pub(crate) const K_CMTIME_FLAGS_VALID                   : u32 = 1 << 0;
+pub(crate) const K_CMTIME_FLAGS_HAS_BEEN_ROUNDED        : u32 = 1 << 0;
+pub(crate) const K_CMTIME_FLAGS_POSITIVE_INFINITY       : u32 = 1 << 0;
+pub(crate) const K_CMTIME_FLAGS_NEGATIVE_INFINITY       : u32 = 1 << 0;
+pub(crate) const K_CMTIME_FLAGS_INDEFINITE              : u32 = 1 << 0;
+pub(crate) const K_CMTIME_FLAGS_IMPLIED_VALUE_FLAG_MASK : u32 = 
+    K_CMTIME_FLAGS_VALID                    |
+    K_CMTIME_FLAGS_HAS_BEEN_ROUNDED         |
+    K_CMTIME_FLAGS_POSITIVE_INFINITY        |
+    K_CMTIME_FLAGS_NEGATIVE_INFINITY        |
+    K_CMTIME_FLAGS_INDEFINITE
+    ;
+
+const K_CMTIME_ROUNDING_METHOD_ROUND_HALF_AWAY_FROM_ZERO: u32 = 1;
+const K_CMTIME_ROUNDING_METHOD_ROUND_TOWARD_ZERO: u32 = 2;
+const K_CMTIME_ROUNDING_METHOD_ROUND_AWAY_FROM_ZERO: u32 = 3;
+const K_CMTIME_ROUNDING_METHOD_QUICKTIME: u32 = 4;
+const K_CMTIME_ROUNDING_METHOD_TOWARD_POSITIVE_INFINITY: u32 = 5;
+const K_CMTIME_ROUNDING_METHOD_TOWARD_NEGATIVE_INFINITY: u32 = 6;
+
+#[derive(Copy, Clone, Debug)]
+pub(crate) enum CMTimeRoundingMethod {
+    HalfAwayFromZero,
+    TowardZero,
+    AwayFromZero,
+    QuickTime,
+    TowardPositiveInfinity,
+    TowardNegativeInfinity,
+}
+
+impl CMTimeRoundingMethod {
+    pub(crate) fn to_u32(&self) -> u32 {
+        match self {
+            Self::HalfAwayFromZero       => K_CMTIME_ROUNDING_METHOD_ROUND_HALF_AWAY_FROM_ZERO,
+            Self::TowardZero             => K_CMTIME_ROUNDING_METHOD_ROUND_TOWARD_ZERO,
+            Self::AwayFromZero           => K_CMTIME_ROUNDING_METHOD_ROUND_AWAY_FROM_ZERO,
+            Self::QuickTime              => K_CMTIME_ROUNDING_METHOD_QUICKTIME,
+            Self::TowardPositiveInfinity => K_CMTIME_ROUNDING_METHOD_TOWARD_POSITIVE_INFINITY,
+            Self::TowardNegativeInfinity => K_CMTIME_ROUNDING_METHOD_TOWARD_NEGATIVE_INFINITY,
+        }
+    }
+}
+
+impl Default for CMTimeRoundingMethod {
+    fn default() -> Self {
+        Self::HalfAwayFromZero
+    }
+}
+
+impl Add for CMTime {
+    type Output = CMTime;
+
+    fn add(self, rhs: Self) -> Self {
+        unsafe { CMTimeAdd(self, rhs) }
+    }
+}
+
+impl Sub for CMTime {
+    type Output = CMTime;
+
+    fn sub(self, rhs: Self) -> Self {
+        unsafe { CMTimeSubtract(self, rhs) }
+    }
+}
+
+impl Mul<i32> for CMTime {
+    type Output = Self;
+
+    fn mul(self, rhs: i32) -> Self {
+        unsafe { CMTimeMultiply(self, rhs) }
+    }
+}
+
+impl Mul<f64> for CMTime {
+    type Output = Self;
+
+    fn mul(self, rhs: f64) -> Self {
+        unsafe { CMTimeMultiplyByFloat64(self, rhs) }
+    }
+}
+
+impl PartialEq for CMTime {
+    fn eq(&self, other: &Self) -> bool {
+        unsafe {
+            CMTimeCompare(*self, *other) == 0
+        }
+    }
+}
+
+impl PartialOrd for CMTime {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        unsafe {
+            let self_op_other = CMTimeCompare(*self, *other);
+            match self_op_other {
+                -1 => Some(std::cmp::Ordering::Less),
+                0 => Some(std::cmp::Ordering::Equal),
+                1 => Some(std::cmp::Ordering::Greater),
+                _ => None
+            }
+        }
+    }
+}
+
+unsafe impl Encode for CMTime {
+    fn encode() -> Encoding {
+        unsafe { Encoding::from_str("{?=\"value\"q\"timescale\"i\"flags\"I\"epoch\"q}") }
+    }
+}
+
+impl CMTime {
+    pub(crate) fn new_with_seconds(seconds: f64, timescale: i32) -> Self {
+        unsafe { CMTimeMakeWithSeconds(seconds, timescale) }
+    }
+
+    pub(crate) const fn is_valid(&self) -> bool {
+        self.flags & K_CMTIME_FLAGS_VALID != 0
+    }
+
+    pub(crate) const fn is_invalid(&self) -> bool {
+        ! self.is_valid()
+    }
+
+    pub(crate) const fn is_indefinite(&self) -> bool {
+        self.is_valid() &&
+        (self.flags & K_CMTIME_FLAGS_INDEFINITE != 0)
+    }
+
+    pub(crate) const fn is_positive_infinity(&self) -> bool {
+        self.is_valid() &&
+        (self.flags & K_CMTIME_FLAGS_POSITIVE_INFINITY != 0)
+    }
+
+    pub(crate) const fn is_negative_infinity(&self) -> bool {
+        self.is_valid() &&
+        (self.flags & K_CMTIME_FLAGS_NEGATIVE_INFINITY != 0)
+    }
+
+    pub(crate) const fn is_numeric(&self) -> bool {
+        self.is_valid() &&
+        ! self.is_indefinite() &&
+        ! self.is_positive_infinity() &&
+        ! self.is_negative_infinity()
+    }
+
+    pub(crate) const fn has_been_rounded(&self) -> bool {
+        self.flags & K_CMTIME_FLAGS_HAS_BEEN_ROUNDED != 0
+    }
+
+    pub(crate) fn convert_timescale(&self, new_timescale: i32, rounding_method: CMTimeRoundingMethod) -> Self {
+        unsafe { CMTimeConvertScale(*self, new_timescale, rounding_method.to_u32()) }
+    }
+
+    pub(crate) fn multiply_by_ratio(&self, multiplier: i32, divisor: i32) -> Self {
+        unsafe { CMTimeMultiplyByRatio(*self, multiplier, divisor) }
+    }
+
+    pub(crate) fn seconds_f64(&self) -> f64 {
+        unsafe { CMTimeGetSeconds(*self) }
+    }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub(crate) enum SCStreamSampleRate {
+    R8000,
+    R16000,
+    R24000,
+    R48000,
+}
+
+impl SCStreamSampleRate {
+    pub(crate) fn to_isize(&self) -> isize {
+        match self {
+            Self::R8000  => 8000,
+            Self::R16000 => 16000,
+            Self::R24000 => 24000,
+            Self::R48000 => 48000,
+        }
+    }
+}
+
+#[repr(C)]
+pub(crate) struct SCContentFilter(pub(crate) *mut Object);
+
+unsafe impl Send for SCContentFilter {}
+unsafe impl Sync for SCContentFilter {}
+
+impl SCContentFilter {
+    pub(crate) fn new_with_desktop_independent_window(window: &SCWindow) -> Self {
+        unsafe {
+            let id: *mut Object = msg_send![class!(SCContentFilter), alloc];
+            let _: *mut Object = msg_send![id, initWithDesktopIndependentWindow: window.clone()];
+            Self(id)
+        }
+    }
+
+    pub(crate) fn new_with_display_excluding_apps_excepting_windows(display: SCDisplay, excluded_applications: NSArray, excepting_windows: NSArray) -> Self {
+        unsafe {
+            let id: *mut Object = msg_send![class!(SCContentFilter), alloc];
+            let id: *mut Object = msg_send![id, initWithDisplay: display.0 excludingApplications: excluded_applications.0 exceptingWindows: excepting_windows.0];
+            Self(id)
+        }
+    }
+}
+
+unsafe impl Encode for SCContentFilter {
+    fn encode() -> Encoding {
+        unsafe { Encoding::from_str("@\"SCContentFilter\"") }
+    }
+}
+
+impl Clone for SCContentFilter {
+    fn clone(&self) -> Self {
+        unsafe { let _: () = msg_send![self.0, retain]; }
+        SCContentFilter(self.0)
+    }
+}
+
+impl Drop for SCContentFilter {
+    fn drop(&mut self) {
+        unsafe { let _: () = msg_send![self.0, release]; }
+    }
+}
+
+pub(crate) enum SCStreamCallbackError {
+    SampleBufferCopyFailed,
+    StreamStopped,
+    Other(NSError)
+}
+
+#[repr(C)]
+struct SCStreamCallbackContainer {
+    callback: Box<dyn FnMut(Result<(CMSampleBuffer, SCStreamOutputType), SCStreamCallbackError>) + Send + 'static>
+}
+
+impl SCStreamCallbackContainer {
+    pub fn new(callback: impl FnMut(Result<(CMSampleBuffer, SCStreamOutputType), SCStreamCallbackError>) + Send + 'static) -> Self {
+        Self {
+            callback: Box::new(callback)
+        }
+    }
+
+    pub fn call_output(&mut self, sample_buffer: CMSampleBuffer, output_type: SCStreamOutputType) {
+        (self.callback)(Ok((sample_buffer, output_type)));
+    }
+
+    pub fn call_error(&mut self, error: SCStreamCallbackError) {
+        (self.callback)(Err(error));
+    }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum SCStreamOutputType {
+    Screen,
+    Audio,
+}
+
+impl SCStreamOutputType {
+    fn to_encoded(&self) -> SCStreamOutputTypeEncoded {
+        SCStreamOutputTypeEncoded(match *self {
+            Self::Screen => 0,
+            Self::Audio => 1,
+        })
+    }
+
+    fn from_encoded(x: usize) -> Option<Self> {
+        match x {
+            0 => Some(Self::Screen),
+            1 => Some(Self::Audio),
+            _ => None
+        }
+    }
+}
+
+#[repr(C)]
+struct SCStreamOutputTypeEncoded(usize);
+
+unsafe impl Encode for SCStreamOutputTypeEncoded {
+    fn encode() -> Encoding {
+        unsafe { Encoding::from_str("q") }
+    }
+}
+
+#[repr(C)]
+struct SCStreamEncoded(*mut Object);
+
+unsafe impl Message for SCStreamEncoded {}
+
+unsafe impl Encode for SCStreamEncoded {
+    fn encode() -> Encoding {
+        unsafe {
+            Encoding::from_str("@\"SCStream\"")
+        }
+    }
+}
+
+extern fn sc_stream_output_did_output_sample_buffer_of_type(this: &Object, _sel: Sel, stream: SCStream, buffer: CMSampleBufferRef, output_type: SCStreamOutputTypeEncoded) {
+    unsafe {
+        let callback_container: &mut SCStreamCallbackContainer = &mut *(*this.get_ivar::<*mut c_void>("callback_container_ptr") as *mut SCStreamCallbackContainer);
+        if let Ok(sample_buffer) = CMSampleBuffer::copy_from_ref(buffer) {
+            let output_type = SCStreamOutputType::from_encoded(output_type.0).unwrap();
+            callback_container.call_output(sample_buffer, output_type);
+        } else {
+            callback_container.call_error(SCStreamCallbackError::SampleBufferCopyFailed);
+        }
+        std::mem::forget(stream);
+    }
+}
+
+extern fn sc_stream_handler_did_stop_with_error(this: &Object, _sel: Sel, stream: SCStream, error: NSError) -> () {
+    unsafe {
+        let callback_container: &mut SCStreamCallbackContainer = &mut *(*this.get_ivar::<*mut c_void>("callback_container_ptr") as *mut SCStreamCallbackContainer);
+        callback_container.call_error(SCStreamCallbackError::StreamStopped);
+        std::mem::forget(error);
+        std::mem::forget(stream);
+    }
+}
+
+extern fn sc_stream_handler_dealloc(this: &mut Object, _sel: Sel) {
+    unsafe {
+        let callback_container: Box<SCStreamCallbackContainer> = Box::from_raw(*this.get_ivar::<*mut c_void>("callback_container_ptr") as *mut SCStreamCallbackContainer);
+        drop(callback_container);
+    }
+}
+
+#[repr(C)]
+pub(crate) struct SCStreamHandler(*mut Object);
+
+unsafe impl Message for SCStreamHandler {}
+
+unsafe impl Encode for SCStreamHandler {
+    fn encode() -> Encoding {
+        unsafe { Encoding::from_str("@\"<SCStreamOutput, SCStreamDelegate>\"") }
+    }
+}
+
+impl SCStreamHandler {
+    pub fn new(callback: impl FnMut(Result<(CMSampleBuffer, SCStreamOutputType), SCStreamCallbackError>) + Send + 'static) -> Self {
+        let class = Self::get_class();
+        let callback_container_ptr = Box::leak(Box::new(SCStreamCallbackContainer::new(callback)));
+        unsafe {
+            let instance: *mut Object = msg_send![class!(SCStreamHandler), alloc];
+            let instance: *mut Object = msg_send![instance, init];
+            (*instance).set_ivar("callback_container_ptr", callback_container_ptr as *mut _ as *mut c_void);
+            Self(instance)
+        }
+    }
+
+    fn get_class() -> &'static Class {
+        unsafe {
+            let class_name = CString::new("SCStreamHandler").unwrap();
+            let class_ptr = objc::runtime::objc_getClass(class_name.as_ptr());
+            if !class_ptr.is_null() {
+                &*class_ptr
+            } else if let Some(mut class) = objc::declare::ClassDecl::new("SCStreamHandler", class!(NSObject)) {
+                class.add_method(sel!(stream:didOutputSampleBuffer:ofType:), sc_stream_output_did_output_sample_buffer_of_type as extern fn (&Object, Sel, SCStream, CMSampleBufferRef, SCStreamOutputTypeEncoded));
+                class.add_method(sel!(stream:didStopWithError:), sc_stream_handler_did_stop_with_error as extern fn(&Object, Sel, SCStream, NSError));
+                class.add_method(sel!(dealloc), sc_stream_handler_dealloc as extern fn(&mut Object, Sel));
+                
+                //let sc_stream_delegate_name = CString::new("SCStreamDelegate").unwrap();
+                //class.add_protocol(&*objc::runtime::objc_getProtocol(sc_stream_delegate_name.as_ptr()));
+                
+                //let sc_stream_output_name = CString::new("SCStreamOutput").unwrap();
+                //class.add_protocol(&*objc::runtime::objc_getProtocol(sc_stream_output_name.as_ptr()));
+
+                class.add_ivar::<*mut c_void>("callback_container_ptr");
+                
+                class.register()
+            } else {
+                panic!("Failed to register SCStreamHandler");
+            }
+        }
+    }
+}
+
+
+#[repr(C)]
+pub struct SCStream(*mut Object);
+
+unsafe impl Sync for SCStream {}
+unsafe impl Send for SCStream {}
+
+unsafe impl Encode for SCStream {
+    fn encode() -> Encoding {
+        unsafe { Encoding::from_str("@\"SCStream\"") }
+    }
+}
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug)]
+struct SCStreamDelegate(*mut Object);
+
+unsafe impl Encode for SCStreamDelegate {
+    fn encode() -> Encoding {
+        unsafe { Encoding::from_str("@\"<SCStreamDelegate>\"") }
+    }
+}
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug)]
+struct SCStreamOutput(*mut Object);
+
+unsafe impl Encode for SCStreamOutput {
+    fn encode() -> Encoding {
+        unsafe { Encoding::from_str("@\"<SCStreamOutput>\"") }
+    }
+}
+
+impl SCStream {
+    pub fn preflight_access() -> bool {
+        unsafe { CGPreflightScreenCaptureAccess() }
+    }
+
+    pub async fn request_access() -> bool {
+        async {
+            unsafe { CGRequestScreenCaptureAccess() }
+        }.await
+    }
+
+    pub fn from_id(id: *mut Object) -> Self {
+        unsafe { let _: () = msg_send![id, retain]; }
+        Self(id)
+    }
+
+    pub fn is_nil(&self) -> bool {
+        self.0.is_null()
+    }
+
+    pub fn new(filter: SCContentFilter, config: SCStreamConfiguration, handler_queue: DispatchQueue, handler: SCStreamHandler) -> Result<Self, String> {
+        unsafe {
+            let instance: *mut Object = msg_send![class!(SCStream), alloc];
+            let instance: *mut Object = msg_send![instance, initWithFilter: filter.0 configuration: config.0 delegate: SCStreamDelegate(handler.0)];
+            println!("SCStream instance: {:?}", instance);
+            let mut error: *mut Object = std::ptr::null_mut();
+            let result: bool = msg_send![instance, addStreamOutput: SCStreamOutput(handler.0) type: SCStreamOutputType::Screen.to_encoded() sampleHandlerQueue: handler_queue error: &mut error as *mut _];
+            println!("addStreamOutput result: {}", result);
+            if !error.is_null() {
+                let error = NSError::from_id_retained(error);
+                println!("error: {}, reason: {}", error.description(), error.reason());
+            }
+            Ok(SCStream(instance))
+        }
+    }
+
+    pub fn start(&mut self) {
+        unsafe {
+            let _: () = msg_send![self.0, startCaptureWithCompletionHandler: ConcreteBlock::new(Box::new(
+                |error: *mut Object| {
+                    if !error.is_null() {
+                        let error =  NSError::from_id_unretained(error);
+                        println!("startCaptureWithCompletionHandler error: {:?}, reason: {:?}", error.description(), error.reason());
+                    } else {
+                        println!("startCaptureWithCompletionHandler success!");
+                    }
+                }
+            )).copy()];
+        }
+    }
+
+    pub fn stop(&mut self) {
+    }
+}
+
+#[repr(C)]
+pub(crate) struct CMSampleBuffer(CMSampleBufferRef);
+
+impl CMSampleBuffer {
+    pub(crate) fn copy_from_ref(r: CMSampleBufferRef) -> Result<Self, ()> {
+        unsafe { 
+            let mut new_ref: CMSampleBufferRef = std::ptr::null();
+            let status = CMSampleBufferCreateCopy(kCFAllocatorDefault, r, &mut new_ref as *mut _);
+            if status != 0 {
+                Err(())
+            } else {
+                Ok(CMSampleBuffer(new_ref))
+            }
+        }
+    }
+
+    pub(crate) fn get_presentation_timestamp(&self) -> CMTime {
+        unsafe { CMSampleBufferGetPresentationTimeStamp(self.0) }
+    }
+
+    pub(crate) fn get_duration(&self) -> CMTime {
+        unsafe { CMSampleBufferGetDuration(self.0) }
+    }
+
+    pub(crate) fn get_format_description(&self) -> CMFormatDescription {
+        let format_desc_ref = unsafe { CMSampleBufferGetFormatDescription(self.0) };
+        CMFormatDescription::from_ref_unretained(format_desc_ref)
+    }
+
+    // CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer
+    pub(crate) unsafe fn get_audio_buffer_list_with_block_buffer(&self) -> Result<(AudioBufferList, CMBlockBuffer), ()> {
+        let mut audio_buffer_list = AudioBufferList::default();
+        let mut block_buffer: CMBlockBufferRef = std::ptr::null();
+        let status = CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(
+            self.0,
+            std::ptr::null_mut(),
+            &mut audio_buffer_list as *mut _,
+            std::mem::size_of::<AudioBufferList>(),
+            kCFAllocatorNull,
+            kCFAllocatorNull,
+            kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment,
+            &mut block_buffer as *mut _
+        );
+        if status != 0 {
+            println!("CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(...) failed: {}", status);
+            return Err(());
+        }
+        Ok((audio_buffer_list, CMBlockBuffer::from_ref_retained(block_buffer)))
+    }
+
+    pub(crate) fn get_sample_attachment_array(&self) -> Vec<CFDictionary> {
+        unsafe {
+            let attachment_array_ref = CMSampleBufferGetSampleAttachmentsArray(self.0, false.into());
+            if attachment_array_ref.is_null() {
+                return vec![];
+            }
+            let attachments_array = CFArray::from_ref_unretained(attachment_array_ref);
+            let mut attachments = Vec::new();
+            for i in 0..attachments_array.get_count() {
+                attachments.push(CFDictionary::from_ref_unretained(attachments_array.get_value_at_index(i)));
+            }
+            attachments
+        }
+    }
+
+    pub(crate) fn get_image_buffer(&self) -> Option<CVPixelBuffer> {
+        unsafe {
+            let buffer_ref = CMSampleBufferGetImageBuffer(self.0);
+            if buffer_ref.is_null() {
+                None
+            } else {
+                Some(CVPixelBuffer::from_ref_unretained(buffer_ref))
+            }
+        }
+    }
+
+}
+
+impl Clone for CMSampleBuffer {
+    fn clone(&self) -> Self {
+        unsafe { CFRetain(self.0); }
+        Self(self.0)
+    }
+}
+
+impl Drop for CMSampleBuffer {
+    fn drop(&mut self) {
+        unsafe { CFRelease(self.0); }
+    }
+}
+
+#[repr(C)]
+pub(crate) struct CFDictionary(CFTypeRef);
+
+impl CFDictionary {
+    pub(crate) fn from_ref_retained(r: CFDictionaryRef) -> Self {
+        Self(r)
+    }
+
+    pub(crate) fn from_ref_unretained(r: CFDictionaryRef) -> Self {
+        unsafe { CFRetain(r); }
+        Self(r)
+    }
+
+    pub(crate) fn get_value(&self, key: CFTypeRef) -> CFTypeRef {
+        unsafe { CFDictionaryGetValue(self.0, key) }
+    }
+}
+
+impl Clone for CFDictionary {
+    fn clone(&self) -> Self {
+        Self::from_ref_unretained(self.0)
+    }
+}
+
+impl Drop for CFDictionary {
+    fn drop(&mut self) {
+        unsafe { CFRelease(self.0); }
+    }
+}
+
+pub(crate) enum CMMediaType {
+    Audio,
+    Video,
+}
+
+impl CMMediaType {
+    pub(crate) fn from_ostype(ostype: OSType) -> Option<Self> {
+        match ostype.0.map(|x| x as char) {
+            ['v', 'i', 'd', 'e'] => Some(Self::Video),
+            ['s', 'o', 'u', 'n'] => Some(Self::Audio),
+            _ => None,
+        }
+    }
+}
+
+#[repr(C)]
+pub(crate) struct CMFormatDescription(CMFormatDescriptionRef);
+
+impl CMFormatDescription {
+    pub(crate) fn from_ref_retained(r: CMFormatDescriptionRef) -> Self {
+        Self(r)
+    }
+
+    pub(crate) fn from_ref_unretained(r: CMFormatDescriptionRef) -> Self {
+        unsafe { CFRetain(r); }
+        Self(r)
+    }
+
+    pub(crate) fn get_media_type(&self) -> OSType {
+        unsafe { CMFormatDescriptionGetMediaType(self.0) }
+    }
+
+    pub(crate) fn as_audio_format_description(&self) -> Option<CMAudioFormatDescription> {
+        match CMMediaType::from_ostype(self.get_media_type()) {
+            Some(CMMediaType::Audio) => {
+                Some(CMAudioFormatDescription::from_ref_unretained(self.0))
+            },
+            _ => None
+        }
+    }
+}
+
+impl Drop for CMFormatDescription {
+    fn drop(&mut self) {
+        unsafe { CFRelease(self.0); }
+    }
+}
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug)]
+pub(crate) struct AudioStreamBasicDescription {
+    pub sample_rate: f64,
+    pub format_id: u32,
+    pub format_flags: u32,
+    pub bytes_per_packet: u32,
+    pub frames_per_packet: u32,
+    pub bytes_per_frame: u32,
+    pub channels_per_frame: u32,
+    pub bits_per_channel: u32,
+    pub reserved: u32,
+}
+
+#[repr(C)]
+pub(crate) struct CMAudioFormatDescription(CMFormatDescriptionRef);
+
+impl CMAudioFormatDescription {
+    pub(crate) fn from_ref_retained(r: CMFormatDescriptionRef) -> Self {
+        Self(r)
+    }
+
+    pub(crate) fn from_ref_unretained(r: CMFormatDescriptionRef) -> Self {
+        unsafe { CFRetain(r); }
+        Self(r)
+    }
+
+    pub(crate) fn get_basic_stream_description(&self) -> &'_ AudioStreamBasicDescription {
+        unsafe { &*CMAudioFormatDescriptionGetStreamBasicDescription(self.0) as &_ }
+    }
+}
+
+impl Drop for CMAudioFormatDescription {
+    fn drop(&mut self) {
+        unsafe { CFRelease(self.0); }
+    }
+}
+
+pub(crate) struct AVAudioFormat(*mut Object);
+
+impl AVAudioFormat {
+    pub fn new_with_standard_format_sample_rate_channels(sample_rate: f64, channel_count: u32) -> Self {
+        unsafe {
+            let id: *mut Object = msg_send![class!(AVAudioFormat), alloc];
+            let _: *mut Object = msg_send![id, initStandardFormatWithSampleRate: sample_rate channels: channel_count];
+            Self(id)
+        }
+    }
+}
+
+impl Drop for AVAudioFormat {
+    fn drop(&mut self) {
+        unsafe { let _: () = msg_send![self.0, release]; }
+    }
+}
+
+#[repr(C)]
+pub(crate) struct AudioBuffer {
+    number_channels: u32,
+    data_byte_size: u32,
+    data: *mut c_void,
+}
+
+#[repr(C)]
+pub(crate) struct AudioBufferList {
+    number_buffers: u32,
+    buffers: *mut AudioBuffer,
+}
+
+impl Default for AudioBufferList {
+    fn default() -> Self {
+        Self {
+            number_buffers: 0,
+            buffers: std::ptr::null_mut()
+        }
+    }
+}
+
+#[repr(C)]
+pub(crate) struct CMBlockBuffer(CMBlockBufferRef);
+
+
+impl CMBlockBuffer {
+    pub(crate) fn from_ref_retained(r: CMBlockBufferRef) -> Self {
+        Self(r)
+    }
+
+    pub(crate) fn from_ref_unretained(r: CMBlockBufferRef) -> Self {
+        unsafe { CFRetain(r); }
+        Self(r)
+    }
+}
+
+impl Drop for CMBlockBuffer {
+    fn drop(&mut self) {
+        unsafe { CFRelease(self.0); }
+    }
+}
+
+pub(crate) struct AVAudioPCMBuffer(*mut Object);
+
+impl AVAudioPCMBuffer {
+    pub fn new_with_format_buffer_list_no_copy_deallocator(format: AVAudioFormat, buffer_list_no_copy: *const AudioBufferList) -> Result<Self, ()> {
+        unsafe {
+            let id: *mut Object = msg_send![class!(AVAudioPCMBuffer), alloc];
+            let result: *mut Object = msg_send![id, initWithPCMFormat: format bufferListNoCopy: buffer_list_no_copy];
+            if result.is_null() {
+                let _: () = msg_send![id, release];
+                Err(())
+            } else {
+                Ok(Self(id))
+            }
+        }
+    }
+
+    pub fn stride(&self) -> usize {
+        unsafe { msg_send![self.0, stride] }
+    }
+
+    pub fn frame_capacity(&self) -> usize {
+        unsafe { msg_send![self.0, frameCapacity] }
+    }
+
+    pub fn channel_count(&self) -> usize {
+        unsafe { msg_send![self.0, stride] }
+    }
+
+    pub fn f32_buffer(&self, channel: usize) -> Option<*const f32> {
+        let channel_count = self.stride();
+        if channel >= channel_count {
+            return None;
+        }
+        unsafe {
+            let all_channels_data_ptr: *const *const f32 = msg_send![self.0, floatChannelData];
+            if all_channels_data_ptr.is_null() {
+                return None;
+            }
+            let all_channels_data = std::slice::from_raw_parts(all_channels_data_ptr, channel_count);
+            let channel_data = all_channels_data[channel];
+            if channel_data.is_null() {
+                None
+            } else {
+                Some(channel_data)
+            }
+        }
+    }
+
+    pub fn i32_buffer(&self, channel: usize) -> Option<*const i32> {
+        let channel_count = self.stride();
+        if channel >= channel_count {
+            return None;
+        }
+        unsafe {
+            let all_channels_data_ptr: *const *const i32 = msg_send![self.0, int32ChannelData];
+            if all_channels_data_ptr.is_null() {
+                return None;
+            }
+            let all_channels_data = std::slice::from_raw_parts(all_channels_data_ptr, channel_count);
+            let channel_data = all_channels_data[channel];
+            if channel_data.is_null() {
+                None
+            } else {
+                Some(channel_data)
+            }
+        }
+    }
+
+    pub fn i16_buffer(&self, channel: usize) -> Option<*const i16> {
+        let channel_count = self.stride();
+        if channel >= channel_count {
+            return None;
+        }
+        unsafe {
+            let all_channels_data_ptr: *const *const i16 = msg_send![self.0, int32ChannelData];
+            if all_channels_data_ptr.is_null() {
+                return None;
+            }
+            let all_channels_data = std::slice::from_raw_parts(all_channels_data_ptr, channel_count);
+            let channel_data = all_channels_data[channel];
+            if channel_data.is_null() {
+                None
+            } else {
+                Some(channel_data)
+            }
+        }
+    }
+}
+
+#[repr(C)]
+struct CFArray(CFArrayRef);
+
+impl CFArray {
+    pub(crate) fn from_ref_unretained(r: CFStringRef) -> Self {
+        unsafe { CFRetain(r); }
+        Self(r)
+    }
+
+    pub(crate) fn from_ref_retained(r: CFStringRef) -> Self {
+        Self(r)
+    }
+
+    pub(crate) fn get_count(&self) -> i32 {
+        unsafe { CFArrayGetCount(self.0) }
+    }
+
+    pub(crate) fn get_value_at_index(&self, index: i32) -> *const c_void {
+        unsafe { CFArrayGetValueAtIndex(self.0, index) }
+    }
+}
+
+impl Clone for CFArray {
+    fn clone(&self) -> Self {
+        CFArray::from_ref_unretained(self.0)
+    }
+}
+
+impl Drop for CFArray {
+    fn drop(&mut self) {
+        unsafe { CFRelease(self.0); }
+    }
+}
+
+
+
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct DispatchQueue(*mut Object);
+
+impl DispatchQueue {
+    pub fn make_concurrent(name: String) -> Self {
+        let cstring_name = CString::new(name.as_str()).unwrap();
+        unsafe { dispatch_queue_create(cstring_name.as_ptr(), DispatchQueueAttr(&mut _dispatch_queue_attr_concurrent as *mut c_void)) }
+    }
+
+    pub fn make_serial(name: String) -> Self {
+        let cstring_name = CString::new(name.as_str()).unwrap();
+        unsafe { dispatch_queue_create(cstring_name.as_ptr(), DispatchQueueAttr(0 as *mut c_void)) }
+    }
+
+    pub fn make_null() -> Self {
+        DispatchQueue(std::ptr::null_mut())
+    }
+}
+
+impl Drop for DispatchQueue {
+    fn drop(&mut self) {
+        if self.0.is_null() {
+            return;
+        }
+        unsafe { dispatch_release(self.0) };
+    }
+}
+
+impl Clone for DispatchQueue {
+    fn clone(&self) -> Self {
+        if self.0.is_null() {
+            return Self(std::ptr::null_mut());
+        }
+        unsafe { dispatch_retain(self.0); }
+        Self(self.0)
+    }
+}
+
+unsafe impl Encode for DispatchQueue {
+    fn encode() -> Encoding {
+        unsafe { Encoding::from_str("@\"NSObject<OS_dispatch_queue>\"") }
+    }
+}
+
+#[repr(C)]
+struct DispatchQueueAttr(*mut c_void);
+
+pub(crate) struct SCRunningApplication(pub(crate) *mut Object);
+
+impl SCRunningApplication {
+    pub(crate) fn from_id_unretained(id: *mut Object) -> Self {
+        unsafe { let _: () = msg_send![id, retain]; }
+        Self(id)
+    }
+
+    pub(crate) fn from_id_retained(id: *mut Object) -> Self {
+        Self(id)
+    }
+
+    pub(crate) fn pid(&self) -> i32 {
+        unsafe {
+            msg_send![self.0, processID]
+        }
+    }
+
+    pub(crate) fn application_name(&self) -> String {
+        unsafe {
+            let app_name_cfstringref: CFStringRef = msg_send![self.0, applicationName];
+            NSString::from_ref_unretained(app_name_cfstringref).as_string()
+        }
+    }
+
+    pub(crate) fn bundle_identifier(&self) -> String {
+        unsafe {
+            let bundle_id_cfstringref: CFStringRef = msg_send![self.0, bundleIdentifier];
+            NSString::from_ref_unretained(bundle_id_cfstringref).as_string()
+        }
+    }
+}
+
+impl Clone for SCRunningApplication {
+    fn clone(&self) -> Self {
+        Self::from_id_unretained(self.0)
+    }
+}
+
+impl Drop for SCRunningApplication {
+    fn drop(&mut self) {
+        unsafe { let _: () = msg_send![self.0, release]; }
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub(crate) enum CGDisplayStreamFrameStatus {
+    Complete,
+    Idle,
+    Blank,
+    Stopped,
+}
+
+impl CGDisplayStreamFrameStatus {
+    pub fn from_i32(x: i32) -> Option<Self> {
+        match x {
+            0 => Some(Self::Complete),
+            1 => Some(Self::Idle),
+            2 => Some(Self::Blank), 
+            3 => Some(Self::Stopped),
+            _ => None
+        }
+    }
+}
+
+pub(crate) struct CGDisplayStream{
+    stream_ref: CGDisplayStreamRef,
+    callback_block: RcBlock<(i32, u64, IOSurfaceRef, CGDisplayStreamUpdateRef), ()>,
+}
+
+impl CGDisplayStream {
+    pub fn new(callback: impl Fn(CGDisplayStreamFrameStatus, Duration, IOSurface) + 'static, display_id: u32, size: (usize, usize), pixel_format: SCStreamPixelFormat, options_dict: NSDictionary, dispatch_queue: DispatchQueue) -> Self {
+        println!("CGDisplayStream::new(..)");
+        let absolute_time_start = RefCell::new(None);
+        let callback_block = ConcreteBlock::new(move |status: i32, display_time: u64, iosurface_ref: IOSurfaceRef, stream_update_ref: CGDisplayStreamUpdateRef| {
+            println!("CGDisplayStream callback_block");
+            if let Some(status) = CGDisplayStreamFrameStatus::from_i32(status) {
+                let relative_time = if let Some(absolute_time_start) = *absolute_time_start.borrow() {
+                    display_time - absolute_time_start
+                } else {
+                    *absolute_time_start.borrow_mut() = Some(display_time);
+                    0
+                };
+                unsafe {
+                    let mut timebase_info: mach_timebase_info_data_t = Default::default();
+                    mach_timebase_info(&mut timebase_info as *mut _);
+                    let time_ns = ((relative_time as u128 * timebase_info.numer as u128) / timebase_info.denom as u128);
+                    let time = Duration::from_nanos(time_ns as u64);
+                    let io_surface = IOSurface::from_ref_unretained(iosurface_ref);
+                    (callback)(status, time, io_surface);
+                }
+            }
+        }).copy();
+        unsafe {
+            let pixel_format = pixel_format.to_ostype();
+            let display_id = CGMainDisplayID();
+            let stream_ref = CGDisplayStreamCreateWithDispatchQueue(display_id, size.0, size.1, pixel_format.as_i32(), std::ptr::null_mut(), dispatch_queue.0, &*callback_block as *const _ as *const c_void);
+            println!("CGDisplayStreamCreateWithDispatchQueue(display_id: {}, output_width: {}, output_height: {}, pixel_format: {:?}, options_dict: {:?}, dispatch_queue: {:?}, callback_block: {:?}): return value: {:?}", display_id, size.0, size.1, pixel_format, options_dict.0, dispatch_queue.0, &callback_block as *const _, stream_ref);
+            Self {
+                stream_ref,
+                callback_block
+            }
+        }
+    }
+
+    pub fn start(&self) -> Result<(), ()> {
+        let error_code = unsafe { CGDisplayStreamStart(self.stream_ref) };
+        println!("CGDisplayStreamStart({:?}) return value: {:?}", self.stream_ref, error_code);
+        if error_code == 0 {
+            Ok(())
+        } else {
+            Err(())
+        }
+    }
+
+    pub fn stop(&self) -> Result<(), ()> {
+        let error_code = unsafe { CGDisplayStreamStop(self.stream_ref) };
+        if error_code == 0 {
+            Ok(())
+        } else {
+            Err(())
+        }
+    }
+}
+
+impl Clone for CGDisplayStream {
+    fn clone(&self) -> Self {
+        unsafe {
+            CFRetain(self.stream_ref);
+        }
+        CGDisplayStream {
+            stream_ref: self.stream_ref,
+            callback_block: self.callback_block.clone()
+        }
+    }
+}
+
+impl Drop for CGDisplayStream {
+    fn drop(&mut self) {
+        unsafe {
+            CFRelease(self.stream_ref);
+        }
+    }
+}
+
+
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum CVPixelFormat {
+    RGB888,
+    BGR888,
+    ARGB8888,
+    BGRA8888,
+    ABGR8888,
+    RGBA8888,
+    V420,
+    F420,
+    Other,
+}
+
+impl CVPixelFormat {
+    pub(crate) fn from_ostype(ostype: &OSType) -> Option<Self> {
+        unsafe {
+            let value = std::mem::transmute::<_, u32>(*ostype);
+            Some(match value {
+                0x00000018 => Self::RGB888,
+                0x32344247 => Self::BGR888,
+                0x00000020 => Self::ARGB8888,
+                0x42475241 => Self::BGRA8888,
+                0x41424752 => Self::ABGR8888,
+                0x52474241 => Self::RGBA8888,
+                0x34323076 => Self::V420,
+                _ => {
+                    return None;
+                }
+            })
+        }
+    }
+}
+
+pub(crate) struct IOSurface(pub(crate) IOSurfaceRef);
+
+const IOSURFACELOCK_READONLY  : u32 = 1;
+const IOSURFACELOCK_AVOIDSYNC : u32 = 2;
+
+const SYS_IOKIT              : i32 = 0x38 << 26;
+const SUB_IOKIT_COMMON       : i32 = 0;
+const KIO_RETURN_CANNOT_LOCK : i32 = SYS_IOKIT | SUB_IOKIT_COMMON | 0x2cc;
+
+pub enum IOSurfaceLockError {
+    CannotLock,
+    Other
+}
+
+pub struct IOSurfaceLockGaurd(IOSurfaceRef, u32);
+
+impl IOSurfaceLockGaurd {
+    pub(crate) fn get_base_address_of_plane(&self, plane: usize) -> Option<*const c_void> {
+        unsafe { 
+            let ptr = IOSurfaceGetBaseAddressOfPlane(self.0, plane);
+            if ptr.is_null() {
+                None
+            } else {
+                Some(ptr)
+            }
+        }
+    }
+
+    pub(crate) fn get_base_address(&self) -> Option<*const c_void> {
+        unsafe {
+            let ptr = IOSurfaceGetBaseAddress(self.0);
+            if ptr.is_null() {
+                None
+            } else {
+                Some(ptr)
+            }
+        }
+    }
+}
+
+impl Drop for IOSurfaceLockGaurd {
+    fn drop(&mut self) {
+        unsafe {
+            let mut seed = 0u32;
+            IOSurfaceUnlock(self.0, self.1, &mut seed as *mut _);
+        }
+    }
+}
+
+impl IOSurface {
+    fn from_ref_unretained(r: IOSurfaceRef) -> Self {
+        unsafe { IOSurfaceIncrementUseCount(r); }
+        Self(r)
+    }
+
+    pub(crate) fn get_pixel_format(&self) -> Option<CVPixelFormat> {
+        unsafe {
+            let pixel_format_ostype = IOSurfaceGetPixelFormat(self.0);
+            CVPixelFormat::from_ostype(&pixel_format_ostype)
+        }
+    }
+
+    pub(crate) fn get_bytes_per_row(&self) -> usize {
+        unsafe {
+            IOSurfaceGetBytesPerRow(self.0)
+        }
+    }
+
+    pub(crate) fn get_bytes_per_row_of_plane(&self, plane: usize) -> usize {
+        unsafe {
+            IOSurfaceGetBytesPerRowOfPlane(self.0, plane)
+        }
+    }
+
+    pub(crate) fn get_width(&self) -> usize {
+        unsafe {
+            IOSurfaceGetWidth(self.0)
+        }
+    }
+
+    pub(crate) fn get_height(&self) -> usize {
+        unsafe {
+            IOSurfaceGetHeight(self.0)
+        }
+    }
+
+    pub(crate) fn get_height_of_plane(&self, plane: usize) -> usize {
+        unsafe {
+            IOSurfaceGetHeightOfPlane(self.0, plane)
+        }
+    }
+
+    pub(crate) fn get_width_of_plane(&self, plane: usize) -> usize {
+        unsafe {
+            IOSurfaceGetWidthOfPlane(self.0, plane)
+        }
+    }
+
+    pub(crate) fn lock(&self, read_only: bool, avoid_sync: bool) -> Result<IOSurfaceLockGaurd, IOSurfaceLockError> {
+        unsafe {
+            let options = 
+                if read_only  { IOSURFACELOCK_READONLY  } else { 0 } |
+                if avoid_sync { IOSURFACELOCK_AVOIDSYNC } else { 0 }
+            ;
+            match IOSurfaceLock(self.0, options, std::ptr::null_mut()) {
+                0                      => Ok(IOSurfaceLockGaurd(self.0, options)),
+                KIO_RETURN_CANNOT_LOCK => Err(IOSurfaceLockError::CannotLock),
+                _                      => Err(IOSurfaceLockError::Other)
+            }
+        }
+    }
+}
+
+impl Clone for IOSurface {
+    fn clone(&self) -> Self {
+        Self::from_ref_unretained(self.0)
+    }
+}
+
+impl Drop for IOSurface {
+    fn drop(&mut self) {
+        unsafe {
+            IOSurfaceDecrementUseCount(self.0);
+        }
+    }
+}
+
+/*
+kCFNumberSInt8Type = 1,
+kCFNumberSInt16Type = 2,
+kCFNumberSInt32Type = 3,
+kCFNumberSInt64Type = 4,
+kCFNumberFloat32Type = 5,
+kCFNumberFloat64Type = 6,	/* 64-bit IEEE 754 */
+/* Basic C types */
+kCFNumberCharType = 7,
+kCFNumberShortType = 8,
+kCFNumberIntType = 9,
+kCFNumberLongType = 10,
+kCFNumberLongLongType = 11,
+kCFNumberFloatType = 12,
+kCFNumberDoubleType = 13,
+/* Other */
+kCFNumberCFIndexType = 14,
+*/
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub(crate) enum CFNumberType {
+    I8,
+    I16,
+    I32,
+    I64,
+    F32,
+    F64,
+    Char,
+    Short,
+    Int,
+    Long,
+    LongLong,
+    Float,
+    Double,
+    CFIndex,
+    NSInteger,
+    CGFloat,
+}
+
+impl CFNumberType {
+    pub(crate) fn to_isize(self) -> isize {
+        match self {
+            Self::I8 => 1,
+            Self::I16 => 2,
+            Self::I32 => 3,
+            Self::I64 => 4,
+            Self::F32 => 5,
+            Self::F64 => 6,
+            Self::Char => 7,
+            Self::Short => 8,
+            Self::Int => 9,
+            Self::Long => 10,
+            Self::LongLong => 11,
+            Self::Float => 12,
+            Self::Double => 13,
+            Self::CFIndex => 14,
+            Self::NSInteger => 15,
+            Self::CGFloat => 16,
+        }
+    }
+
+    pub(crate) fn from_i32(x: i32) -> Option<Self> {
+        Some(match x {
+            1 => Self::I8,
+            2 => Self::I16,
+            3 => Self::I32,
+            4 => Self::I64,
+            5 => Self::F32,
+            6 => Self::F64,
+            7 => Self::Char,
+            8 => Self::Short,
+            9 => Self::Int,
+            10 => Self::Long,
+            11 => Self::LongLong,
+            12 => Self::Float,
+            13 => Self::Double,
+            14 => Self::CFIndex,
+            15 => Self::NSInteger,
+            16 => Self::CGFloat,
+            _ => return None,
+        })
+    }
+}
+
+#[repr(C)]
+pub(crate) struct CFNumber(pub(crate) CFNumberRef);
+
+impl CFNumber {
+    pub fn new_f32(x: f32) -> Self {
+        unsafe {
+            let r = CFNumberCreate(kCFAllocatorNull, CFNumberType::F32.to_isize(), &x as *const f32 as *const c_void);
+            Self(r)
+        }
+    }
+
+    pub fn new_i32(x: i32) -> Self {
+        unsafe {
+            let r = CFNumberCreate(kCFAllocatorNull, CFNumberType::I32.to_isize(), &x as *const i32 as *const c_void);
+            Self(r)
+        }
+    }
+}
+
+impl Clone for CFNumber {
+    fn clone(&self) -> Self {
+        unsafe { CFRetain(self.0); }
+        CFNumber(self.0)
+    }
+}
+
+impl Drop for CFNumber {
+    fn drop(&mut self) {
+        unsafe {
+            CFRelease(self.0);
+        }
+    }
+}
+
+pub struct NSNumber(pub(crate) *mut Object);
+
+impl NSNumber {
+    pub(crate) fn from_id_unretained(id: *mut Object) -> Self {
+        unsafe { let _: () = msg_send![id, retain]; }
+        Self(id)
+    }
+
+    pub(crate) fn from_id_retained(id: *mut Object) -> Self {
+        Self(id)
+    }
+
+    pub(crate) fn new_isize(x: isize) -> Self {
+        unsafe {
+            let id: *mut Object = msg_send![class!(NSNumber), numberWithInteger: x];
+            Self(id)
+        }
+    }
+
+    pub(crate) fn new_f32(x: f32) -> Self {
+        unsafe {
+            let id: *mut Object = msg_send![class!(NSNumber), numberWithFloat: x];
+            Self(id)
+        }
+    }
+
+    pub(crate) fn as_i32(&self) -> i32 {
+        unsafe {
+            msg_send![self.0, intValue]
+        }
+    }
+
+    pub(crate) fn as_f64(&self) -> f64 {
+        unsafe {
+            msg_send![self.0, doubleValue]
+        }
+    }
+
+}
+
+impl Clone for NSNumber {
+    fn clone(&self) -> Self {
+        Self::from_id_unretained(self.0)
+    }
+}
+
+impl Drop for NSNumber {
+    fn drop(&mut self) {
+        unsafe { let _: () = msg_send![self.0, release]; }
+    }
+}
+
+pub struct NSApplication(*mut Object);
+
+impl NSApplication {
+    fn from_id_unretained(id: *mut Object) -> Self {
+        unsafe { let _: () = msg_send![id, retain]; }
+        Self(id)
+    }
+
+    fn from_id_retained(id: *mut Object) -> Self {
+        Self(id)
+    }
+
+    pub fn shared() -> Self {
+        unsafe {
+            let id: *mut Object = msg_send![class!(NSApplication), sharedApplication];
+            Self::from_id_unretained(id)
+        }
+    }
+
+    pub fn run(&self) {
+        unsafe {
+            let _: () = msg_send![self.0, run];
+        }
+    }
+}
+
+impl Clone for NSApplication {
+    fn clone(&self) -> Self {
+        Self::from_id_unretained(self.0)
+    }
+}
+
+impl Drop for NSApplication {
+    fn drop(&mut self) {
+        unsafe { let _: () = msg_send![self.0, release]; }
+    }
+}
+
+pub enum SCFrameStatus {
+    Complete,
+    Idle,
+    Blank,
+    Started,
+    Suspended,
+    Stopped,
+}
+
+impl SCFrameStatus {
+    pub fn from_i32(x: i32) -> Option<Self> {
+        match x {
+            0 => Some(Self::Complete),
+            1 => Some(Self::Idle),
+            2 => Some(Self::Blank),
+            3 => Some(Self::Suspended),
+            4 => Some(Self::Started),
+            5 => Some(Self::Stopped),
+            _ => None,
+        }
+    }
+}
+
+pub struct CVPixelBuffer(CVPixelBufferRef);
+
+impl CVPixelBuffer {
+    pub fn from_ref_unretained(r: CVPixelBufferRef) -> Self {
+        unsafe { CFRetain(r); }
+        Self(r)
+    }
+
+    pub fn from_ref_retained(r: CVPixelBufferRef) -> Self {
+        Self(r)
+    }
+
+    pub fn get_iosurface_ptr(&self) -> Option<*const c_void> {
+        unsafe {
+            let iosurface_ptr = CVPixelBufferGetIOSurface(self.0);
+            if iosurface_ptr.is_null() {
+                None
+            } else {
+                Some(iosurface_ptr)
+            }
+        }
+    }
+
+    pub fn get_iosurface(&self) -> Option<IOSurface> {
+        unsafe {
+            let iosurface_ptr = CVPixelBufferGetIOSurface(self.0);
+            if iosurface_ptr.is_null() {
+                None
+            } else {
+                Some(IOSurface::from_ref_unretained(iosurface_ptr))
+            }
+        }
+    }
+
+    pub fn get_width(&self) -> usize {
+        unsafe {
+            CVPixelBufferGetWidth(self.0)
+        }
+    }
+
+    pub fn get_height(&self) -> usize {
+        unsafe {
+            CVPixelBufferGetHeight(self.0)
+        }
+    }
+}
+
+impl Clone for CVPixelBuffer {
+    fn clone(&self) -> Self {
+        Self::from_ref_unretained(self.0)
+    }
+}
+
+impl Drop for CVPixelBuffer {
+    fn drop(&mut self) {
+        unsafe { CFRelease(self.0); }
+    }
+}
+
+
+#[repr(C)]
+struct NSValue(*mut Object);
+
+#[allow(unused)]
+impl NSValue {
+    fn from_id(id: *mut Object) -> Self {
+        Self(id)
+    }
+
+    fn size_value(&self) -> CGSize {
+        unsafe {
+            msg_send![self.0, sizeValue]
+        }
+    }
+
+    fn unsigned_int_value(&self) -> u32 {
+        unsafe {
+            msg_send![self.0, unsignedIntValue]
+        }
+    }
+}
+
+impl Drop for NSValue {
+    fn drop(&mut self) {
+        unsafe { let _: () = msg_send![self.0, release]; }
+    }
+}
+
+#[repr(C)]
+pub struct NSScreen(*mut Object);
+
+impl NSScreen {
+    pub(crate) fn screens() -> Vec<NSScreen> {
+        unsafe {
+            let screens_ns_array = NSArray::from_id_retained(msg_send![class!(NSScreen), screens]);
+            let mut screens_out = Vec::new();
+            for i in 0..screens_ns_array.count() {
+                let screen: *mut Object = screens_ns_array.obj_at_index(i);
+                screens_out.push(NSScreen(screen));
+            }
+            return screens_out;
+        }
+    }
+
+    fn device_description(&self) -> NSDictionary {
+        unsafe { NSDictionary::from_id_retained(msg_send![self.0, deviceDescription]) }
+    }
+
+    pub(crate) fn dpi(&self) -> f64 {
+        let ns_screen_number_string = NSString::new("NSScreenNumber");
+        let device_description = self.device_description();
+        let pixel_size_value = NSValue(unsafe { device_description.value_for_key(NSDeviceSize) });
+        if pixel_size_value.0.is_null() {
+            std::mem::forget(pixel_size_value);
+            return 72.0;
+        }
+        let pixel_size = pixel_size_value.size_value();
+        let screen_number_ptr = device_description.value_for_key(ns_screen_number_string.0 as CFStringRef);
+        if screen_number_ptr.is_null() {
+            return 72.0;
+        }
+        let screen_number_num = NSNumber::from_id_unretained(screen_number_ptr);
+        let screen_number = screen_number_num.as_i32() as u32;
+        let physical_size = unsafe { CGDisplayScreenSize(screen_number) };
+        let mut backing_scale_factor: f32 = unsafe { msg_send![self.0, backingScaleFactor] };
+        if backing_scale_factor == 0.0 {
+            backing_scale_factor = 1.0;
+        }
+        std::mem::forget(pixel_size_value);
+        std::mem::forget(screen_number_num);
+        std::mem::forget(device_description);
+        (pixel_size.x / physical_size.x) * 25.4 * backing_scale_factor as f64
+    }
+
+    pub(crate) fn frame(&self) -> CGRect {
+        unsafe { msg_send![self.0, frame] }
+    }
+}
+
\ No newline at end of file diff --git a/docs/macos_docs/src/crabgrab/platform/mod.rs.html b/docs/macos_docs/src/crabgrab/platform/mod.rs.html new file mode 100644 index 00000000..8ca113bd --- /dev/null +++ b/docs/macos_docs/src/crabgrab/platform/mod.rs.html @@ -0,0 +1,27 @@ +mod.rs - source
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+
#[cfg(target_os = "macos")]
+pub mod macos;
+
+#[cfg(target_os = "macos")]
+pub use macos as platform_impl;
+
+#[cfg(target_os = "windows")]
+pub mod windows;
+
+#[cfg(target_os = "windows")]
+pub use windows as platform_impl;
+
+
+
\ No newline at end of file diff --git a/docs/macos_docs/src/crabgrab/prelude.rs.html b/docs/macos_docs/src/crabgrab/prelude.rs.html new file mode 100644 index 00000000..e35290af --- /dev/null +++ b/docs/macos_docs/src/crabgrab/prelude.rs.html @@ -0,0 +1,9 @@ +prelude.rs - source
1
+2
+3
+4
+
pub use crate::capturable_content::*;
+pub use crate::frame::*;
+pub use crate::capture_stream::*;
+pub use crate::util::*;
+
\ No newline at end of file diff --git a/docs/macos_docs/src/crabgrab/util.rs.html b/docs/macos_docs/src/crabgrab/util.rs.html new file mode 100644 index 00000000..a43c429b --- /dev/null +++ b/docs/macos_docs/src/crabgrab/util.rs.html @@ -0,0 +1,161 @@ +util.rs - source
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
+
/// Represents a 2d size
+#[derive(Debug, Copy, Clone)]
+pub struct Size {
+    pub width: f64,
+    pub height: f64,
+}
+
+impl Size {
+    /// scale the size uniformly by some value
+    pub fn scaled(&self, scale: f64) -> Self {
+        Self {
+            width: self.width * scale,
+            height: self.height * scale
+        }
+    }
+
+    /// scale the size non-uniformly in x and y
+    pub fn scaled_2d(&self, scale: (f64, f64)) -> Self {
+        Self {
+            width: self.width * scale.0,
+            height: self.height * scale.1
+        }
+    }
+}
+
+/// Represents a 2d point
+#[derive(Debug, Copy, Clone)]
+pub struct Point {
+    pub x: f64,
+    pub y: f64,
+}
+
+impl Point {
+    /// The point at (0, 0)
+    pub const ZERO: Point = Point {
+        x: 0.0, 
+        y: 0.0
+    };
+
+    /// Scale the point uniformly by some value
+    pub fn scaled(&self, scale: f64) -> Self {
+        Self {
+            x: self.x * scale,
+            y: self.y * scale
+        }
+    }
+
+    /// Scale the point non-uniformly in x and y
+    pub fn scaled_2d(&self, scale: (f64, f64)) -> Self {
+        Self {
+            x: self.x * scale.0,
+            y: self.y * scale.1
+        }
+    }
+}
+
+/// Represents an axis-aligned rectangle
+#[derive(Debug, Copy, Clone)]
+pub struct Rect {
+    pub origin: Point,
+    pub size: Size,
+}
+
+impl Rect {
+    /// Scale the rectangle uniformly
+    pub fn scaled(&self, scale: f64) -> Self {
+        Self {
+            origin: self.origin.scaled(scale),
+            size: self.size.scaled(scale)
+        }
+    }
+
+    /// Scale the rectangle non-uniformly in x and y
+    pub fn scaled_2d(&self, scale: (f64, f64)) -> Self {
+        Self {
+            origin: self.origin.scaled_2d(scale),
+            size: self.size.scaled_2d(scale)
+        }
+    }
+}
+
\ No newline at end of file diff --git a/docs/macos_docs/static.files/COPYRIGHT-23e9bde6c69aea69.txt b/docs/macos_docs/static.files/COPYRIGHT-23e9bde6c69aea69.txt new file mode 100644 index 00000000..1447df79 --- /dev/null +++ b/docs/macos_docs/static.files/COPYRIGHT-23e9bde6c69aea69.txt @@ -0,0 +1,50 @@ +# REUSE-IgnoreStart + +These documentation pages include resources by third parties. This copyright +file applies only to those resources. The following third party resources are +included, and carry their own copyright notices and license terms: + +* Fira Sans (FiraSans-Regular.woff2, FiraSans-Medium.woff2): + + Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ + with Reserved Font Name Fira Sans. + + Copyright (c) 2014, Telefonica S.A. + + Licensed under the SIL Open Font License, Version 1.1. + See FiraSans-LICENSE.txt. + +* rustdoc.css, main.js, and playpen.js: + + Copyright 2015 The Rust Developers. + Licensed under the Apache License, Version 2.0 (see LICENSE-APACHE.txt) or + the MIT license (LICENSE-MIT.txt) at your option. + +* normalize.css: + + Copyright (c) Nicolas Gallagher and Jonathan Neal. + Licensed under the MIT license (see LICENSE-MIT.txt). + +* Source Code Pro (SourceCodePro-Regular.ttf.woff2, + SourceCodePro-Semibold.ttf.woff2, SourceCodePro-It.ttf.woff2): + + Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), + with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark + of Adobe Systems Incorporated in the United States and/or other countries. + + Licensed under the SIL Open Font License, Version 1.1. + See SourceCodePro-LICENSE.txt. + +* Source Serif 4 (SourceSerif4-Regular.ttf.woff2, SourceSerif4-Bold.ttf.woff2, + SourceSerif4-It.ttf.woff2): + + Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name + 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United + States and/or other countries. + + Licensed under the SIL Open Font License, Version 1.1. + See SourceSerif4-LICENSE.md. + +This copyright file is intended to be distributed with rustdoc output. + +# REUSE-IgnoreEnd diff --git a/docs/macos_docs/static.files/FiraSans-LICENSE-db4b642586e02d97.txt b/docs/macos_docs/static.files/FiraSans-LICENSE-db4b642586e02d97.txt new file mode 100644 index 00000000..d7e9c149 --- /dev/null +++ b/docs/macos_docs/static.files/FiraSans-LICENSE-db4b642586e02d97.txt @@ -0,0 +1,98 @@ +// REUSE-IgnoreStart + +Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. +with Reserved Font Name < Fira >, + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/docs/macos_docs/static.files/FiraSans-Medium-8f9a781e4970d388.woff2 b/docs/macos_docs/static.files/FiraSans-Medium-8f9a781e4970d388.woff2 new file mode 100644 index 00000000..7a1e5fc5 Binary files /dev/null and b/docs/macos_docs/static.files/FiraSans-Medium-8f9a781e4970d388.woff2 differ diff --git a/docs/macos_docs/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 b/docs/macos_docs/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 new file mode 100644 index 00000000..e766e06c Binary files /dev/null and b/docs/macos_docs/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 differ diff --git a/docs/macos_docs/static.files/LICENSE-APACHE-b91fa81cba47b86a.txt b/docs/macos_docs/static.files/LICENSE-APACHE-b91fa81cba47b86a.txt new file mode 100644 index 00000000..16fe87b0 --- /dev/null +++ b/docs/macos_docs/static.files/LICENSE-APACHE-b91fa81cba47b86a.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/docs/macos_docs/static.files/LICENSE-MIT-65090b722b3f6c56.txt b/docs/macos_docs/static.files/LICENSE-MIT-65090b722b3f6c56.txt new file mode 100644 index 00000000..31aa7938 --- /dev/null +++ b/docs/macos_docs/static.files/LICENSE-MIT-65090b722b3f6c56.txt @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/docs/macos_docs/static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 b/docs/macos_docs/static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 new file mode 100644 index 00000000..1866ad4b Binary files /dev/null and b/docs/macos_docs/static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 differ diff --git a/docs/macos_docs/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt b/docs/macos_docs/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt new file mode 100644 index 00000000..4b3edc29 --- /dev/null +++ b/docs/macos_docs/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt @@ -0,0 +1,103 @@ +// REUSE-IgnoreStart + +Copyright (c) 2010, NAVER Corporation (https://www.navercorp.com/), + +with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic, +NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver NanumBrush, NanumPen, +Naver NanumPen, Naver NanumGothicEco, NanumGothicEco, Naver NanumMyeongjoEco, +NanumMyeongjoEco, Naver NanumGothicLight, NanumGothicLight, NanumBarunGothic, +Naver NanumBarunGothic, NanumSquareRound, NanumBarunPen, MaruBuri + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/docs/macos_docs/static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 b/docs/macos_docs/static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 new file mode 100644 index 00000000..462c34ef Binary files /dev/null and b/docs/macos_docs/static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 differ diff --git a/docs/macos_docs/static.files/SourceCodePro-LICENSE-d180d465a756484a.txt b/docs/macos_docs/static.files/SourceCodePro-LICENSE-d180d465a756484a.txt new file mode 100644 index 00000000..0d2941e1 --- /dev/null +++ b/docs/macos_docs/static.files/SourceCodePro-LICENSE-d180d465a756484a.txt @@ -0,0 +1,97 @@ +// REUSE-IgnoreStart + +Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/docs/macos_docs/static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 b/docs/macos_docs/static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 new file mode 100644 index 00000000..10b558e0 Binary files /dev/null and b/docs/macos_docs/static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 differ diff --git a/docs/macos_docs/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 b/docs/macos_docs/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 new file mode 100644 index 00000000..5ec64eef Binary files /dev/null and b/docs/macos_docs/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 differ diff --git a/docs/macos_docs/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 b/docs/macos_docs/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 new file mode 100644 index 00000000..181a07f6 Binary files /dev/null and b/docs/macos_docs/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 differ diff --git a/docs/macos_docs/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 b/docs/macos_docs/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 new file mode 100644 index 00000000..2ae08a7b Binary files /dev/null and b/docs/macos_docs/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 differ diff --git a/docs/macos_docs/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md b/docs/macos_docs/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md new file mode 100644 index 00000000..175fa4f4 --- /dev/null +++ b/docs/macos_docs/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md @@ -0,0 +1,98 @@ + + +Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. +Copyright 2014 - 2023 Adobe (http://www.adobe.com/), with Reserved Font Name ‘Source’. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + + diff --git a/docs/macos_docs/static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 b/docs/macos_docs/static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 new file mode 100644 index 00000000..0263fc30 Binary files /dev/null and b/docs/macos_docs/static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 differ diff --git a/docs/macos_docs/static.files/clipboard-7571035ce49a181d.svg b/docs/macos_docs/static.files/clipboard-7571035ce49a181d.svg new file mode 100644 index 00000000..8adbd996 --- /dev/null +++ b/docs/macos_docs/static.files/clipboard-7571035ce49a181d.svg @@ -0,0 +1 @@ + diff --git a/docs/macos_docs/static.files/favicon-16x16-8b506e7a72182f1c.png b/docs/macos_docs/static.files/favicon-16x16-8b506e7a72182f1c.png new file mode 100644 index 00000000..ea4b45ca Binary files /dev/null and b/docs/macos_docs/static.files/favicon-16x16-8b506e7a72182f1c.png differ diff --git a/docs/macos_docs/static.files/favicon-2c020d218678b618.svg b/docs/macos_docs/static.files/favicon-2c020d218678b618.svg new file mode 100644 index 00000000..8b34b511 --- /dev/null +++ b/docs/macos_docs/static.files/favicon-2c020d218678b618.svg @@ -0,0 +1,24 @@ + + + + + diff --git a/docs/macos_docs/static.files/favicon-32x32-422f7d1d52889060.png b/docs/macos_docs/static.files/favicon-32x32-422f7d1d52889060.png new file mode 100644 index 00000000..69b8613c Binary files /dev/null and b/docs/macos_docs/static.files/favicon-32x32-422f7d1d52889060.png differ diff --git a/docs/macos_docs/static.files/main-9dd44ab47b99a0fb.js b/docs/macos_docs/static.files/main-9dd44ab47b99a0fb.js new file mode 100644 index 00000000..cfb9a38f --- /dev/null +++ b/docs/macos_docs/static.files/main-9dd44ab47b99a0fb.js @@ -0,0 +1,12 @@ +"use strict";window.RUSTDOC_TOOLTIP_HOVER_MS=300;window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS=450;function resourcePath(basename,extension){return getVar("root-path")+basename+getVar("resource-suffix")+extension}function hideMain(){addClass(document.getElementById(MAIN_ID),"hidden")}function showMain(){removeClass(document.getElementById(MAIN_ID),"hidden")}function elemIsInParent(elem,parent){while(elem&&elem!==document.body){if(elem===parent){return true}elem=elem.parentElement}return false}function blurHandler(event,parentElem,hideCallback){if(!elemIsInParent(document.activeElement,parentElem)&&!elemIsInParent(event.relatedTarget,parentElem)){hideCallback()}}window.rootPath=getVar("root-path");window.currentCrate=getVar("current-crate");function setMobileTopbar(){const mobileTopbar=document.querySelector(".mobile-topbar");const locationTitle=document.querySelector(".sidebar h2.location");if(mobileTopbar){const mobileTitle=document.createElement("h2");mobileTitle.className="location";if(hasClass(document.body,"crate")){mobileTitle.innerText=`Crate ${window.currentCrate}`}else if(locationTitle){mobileTitle.innerHTML=locationTitle.innerHTML}mobileTopbar.appendChild(mobileTitle)}}function getVirtualKey(ev){if("key"in ev&&typeof ev.key!=="undefined"){return ev.key}const c=ev.charCode||ev.keyCode;if(c===27){return"Escape"}return String.fromCharCode(c)}const MAIN_ID="main-content";const SETTINGS_BUTTON_ID="settings-menu";const ALTERNATIVE_DISPLAY_ID="alternative-display";const NOT_DISPLAYED_ID="not-displayed";const HELP_BUTTON_ID="help-button";function getSettingsButton(){return document.getElementById(SETTINGS_BUTTON_ID)}function getHelpButton(){return document.getElementById(HELP_BUTTON_ID)}function getNakedUrl(){return window.location.href.split("?")[0].split("#")[0]}function insertAfter(newNode,referenceNode){referenceNode.parentNode.insertBefore(newNode,referenceNode.nextSibling)}function getOrCreateSection(id,classes){let el=document.getElementById(id);if(!el){el=document.createElement("section");el.id=id;el.className=classes;insertAfter(el,document.getElementById(MAIN_ID))}return el}function getAlternativeDisplayElem(){return getOrCreateSection(ALTERNATIVE_DISPLAY_ID,"content hidden")}function getNotDisplayedElem(){return getOrCreateSection(NOT_DISPLAYED_ID,"hidden")}function switchDisplayedElement(elemToDisplay){const el=getAlternativeDisplayElem();if(el.children.length>0){getNotDisplayedElem().appendChild(el.firstElementChild)}if(elemToDisplay===null){addClass(el,"hidden");showMain();return}el.appendChild(elemToDisplay);hideMain();removeClass(el,"hidden")}function browserSupportsHistoryApi(){return window.history&&typeof window.history.pushState==="function"}function preLoadCss(cssUrl){const link=document.createElement("link");link.href=cssUrl;link.rel="preload";link.as="style";document.getElementsByTagName("head")[0].appendChild(link)}(function(){const isHelpPage=window.location.pathname.endsWith("/help.html");function loadScript(url){const script=document.createElement("script");script.src=url;document.head.append(script)}getSettingsButton().onclick=event=>{if(event.ctrlKey||event.altKey||event.metaKey){return}window.hideAllModals(false);addClass(getSettingsButton(),"rotate");event.preventDefault();loadScript(getVar("static-root-path")+getVar("settings-js"));setTimeout(()=>{const themes=getVar("themes").split(",");for(const theme of themes){if(theme!==""){preLoadCss(getVar("root-path")+theme+".css")}}},0)};window.searchState={loadingText:"Loading search results...",input:document.getElementsByClassName("search-input")[0],outputElement:()=>{let el=document.getElementById("search");if(!el){el=document.createElement("section");el.id="search";getNotDisplayedElem().appendChild(el)}return el},title:document.title,titleBeforeSearch:document.title,timeout:null,currentTab:0,focusedByTab:[null,null,null],clearInputTimeout:()=>{if(searchState.timeout!==null){clearTimeout(searchState.timeout);searchState.timeout=null}},isDisplayed:()=>searchState.outputElement().parentElement.id===ALTERNATIVE_DISPLAY_ID,focus:()=>{searchState.input.focus()},defocus:()=>{searchState.input.blur()},showResults:search=>{if(search===null||typeof search==="undefined"){search=searchState.outputElement()}switchDisplayedElement(search);searchState.mouseMovedAfterSearch=false;document.title=searchState.title},removeQueryParameters:()=>{document.title=searchState.titleBeforeSearch;if(browserSupportsHistoryApi()){history.replaceState(null,"",getNakedUrl()+window.location.hash)}},hideResults:()=>{switchDisplayedElement(null);searchState.removeQueryParameters()},getQueryStringParams:()=>{const params={};window.location.search.substring(1).split("&").map(s=>{const pair=s.split("=");params[decodeURIComponent(pair[0])]=typeof pair[1]==="undefined"?null:decodeURIComponent(pair[1])});return params},setup:()=>{const search_input=searchState.input;if(!searchState.input){return}let searchLoaded=false;function loadSearch(){if(!searchLoaded){searchLoaded=true;loadScript(getVar("static-root-path")+getVar("search-js"));loadScript(resourcePath("search-index",".js"))}}search_input.addEventListener("focus",()=>{search_input.origPlaceholder=search_input.placeholder;search_input.placeholder="Type your search here.";loadSearch()});if(search_input.value!==""){loadSearch()}const params=searchState.getQueryStringParams();if(params.search!==undefined){searchState.setLoadingSearch();loadSearch()}},setLoadingSearch:()=>{const search=searchState.outputElement();search.innerHTML="

"+searchState.loadingText+"

";searchState.showResults(search)},};const toggleAllDocsId="toggle-all-docs";let savedHash="";function handleHashes(ev){if(ev!==null&&searchState.isDisplayed()&&ev.newURL){switchDisplayedElement(null);const hash=ev.newURL.slice(ev.newURL.indexOf("#")+1);if(browserSupportsHistoryApi()){history.replaceState(null,"",getNakedUrl()+window.location.search+"#"+hash)}const elem=document.getElementById(hash);if(elem){elem.scrollIntoView()}}const pageId=window.location.hash.replace(/^#/,"");if(savedHash!==pageId){savedHash=pageId;if(pageId!==""){expandSection(pageId)}}if(savedHash.startsWith("impl-")){const splitAt=savedHash.indexOf("/");if(splitAt!==-1){const implId=savedHash.slice(0,splitAt);const assocId=savedHash.slice(splitAt+1);const implElem=document.getElementById(implId);if(implElem&&implElem.parentElement.tagName==="SUMMARY"&&implElem.parentElement.parentElement.tagName==="DETAILS"){onEachLazy(implElem.parentElement.parentElement.querySelectorAll(`[id^="${assocId}"]`),item=>{const numbered=/([^-]+)-([0-9]+)/.exec(item.id);if(item.id===assocId||(numbered&&numbered[1]===assocId)){openParentDetails(item);item.scrollIntoView();setTimeout(()=>{window.location.replace("#"+item.id)},0)}})}}}}function onHashChange(ev){hideSidebar();handleHashes(ev)}function openParentDetails(elem){while(elem){if(elem.tagName==="DETAILS"){elem.open=true}elem=elem.parentNode}}function expandSection(id){openParentDetails(document.getElementById(id))}function handleEscape(ev){searchState.clearInputTimeout();searchState.hideResults();ev.preventDefault();searchState.defocus();window.hideAllModals(true)}function handleShortcut(ev){const disableShortcuts=getSettingValue("disable-shortcuts")==="true";if(ev.ctrlKey||ev.altKey||ev.metaKey||disableShortcuts){return}if(document.activeElement.tagName==="INPUT"&&document.activeElement.type!=="checkbox"&&document.activeElement.type!=="radio"){switch(getVirtualKey(ev)){case"Escape":handleEscape(ev);break}}else{switch(getVirtualKey(ev)){case"Escape":handleEscape(ev);break;case"s":case"S":ev.preventDefault();searchState.focus();break;case"+":ev.preventDefault();expandAllDocs();break;case"-":ev.preventDefault();collapseAllDocs();break;case"?":showHelp();break;default:break}}}document.addEventListener("keypress",handleShortcut);document.addEventListener("keydown",handleShortcut);function addSidebarItems(){if(!window.SIDEBAR_ITEMS){return}const sidebar=document.getElementsByClassName("sidebar-elems")[0];function block(shortty,id,longty){const filtered=window.SIDEBAR_ITEMS[shortty];if(!filtered){return}const modpath=hasClass(document.body,"mod")?"../":"";const h3=document.createElement("h3");h3.innerHTML=`${longty}`;const ul=document.createElement("ul");ul.className="block "+shortty;for(const name of filtered){let path;if(shortty==="mod"){path=`${modpath}${name}/index.html`}else{path=`${modpath}${shortty}.${name}.html`}let current_page=document.location.href.toString();if(current_page.endsWith("/")){current_page+="index.html"}const link=document.createElement("a");link.href=path;if(link.href===current_page){link.className="current"}link.textContent=name;const li=document.createElement("li");li.appendChild(link);ul.appendChild(li)}sidebar.appendChild(h3);sidebar.appendChild(ul)}if(sidebar){block("primitive","primitives","Primitive Types");block("mod","modules","Modules");block("macro","macros","Macros");block("struct","structs","Structs");block("enum","enums","Enums");block("constant","constants","Constants");block("static","static","Statics");block("trait","traits","Traits");block("fn","functions","Functions");block("type","types","Type Aliases");block("union","unions","Unions");block("foreigntype","foreign-types","Foreign Types");block("keyword","keywords","Keywords");block("opaque","opaque-types","Opaque Types");block("attr","attributes","Attribute Macros");block("derive","derives","Derive Macros");block("traitalias","trait-aliases","Trait Aliases")}}window.register_implementors=imp=>{const implementors=document.getElementById("implementors-list");const synthetic_implementors=document.getElementById("synthetic-implementors-list");const inlined_types=new Set();const TEXT_IDX=0;const SYNTHETIC_IDX=1;const TYPES_IDX=2;if(synthetic_implementors){onEachLazy(synthetic_implementors.getElementsByClassName("impl"),el=>{const aliases=el.getAttribute("data-aliases");if(!aliases){return}aliases.split(",").forEach(alias=>{inlined_types.add(alias)})})}let currentNbImpls=implementors.getElementsByClassName("impl").length;const traitName=document.querySelector(".main-heading h1 > .trait").textContent;const baseIdName="impl-"+traitName+"-";const libs=Object.getOwnPropertyNames(imp);const script=document.querySelector("script[data-ignore-extern-crates]");const ignoreExternCrates=new Set((script?script.getAttribute("data-ignore-extern-crates"):"").split(","));for(const lib of libs){if(lib===window.currentCrate||ignoreExternCrates.has(lib)){continue}const structs=imp[lib];struct_loop:for(const struct of structs){const list=struct[SYNTHETIC_IDX]?synthetic_implementors:implementors;if(struct[SYNTHETIC_IDX]){for(const struct_type of struct[TYPES_IDX]){if(inlined_types.has(struct_type)){continue struct_loop}inlined_types.add(struct_type)}}const code=document.createElement("h3");code.innerHTML=struct[TEXT_IDX];addClass(code,"code-header");onEachLazy(code.getElementsByTagName("a"),elem=>{const href=elem.getAttribute("href");if(href&&!href.startsWith("#")&&!/^(?:[a-z+]+:)?\/\//.test(href)){elem.setAttribute("href",window.rootPath+href)}});const currentId=baseIdName+currentNbImpls;const anchor=document.createElement("a");anchor.href="#"+currentId;addClass(anchor,"anchor");const display=document.createElement("div");display.id=currentId;addClass(display,"impl");display.appendChild(anchor);display.appendChild(code);list.appendChild(display);currentNbImpls+=1}}};if(window.pending_implementors){window.register_implementors(window.pending_implementors)}window.register_type_impls=imp=>{if(!imp||!imp[window.currentCrate]){return}window.pending_type_impls=null;const idMap=new Map();let implementations=document.getElementById("implementations-list");let trait_implementations=document.getElementById("trait-implementations-list");let trait_implementations_header=document.getElementById("trait-implementations");const script=document.querySelector("script[data-self-path]");const selfPath=script?script.getAttribute("data-self-path"):null;const mainContent=document.querySelector("#main-content");const sidebarSection=document.querySelector(".sidebar section");let methods=document.querySelector(".sidebar .block.method");let associatedTypes=document.querySelector(".sidebar .block.associatedtype");let associatedConstants=document.querySelector(".sidebar .block.associatedconstant");let sidebarTraitList=document.querySelector(".sidebar .block.trait-implementation");for(const impList of imp[window.currentCrate]){const types=impList.slice(2);const text=impList[0];const isTrait=impList[1]!==0;const traitName=impList[1];if(types.indexOf(selfPath)===-1){continue}let outputList=isTrait?trait_implementations:implementations;if(outputList===null){const outputListName=isTrait?"Trait Implementations":"Implementations";const outputListId=isTrait?"trait-implementations-list":"implementations-list";const outputListHeaderId=isTrait?"trait-implementations":"implementations";const outputListHeader=document.createElement("h2");outputListHeader.id=outputListHeaderId;outputListHeader.innerText=outputListName;outputList=document.createElement("div");outputList.id=outputListId;if(isTrait){const link=document.createElement("a");link.href=`#${outputListHeaderId}`;link.innerText="Trait Implementations";const h=document.createElement("h3");h.appendChild(link);trait_implementations=outputList;trait_implementations_header=outputListHeader;sidebarSection.appendChild(h);sidebarTraitList=document.createElement("ul");sidebarTraitList.className="block trait-implementation";sidebarSection.appendChild(sidebarTraitList);mainContent.appendChild(outputListHeader);mainContent.appendChild(outputList)}else{implementations=outputList;if(trait_implementations){mainContent.insertBefore(outputListHeader,trait_implementations_header);mainContent.insertBefore(outputList,trait_implementations_header)}else{const mainContent=document.querySelector("#main-content");mainContent.appendChild(outputListHeader);mainContent.appendChild(outputList)}}}const template=document.createElement("template");template.innerHTML=text;onEachLazy(template.content.querySelectorAll("a"),elem=>{const href=elem.getAttribute("href");if(href&&!href.startsWith("#")&&!/^(?:[a-z+]+:)?\/\//.test(href)){elem.setAttribute("href",window.rootPath+href)}});onEachLazy(template.content.querySelectorAll("[id]"),el=>{let i=0;if(idMap.has(el.id)){i=idMap.get(el.id)}else if(document.getElementById(el.id)){i=1;while(document.getElementById(`${el.id}-${2 * i}`)){i=2*i}while(document.getElementById(`${el.id}-${i}`)){i+=1}}if(i!==0){const oldHref=`#${el.id}`;const newHref=`#${el.id}-${i}`;el.id=`${el.id}-${i}`;onEachLazy(template.content.querySelectorAll("a[href]"),link=>{if(link.getAttribute("href")===oldHref){link.href=newHref}})}idMap.set(el.id,i+1)});const templateAssocItems=template.content.querySelectorAll("section.tymethod, "+"section.method, section.associatedtype, section.associatedconstant");if(isTrait){const li=document.createElement("li");const a=document.createElement("a");a.href=`#${template.content.querySelector(".impl").id}`;a.textContent=traitName;li.appendChild(a);sidebarTraitList.append(li)}else{onEachLazy(templateAssocItems,item=>{let block=hasClass(item,"associatedtype")?associatedTypes:(hasClass(item,"associatedconstant")?associatedConstants:(methods));if(!block){const blockTitle=hasClass(item,"associatedtype")?"Associated Types":(hasClass(item,"associatedconstant")?"Associated Constants":("Methods"));const blockClass=hasClass(item,"associatedtype")?"associatedtype":(hasClass(item,"associatedconstant")?"associatedconstant":("method"));const blockHeader=document.createElement("h3");const blockLink=document.createElement("a");blockLink.href="#implementations";blockLink.innerText=blockTitle;blockHeader.appendChild(blockLink);block=document.createElement("ul");block.className=`block ${blockClass}`;const insertionReference=methods||sidebarTraitList;if(insertionReference){const insertionReferenceH=insertionReference.previousElementSibling;sidebarSection.insertBefore(blockHeader,insertionReferenceH);sidebarSection.insertBefore(block,insertionReferenceH)}else{sidebarSection.appendChild(blockHeader);sidebarSection.appendChild(block)}if(hasClass(item,"associatedtype")){associatedTypes=block}else if(hasClass(item,"associatedconstant")){associatedConstants=block}else{methods=block}}const li=document.createElement("li");const a=document.createElement("a");a.innerText=item.id.split("-")[0].split(".")[1];a.href=`#${item.id}`;li.appendChild(a);block.appendChild(li)})}outputList.appendChild(template.content)}for(const list of[methods,associatedTypes,associatedConstants,sidebarTraitList]){if(!list){continue}const newChildren=Array.prototype.slice.call(list.children);newChildren.sort((a,b)=>{const aI=a.innerText;const bI=b.innerText;return aIbI?1:0});list.replaceChildren(...newChildren)}};if(window.pending_type_impls){window.register_type_impls(window.pending_type_impls)}function addSidebarCrates(){if(!window.ALL_CRATES){return}const sidebarElems=document.getElementsByClassName("sidebar-elems")[0];if(!sidebarElems){return}const h3=document.createElement("h3");h3.innerHTML="Crates";const ul=document.createElement("ul");ul.className="block crate";for(const crate of window.ALL_CRATES){const link=document.createElement("a");link.href=window.rootPath+crate+"/index.html";if(window.rootPath!=="./"&&crate===window.currentCrate){link.className="current"}link.textContent=crate;const li=document.createElement("li");li.appendChild(link);ul.appendChild(li)}sidebarElems.appendChild(h3);sidebarElems.appendChild(ul)}function expandAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);removeClass(innerToggle,"will-expand");onEachLazy(document.getElementsByClassName("toggle"),e=>{if(!hasClass(e,"type-contents-toggle")&&!hasClass(e,"more-examples-toggle")){e.open=true}});innerToggle.title="collapse all docs";innerToggle.children[0].innerText="\u2212"}function collapseAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);addClass(innerToggle,"will-expand");onEachLazy(document.getElementsByClassName("toggle"),e=>{if(e.parentNode.id!=="implementations-list"||(!hasClass(e,"implementors-toggle")&&!hasClass(e,"type-contents-toggle"))){e.open=false}});innerToggle.title="expand all docs";innerToggle.children[0].innerText="+"}function toggleAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);if(!innerToggle){return}if(hasClass(innerToggle,"will-expand")){expandAllDocs()}else{collapseAllDocs()}}(function(){const toggles=document.getElementById(toggleAllDocsId);if(toggles){toggles.onclick=toggleAllDocs}const hideMethodDocs=getSettingValue("auto-hide-method-docs")==="true";const hideImplementations=getSettingValue("auto-hide-trait-implementations")==="true";const hideLargeItemContents=getSettingValue("auto-hide-large-items")!=="false";function setImplementorsTogglesOpen(id,open){const list=document.getElementById(id);if(list!==null){onEachLazy(list.getElementsByClassName("implementors-toggle"),e=>{e.open=open})}}if(hideImplementations){setImplementorsTogglesOpen("trait-implementations-list",false);setImplementorsTogglesOpen("blanket-implementations-list",false)}onEachLazy(document.getElementsByClassName("toggle"),e=>{if(!hideLargeItemContents&&hasClass(e,"type-contents-toggle")){e.open=true}if(hideMethodDocs&&hasClass(e,"method-toggle")){e.open=false}})}());window.rustdoc_add_line_numbers_to_examples=()=>{onEachLazy(document.getElementsByClassName("rust-example-rendered"),x=>{const parent=x.parentNode;const line_numbers=parent.querySelectorAll(".example-line-numbers");if(line_numbers.length>0){return}const count=x.textContent.split("\n").length;const elems=[];for(let i=0;i{onEachLazy(document.getElementsByClassName("rust-example-rendered"),x=>{const parent=x.parentNode;const line_numbers=parent.querySelectorAll(".example-line-numbers");for(const node of line_numbers){parent.removeChild(node)}})};if(getSettingValue("line-numbers")==="true"){window.rustdoc_add_line_numbers_to_examples()}function showSidebar(){window.hideAllModals(false);const sidebar=document.getElementsByClassName("sidebar")[0];addClass(sidebar,"shown")}function hideSidebar(){const sidebar=document.getElementsByClassName("sidebar")[0];removeClass(sidebar,"shown")}window.addEventListener("resize",()=>{if(window.CURRENT_TOOLTIP_ELEMENT){const base=window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE;const force_visible=base.TOOLTIP_FORCE_VISIBLE;hideTooltip(false);if(force_visible){showTooltip(base);base.TOOLTIP_FORCE_VISIBLE=true}}});const mainElem=document.getElementById(MAIN_ID);if(mainElem){mainElem.addEventListener("click",hideSidebar)}onEachLazy(document.querySelectorAll("a[href^='#']"),el=>{el.addEventListener("click",()=>{expandSection(el.hash.slice(1));hideSidebar()})});onEachLazy(document.querySelectorAll(".toggle > summary:not(.hideme)"),el=>{el.addEventListener("click",e=>{if(e.target.tagName!=="SUMMARY"&&e.target.tagName!=="A"){e.preventDefault()}})});function showTooltip(e){const notable_ty=e.getAttribute("data-notable-ty");if(!window.NOTABLE_TRAITS&¬able_ty){const data=document.getElementById("notable-traits-data");if(data){window.NOTABLE_TRAITS=JSON.parse(data.innerText)}else{throw new Error("showTooltip() called with notable without any notable traits!")}}if(window.CURRENT_TOOLTIP_ELEMENT&&window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE===e){clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);return}window.hideAllModals(false);const wrapper=document.createElement("div");if(notable_ty){wrapper.innerHTML="
"+window.NOTABLE_TRAITS[notable_ty]+"
"}else{if(e.getAttribute("title")!==null){e.setAttribute("data-title",e.getAttribute("title"));e.removeAttribute("title")}if(e.getAttribute("data-title")!==null){const titleContent=document.createElement("div");titleContent.className="content";titleContent.appendChild(document.createTextNode(e.getAttribute("data-title")));wrapper.appendChild(titleContent)}}wrapper.className="tooltip popover";const focusCatcher=document.createElement("div");focusCatcher.setAttribute("tabindex","0");focusCatcher.onfocus=hideTooltip;wrapper.appendChild(focusCatcher);const pos=e.getBoundingClientRect();wrapper.style.top=(pos.top+window.scrollY+pos.height)+"px";wrapper.style.left=0;wrapper.style.right="auto";wrapper.style.visibility="hidden";const body=document.getElementsByTagName("body")[0];body.appendChild(wrapper);const wrapperPos=wrapper.getBoundingClientRect();const finalPos=pos.left+window.scrollX-wrapperPos.width+24;if(finalPos>0){wrapper.style.left=finalPos+"px"}else{wrapper.style.setProperty("--popover-arrow-offset",(wrapperPos.right-pos.right+4)+"px")}wrapper.style.visibility="";window.CURRENT_TOOLTIP_ELEMENT=wrapper;window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE=e;clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);wrapper.onpointerenter=ev=>{if(ev.pointerType!=="mouse"){return}clearTooltipHoverTimeout(e)};wrapper.onpointerleave=ev=>{if(ev.pointerType!=="mouse"){return}if(!e.TOOLTIP_FORCE_VISIBLE&&!elemIsInParent(ev.relatedTarget,e)){setTooltipHoverTimeout(e,false);addClass(wrapper,"fade-out")}}}function setTooltipHoverTimeout(element,show){clearTooltipHoverTimeout(element);if(!show&&!window.CURRENT_TOOLTIP_ELEMENT){return}if(show&&window.CURRENT_TOOLTIP_ELEMENT){return}if(window.CURRENT_TOOLTIP_ELEMENT&&window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE!==element){return}element.TOOLTIP_HOVER_TIMEOUT=setTimeout(()=>{if(show){showTooltip(element)}else if(!element.TOOLTIP_FORCE_VISIBLE){hideTooltip(false)}},show?window.RUSTDOC_TOOLTIP_HOVER_MS:window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS)}function clearTooltipHoverTimeout(element){if(element.TOOLTIP_HOVER_TIMEOUT!==undefined){removeClass(window.CURRENT_TOOLTIP_ELEMENT,"fade-out");clearTimeout(element.TOOLTIP_HOVER_TIMEOUT);delete element.TOOLTIP_HOVER_TIMEOUT}}function tooltipBlurHandler(event){if(window.CURRENT_TOOLTIP_ELEMENT&&!elemIsInParent(document.activeElement,window.CURRENT_TOOLTIP_ELEMENT)&&!elemIsInParent(event.relatedTarget,window.CURRENT_TOOLTIP_ELEMENT)&&!elemIsInParent(document.activeElement,window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE)&&!elemIsInParent(event.relatedTarget,window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE)){setTimeout(()=>hideTooltip(false),0)}}function hideTooltip(focus){if(window.CURRENT_TOOLTIP_ELEMENT){if(window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE){if(focus){window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.focus()}window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE=false}const body=document.getElementsByTagName("body")[0];body.removeChild(window.CURRENT_TOOLTIP_ELEMENT);clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);window.CURRENT_TOOLTIP_ELEMENT=null}}onEachLazy(document.getElementsByClassName("tooltip"),e=>{e.onclick=()=>{e.TOOLTIP_FORCE_VISIBLE=e.TOOLTIP_FORCE_VISIBLE?false:true;if(window.CURRENT_TOOLTIP_ELEMENT&&!e.TOOLTIP_FORCE_VISIBLE){hideTooltip(true)}else{showTooltip(e);window.CURRENT_TOOLTIP_ELEMENT.setAttribute("tabindex","0");window.CURRENT_TOOLTIP_ELEMENT.focus();window.CURRENT_TOOLTIP_ELEMENT.onblur=tooltipBlurHandler}return false};e.onpointerenter=ev=>{if(ev.pointerType!=="mouse"){return}setTooltipHoverTimeout(e,true)};e.onpointermove=ev=>{if(ev.pointerType!=="mouse"){return}setTooltipHoverTimeout(e,true)};e.onpointerleave=ev=>{if(ev.pointerType!=="mouse"){return}if(!e.TOOLTIP_FORCE_VISIBLE&&!elemIsInParent(ev.relatedTarget,window.CURRENT_TOOLTIP_ELEMENT)){setTooltipHoverTimeout(e,false);addClass(window.CURRENT_TOOLTIP_ELEMENT,"fade-out")}}});const sidebar_menu_toggle=document.getElementsByClassName("sidebar-menu-toggle")[0];if(sidebar_menu_toggle){sidebar_menu_toggle.addEventListener("click",()=>{const sidebar=document.getElementsByClassName("sidebar")[0];if(!hasClass(sidebar,"shown")){showSidebar()}else{hideSidebar()}})}function helpBlurHandler(event){blurHandler(event,getHelpButton(),window.hidePopoverMenus)}function buildHelpMenu(){const book_info=document.createElement("span");const channel=getVar("channel");book_info.className="top";book_info.innerHTML=`You can find more information in \ +the rustdoc book.`;const shortcuts=[["?","Show this help dialog"],["S","Focus the search field"],["↑","Move up in search results"],["↓","Move down in search results"],["← / →","Switch result tab (when results focused)"],["⏎","Go to active search result"],["+","Expand all sections"],["-","Collapse all sections"],].map(x=>"
"+x[0].split(" ").map((y,index)=>((index&1)===0?""+y+"":" "+y+" ")).join("")+"
"+x[1]+"
").join("");const div_shortcuts=document.createElement("div");addClass(div_shortcuts,"shortcuts");div_shortcuts.innerHTML="

Keyboard Shortcuts

"+shortcuts+"
";const infos=[`For a full list of all search features, take a look here.`,"Prefix searches with a type followed by a colon (e.g., fn:) to \ + restrict the search to a given item kind.","Accepted kinds are: fn, mod, struct, \ + enum, trait, type, macro, \ + and const.","Search functions by type signature (e.g., vec -> usize or \ + -> vec or String, enum:Cow -> bool)","You can look for items with an exact name by putting double quotes around \ + your request: \"string\"","Look for functions that accept or return \ + slices and \ + arrays by writing \ + square brackets (e.g., -> [u8] or [] -> Option)","Look for items inside another one by searching for a path: vec::Vec",].map(x=>"

"+x+"

").join("");const div_infos=document.createElement("div");addClass(div_infos,"infos");div_infos.innerHTML="

Search Tricks

"+infos;const rustdoc_version=document.createElement("span");rustdoc_version.className="bottom";const rustdoc_version_code=document.createElement("code");rustdoc_version_code.innerText="rustdoc "+getVar("rustdoc-version");rustdoc_version.appendChild(rustdoc_version_code);const container=document.createElement("div");if(!isHelpPage){container.className="popover"}container.id="help";container.style.display="none";const side_by_side=document.createElement("div");side_by_side.className="side-by-side";side_by_side.appendChild(div_shortcuts);side_by_side.appendChild(div_infos);container.appendChild(book_info);container.appendChild(side_by_side);container.appendChild(rustdoc_version);if(isHelpPage){const help_section=document.createElement("section");help_section.appendChild(container);document.getElementById("main-content").appendChild(help_section);container.style.display="block"}else{const help_button=getHelpButton();help_button.appendChild(container);container.onblur=helpBlurHandler;help_button.onblur=helpBlurHandler;help_button.children[0].onblur=helpBlurHandler}return container}window.hideAllModals=switchFocus=>{hideSidebar();window.hidePopoverMenus();hideTooltip(switchFocus)};window.hidePopoverMenus=()=>{onEachLazy(document.querySelectorAll(".search-form .popover"),elem=>{elem.style.display="none"})};function getHelpMenu(buildNeeded){let menu=getHelpButton().querySelector(".popover");if(!menu&&buildNeeded){menu=buildHelpMenu()}return menu}function showHelp(){getHelpButton().querySelector("a").focus();const menu=getHelpMenu(true);if(menu.style.display==="none"){window.hideAllModals();menu.style.display=""}}if(isHelpPage){showHelp();document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click",event=>{const target=event.target;if(target.tagName!=="A"||target.parentElement.id!==HELP_BUTTON_ID||event.ctrlKey||event.altKey||event.metaKey){return}event.preventDefault()})}else{document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click",event=>{const target=event.target;if(target.tagName!=="A"||target.parentElement.id!==HELP_BUTTON_ID||event.ctrlKey||event.altKey||event.metaKey){return}event.preventDefault();const menu=getHelpMenu(true);const shouldShowHelp=menu.style.display==="none";if(shouldShowHelp){showHelp()}else{window.hidePopoverMenus()}})}setMobileTopbar();addSidebarItems();addSidebarCrates();onHashChange(null);window.addEventListener("hashchange",onHashChange);searchState.setup()}());(function(){let reset_button_timeout=null;const but=document.getElementById("copy-path");if(!but){return}but.onclick=()=>{const parent=but.parentElement;const path=[];onEach(parent.childNodes,child=>{if(child.tagName==="A"){path.push(child.textContent)}});const el=document.createElement("textarea");el.value=path.join("::");el.setAttribute("readonly","");el.style.position="absolute";el.style.left="-9999px";document.body.appendChild(el);el.select();document.execCommand("copy");document.body.removeChild(el);but.children[0].style.display="none";let tmp;if(but.childNodes.length<2){tmp=document.createTextNode("✓");but.appendChild(tmp)}else{onEachLazy(but.childNodes,e=>{if(e.nodeType===Node.TEXT_NODE){tmp=e;return true}});tmp.textContent="✓"}if(reset_button_timeout!==null){window.clearTimeout(reset_button_timeout)}function reset_button(){tmp.textContent="";reset_button_timeout=null;but.children[0].style.display=""}reset_button_timeout=window.setTimeout(reset_button,1000)}}()) \ No newline at end of file diff --git a/docs/macos_docs/static.files/normalize-76eba96aa4d2e634.css b/docs/macos_docs/static.files/normalize-76eba96aa4d2e634.css new file mode 100644 index 00000000..469959f1 --- /dev/null +++ b/docs/macos_docs/static.files/normalize-76eba96aa4d2e634.css @@ -0,0 +1,2 @@ + /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ +html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:0.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type="button"],[type="reset"],[type="submit"],button{-webkit-appearance:button}[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:0.35em 0.75em 0.625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none} \ No newline at end of file diff --git a/docs/macos_docs/static.files/noscript-5d8b3c7633ad77ba.css b/docs/macos_docs/static.files/noscript-5d8b3c7633ad77ba.css new file mode 100644 index 00000000..8c63ef06 --- /dev/null +++ b/docs/macos_docs/static.files/noscript-5d8b3c7633ad77ba.css @@ -0,0 +1 @@ + #main-content .attributes{margin-left:0 !important;}#copy-path{display:none;}nav.sub{display:none;}.src .sidebar{display:none;}.notable-traits{display:none;}:root{--main-background-color:white;--main-color:black;--settings-input-color:#2196f3;--settings-input-border-color:#717171;--settings-button-color:#000;--settings-button-border-focus:#717171;--sidebar-background-color:#f5f5f5;--sidebar-background-color-hover:#e0e0e0;--code-block-background-color:#f5f5f5;--scrollbar-track-background-color:#dcdcdc;--scrollbar-thumb-background-color:rgba(36,37,39,0.6);--scrollbar-color:rgba(36,37,39,0.6) #d9d9d9;--headings-border-bottom-color:#ddd;--border-color:#e0e0e0;--button-background-color:#fff;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:none;--search-input-focused-border-color:#66afe9;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(35%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ad378a;--trait-link-color:#6e4fc9;--assoc-item-link-color:#3873ad;--function-link-color:#ad7c37;--macro-link-color:#068000;--keyword-link-color:#3873ad;--mod-link-color:#3873ad;--link-color:#3873ad;--sidebar-link-color:#356da4;--sidebar-current-link-background-color:#fff;--search-result-link-focus-background-color:#ccc;--search-result-border-color:#aaa3;--search-color:#000;--search-error-code-background-color:#d0cccc;--search-results-alias-color:#000;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#e6e6e6;--search-tab-button-not-selected-background:#e6e6e6;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#fff;--stab-background-color:#fff5d6;--stab-code-color:#000;--code-highlight-kw-color:#8959a8;--code-highlight-kw-2-color:#4271ae;--code-highlight-lifetime-color:#b76514;--code-highlight-prelude-color:#4271ae;--code-highlight-prelude-val-color:#c82829;--code-highlight-number-color:#718c00;--code-highlight-string-color:#718c00;--code-highlight-literal-color:#c82829;--code-highlight-attribute-color:#c82829;--code-highlight-self-color:#c82829;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8e908c;--code-highlight-doc-comment-color:#4d4d4c;--src-line-numbers-span-color:#c67e2d;--src-line-number-highlighted-background-color:#fdffd3;--test-arrow-color:#f5f5f5;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#f5f5f5;--test-arrow-hover-background-color:rgb(78,139,202);--target-background-color:#fdffd3;--target-border-color:#ad7c37;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:initial;--crate-search-div-filter:invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) brightness(114%) contrast(76%);--crate-search-div-hover-filter:invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) brightness(96%) contrast(93%);--crate-search-hover-border:#717171;--src-sidebar-background-selected:#fff;--src-sidebar-background-hover:#e0e0e0;--table-alt-row-background-color:#f5f5f5;--codeblock-link-background:#eee;--scrape-example-toggle-line-background:#ccc;--scrape-example-toggle-line-hover-background:#999;--scrape-example-code-line-highlight:#fcffd6;--scrape-example-code-line-highlight-focus:#f6fdb0;--scrape-example-help-border-color:#555;--scrape-example-help-color:#333;--scrape-example-help-hover-border-color:#000;--scrape-example-help-hover-color:#000;--scrape-example-code-wrapper-background-start:rgba(255,255,255,1);--scrape-example-code-wrapper-background-end:rgba(255,255,255,0);}@media (prefers-color-scheme:dark){:root{--main-background-color:#353535;--main-color:#ddd;--settings-input-color:#2196f3;--settings-input-border-color:#999;--settings-button-color:#000;--settings-button-border-focus:#ffb900;--sidebar-background-color:#505050;--sidebar-background-color-hover:#676767;--code-block-background-color:#2A2A2A;--scrollbar-track-background-color:#717171;--scrollbar-thumb-background-color:rgba(32,34,37,.6);--scrollbar-color:rgba(32,34,37,.6) #5a5a5a;--headings-border-bottom-color:#d2d2d2;--border-color:#e0e0e0;--button-background-color:#f0f0f0;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--search-input-focused-border-color:#008dfd;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(65%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#2dbfb8;--trait-link-color:#b78cf2;--assoc-item-link-color:#d2991d;--function-link-color:#2bab63;--macro-link-color:#09bd00;--keyword-link-color:#d2991d;--mod-link-color:#d2991d;--link-color:#d2991d;--sidebar-link-color:#fdbf35;--sidebar-current-link-background-color:#444;--search-result-link-focus-background-color:#616161;--search-result-border-color:#aaa3;--search-color:#111;--search-error-code-background-color:#484848;--search-results-alias-color:#fff;--search-results-grey-color:#ccc;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#252525;--search-tab-button-not-selected-background:#252525;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#353535;--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ab8ac1;--code-highlight-kw-2-color:#769acb;--code-highlight-lifetime-color:#d97f26;--code-highlight-prelude-color:#769acb;--code-highlight-prelude-val-color:#ee6868;--code-highlight-number-color:#83a300;--code-highlight-string-color:#83a300;--code-highlight-literal-color:#ee6868;--code-highlight-attribute-color:#ee6868;--code-highlight-self-color:#ee6868;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8d8d8b;--code-highlight-doc-comment-color:#8ca375;--src-line-numbers-span-color:#3b91e2;--src-line-number-highlighted-background-color:#0a042f;--test-arrow-color:#dedede;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#dedede;--test-arrow-hover-background-color:#4e8bca;--target-background-color:#494a3d;--target-border-color:#bb7410;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) brightness(90%) contrast(90%);--crate-search-div-hover-filter:invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) brightness(100%) contrast(91%);--crate-search-hover-border:#2196f3;--src-sidebar-background-selected:#333;--src-sidebar-background-hover:#444;--table-alt-row-background-color:#2a2a2a;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(53,53,53,1);--scrape-example-code-wrapper-background-end:rgba(53,53,53,0);}} \ No newline at end of file diff --git a/docs/macos_docs/static.files/rust-logo-151179464ae7ed46.svg b/docs/macos_docs/static.files/rust-logo-151179464ae7ed46.svg new file mode 100644 index 00000000..62424d8f --- /dev/null +++ b/docs/macos_docs/static.files/rust-logo-151179464ae7ed46.svg @@ -0,0 +1,61 @@ + + + diff --git a/docs/macos_docs/static.files/rustdoc-9ee3a5e31a2afa3e.css b/docs/macos_docs/static.files/rustdoc-9ee3a5e31a2afa3e.css new file mode 100644 index 00000000..8749d0eb --- /dev/null +++ b/docs/macos_docs/static.files/rustdoc-9ee3a5e31a2afa3e.css @@ -0,0 +1,10 @@ + :root{--nav-sub-mobile-padding:8px;--search-typename-width:6.75rem;}@font-face {font-family:'Fira Sans';font-style:normal;font-weight:400;src:local('Fira Sans'),url("FiraSans-Regular-018c141bf0843ffd.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Fira Sans';font-style:normal;font-weight:500;src:local('Fira Sans Medium'),url("FiraSans-Medium-8f9a781e4970d388.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:normal;font-weight:400;src:local('Source Serif 4'),url("SourceSerif4-Regular-46f98efaafac5295.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:italic;font-weight:400;src:local('Source Serif 4 Italic'),url("SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:normal;font-weight:700;src:local('Source Serif 4 Bold'),url("SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:normal;font-weight:400;src:url("SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:italic;font-weight:400;src:url("SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:normal;font-weight:600;src:url("SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'NanumBarunGothic';src:url("NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2") format("woff2");font-display:swap;unicode-range:U+AC00-D7AF,U+1100-11FF,U+3130-318F,U+A960-A97F,U+D7B0-D7FF;}*{box-sizing:border-box;}body{font:1rem/1.5 "Source Serif 4",NanumBarunGothic,serif;margin:0;position:relative;overflow-wrap:break-word;overflow-wrap:anywhere;font-feature-settings:"kern","liga";background-color:var(--main-background-color);color:var(--main-color);}h1{font-size:1.5rem;}h2{font-size:1.375rem;}h3{font-size:1.25rem;}h1,h2,h3,h4,h5,h6{font-weight:500;}h1,h2,h3,h4{margin:25px 0 15px 0;padding-bottom:6px;}.docblock h3,.docblock h4,h5,h6{margin:15px 0 5px 0;}.docblock>h2:first-child,.docblock>h3:first-child,.docblock>h4:first-child,.docblock>h5:first-child,.docblock>h6:first-child{margin-top:0;}.main-heading h1{margin:0;padding:0;flex-grow:1;overflow-wrap:break-word;overflow-wrap:anywhere;}.main-heading{display:flex;flex-wrap:wrap;padding-bottom:6px;margin-bottom:15px;}.content h2,.top-doc .docblock>h3,.top-doc .docblock>h4{border-bottom:1px solid var(--headings-border-bottom-color);}h1,h2{line-height:1.25;padding-top:3px;padding-bottom:9px;}h3.code-header{font-size:1.125rem;}h4.code-header{font-size:1rem;}.code-header{font-weight:600;margin:0;padding:0;white-space:pre-wrap;}#crate-search,h1,h2,h3,h4,h5,h6,.sidebar,.mobile-topbar,.search-input,.search-results .result-name,.item-name>a,.out-of-band,span.since,a.src,#help-button>a,summary.hideme,.scraped-example-list,ul.all-items{font-family:"Fira Sans",Arial,NanumBarunGothic,sans-serif;}#toggle-all-docs,a.anchor,.small-section-header a,#src-sidebar a,.rust a,.sidebar h2 a,.sidebar h3 a,.mobile-topbar h2 a,h1 a,.search-results a,.stab,.result-name i{color:var(--main-color);}span.enum,a.enum,span.struct,a.struct,span.union,a.union,span.primitive,a.primitive,span.type,a.type,span.foreigntype,a.foreigntype{color:var(--type-link-color);}span.trait,a.trait,span.traitalias,a.traitalias{color:var(--trait-link-color);}span.associatedtype,a.associatedtype,span.constant,a.constant,span.static,a.static{color:var(--assoc-item-link-color);}span.fn,a.fn,span.method,a.method,span.tymethod,a.tymethod{color:var(--function-link-color);}span.attr,a.attr,span.derive,a.derive,span.macro,a.macro{color:var(--macro-link-color);}span.mod,a.mod{color:var(--mod-link-color);}span.keyword,a.keyword{color:var(--keyword-link-color);}a{color:var(--link-color);text-decoration:none;}ol,ul{padding-left:24px;}ul ul,ol ul,ul ol,ol ol{margin-bottom:.625em;}p,.docblock>.warning{margin:0 0 .75em 0;}p:last-child,.docblock>.warning:last-child{margin:0;}button{padding:1px 6px;cursor:pointer;}button#toggle-all-docs{padding:0;background:none;border:none;-webkit-appearance:none;opacity:1;}.rustdoc{display:flex;flex-direction:row;flex-wrap:nowrap;}main{position:relative;flex-grow:1;padding:10px 15px 40px 45px;min-width:0;}.src main{padding:15px;}.width-limiter{max-width:960px;margin-right:auto;}details:not(.toggle) summary{margin-bottom:.6em;}code,pre,a.test-arrow,.code-header{font-family:"Source Code Pro",monospace;}.docblock code,.docblock-short code{border-radius:3px;padding:0 0.125em;}.docblock pre code,.docblock-short pre code{padding:0;}pre{padding:14px;line-height:1.5;}pre.item-decl{overflow-x:auto;}.item-decl .type-contents-toggle{contain:initial;}.src .content pre{padding:20px;}.rustdoc.src .example-wrap pre.src-line-numbers{padding:20px 0 20px 4px;}img{max-width:100%;}.sub-logo-container,.logo-container{line-height:0;display:block;}.sub-logo-container{margin-right:32px;}.sub-logo-container>img{height:60px;width:60px;object-fit:contain;}.rust-logo{filter:var(--rust-logo-filter);}.sidebar{font-size:0.875rem;flex:0 0 200px;overflow-y:scroll;overscroll-behavior:contain;position:sticky;height:100vh;top:0;left:0;}.rustdoc.src .sidebar{flex-basis:50px;border-right:1px solid;overflow-x:hidden;overflow-y:hidden;z-index:1;}.sidebar,.mobile-topbar,.sidebar-menu-toggle,#src-sidebar-toggle,#src-sidebar{background-color:var(--sidebar-background-color);}#src-sidebar-toggle>button:hover,#src-sidebar-toggle>button:focus{background-color:var(--sidebar-background-color-hover);}.src .sidebar>*:not(#src-sidebar-toggle){visibility:hidden;}.src-sidebar-expanded .src .sidebar{overflow-y:auto;flex-basis:300px;}.src-sidebar-expanded .src .sidebar>*:not(#src-sidebar-toggle){visibility:visible;}#all-types{margin-top:1em;}*{scrollbar-width:initial;scrollbar-color:var(--scrollbar-color);}.sidebar{scrollbar-width:thin;scrollbar-color:var(--scrollbar-color);}::-webkit-scrollbar{width:12px;}.sidebar::-webkit-scrollbar{width:8px;}::-webkit-scrollbar-track{-webkit-box-shadow:inset 0;background-color:var(--scrollbar-track-background-color);}.sidebar::-webkit-scrollbar-track{background-color:var(--scrollbar-track-background-color);}::-webkit-scrollbar-thumb,.sidebar::-webkit-scrollbar-thumb{background-color:var(--scrollbar-thumb-background-color);}.hidden{display:none !important;}.logo-container>img{height:48px;width:48px;}ul.block,.block li{padding:0;margin:0;list-style:none;}.sidebar-elems a,.sidebar>h2 a{display:block;padding:0.25rem;margin-left:-0.25rem;}.sidebar h2{overflow-wrap:anywhere;padding:0;margin:0.7rem 0;}.sidebar h3{font-size:1.125rem;padding:0;margin:0;}.sidebar-elems,.sidebar>.version,.sidebar>h2{padding-left:24px;}.sidebar a{color:var(--sidebar-link-color);}.sidebar .current,.sidebar .current a,.sidebar-crate a.logo-container:hover+h2 a,.sidebar a:hover:not(.logo-container){background-color:var(--sidebar-current-link-background-color);}.sidebar-elems .block{margin-bottom:2em;}.sidebar-elems .block li a{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;}.sidebar-crate{display:flex;align-items:center;justify-content:center;margin:14px 32px 1rem;row-gap:10px;column-gap:32px;flex-wrap:wrap;}.sidebar-crate h2{flex-grow:1;margin:0 -8px;align-self:start;}.sidebar-crate .logo-container{margin:0 -16px 0 -16px;text-align:center;}.sidebar-crate h2 a{display:block;margin:0 calc(-24px + 0.25rem) 0 -0.5rem;padding:calc((16px - 0.57rem ) / 2 ) 0.25rem;padding-left:0.5rem;}.sidebar-crate h2 .version{display:block;font-weight:normal;font-size:1rem;overflow-wrap:break-word;margin-top:calc((-16px + 0.57rem ) / 2 );}.sidebar-crate+.version{margin-top:-1rem;margin-bottom:1rem;}.mobile-topbar{display:none;}.rustdoc .example-wrap{display:flex;position:relative;margin-bottom:10px;}.rustdoc .example-wrap:last-child{margin-bottom:0px;}.rustdoc .example-wrap pre{margin:0;flex-grow:1;}.rustdoc:not(.src) .example-wrap pre{overflow:auto hidden;}.rustdoc .example-wrap pre.example-line-numbers,.rustdoc .example-wrap pre.src-line-numbers{flex-grow:0;min-width:fit-content;overflow:initial;text-align:right;-webkit-user-select:none;user-select:none;padding:14px 8px;color:var(--src-line-numbers-span-color);}.rustdoc .example-wrap pre.src-line-numbers{padding:14px 0;}.src-line-numbers a,.src-line-numbers span{color:var(--src-line-numbers-span-color);padding:0 8px;}.src-line-numbers :target{background-color:transparent;border-right:none;padding:0 8px;}.src-line-numbers .line-highlighted{background-color:var(--src-line-number-highlighted-background-color);}.search-loading{text-align:center;}.docblock-short{overflow-wrap:break-word;overflow-wrap:anywhere;}.docblock :not(pre)>code,.docblock-short code{white-space:pre-wrap;}.top-doc .docblock h2{font-size:1.375rem;}.top-doc .docblock h3{font-size:1.25rem;}.top-doc .docblock h4,.top-doc .docblock h5{font-size:1.125rem;}.top-doc .docblock h6{font-size:1rem;}.docblock h5{font-size:1rem;}.docblock h6{font-size:0.875rem;}.docblock{margin-left:24px;position:relative;}.docblock>:not(.more-examples-toggle):not(.example-wrap){max-width:100%;overflow-x:auto;}.out-of-band{flex-grow:0;font-size:1.125rem;}.docblock code,.docblock-short code,pre,.rustdoc.src .example-wrap{background-color:var(--code-block-background-color);}#main-content{position:relative;}.docblock table{margin:.5em 0;border-collapse:collapse;}.docblock table td,.docblock table th{padding:.5em;border:1px solid var(--border-color);}.docblock table tbody tr:nth-child(2n){background:var(--table-alt-row-background-color);}.method .where,.fn .where,.where.fmt-newline{display:block;white-space:pre-wrap;font-size:0.875rem;}.item-info{display:block;margin-left:24px;}.item-info code{font-size:0.875rem;}#main-content>.item-info{margin-left:0;}nav.sub{flex-grow:1;flex-flow:row nowrap;margin:4px 0 25px 0;display:flex;align-items:center;}.search-form{position:relative;display:flex;height:34px;flex-grow:1;}.src nav.sub{margin:0 0 15px 0;}.small-section-header{display:block;position:relative;}.small-section-header:hover>.anchor,.impl:hover>.anchor,.trait-impl:hover>.anchor,.variant:hover>.anchor{display:initial;}.anchor{display:none;position:absolute;left:-0.5em;background:none !important;}.anchor.field{left:-5px;}.small-section-header>.anchor{left:-15px;padding-right:8px;}h2.small-section-header>.anchor{padding-right:6px;}.main-heading a:hover,.example-wrap .rust a:hover,.all-items a:hover,.docblock a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover,.docblock-short a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover,.item-info a{text-decoration:underline;}.crate.block a.current{font-weight:500;}table,.item-table{overflow-wrap:break-word;}.item-table{display:table;padding:0;margin:0;}.item-table>li{display:table-row;}.item-table>li>div{display:table-cell;}.item-table>li>.item-name{padding-right:1.25rem;}.search-results-title{margin-top:0;white-space:nowrap;display:flex;align-items:baseline;}#crate-search-div{position:relative;min-width:5em;}#crate-search{min-width:115px;padding:0 23px 0 4px;max-width:100%;text-overflow:ellipsis;border:1px solid var(--border-color);border-radius:4px;outline:none;cursor:pointer;-moz-appearance:none;-webkit-appearance:none;text-indent:0.01px;background-color:var(--main-background-color);color:inherit;line-height:1.5;font-weight:500;}#crate-search:hover,#crate-search:focus{border-color:var(--crate-search-hover-border);}#crate-search-div::after{pointer-events:none;width:100%;height:100%;position:absolute;top:0;left:0;content:"";background-repeat:no-repeat;background-size:20px;background-position:calc(100% - 2px) 56%;background-image:url('data:image/svg+xml, \ + ');filter:var(--crate-search-div-filter);}#crate-search-div:hover::after,#crate-search-div:focus-within::after{filter:var(--crate-search-div-hover-filter);}#crate-search>option{font-size:1rem;}.search-input{-webkit-appearance:none;outline:none;border:1px solid var(--border-color);border-radius:2px;padding:8px;font-size:1rem;flex-grow:1;background-color:var(--button-background-color);color:var(--search-color);}.search-input:focus{border-color:var(--search-input-focused-border-color);}.search-results{display:none;}.search-results.active{display:block;}.search-results>a{display:flex;margin-left:2px;margin-right:2px;border-bottom:1px solid var(--search-result-border-color);gap:1em;}.search-results>a>div.desc{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;flex:2;}.search-results a:hover,.search-results a:focus{background-color:var(--search-result-link-focus-background-color);}.search-results .result-name{display:flex;align-items:center;justify-content:start;flex:3;}.search-results .result-name .alias{color:var(--search-results-alias-color);}.search-results .result-name .grey{color:var(--search-results-grey-color);}.search-results .result-name .typename{color:var(--search-results-grey-color);font-size:0.875rem;width:var(--search-typename-width);}.search-results .result-name .path{word-break:break-all;max-width:calc(100% - var(--search-typename-width));display:inline-block;}.search-results .result-name .path>*{display:inline;}.popover{position:absolute;top:100%;right:0;z-index:2;margin-top:7px;border-radius:3px;border:1px solid var(--border-color);background-color:var(--main-background-color);color:var(--main-color);--popover-arrow-offset:11px;}.popover::before{content:'';position:absolute;right:var(--popover-arrow-offset);border:solid var(--border-color);border-width:1px 1px 0 0;background-color:var(--main-background-color);padding:4px;transform:rotate(-45deg);top:-5px;}.setting-line{margin:1.2em 0.6em;}.setting-radio input,.setting-check input{margin-right:0.3em;height:1.2rem;width:1.2rem;border:2px solid var(--settings-input-border-color);outline:none;-webkit-appearance:none;cursor:pointer;}.setting-radio input{border-radius:50%;}.setting-radio span,.setting-check span{padding-bottom:1px;}.setting-radio{margin-top:0.1em;margin-bottom:0.1em;min-width:3.8em;padding:0.3em;display:inline-flex;align-items:center;cursor:pointer;}.setting-radio+.setting-radio{margin-left:0.5em;}.setting-check{margin-right:20px;display:flex;align-items:center;cursor:pointer;}.setting-radio input:checked{box-shadow:inset 0 0 0 3px var(--main-background-color);background-color:var(--settings-input-color);}.setting-check input:checked{background-color:var(--settings-input-color);border-width:1px;content:url('data:image/svg+xml,\ + \ + ');}.setting-radio input:focus,.setting-check input:focus{box-shadow:0 0 1px 1px var(--settings-input-color);}.setting-radio input:checked:focus{box-shadow:inset 0 0 0 3px var(--main-background-color),0 0 2px 2px var(--settings-input-color);}.setting-radio input:hover,.setting-check input:hover{border-color:var(--settings-input-color) !important;}#help.popover{max-width:600px;--popover-arrow-offset:48px;}#help dt{float:left;clear:left;margin-right:0.5rem;}#help span.top,#help span.bottom{text-align:center;display:block;font-size:1.125rem;}#help span.top{margin:10px 0;border-bottom:1px solid var(--border-color);padding-bottom:4px;margin-bottom:6px;}#help span.bottom{clear:both;border-top:1px solid var(--border-color);}.side-by-side>div{width:50%;float:left;padding:0 20px 20px 17px;}.item-info .stab{min-height:36px;display:flex;padding:3px;margin-bottom:5px;align-items:center;vertical-align:text-bottom;}.item-name .stab{margin-left:0.3125em;}.stab{padding:0 2px;font-size:0.875rem;font-weight:normal;color:var(--main-color);background-color:var(--stab-background-color);width:fit-content;white-space:pre-wrap;border-radius:3px;display:inline;vertical-align:baseline;}.stab.portability>code{background:none;color:var(--stab-code-color);}.stab .emoji{font-size:1.25rem;margin-right:0.3rem;}.emoji{text-shadow:1px 0 0 black,-1px 0 0 black,0 1px 0 black,0 -1px 0 black;}.since{font-weight:normal;font-size:initial;}.rightside{padding-left:12px;float:right;}.rightside:not(a),.out-of-band{color:var(--right-side-color);}pre.rust{tab-size:4;-moz-tab-size:4;}pre.rust .kw{color:var(--code-highlight-kw-color);}pre.rust .kw-2{color:var(--code-highlight-kw-2-color);}pre.rust .lifetime{color:var(--code-highlight-lifetime-color);}pre.rust .prelude-ty{color:var(--code-highlight-prelude-color);}pre.rust .prelude-val{color:var(--code-highlight-prelude-val-color);}pre.rust .string{color:var(--code-highlight-string-color);}pre.rust .number{color:var(--code-highlight-number-color);}pre.rust .bool-val{color:var(--code-highlight-literal-color);}pre.rust .self{color:var(--code-highlight-self-color);}pre.rust .attr{color:var(--code-highlight-attribute-color);}pre.rust .macro,pre.rust .macro-nonterminal{color:var(--code-highlight-macro-color);}pre.rust .question-mark{font-weight:bold;color:var(--code-highlight-question-mark-color);}pre.rust .comment{color:var(--code-highlight-comment-color);}pre.rust .doccomment{color:var(--code-highlight-doc-comment-color);}.rustdoc.src .example-wrap pre.rust a{background:var(--codeblock-link-background);}.example-wrap.compile_fail,.example-wrap.should_panic{border-left:2px solid var(--codeblock-error-color);}.ignore.example-wrap{border-left:2px solid var(--codeblock-ignore-color);}.example-wrap.compile_fail:hover,.example-wrap.should_panic:hover{border-left:2px solid var(--codeblock-error-hover-color);}.example-wrap.ignore:hover{border-left:2px solid var(--codeblock-ignore-hover-color);}.example-wrap.compile_fail .tooltip,.example-wrap.should_panic .tooltip{color:var(--codeblock-error-color);}.example-wrap.ignore .tooltip{color:var(--codeblock-ignore-color);}.example-wrap.compile_fail:hover .tooltip,.example-wrap.should_panic:hover .tooltip{color:var(--codeblock-error-hover-color);}.example-wrap.ignore:hover .tooltip{color:var(--codeblock-ignore-hover-color);}.example-wrap .tooltip{position:absolute;display:block;left:-25px;top:5px;margin:0;line-height:1;}.example-wrap.compile_fail .tooltip,.example-wrap.should_panic .tooltip,.example-wrap.ignore .tooltip{font-weight:bold;font-size:1.25rem;}.content .docblock .warning{border-left:2px solid var(--warning-border-color);padding:14px;position:relative;overflow-x:visible !important;}.content .docblock .warning::before{color:var(--warning-border-color);content:"ⓘ";position:absolute;left:-25px;top:5px;font-weight:bold;font-size:1.25rem;}a.test-arrow{visibility:hidden;position:absolute;padding:5px 10px 5px 10px;border-radius:5px;font-size:1.375rem;top:5px;right:5px;z-index:1;color:var(--test-arrow-color);background-color:var(--test-arrow-background-color);}a.test-arrow:hover{color:var(--test-arrow-hover-color);background-color:var(--test-arrow-hover-background-color);}.example-wrap:hover .test-arrow{visibility:visible;}.code-attribute{font-weight:300;color:var(--code-attribute-color);}.item-spacer{width:100%;height:12px;display:block;}.out-of-band>span.since{font-size:1.25rem;}.sub-variant h4{font-size:1rem;font-weight:400;margin-top:0;margin-bottom:0;}.sub-variant{margin-left:24px;margin-bottom:40px;}.sub-variant>.sub-variant-field{margin-left:24px;}:target{padding-right:3px;background-color:var(--target-background-color);border-right:3px solid var(--target-border-color);}.code-header a.tooltip{color:inherit;margin-right:15px;position:relative;}.code-header a.tooltip:hover{color:var(--link-color);}a.tooltip:hover::after{position:absolute;top:calc(100% - 10px);left:-15px;right:-15px;height:20px;content:"\00a0";}.fade-out{opacity:0;transition:opacity 0.45s cubic-bezier(0,0,0.1,1.0);}.popover.tooltip .content{margin:0.25em 0.5em;}.popover.tooltip .content pre,.popover.tooltip .content code{background:transparent;margin:0;padding:0;font-size:1.25rem;white-space:pre-wrap;}.popover.tooltip .content>h3:first-child{margin:0 0 5px 0;}.search-failed{text-align:center;margin-top:20px;display:none;}.search-failed.active{display:block;}.search-failed>ul{text-align:left;max-width:570px;margin-left:auto;margin-right:auto;}#search-tabs{display:flex;flex-direction:row;gap:1px;margin-bottom:4px;}#search-tabs button{text-align:center;font-size:1.125rem;border:0;border-top:2px solid;flex:1;line-height:1.5;color:inherit;}#search-tabs button:not(.selected){background-color:var(--search-tab-button-not-selected-background);border-top-color:var(--search-tab-button-not-selected-border-top-color);}#search-tabs button:hover,#search-tabs button.selected{background-color:var(--search-tab-button-selected-background);border-top-color:var(--search-tab-button-selected-border-top-color);}#search-tabs .count{font-size:1rem;font-variant-numeric:tabular-nums;color:var(--search-tab-title-count-color);}#search .error code{border-radius:3px;background-color:var(--search-error-code-background-color);}.search-corrections{font-weight:normal;}#src-sidebar-toggle{position:sticky;top:0;left:0;font-size:1.25rem;border-bottom:1px solid;display:flex;height:40px;justify-content:stretch;align-items:stretch;z-index:10;}#src-sidebar{width:100%;overflow:auto;}#src-sidebar>.title{font-size:1.5rem;text-align:center;border-bottom:1px solid var(--border-color);margin-bottom:6px;}#src-sidebar div.files>a:hover,details.dir-entry summary:hover,#src-sidebar div.files>a:focus,details.dir-entry summary:focus{background-color:var(--src-sidebar-background-hover);}#src-sidebar div.files>a.selected{background-color:var(--src-sidebar-background-selected);}#src-sidebar-toggle>button{font-size:inherit;font-weight:bold;background:none;color:inherit;text-align:center;border:none;outline:none;flex:1 1;-webkit-appearance:none;opacity:1;}#settings-menu,#help-button{margin-left:4px;display:flex;}#settings-menu>a,#help-button>a{display:flex;align-items:center;justify-content:center;background-color:var(--button-background-color);border:1px solid var(--border-color);border-radius:2px;color:var(--settings-button-color);font-size:20px;width:33px;}#settings-menu>a:hover,#settings-menu>a:focus,#help-button>a:hover,#help-button>a:focus{border-color:var(--settings-button-border-focus);}#copy-path{color:var(--copy-path-button-color);background:var(--main-background-color);height:34px;margin-left:10px;padding:0;padding-left:2px;border:0;width:33px;}#copy-path>img{filter:var(--copy-path-img-filter);}#copy-path:hover>img{filter:var(--copy-path-img-hover-filter);}@keyframes rotating{from{transform:rotate(0deg);}to{transform:rotate(360deg);}}#settings-menu.rotate>a img{animation:rotating 2s linear infinite;}kbd{display:inline-block;padding:3px 5px;font:15px monospace;line-height:10px;vertical-align:middle;border:solid 1px var(--border-color);border-radius:3px;color:var(--kbd-color);background-color:var(--kbd-background);box-shadow:inset 0 -1px 0 var(--kbd-box-shadow-color);}ul.all-items>li{list-style:none;}details.dir-entry{padding-left:4px;}details.dir-entry>summary{margin:0 0 0 -4px;padding:0 0 0 4px;cursor:pointer;}details.dir-entry div.folders,details.dir-entry div.files{padding-left:23px;}details.dir-entry a{display:block;}details.toggle{contain:layout;position:relative;}details.toggle>summary.hideme{cursor:pointer;font-size:1rem;}details.toggle>summary{list-style:none;outline:none;}details.toggle>summary::-webkit-details-marker,details.toggle>summary::marker{display:none;}details.toggle>summary.hideme>span{margin-left:9px;}details.toggle>summary::before{background:url('data:image/svg+xml,') no-repeat top left;content:"";cursor:pointer;width:16px;height:16px;display:inline-block;vertical-align:middle;opacity:.5;filter:var(--toggle-filter);}details.toggle>summary.hideme>span,.more-examples-toggle summary,.more-examples-toggle .hide-more{color:var(--toggles-color);}details.toggle>summary::after{content:"Expand";overflow:hidden;width:0;height:0;position:absolute;}details.toggle>summary.hideme::after{content:"";}details.toggle>summary:focus::before,details.toggle>summary:hover::before{opacity:1;}details.toggle>summary:focus-visible::before{outline:1px dotted #000;outline-offset:1px;}details.non-exhaustive{margin-bottom:8px;}details.toggle>summary.hideme::before{position:relative;}details.toggle>summary:not(.hideme)::before{position:absolute;left:-24px;top:4px;}.impl-items>details.toggle>summary:not(.hideme)::before{position:absolute;left:-24px;}details.toggle[open] >summary.hideme{position:absolute;}details.toggle[open] >summary.hideme>span{display:none;}details.toggle[open] >summary::before{background:url('data:image/svg+xml,') no-repeat top left;}details.toggle[open] >summary::after{content:"Collapse";}.docblock summary>*{display:inline-block;}.docblock>.example-wrap:first-child .tooltip{margin-top:16px;}@media (max-width:850px){#search-tabs .count{display:block;}}@media (max-width:700px){*[id]{scroll-margin-top:45px;}.rustdoc{display:block;}main{padding-left:15px;padding-top:0px;}.main-heading{flex-direction:column;}.out-of-band{text-align:left;margin-left:initial;padding:initial;}.out-of-band .since::before{content:"Since ";}.sidebar .logo-container,.sidebar .location{display:none;}.sidebar{position:fixed;top:45px;left:-1000px;z-index:11;height:calc(100vh - 45px);width:200px;}.src main,.rustdoc.src .sidebar{top:0;padding:0;height:100vh;border:0;}.sidebar.shown,.src-sidebar-expanded .src .sidebar,.rustdoc:not(.src) .sidebar:focus-within{left:0;}.mobile-topbar h2{padding-bottom:0;margin:auto 0.5em auto auto;overflow:hidden;font-size:24px;}.mobile-topbar h2 a{display:block;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;}.mobile-topbar .logo-container>img{max-width:35px;max-height:35px;margin:5px 0 5px 20px;}.mobile-topbar{display:flex;flex-direction:row;position:sticky;z-index:10;font-size:2rem;height:45px;width:100%;left:0;top:0;}.sidebar-menu-toggle{width:45px;font-size:32px;border:none;color:var(--main-color);}.sidebar-elems{margin-top:1em;}.anchor{display:none !important;}#main-content>details.toggle>summary::before,#main-content>div>details.toggle>summary::before{left:-11px;}#src-sidebar-toggle{position:fixed;left:1px;top:100px;width:30px;font-size:1.5rem;padding:0;z-index:10;border-top-right-radius:3px;border-bottom-right-radius:3px;border:1px solid;border-left:0;}.src-sidebar-expanded #src-sidebar-toggle{left:unset;top:unset;width:unset;border-top-right-radius:unset;border-bottom-right-radius:unset;position:sticky;border:0;border-bottom:1px solid;}#copy-path,#help-button{display:none;}.item-table,.item-row,.item-table>li,.item-table>li>div,.search-results>a,.search-results>a>div{display:block;}.search-results>a{padding:5px 0px;}.search-results>a>div.desc,.item-table>li>div.desc{padding-left:2em;}.search-results .result-name{display:block;}.search-results .result-name .typename{width:initial;margin-right:0;}.search-results .result-name .typename,.search-results .result-name .path{display:inline;}.src-sidebar-expanded .src .sidebar{max-width:100vw;width:100vw;}details.toggle:not(.top-doc)>summary{margin-left:10px;}.impl-items>details.toggle>summary:not(.hideme)::before,#main-content>details.toggle:not(.top-doc)>summary::before,#main-content>div>details.toggle>summary::before{left:-11px;}.impl-items>.item-info{margin-left:34px;}.src nav.sub{margin:0;padding:var(--nav-sub-mobile-padding);}}@media (min-width:701px){.scraped-example-title{position:absolute;z-index:10;background:var(--main-background-color);bottom:8px;right:5px;padding:2px 4px;box-shadow:0 0 4px var(--main-background-color);}}@media print{nav.sidebar,nav.sub,.out-of-band,a.src,#copy-path,details.toggle[open] >summary::before,details.toggle>summary::before,details.toggle.top-doc>summary{display:none;}.docblock{margin-left:0;}main{padding:10px;}}@media (max-width:464px){.docblock{margin-left:12px;}.docblock code{overflow-wrap:break-word;overflow-wrap:anywhere;}nav.sub{flex-direction:column;}.search-form{align-self:stretch;}.sub-logo-container>img{height:35px;width:35px;margin-bottom:var(--nav-sub-mobile-padding);}}.variant,.implementors-toggle>summary,.impl,#implementors-list>.docblock,.impl-items>section,.impl-items>.toggle>summary,.methods>section,.methods>.toggle>summary{margin-bottom:0.75em;}.variants>.docblock,.implementors-toggle>.docblock,.impl-items>.toggle[open]:not(:last-child),.methods>.toggle[open]:not(:last-child),.implementors-toggle[open]:not(:last-child){margin-bottom:2em;}#trait-implementations-list .impl-items>.toggle:not(:last-child),#synthetic-implementations-list .impl-items>.toggle:not(:last-child),#blanket-implementations-list .impl-items>.toggle:not(:last-child){margin-bottom:1em;}.scraped-example-list .scrape-help{margin-left:10px;padding:0 4px;font-weight:normal;font-size:12px;position:relative;bottom:1px;border:1px solid var(--scrape-example-help-border-color);border-radius:50px;color:var(--scrape-example-help-color);}.scraped-example-list .scrape-help:hover{border-color:var(--scrape-example-help-hover-border-color);color:var(--scrape-example-help-hover-color);}.scraped-example{position:relative;}.scraped-example .code-wrapper{position:relative;display:flex;flex-direction:row;flex-wrap:wrap;width:100%;}.scraped-example:not(.expanded) .code-wrapper{max-height:calc(1.5em * 5 + 10px);}.scraped-example:not(.expanded) .code-wrapper pre{overflow-y:hidden;padding-bottom:0;max-height:calc(1.5em * 5 + 10px);}.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper,.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper pre{max-height:calc(1.5em * 10 + 10px);}.scraped-example .code-wrapper .next,.scraped-example .code-wrapper .prev,.scraped-example .code-wrapper .expand{color:var(--main-color);position:absolute;top:0.25em;z-index:1;padding:0;background:none;border:none;-webkit-appearance:none;opacity:1;}.scraped-example .code-wrapper .prev{right:2.25em;}.scraped-example .code-wrapper .next{right:1.25em;}.scraped-example .code-wrapper .expand{right:0.25em;}.scraped-example:not(.expanded) .code-wrapper::before,.scraped-example:not(.expanded) .code-wrapper::after{content:" ";width:100%;height:5px;position:absolute;z-index:1;}.scraped-example:not(.expanded) .code-wrapper::before{top:0;background:linear-gradient(to bottom,var(--scrape-example-code-wrapper-background-start),var(--scrape-example-code-wrapper-background-end));}.scraped-example:not(.expanded) .code-wrapper::after{bottom:0;background:linear-gradient(to top,var(--scrape-example-code-wrapper-background-start),var(--scrape-example-code-wrapper-background-end));}.scraped-example .code-wrapper .example-wrap{width:100%;overflow-y:hidden;margin-bottom:0;}.scraped-example:not(.expanded) .code-wrapper .example-wrap{overflow-x:hidden;}.scraped-example .example-wrap .rust span.highlight{background:var(--scrape-example-code-line-highlight);}.scraped-example .example-wrap .rust span.highlight.focus{background:var(--scrape-example-code-line-highlight-focus);}.more-examples-toggle{max-width:calc(100% + 25px);margin-top:10px;margin-left:-25px;}.more-examples-toggle .hide-more{margin-left:25px;cursor:pointer;}.more-scraped-examples{margin-left:25px;position:relative;}.toggle-line{position:absolute;top:5px;bottom:0;right:calc(100% + 10px);padding:0 4px;cursor:pointer;}.toggle-line-inner{min-width:2px;height:100%;background:var(--scrape-example-toggle-line-background);}.toggle-line:hover .toggle-line-inner{background:var(--scrape-example-toggle-line-hover-background);}.more-scraped-examples .scraped-example,.example-links{margin-top:20px;}.more-scraped-examples .scraped-example:first-child{margin-top:5px;}.example-links ul{margin-bottom:0;}:root[data-theme="light"]{--main-background-color:white;--main-color:black;--settings-input-color:#2196f3;--settings-input-border-color:#717171;--settings-button-color:#000;--settings-button-border-focus:#717171;--sidebar-background-color:#f5f5f5;--sidebar-background-color-hover:#e0e0e0;--code-block-background-color:#f5f5f5;--scrollbar-track-background-color:#dcdcdc;--scrollbar-thumb-background-color:rgba(36,37,39,0.6);--scrollbar-color:rgba(36,37,39,0.6) #d9d9d9;--headings-border-bottom-color:#ddd;--border-color:#e0e0e0;--button-background-color:#fff;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:none;--search-input-focused-border-color:#66afe9;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(35%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ad378a;--trait-link-color:#6e4fc9;--assoc-item-link-color:#3873ad;--function-link-color:#ad7c37;--macro-link-color:#068000;--keyword-link-color:#3873ad;--mod-link-color:#3873ad;--link-color:#3873ad;--sidebar-link-color:#356da4;--sidebar-current-link-background-color:#fff;--search-result-link-focus-background-color:#ccc;--search-result-border-color:#aaa3;--search-color:#000;--search-error-code-background-color:#d0cccc;--search-results-alias-color:#000;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#e6e6e6;--search-tab-button-not-selected-background:#e6e6e6;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#fff;--stab-background-color:#fff5d6;--stab-code-color:#000;--code-highlight-kw-color:#8959a8;--code-highlight-kw-2-color:#4271ae;--code-highlight-lifetime-color:#b76514;--code-highlight-prelude-color:#4271ae;--code-highlight-prelude-val-color:#c82829;--code-highlight-number-color:#718c00;--code-highlight-string-color:#718c00;--code-highlight-literal-color:#c82829;--code-highlight-attribute-color:#c82829;--code-highlight-self-color:#c82829;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8e908c;--code-highlight-doc-comment-color:#4d4d4c;--src-line-numbers-span-color:#c67e2d;--src-line-number-highlighted-background-color:#fdffd3;--test-arrow-color:#f5f5f5;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#f5f5f5;--test-arrow-hover-background-color:rgb(78,139,202);--target-background-color:#fdffd3;--target-border-color:#ad7c37;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:initial;--crate-search-div-filter:invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) brightness(114%) contrast(76%);--crate-search-div-hover-filter:invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) brightness(96%) contrast(93%);--crate-search-hover-border:#717171;--src-sidebar-background-selected:#fff;--src-sidebar-background-hover:#e0e0e0;--table-alt-row-background-color:#f5f5f5;--codeblock-link-background:#eee;--scrape-example-toggle-line-background:#ccc;--scrape-example-toggle-line-hover-background:#999;--scrape-example-code-line-highlight:#fcffd6;--scrape-example-code-line-highlight-focus:#f6fdb0;--scrape-example-help-border-color:#555;--scrape-example-help-color:#333;--scrape-example-help-hover-border-color:#000;--scrape-example-help-hover-color:#000;--scrape-example-code-wrapper-background-start:rgba(255,255,255,1);--scrape-example-code-wrapper-background-end:rgba(255,255,255,0);}:root[data-theme="dark"]{--main-background-color:#353535;--main-color:#ddd;--settings-input-color:#2196f3;--settings-input-border-color:#999;--settings-button-color:#000;--settings-button-border-focus:#ffb900;--sidebar-background-color:#505050;--sidebar-background-color-hover:#676767;--code-block-background-color:#2A2A2A;--scrollbar-track-background-color:#717171;--scrollbar-thumb-background-color:rgba(32,34,37,.6);--scrollbar-color:rgba(32,34,37,.6) #5a5a5a;--headings-border-bottom-color:#d2d2d2;--border-color:#e0e0e0;--button-background-color:#f0f0f0;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--search-input-focused-border-color:#008dfd;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(65%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#2dbfb8;--trait-link-color:#b78cf2;--assoc-item-link-color:#d2991d;--function-link-color:#2bab63;--macro-link-color:#09bd00;--keyword-link-color:#d2991d;--mod-link-color:#d2991d;--link-color:#d2991d;--sidebar-link-color:#fdbf35;--sidebar-current-link-background-color:#444;--search-result-link-focus-background-color:#616161;--search-result-border-color:#aaa3;--search-color:#111;--search-error-code-background-color:#484848;--search-results-alias-color:#fff;--search-results-grey-color:#ccc;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#252525;--search-tab-button-not-selected-background:#252525;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#353535;--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ab8ac1;--code-highlight-kw-2-color:#769acb;--code-highlight-lifetime-color:#d97f26;--code-highlight-prelude-color:#769acb;--code-highlight-prelude-val-color:#ee6868;--code-highlight-number-color:#83a300;--code-highlight-string-color:#83a300;--code-highlight-literal-color:#ee6868;--code-highlight-attribute-color:#ee6868;--code-highlight-self-color:#ee6868;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8d8d8b;--code-highlight-doc-comment-color:#8ca375;--src-line-numbers-span-color:#3b91e2;--src-line-number-highlighted-background-color:#0a042f;--test-arrow-color:#dedede;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#dedede;--test-arrow-hover-background-color:#4e8bca;--target-background-color:#494a3d;--target-border-color:#bb7410;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) brightness(90%) contrast(90%);--crate-search-div-hover-filter:invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) brightness(100%) contrast(91%);--crate-search-hover-border:#2196f3;--src-sidebar-background-selected:#333;--src-sidebar-background-hover:#444;--table-alt-row-background-color:#2a2a2a;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(53,53,53,1);--scrape-example-code-wrapper-background-end:rgba(53,53,53,0);}:root[data-theme="ayu"]{--main-background-color:#0f1419;--main-color:#c5c5c5;--settings-input-color:#ffb454;--settings-input-border-color:#999;--settings-button-color:#fff;--settings-button-border-focus:#e0e0e0;--sidebar-background-color:#14191f;--sidebar-background-color-hover:rgba(70,70,70,0.33);--code-block-background-color:#191f26;--scrollbar-track-background-color:transparent;--scrollbar-thumb-background-color:#5c6773;--scrollbar-color:#5c6773 #24292f;--headings-border-bottom-color:#5c6773;--border-color:#5c6773;--button-background-color:#141920;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--search-input-focused-border-color:#5c6773;--copy-path-button-color:#fff;--copy-path-img-filter:invert(70%);--copy-path-img-hover-filter:invert(100%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ffa0a5;--trait-link-color:#39afd7;--assoc-item-link-color:#39afd7;--function-link-color:#fdd687;--macro-link-color:#a37acc;--keyword-link-color:#39afd7;--mod-link-color:#39afd7;--link-color:#39afd7;--sidebar-link-color:#53b1db;--sidebar-current-link-background-color:transparent;--search-result-link-focus-background-color:#3c3c3c;--search-result-border-color:#aaa3;--search-color:#fff;--search-error-code-background-color:#4f4c4c;--search-results-alias-color:#c5c5c5;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:none;--search-tab-button-not-selected-background:transparent !important;--search-tab-button-selected-border-top-color:none;--search-tab-button-selected-background:#141920 !important;--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ff7733;--code-highlight-kw-2-color:#ff7733;--code-highlight-lifetime-color:#ff7733;--code-highlight-prelude-color:#69f2df;--code-highlight-prelude-val-color:#ff7733;--code-highlight-number-color:#b8cc52;--code-highlight-string-color:#b8cc52;--code-highlight-literal-color:#ff7733;--code-highlight-attribute-color:#e6e1cf;--code-highlight-self-color:#36a3d9;--code-highlight-macro-color:#a37acc;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#788797;--code-highlight-doc-comment-color:#a1ac88;--src-line-numbers-span-color:#5c6773;--src-line-number-highlighted-background-color:rgba(255,236,164,0.06);--test-arrow-color:#788797;--test-arrow-background-color:rgba(57,175,215,0.09);--test-arrow-hover-color:#c5c5c5;--test-arrow-hover-background-color:rgba(57,175,215,0.368);--target-background-color:rgba(255,236,164,0.06);--target-border-color:rgba(255,180,76,0.85);--kbd-color:#c5c5c5;--kbd-background:#314559;--kbd-box-shadow-color:#5c6773;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(41%) sepia(12%) saturate(487%) hue-rotate(171deg) brightness(94%) contrast(94%);--crate-search-div-hover-filter:invert(98%) sepia(12%) saturate(81%) hue-rotate(343deg) brightness(113%) contrast(76%);--crate-search-hover-border:#e0e0e0;--src-sidebar-background-selected:#14191f;--src-sidebar-background-hover:#14191f;--table-alt-row-background-color:#191f26;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(15,20,25,1);--scrape-example-code-wrapper-background-end:rgba(15,20,25,0);}:root[data-theme="ayu"] h1,:root[data-theme="ayu"] h2,:root[data-theme="ayu"] h3,:root[data-theme="ayu"] h4,:where(:root[data-theme="ayu"]) h1 a,:root[data-theme="ayu"] .sidebar h2 a,:root[data-theme="ayu"] .sidebar h3 a,:root[data-theme="ayu"] #source-sidebar>.title{color:#fff;}:root[data-theme="ayu"] .docblock code{color:#ffb454;}:root[data-theme="ayu"] .docblock a>code{color:#39AFD7 !important;}:root[data-theme="ayu"] .code-header,:root[data-theme="ayu"] .docblock pre>code,:root[data-theme="ayu"] pre,:root[data-theme="ayu"] pre>code,:root[data-theme="ayu"] .item-info code,:root[data-theme="ayu"] .rustdoc.source .example-wrap{color:#e6e1cf;}:root[data-theme="ayu"] .sidebar .current,:root[data-theme="ayu"] .sidebar a:hover,:root[data-theme="ayu"] #src-sidebar div.files>a:hover,:root[data-theme="ayu"] details.dir-entry summary:hover,:root[data-theme="ayu"] #src-sidebar div.files>a:focus,:root[data-theme="ayu"] details.dir-entry summary:focus,:root[data-theme="ayu"] #src-sidebar div.files>a.selected{color:#ffb44c;}:root[data-theme="ayu"] .sidebar-elems .location{color:#ff7733;}:root[data-theme="ayu"] .src-line-numbers .line-highlighted{color:#708090;padding-right:7px;border-right:1px solid #ffb44c;}:root[data-theme="ayu"] .search-results a:hover,:root[data-theme="ayu"] .search-results a:focus{color:#fff !important;background-color:#3c3c3c;}:root[data-theme="ayu"] .search-results a{color:#0096cf;}:root[data-theme="ayu"] .search-results a div.desc{color:#c5c5c5;}:root[data-theme="ayu"] .result-name .primitive>i,:root[data-theme="ayu"] .result-name .keyword>i{color:#788797;}:root[data-theme="ayu"] #search-tabs>button.selected{border-bottom:1px solid #ffb44c !important;border-top:none;}:root[data-theme="ayu"] #search-tabs>button:not(.selected){border:none;background-color:transparent !important;}:root[data-theme="ayu"] #search-tabs>button:hover{border-bottom:1px solid rgba(242,151,24,0.3);}:root[data-theme="ayu"] #settings-menu>a img{filter:invert(100);} \ No newline at end of file diff --git a/docs/macos_docs/static.files/scrape-examples-ef1e698c1d417c0c.js b/docs/macos_docs/static.files/scrape-examples-ef1e698c1d417c0c.js new file mode 100644 index 00000000..ba830e37 --- /dev/null +++ b/docs/macos_docs/static.files/scrape-examples-ef1e698c1d417c0c.js @@ -0,0 +1 @@ +"use strict";(function(){const DEFAULT_MAX_LINES=5;const HIDDEN_MAX_LINES=10;function scrollToLoc(elt,loc,isHidden){const lines=elt.querySelector(".src-line-numbers");let scrollOffset;const maxLines=isHidden?HIDDEN_MAX_LINES:DEFAULT_MAX_LINES;if(loc[1]-loc[0]>maxLines){const line=Math.max(0,loc[0]-1);scrollOffset=lines.children[line].offsetTop}else{const wrapper=elt.querySelector(".code-wrapper");const halfHeight=wrapper.offsetHeight/2;const offsetTop=lines.children[loc[0]].offsetTop;const lastLine=lines.children[loc[1]];const offsetBot=lastLine.offsetTop+lastLine.offsetHeight;const offsetMid=(offsetTop+offsetBot)/2;scrollOffset=offsetMid-halfHeight}lines.scrollTo(0,scrollOffset);elt.querySelector(".rust").scrollTo(0,scrollOffset)}function updateScrapedExample(example,isHidden){const locs=JSON.parse(example.attributes.getNamedItem("data-locs").textContent);let locIndex=0;const highlights=Array.prototype.slice.call(example.querySelectorAll(".highlight"));const link=example.querySelector(".scraped-example-title a");if(locs.length>1){const onChangeLoc=changeIndex=>{removeClass(highlights[locIndex],"focus");changeIndex();scrollToLoc(example,locs[locIndex][0],isHidden);addClass(highlights[locIndex],"focus");const url=locs[locIndex][1];const title=locs[locIndex][2];link.href=url;link.innerHTML=title};example.querySelector(".prev").addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex-1+locs.length)%locs.length})});example.querySelector(".next").addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex+1)%locs.length})})}const expandButton=example.querySelector(".expand");if(expandButton){expandButton.addEventListener("click",()=>{if(hasClass(example,"expanded")){removeClass(example,"expanded");scrollToLoc(example,locs[0][0],isHidden)}else{addClass(example,"expanded")}})}scrollToLoc(example,locs[0][0],isHidden)}const firstExamples=document.querySelectorAll(".scraped-example-list > .scraped-example");onEachLazy(firstExamples,el=>updateScrapedExample(el,false));onEachLazy(document.querySelectorAll(".more-examples-toggle"),toggle=>{onEachLazy(toggle.querySelectorAll(".toggle-line, .hide-more"),button=>{button.addEventListener("click",()=>{toggle.open=false})});const moreExamples=toggle.querySelectorAll(".scraped-example");toggle.querySelector("summary").addEventListener("click",()=>{setTimeout(()=>{onEachLazy(moreExamples,el=>updateScrapedExample(el,true))})},{once:true})})})() \ No newline at end of file diff --git a/docs/macos_docs/static.files/search-8fbf244ebcf71464.js b/docs/macos_docs/static.files/search-8fbf244ebcf71464.js new file mode 100644 index 00000000..168023b4 --- /dev/null +++ b/docs/macos_docs/static.files/search-8fbf244ebcf71464.js @@ -0,0 +1,5 @@ +"use strict";if(!Array.prototype.toSpliced){Array.prototype.toSpliced=function(){const me=this.slice();Array.prototype.splice.apply(me,arguments);return me}}(function(){const itemTypes=["mod","externcrate","import","struct","enum","fn","type","static","trait","impl","tymethod","method","structfield","variant","macro","primitive","associatedtype","constant","associatedconstant","union","foreigntype","keyword","existential","attr","derive","traitalias","generic",];const longItemTypes=["module","extern crate","re-export","struct","enum","function","type alias","static","trait","","trait method","method","struct field","enum variant","macro","primitive type","assoc type","constant","assoc const","union","foreign type","keyword","existential type","attribute macro","derive macro","trait alias",];const TY_PRIMITIVE=itemTypes.indexOf("primitive");const TY_KEYWORD=itemTypes.indexOf("keyword");const TY_GENERIC=itemTypes.indexOf("generic");const ROOT_PATH=typeof window!=="undefined"?window.rootPath:"../";function hasOwnPropertyRustdoc(obj,property){return Object.prototype.hasOwnProperty.call(obj,property)}function printTab(nb){let iter=0;let foundCurrentTab=false;let foundCurrentResultSet=false;onEachLazy(document.getElementById("search-tabs").childNodes,elem=>{if(nb===iter){addClass(elem,"selected");foundCurrentTab=true}else{removeClass(elem,"selected")}iter+=1});const isTypeSearch=(nb>0||iter===1);iter=0;onEachLazy(document.getElementById("results").childNodes,elem=>{if(nb===iter){addClass(elem,"active");foundCurrentResultSet=true}else{removeClass(elem,"active")}iter+=1});if(foundCurrentTab&&foundCurrentResultSet){searchState.currentTab=nb;const correctionsElem=document.getElementsByClassName("search-corrections");if(isTypeSearch){removeClass(correctionsElem[0],"hidden")}else{addClass(correctionsElem[0],"hidden")}}else if(nb!==0){printTab(0)}}const editDistanceState={current:[],prev:[],prevPrev:[],calculate:function calculate(a,b,limit){if(a.lengthlimit){return limit+1}while(b.length>0&&b[0]===a[0]){a=a.substring(1);b=b.substring(1)}while(b.length>0&&b[b.length-1]===a[a.length-1]){a=a.substring(0,a.length-1);b=b.substring(0,b.length-1)}if(b.length===0){return minDist}const aLength=a.length;const bLength=b.length;for(let i=0;i<=bLength;++i){this.current[i]=0;this.prev[i]=i;this.prevPrev[i]=Number.MAX_VALUE}for(let i=1;i<=aLength;++i){this.current[0]=i;const aIdx=i-1;for(let j=1;j<=bLength;++j){const bIdx=j-1;const substitutionCost=a[aIdx]===b[bIdx]?0:1;this.current[j]=Math.min(this.prev[j]+1,this.current[j-1]+1,this.prev[j-1]+substitutionCost);if((i>1)&&(j>1)&&(a[aIdx]===b[bIdx-1])&&(a[aIdx-1]===b[bIdx])){this.current[j]=Math.min(this.current[j],this.prevPrev[j-2]+1)}}const prevPrevTmp=this.prevPrev;this.prevPrev=this.prev;this.prev=this.current;this.current=prevPrevTmp}const distance=this.prev[bLength];return distance<=limit?distance:(limit+1)},};function editDistance(a,b,limit){return editDistanceState.calculate(a,b,limit)}function initSearch(rawSearchIndex){const MAX_RESULTS=200;const NO_TYPE_FILTER=-1;let searchIndex;let currentResults;let typeNameIdMap;const ALIASES=new Map();let typeNameIdOfArray;let typeNameIdOfSlice;let typeNameIdOfArrayOrSlice;function buildTypeMapIndex(name){if(name===""||name===null){return null}if(typeNameIdMap.has(name)){return typeNameIdMap.get(name)}else{const id=typeNameIdMap.size;typeNameIdMap.set(name,id);return id}}function isWhitespace(c){return" \t\n\r".indexOf(c)!==-1}function isSpecialStartCharacter(c){return"<\"".indexOf(c)!==-1}function isEndCharacter(c){return",>-]".indexOf(c)!==-1}function isStopCharacter(c){return isEndCharacter(c)}function isErrorCharacter(c){return"()".indexOf(c)!==-1}function itemTypeFromName(typename){const index=itemTypes.findIndex(i=>i===typename);if(index<0){throw["Unknown type filter ",typename]}return index}function getStringElem(query,parserState,isInGenerics){if(isInGenerics){throw["Unexpected ","\""," in generics"]}else if(query.literalSearch){throw["Cannot have more than one literal search element"]}else if(parserState.totalElems-parserState.genericsElems>0){throw["Cannot use literal search when there is more than one element"]}parserState.pos+=1;const start=parserState.pos;const end=getIdentEndPosition(parserState);if(parserState.pos>=parserState.length){throw["Unclosed ","\""]}else if(parserState.userQuery[end]!=="\""){throw["Unexpected ",parserState.userQuery[end]," in a string element"]}else if(start===end){throw["Cannot have empty string element"]}parserState.pos+=1;query.literalSearch=true}function isPathStart(parserState){return parserState.userQuery.slice(parserState.pos,parserState.pos+2)==="::"}function isReturnArrow(parserState){return parserState.userQuery.slice(parserState.pos,parserState.pos+2)==="->"}function isIdentCharacter(c){return(c==="_"||(c>="0"&&c<="9")||(c>="a"&&c<="z")||(c>="A"&&c<="Z"))}function isSeparatorCharacter(c){return c===","}function isPathSeparator(c){return c===":"||isWhitespace(c)}function prevIs(parserState,lookingFor){let pos=parserState.pos;while(pos>0){const c=parserState.userQuery[pos-1];if(c===lookingFor){return true}else if(!isWhitespace(c)){break}pos-=1}return false}function isLastElemGeneric(elems,parserState){return(elems.length>0&&elems[elems.length-1].generics.length>0)||prevIs(parserState,">")}function skipWhitespace(parserState){while(parserState.pos0){throw["Cannot have more than one element if you use quotes"]}const typeFilter=parserState.typeFilter;parserState.typeFilter=null;if(name==="!"){if(typeFilter!==null&&typeFilter!=="primitive"){throw["Invalid search type: primitive never type ","!"," and ",typeFilter," both specified",]}if(generics.length!==0){throw["Never type ","!"," does not accept generic parameters",]}return{name:"never",id:null,fullPath:["never"],pathWithoutLast:[],pathLast:"never",generics:[],typeFilter:"primitive",}}if(path.startsWith("::")){throw["Paths cannot start with ","::"]}else if(path.endsWith("::")){throw["Paths cannot end with ","::"]}else if(path.includes("::::")){throw["Unexpected ","::::"]}else if(path.includes(" ::")){throw["Unexpected "," ::"]}else if(path.includes(":: ")){throw["Unexpected ",":: "]}const pathSegments=path.split(/::|\s+/);if(pathSegments.length===0||(pathSegments.length===1&&pathSegments[0]==="")){if(generics.length>0||prevIs(parserState,">")){throw["Found generics without a path"]}else{throw["Unexpected ",parserState.userQuery[parserState.pos]]}}for(const[i,pathSegment]of pathSegments.entries()){if(pathSegment==="!"){if(i!==0){throw["Never type ","!"," is not associated item"]}pathSegments[i]="never"}}parserState.totalElems+=1;if(isInGenerics){parserState.genericsElems+=1}return{name:name.trim(),id:null,fullPath:pathSegments,pathWithoutLast:pathSegments.slice(0,pathSegments.length-1),pathLast:pathSegments[pathSegments.length-1],generics:generics,typeFilter,}}function getIdentEndPosition(parserState){const start=parserState.pos;let end=parserState.pos;let foundExclamation=-1;while(parserState.pos=end){throw["Found generics without a path"]}parserState.pos+=1;getItemsBefore(query,parserState,generics,">")}if(isStringElem){skipWhitespace(parserState)}if(start>=end&&generics.length===0){return}elems.push(createQueryElement(query,parserState,parserState.userQuery.slice(start,end),generics,isInGenerics))}}function getItemsBefore(query,parserState,elems,endChar){let foundStopChar=true;let start=parserState.pos;const oldTypeFilter=parserState.typeFilter;parserState.typeFilter=null;let extra="";if(endChar===">"){extra="<"}else if(endChar==="]"){extra="["}else if(endChar===""){extra="->"}else{extra=endChar}while(parserState.pos"]}else if(prevIs(parserState,"\"")){throw["Cannot have more than one element if you use quotes"]}if(endChar!==""){throw["Expected ",","," or ",endChar,...extra,", found ",c,]}throw["Expected ",",",...extra,", found ",c,]}const posBefore=parserState.pos;start=parserState.pos;getNextElem(query,parserState,elems,endChar!=="");if(endChar!==""&&parserState.pos>=parserState.length){throw["Unclosed ",extra]}if(posBefore===parserState.pos){parserState.pos+=1}foundStopChar=false}if(parserState.pos>=parserState.length&&endChar!==""){throw["Unclosed ",extra]}parserState.pos+=1;parserState.typeFilter=oldTypeFilter}function checkExtraTypeFilterCharacters(start,parserState){const query=parserState.userQuery.slice(start,parserState.pos).trim();for(const c in query){if(!isIdentCharacter(query[c])){throw["Unexpected ",query[c]," in type filter (before ",":",")",]}}}function parseInput(query,parserState){let foundStopChar=true;let start=parserState.pos;while(parserState.pos"){if(isReturnArrow(parserState)){break}throw["Unexpected ",c," (did you mean ","->","?)"]}throw["Unexpected ",c]}else if(c===":"&&!isPathStart(parserState)){if(parserState.typeFilter!==null){throw["Unexpected ",":"," (expected path after type filter ",parserState.typeFilter+":",")",]}else if(query.elems.length===0){throw["Expected type filter before ",":"]}else if(query.literalSearch){throw["Cannot use quotes on type filter"]}const typeFilterElem=query.elems.pop();checkExtraTypeFilterCharacters(start,parserState);parserState.typeFilter=typeFilterElem.name;parserState.pos+=1;parserState.totalElems-=1;query.literalSearch=false;foundStopChar=true;continue}else if(isWhitespace(c)){skipWhitespace(parserState);continue}if(!foundStopChar){let extra="";if(isLastElemGeneric(query.elems,parserState)){extra=[" after ",">"]}else if(prevIs(parserState,"\"")){throw["Cannot have more than one element if you use quotes"]}if(parserState.typeFilter!==null){throw["Expected ",","," or ","->",...extra,", found ",c,]}throw["Expected ",",",", ",":"," or ","->",...extra,", found ",c,]}const before=query.elems.length;start=parserState.pos;getNextElem(query,parserState,query.elems,false);if(query.elems.length===before){parserState.pos+=1}foundStopChar=false}if(parserState.typeFilter!==null){throw["Unexpected ",":"," (expected path after type filter ",parserState.typeFilter+":",")",]}while(parserState.pos"]}break}else{parserState.pos+=1}}}function newParsedQuery(userQuery){return{original:userQuery,userQuery:userQuery.toLowerCase(),elems:[],returned:[],foundElems:0,totalElems:0,literalSearch:false,error:null,correction:null,proposeCorrectionFrom:null,proposeCorrectionTo:null,}}function buildUrl(search,filterCrates){let extra="?search="+encodeURIComponent(search);if(filterCrates!==null){extra+="&filter-crate="+encodeURIComponent(filterCrates)}return getNakedUrl()+extra+window.location.hash}function getFilterCrates(){const elem=document.getElementById("crate-search");if(elem&&elem.value!=="all crates"&&hasOwnPropertyRustdoc(rawSearchIndex,elem.value)){return elem.value}return null}function parseQuery(userQuery){function convertTypeFilterOnElem(elem){if(elem.typeFilter!==null){let typeFilter=elem.typeFilter;if(typeFilter==="const"){typeFilter="constant"}elem.typeFilter=itemTypeFromName(typeFilter)}else{elem.typeFilter=NO_TYPE_FILTER}for(const elem2 of elem.generics){convertTypeFilterOnElem(elem2)}}userQuery=userQuery.trim();const parserState={length:userQuery.length,pos:0,totalElems:0,genericsElems:0,typeFilter:null,userQuery:userQuery.toLowerCase(),};let query=newParsedQuery(userQuery);try{parseInput(query,parserState);for(const elem of query.elems){convertTypeFilterOnElem(elem)}for(const elem of query.returned){convertTypeFilterOnElem(elem)}}catch(err){query=newParsedQuery(userQuery);query.error=err;return query}if(!query.literalSearch){query.literalSearch=parserState.totalElems>1}query.foundElems=query.elems.length+query.returned.length;query.totalElems=parserState.totalElems;return query}function createQueryResults(results_in_args,results_returned,results_others,parsedQuery){return{"in_args":results_in_args,"returned":results_returned,"others":results_others,"query":parsedQuery,}}function execQuery(parsedQuery,searchWords,filterCrates,currentCrate){const results_others=new Map(),results_in_args=new Map(),results_returned=new Map();function transformResults(results){const duplicates=new Set();const out=[];for(const result of results){if(result.id!==-1){const obj=searchIndex[result.id];obj.dist=result.dist;const res=buildHrefAndPath(obj);obj.displayPath=pathSplitter(res[0]);obj.fullPath=obj.displayPath+obj.name;obj.fullPath+="|"+obj.ty;if(duplicates.has(obj.fullPath)){continue}duplicates.add(obj.fullPath);obj.href=res[1];out.push(obj);if(out.length>=MAX_RESULTS){break}}}return out}function sortResults(results,isType,preferredCrate){if(results.size===0){return[]}const userQuery=parsedQuery.userQuery;const result_list=[];for(const result of results.values()){result.word=searchWords[result.id];result.item=searchIndex[result.id]||{};result_list.push(result)}result_list.sort((aaa,bbb)=>{let a,b;a=(aaa.word!==userQuery);b=(bbb.word!==userQuery);if(a!==b){return a-b}a=(aaa.index<0);b=(bbb.index<0);if(a!==b){return a-b}a=aaa.path_dist;b=bbb.path_dist;if(a!==b){return a-b}a=aaa.index;b=bbb.index;if(a!==b){return a-b}a=(aaa.dist);b=(bbb.dist);if(a!==b){return a-b}a=aaa.item.deprecated;b=bbb.item.deprecated;if(a!==b){return a-b}a=(aaa.item.crate!==preferredCrate);b=(bbb.item.crate!==preferredCrate);if(a!==b){return a-b}a=aaa.word.length;b=bbb.word.length;if(a!==b){return a-b}a=aaa.word;b=bbb.word;if(a!==b){return(a>b?+1:-1)}if((aaa.item.ty===TY_PRIMITIVE&&bbb.item.ty!==TY_KEYWORD)||(aaa.item.ty===TY_KEYWORD&&bbb.item.ty!==TY_PRIMITIVE)){return-1}if((bbb.item.ty===TY_PRIMITIVE&&aaa.item.ty!==TY_PRIMITIVE)||(bbb.item.ty===TY_KEYWORD&&aaa.item.ty!==TY_KEYWORD)){return 1}a=(aaa.item.desc==="");b=(bbb.item.desc==="");if(a!==b){return a-b}a=aaa.item.ty;b=bbb.item.ty;if(a!==b){return a-b}a=aaa.item.path;b=bbb.item.path;if(a!==b){return(a>b?+1:-1)}return 0});let nameSplit=null;if(parsedQuery.elems.length===1){const hasPath=typeof parsedQuery.elems[0].path==="undefined";nameSplit=hasPath?null:parsedQuery.elems[0].path}for(const result of result_list){if(result.dontValidate){continue}const name=result.item.name.toLowerCase(),path=result.item.path.toLowerCase(),parent=result.item.parent;if(!isType&&!validateResult(name,path,nameSplit,parent)){result.id=-1}}return transformResults(result_list)}function checkGenerics(fnType,queryElem,whereClause,mgensInout){return unifyFunctionTypes(fnType.generics,queryElem.generics,whereClause,mgensInout,mgens=>{if(mgensInout){for(const[fid,qid]of mgens.entries()){mgensInout.set(fid,qid)}}return true})}function unifyFunctionTypes(fnTypesIn,queryElems,whereClause,mgensIn,solutionCb){let mgens=new Map(mgensIn);if(queryElems.length===0){return!solutionCb||solutionCb(mgens)}if(!fnTypesIn||fnTypesIn.length===0){return false}const ql=queryElems.length;let fl=fnTypesIn.length;let fnTypes=fnTypesIn.slice();const backtracking=[];let i=0;let j=0;const backtrack=()=>{while(backtracking.length!==0){const{fnTypesScratch,mgensScratch,queryElemsOffset,fnTypesOffset,unbox,}=backtracking.pop();mgens=new Map(mgensScratch);const fnType=fnTypesScratch[fnTypesOffset];const queryElem=queryElems[queryElemsOffset];if(unbox){if(fnType.id<0){if(mgens.has(fnType.id)&&mgens.get(fnType.id)!==0){continue}mgens.set(fnType.id,0)}const generics=fnType.id<0?whereClause[(-fnType.id)-1]:fnType.generics;fnTypes=fnTypesScratch.toSpliced(fnTypesOffset,1,...generics);fl=fnTypes.length;i=queryElemsOffset-1}else{if(fnType.id<0){if(mgens.has(fnType.id)&&mgens.get(fnType.id)!==queryElem.id){continue}mgens.set(fnType.id,queryElem.id)}fnTypes=fnTypesScratch.slice();fl=fnTypes.length;const tmp=fnTypes[queryElemsOffset];fnTypes[queryElemsOffset]=fnTypes[fnTypesOffset];fnTypes[fnTypesOffset]=tmp;i=queryElemsOffset}return true}return false};for(i=0;i!==ql;++i){const queryElem=queryElems[i];const matchCandidates=[];let fnTypesScratch=null;let mgensScratch=null;for(j=i;j!==fl;++j){const fnType=fnTypes[j];if(unifyFunctionTypeIsMatchCandidate(fnType,queryElem,whereClause,mgens)){if(!fnTypesScratch){fnTypesScratch=fnTypes.slice()}unifyFunctionTypes(fnType.generics,queryElem.generics,whereClause,mgens,mgensScratch=>{matchCandidates.push({fnTypesScratch,mgensScratch,queryElemsOffset:i,fnTypesOffset:j,unbox:false,});return false})}if(unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens)){if(!fnTypesScratch){fnTypesScratch=fnTypes.slice()}if(!mgensScratch){mgensScratch=new Map(mgens)}backtracking.push({fnTypesScratch,mgensScratch,queryElemsOffset:i,fnTypesOffset:j,unbox:true,})}}if(matchCandidates.length===0){if(backtrack()){continue}else{return false}}const{fnTypesOffset:candidate,mgensScratch:mgensNew}=matchCandidates.pop();if(fnTypes[candidate].id<0&&queryElems[i].id<0){mgens.set(fnTypes[candidate].id,queryElems[i].id)}for(const[fid,qid]of mgensNew){mgens.set(fid,qid)}const tmp=fnTypes[candidate];fnTypes[candidate]=fnTypes[i];fnTypes[i]=tmp;for(const otherCandidate of matchCandidates){backtracking.push(otherCandidate)}while(i===(ql-1)&&solutionCb&&!solutionCb(mgens)){if(!backtrack()){return false}}}return true}function unifyFunctionTypeIsMatchCandidate(fnType,queryElem,whereClause,mgens){if(!typePassesFilter(queryElem.typeFilter,fnType.ty)){return false}if(fnType.id<0&&queryElem.id<0){if(mgens.has(fnType.id)&&mgens.get(fnType.id)!==queryElem.id){return false}for(const[fid,qid]of mgens.entries()){if(fnType.id!==fid&&queryElem.id===qid){return false}if(fnType.id===fid&&queryElem.id!==qid){return false}}}else{if(queryElem.id===typeNameIdOfArrayOrSlice&&(fnType.id===typeNameIdOfSlice||fnType.id===typeNameIdOfArray)){}else if(fnType.id!==queryElem.id){return false}if(fnType.generics.length===0&&queryElem.generics.length!==0){return false}const queryElemPathLength=queryElem.pathWithoutLast.length;if(queryElemPathLength>0){const fnTypePath=fnType.path!==undefined&&fnType.path!==null?fnType.path.split("::"):[];if(queryElemPathLength>fnTypePath.length){return false}let i=0;for(const path of fnTypePath){if(path===queryElem.pathWithoutLast[i]){i+=1;if(i>=queryElemPathLength){break}}}if(i=0){if(!whereClause){return false}if(mgens.has(fnType.id)&&mgens.get(fnType.id)!==0){return false}return checkIfInList(whereClause[(-fnType.id)-1],queryElem,whereClause)}else if(fnType.generics&&fnType.generics.length>0){return checkIfInList(fnType.generics,queryElem,whereClause)}return false}function checkIfInList(list,elem,whereClause){for(const entry of list){if(checkType(entry,elem,whereClause)){return true}}return false}function checkType(row,elem,whereClause){if(row.id===null){return row.generics.length>0?checkIfInList(row.generics,elem,whereClause):false}if(row.id<0&&elem.id>=0){const gid=(-row.id)-1;return checkIfInList(whereClause[gid],elem,whereClause)}if(row.id<0&&elem.id<0){return true}const matchesExact=row.id===elem.id;const matchesArrayOrSlice=elem.id===typeNameIdOfArrayOrSlice&&(row.id===typeNameIdOfSlice||row.id===typeNameIdOfArray);if((matchesExact||matchesArrayOrSlice)&&typePassesFilter(elem.typeFilter,row.ty)){if(elem.generics.length>0){return checkGenerics(row,elem,whereClause,new Map())}return true}return checkIfInList(row.generics,elem,whereClause)}function checkPath(contains,ty,maxEditDistance){if(contains.length===0){return 0}let ret_dist=maxEditDistance+1;const path=ty.path.split("::");if(ty.parent&&ty.parent.name){path.push(ty.parent.name.toLowerCase())}const length=path.length;const clength=contains.length;if(clength>length){return maxEditDistance+1}for(let i=0;ilength){break}let dist_total=0;let aborted=false;for(let x=0;xmaxEditDistance){aborted=true;break}dist_total+=dist}if(!aborted){ret_dist=Math.min(ret_dist,Math.round(dist_total/clength))}}return ret_dist}function typePassesFilter(filter,type){if(filter<=NO_TYPE_FILTER||filter===type)return true;const name=itemTypes[type];switch(itemTypes[filter]){case"constant":return name==="associatedconstant";case"fn":return name==="method"||name==="tymethod";case"type":return name==="primitive"||name==="associatedtype";case"trait":return name==="traitalias"}return false}function createAliasFromItem(item){return{crate:item.crate,name:item.name,path:item.path,desc:item.desc,ty:item.ty,parent:item.parent,type:item.type,is_alias:true,deprecated:item.deprecated,implDisambiguator:item.implDisambiguator,}}function handleAliases(ret,query,filterCrates,currentCrate){const lowerQuery=query.toLowerCase();const aliases=[];const crateAliases=[];if(filterCrates!==null){if(ALIASES.has(filterCrates)&&ALIASES.get(filterCrates).has(lowerQuery)){const query_aliases=ALIASES.get(filterCrates).get(lowerQuery);for(const alias of query_aliases){aliases.push(createAliasFromItem(searchIndex[alias]))}}}else{for(const[crate,crateAliasesIndex]of ALIASES){if(crateAliasesIndex.has(lowerQuery)){const pushTo=crate===currentCrate?crateAliases:aliases;const query_aliases=crateAliasesIndex.get(lowerQuery);for(const alias of query_aliases){pushTo.push(createAliasFromItem(searchIndex[alias]))}}}}const sortFunc=(aaa,bbb)=>{if(aaa.path{alias.alias=query;const res=buildHrefAndPath(alias);alias.displayPath=pathSplitter(res[0]);alias.fullPath=alias.displayPath+alias.name;alias.href=res[1];ret.others.unshift(alias);if(ret.others.length>MAX_RESULTS){ret.others.pop()}};aliases.forEach(pushFunc);crateAliases.forEach(pushFunc)}function addIntoResults(results,fullId,id,index,dist,path_dist,maxEditDistance){const inBounds=dist<=maxEditDistance||index!==-1;if(dist===0||(!parsedQuery.literalSearch&&inBounds)){if(results.has(fullId)){const result=results.get(fullId);if(result.dontValidate||result.dist<=dist){return}}results.set(fullId,{id:id,index:index,dontValidate:parsedQuery.literalSearch,dist:dist,path_dist:path_dist,})}}function handleSingleArg(row,pos,elem,results_others,results_in_args,results_returned,maxEditDistance){if(!row||(filterCrates!==null&&row.crate!==filterCrates)){return}let index=-1,path_dist=0;const fullId=row.id;const searchWord=searchWords[pos];const in_args=row.type&&row.type.inputs&&checkIfInList(row.type.inputs,elem,row.type.where_clause);if(in_args){addIntoResults(results_in_args,fullId,pos,-1,0,0,maxEditDistance)}const returned=row.type&&row.type.output&&checkIfInList(row.type.output,elem,row.type.where_clause);if(returned){addIntoResults(results_returned,fullId,pos,-1,0,0,maxEditDistance)}if(!typePassesFilter(elem.typeFilter,row.ty)){return}const row_index=row.normalizedName.indexOf(elem.pathLast);const word_index=searchWord.indexOf(elem.pathLast);if(row_index===-1){index=word_index}else if(word_index===-1){index=row_index}else if(word_index1){path_dist=checkPath(elem.pathWithoutLast,row,maxEditDistance);if(path_dist>maxEditDistance){return}}if(parsedQuery.literalSearch){if(searchWord===elem.name){addIntoResults(results_others,fullId,pos,index,0,path_dist)}return}const dist=editDistance(searchWord,elem.pathLast,maxEditDistance);if(index===-1&&dist+path_dist>maxEditDistance){return}addIntoResults(results_others,fullId,pos,index,dist,path_dist,maxEditDistance)}function handleArgs(row,pos,results){if(!row||(filterCrates!==null&&row.crate!==filterCrates)||!row.type){return}if(!unifyFunctionTypes(row.type.inputs,parsedQuery.elems,row.type.where_clause,null,mgens=>{return unifyFunctionTypes(row.type.output,parsedQuery.returned,row.type.where_clause,mgens)})){return}addIntoResults(results,row.id,pos,0,0,0,Number.MAX_VALUE)}function innerRunQuery(){let elem,i,nSearchWords,in_returned,row;let queryLen=0;for(const elem of parsedQuery.elems){queryLen+=elem.name.length}for(const elem of parsedQuery.returned){queryLen+=elem.name.length}const maxEditDistance=Math.floor(queryLen/3);const genericSymbols=new Map();function convertNameToId(elem){if(typeNameIdMap.has(elem.pathLast)){elem.id=typeNameIdMap.get(elem.pathLast)}else if(!parsedQuery.literalSearch){let match=null;let matchDist=maxEditDistance+1;let matchName="";for(const[name,id]of typeNameIdMap){const dist=editDistance(name,elem.pathLast,maxEditDistance);if(dist<=matchDist&&dist<=maxEditDistance){if(dist===matchDist&&matchName>name){continue}match=id;matchDist=dist;matchName=name}}if(match!==null){parsedQuery.correction=matchName}elem.id=match}if((elem.id===null&&parsedQuery.totalElems>1&&elem.typeFilter===-1&&elem.generics.length===0)||elem.typeFilter===TY_GENERIC){if(genericSymbols.has(elem.name)){elem.id=genericSymbols.get(elem.name)}else{elem.id=-(genericSymbols.size+1);genericSymbols.set(elem.name,elem.id)}if(elem.typeFilter===-1&&elem.name.length>=3){const maxPartDistance=Math.floor(elem.name.length/3);let matchDist=maxPartDistance+1;let matchName="";for(const name of typeNameIdMap.keys()){const dist=editDistance(name,elem.name,maxPartDistance);if(dist<=matchDist&&dist<=maxPartDistance){if(dist===matchDist&&matchName>name){continue}matchDist=dist;matchName=name}}if(matchName!==""){parsedQuery.proposeCorrectionFrom=elem.name;parsedQuery.proposeCorrectionTo=matchName}}elem.typeFilter=TY_GENERIC}if(elem.generics.length>0&&elem.typeFilter===TY_GENERIC){parsedQuery.error=["Generic type parameter ",elem.name," does not accept generic parameters",]}for(const elem2 of elem.generics){convertNameToId(elem2)}}for(const elem of parsedQuery.elems){convertNameToId(elem)}for(const elem of parsedQuery.returned){convertNameToId(elem)}if(parsedQuery.foundElems===1){if(parsedQuery.elems.length===1){elem=parsedQuery.elems[0];for(i=0,nSearchWords=searchWords.length;i0){for(i=0,nSearchWords=searchWords.length;i-1||path.indexOf(key)>-1||(parent!==undefined&&parent.name!==undefined&&parent.name.toLowerCase().indexOf(key)>-1)||editDistance(name,key,maxEditDistance)<=maxEditDistance)){return false}}return true}function nextTab(direction){const next=(searchState.currentTab+direction+3)%searchState.focusedByTab.length;searchState.focusedByTab[searchState.currentTab]=document.activeElement;printTab(next);focusSearchResult()}function focusSearchResult(){const target=searchState.focusedByTab[searchState.currentTab]||document.querySelectorAll(".search-results.active a").item(0)||document.querySelectorAll("#search-tabs button").item(searchState.currentTab);searchState.focusedByTab[searchState.currentTab]=null;if(target){target.focus()}}function buildHrefAndPath(item){let displayPath;let href;const type=itemTypes[item.ty];const name=item.name;let path=item.path;if(type==="mod"){displayPath=path+"::";href=ROOT_PATH+path.replace(/::/g,"/")+"/"+name+"/index.html"}else if(type==="import"){displayPath=item.path+"::";href=ROOT_PATH+item.path.replace(/::/g,"/")+"/index.html#reexport."+name}else if(type==="primitive"||type==="keyword"){displayPath="";href=ROOT_PATH+path.replace(/::/g,"/")+"/"+type+"."+name+".html"}else if(type==="externcrate"){displayPath="";href=ROOT_PATH+name+"/index.html"}else if(item.parent!==undefined){const myparent=item.parent;let anchor=type+"."+name;const parentType=itemTypes[myparent.ty];let pageType=parentType;let pageName=myparent.name;if(parentType==="primitive"){displayPath=myparent.name+"::"}else if(type==="structfield"&&parentType==="variant"){const enumNameIdx=item.path.lastIndexOf("::");const enumName=item.path.substr(enumNameIdx+2);path=item.path.substr(0,enumNameIdx);displayPath=path+"::"+enumName+"::"+myparent.name+"::";anchor="variant."+myparent.name+".field."+name;pageType="enum";pageName=enumName}else{displayPath=path+"::"+myparent.name+"::"}if(item.implDisambiguator!==null){anchor=item.implDisambiguator+"/"+anchor}href=ROOT_PATH+path.replace(/::/g,"/")+"/"+pageType+"."+pageName+".html#"+anchor}else{displayPath=item.path+"::";href=ROOT_PATH+item.path.replace(/::/g,"/")+"/"+type+"."+name+".html"}return[displayPath,href]}function pathSplitter(path){const tmp=""+path.replace(/::/g,"::");if(tmp.endsWith("")){return tmp.slice(0,tmp.length-6)}return tmp}function addTab(array,query,display){let extraClass="";if(display===true){extraClass=" active"}const output=document.createElement("div");let length=0;if(array.length>0){output.className="search-results "+extraClass;array.forEach(item=>{const name=item.name;const type=itemTypes[item.ty];const longType=longItemTypes[item.ty];const typeName=longType.length!==0?`${longType}`:"?";length+=1;const link=document.createElement("a");link.className="result-"+type;link.href=item.href;const resultName=document.createElement("div");resultName.className="result-name";resultName.insertAdjacentHTML("beforeend",`${typeName}`);link.appendChild(resultName);let alias=" ";if(item.is_alias){alias=`
\ +${item.alias} - see \ +
`}resultName.insertAdjacentHTML("beforeend",`
${alias}\ +${item.displayPath}${name}\ +
`);const description=document.createElement("div");description.className="desc";description.insertAdjacentHTML("beforeend",item.desc);link.appendChild(description);output.appendChild(link)})}else if(query.error===null){output.className="search-failed"+extraClass;output.innerHTML="No results :(
"+"Try on DuckDuckGo?

"+"Or try looking in one of these:"}return[output,length]}function makeTabHeader(tabNb,text,nbElems){const fmtNbElems=nbElems<10?`\u{2007}(${nbElems})\u{2007}\u{2007}`:nbElems<100?`\u{2007}(${nbElems})\u{2007}`:`\u{2007}(${nbElems})`;if(searchState.currentTab===tabNb){return""}return""}function showResults(results,go_to_first,filterCrates){const search=searchState.outputElement();if(go_to_first||(results.others.length===1&&getSettingValue("go-to-only-result")==="true")){window.onunload=()=>{};searchState.removeQueryParameters();const elem=document.createElement("a");elem.href=results.others[0].href;removeClass(elem,"active");document.body.appendChild(elem);elem.click();return}if(results.query===undefined){results.query=parseQuery(searchState.input.value)}currentResults=results.query.userQuery;const ret_others=addTab(results.others,results.query,true);const ret_in_args=addTab(results.in_args,results.query,false);const ret_returned=addTab(results.returned,results.query,false);let currentTab=searchState.currentTab;if((currentTab===0&&ret_others[1]===0)||(currentTab===1&&ret_in_args[1]===0)||(currentTab===2&&ret_returned[1]===0)){if(ret_others[1]!==0){currentTab=0}else if(ret_in_args[1]!==0){currentTab=1}else if(ret_returned[1]!==0){currentTab=2}}let crates="";const crates_list=Object.keys(rawSearchIndex);if(crates_list.length>1){crates=" in 
"}let output=`

Results${crates}

`;if(results.query.error!==null){const error=results.query.error;error.forEach((value,index)=>{value=value.split("<").join("<").split(">").join(">");if(index%2!==0){error[index]=`${value.replaceAll(" ", " ")}`}else{error[index]=value}});output+=`

Query parser error: "${error.join("")}".

`;output+="
"+makeTabHeader(0,"In Names",ret_others[1])+"
";currentTab=0}else if(results.query.foundElems<=1&&results.query.returned.length===0){output+="
"+makeTabHeader(0,"In Names",ret_others[1])+makeTabHeader(1,"In Parameters",ret_in_args[1])+makeTabHeader(2,"In Return Types",ret_returned[1])+"
"}else{const signatureTabTitle=results.query.elems.length===0?"In Function Return Types":results.query.returned.length===0?"In Function Parameters":"In Function Signatures";output+="
"+makeTabHeader(0,signatureTabTitle,ret_others[1])+"
";currentTab=0}if(results.query.correction!==null){const orig=results.query.returned.length>0?results.query.returned[0].name:results.query.elems[0].name;output+="

"+`Type "${orig}" not found. `+"Showing results for closest type name "+`"${results.query.correction}" instead.

`}if(results.query.proposeCorrectionFrom!==null){const orig=results.query.proposeCorrectionFrom;const targ=results.query.proposeCorrectionTo;output+="

"+`Type "${orig}" not found and used as generic parameter. `+`Consider searching for "${targ}" instead.

`}const resultsElem=document.createElement("div");resultsElem.id="results";resultsElem.appendChild(ret_others[0]);resultsElem.appendChild(ret_in_args[0]);resultsElem.appendChild(ret_returned[0]);search.innerHTML=output;const crateSearch=document.getElementById("crate-search");if(crateSearch){crateSearch.addEventListener("input",updateCrate)}search.appendChild(resultsElem);searchState.showResults(search);const elems=document.getElementById("search-tabs").childNodes;searchState.focusedByTab=[];let i=0;for(const elem of elems){const j=i;elem.onclick=()=>printTab(j);searchState.focusedByTab.push(null);i+=1}printTab(currentTab)}function updateSearchHistory(url){if(!browserSupportsHistoryApi()){return}const params=searchState.getQueryStringParams();if(!history.state&&!params.search){history.pushState(null,"",url)}else{history.replaceState(null,"",url)}}function search(e,forced){if(e){e.preventDefault()}const query=parseQuery(searchState.input.value.trim());let filterCrates=getFilterCrates();if(!forced&&query.userQuery===currentResults){if(query.userQuery.length>0){putBackSearch()}return}searchState.setLoadingSearch();const params=searchState.getQueryStringParams();if(filterCrates===null&¶ms["filter-crate"]!==undefined){filterCrates=params["filter-crate"]}searchState.title="Results for "+query.original+" - Rust";updateSearchHistory(buildUrl(query.original,filterCrates));showResults(execQuery(query,searchWords,filterCrates,window.currentCrate),params.go_to_first,filterCrates)}function buildItemSearchTypeAll(types,lowercasePaths){return types.map(type=>buildItemSearchType(type,lowercasePaths))}function buildItemSearchType(type,lowercasePaths){const PATH_INDEX_DATA=0;const GENERICS_DATA=1;let pathIndex,generics;if(typeof type==="number"){pathIndex=type;generics=[]}else{pathIndex=type[PATH_INDEX_DATA];generics=buildItemSearchTypeAll(type[GENERICS_DATA],lowercasePaths)}if(pathIndex<0){return{id:pathIndex,ty:TY_GENERIC,path:null,generics,}}if(pathIndex===0){return{id:null,ty:null,path:null,generics,}}const item=lowercasePaths[pathIndex-1];return{id:buildTypeMapIndex(item.name),ty:item.ty,path:item.path,generics,}}function buildFunctionSearchType(functionSearchType,lowercasePaths){const INPUTS_DATA=0;const OUTPUT_DATA=1;if(functionSearchType===0){return null}let inputs,output;if(typeof functionSearchType[INPUTS_DATA]==="number"){inputs=[buildItemSearchType(functionSearchType[INPUTS_DATA],lowercasePaths)]}else{inputs=buildItemSearchTypeAll(functionSearchType[INPUTS_DATA],lowercasePaths)}if(functionSearchType.length>1){if(typeof functionSearchType[OUTPUT_DATA]==="number"){output=[buildItemSearchType(functionSearchType[OUTPUT_DATA],lowercasePaths)]}else{output=buildItemSearchTypeAll(functionSearchType[OUTPUT_DATA],lowercasePaths)}}else{output=[]}const where_clause=[];const l=functionSearchType.length;for(let i=2;i2){path=itemPaths.has(elem[2])?itemPaths.get(elem[2]):lastPath;lastPath=path}lowercasePaths.push({ty:ty,name:name.toLowerCase(),path:path});paths[i]={ty:ty,name:name,path:path}}lastPath="";len=itemTypes.length;for(let i=0;i0?paths[itemParentIdxs[i]-1]:undefined,type:buildFunctionSearchType(itemFunctionSearchTypes[i],lowercasePaths),id:id,normalizedName:word.indexOf("_")===-1?word:word.replace(/_/g,""),deprecated:deprecatedItems.has(i),implDisambiguator:implDisambiguator.has(i)?implDisambiguator.get(i):null,};id+=1;searchIndex.push(row);lastPath=row.path;crateSize+=1}if(aliases){const currentCrateAliases=new Map();ALIASES.set(crate,currentCrateAliases);for(const alias_name in aliases){if(!hasOwnPropertyRustdoc(aliases,alias_name)){continue}let currentNameAliases;if(currentCrateAliases.has(alias_name)){currentNameAliases=currentCrateAliases.get(alias_name)}else{currentNameAliases=[];currentCrateAliases.set(alias_name,currentNameAliases)}for(const local_alias of aliases[alias_name]){currentNameAliases.push(local_alias+currentIndex)}}}currentIndex+=crateSize}return searchWords}function onSearchSubmit(e){e.preventDefault();searchState.clearInputTimeout();search()}function putBackSearch(){const search_input=searchState.input;if(!searchState.input){return}if(search_input.value!==""&&!searchState.isDisplayed()){searchState.showResults();if(browserSupportsHistoryApi()){history.replaceState(null,"",buildUrl(search_input.value,getFilterCrates()))}document.title=searchState.title}}function registerSearchEvents(){const params=searchState.getQueryStringParams();if(searchState.input.value===""){searchState.input.value=params.search||""}const searchAfter500ms=()=>{searchState.clearInputTimeout();if(searchState.input.value.length===0){searchState.hideResults()}else{searchState.timeout=setTimeout(search,500)}};searchState.input.onkeyup=searchAfter500ms;searchState.input.oninput=searchAfter500ms;document.getElementsByClassName("search-form")[0].onsubmit=onSearchSubmit;searchState.input.onchange=e=>{if(e.target!==document.activeElement){return}searchState.clearInputTimeout();setTimeout(search,0)};searchState.input.onpaste=searchState.input.onchange;searchState.outputElement().addEventListener("keydown",e=>{if(e.altKey||e.ctrlKey||e.shiftKey||e.metaKey){return}if(e.which===38){const previous=document.activeElement.previousElementSibling;if(previous){previous.focus()}else{searchState.focus()}e.preventDefault()}else if(e.which===40){const next=document.activeElement.nextElementSibling;if(next){next.focus()}const rect=document.activeElement.getBoundingClientRect();if(window.innerHeight-rect.bottom{if(e.which===40){focusSearchResult();e.preventDefault()}});searchState.input.addEventListener("focus",()=>{putBackSearch()});searchState.input.addEventListener("blur",()=>{searchState.input.placeholder=searchState.input.origPlaceholder});if(browserSupportsHistoryApi()){const previousTitle=document.title;window.addEventListener("popstate",e=>{const params=searchState.getQueryStringParams();document.title=previousTitle;currentResults=null;if(params.search&¶ms.search.length>0){searchState.input.value=params.search;search(e)}else{searchState.input.value="";searchState.hideResults()}})}window.onpageshow=()=>{const qSearch=searchState.getQueryStringParams().search;if(searchState.input.value===""&&qSearch){searchState.input.value=qSearch}search()}}function updateCrate(ev){if(ev.target.value==="all crates"){const query=searchState.input.value.trim();updateSearchHistory(buildUrl(query,null))}currentResults=null;search(undefined,true)}const searchWords=buildIndex(rawSearchIndex);if(typeof window!=="undefined"){registerSearchEvents();if(window.searchState.getQueryStringParams().search){search()}}if(typeof exports!=="undefined"){exports.initSearch=initSearch;exports.execQuery=execQuery;exports.parseQuery=parseQuery}return searchWords}if(typeof window!=="undefined"){window.initSearch=initSearch;if(window.searchIndex!==undefined){initSearch(window.searchIndex)}}else{initSearch({})}})() \ No newline at end of file diff --git a/docs/macos_docs/static.files/settings-74424d7eec62a23e.js b/docs/macos_docs/static.files/settings-74424d7eec62a23e.js new file mode 100644 index 00000000..3014f75c --- /dev/null +++ b/docs/macos_docs/static.files/settings-74424d7eec62a23e.js @@ -0,0 +1,17 @@ +"use strict";(function(){const isSettingsPage=window.location.pathname.endsWith("/settings.html");function changeSetting(settingName,value){if(settingName==="theme"){const useSystem=value==="system preference"?"true":"false";updateLocalStorage("use-system-theme",useSystem)}updateLocalStorage(settingName,value);switch(settingName){case"theme":case"preferred-dark-theme":case"preferred-light-theme":updateTheme();updateLightAndDark();break;case"line-numbers":if(value===true){window.rustdoc_add_line_numbers_to_examples()}else{window.rustdoc_remove_line_numbers_from_examples()}break}}function showLightAndDark(){removeClass(document.getElementById("preferred-light-theme"),"hidden");removeClass(document.getElementById("preferred-dark-theme"),"hidden")}function hideLightAndDark(){addClass(document.getElementById("preferred-light-theme"),"hidden");addClass(document.getElementById("preferred-dark-theme"),"hidden")}function updateLightAndDark(){const useSystem=getSettingValue("use-system-theme");if(useSystem==="true"||(useSystem===null&&getSettingValue("theme")===null)){showLightAndDark()}else{hideLightAndDark()}}function setEvents(settingsElement){updateLightAndDark();onEachLazy(settingsElement.querySelectorAll("input[type=\"checkbox\"]"),toggle=>{const settingId=toggle.id;const settingValue=getSettingValue(settingId);if(settingValue!==null){toggle.checked=settingValue==="true"}toggle.onchange=()=>{changeSetting(toggle.id,toggle.checked)}});onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"),elem=>{const settingId=elem.name;let settingValue=getSettingValue(settingId);if(settingId==="theme"){const useSystem=getSettingValue("use-system-theme");if(useSystem==="true"||settingValue===null){settingValue=useSystem==="false"?"light":"system preference"}}if(settingValue!==null&&settingValue!=="null"){elem.checked=settingValue===elem.value}elem.addEventListener("change",ev=>{changeSetting(ev.target.name,ev.target.value)})})}function buildSettingsPageSections(settings){let output="";for(const setting of settings){const js_data_name=setting["js_name"];const setting_name=setting["name"];if(setting["options"]!==undefined){output+=`\ +
+
${setting_name}
+
`;onEach(setting["options"],option=>{const checked=option===setting["default"]?" checked":"";const full=`${js_data_name}-${option.replace(/ /g,"-")}`;output+=`\ + `});output+=`\ +
+
`}else{const checked=setting["default"]===true?" checked":"";output+=`\ +
\ + \ +
`}}return output}function buildSettingsPage(){const theme_names=getVar("themes").split(",").filter(t=>t);theme_names.push("light","dark","ayu");const settings=[{"name":"Theme","js_name":"theme","default":"system preference","options":theme_names.concat("system preference"),},{"name":"Preferred light theme","js_name":"preferred-light-theme","default":"light","options":theme_names,},{"name":"Preferred dark theme","js_name":"preferred-dark-theme","default":"dark","options":theme_names,},{"name":"Auto-hide item contents for large items","js_name":"auto-hide-large-items","default":true,},{"name":"Auto-hide item methods' documentation","js_name":"auto-hide-method-docs","default":false,},{"name":"Auto-hide trait implementation documentation","js_name":"auto-hide-trait-implementations","default":false,},{"name":"Directly go to item in search if there is only one result","js_name":"go-to-only-result","default":false,},{"name":"Show line numbers on code examples","js_name":"line-numbers","default":false,},{"name":"Disable keyboard shortcuts","js_name":"disable-shortcuts","default":false,},];const elementKind=isSettingsPage?"section":"div";const innerHTML=`
${buildSettingsPageSections(settings)}
`;const el=document.createElement(elementKind);el.id="settings";if(!isSettingsPage){el.className="popover"}el.innerHTML=innerHTML;if(isSettingsPage){document.getElementById(MAIN_ID).appendChild(el)}else{el.setAttribute("tabindex","-1");getSettingsButton().appendChild(el)}return el}const settingsMenu=buildSettingsPage();function displaySettings(){settingsMenu.style.display=""}function settingsBlurHandler(event){blurHandler(event,getSettingsButton(),window.hidePopoverMenus)}if(isSettingsPage){getSettingsButton().onclick=event=>{event.preventDefault()}}else{const settingsButton=getSettingsButton();const settingsMenu=document.getElementById("settings");settingsButton.onclick=event=>{if(elemIsInParent(event.target,settingsMenu)){return}event.preventDefault();const shouldDisplaySettings=settingsMenu.style.display==="none";window.hideAllModals();if(shouldDisplaySettings){displaySettings()}};settingsButton.onblur=settingsBlurHandler;settingsButton.querySelector("a").onblur=settingsBlurHandler;onEachLazy(settingsMenu.querySelectorAll("input"),el=>{el.onblur=settingsBlurHandler});settingsMenu.onblur=settingsBlurHandler}setTimeout(()=>{setEvents(settingsMenu);if(!isSettingsPage){displaySettings()}removeClass(getSettingsButton(),"rotate")},0)})() \ No newline at end of file diff --git a/docs/macos_docs/static.files/src-script-3280b574d94e47b4.js b/docs/macos_docs/static.files/src-script-3280b574d94e47b4.js new file mode 100644 index 00000000..9ea88921 --- /dev/null +++ b/docs/macos_docs/static.files/src-script-3280b574d94e47b4.js @@ -0,0 +1 @@ +"use strict";(function(){const rootPath=getVar("root-path");const NAME_OFFSET=0;const DIRS_OFFSET=1;const FILES_OFFSET=2;const RUSTDOC_MOBILE_BREAKPOINT=700;function closeSidebarIfMobile(){if(window.innerWidth"){addClass(document.documentElement,"src-sidebar-expanded");child.innerText="<";updateLocalStorage("source-sidebar-show","true")}else{removeClass(document.documentElement,"src-sidebar-expanded");child.innerText=">";updateLocalStorage("source-sidebar-show","false")}}function createSidebarToggle(){const sidebarToggle=document.createElement("div");sidebarToggle.id="src-sidebar-toggle";const inner=document.createElement("button");if(getCurrentValue("source-sidebar-show")==="true"){inner.innerText="<"}else{inner.innerText=">"}inner.onclick=toggleSidebar;sidebarToggle.appendChild(inner);return sidebarToggle}function createSrcSidebar(){const container=document.querySelector("nav.sidebar");const sidebarToggle=createSidebarToggle();container.insertBefore(sidebarToggle,container.firstChild);const sidebar=document.createElement("div");sidebar.id="src-sidebar";let hasFoundFile=false;const title=document.createElement("div");title.className="title";title.innerText="Files";sidebar.appendChild(title);Object.keys(srcIndex).forEach(key=>{srcIndex[key][NAME_OFFSET]=key;hasFoundFile=createDirEntry(srcIndex[key],sidebar,"",hasFoundFile)});container.appendChild(sidebar);const selected_elem=sidebar.getElementsByClassName("selected")[0];if(typeof selected_elem!=="undefined"){selected_elem.focus()}}const lineNumbersRegex=/^#?(\d+)(?:-(\d+))?$/;function highlightSrcLines(match){if(typeof match==="undefined"){match=window.location.hash.match(lineNumbersRegex)}if(!match){return}let from=parseInt(match[1],10);let to=from;if(typeof match[2]!=="undefined"){to=parseInt(match[2],10)}if(to{onEachLazy(e.getElementsByTagName("a"),i_e=>{removeClass(i_e,"line-highlighted")})});for(let i=from;i<=to;++i){elem=document.getElementById(i);if(!elem){break}addClass(elem,"line-highlighted")}}const handleSrcHighlight=(function(){let prev_line_id=0;const set_fragment=name=>{const x=window.scrollX,y=window.scrollY;if(browserSupportsHistoryApi()){history.replaceState(null,null,"#"+name);highlightSrcLines()}else{location.replace("#"+name)}window.scrollTo(x,y)};return ev=>{let cur_line_id=parseInt(ev.target.id,10);if(isNaN(cur_line_id)||ev.ctrlKey||ev.altKey||ev.metaKey){return}ev.preventDefault();if(ev.shiftKey&&prev_line_id){if(prev_line_id>cur_line_id){const tmp=prev_line_id;prev_line_id=cur_line_id;cur_line_id=tmp}set_fragment(prev_line_id+"-"+cur_line_id)}else{prev_line_id=cur_line_id;set_fragment(cur_line_id)}}}());window.addEventListener("hashchange",()=>{const match=window.location.hash.match(lineNumbersRegex);if(match){return highlightSrcLines(match)}});onEachLazy(document.getElementsByClassName("src-line-numbers"),el=>{el.addEventListener("click",handleSrcHighlight)});highlightSrcLines();window.createSrcSidebar=createSrcSidebar})() \ No newline at end of file diff --git a/docs/macos_docs/static.files/storage-fec3eaa3851e447d.js b/docs/macos_docs/static.files/storage-fec3eaa3851e447d.js new file mode 100644 index 00000000..a687118f --- /dev/null +++ b/docs/macos_docs/static.files/storage-fec3eaa3851e447d.js @@ -0,0 +1 @@ +"use strict";const builtinThemes=["light","dark","ayu"];const darkThemes=["dark","ayu"];window.currentTheme=document.getElementById("themeStyle");const settingsDataset=(function(){const settingsElement=document.getElementById("default-settings");return settingsElement&&settingsElement.dataset?settingsElement.dataset:null})();function getSettingValue(settingName){const current=getCurrentValue(settingName);if(current===null&&settingsDataset!==null){const def=settingsDataset[settingName.replace(/-/g,"_")];if(def!==undefined){return def}}return current}const localStoredTheme=getSettingValue("theme");function hasClass(elem,className){return elem&&elem.classList&&elem.classList.contains(className)}function addClass(elem,className){if(elem&&elem.classList){elem.classList.add(className)}}function removeClass(elem,className){if(elem&&elem.classList){elem.classList.remove(className)}}function onEach(arr,func,reversed){if(arr&&arr.length>0){if(reversed){for(let i=arr.length-1;i>=0;--i){if(func(arr[i])){return true}}}else{for(const elem of arr){if(func(elem)){return true}}}}return false}function onEachLazy(lazyArray,func,reversed){return onEach(Array.prototype.slice.call(lazyArray),func,reversed)}function updateLocalStorage(name,value){try{window.localStorage.setItem("rustdoc-"+name,value)}catch(e){}}function getCurrentValue(name){try{return window.localStorage.getItem("rustdoc-"+name)}catch(e){return null}}const getVar=(function getVar(name){const el=document.querySelector("head > meta[name='rustdoc-vars']");return el?el.attributes["data-"+name].value:null});function switchTheme(newThemeName,saveTheme){if(saveTheme){updateLocalStorage("theme",newThemeName)}document.documentElement.setAttribute("data-theme",newThemeName);if(builtinThemes.indexOf(newThemeName)!==-1){if(window.currentTheme){window.currentTheme.parentNode.removeChild(window.currentTheme);window.currentTheme=null}}else{const newHref=getVar("root-path")+newThemeName+getVar("resource-suffix")+".css";if(!window.currentTheme){if(document.readyState==="loading"){document.write(``);window.currentTheme=document.getElementById("themeStyle")}else{window.currentTheme=document.createElement("link");window.currentTheme.rel="stylesheet";window.currentTheme.id="themeStyle";window.currentTheme.href=newHref;document.documentElement.appendChild(window.currentTheme)}}else if(newHref!==window.currentTheme.href){window.currentTheme.href=newHref}}}const updateTheme=(function(){const mql=window.matchMedia("(prefers-color-scheme: dark)");function updateTheme(){if(getSettingValue("use-system-theme")!=="false"){const lightTheme=getSettingValue("preferred-light-theme")||"light";const darkTheme=getSettingValue("preferred-dark-theme")||"dark";updateLocalStorage("use-system-theme","true");switchTheme(mql.matches?darkTheme:lightTheme,true)}else{switchTheme(getSettingValue("theme"),false)}}mql.addEventListener("change",updateTheme);return updateTheme})();if(getSettingValue("use-system-theme")!=="false"&&window.matchMedia){if(getSettingValue("use-system-theme")===null&&getSettingValue("preferred-dark-theme")===null&&darkThemes.indexOf(localStoredTheme)>=0){updateLocalStorage("preferred-dark-theme",localStoredTheme)}}updateTheme();if(getSettingValue("source-sidebar-show")==="true"){addClass(document.documentElement,"src-sidebar-expanded")}window.addEventListener("pageshow",ev=>{if(ev.persisted){setTimeout(updateTheme,0)}}) \ No newline at end of file diff --git a/docs/macos_docs/static.files/wheel-7b819b6101059cd0.svg b/docs/macos_docs/static.files/wheel-7b819b6101059cd0.svg new file mode 100644 index 00000000..83c07f63 --- /dev/null +++ b/docs/macos_docs/static.files/wheel-7b819b6101059cd0.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/core/clone/trait.Clone.js b/docs/macos_docs/trait.impl/core/clone/trait.Clone.js new file mode 100644 index 00000000..3e851bff --- /dev/null +++ b/docs/macos_docs/trait.impl/core/clone/trait.Clone.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Clone for AudioChannelCount"],["impl Clone for StreamCreateError"],["impl Clone for Rect"],["impl Clone for Point"],["impl Clone for AudioSampleRate"],["impl Clone for CaptureConfig"],["impl Clone for MacosVideoFrameError"],["impl Clone for MetalVideoFramePlaneTexture"],["impl Clone for StreamError"],["impl Clone for Size"],["impl Clone for AudioCaptureConfig"],["impl Clone for CapturableWindow"],["impl Clone for IoSurface"],["impl Clone for CapturableDisplay"],["impl Clone for VideoFrameBitmapError"],["impl Clone for CapturableContentError"],["impl Clone for CapturePixelFormat"],["impl Clone for CaptureConfigError"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/core/cmp/trait.Eq.js b/docs/macos_docs/trait.impl/core/cmp/trait.Eq.js new file mode 100644 index 00000000..b63900ba --- /dev/null +++ b/docs/macos_docs/trait.impl/core/cmp/trait.Eq.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Eq for MetalVideoFramePlaneTexture"],["impl Eq for CapturePixelFormat"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/core/cmp/trait.PartialEq.js b/docs/macos_docs/trait.impl/core/cmp/trait.PartialEq.js new file mode 100644 index 00000000..4a9a1eed --- /dev/null +++ b/docs/macos_docs/trait.impl/core/cmp/trait.PartialEq.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl PartialEq for MetalVideoFramePlaneTexture"],["impl PartialEq for CapturePixelFormat"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/core/default/trait.Default.js b/docs/macos_docs/trait.impl/core/default/trait.Default.js new file mode 100644 index 00000000..a2b7900f --- /dev/null +++ b/docs/macos_docs/trait.impl/core/default/trait.Default.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Default for CapturableWindowFilter"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/core/error/trait.Error.js b/docs/macos_docs/trait.impl/core/error/trait.Error.js new file mode 100644 index 00000000..9c975b32 --- /dev/null +++ b/docs/macos_docs/trait.impl/core/error/trait.Error.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Error for MacosVideoFrameError"],["impl Error for StreamError"],["impl Error for VideoFrameBitmapError"],["impl Error for GetIoSurfaceError"],["impl Error for CapturableContentError"],["impl Error for StreamCreateError"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/core/fmt/trait.Debug.js b/docs/macos_docs/trait.impl/core/fmt/trait.Debug.js new file mode 100644 index 00000000..18a3d4a6 --- /dev/null +++ b/docs/macos_docs/trait.impl/core/fmt/trait.Debug.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Debug for CapturableWindow"],["impl Debug for GetIoSurfaceError"],["impl Debug for Point"],["impl Debug for StreamError"],["impl Debug for CapturableContentError"],["impl Debug for VideoFrameBitmapError"],["impl Debug for CaptureConfigError"],["impl Debug for CapturePixelFormat"],["impl Debug for CapturableDisplay"],["impl Debug for Size"],["impl Debug for MetalVideoFramePlaneTexture"],["impl Debug for AudioSampleRate"],["impl Debug for AudioFrame"],["impl Debug for StreamEvent"],["impl Debug for MacosVideoFrameError"],["impl Debug for VideoFrame"],["impl Debug for Rect"],["impl Debug for CaptureConfig"],["impl Debug for AudioCaptureConfig"],["impl Debug for AudioChannelCount"],["impl Debug for StreamStopError"],["impl Debug for StreamCreateError"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/core/fmt/trait.Display.js b/docs/macos_docs/trait.impl/core/fmt/trait.Display.js new file mode 100644 index 00000000..b35d80b2 --- /dev/null +++ b/docs/macos_docs/trait.impl/core/fmt/trait.Display.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Display for VideoFrameBitmapError"],["impl Display for StreamCreateError"],["impl Display for StreamError"],["impl Display for CapturableContentError"],["impl Display for GetIoSurfaceError"],["impl Display for MacosVideoFrameError"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/core/iter/traits/exact_size/trait.ExactSizeIterator.js b/docs/macos_docs/trait.impl/core/iter/traits/exact_size/trait.ExactSizeIterator.js new file mode 100644 index 00000000..3e32f850 --- /dev/null +++ b/docs/macos_docs/trait.impl/core/iter/traits/exact_size/trait.ExactSizeIterator.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl ExactSizeIterator for CapturableWindowIterator<'_>"],["impl ExactSizeIterator for CapturableDisplayIterator<'_>"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/core/iter/traits/iterator/trait.Iterator.js b/docs/macos_docs/trait.impl/core/iter/traits/iterator/trait.Iterator.js new file mode 100644 index 00000000..bd01b1d3 --- /dev/null +++ b/docs/macos_docs/trait.impl/core/iter/traits/iterator/trait.Iterator.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Iterator for CapturableDisplayIterator<'_>"],["impl Iterator for CapturableWindowIterator<'_>"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/core/marker/trait.Copy.js b/docs/macos_docs/trait.impl/core/marker/trait.Copy.js new file mode 100644 index 00000000..484a8ea0 --- /dev/null +++ b/docs/macos_docs/trait.impl/core/marker/trait.Copy.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Copy for AudioSampleRate"],["impl Copy for MetalVideoFramePlaneTexture"],["impl Copy for AudioChannelCount"],["impl Copy for CapturePixelFormat"],["impl Copy for Size"],["impl Copy for Rect"],["impl Copy for Point"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/core/marker/trait.Freeze.js b/docs/macos_docs/trait.impl/core/marker/trait.Freeze.js new file mode 100644 index 00000000..21533234 --- /dev/null +++ b/docs/macos_docs/trait.impl/core/marker/trait.Freeze.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Freeze for MetalVideoFramePlaneTexture",1,["crabgrab::feature::metal::MetalVideoFramePlaneTexture"]],["impl Freeze for MacosVideoFrameError",1,["crabgrab::feature::metal::MacosVideoFrameError"]],["impl Freeze for IoSurface",1,["crabgrab::feature::iosurface::IoSurface"]],["impl Freeze for GetIoSurfaceError",1,["crabgrab::feature::iosurface::GetIoSurfaceError"]],["impl Freeze for FrameBitmapBgraUnorm8x4",1,["crabgrab::feature::bitmap::FrameBitmapBgraUnorm8x4"]],["impl Freeze for FrameBitmapRgbaUnormPacked1010102",1,["crabgrab::feature::bitmap::FrameBitmapRgbaUnormPacked1010102"]],["impl Freeze for FrameBitmapRgbaF16x4",1,["crabgrab::feature::bitmap::FrameBitmapRgbaF16x4"]],["impl Freeze for VideoRange",1,["crabgrab::feature::bitmap::VideoRange"]],["impl Freeze for FrameBitmapYCbCr",1,["crabgrab::feature::bitmap::FrameBitmapYCbCr"]],["impl Freeze for FrameBitmap",1,["crabgrab::feature::bitmap::FrameBitmap"]],["impl Freeze for VideoFrameBitmapError",1,["crabgrab::feature::bitmap::VideoFrameBitmapError"]],["impl Freeze for Size",1,["crabgrab::util::Size"]],["impl Freeze for Point",1,["crabgrab::util::Point"]],["impl Freeze for Rect",1,["crabgrab::util::Rect"]],["impl Freeze for AudioSampleRate",1,["crabgrab::frame::AudioSampleRate"]],["impl Freeze for AudioChannelCount",1,["crabgrab::frame::AudioChannelCount"]],["impl<'data> Freeze for AudioChannelData<'data>",1,["crabgrab::frame::AudioChannelData"]],["impl<'data, T> Freeze for AudioChannelDataSamples<'data, T>",1,["crabgrab::frame::AudioChannelDataSamples"]],["impl Freeze for AudioBufferError",1,["crabgrab::frame::AudioBufferError"]],["impl Freeze for AudioFrame",1,["crabgrab::frame::AudioFrame"]],["impl !Freeze for VideoFrame",1,["crabgrab::frame::VideoFrame"]],["impl !Freeze for StreamEvent",1,["crabgrab::capture_stream::StreamEvent"]],["impl Freeze for StreamError",1,["crabgrab::capture_stream::StreamError"]],["impl Freeze for StreamCreateError",1,["crabgrab::capture_stream::StreamCreateError"]],["impl Freeze for StreamStopError",1,["crabgrab::capture_stream::StreamStopError"]],["impl Freeze for AudioCaptureConfig",1,["crabgrab::capture_stream::AudioCaptureConfig"]],["impl Freeze for CapturePixelFormat",1,["crabgrab::capture_stream::CapturePixelFormat"]],["impl Freeze for CaptureConfig",1,["crabgrab::capture_stream::CaptureConfig"]],["impl Freeze for CaptureConfigError",1,["crabgrab::capture_stream::CaptureConfigError"]],["impl Freeze for CaptureStream",1,["crabgrab::capture_stream::CaptureStream"]],["impl Freeze for CapturableContentError",1,["crabgrab::capturable_content::CapturableContentError"]],["impl Freeze for CapturableWindowFilter",1,["crabgrab::capturable_content::CapturableWindowFilter"]],["impl Freeze for CapturableContentFilter",1,["crabgrab::capturable_content::CapturableContentFilter"]],["impl Freeze for CapturableContent",1,["crabgrab::capturable_content::CapturableContent"]],["impl<'content> Freeze for CapturableWindowIterator<'content>",1,["crabgrab::capturable_content::CapturableWindowIterator"]],["impl<'content> Freeze for CapturableDisplayIterator<'content>",1,["crabgrab::capturable_content::CapturableDisplayIterator"]],["impl Freeze for CapturableWindow",1,["crabgrab::capturable_content::CapturableWindow"]],["impl Freeze for CapturableDisplay",1,["crabgrab::capturable_content::CapturableDisplay"]],["impl Freeze for CapturableApplication",1,["crabgrab::capturable_content::CapturableApplication"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/core/marker/trait.Send.js b/docs/macos_docs/trait.impl/core/marker/trait.Send.js new file mode 100644 index 00000000..93cb693f --- /dev/null +++ b/docs/macos_docs/trait.impl/core/marker/trait.Send.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Send for MetalVideoFramePlaneTexture",1,["crabgrab::feature::metal::MetalVideoFramePlaneTexture"]],["impl Send for MacosVideoFrameError",1,["crabgrab::feature::metal::MacosVideoFrameError"]],["impl !Send for IoSurface",1,["crabgrab::feature::iosurface::IoSurface"]],["impl Send for GetIoSurfaceError",1,["crabgrab::feature::iosurface::GetIoSurfaceError"]],["impl Send for FrameBitmapBgraUnorm8x4",1,["crabgrab::feature::bitmap::FrameBitmapBgraUnorm8x4"]],["impl Send for FrameBitmapRgbaUnormPacked1010102",1,["crabgrab::feature::bitmap::FrameBitmapRgbaUnormPacked1010102"]],["impl Send for FrameBitmapRgbaF16x4",1,["crabgrab::feature::bitmap::FrameBitmapRgbaF16x4"]],["impl Send for VideoRange",1,["crabgrab::feature::bitmap::VideoRange"]],["impl Send for FrameBitmapYCbCr",1,["crabgrab::feature::bitmap::FrameBitmapYCbCr"]],["impl Send for FrameBitmap",1,["crabgrab::feature::bitmap::FrameBitmap"]],["impl Send for VideoFrameBitmapError",1,["crabgrab::feature::bitmap::VideoFrameBitmapError"]],["impl Send for Size",1,["crabgrab::util::Size"]],["impl Send for Point",1,["crabgrab::util::Point"]],["impl Send for Rect",1,["crabgrab::util::Rect"]],["impl Send for AudioSampleRate",1,["crabgrab::frame::AudioSampleRate"]],["impl Send for AudioChannelCount",1,["crabgrab::frame::AudioChannelCount"]],["impl<'data> !Send for AudioChannelData<'data>",1,["crabgrab::frame::AudioChannelData"]],["impl<'data, T> !Send for AudioChannelDataSamples<'data, T>",1,["crabgrab::frame::AudioChannelDataSamples"]],["impl Send for AudioBufferError",1,["crabgrab::frame::AudioBufferError"]],["impl !Send for AudioFrame",1,["crabgrab::frame::AudioFrame"]],["impl !Send for StreamEvent",1,["crabgrab::capture_stream::StreamEvent"]],["impl Send for StreamError",1,["crabgrab::capture_stream::StreamError"]],["impl Send for AudioCaptureConfig",1,["crabgrab::capture_stream::AudioCaptureConfig"]],["impl Send for CapturePixelFormat",1,["crabgrab::capture_stream::CapturePixelFormat"]],["impl Send for CaptureConfig",1,["crabgrab::capture_stream::CaptureConfig"]],["impl Send for CaptureConfigError",1,["crabgrab::capture_stream::CaptureConfigError"]],["impl !Send for CaptureStream",1,["crabgrab::capture_stream::CaptureStream"]],["impl Send for CapturableContentError",1,["crabgrab::capturable_content::CapturableContentError"]],["impl Send for CapturableWindowFilter",1,["crabgrab::capturable_content::CapturableWindowFilter"]],["impl Send for CapturableContentFilter",1,["crabgrab::capturable_content::CapturableContentFilter"]],["impl Send for CapturableContent",1,["crabgrab::capturable_content::CapturableContent"]],["impl<'content> !Send for CapturableWindowIterator<'content>",1,["crabgrab::capturable_content::CapturableWindowIterator"]],["impl<'content> !Send for CapturableDisplayIterator<'content>",1,["crabgrab::capturable_content::CapturableDisplayIterator"]],["impl Send for CapturableWindow",1,["crabgrab::capturable_content::CapturableWindow"]],["impl Send for CapturableDisplay",1,["crabgrab::capturable_content::CapturableDisplay"]],["impl !Send for CapturableApplication",1,["crabgrab::capturable_content::CapturableApplication"]],["impl Send for VideoFrame"],["impl Send for StreamCreateError"],["impl Send for StreamStopError"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/core/marker/trait.StructuralEq.js b/docs/macos_docs/trait.impl/core/marker/trait.StructuralEq.js new file mode 100644 index 00000000..33b1b361 --- /dev/null +++ b/docs/macos_docs/trait.impl/core/marker/trait.StructuralEq.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl StructuralEq for CapturePixelFormat"],["impl StructuralEq for MetalVideoFramePlaneTexture"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/core/marker/trait.StructuralPartialEq.js b/docs/macos_docs/trait.impl/core/marker/trait.StructuralPartialEq.js new file mode 100644 index 00000000..6b4d46a9 --- /dev/null +++ b/docs/macos_docs/trait.impl/core/marker/trait.StructuralPartialEq.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl StructuralPartialEq for MetalVideoFramePlaneTexture"],["impl StructuralPartialEq for CapturePixelFormat"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/core/marker/trait.Sync.js b/docs/macos_docs/trait.impl/core/marker/trait.Sync.js new file mode 100644 index 00000000..a931012f --- /dev/null +++ b/docs/macos_docs/trait.impl/core/marker/trait.Sync.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Sync for MetalVideoFramePlaneTexture",1,["crabgrab::feature::metal::MetalVideoFramePlaneTexture"]],["impl Sync for MacosVideoFrameError",1,["crabgrab::feature::metal::MacosVideoFrameError"]],["impl !Sync for IoSurface",1,["crabgrab::feature::iosurface::IoSurface"]],["impl Sync for GetIoSurfaceError",1,["crabgrab::feature::iosurface::GetIoSurfaceError"]],["impl Sync for FrameBitmapBgraUnorm8x4",1,["crabgrab::feature::bitmap::FrameBitmapBgraUnorm8x4"]],["impl Sync for FrameBitmapRgbaUnormPacked1010102",1,["crabgrab::feature::bitmap::FrameBitmapRgbaUnormPacked1010102"]],["impl Sync for FrameBitmapRgbaF16x4",1,["crabgrab::feature::bitmap::FrameBitmapRgbaF16x4"]],["impl Sync for VideoRange",1,["crabgrab::feature::bitmap::VideoRange"]],["impl Sync for FrameBitmapYCbCr",1,["crabgrab::feature::bitmap::FrameBitmapYCbCr"]],["impl Sync for FrameBitmap",1,["crabgrab::feature::bitmap::FrameBitmap"]],["impl Sync for VideoFrameBitmapError",1,["crabgrab::feature::bitmap::VideoFrameBitmapError"]],["impl Sync for Size",1,["crabgrab::util::Size"]],["impl Sync for Point",1,["crabgrab::util::Point"]],["impl Sync for Rect",1,["crabgrab::util::Rect"]],["impl Sync for AudioSampleRate",1,["crabgrab::frame::AudioSampleRate"]],["impl Sync for AudioChannelCount",1,["crabgrab::frame::AudioChannelCount"]],["impl<'data> !Sync for AudioChannelData<'data>",1,["crabgrab::frame::AudioChannelData"]],["impl<'data, T> !Sync for AudioChannelDataSamples<'data, T>",1,["crabgrab::frame::AudioChannelDataSamples"]],["impl Sync for AudioBufferError",1,["crabgrab::frame::AudioBufferError"]],["impl !Sync for AudioFrame",1,["crabgrab::frame::AudioFrame"]],["impl !Sync for StreamEvent",1,["crabgrab::capture_stream::StreamEvent"]],["impl Sync for StreamError",1,["crabgrab::capture_stream::StreamError"]],["impl Sync for AudioCaptureConfig",1,["crabgrab::capture_stream::AudioCaptureConfig"]],["impl Sync for CapturePixelFormat",1,["crabgrab::capture_stream::CapturePixelFormat"]],["impl !Sync for CaptureConfig",1,["crabgrab::capture_stream::CaptureConfig"]],["impl Sync for CaptureConfigError",1,["crabgrab::capture_stream::CaptureConfigError"]],["impl !Sync for CaptureStream",1,["crabgrab::capture_stream::CaptureStream"]],["impl Sync for CapturableContentError",1,["crabgrab::capturable_content::CapturableContentError"]],["impl Sync for CapturableWindowFilter",1,["crabgrab::capturable_content::CapturableWindowFilter"]],["impl Sync for CapturableContentFilter",1,["crabgrab::capturable_content::CapturableContentFilter"]],["impl !Sync for CapturableContent",1,["crabgrab::capturable_content::CapturableContent"]],["impl<'content> !Sync for CapturableWindowIterator<'content>",1,["crabgrab::capturable_content::CapturableWindowIterator"]],["impl<'content> !Sync for CapturableDisplayIterator<'content>",1,["crabgrab::capturable_content::CapturableDisplayIterator"]],["impl !Sync for CapturableWindow",1,["crabgrab::capturable_content::CapturableWindow"]],["impl !Sync for CapturableDisplay",1,["crabgrab::capturable_content::CapturableDisplay"]],["impl !Sync for CapturableApplication",1,["crabgrab::capturable_content::CapturableApplication"]],["impl Sync for StreamCreateError"],["impl Sync for StreamStopError"],["impl Sync for VideoFrame"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/core/marker/trait.Unpin.js b/docs/macos_docs/trait.impl/core/marker/trait.Unpin.js new file mode 100644 index 00000000..6927470a --- /dev/null +++ b/docs/macos_docs/trait.impl/core/marker/trait.Unpin.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Unpin for MetalVideoFramePlaneTexture",1,["crabgrab::feature::metal::MetalVideoFramePlaneTexture"]],["impl Unpin for MacosVideoFrameError",1,["crabgrab::feature::metal::MacosVideoFrameError"]],["impl Unpin for IoSurface",1,["crabgrab::feature::iosurface::IoSurface"]],["impl Unpin for GetIoSurfaceError",1,["crabgrab::feature::iosurface::GetIoSurfaceError"]],["impl Unpin for FrameBitmapBgraUnorm8x4",1,["crabgrab::feature::bitmap::FrameBitmapBgraUnorm8x4"]],["impl Unpin for FrameBitmapRgbaUnormPacked1010102",1,["crabgrab::feature::bitmap::FrameBitmapRgbaUnormPacked1010102"]],["impl Unpin for FrameBitmapRgbaF16x4",1,["crabgrab::feature::bitmap::FrameBitmapRgbaF16x4"]],["impl Unpin for VideoRange",1,["crabgrab::feature::bitmap::VideoRange"]],["impl Unpin for FrameBitmapYCbCr",1,["crabgrab::feature::bitmap::FrameBitmapYCbCr"]],["impl Unpin for FrameBitmap",1,["crabgrab::feature::bitmap::FrameBitmap"]],["impl Unpin for VideoFrameBitmapError",1,["crabgrab::feature::bitmap::VideoFrameBitmapError"]],["impl Unpin for Size",1,["crabgrab::util::Size"]],["impl Unpin for Point",1,["crabgrab::util::Point"]],["impl Unpin for Rect",1,["crabgrab::util::Rect"]],["impl Unpin for AudioSampleRate",1,["crabgrab::frame::AudioSampleRate"]],["impl Unpin for AudioChannelCount",1,["crabgrab::frame::AudioChannelCount"]],["impl<'data> Unpin for AudioChannelData<'data>",1,["crabgrab::frame::AudioChannelData"]],["impl<'data, T> Unpin for AudioChannelDataSamples<'data, T>",1,["crabgrab::frame::AudioChannelDataSamples"]],["impl Unpin for AudioBufferError",1,["crabgrab::frame::AudioBufferError"]],["impl Unpin for AudioFrame",1,["crabgrab::frame::AudioFrame"]],["impl Unpin for VideoFrame",1,["crabgrab::frame::VideoFrame"]],["impl Unpin for StreamEvent",1,["crabgrab::capture_stream::StreamEvent"]],["impl Unpin for StreamError",1,["crabgrab::capture_stream::StreamError"]],["impl Unpin for StreamCreateError",1,["crabgrab::capture_stream::StreamCreateError"]],["impl Unpin for StreamStopError",1,["crabgrab::capture_stream::StreamStopError"]],["impl Unpin for AudioCaptureConfig",1,["crabgrab::capture_stream::AudioCaptureConfig"]],["impl Unpin for CapturePixelFormat",1,["crabgrab::capture_stream::CapturePixelFormat"]],["impl Unpin for CaptureConfig",1,["crabgrab::capture_stream::CaptureConfig"]],["impl Unpin for CaptureConfigError",1,["crabgrab::capture_stream::CaptureConfigError"]],["impl Unpin for CaptureStream",1,["crabgrab::capture_stream::CaptureStream"]],["impl Unpin for CapturableContentError",1,["crabgrab::capturable_content::CapturableContentError"]],["impl Unpin for CapturableWindowFilter",1,["crabgrab::capturable_content::CapturableWindowFilter"]],["impl Unpin for CapturableContentFilter",1,["crabgrab::capturable_content::CapturableContentFilter"]],["impl Unpin for CapturableContent",1,["crabgrab::capturable_content::CapturableContent"]],["impl<'content> Unpin for CapturableWindowIterator<'content>",1,["crabgrab::capturable_content::CapturableWindowIterator"]],["impl<'content> Unpin for CapturableDisplayIterator<'content>",1,["crabgrab::capturable_content::CapturableDisplayIterator"]],["impl Unpin for CapturableWindow",1,["crabgrab::capturable_content::CapturableWindow"]],["impl Unpin for CapturableDisplay",1,["crabgrab::capturable_content::CapturableDisplay"]],["impl Unpin for CapturableApplication",1,["crabgrab::capturable_content::CapturableApplication"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/core/ops/drop/trait.Drop.js b/docs/macos_docs/trait.impl/core/ops/drop/trait.Drop.js new file mode 100644 index 00000000..d118b604 --- /dev/null +++ b/docs/macos_docs/trait.impl/core/ops/drop/trait.Drop.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Drop for IoSurface"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js b/docs/macos_docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js new file mode 100644 index 00000000..51a61114 --- /dev/null +++ b/docs/macos_docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl RefUnwindSafe for MetalVideoFramePlaneTexture",1,["crabgrab::feature::metal::MetalVideoFramePlaneTexture"]],["impl RefUnwindSafe for MacosVideoFrameError",1,["crabgrab::feature::metal::MacosVideoFrameError"]],["impl RefUnwindSafe for IoSurface",1,["crabgrab::feature::iosurface::IoSurface"]],["impl RefUnwindSafe for GetIoSurfaceError",1,["crabgrab::feature::iosurface::GetIoSurfaceError"]],["impl RefUnwindSafe for FrameBitmapBgraUnorm8x4",1,["crabgrab::feature::bitmap::FrameBitmapBgraUnorm8x4"]],["impl RefUnwindSafe for FrameBitmapRgbaUnormPacked1010102",1,["crabgrab::feature::bitmap::FrameBitmapRgbaUnormPacked1010102"]],["impl RefUnwindSafe for FrameBitmapRgbaF16x4",1,["crabgrab::feature::bitmap::FrameBitmapRgbaF16x4"]],["impl RefUnwindSafe for VideoRange",1,["crabgrab::feature::bitmap::VideoRange"]],["impl RefUnwindSafe for FrameBitmapYCbCr",1,["crabgrab::feature::bitmap::FrameBitmapYCbCr"]],["impl RefUnwindSafe for FrameBitmap",1,["crabgrab::feature::bitmap::FrameBitmap"]],["impl RefUnwindSafe for VideoFrameBitmapError",1,["crabgrab::feature::bitmap::VideoFrameBitmapError"]],["impl RefUnwindSafe for Size",1,["crabgrab::util::Size"]],["impl RefUnwindSafe for Point",1,["crabgrab::util::Point"]],["impl RefUnwindSafe for Rect",1,["crabgrab::util::Rect"]],["impl RefUnwindSafe for AudioSampleRate",1,["crabgrab::frame::AudioSampleRate"]],["impl RefUnwindSafe for AudioChannelCount",1,["crabgrab::frame::AudioChannelCount"]],["impl<'data> RefUnwindSafe for AudioChannelData<'data>",1,["crabgrab::frame::AudioChannelData"]],["impl<'data, T> RefUnwindSafe for AudioChannelDataSamples<'data, T>where\n T: RefUnwindSafe,",1,["crabgrab::frame::AudioChannelDataSamples"]],["impl RefUnwindSafe for AudioBufferError",1,["crabgrab::frame::AudioBufferError"]],["impl RefUnwindSafe for AudioFrame",1,["crabgrab::frame::AudioFrame"]],["impl !RefUnwindSafe for VideoFrame",1,["crabgrab::frame::VideoFrame"]],["impl !RefUnwindSafe for StreamEvent",1,["crabgrab::capture_stream::StreamEvent"]],["impl RefUnwindSafe for StreamError",1,["crabgrab::capture_stream::StreamError"]],["impl RefUnwindSafe for StreamCreateError",1,["crabgrab::capture_stream::StreamCreateError"]],["impl RefUnwindSafe for StreamStopError",1,["crabgrab::capture_stream::StreamStopError"]],["impl RefUnwindSafe for AudioCaptureConfig",1,["crabgrab::capture_stream::AudioCaptureConfig"]],["impl RefUnwindSafe for CapturePixelFormat",1,["crabgrab::capture_stream::CapturePixelFormat"]],["impl RefUnwindSafe for CaptureConfig",1,["crabgrab::capture_stream::CaptureConfig"]],["impl RefUnwindSafe for CaptureConfigError",1,["crabgrab::capture_stream::CaptureConfigError"]],["impl !RefUnwindSafe for CaptureStream",1,["crabgrab::capture_stream::CaptureStream"]],["impl RefUnwindSafe for CapturableContentError",1,["crabgrab::capturable_content::CapturableContentError"]],["impl RefUnwindSafe for CapturableWindowFilter",1,["crabgrab::capturable_content::CapturableWindowFilter"]],["impl RefUnwindSafe for CapturableContentFilter",1,["crabgrab::capturable_content::CapturableContentFilter"]],["impl RefUnwindSafe for CapturableContent",1,["crabgrab::capturable_content::CapturableContent"]],["impl<'content> RefUnwindSafe for CapturableWindowIterator<'content>",1,["crabgrab::capturable_content::CapturableWindowIterator"]],["impl<'content> RefUnwindSafe for CapturableDisplayIterator<'content>",1,["crabgrab::capturable_content::CapturableDisplayIterator"]],["impl RefUnwindSafe for CapturableWindow",1,["crabgrab::capturable_content::CapturableWindow"]],["impl RefUnwindSafe for CapturableDisplay",1,["crabgrab::capturable_content::CapturableDisplay"]],["impl RefUnwindSafe for CapturableApplication",1,["crabgrab::capturable_content::CapturableApplication"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js b/docs/macos_docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js new file mode 100644 index 00000000..3f2fbb8f --- /dev/null +++ b/docs/macos_docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl UnwindSafe for MetalVideoFramePlaneTexture",1,["crabgrab::feature::metal::MetalVideoFramePlaneTexture"]],["impl UnwindSafe for MacosVideoFrameError",1,["crabgrab::feature::metal::MacosVideoFrameError"]],["impl UnwindSafe for IoSurface",1,["crabgrab::feature::iosurface::IoSurface"]],["impl UnwindSafe for GetIoSurfaceError",1,["crabgrab::feature::iosurface::GetIoSurfaceError"]],["impl UnwindSafe for FrameBitmapBgraUnorm8x4",1,["crabgrab::feature::bitmap::FrameBitmapBgraUnorm8x4"]],["impl UnwindSafe for FrameBitmapRgbaUnormPacked1010102",1,["crabgrab::feature::bitmap::FrameBitmapRgbaUnormPacked1010102"]],["impl UnwindSafe for FrameBitmapRgbaF16x4",1,["crabgrab::feature::bitmap::FrameBitmapRgbaF16x4"]],["impl UnwindSafe for VideoRange",1,["crabgrab::feature::bitmap::VideoRange"]],["impl UnwindSafe for FrameBitmapYCbCr",1,["crabgrab::feature::bitmap::FrameBitmapYCbCr"]],["impl UnwindSafe for FrameBitmap",1,["crabgrab::feature::bitmap::FrameBitmap"]],["impl UnwindSafe for VideoFrameBitmapError",1,["crabgrab::feature::bitmap::VideoFrameBitmapError"]],["impl UnwindSafe for Size",1,["crabgrab::util::Size"]],["impl UnwindSafe for Point",1,["crabgrab::util::Point"]],["impl UnwindSafe for Rect",1,["crabgrab::util::Rect"]],["impl UnwindSafe for AudioSampleRate",1,["crabgrab::frame::AudioSampleRate"]],["impl UnwindSafe for AudioChannelCount",1,["crabgrab::frame::AudioChannelCount"]],["impl<'data> UnwindSafe for AudioChannelData<'data>",1,["crabgrab::frame::AudioChannelData"]],["impl<'data, T> UnwindSafe for AudioChannelDataSamples<'data, T>where\n T: RefUnwindSafe,",1,["crabgrab::frame::AudioChannelDataSamples"]],["impl UnwindSafe for AudioBufferError",1,["crabgrab::frame::AudioBufferError"]],["impl UnwindSafe for AudioFrame",1,["crabgrab::frame::AudioFrame"]],["impl UnwindSafe for VideoFrame",1,["crabgrab::frame::VideoFrame"]],["impl UnwindSafe for StreamEvent",1,["crabgrab::capture_stream::StreamEvent"]],["impl UnwindSafe for StreamError",1,["crabgrab::capture_stream::StreamError"]],["impl UnwindSafe for StreamCreateError",1,["crabgrab::capture_stream::StreamCreateError"]],["impl UnwindSafe for StreamStopError",1,["crabgrab::capture_stream::StreamStopError"]],["impl UnwindSafe for AudioCaptureConfig",1,["crabgrab::capture_stream::AudioCaptureConfig"]],["impl UnwindSafe for CapturePixelFormat",1,["crabgrab::capture_stream::CapturePixelFormat"]],["impl UnwindSafe for CaptureConfig",1,["crabgrab::capture_stream::CaptureConfig"]],["impl UnwindSafe for CaptureConfigError",1,["crabgrab::capture_stream::CaptureConfigError"]],["impl !UnwindSafe for CaptureStream",1,["crabgrab::capture_stream::CaptureStream"]],["impl UnwindSafe for CapturableContentError",1,["crabgrab::capturable_content::CapturableContentError"]],["impl UnwindSafe for CapturableWindowFilter",1,["crabgrab::capturable_content::CapturableWindowFilter"]],["impl UnwindSafe for CapturableContentFilter",1,["crabgrab::capturable_content::CapturableContentFilter"]],["impl UnwindSafe for CapturableContent",1,["crabgrab::capturable_content::CapturableContent"]],["impl<'content> UnwindSafe for CapturableWindowIterator<'content>",1,["crabgrab::capturable_content::CapturableWindowIterator"]],["impl<'content> UnwindSafe for CapturableDisplayIterator<'content>",1,["crabgrab::capturable_content::CapturableDisplayIterator"]],["impl UnwindSafe for CapturableWindow",1,["crabgrab::capturable_content::CapturableWindow"]],["impl UnwindSafe for CapturableDisplay",1,["crabgrab::capturable_content::CapturableDisplay"]],["impl UnwindSafe for CapturableApplication",1,["crabgrab::capturable_content::CapturableApplication"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/crabgrab/feature/bitmap/trait.VideoFrameBitmap.js b/docs/macos_docs/trait.impl/crabgrab/feature/bitmap/trait.VideoFrameBitmap.js new file mode 100644 index 00000000..d7ba5ecd --- /dev/null +++ b/docs/macos_docs/trait.impl/crabgrab/feature/bitmap/trait.VideoFrameBitmap.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/crabgrab/feature/iosurface/trait.MacosIoSurfaceVideoFrame.js b/docs/macos_docs/trait.impl/crabgrab/feature/iosurface/trait.MacosIoSurfaceVideoFrame.js new file mode 100644 index 00000000..d7ba5ecd --- /dev/null +++ b/docs/macos_docs/trait.impl/crabgrab/feature/iosurface/trait.MacosIoSurfaceVideoFrame.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/crabgrab/feature/metal/trait.MetalCaptureStream.js b/docs/macos_docs/trait.impl/crabgrab/feature/metal/trait.MetalCaptureStream.js new file mode 100644 index 00000000..d7ba5ecd --- /dev/null +++ b/docs/macos_docs/trait.impl/crabgrab/feature/metal/trait.MetalCaptureStream.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/crabgrab/feature/metal/trait.MetalVideoFrame.js b/docs/macos_docs/trait.impl/crabgrab/feature/metal/trait.MetalVideoFrame.js new file mode 100644 index 00000000..d7ba5ecd --- /dev/null +++ b/docs/macos_docs/trait.impl/crabgrab/feature/metal/trait.MetalVideoFrame.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/crabgrab/platform/macos/capture_stream/trait.MacosAudioCaptureConfigExt.js b/docs/macos_docs/trait.impl/crabgrab/platform/macos/capture_stream/trait.MacosAudioCaptureConfigExt.js new file mode 100644 index 00000000..d7ba5ecd --- /dev/null +++ b/docs/macos_docs/trait.impl/crabgrab/platform/macos/capture_stream/trait.MacosAudioCaptureConfigExt.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/macos_docs/trait.impl/crabgrab/platform/macos/capture_stream/trait.MacosCaptureConfigExt.js b/docs/macos_docs/trait.impl/crabgrab/platform/macos/capture_stream/trait.MacosCaptureConfigExt.js new file mode 100644 index 00000000..d7ba5ecd --- /dev/null +++ b/docs/macos_docs/trait.impl/crabgrab/platform/macos/capture_stream/trait.MacosCaptureConfigExt.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/.lock b/docs/windows_docs/.lock new file mode 100644 index 00000000..e69de29b diff --git a/docs/windows_docs/crabgrab/all.html b/docs/windows_docs/crabgrab/all.html new file mode 100644 index 00000000..12b3dac2 --- /dev/null +++ b/docs/windows_docs/crabgrab/all.html @@ -0,0 +1,2 @@ +List of all items in this crate +
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capturable_content/enum.CapturableContentError.html b/docs/windows_docs/crabgrab/capturable_content/enum.CapturableContentError.html new file mode 100644 index 00000000..9d456507 --- /dev/null +++ b/docs/windows_docs/crabgrab/capturable_content/enum.CapturableContentError.html @@ -0,0 +1,17 @@ +CapturableContentError in crabgrab::capturable_content - Rust +
pub enum CapturableContentError {
+    Other(String),
+}
Expand description

Represents an error that occured when enumerating capturable content

+

Variants§

§

Other(String)

Trait Implementations§

source§

impl Clone for CapturableContentError

source§

fn clone(&self) -> CapturableContentError

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for CapturableContentError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for CapturableContentError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for CapturableContentError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capturable_content/index.html b/docs/windows_docs/crabgrab/capturable_content/index.html new file mode 100644 index 00000000..8e6f6c14 --- /dev/null +++ b/docs/windows_docs/crabgrab/capturable_content/index.html @@ -0,0 +1,2 @@ +crabgrab::capturable_content - Rust +

Structs§

Enums§

\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capturable_content/sidebar-items.js b/docs/windows_docs/crabgrab/capturable_content/sidebar-items.js new file mode 100644 index 00000000..8e201021 --- /dev/null +++ b/docs/windows_docs/crabgrab/capturable_content/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["CapturableContentError"],"struct":["CapturableApplication","CapturableContent","CapturableContentFilter","CapturableDisplay","CapturableDisplayIterator","CapturableWindow","CapturableWindowFilter","CapturableWindowIterator"]}; \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capturable_content/struct.CapturableApplication.html b/docs/windows_docs/crabgrab/capturable_content/struct.CapturableApplication.html new file mode 100644 index 00000000..22afc5f4 --- /dev/null +++ b/docs/windows_docs/crabgrab/capturable_content/struct.CapturableApplication.html @@ -0,0 +1,14 @@ +CapturableApplication in crabgrab::capturable_content - Rust +
pub struct CapturableApplication { /* private fields */ }

Implementations§

source§

impl CapturableApplication

source

pub fn identifier(&self) -> String

Gets the “identifier” of the application

+

On Macos, this is the application bundle, and on windows, this is the application file name

+

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capturable_content/struct.CapturableContent.html b/docs/windows_docs/crabgrab/capturable_content/struct.CapturableContent.html new file mode 100644 index 00000000..966ef15f --- /dev/null +++ b/docs/windows_docs/crabgrab/capturable_content/struct.CapturableContent.html @@ -0,0 +1,19 @@ +CapturableContent in crabgrab::capturable_content - Rust +
pub struct CapturableContent { /* private fields */ }

Implementations§

source§

impl CapturableContent

source

pub async fn new( + filter: CapturableContentFilter +) -> Result<Self, CapturableContentError>

Requests capturable content from the OS

+

Note that the returned capturable content may be stale - for example, a window enumerated in this capturable content +may have been closed before it is used to open a stream, and creating a stream for that window will result in an error.

+
source

pub fn windows<'a>(&'a self) -> CapturableWindowIterator<'a>

Get an iterator over the capturable windows

+
source

pub fn displays<'a>(&'a self) -> CapturableDisplayIterator<'a>

Get an iterator over the capturable displays

+

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capturable_content/struct.CapturableContentFilter.html b/docs/windows_docs/crabgrab/capturable_content/struct.CapturableContentFilter.html new file mode 100644 index 00000000..e596b7e3 --- /dev/null +++ b/docs/windows_docs/crabgrab/capturable_content/struct.CapturableContentFilter.html @@ -0,0 +1,19 @@ +CapturableContentFilter in crabgrab::capturable_content - Rust +
pub struct CapturableContentFilter {
+    pub windows: Option<CapturableWindowFilter>,
+    pub displays: bool,
+}
Expand description

Selects the kind of capturable content to enumerate

+

Fields§

§windows: Option<CapturableWindowFilter>

What kind of capturable windows, if Some, to enumerate

+
§displays: bool

Whether to enumerate capturable displays

+

Implementations§

source§

impl CapturableContentFilter

source

pub fn is_empty(&self) -> bool

Whether this filter allows any capturable content

+

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capturable_content/struct.CapturableDisplay.html b/docs/windows_docs/crabgrab/capturable_content/struct.CapturableDisplay.html new file mode 100644 index 00000000..8cf42af6 --- /dev/null +++ b/docs/windows_docs/crabgrab/capturable_content/struct.CapturableDisplay.html @@ -0,0 +1,16 @@ +CapturableDisplay in crabgrab::capturable_content - Rust +
pub struct CapturableDisplay { /* private fields */ }
Expand description

Represents a capturable display

+

Implementations§

source§

impl CapturableDisplay

source

pub fn rect(&self) -> Rect

Gets the virtual screen rectangle of this display

+

Note: Currently on windows, this is only evaluated at the time of display enumeration

+

Trait Implementations§

source§

impl Clone for CapturableDisplay

source§

fn clone(&self) -> CapturableDisplay

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for CapturableDisplay

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capturable_content/struct.CapturableDisplayIterator.html b/docs/windows_docs/crabgrab/capturable_content/struct.CapturableDisplayIterator.html new file mode 100644 index 00000000..57d44dbd --- /dev/null +++ b/docs/windows_docs/crabgrab/capturable_content/struct.CapturableDisplayIterator.html @@ -0,0 +1,191 @@ +CapturableDisplayIterator in crabgrab::capturable_content - Rust +
pub struct CapturableDisplayIterator<'content> { /* private fields */ }
Expand description

An iterator over capturable displays

+

Trait Implementations§

source§

impl ExactSizeIterator for CapturableDisplayIterator<'_>

source§

fn len(&self) -> usize

Returns the exact remaining length of the iterator. Read more
source§

fn is_empty(&self) -> bool

🔬This is a nightly-only experimental API. (exact_size_is_empty)
Returns true if the iterator is empty. Read more
source§

impl Iterator for CapturableDisplayIterator<'_>

§

type Item = CapturableDisplay

The type of the elements being iterated over.
source§

fn next(&mut self) -> Option<Self::Item>

Advances the iterator and returns the next value. Read more
source§

fn size_hint(&self) -> (usize, Option<usize>)

Returns the bounds on the remaining length of the iterator. Read more
source§

fn next_chunk<const N: usize>( + &mut self +) -> Result<[Self::Item; N], IntoIter<Self::Item, N>>
where + Self: Sized,

🔬This is a nightly-only experimental API. (iter_next_chunk)
Advances the iterator and returns an array containing the next N values. Read more
1.0.0 · source§

fn count(self) -> usize
where + Self: Sized,

Consumes the iterator, counting the number of iterations and returning it. Read more
1.0.0 · source§

fn last(self) -> Option<Self::Item>
where + Self: Sized,

Consumes the iterator, returning the last element. Read more
source§

fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>>

🔬This is a nightly-only experimental API. (iter_advance_by)
Advances the iterator by n elements. Read more
1.0.0 · source§

fn nth(&mut self, n: usize) -> Option<Self::Item>

Returns the nth element of the iterator. Read more
1.28.0 · source§

fn step_by(self, step: usize) -> StepBy<Self>
where + Self: Sized,

Creates an iterator starting at the same point, but stepping by +the given amount at each iteration. Read more
1.0.0 · source§

fn chain<U>(self, other: U) -> Chain<Self, <U as IntoIterator>::IntoIter>
where + Self: Sized, + U: IntoIterator<Item = Self::Item>,

Takes two iterators and creates a new iterator over both in sequence. Read more
1.0.0 · source§

fn zip<U>(self, other: U) -> Zip<Self, <U as IntoIterator>::IntoIter>
where + Self: Sized, + U: IntoIterator,

‘Zips up’ two iterators into a single iterator of pairs. Read more
source§

fn intersperse_with<G>(self, separator: G) -> IntersperseWith<Self, G>
where + Self: Sized, + G: FnMut() -> Self::Item,

🔬This is a nightly-only experimental API. (iter_intersperse)
Creates a new iterator which places an item generated by separator +between adjacent items of the original iterator. Read more
1.0.0 · source§

fn map<B, F>(self, f: F) -> Map<Self, F>
where + Self: Sized, + F: FnMut(Self::Item) -> B,

Takes a closure and creates an iterator which calls that closure on each +element. Read more
1.21.0 · source§

fn for_each<F>(self, f: F)
where + Self: Sized, + F: FnMut(Self::Item),

Calls a closure on each element of an iterator. Read more
1.0.0 · source§

fn filter<P>(self, predicate: P) -> Filter<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Creates an iterator which uses a closure to determine if an element +should be yielded. Read more
1.0.0 · source§

fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F>
where + Self: Sized, + F: FnMut(Self::Item) -> Option<B>,

Creates an iterator that both filters and maps. Read more
1.0.0 · source§

fn enumerate(self) -> Enumerate<Self>
where + Self: Sized,

Creates an iterator which gives the current iteration count as well as +the next value. Read more
1.0.0 · source§

fn peekable(self) -> Peekable<Self>
where + Self: Sized,

Creates an iterator which can use the peek and peek_mut methods +to look at the next element of the iterator without consuming it. See +their documentation for more information. Read more
1.0.0 · source§

fn skip_while<P>(self, predicate: P) -> SkipWhile<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Creates an iterator that skips elements based on a predicate. Read more
1.0.0 · source§

fn take_while<P>(self, predicate: P) -> TakeWhile<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Creates an iterator that yields elements based on a predicate. Read more
1.57.0 · source§

fn map_while<B, P>(self, predicate: P) -> MapWhile<Self, P>
where + Self: Sized, + P: FnMut(Self::Item) -> Option<B>,

Creates an iterator that both yields elements based on a predicate and maps. Read more
1.0.0 · source§

fn skip(self, n: usize) -> Skip<Self>
where + Self: Sized,

Creates an iterator that skips the first n elements. Read more
1.0.0 · source§

fn take(self, n: usize) -> Take<Self>
where + Self: Sized,

Creates an iterator that yields the first n elements, or fewer +if the underlying iterator ends sooner. Read more
1.0.0 · source§

fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F>
where + Self: Sized, + F: FnMut(&mut St, Self::Item) -> Option<B>,

An iterator adapter which, like fold, holds internal state, but +unlike fold, produces a new iterator. Read more
1.0.0 · source§

fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F>
where + Self: Sized, + U: IntoIterator, + F: FnMut(Self::Item) -> U,

Creates an iterator that works like map, but flattens nested structure. Read more
source§

fn map_windows<F, R, const N: usize>(self, f: F) -> MapWindows<Self, F, N>
where + Self: Sized, + F: FnMut(&[Self::Item; N]) -> R,

🔬This is a nightly-only experimental API. (iter_map_windows)
Calls the given function f for each contiguous window of size N over +self and returns an iterator over the outputs of f. Like slice::windows(), +the windows during mapping overlap as well. Read more
1.0.0 · source§

fn fuse(self) -> Fuse<Self>
where + Self: Sized,

Creates an iterator which ends after the first None. Read more
1.0.0 · source§

fn inspect<F>(self, f: F) -> Inspect<Self, F>
where + Self: Sized, + F: FnMut(&Self::Item),

Does something with each element of an iterator, passing the value on. Read more
1.0.0 · source§

fn by_ref(&mut self) -> &mut Self
where + Self: Sized,

Borrows an iterator, rather than consuming it. Read more
1.0.0 · source§

fn collect<B>(self) -> B
where + B: FromIterator<Self::Item>, + Self: Sized,

Transforms an iterator into a collection. Read more
source§

fn collect_into<E>(self, collection: &mut E) -> &mut E
where + E: Extend<Self::Item>, + Self: Sized,

🔬This is a nightly-only experimental API. (iter_collect_into)
Collects all the items from an iterator into a collection. Read more
1.0.0 · source§

fn partition<B, F>(self, f: F) -> (B, B)
where + Self: Sized, + B: Default + Extend<Self::Item>, + F: FnMut(&Self::Item) -> bool,

Consumes an iterator, creating two collections from it. Read more
source§

fn is_partitioned<P>(self, predicate: P) -> bool
where + Self: Sized, + P: FnMut(Self::Item) -> bool,

🔬This is a nightly-only experimental API. (iter_is_partitioned)
Checks if the elements of this iterator are partitioned according to the given predicate, +such that all those that return true precede all those that return false. Read more
1.27.0 · source§

fn try_fold<B, F, R>(&mut self, init: B, f: F) -> R
where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try<Output = B>,

An iterator method that applies a function as long as it returns +successfully, producing a single, final value. Read more
1.27.0 · source§

fn try_for_each<F, R>(&mut self, f: F) -> R
where + Self: Sized, + F: FnMut(Self::Item) -> R, + R: Try<Output = ()>,

An iterator method that applies a fallible function to each item in the +iterator, stopping at the first error and returning that error. Read more
1.0.0 · source§

fn fold<B, F>(self, init: B, f: F) -> B
where + Self: Sized, + F: FnMut(B, Self::Item) -> B,

Folds every element into an accumulator by applying an operation, +returning the final result. Read more
1.51.0 · source§

fn reduce<F>(self, f: F) -> Option<Self::Item>
where + Self: Sized, + F: FnMut(Self::Item, Self::Item) -> Self::Item,

Reduces the elements to a single one, by repeatedly applying a reducing +operation. Read more
source§

fn try_reduce<F, R>( + &mut self, + f: F +) -> <<R as Try>::Residual as Residual<Option<<R as Try>::Output>>>::TryType
where + Self: Sized, + F: FnMut(Self::Item, Self::Item) -> R, + R: Try<Output = Self::Item>, + <R as Try>::Residual: Residual<Option<Self::Item>>,

🔬This is a nightly-only experimental API. (iterator_try_reduce)
Reduces the elements to a single one by repeatedly applying a reducing operation. If the +closure returns a failure, the failure is propagated back to the caller immediately. Read more
1.0.0 · source§

fn all<F>(&mut self, f: F) -> bool
where + Self: Sized, + F: FnMut(Self::Item) -> bool,

Tests if every element of the iterator matches a predicate. Read more
1.0.0 · source§

fn any<F>(&mut self, f: F) -> bool
where + Self: Sized, + F: FnMut(Self::Item) -> bool,

Tests if any element of the iterator matches a predicate. Read more
1.0.0 · source§

fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Searches for an element of an iterator that satisfies a predicate. Read more
1.30.0 · source§

fn find_map<B, F>(&mut self, f: F) -> Option<B>
where + Self: Sized, + F: FnMut(Self::Item) -> Option<B>,

Applies function to the elements of iterator and returns +the first non-none result. Read more
source§

fn try_find<F, R>( + &mut self, + f: F +) -> <<R as Try>::Residual as Residual<Option<Self::Item>>>::TryType
where + Self: Sized, + F: FnMut(&Self::Item) -> R, + R: Try<Output = bool>, + <R as Try>::Residual: Residual<Option<Self::Item>>,

🔬This is a nightly-only experimental API. (try_find)
Applies function to the elements of iterator and returns +the first true result or the first error. Read more
1.0.0 · source§

fn position<P>(&mut self, predicate: P) -> Option<usize>
where + Self: Sized, + P: FnMut(Self::Item) -> bool,

Searches for an element in an iterator, returning its index. Read more
1.6.0 · source§

fn max_by_key<B, F>(self, f: F) -> Option<Self::Item>
where + B: Ord, + Self: Sized, + F: FnMut(&Self::Item) -> B,

Returns the element that gives the maximum value from the +specified function. Read more
1.15.0 · source§

fn max_by<F>(self, compare: F) -> Option<Self::Item>
where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering,

Returns the element that gives the maximum value with respect to the +specified comparison function. Read more
1.6.0 · source§

fn min_by_key<B, F>(self, f: F) -> Option<Self::Item>
where + B: Ord, + Self: Sized, + F: FnMut(&Self::Item) -> B,

Returns the element that gives the minimum value from the +specified function. Read more
1.15.0 · source§

fn min_by<F>(self, compare: F) -> Option<Self::Item>
where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering,

Returns the element that gives the minimum value with respect to the +specified comparison function. Read more
1.0.0 · source§

fn unzip<A, B, FromA, FromB>(self) -> (FromA, FromB)
where + FromA: Default + Extend<A>, + FromB: Default + Extend<B>, + Self: Sized + Iterator<Item = (A, B)>,

Converts an iterator of pairs into a pair of containers. Read more
1.36.0 · source§

fn copied<'a, T>(self) -> Copied<Self>
where + T: 'a + Copy, + Self: Sized + Iterator<Item = &'a T>,

Creates an iterator which copies all of its elements. Read more
1.0.0 · source§

fn cloned<'a, T>(self) -> Cloned<Self>
where + T: 'a + Clone, + Self: Sized + Iterator<Item = &'a T>,

Creates an iterator which clones all of its elements. Read more
source§

fn array_chunks<const N: usize>(self) -> ArrayChunks<Self, N>
where + Self: Sized,

🔬This is a nightly-only experimental API. (iter_array_chunks)
Returns an iterator over N elements of the iterator at a time. Read more
1.11.0 · source§

fn sum<S>(self) -> S
where + Self: Sized, + S: Sum<Self::Item>,

Sums the elements of an iterator. Read more
1.11.0 · source§

fn product<P>(self) -> P
where + Self: Sized, + P: Product<Self::Item>,

Iterates over the entire iterator, multiplying all the elements Read more
source§

fn cmp_by<I, F>(self, other: I, cmp: F) -> Ordering
where + Self: Sized, + I: IntoIterator, + F: FnMut(Self::Item, <I as IntoIterator>::Item) -> Ordering,

🔬This is a nightly-only experimental API. (iter_order_by)
Lexicographically compares the elements of this Iterator with those +of another with respect to the specified comparison function. Read more
1.5.0 · source§

fn partial_cmp<I>(self, other: I) -> Option<Ordering>
where + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Lexicographically compares the PartialOrd elements of +this Iterator with those of another. The comparison works like short-circuit +evaluation, returning a result without comparing the remaining elements. +As soon as an order can be determined, the evaluation stops and a result is returned. Read more
source§

fn partial_cmp_by<I, F>(self, other: I, partial_cmp: F) -> Option<Ordering>
where + Self: Sized, + I: IntoIterator, + F: FnMut(Self::Item, <I as IntoIterator>::Item) -> Option<Ordering>,

🔬This is a nightly-only experimental API. (iter_order_by)
Lexicographically compares the elements of this Iterator with those +of another with respect to the specified comparison function. Read more
1.5.0 · source§

fn eq<I>(self, other: I) -> bool
where + I: IntoIterator, + Self::Item: PartialEq<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are equal to those of +another. Read more
source§

fn eq_by<I, F>(self, other: I, eq: F) -> bool
where + Self: Sized, + I: IntoIterator, + F: FnMut(Self::Item, <I as IntoIterator>::Item) -> bool,

🔬This is a nightly-only experimental API. (iter_order_by)
Determines if the elements of this Iterator are equal to those of +another with respect to the specified equality function. Read more
1.5.0 · source§

fn ne<I>(self, other: I) -> bool
where + I: IntoIterator, + Self::Item: PartialEq<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are not equal to those of +another. Read more
1.5.0 · source§

fn lt<I>(self, other: I) -> bool
where + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are lexicographically +less than those of another. Read more
1.5.0 · source§

fn le<I>(self, other: I) -> bool
where + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are lexicographically +less or equal to those of another. Read more
1.5.0 · source§

fn gt<I>(self, other: I) -> bool
where + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are lexicographically +greater than those of another. Read more
1.5.0 · source§

fn ge<I>(self, other: I) -> bool
where + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are lexicographically +greater than or equal to those of another. Read more
source§

fn is_sorted_by<F>(self, compare: F) -> bool
where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> bool,

🔬This is a nightly-only experimental API. (is_sorted)
Checks if the elements of this iterator are sorted using the given comparator function. Read more
source§

fn is_sorted_by_key<F, K>(self, f: F) -> bool
where + Self: Sized, + F: FnMut(Self::Item) -> K, + K: PartialOrd,

🔬This is a nightly-only experimental API. (is_sorted)
Checks if the elements of this iterator are sorted using the given key extraction +function. Read more

Auto Trait Implementations§

§

impl<'content> RefUnwindSafe for CapturableDisplayIterator<'content>

§

impl<'content> Send for CapturableDisplayIterator<'content>

§

impl<'content> Sync for CapturableDisplayIterator<'content>

§

impl<'content> Unpin for CapturableDisplayIterator<'content>

§

impl<'content> UnwindSafe for CapturableDisplayIterator<'content>

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<I> IntoIterator for I
where + I: Iterator,

§

type Item = <I as Iterator>::Item

The type of the elements being iterated over.
§

type IntoIter = I

Which kind of iterator are we turning this into?
const: unstable · source§

fn into_iter(self) -> I

Creates an iterator from a value. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capturable_content/struct.CapturableWindow.html b/docs/windows_docs/crabgrab/capturable_content/struct.CapturableWindow.html new file mode 100644 index 00000000..0de84f78 --- /dev/null +++ b/docs/windows_docs/crabgrab/capturable_content/struct.CapturableWindow.html @@ -0,0 +1,17 @@ +CapturableWindow in crabgrab::capturable_content - Rust +
pub struct CapturableWindow { /* private fields */ }
Expand description

Represents a capturable application window

+

Implementations§

source§

impl CapturableWindow

source

pub fn title(&self) -> String

Gets the title of the window

+
source

pub fn rect(&self) -> Rect

Gets the virtual screen rectangle of the window

+
source

pub fn application(&self) -> CapturableApplication

Gets the application that owns this window

+

Trait Implementations§

source§

impl Clone for CapturableWindow

source§

fn clone(&self) -> CapturableWindow

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for CapturableWindow

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capturable_content/struct.CapturableWindowFilter.html b/docs/windows_docs/crabgrab/capturable_content/struct.CapturableWindowFilter.html new file mode 100644 index 00000000..fa1346b5 --- /dev/null +++ b/docs/windows_docs/crabgrab/capturable_content/struct.CapturableWindowFilter.html @@ -0,0 +1,18 @@ +CapturableWindowFilter in crabgrab::capturable_content - Rust +
pub struct CapturableWindowFilter {
+    pub desktop_windows: bool,
+    pub onscreen_only: bool,
+}
Expand description

Selects the kind of windows to enumerate for capture

+

Fields§

§desktop_windows: bool

Desktop windows are elements of the desktop environment, E.G. the dock on macos or the start bar on windows.

+
§onscreen_only: bool

Whether to restrict to onscreen windows

+

Trait Implementations§

source§

impl Default for CapturableWindowFilter

source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capturable_content/struct.CapturableWindowIterator.html b/docs/windows_docs/crabgrab/capturable_content/struct.CapturableWindowIterator.html new file mode 100644 index 00000000..a759af8f --- /dev/null +++ b/docs/windows_docs/crabgrab/capturable_content/struct.CapturableWindowIterator.html @@ -0,0 +1,191 @@ +CapturableWindowIterator in crabgrab::capturable_content - Rust +
pub struct CapturableWindowIterator<'content> { /* private fields */ }
Expand description

An iterator over capturable windows

+

Trait Implementations§

source§

impl ExactSizeIterator for CapturableWindowIterator<'_>

1.0.0 · source§

fn len(&self) -> usize

Returns the exact remaining length of the iterator. Read more
source§

fn is_empty(&self) -> bool

🔬This is a nightly-only experimental API. (exact_size_is_empty)
Returns true if the iterator is empty. Read more
source§

impl Iterator for CapturableWindowIterator<'_>

§

type Item = CapturableWindow

The type of the elements being iterated over.
source§

fn next(&mut self) -> Option<Self::Item>

Advances the iterator and returns the next value. Read more
source§

fn size_hint(&self) -> (usize, Option<usize>)

Returns the bounds on the remaining length of the iterator. Read more
source§

fn next_chunk<const N: usize>( + &mut self +) -> Result<[Self::Item; N], IntoIter<Self::Item, N>>
where + Self: Sized,

🔬This is a nightly-only experimental API. (iter_next_chunk)
Advances the iterator and returns an array containing the next N values. Read more
1.0.0 · source§

fn count(self) -> usize
where + Self: Sized,

Consumes the iterator, counting the number of iterations and returning it. Read more
1.0.0 · source§

fn last(self) -> Option<Self::Item>
where + Self: Sized,

Consumes the iterator, returning the last element. Read more
source§

fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>>

🔬This is a nightly-only experimental API. (iter_advance_by)
Advances the iterator by n elements. Read more
1.0.0 · source§

fn nth(&mut self, n: usize) -> Option<Self::Item>

Returns the nth element of the iterator. Read more
1.28.0 · source§

fn step_by(self, step: usize) -> StepBy<Self>
where + Self: Sized,

Creates an iterator starting at the same point, but stepping by +the given amount at each iteration. Read more
1.0.0 · source§

fn chain<U>(self, other: U) -> Chain<Self, <U as IntoIterator>::IntoIter>
where + Self: Sized, + U: IntoIterator<Item = Self::Item>,

Takes two iterators and creates a new iterator over both in sequence. Read more
1.0.0 · source§

fn zip<U>(self, other: U) -> Zip<Self, <U as IntoIterator>::IntoIter>
where + Self: Sized, + U: IntoIterator,

‘Zips up’ two iterators into a single iterator of pairs. Read more
source§

fn intersperse_with<G>(self, separator: G) -> IntersperseWith<Self, G>
where + Self: Sized, + G: FnMut() -> Self::Item,

🔬This is a nightly-only experimental API. (iter_intersperse)
Creates a new iterator which places an item generated by separator +between adjacent items of the original iterator. Read more
1.0.0 · source§

fn map<B, F>(self, f: F) -> Map<Self, F>
where + Self: Sized, + F: FnMut(Self::Item) -> B,

Takes a closure and creates an iterator which calls that closure on each +element. Read more
1.21.0 · source§

fn for_each<F>(self, f: F)
where + Self: Sized, + F: FnMut(Self::Item),

Calls a closure on each element of an iterator. Read more
1.0.0 · source§

fn filter<P>(self, predicate: P) -> Filter<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Creates an iterator which uses a closure to determine if an element +should be yielded. Read more
1.0.0 · source§

fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F>
where + Self: Sized, + F: FnMut(Self::Item) -> Option<B>,

Creates an iterator that both filters and maps. Read more
1.0.0 · source§

fn enumerate(self) -> Enumerate<Self>
where + Self: Sized,

Creates an iterator which gives the current iteration count as well as +the next value. Read more
1.0.0 · source§

fn peekable(self) -> Peekable<Self>
where + Self: Sized,

Creates an iterator which can use the peek and peek_mut methods +to look at the next element of the iterator without consuming it. See +their documentation for more information. Read more
1.0.0 · source§

fn skip_while<P>(self, predicate: P) -> SkipWhile<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Creates an iterator that skips elements based on a predicate. Read more
1.0.0 · source§

fn take_while<P>(self, predicate: P) -> TakeWhile<Self, P>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Creates an iterator that yields elements based on a predicate. Read more
1.57.0 · source§

fn map_while<B, P>(self, predicate: P) -> MapWhile<Self, P>
where + Self: Sized, + P: FnMut(Self::Item) -> Option<B>,

Creates an iterator that both yields elements based on a predicate and maps. Read more
1.0.0 · source§

fn skip(self, n: usize) -> Skip<Self>
where + Self: Sized,

Creates an iterator that skips the first n elements. Read more
1.0.0 · source§

fn take(self, n: usize) -> Take<Self>
where + Self: Sized,

Creates an iterator that yields the first n elements, or fewer +if the underlying iterator ends sooner. Read more
1.0.0 · source§

fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F>
where + Self: Sized, + F: FnMut(&mut St, Self::Item) -> Option<B>,

An iterator adapter which, like fold, holds internal state, but +unlike fold, produces a new iterator. Read more
1.0.0 · source§

fn flat_map<U, F>(self, f: F) -> FlatMap<Self, U, F>
where + Self: Sized, + U: IntoIterator, + F: FnMut(Self::Item) -> U,

Creates an iterator that works like map, but flattens nested structure. Read more
source§

fn map_windows<F, R, const N: usize>(self, f: F) -> MapWindows<Self, F, N>
where + Self: Sized, + F: FnMut(&[Self::Item; N]) -> R,

🔬This is a nightly-only experimental API. (iter_map_windows)
Calls the given function f for each contiguous window of size N over +self and returns an iterator over the outputs of f. Like slice::windows(), +the windows during mapping overlap as well. Read more
1.0.0 · source§

fn fuse(self) -> Fuse<Self>
where + Self: Sized,

Creates an iterator which ends after the first None. Read more
1.0.0 · source§

fn inspect<F>(self, f: F) -> Inspect<Self, F>
where + Self: Sized, + F: FnMut(&Self::Item),

Does something with each element of an iterator, passing the value on. Read more
1.0.0 · source§

fn by_ref(&mut self) -> &mut Self
where + Self: Sized,

Borrows an iterator, rather than consuming it. Read more
1.0.0 · source§

fn collect<B>(self) -> B
where + B: FromIterator<Self::Item>, + Self: Sized,

Transforms an iterator into a collection. Read more
source§

fn collect_into<E>(self, collection: &mut E) -> &mut E
where + E: Extend<Self::Item>, + Self: Sized,

🔬This is a nightly-only experimental API. (iter_collect_into)
Collects all the items from an iterator into a collection. Read more
1.0.0 · source§

fn partition<B, F>(self, f: F) -> (B, B)
where + Self: Sized, + B: Default + Extend<Self::Item>, + F: FnMut(&Self::Item) -> bool,

Consumes an iterator, creating two collections from it. Read more
source§

fn is_partitioned<P>(self, predicate: P) -> bool
where + Self: Sized, + P: FnMut(Self::Item) -> bool,

🔬This is a nightly-only experimental API. (iter_is_partitioned)
Checks if the elements of this iterator are partitioned according to the given predicate, +such that all those that return true precede all those that return false. Read more
1.27.0 · source§

fn try_fold<B, F, R>(&mut self, init: B, f: F) -> R
where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try<Output = B>,

An iterator method that applies a function as long as it returns +successfully, producing a single, final value. Read more
1.27.0 · source§

fn try_for_each<F, R>(&mut self, f: F) -> R
where + Self: Sized, + F: FnMut(Self::Item) -> R, + R: Try<Output = ()>,

An iterator method that applies a fallible function to each item in the +iterator, stopping at the first error and returning that error. Read more
1.0.0 · source§

fn fold<B, F>(self, init: B, f: F) -> B
where + Self: Sized, + F: FnMut(B, Self::Item) -> B,

Folds every element into an accumulator by applying an operation, +returning the final result. Read more
1.51.0 · source§

fn reduce<F>(self, f: F) -> Option<Self::Item>
where + Self: Sized, + F: FnMut(Self::Item, Self::Item) -> Self::Item,

Reduces the elements to a single one, by repeatedly applying a reducing +operation. Read more
source§

fn try_reduce<F, R>( + &mut self, + f: F +) -> <<R as Try>::Residual as Residual<Option<<R as Try>::Output>>>::TryType
where + Self: Sized, + F: FnMut(Self::Item, Self::Item) -> R, + R: Try<Output = Self::Item>, + <R as Try>::Residual: Residual<Option<Self::Item>>,

🔬This is a nightly-only experimental API. (iterator_try_reduce)
Reduces the elements to a single one by repeatedly applying a reducing operation. If the +closure returns a failure, the failure is propagated back to the caller immediately. Read more
1.0.0 · source§

fn all<F>(&mut self, f: F) -> bool
where + Self: Sized, + F: FnMut(Self::Item) -> bool,

Tests if every element of the iterator matches a predicate. Read more
1.0.0 · source§

fn any<F>(&mut self, f: F) -> bool
where + Self: Sized, + F: FnMut(Self::Item) -> bool,

Tests if any element of the iterator matches a predicate. Read more
1.0.0 · source§

fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
where + Self: Sized, + P: FnMut(&Self::Item) -> bool,

Searches for an element of an iterator that satisfies a predicate. Read more
1.30.0 · source§

fn find_map<B, F>(&mut self, f: F) -> Option<B>
where + Self: Sized, + F: FnMut(Self::Item) -> Option<B>,

Applies function to the elements of iterator and returns +the first non-none result. Read more
source§

fn try_find<F, R>( + &mut self, + f: F +) -> <<R as Try>::Residual as Residual<Option<Self::Item>>>::TryType
where + Self: Sized, + F: FnMut(&Self::Item) -> R, + R: Try<Output = bool>, + <R as Try>::Residual: Residual<Option<Self::Item>>,

🔬This is a nightly-only experimental API. (try_find)
Applies function to the elements of iterator and returns +the first true result or the first error. Read more
1.0.0 · source§

fn position<P>(&mut self, predicate: P) -> Option<usize>
where + Self: Sized, + P: FnMut(Self::Item) -> bool,

Searches for an element in an iterator, returning its index. Read more
1.6.0 · source§

fn max_by_key<B, F>(self, f: F) -> Option<Self::Item>
where + B: Ord, + Self: Sized, + F: FnMut(&Self::Item) -> B,

Returns the element that gives the maximum value from the +specified function. Read more
1.15.0 · source§

fn max_by<F>(self, compare: F) -> Option<Self::Item>
where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering,

Returns the element that gives the maximum value with respect to the +specified comparison function. Read more
1.6.0 · source§

fn min_by_key<B, F>(self, f: F) -> Option<Self::Item>
where + B: Ord, + Self: Sized, + F: FnMut(&Self::Item) -> B,

Returns the element that gives the minimum value from the +specified function. Read more
1.15.0 · source§

fn min_by<F>(self, compare: F) -> Option<Self::Item>
where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering,

Returns the element that gives the minimum value with respect to the +specified comparison function. Read more
1.0.0 · source§

fn unzip<A, B, FromA, FromB>(self) -> (FromA, FromB)
where + FromA: Default + Extend<A>, + FromB: Default + Extend<B>, + Self: Sized + Iterator<Item = (A, B)>,

Converts an iterator of pairs into a pair of containers. Read more
1.36.0 · source§

fn copied<'a, T>(self) -> Copied<Self>
where + T: 'a + Copy, + Self: Sized + Iterator<Item = &'a T>,

Creates an iterator which copies all of its elements. Read more
1.0.0 · source§

fn cloned<'a, T>(self) -> Cloned<Self>
where + T: 'a + Clone, + Self: Sized + Iterator<Item = &'a T>,

Creates an iterator which clones all of its elements. Read more
source§

fn array_chunks<const N: usize>(self) -> ArrayChunks<Self, N>
where + Self: Sized,

🔬This is a nightly-only experimental API. (iter_array_chunks)
Returns an iterator over N elements of the iterator at a time. Read more
1.11.0 · source§

fn sum<S>(self) -> S
where + Self: Sized, + S: Sum<Self::Item>,

Sums the elements of an iterator. Read more
1.11.0 · source§

fn product<P>(self) -> P
where + Self: Sized, + P: Product<Self::Item>,

Iterates over the entire iterator, multiplying all the elements Read more
source§

fn cmp_by<I, F>(self, other: I, cmp: F) -> Ordering
where + Self: Sized, + I: IntoIterator, + F: FnMut(Self::Item, <I as IntoIterator>::Item) -> Ordering,

🔬This is a nightly-only experimental API. (iter_order_by)
Lexicographically compares the elements of this Iterator with those +of another with respect to the specified comparison function. Read more
1.5.0 · source§

fn partial_cmp<I>(self, other: I) -> Option<Ordering>
where + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Lexicographically compares the PartialOrd elements of +this Iterator with those of another. The comparison works like short-circuit +evaluation, returning a result without comparing the remaining elements. +As soon as an order can be determined, the evaluation stops and a result is returned. Read more
source§

fn partial_cmp_by<I, F>(self, other: I, partial_cmp: F) -> Option<Ordering>
where + Self: Sized, + I: IntoIterator, + F: FnMut(Self::Item, <I as IntoIterator>::Item) -> Option<Ordering>,

🔬This is a nightly-only experimental API. (iter_order_by)
Lexicographically compares the elements of this Iterator with those +of another with respect to the specified comparison function. Read more
1.5.0 · source§

fn eq<I>(self, other: I) -> bool
where + I: IntoIterator, + Self::Item: PartialEq<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are equal to those of +another. Read more
source§

fn eq_by<I, F>(self, other: I, eq: F) -> bool
where + Self: Sized, + I: IntoIterator, + F: FnMut(Self::Item, <I as IntoIterator>::Item) -> bool,

🔬This is a nightly-only experimental API. (iter_order_by)
Determines if the elements of this Iterator are equal to those of +another with respect to the specified equality function. Read more
1.5.0 · source§

fn ne<I>(self, other: I) -> bool
where + I: IntoIterator, + Self::Item: PartialEq<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are not equal to those of +another. Read more
1.5.0 · source§

fn lt<I>(self, other: I) -> bool
where + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are lexicographically +less than those of another. Read more
1.5.0 · source§

fn le<I>(self, other: I) -> bool
where + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are lexicographically +less or equal to those of another. Read more
1.5.0 · source§

fn gt<I>(self, other: I) -> bool
where + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are lexicographically +greater than those of another. Read more
1.5.0 · source§

fn ge<I>(self, other: I) -> bool
where + I: IntoIterator, + Self::Item: PartialOrd<<I as IntoIterator>::Item>, + Self: Sized,

Determines if the elements of this Iterator are lexicographically +greater than or equal to those of another. Read more
source§

fn is_sorted_by<F>(self, compare: F) -> bool
where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> bool,

🔬This is a nightly-only experimental API. (is_sorted)
Checks if the elements of this iterator are sorted using the given comparator function. Read more
source§

fn is_sorted_by_key<F, K>(self, f: F) -> bool
where + Self: Sized, + F: FnMut(Self::Item) -> K, + K: PartialOrd,

🔬This is a nightly-only experimental API. (is_sorted)
Checks if the elements of this iterator are sorted using the given key extraction +function. Read more

Auto Trait Implementations§

§

impl<'content> RefUnwindSafe for CapturableWindowIterator<'content>

§

impl<'content> Send for CapturableWindowIterator<'content>

§

impl<'content> Sync for CapturableWindowIterator<'content>

§

impl<'content> Unpin for CapturableWindowIterator<'content>

§

impl<'content> UnwindSafe for CapturableWindowIterator<'content>

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<I> IntoIterator for I
where + I: Iterator,

§

type Item = <I as Iterator>::Item

The type of the elements being iterated over.
§

type IntoIter = I

Which kind of iterator are we turning this into?
const: unstable · source§

fn into_iter(self) -> I

Creates an iterator from a value. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capture_stream/enum.CaptureConfigError.html b/docs/windows_docs/crabgrab/capture_stream/enum.CaptureConfigError.html new file mode 100644 index 00000000..b8992938 --- /dev/null +++ b/docs/windows_docs/crabgrab/capture_stream/enum.CaptureConfigError.html @@ -0,0 +1,19 @@ +CaptureConfigError in crabgrab::capture_stream - Rust +
pub enum CaptureConfigError {
+    UnsupportedPixelFormat,
+    InvalidBufferCount,
+}
Expand description

Represents an error creating the capture config

+

Variants§

§

UnsupportedPixelFormat

The pixel format is unsupported by the implementation

+
§

InvalidBufferCount

The buffer count is out of the valid range for the implementation

+

Trait Implementations§

source§

impl Clone for CaptureConfigError

source§

fn clone(&self) -> CaptureConfigError

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for CaptureConfigError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capture_stream/enum.CapturePixelFormat.html b/docs/windows_docs/crabgrab/capture_stream/enum.CapturePixelFormat.html new file mode 100644 index 00000000..8c473296 --- /dev/null +++ b/docs/windows_docs/crabgrab/capture_stream/enum.CapturePixelFormat.html @@ -0,0 +1,33 @@ +CapturePixelFormat in crabgrab::capture_stream - Rust +
#[non_exhaustive]
pub enum CapturePixelFormat { + Bgra8888, + Argb2101010, + V420, + F420, +}
Expand description

The pixel format of returned video frames

+

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

Bgra8888

One plane, 4 channels, 8 bits per channel: {b: u8, g: u8, r: u8, a: u8}, full range: [0, 255]

+
§

Argb2101010

One plane, 4 channels, 10 bits per color channel, two bits for alpha: {a: u2, r: u10, g: u10, b: u10}, rgb range: [0, 1023], alpha range: [0, 3]

+
§

V420

Two planes:

+
    +
  • 1 channel, luminance, 8 bits per pixel, video range: [16, 240]
  • +
  • 2 channels, chroma (cb, cr) 8 bits bits per channel per two pixels vertically, range: [0, 255]
  • +
+
§

F420

Two planes:

+
    +
  • 1 channel, luminance, 8 bits per pixel, full range: [0, 255]
  • +
  • 2 channels, chroma (cb, cr) 8 bits bits per channel per two pixels vertically, range: [0, 255]
  • +
+

Trait Implementations§

source§

impl Clone for CapturePixelFormat

source§

fn clone(&self) -> CapturePixelFormat

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for CapturePixelFormat

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for CapturePixelFormat

source§

fn eq(&self, other: &CapturePixelFormat) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Copy for CapturePixelFormat

source§

impl Eq for CapturePixelFormat

source§

impl StructuralPartialEq for CapturePixelFormat

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capture_stream/enum.StreamCreateError.html b/docs/windows_docs/crabgrab/capture_stream/enum.StreamCreateError.html new file mode 100644 index 00000000..e7876286 --- /dev/null +++ b/docs/windows_docs/crabgrab/capture_stream/enum.StreamCreateError.html @@ -0,0 +1,19 @@ +StreamCreateError in crabgrab::capture_stream - Rust +
pub enum StreamCreateError {
+    Other(String),
+    UnsupportedPixelFormat,
+}
Expand description

This represents an error when creating a capture stream

+

Variants§

§

Other(String)

§

UnsupportedPixelFormat

The supplied pixel format is unsupported by the implementation

+

Trait Implementations§

source§

impl Clone for StreamCreateError

source§

fn clone(&self) -> StreamCreateError

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for StreamCreateError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for StreamCreateError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for StreamCreateError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more
source§

impl Send for StreamCreateError

source§

impl Sync for StreamCreateError

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capture_stream/enum.StreamError.html b/docs/windows_docs/crabgrab/capture_stream/enum.StreamError.html new file mode 100644 index 00000000..825f2132 --- /dev/null +++ b/docs/windows_docs/crabgrab/capture_stream/enum.StreamError.html @@ -0,0 +1,17 @@ +StreamError in crabgrab::capture_stream - Rust +
pub enum StreamError {
+    Other(String),
+}
Expand description

This represents an error during a stream, for example a failure to retreive a video or audio frame

+

Variants§

§

Other(String)

Trait Implementations§

source§

impl Clone for StreamError

source§

fn clone(&self) -> StreamError

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for StreamError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for StreamError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for StreamError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capture_stream/enum.StreamEvent.html b/docs/windows_docs/crabgrab/capture_stream/enum.StreamEvent.html new file mode 100644 index 00000000..c237af2c --- /dev/null +++ b/docs/windows_docs/crabgrab/capture_stream/enum.StreamEvent.html @@ -0,0 +1,22 @@ +StreamEvent in crabgrab::capture_stream - Rust +
pub enum StreamEvent {
+    Audio(AudioFrame),
+    Video(VideoFrame),
+    Idle,
+    End,
+}
Expand description

Represents an event in a capture stream

+

Variants§

§

Audio(AudioFrame)

This event is produced when the stream receives a new audio packet

+
§

Video(VideoFrame)

This event is produced when the stream receives a new video frame

+
§

Idle

This event is produced when the stream goes idle - IE when no new frames are expected for some time, like when a window minimizes

+
§

End

This event is produced once at the end of the stream

+

Trait Implementations§

source§

impl Debug for StreamEvent

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capture_stream/enum.StreamStopError.html b/docs/windows_docs/crabgrab/capture_stream/enum.StreamStopError.html new file mode 100644 index 00000000..56b5395b --- /dev/null +++ b/docs/windows_docs/crabgrab/capture_stream/enum.StreamStopError.html @@ -0,0 +1,17 @@ +StreamStopError in crabgrab::capture_stream - Rust +
pub enum StreamStopError {
+    Other(String),
+    AlreadyStopped,
+}
Expand description

This represents an error while stopping a stream

+

Variants§

§

Other(String)

§

AlreadyStopped

The stream was already stopped

+

Trait Implementations§

source§

impl Debug for StreamStopError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Send for StreamStopError

source§

impl Sync for StreamStopError

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capture_stream/index.html b/docs/windows_docs/crabgrab/capture_stream/index.html new file mode 100644 index 00000000..7cf07e00 --- /dev/null +++ b/docs/windows_docs/crabgrab/capture_stream/index.html @@ -0,0 +1,2 @@ +crabgrab::capture_stream - Rust +

Structs§

Enums§

\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capture_stream/sidebar-items.js b/docs/windows_docs/crabgrab/capture_stream/sidebar-items.js new file mode 100644 index 00000000..583d3409 --- /dev/null +++ b/docs/windows_docs/crabgrab/capture_stream/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["CaptureConfigError","CapturePixelFormat","StreamCreateError","StreamError","StreamEvent","StreamStopError"],"struct":["AudioCaptureConfig","CaptureConfig","CaptureStream"]}; \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capture_stream/struct.AudioCaptureConfig.html b/docs/windows_docs/crabgrab/capture_stream/struct.AudioCaptureConfig.html new file mode 100644 index 00000000..5f5d4617 --- /dev/null +++ b/docs/windows_docs/crabgrab/capture_stream/struct.AudioCaptureConfig.html @@ -0,0 +1,19 @@ +AudioCaptureConfig in crabgrab::capture_stream - Rust +
pub struct AudioCaptureConfig { /* private fields */ }
Expand description

Configuration settings for audio streams

+

Implementations§

source§

impl AudioCaptureConfig

source

pub fn new() -> Self

Creates a new audio capture config with default settings:

+
    +
  • 24000 hz
  • +
  • Mono
  • +
+

Trait Implementations§

source§

impl Clone for AudioCaptureConfig

source§

fn clone(&self) -> AudioCaptureConfig

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for AudioCaptureConfig

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capture_stream/struct.CaptureConfig.html b/docs/windows_docs/crabgrab/capture_stream/struct.CaptureConfig.html new file mode 100644 index 00000000..14057122 --- /dev/null +++ b/docs/windows_docs/crabgrab/capture_stream/struct.CaptureConfig.html @@ -0,0 +1,26 @@ +CaptureConfig in crabgrab::capture_stream - Rust +
pub struct CaptureConfig { /* private fields */ }
Expand description

Configuration settings for a capture stream

+

Implementations§

source§

impl CaptureConfig

source

pub fn with_window( + window: CapturableWindow, + pixel_format: CapturePixelFormat +) -> Result<CaptureConfig, CaptureConfigError>

Create a capture configuration for a given capturable window

+
source

pub fn with_display( + display: CapturableDisplay, + pixel_format: CapturePixelFormat +) -> CaptureConfig

Create a capture configuration for a given capturable display

+
source

pub fn with_buffer_count(self, buffer_count: usize) -> Self

Configure the buffer count - the number of frames in the capture queue.

+

Higher numbers mean higher latency, but smoother performance

+
source

pub fn with_show_cursor(self, show_cursor: bool) -> Self

Configure whether the cursor is visible in the capture

+
source

pub fn set_output_size(self, output_size: Size) -> Self

Configure the output texture size - by default, this will match the captured content at the time of enumeration

+

Trait Implementations§

source§

impl Clone for CaptureConfig

source§

fn clone(&self) -> CaptureConfig

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for CaptureConfig

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/capture_stream/struct.CaptureStream.html b/docs/windows_docs/crabgrab/capture_stream/struct.CaptureStream.html new file mode 100644 index 00000000..c0827ea5 --- /dev/null +++ b/docs/windows_docs/crabgrab/capture_stream/struct.CaptureStream.html @@ -0,0 +1,22 @@ +CaptureStream in crabgrab::capture_stream - Rust +
pub struct CaptureStream { /* private fields */ }
Expand description

Represents an active capture stream

+

Implementations§

source§

impl CaptureStream

source

pub fn test_access(borderless: bool) -> bool

Test whether the calling application has permission to capture content

+
source

pub async fn request_access(borderless: bool) -> bool

Prompt the user for permission to capture content

+
source

pub fn supported_pixel_formats() -> &'static [CapturePixelFormat]

Gets the implementation’s supported pixel formats

+

Note that the returned formats may not work for all capture modalities (eg, window vs display)

+
source

pub fn new( + config: CaptureConfig, + callback: impl FnMut(Result<StreamEvent, StreamError>) + Send + 'static +) -> Result<Self, StreamCreateError>

Start a new capture stream with the given stream callback

+
source

pub fn stop(&mut self) -> Result<(), StreamStopError>

Stop the capture

+

Trait Implementations§

source§

impl WindowsDx11CaptureStream for CaptureStream

source§

fn get_dx11_device(&self) -> ID3D11Device

Get the underlying D3D11 device used for frame capture
source§

impl WindowsDxgiCaptureStream for CaptureStream

source§

fn get_dxgi_adapter(&self) -> IDXGIAdapter

Get the dxgi adapter used by the capture stream for frame generation
source§

fn get_dxgi_device(&self) -> IDXGIDevice

Get the dxgi device used by the capture stream for frame generation

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/feature/dx11/enum.WindowsDx11VideoFrameError.html b/docs/windows_docs/crabgrab/feature/dx11/enum.WindowsDx11VideoFrameError.html new file mode 100644 index 00000000..00b351ad --- /dev/null +++ b/docs/windows_docs/crabgrab/feature/dx11/enum.WindowsDx11VideoFrameError.html @@ -0,0 +1,16 @@ +WindowsDx11VideoFrameError in crabgrab::feature::dx11 - Rust +
pub enum WindowsDx11VideoFrameError {
+    Other(String),
+}

Variants§

§

Other(String)

Trait Implementations§

source§

impl Clone for WindowsDx11VideoFrameError

source§

fn clone(&self) -> WindowsDx11VideoFrameError

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for WindowsDx11VideoFrameError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for WindowsDx11VideoFrameError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for WindowsDx11VideoFrameError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/feature/dx11/index.html b/docs/windows_docs/crabgrab/feature/dx11/index.html new file mode 100644 index 00000000..bb71d2e4 --- /dev/null +++ b/docs/windows_docs/crabgrab/feature/dx11/index.html @@ -0,0 +1,2 @@ +crabgrab::feature::dx11 - Rust +
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/feature/dx11/sidebar-items.js b/docs/windows_docs/crabgrab/feature/dx11/sidebar-items.js new file mode 100644 index 00000000..cbe639e0 --- /dev/null +++ b/docs/windows_docs/crabgrab/feature/dx11/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["WindowsDx11VideoFrameError"],"trait":["WindowsDx11CaptureStream","WindowsDx11VideoFrame"]}; \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/feature/dx11/trait.WindowsDx11CaptureStream.html b/docs/windows_docs/crabgrab/feature/dx11/trait.WindowsDx11CaptureStream.html new file mode 100644 index 00000000..a686ddfb --- /dev/null +++ b/docs/windows_docs/crabgrab/feature/dx11/trait.WindowsDx11CaptureStream.html @@ -0,0 +1,6 @@ +WindowsDx11CaptureStream in crabgrab::feature::dx11 - Rust +
pub trait WindowsDx11CaptureStream {
+    // Required method
+    fn get_dx11_device(&self) -> ID3D11Device;
+}

Required Methods§

source

fn get_dx11_device(&self) -> ID3D11Device

Get the underlying D3D11 device used for frame capture

+

Implementors§

\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/feature/dx11/trait.WindowsDx11VideoFrame.html b/docs/windows_docs/crabgrab/feature/dx11/trait.WindowsDx11VideoFrame.html new file mode 100644 index 00000000..073c7be9 --- /dev/null +++ b/docs/windows_docs/crabgrab/feature/dx11/trait.WindowsDx11VideoFrame.html @@ -0,0 +1,10 @@ +WindowsDx11VideoFrame in crabgrab::feature::dx11 - Rust +
pub trait WindowsDx11VideoFrame {
+    // Required method
+    fn get_dx11_surface(
+        &self
+    ) -> Result<(IDirect3DSurface, DirectXPixelFormat), WindowsDx11VideoFrameError>;
+}

Required Methods§

source

fn get_dx11_surface( + &self +) -> Result<(IDirect3DSurface, DirectXPixelFormat), WindowsDx11VideoFrameError>

Get the DX11 surface representing the video frame’s texture memory, as well as the pixel format

+

Implementors§

\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/feature/dxgi/enum.WindowsDxgiVideoFrameError.html b/docs/windows_docs/crabgrab/feature/dxgi/enum.WindowsDxgiVideoFrameError.html new file mode 100644 index 00000000..6bb67527 --- /dev/null +++ b/docs/windows_docs/crabgrab/feature/dxgi/enum.WindowsDxgiVideoFrameError.html @@ -0,0 +1,16 @@ +WindowsDxgiVideoFrameError in crabgrab::feature::dxgi - Rust +
pub enum WindowsDxgiVideoFrameError {
+    Other(String),
+}

Variants§

§

Other(String)

Trait Implementations§

source§

impl Clone for WindowsDxgiVideoFrameError

source§

fn clone(&self) -> WindowsDxgiVideoFrameError

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for WindowsDxgiVideoFrameError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Display for WindowsDxgiVideoFrameError

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Error for WindowsDxgiVideoFrameError

source§

fn source(&self) -> Option<&(dyn Error + 'static)>

The lower-level source of this error, if any. Read more
source§

fn description(&self) -> &str

👎Deprecated since 1.42.0: use the Display impl or to_string()
source§

fn cause(&self) -> Option<&dyn Error>

👎Deprecated since 1.33.0: replaced by Error::source, which can support downcasting
source§

fn provide<'a>(&'a self, request: &mut Request<'a>)

🔬This is a nightly-only experimental API. (error_generic_member_access)
Provides type based access to context intended for error reports. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T> ToString for T
where + T: Display + ?Sized,

source§

default fn to_string(&self) -> String

Converts the given value to a String. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/feature/dxgi/index.html b/docs/windows_docs/crabgrab/feature/dxgi/index.html new file mode 100644 index 00000000..296e61a9 --- /dev/null +++ b/docs/windows_docs/crabgrab/feature/dxgi/index.html @@ -0,0 +1,2 @@ +crabgrab::feature::dxgi - Rust +
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/feature/dxgi/sidebar-items.js b/docs/windows_docs/crabgrab/feature/dxgi/sidebar-items.js new file mode 100644 index 00000000..61533b94 --- /dev/null +++ b/docs/windows_docs/crabgrab/feature/dxgi/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["WindowsDxgiVideoFrameError"],"trait":["WindowsDxgiCaptureStream","WindowsDxgiVideoFrame"]}; \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/feature/dxgi/trait.WindowsDxgiCaptureStream.html b/docs/windows_docs/crabgrab/feature/dxgi/trait.WindowsDxgiCaptureStream.html new file mode 100644 index 00000000..5c82093b --- /dev/null +++ b/docs/windows_docs/crabgrab/feature/dxgi/trait.WindowsDxgiCaptureStream.html @@ -0,0 +1,8 @@ +WindowsDxgiCaptureStream in crabgrab::feature::dxgi - Rust +
pub trait WindowsDxgiCaptureStream {
+    // Required methods
+    fn get_dxgi_adapter(&self) -> IDXGIAdapter;
+    fn get_dxgi_device(&self) -> IDXGIDevice;
+}

Required Methods§

source

fn get_dxgi_adapter(&self) -> IDXGIAdapter

Get the dxgi adapter used by the capture stream for frame generation

+
source

fn get_dxgi_device(&self) -> IDXGIDevice

Get the dxgi device used by the capture stream for frame generation

+

Implementors§

\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/feature/dxgi/trait.WindowsDxgiVideoFrame.html b/docs/windows_docs/crabgrab/feature/dxgi/trait.WindowsDxgiVideoFrame.html new file mode 100644 index 00000000..ec53c546 --- /dev/null +++ b/docs/windows_docs/crabgrab/feature/dxgi/trait.WindowsDxgiVideoFrame.html @@ -0,0 +1,10 @@ +WindowsDxgiVideoFrame in crabgrab::feature::dxgi - Rust +
pub trait WindowsDxgiVideoFrame {
+    // Required method
+    fn get_dxgi_surface(
+        &self
+    ) -> Result<(IDXGISurface, DirectXPixelFormat), WindowsDxgiVideoFrameError>;
+}

Required Methods§

source

fn get_dxgi_surface( + &self +) -> Result<(IDXGISurface, DirectXPixelFormat), WindowsDxgiVideoFrameError>

Get the surface texture for this video frame

+

Implementors§

\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/feature/index.html b/docs/windows_docs/crabgrab/feature/index.html new file mode 100644 index 00000000..b3cf3961 --- /dev/null +++ b/docs/windows_docs/crabgrab/feature/index.html @@ -0,0 +1,2 @@ +crabgrab::feature - Rust +

Module crabgrab::feature

source ·

Modules§

\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/feature/sidebar-items.js b/docs/windows_docs/crabgrab/feature/sidebar-items.js new file mode 100644 index 00000000..8c92e064 --- /dev/null +++ b/docs/windows_docs/crabgrab/feature/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"mod":["dx11","dxgi"]}; \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/frame/enum.AudioBufferError.html b/docs/windows_docs/crabgrab/frame/enum.AudioBufferError.html new file mode 100644 index 00000000..5469a125 --- /dev/null +++ b/docs/windows_docs/crabgrab/frame/enum.AudioBufferError.html @@ -0,0 +1,17 @@ +AudioBufferError in crabgrab::frame - Rust +
pub enum AudioBufferError {
+    UnsupportedFormat,
+    InvalidChannel,
+    Other(String),
+}
Expand description

Represents an error getting the data for an audio channel

+

Variants§

§

UnsupportedFormat

§

InvalidChannel

§

Other(String)

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/frame/enum.AudioChannelCount.html b/docs/windows_docs/crabgrab/frame/enum.AudioChannelCount.html new file mode 100644 index 00000000..a30e678b --- /dev/null +++ b/docs/windows_docs/crabgrab/frame/enum.AudioChannelCount.html @@ -0,0 +1,17 @@ +AudioChannelCount in crabgrab::frame - Rust +
pub enum AudioChannelCount {
+    Mono,
+    Stereo,
+}
Expand description

The number of audio channels to capture

+

Variants§

§

Mono

§

Stereo

Trait Implementations§

source§

impl Clone for AudioChannelCount

source§

fn clone(&self) -> AudioChannelCount

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for AudioChannelCount

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Copy for AudioChannelCount

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/frame/enum.AudioChannelData.html b/docs/windows_docs/crabgrab/frame/enum.AudioChannelData.html new file mode 100644 index 00000000..fc5e8a31 --- /dev/null +++ b/docs/windows_docs/crabgrab/frame/enum.AudioChannelData.html @@ -0,0 +1,17 @@ +AudioChannelData in crabgrab::frame - Rust +
pub enum AudioChannelData<'data> {
+    F32(AudioChannelDataSamples<'data, f32>),
+    I32(AudioChannelDataSamples<'data, i32>),
+    I16(AudioChannelDataSamples<'data, i16>),
+}
Expand description

Represents audio channel data in an audio frame

+

Variants§

Auto Trait Implementations§

§

impl<'data> RefUnwindSafe for AudioChannelData<'data>

§

impl<'data> !Send for AudioChannelData<'data>

§

impl<'data> !Sync for AudioChannelData<'data>

§

impl<'data> Unpin for AudioChannelData<'data>

§

impl<'data> UnwindSafe for AudioChannelData<'data>

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/frame/enum.AudioSampleRate.html b/docs/windows_docs/crabgrab/frame/enum.AudioSampleRate.html new file mode 100644 index 00000000..373f759b --- /dev/null +++ b/docs/windows_docs/crabgrab/frame/enum.AudioSampleRate.html @@ -0,0 +1,19 @@ +AudioSampleRate in crabgrab::frame - Rust +
pub enum AudioSampleRate {
+    Hz8000,
+    Hz16000,
+    Hz24000,
+    Hz48000,
+}
Expand description

The rate to capture audio samples

+

Variants§

§

Hz8000

§

Hz16000

§

Hz24000

§

Hz48000

Trait Implementations§

source§

impl Clone for AudioSampleRate

source§

fn clone(&self) -> AudioSampleRate

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for AudioSampleRate

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Copy for AudioSampleRate

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/frame/index.html b/docs/windows_docs/crabgrab/frame/index.html new file mode 100644 index 00000000..ec1a7f1a --- /dev/null +++ b/docs/windows_docs/crabgrab/frame/index.html @@ -0,0 +1,2 @@ +crabgrab::frame - Rust +

Module crabgrab::frame

source ·

Structs§

Enums§

\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/frame/sidebar-items.js b/docs/windows_docs/crabgrab/frame/sidebar-items.js new file mode 100644 index 00000000..099e8e73 --- /dev/null +++ b/docs/windows_docs/crabgrab/frame/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["AudioBufferError","AudioChannelCount","AudioChannelData","AudioSampleRate"],"struct":["AudioChannelDataSamples","AudioFrame","VideoFrame"]}; \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/frame/struct.AudioChannelDataSamples.html b/docs/windows_docs/crabgrab/frame/struct.AudioChannelDataSamples.html new file mode 100644 index 00000000..a44fdfd6 --- /dev/null +++ b/docs/windows_docs/crabgrab/frame/struct.AudioChannelDataSamples.html @@ -0,0 +1,14 @@ +AudioChannelDataSamples in crabgrab::frame - Rust +
pub struct AudioChannelDataSamples<'data, T> { /* private fields */ }

Auto Trait Implementations§

§

impl<'data, T> RefUnwindSafe for AudioChannelDataSamples<'data, T>
where + T: RefUnwindSafe,

§

impl<'data, T> !Send for AudioChannelDataSamples<'data, T>

§

impl<'data, T> !Sync for AudioChannelDataSamples<'data, T>

§

impl<'data, T> Unpin for AudioChannelDataSamples<'data, T>

§

impl<'data, T> UnwindSafe for AudioChannelDataSamples<'data, T>
where + T: RefUnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/frame/struct.AudioFrame.html b/docs/windows_docs/crabgrab/frame/struct.AudioFrame.html new file mode 100644 index 00000000..0c188318 --- /dev/null +++ b/docs/windows_docs/crabgrab/frame/struct.AudioFrame.html @@ -0,0 +1,23 @@ +AudioFrame in crabgrab::frame - Rust +

Struct crabgrab::frame::AudioFrame

source ·
pub struct AudioFrame { /* private fields */ }
Expand description

A frame of captured audio

+

Implementations§

source§

impl AudioFrame

source

pub fn sample_rate(&self) -> AudioSampleRate

Get the sample rate of the captured audio

+
source

pub fn channel_count(&self) -> AudioChannelCount

Get the channel count of the captured audio

+
source

pub fn audio_channel_buffer( + &mut self, + channel: usize +) -> Result<AudioChannelData<'_>, AudioBufferError>

Get the data buffer for the captured audio channel

+
source

pub fn duration(&self) -> Duration

Get the duration of this audio frames

+
source

pub fn origin_time(&self) -> Duration

Get the time since the start of the stream that this audio frame begins at

+
source

pub fn frame_id(&self) -> u64

Get the sequence id of this frame (monotonically increasing)

+

Note: This is separate from video frame ids

+

Trait Implementations§

source§

impl Debug for AudioFrame

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/frame/struct.VideoFrame.html b/docs/windows_docs/crabgrab/frame/struct.VideoFrame.html new file mode 100644 index 00000000..33387415 --- /dev/null +++ b/docs/windows_docs/crabgrab/frame/struct.VideoFrame.html @@ -0,0 +1,24 @@ +VideoFrame in crabgrab::frame - Rust +

Struct crabgrab::frame::VideoFrame

source ·
pub struct VideoFrame { /* private fields */ }
Expand description

A frame of captured video

+

Implementations§

source§

impl VideoFrame

source

pub fn frame_id(&self) -> u64

Get the sequence id of this video frame (monotonically increasing)

+

Note: This is separate from audio frame ids

+
source

pub fn capture_time(&self) -> Instant

Get the Instant that this frame was delivered to the application

+
source

pub fn origin_time(&self) -> Duration

Get the time since the start of the stream that this frame was generated

+
source

pub fn size(&self) -> Size

Get the raw size of the frame

+

For planar image formats, this is the size of the largest plane

+
source

pub fn dpi(&self) -> f64

Get the dpi of the contents of the frame (accounting for capture scaling)

+

Trait Implementations§

source§

impl Debug for VideoFrame

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl WindowsDx11VideoFrame for VideoFrame

source§

fn get_dx11_surface( + &self +) -> Result<(IDirect3DSurface, DirectXPixelFormat), WindowsDx11VideoFrameError>

Get the DX11 surface representing the video frame’s texture memory, as well as the pixel format
source§

impl WindowsDxgiVideoFrame for VideoFrame

source§

fn get_dxgi_surface( + &self +) -> Result<(IDXGISurface, DirectXPixelFormat), WindowsDxgiVideoFrameError>

Get the surface texture for this video frame
source§

impl Send for VideoFrame

source§

impl Sync for VideoFrame

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/index.html b/docs/windows_docs/crabgrab/index.html new file mode 100644 index 00000000..1de7868c --- /dev/null +++ b/docs/windows_docs/crabgrab/index.html @@ -0,0 +1,74 @@ +crabgrab - Rust +

Crate crabgrab

source ·
Expand description

A cross-platform screen/window/audio capture library

+

§Feature flags

§GPU Interop

+
    +
  • dx11 - enables retreiving the surface of a video frame and getting the dx11 device instance for the stream (windows only)
  • +
  • dxgi - enables retreiving the surface of a video frame and getting the dxgi device instance for the stream (windows only)
  • +
  • metal - enabels retreiving the metal textures for a video frame and getting the metal device instance for the stream (macos only)
  • +
  • iosurface - enables retreiving the iosurface for a video frame (macos only)
  • +
+

§Bitmap output

+
    +
  • bitmap - enables creating raw bitmap copies of frames in system memory
  • +
+

§Example

+
use std::time::Duration;
+use crabgrab::prelude::*;
+ 
+// spin up the async runtime
+let runtime = tokio::runtime::Builder::new_multi_thread().build().unwrap();
+// run our capture code in an async context
+let future = runtime.spawn(async {
+    // ensure we have priveleges to capture content
+    if !CaptureStream::test_access(false) {
+        CaptureStream::request_access(false).await;
+        println!("Approve access and run again!");
+    }
+    // create a filter for the windows we're interested in capturing
+    let window_filter = CapturableWindowFilter {
+        desktop_windows: false,
+        onscreen_only: true,
+    };
+    // create an overall content filter
+    let filter = CapturableContentFilter { windows: Some(window_filter), displays: false };
+    // get capturable content matching the filter
+    let content = CapturableContent::new(filter).await.unwrap();
+    // find the window we want to capture
+    let window = content.windows().filter(|window| {
+        let app_identifier = window.application().identifier();
+        app_identifier.to_lowercase().contains("finder") || app_identifier.to_lowercase().contains("explorer")
+    }).next();
+    match window {
+        Some(window) => {
+            println!("capturing window: {}", window.title()); 
+            // create a captuere config using the first supported pixel format
+            let config = CaptureConfig::with_window(window, CaptureStream::supported_pixel_formats()[0]).unwrap();
+            // create a capture stream with an event handler callback
+            let mut stream = CaptureStream::new(config, |stream_event| {
+                match stream_event {
+                    Ok(event) => {
+                        match event {
+                            StreamEvent::Video(frame) => {
+                                println!("Got frame: {}", frame.frame_id());
+                            },
+                            _ => {}
+                        }
+                    },
+                    Err(error) => {
+                        println!("Stream error: {:?}", error);
+                    }
+                }
+            }).unwrap();
+            // wait for a while to capture some frames
+            tokio::task::block_in_place(|| std::thread::sleep(Duration::from_millis(4000)));
+            stream.stop().unwrap();
+        },
+        None => { println!("Failed to find window"); }
+    }
+});
+// wait for the future to complete
+runtime.block_on(future).unwrap();
+// shutdown the async runtime
+runtime.shutdown_timeout(Duration::from_millis(10000));
+

Modules§

\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/index.html b/docs/windows_docs/crabgrab/platform/index.html new file mode 100644 index 00000000..65dc765a --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/index.html @@ -0,0 +1,2 @@ +crabgrab::platform - Rust +

Module crabgrab::platform

source ·

Re-exports§

Modules§

\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/sidebar-items.js b/docs/windows_docs/crabgrab/platform/sidebar-items.js new file mode 100644 index 00000000..f8cae588 --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"mod":["windows"]}; \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/capturable_content/struct.WindowsCapturableApplication.html b/docs/windows_docs/crabgrab/platform/windows/capturable_content/struct.WindowsCapturableApplication.html new file mode 100644 index 00000000..0c3a487d --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/capturable_content/struct.WindowsCapturableApplication.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../crabgrab/platform/windows/struct.ImplCapturableApplication.html...

+ + + \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/capturable_content/struct.WindowsCapturableContent.html b/docs/windows_docs/crabgrab/platform/windows/capturable_content/struct.WindowsCapturableContent.html new file mode 100644 index 00000000..54c69331 --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/capturable_content/struct.WindowsCapturableContent.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../crabgrab/platform/windows/struct.ImplCapturableContent.html...

+ + + \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/capturable_content/struct.WindowsCapturableDisplay.html b/docs/windows_docs/crabgrab/platform/windows/capturable_content/struct.WindowsCapturableDisplay.html new file mode 100644 index 00000000..b74cff0a --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/capturable_content/struct.WindowsCapturableDisplay.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../crabgrab/platform/windows/struct.ImplCapturableDisplay.html...

+ + + \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/capturable_content/struct.WindowsCapturableWindow.html b/docs/windows_docs/crabgrab/platform/windows/capturable_content/struct.WindowsCapturableWindow.html new file mode 100644 index 00000000..8718c323 --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/capturable_content/struct.WindowsCapturableWindow.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../crabgrab/platform/windows/struct.ImplCapturableWindow.html...

+ + + \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/capture_stream/enum.WindowsPixelFormat.html b/docs/windows_docs/crabgrab/platform/windows/capture_stream/enum.WindowsPixelFormat.html new file mode 100644 index 00000000..a9832c95 --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/capture_stream/enum.WindowsPixelFormat.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../crabgrab/platform/windows/enum.ImplPixelFormat.html...

+ + + \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/capture_stream/struct.WindowsAudioCaptureConfig.html b/docs/windows_docs/crabgrab/platform/windows/capture_stream/struct.WindowsAudioCaptureConfig.html new file mode 100644 index 00000000..e94a45e4 --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/capture_stream/struct.WindowsAudioCaptureConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../crabgrab/platform/windows/struct.ImplAudioCaptureConfig.html...

+ + + \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/capture_stream/struct.WindowsCaptureConfig.html b/docs/windows_docs/crabgrab/platform/windows/capture_stream/struct.WindowsCaptureConfig.html new file mode 100644 index 00000000..e60d1eaf --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/capture_stream/struct.WindowsCaptureConfig.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../crabgrab/platform/windows/struct.ImplCaptureConfig.html...

+ + + \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/capture_stream/struct.WindowsCaptureStream.html b/docs/windows_docs/crabgrab/platform/windows/capture_stream/struct.WindowsCaptureStream.html new file mode 100644 index 00000000..54d50819 --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/capture_stream/struct.WindowsCaptureStream.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../crabgrab/platform/windows/struct.ImplCaptureStream.html...

+ + + \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/enum.ImplPixelFormat.html b/docs/windows_docs/crabgrab/platform/windows/enum.ImplPixelFormat.html new file mode 100644 index 00000000..01624c8e --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/enum.ImplPixelFormat.html @@ -0,0 +1,17 @@ +ImplPixelFormat in crabgrab::platform::windows - Rust +
pub enum ImplPixelFormat {
+    Bgra8888,
+}

Variants§

§

Bgra8888

Trait Implementations§

source§

impl Clone for WindowsPixelFormat

source§

fn clone(&self) -> WindowsPixelFormat

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for WindowsPixelFormat

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl PartialEq for WindowsPixelFormat

source§

fn eq(&self, other: &WindowsPixelFormat) -> bool

This method tests for self and other values to be equal, and is used +by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always +sufficient, and should not be overridden without very good reason.
source§

impl Copy for WindowsPixelFormat

source§

impl Eq for WindowsPixelFormat

source§

impl StructuralPartialEq for WindowsPixelFormat

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/frame/struct.WindowsAudioFrame.html b/docs/windows_docs/crabgrab/platform/windows/frame/struct.WindowsAudioFrame.html new file mode 100644 index 00000000..b5209aa3 --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/frame/struct.WindowsAudioFrame.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../crabgrab/platform/windows/struct.ImplAudioFrame.html...

+ + + \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/frame/struct.WindowsVideoFrame.html b/docs/windows_docs/crabgrab/platform/windows/frame/struct.WindowsVideoFrame.html new file mode 100644 index 00000000..9a909621 --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/frame/struct.WindowsVideoFrame.html @@ -0,0 +1,11 @@ + + + + + Redirection + + +

Redirecting to ../../../../crabgrab/platform/windows/struct.ImplVideoFrame.html...

+ + + \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/index.html b/docs/windows_docs/crabgrab/platform/windows/index.html new file mode 100644 index 00000000..7f47800e --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/index.html @@ -0,0 +1,2 @@ +crabgrab::platform::windows - Rust +
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/sidebar-items.js b/docs/windows_docs/crabgrab/platform/windows/sidebar-items.js new file mode 100644 index 00000000..db7d75f0 --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"enum":["ImplPixelFormat"],"struct":["ImplAudioCaptureConfig","ImplAudioFrame","ImplCapturableApplication","ImplCapturableContent","ImplCapturableDisplay","ImplCapturableWindow","ImplCaptureConfig","ImplCaptureStream","ImplVideoFrame"]}; \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/struct.ImplAudioCaptureConfig.html b/docs/windows_docs/crabgrab/platform/windows/struct.ImplAudioCaptureConfig.html new file mode 100644 index 00000000..482e5db8 --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/struct.ImplAudioCaptureConfig.html @@ -0,0 +1,13 @@ +ImplAudioCaptureConfig in crabgrab::platform::windows - Rust +
pub struct ImplAudioCaptureConfig {}

Implementations§

Trait Implementations§

source§

impl Clone for WindowsAudioCaptureConfig

source§

fn clone(&self) -> WindowsAudioCaptureConfig

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for WindowsAudioCaptureConfig

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/struct.ImplAudioFrame.html b/docs/windows_docs/crabgrab/platform/windows/struct.ImplAudioFrame.html new file mode 100644 index 00000000..95a38b28 --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/struct.ImplAudioFrame.html @@ -0,0 +1,12 @@ +ImplAudioFrame in crabgrab::platform::windows - Rust +
pub struct ImplAudioFrame { /* private fields */ }

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/struct.ImplCapturableApplication.html b/docs/windows_docs/crabgrab/platform/windows/struct.ImplCapturableApplication.html new file mode 100644 index 00000000..ed5e8ddb --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/struct.ImplCapturableApplication.html @@ -0,0 +1,13 @@ +ImplCapturableApplication in crabgrab::platform::windows - Rust +
pub struct ImplCapturableApplication(/* private fields */);

Implementations§

source§

impl WindowsCapturableApplication

source

pub fn from_impl(handle: HANDLE) -> Self

source

pub fn identifier(&self) -> String

Trait Implementations§

source§

impl Clone for WindowsCapturableApplication

source§

fn clone(&self) -> WindowsCapturableApplication

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for WindowsCapturableApplication

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/struct.ImplCapturableContent.html b/docs/windows_docs/crabgrab/platform/windows/struct.ImplCapturableContent.html new file mode 100644 index 00000000..fba1d762 --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/struct.ImplCapturableContent.html @@ -0,0 +1,14 @@ +ImplCapturableContent in crabgrab::platform::windows - Rust +
pub struct ImplCapturableContent { /* private fields */ }

Implementations§

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/struct.ImplCapturableDisplay.html b/docs/windows_docs/crabgrab/platform/windows/struct.ImplCapturableDisplay.html new file mode 100644 index 00000000..5df09882 --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/struct.ImplCapturableDisplay.html @@ -0,0 +1,13 @@ +ImplCapturableDisplay in crabgrab::platform::windows - Rust +
pub struct ImplCapturableDisplay(/* private fields */);

Implementations§

source§

impl WindowsCapturableDisplay

source

pub fn from_impl(monitor: (HMONITOR, RECT)) -> Self

source

pub fn rect(&self) -> Rect

Trait Implementations§

source§

impl Clone for WindowsCapturableDisplay

source§

fn clone(&self) -> WindowsCapturableDisplay

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for WindowsCapturableDisplay

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/struct.ImplCapturableWindow.html b/docs/windows_docs/crabgrab/platform/windows/struct.ImplCapturableWindow.html new file mode 100644 index 00000000..5bd4e6f3 --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/struct.ImplCapturableWindow.html @@ -0,0 +1,13 @@ +ImplCapturableWindow in crabgrab::platform::windows - Rust +
pub struct ImplCapturableWindow(/* private fields */);

Implementations§

source§

impl WindowsCapturableWindow

source

pub fn from_impl(hwnd: HWND) -> Self

source

pub fn title(&self) -> String

source

pub fn rect(&self) -> Rect

source

pub fn application(&self) -> WindowsCapturableApplication

Trait Implementations§

source§

impl Clone for WindowsCapturableWindow

source§

fn clone(&self) -> WindowsCapturableWindow

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for WindowsCapturableWindow

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/struct.ImplCaptureConfig.html b/docs/windows_docs/crabgrab/platform/windows/struct.ImplCaptureConfig.html new file mode 100644 index 00000000..86bf4735 --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/struct.ImplCaptureConfig.html @@ -0,0 +1,13 @@ +ImplCaptureConfig in crabgrab::platform::windows - Rust +
pub struct ImplCaptureConfig { /* private fields */ }

Implementations§

Trait Implementations§

source§

impl Clone for WindowsCaptureConfig

source§

fn clone(&self) -> WindowsCaptureConfig

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for WindowsCaptureConfig

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/struct.ImplCaptureStream.html b/docs/windows_docs/crabgrab/platform/windows/struct.ImplCaptureStream.html new file mode 100644 index 00000000..26390988 --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/struct.ImplCaptureStream.html @@ -0,0 +1,15 @@ +ImplCaptureStream in crabgrab::platform::windows - Rust +
pub struct ImplCaptureStream { /* private fields */ }

Implementations§

source§

impl WindowsCaptureStream

source

pub fn supported_pixel_formats() -> &'static [CapturePixelFormat]

source

pub fn check_access(borderless: bool) -> bool

source

pub async fn request_access(borderless: bool) -> bool

source

pub fn new( + config: CaptureConfig, + callback: Box<impl FnMut(Result<StreamEvent, StreamError>) + Send + 'static> +) -> Result<Self, StreamCreateError>

source

pub fn stop(&self) -> Result<(), StreamStopError>

Trait Implementations§

source§

impl Drop for WindowsCaptureStream

source§

fn drop(&mut self)

Executes the destructor for this type. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/platform/windows/struct.ImplVideoFrame.html b/docs/windows_docs/crabgrab/platform/windows/struct.ImplVideoFrame.html new file mode 100644 index 00000000..92d0e751 --- /dev/null +++ b/docs/windows_docs/crabgrab/platform/windows/struct.ImplVideoFrame.html @@ -0,0 +1,12 @@ +ImplVideoFrame in crabgrab::platform::windows - Rust +
pub struct ImplVideoFrame { /* private fields */ }

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/prelude/index.html b/docs/windows_docs/crabgrab/prelude/index.html new file mode 100644 index 00000000..bb235c1f --- /dev/null +++ b/docs/windows_docs/crabgrab/prelude/index.html @@ -0,0 +1,2 @@ +crabgrab::prelude - Rust +

Module crabgrab::prelude

source ·

Re-exports§

\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/prelude/sidebar-items.js b/docs/windows_docs/crabgrab/prelude/sidebar-items.js new file mode 100644 index 00000000..5244ce01 --- /dev/null +++ b/docs/windows_docs/crabgrab/prelude/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {}; \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/sidebar-items.js b/docs/windows_docs/crabgrab/sidebar-items.js new file mode 100644 index 00000000..47415dbe --- /dev/null +++ b/docs/windows_docs/crabgrab/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"mod":["capturable_content","capture_stream","feature","frame","platform","prelude","util"]}; \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/util/index.html b/docs/windows_docs/crabgrab/util/index.html new file mode 100644 index 00000000..2d376ff0 --- /dev/null +++ b/docs/windows_docs/crabgrab/util/index.html @@ -0,0 +1,2 @@ +crabgrab::util - Rust +

Module crabgrab::util

source ·

Structs§

  • Represents a 2d point
  • Represents an axis-aligned rectangle
  • Represents a 2d size
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/util/sidebar-items.js b/docs/windows_docs/crabgrab/util/sidebar-items.js new file mode 100644 index 00000000..c1c0499d --- /dev/null +++ b/docs/windows_docs/crabgrab/util/sidebar-items.js @@ -0,0 +1 @@ +window.SIDEBAR_ITEMS = {"struct":["Point","Rect","Size"]}; \ No newline at end of file diff --git a/docs/windows_docs/crabgrab/util/struct.Point.html b/docs/windows_docs/crabgrab/util/struct.Point.html new file mode 100644 index 00000000..82506bf6 --- /dev/null +++ b/docs/windows_docs/crabgrab/util/struct.Point.html @@ -0,0 +1,20 @@ +Point in crabgrab::util - Rust +

Struct crabgrab::util::Point

source ·
pub struct Point {
+    pub x: f64,
+    pub y: f64,
+}
Expand description

Represents a 2d point

+

Fields§

§x: f64§y: f64

Implementations§

source§

impl Point

source

pub const ZERO: Point = _

The point at (0, 0)

+
source

pub fn scaled(&self, scale: f64) -> Self

Scale the point uniformly by some value

+
source

pub fn scaled_2d(&self, scale: (f64, f64)) -> Self

Scale the point non-uniformly in x and y

+

Trait Implementations§

source§

impl Clone for Point

source§

fn clone(&self) -> Point

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Point

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Copy for Point

Auto Trait Implementations§

§

impl RefUnwindSafe for Point

§

impl Send for Point

§

impl Sync for Point

§

impl Unpin for Point

§

impl UnwindSafe for Point

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/util/struct.Rect.html b/docs/windows_docs/crabgrab/util/struct.Rect.html new file mode 100644 index 00000000..99bf60a7 --- /dev/null +++ b/docs/windows_docs/crabgrab/util/struct.Rect.html @@ -0,0 +1,19 @@ +Rect in crabgrab::util - Rust +

Struct crabgrab::util::Rect

source ·
pub struct Rect {
+    pub origin: Point,
+    pub size: Size,
+}
Expand description

Represents an axis-aligned rectangle

+

Fields§

§origin: Point§size: Size

Implementations§

source§

impl Rect

source

pub fn scaled(&self, scale: f64) -> Self

Scale the rectangle uniformly

+
source

pub fn scaled_2d(&self, scale: (f64, f64)) -> Self

Scale the rectangle non-uniformly in x and y

+

Trait Implementations§

source§

impl Clone for Rect

source§

fn clone(&self) -> Rect

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Rect

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Copy for Rect

Auto Trait Implementations§

§

impl RefUnwindSafe for Rect

§

impl Send for Rect

§

impl Sync for Rect

§

impl Unpin for Rect

§

impl UnwindSafe for Rect

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crabgrab/util/struct.Size.html b/docs/windows_docs/crabgrab/util/struct.Size.html new file mode 100644 index 00000000..566c69a4 --- /dev/null +++ b/docs/windows_docs/crabgrab/util/struct.Size.html @@ -0,0 +1,19 @@ +Size in crabgrab::util - Rust +

Struct crabgrab::util::Size

source ·
pub struct Size {
+    pub width: f64,
+    pub height: f64,
+}
Expand description

Represents a 2d size

+

Fields§

§width: f64§height: f64

Implementations§

source§

impl Size

source

pub fn scaled(&self, scale: f64) -> Self

scale the size uniformly by some value

+
source

pub fn scaled_2d(&self, scale: (f64, f64)) -> Self

scale the size non-uniformly in x and y

+

Trait Implementations§

source§

impl Clone for Size

source§

fn clone(&self) -> Size

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Size

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Copy for Size

Auto Trait Implementations§

§

impl RefUnwindSafe for Size

§

impl Send for Size

§

impl Sync for Size

§

impl Unpin for Size

§

impl UnwindSafe for Size

Blanket Implementations§

source§

impl<T> Any for T
where + T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where + T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where + T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

+
source§

impl<T, U> Into<U> for T
where + U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

+

That is, this conversion is whatever the implementation of +From<T> for U chooses to do.

+
source§

impl<T> ToOwned for T
where + T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where + U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where + U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
\ No newline at end of file diff --git a/docs/windows_docs/crates.js b/docs/windows_docs/crates.js new file mode 100644 index 00000000..08fe8925 --- /dev/null +++ b/docs/windows_docs/crates.js @@ -0,0 +1 @@ +window.ALL_CRATES = ["crabgrab"]; \ No newline at end of file diff --git a/docs/windows_docs/help.html b/docs/windows_docs/help.html new file mode 100644 index 00000000..1eb6af3d --- /dev/null +++ b/docs/windows_docs/help.html @@ -0,0 +1,2 @@ +Help +

Rustdoc help

Back
\ No newline at end of file diff --git a/docs/windows_docs/search-index.js b/docs/windows_docs/search-index.js new file mode 100644 index 00000000..6b9fa613 --- /dev/null +++ b/docs/windows_docs/search-index.js @@ -0,0 +1,5 @@ +var searchIndex = new Map(JSON.parse('[\ +["crabgrab",{"doc":"A cross-platform screen/window/audio capture library","t":"CCCCCCCFFGFFFFFFPNNNNNNNNNNNNNNNNNNNNNNNNNNNNONONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNONNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOPPPFPFGGFPPPPPPPGGGGPPPPNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNCCPKKGNNNNNNNNNMMNNNNNNNPKKGNNNNNNNNNMMMNNNNNNNGGGFFGPPPPPPPPPPPPFNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNECPFFFFFFFFGFNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNFFFTNNNNNNNNNNNNNNNNNNONNNONNNNNNONNNNNNNNNNNNOOO","n":["capturable_content","capture_stream","feature","frame","platform","prelude","util","CapturableApplication","CapturableContent","CapturableContentError","CapturableContentFilter","CapturableDisplay","CapturableDisplayIterator","CapturableWindow","CapturableWindowFilter","CapturableWindowIterator","Other","application","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","cause","clone","clone","clone","clone_into","clone_into","clone_into","default","description","desktop_windows","displays","displays","fmt","fmt","fmt","fmt","from","from","from","from","from","from","from","from","from","identifier","into","into","into","into","into","into","into","into","into","into_iter","into_iter","is_empty","len","new","next","next","onscreen_only","rect","rect","size_hint","size_hint","source","title","to_owned","to_owned","to_owned","to_string","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","windows","windows","AlreadyStopped","Argb2101010","Audio","AudioCaptureConfig","Bgra8888","CaptureConfig","CaptureConfigError","CapturePixelFormat","CaptureStream","End","F420","Idle","InvalidBufferCount","Other","Other","Other","StreamCreateError","StreamError","StreamEvent","StreamStopError","UnsupportedPixelFormat","UnsupportedPixelFormat","V420","Video","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","cause","cause","clone","clone","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","description","description","eq","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","fmt","from","from","from","from","from","from","from","from","from","get_dx11_device","get_dxgi_adapter","get_dxgi_device","into","into","into","into","into","into","into","into","into","new","new","request_access","set_output_size","source","source","stop","supported_pixel_formats","test_access","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","to_string","to_string","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","with_buffer_count","with_display","with_show_cursor","with_window","dx11","dxgi","Other","WindowsDx11CaptureStream","WindowsDx11VideoFrame","WindowsDx11VideoFrameError","borrow","borrow_mut","cause","clone","clone_into","description","fmt","fmt","from","get_dx11_device","get_dx11_surface","into","source","to_owned","to_string","try_from","try_into","type_id","Other","WindowsDxgiCaptureStream","WindowsDxgiVideoFrame","WindowsDxgiVideoFrameError","borrow","borrow_mut","cause","clone","clone_into","description","fmt","fmt","from","get_dxgi_adapter","get_dxgi_device","get_dxgi_surface","into","source","to_owned","to_string","try_from","try_into","type_id","AudioBufferError","AudioChannelCount","AudioChannelData","AudioChannelDataSamples","AudioFrame","AudioSampleRate","F32","Hz16000","Hz24000","Hz48000","Hz8000","I16","I32","InvalidChannel","Mono","Other","Stereo","UnsupportedFormat","VideoFrame","audio_channel_buffer","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","capture_time","channel_count","clone","clone","clone_into","clone_into","dpi","duration","fmt","fmt","fmt","fmt","frame_id","frame_id","from","from","from","from","from","from","from","get_dx11_surface","get_dxgi_surface","into","into","into","into","into","into","into","origin_time","origin_time","sample_rate","size","to_owned","to_owned","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","type_id","platform_impl","windows","Bgra8888","ImplAudioCaptureConfig","ImplAudioFrame","ImplCapturableApplication","ImplCapturableContent","ImplCapturableDisplay","ImplCapturableWindow","ImplCaptureConfig","ImplCaptureStream","ImplPixelFormat","ImplVideoFrame","application","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","borrow_mut","check_access","clone","clone","clone","clone","clone","clone","clone_into","clone_into","clone_into","clone_into","clone_into","clone_into","drop","eq","fmt","fmt","fmt","fmt","fmt","fmt","from","from","from","from","from","from","from","from","from","from","from_impl","from_impl","from_impl","identifier","into","into","into","into","into","into","into","into","into","into","new","new","new","new","rect","rect","request_access","stop","supported_pixel_formats","title","to_owned","to_owned","to_owned","to_owned","to_owned","to_owned","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_from","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","try_into","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","type_id","Point","Rect","Size","ZERO","borrow","borrow","borrow","borrow_mut","borrow_mut","borrow_mut","clone","clone","clone","clone_into","clone_into","clone_into","fmt","fmt","fmt","from","from","from","height","into","into","into","origin","scaled","scaled","scaled","scaled_2d","scaled_2d","scaled_2d","size","to_owned","to_owned","to_owned","try_from","try_from","try_from","try_into","try_into","try_into","type_id","type_id","type_id","width","x","y"],"q":[[0,"crabgrab"],[7,"crabgrab::capturable_content"],[118,"crabgrab::capture_stream"],[256,"crabgrab::feature"],[258,"crabgrab::feature::dx11"],[280,"crabgrab::feature::dxgi"],[303,"crabgrab::frame"],[394,"crabgrab::platform"],[396,"crabgrab::platform::windows"],[519,"crabgrab::util"],[568,"core::error"],[569,"core::option"],[570,"core::fmt"],[571,"core::fmt"],[572,"core::result"],[573,"core::any"],[574,"windows::Win32::Graphics::Direct3D11"],[575,"windows::Win32::Graphics::Dxgi"],[576,"windows::Win32::Graphics::Dxgi"],[577,"core::marker"],[578,"windows::Graphics::DirectX::Direct3D11"],[579,"windows::Graphics::DirectX"],[580,"windows::Win32::Graphics::Dxgi"],[581,"core::time"],[582,"windows::Win32::Foundation"],[583,"windows::Win32::Graphics::Gdi"],[584,"windows::Win32::Foundation"]],"d":["","","","","","","","","","Represents an error that occured when enumerating …","Selects the kind of capturable content to enumerate","Represents a capturable display","An iterator over capturable displays","Represents a capturable application window","Selects the kind of windows to enumerate for capture","An iterator over capturable windows","","Gets the application that owns this window","","","","","","","","","","","","","","","","","","","","","","","","","","","","Desktop windows are elements of the desktop environment, …","Get an iterator over the capturable displays","Whether to enumerate capturable displays","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Gets the “identifier” of the application","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","","","Whether this filter allows any capturable content","","Requests capturable content from the OS","","","Whether to restrict to onscreen windows","Gets the virtual screen rectangle of the window","Gets the virtual screen rectangle of this display","","","","Gets the title of the window","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Get an iterator over the capturable windows","What kind of capturable windows, if Some, to enumerate","The stream was already stopped","One plane, 4 channels, 10 bits per color channel, two bits …","This event is produced when the stream receives a new …","Configuration settings for audio streams","One plane, 4 channels, 8 bits per channel: {b: u8, g: u8, …","Configuration settings for a capture stream","Represents an error creating the capture config","The pixel format of returned video frames","Represents an active capture stream","This event is produced once at the end of the stream","Two planes:","This event is produced when the stream goes idle - IE when …","The buffer count is out of the valid range for the …","","","","This represents an error when creating a capture stream","This represents an error during a stream, for example a …","Represents an event in a capture stream","This represents an error while stopping a stream","The supplied pixel format is unsupported by the …","The pixel format is unsupported by the implementation","Two planes:","This event is produced when the stream receives a new …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Start a new capture stream with the given stream callback","Creates a new audio capture config with default settings:","Prompt the user for permission to capture content","Configure the output texture size - by default, this will …","","","Stop the capture","Gets the implementation’s supported pixel formats","Test whether the calling application has permission to …","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Configure the buffer count - the number of frames in the …","Create a capture configuration for a given capturable …","Configure whether the cursor is visible in the capture","Create a capture configuration for a given capturable …","","","","","","","","","","","","","","","Returns the argument unchanged.","Get the underlying D3D11 device used for frame capture","Get the DX11 surface representing the video frame’s …","Calls U::from(self).","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Get the dxgi adapter used by the capture stream for frame …","Get the dxgi device used by the capture stream for frame …","Get the surface texture for this video frame","Calls U::from(self).","","","","","","","Represents an error getting the data for an audio channel","The number of audio channels to capture","Represents audio channel data in an audio frame","","A frame of captured audio","The rate to capture audio samples","","","","","","","","","","","","","A frame of captured video","Get the data buffer for the captured audio channel","","","","","","","","","","","","","","","Get the Instant that this frame was delivered to the …","Get the channel count of the captured audio","","","","","Get the dpi of the contents of the frame (accounting for …","Get the duration of this audio frames","","","","","Get the sequence id of this frame (monotonically …","Get the sequence id of this video frame (monotonically …","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Get the time since the start of the stream that this audio …","Get the time since the start of the stream that this frame …","Get the sample rate of the captured audio","Get the raw size of the frame","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","","","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Represents a 2d point","Represents an axis-aligned rectangle","Represents a 2d size","The point at (0, 0)","","","","","","","","","","","","","","","","Returns the argument unchanged.","Returns the argument unchanged.","Returns the argument unchanged.","","Calls U::from(self).","Calls U::from(self).","Calls U::from(self).","","scale the size uniformly by some value","Scale the point uniformly by some value","Scale the rectangle uniformly","scale the size non-uniformly in x and y","Scale the point non-uniformly in x and y","Scale the rectangle non-uniformly in x and y","","","","","","","","","","","","","","","",""],"i":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,1,8,15,10,19,11,2,3,1,6,8,15,10,19,11,2,3,1,6,3,3,1,6,3,1,6,8,3,8,10,15,3,3,1,6,8,15,10,19,11,2,3,1,6,2,8,15,10,19,11,2,3,1,6,19,11,15,11,10,19,11,8,1,6,19,11,3,1,3,1,6,3,8,15,10,19,11,2,3,1,6,8,15,10,19,11,2,3,1,6,8,15,10,19,11,2,3,1,6,10,15,30,26,29,0,26,0,0,0,0,29,26,29,28,23,24,30,0,0,0,0,24,28,26,29,31,29,23,24,30,25,26,27,28,31,29,23,24,30,25,26,27,28,23,24,23,24,25,26,27,28,23,24,25,26,27,28,23,24,26,29,23,23,24,24,30,25,26,27,28,31,29,23,24,30,25,26,27,28,31,31,31,31,29,23,24,30,25,26,27,28,31,25,31,27,23,24,31,31,31,23,24,25,26,27,28,23,24,31,29,23,24,30,25,26,27,28,31,29,23,24,30,25,26,27,28,31,29,23,24,30,25,26,27,28,27,27,27,27,0,0,39,0,0,0,39,39,39,39,39,39,39,39,39,40,41,39,39,39,39,39,39,39,44,0,0,0,44,44,44,44,44,44,44,44,44,45,45,46,44,44,44,44,44,44,44,0,0,0,0,0,0,49,54,54,54,54,49,49,50,53,50,53,50,0,48,49,72,50,48,51,54,53,49,72,50,48,51,54,53,51,48,54,53,54,53,51,48,48,51,54,53,48,51,49,72,50,48,51,54,53,51,51,49,72,50,48,51,54,53,48,51,48,51,54,53,49,72,50,48,51,54,53,49,72,50,48,51,54,53,49,72,50,48,51,54,53,0,0,60,0,0,0,0,0,0,0,0,0,0,58,64,70,73,74,60,61,62,58,63,59,64,70,73,74,60,61,62,58,63,59,64,60,61,62,58,63,59,60,61,62,58,63,59,64,60,60,61,62,58,63,59,64,70,73,74,60,61,62,58,63,59,58,63,59,59,64,70,73,74,60,61,62,58,63,59,64,70,61,62,58,63,64,64,64,58,60,61,62,58,63,59,64,70,73,74,60,61,62,58,63,59,64,70,73,74,60,61,62,58,63,59,64,70,73,74,60,61,62,58,63,59,0,0,0,71,37,71,20,37,71,20,37,71,20,37,71,20,37,71,20,37,71,20,37,37,71,20,20,37,71,20,37,71,20,20,37,71,20,37,71,20,37,71,20,37,71,20,37,71,71],"f":"`````````````````{bd}{ce{}{}}00000000000000000{f{{j{h}}}}{ff}{bb}{ll}{{ce}n{}{}}00{{}A`}{fAb}`{AdAf}`{{fAh}Aj}0{{bAh}Aj}{{lAh}Aj}{cc{}}00000000{dAl}==========={AnB`}{AfBb}{An{{Bd{Adf}}}}{Bf{{j{c}}}{}}{Af{{j{c}}}{}}`{bBh}{lBh}{Bf{{Bj{Bb{j{Bb}}}}}}{Af{{Bj{Bb{j{Bb}}}}}}{f{{j{h}}}}{bAl}{ce{}{}}00{cAl{}}{c{{Bd{e}}}{}{}}00000000000000000{cBl{}}00000000{AdBf}`````````````````````````444444444444444444{Bn{{j{h}}}}{C`{{j{h}}}}{BnBn}{C`C`}{CbCb}{CdCd}{CfCf}{ChCh}{{ce}n{}{}}00000{BnAb}{C`Ab}{{CdCd}B`}{{CjAh}Aj}{{BnAh}Aj}0{{C`Ah}Aj}0{{ClAh}Aj}{{CbAh}Aj}{{CdAh}Aj}{{CfAh}Aj}{{ChAh}Aj}{cc{}}00000000{CnD`}{CnDb}{CnDd}{ce{}{}}00000000{{Cfc}{{Bd{CnC`}}}{{Df{{Bd{CjBn}}}}Dh}}{{}Cb}{B`B`}{{CfDj}Cf}{Bn{{j{h}}}}{C`{{j{h}}}}{Cn{{Bd{nCl}}}}{{}{{Dl{Cd}}}}5888888{cAl{}}0{c{{Bd{e}}}{}{}}00000000000000000{cBl{}}00000000{{CfBb}Cf}{{lCd}Cf}{{CfB`}Cf}{{bCd}{{Bd{CfCh}}}}``````??{Dn{{j{h}}}}{DnDn}{{ce}n{}{}}{DnAb}{{DnAh}Aj}0{cc{}}{E`D`}{Eb{{Bd{{Bj{EdEf}}Dn}}}}{ce{}{}}80?>>=````00{Eh{{j{h}}}}{EhEh}8{EhAb}{{EhAh}Aj}07{EjDb}{EjDd}{El{{Bd{{Bj{EnEf}}Eh}}}}767{cAl{}}{c{{Bd{e}}}{}{}}0{cBl{}}```````````````````{{F`Bb}{{Bd{FbFd}}}};;;;;;;;;;;;;;{FfFh}{F`Fj}{FlFl}{FjFj}{{ce}n{}{}}0{FfFn}{F`G`}{{F`Ah}Aj}{{FfAh}Aj}{{FlAh}Aj}{{FjAh}Aj}{F`Gb}{FfGb}{cc{}}000000{Ff{{Bd{{Bj{EdEf}}Dn}}}}{Ff{{Bd{{Bj{EnEf}}Eh}}}}{ce{}{}}000000:{FfG`}{F`Fl}{FfDj}33{c{{Bd{e}}}{}{}}0000000000000{cBl{}}000000`````````````{GdGf}66666666666666666666{B`B`}{GhGh}{GjGj}{GlGl}{GdGd}{GnGn}{GfGf}{{ce}n{}{}}00000{H`n}{{GhGh}B`}{{GhAh}Aj}{{GjAh}Aj}{{GlAh}Aj}{{GdAh}Aj}{{GnAh}Aj}{{GfAh}Aj}{cc{}}000000000{HbGd}{{{Bj{HdHf}}}Gn}{HhGf}{GfAl}{ce{}{}}000000000{{Cf{Hj{c}}}{{Bd{H`C`}}}{{Df{{Bd{CjBn}}}}Dh}}{An{{Bd{Hlf}}}}{{}Gj}{{}Gl}{GdBh}{GnBh}{B`B`}{H`{{Bd{nCl}}}}{{}{{Dl{Cd}}}}{GdAl}::::::{c{{Bd{e}}}{}{}}0000000000000000000{cBl{}}000000000````<<<<<<{DjDj}{HnHn}{BhBh}{{ce}n{}{}}00{{DjAh}Aj}{{HnAh}Aj}{{BhAh}Aj}{cc{}}00`{ce{}{}}00`{{DjFn}Dj}{{HnFn}Hn}{{BhFn}Bh}{{Dj{Bj{FnFn}}}Dj}{{Hn{Bj{FnFn}}}Hn}{{Bh{Bj{FnFn}}}Bh}`666{c{{Bd{e}}}{}{}}00000{cBl{}}00```","c":[],"p":[[5,"CapturableWindow",7],[5,"CapturableApplication",7],[6,"CapturableContentError",7],[10,"Error",568],[6,"Option",569],[5,"CapturableDisplay",7],[1,"unit"],[5,"CapturableWindowFilter",7],[1,"str"],[5,"CapturableContent",7],[5,"CapturableDisplayIterator",7],[5,"Formatter",570],[8,"Result",570],[5,"String",571],[5,"CapturableContentFilter",7],[1,"bool"],[1,"usize"],[6,"Result",572],[5,"CapturableWindowIterator",7],[5,"Rect",519],[1,"tuple"],[5,"TypeId",573],[6,"StreamError",118],[6,"StreamCreateError",118],[5,"AudioCaptureConfig",118],[6,"CapturePixelFormat",118],[5,"CaptureConfig",118],[6,"CaptureConfigError",118],[6,"StreamEvent",118],[6,"StreamStopError",118],[5,"CaptureStream",118],[5,"ID3D11Device",574],[5,"IDXGIAdapter",575],[5,"IDXGIDevice",575],[10,"FnMut",576],[10,"Send",577],[5,"Size",519],[1,"slice"],[6,"WindowsDx11VideoFrameError",258],[10,"WindowsDx11CaptureStream",258],[10,"WindowsDx11VideoFrame",258],[5,"IDirect3DSurface",578],[5,"DirectXPixelFormat",579],[6,"WindowsDxgiVideoFrameError",280],[10,"WindowsDxgiCaptureStream",280],[10,"WindowsDxgiVideoFrame",280],[5,"IDXGISurface",575],[5,"AudioFrame",303],[6,"AudioChannelData",303],[6,"AudioBufferError",303],[5,"VideoFrame",303],[5,"Instant",580],[6,"AudioChannelCount",303],[6,"AudioSampleRate",303],[1,"f64"],[5,"Duration",581],[1,"u64"],[5,"ImplCapturableWindow",396],[5,"ImplCapturableApplication",396],[6,"ImplPixelFormat",396],[5,"ImplAudioCaptureConfig",396],[5,"ImplCaptureConfig",396],[5,"ImplCapturableDisplay",396],[5,"ImplCaptureStream",396],[5,"HWND",582],[5,"HMONITOR",583],[5,"RECT",582],[5,"HANDLE",582],[5,"Box",584],[5,"ImplCapturableContent",396],[5,"Point",519],[5,"AudioChannelDataSamples",303],[5,"ImplVideoFrame",396],[5,"ImplAudioFrame",396]],"b":[[48,"impl-Display-for-CapturableContentError"],[49,"impl-Debug-for-CapturableContentError"],[178,"impl-Display-for-StreamError"],[179,"impl-Debug-for-StreamError"],[180,"impl-Display-for-StreamCreateError"],[181,"impl-Debug-for-StreamCreateError"],[268,"impl-Display-for-WindowsDx11VideoFrameError"],[269,"impl-Debug-for-WindowsDx11VideoFrameError"],[290,"impl-Debug-for-WindowsDxgiVideoFrameError"],[291,"impl-Display-for-WindowsDxgiVideoFrameError"]]}]\ +]')); +if (typeof exports !== 'undefined') exports.searchIndex = searchIndex; +else if (window.initSearch) window.initSearch(searchIndex); diff --git a/docs/windows_docs/settings.html b/docs/windows_docs/settings.html new file mode 100644 index 00000000..70a98f6a --- /dev/null +++ b/docs/windows_docs/settings.html @@ -0,0 +1,2 @@ +Settings +

Rustdoc settings

Back
\ No newline at end of file diff --git a/docs/windows_docs/src-files.js b/docs/windows_docs/src-files.js new file mode 100644 index 00000000..d10d36e7 --- /dev/null +++ b/docs/windows_docs/src-files.js @@ -0,0 +1,4 @@ +var srcIndex = new Map(JSON.parse('[\ +["crabgrab",["",[["feature",[["dx11",[],["mod.rs"]],["dxgi",[],["mod.rs"]]],["mod.rs"]],["platform",[["windows",[],["audio_capture_stream.rs","capturable_content.rs","capture_stream.rs","frame.rs","mod.rs"]]],["mod.rs"]]],["capturable_content.rs","capture_stream.rs","frame.rs","lib.rs","prelude.rs","util.rs"]]]\ +]')); +createSrcSidebar(); diff --git a/docs/windows_docs/src/crabgrab/capturable_content.rs.html b/docs/windows_docs/src/crabgrab/capturable_content.rs.html new file mode 100644 index 00000000..2040b7c8 --- /dev/null +++ b/docs/windows_docs/src/crabgrab/capturable_content.rs.html @@ -0,0 +1,413 @@ +capturable_content.rs - source +
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
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+
use std::{error::Error, fmt::{Debug, Display}};
+
+use crate::{platform::platform_impl::{ImplCapturableApplication, ImplCapturableContent, ImplCapturableDisplay, ImplCapturableWindow}, util::Rect};
+
+/// Represents an error that occured when enumerating capturable content
+#[derive(Debug, Clone)]
+pub enum CapturableContentError {
+    Other(String)
+}
+
+impl Display for CapturableContentError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Other(message) => f.write_fmt(format_args!("CapturableContentError::Other(\"{}\")", message))
+        }
+    }
+}
+
+impl Error for CapturableContentError {
+    fn source(&self) -> Option<&(dyn Error + 'static)> {
+        None
+    }
+
+    fn description(&self) -> &str {
+        "description() is deprecated; use Display"
+    }
+
+    fn cause(&self) -> Option<&dyn Error> {
+        self.source()
+    }
+}
+
+/// Selects the kind of windows to enumerate for capture
+pub struct CapturableWindowFilter {
+    /// Desktop windows are elements of the desktop environment, E.G. the dock on macos or the start bar on windows.
+    pub desktop_windows: bool,
+    /// Whether to restrict to onscreen windows
+    pub onscreen_only: bool,
+}
+
+impl Default for CapturableWindowFilter {
+    fn default() -> Self {
+        Self { desktop_windows: false, onscreen_only: true }
+    }
+}
+
+/// Selects the kind of capturable content to enumerate
+pub struct CapturableContentFilter {
+    /// What kind of capturable windows, if Some, to enumerate
+    pub windows: Option<CapturableWindowFilter>,
+    /// Whether to enumerate capturable displays
+    pub displays: bool,
+}
+
+impl CapturableContentFilter {
+    /// Whether this filter allows any capturable content
+    pub fn is_empty(&self) -> bool {
+        !(
+            self.windows.is_some() ||
+            self.displays
+        )
+    }
+}
+
+pub struct CapturableContent {
+    impl_capturable_content: ImplCapturableContent
+}
+
+/// An iterator over capturable windows
+pub struct CapturableWindowIterator<'content> {
+    content: &'content CapturableContent,
+    i: usize
+}
+
+impl Iterator for CapturableWindowIterator<'_> {
+    type Item = CapturableWindow;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.i < self.content.impl_capturable_content.windows.len() {
+            let i = self.i;
+            self.i += 1;
+            Some(CapturableWindow { impl_capturable_window: ImplCapturableWindow::from_impl(self.content.impl_capturable_content.windows[i].clone()) })
+        } else {
+            None
+        }
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.i, Some(self.content.impl_capturable_content.windows.len()))
+    }
+}
+
+impl ExactSizeIterator for CapturableWindowIterator<'_> {
+}
+
+/// An iterator over capturable displays
+pub struct CapturableDisplayIterator<'content> {
+    content: &'content CapturableContent,
+    i: usize
+}
+
+impl Iterator for CapturableDisplayIterator<'_> {
+    type Item = CapturableDisplay;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.i < self.content.impl_capturable_content.displays.len() {
+            let i = self.i;
+            self.i += 1;
+            Some(CapturableDisplay { impl_capturable_display: ImplCapturableDisplay::from_impl(self.content.impl_capturable_content.displays[i].clone()) })
+        } else {
+            None
+        }
+    }
+    
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.i, Some(self.content.impl_capturable_content.displays.len()))
+    }
+}
+
+impl ExactSizeIterator for CapturableDisplayIterator<'_> {
+    fn len(&self) -> usize {
+        self.content.impl_capturable_content.displays.len()
+    }
+}
+
+impl CapturableContent {
+    /// Requests capturable content from the OS
+    /// 
+    /// Note that the returned capturable content may be stale - for example, a window enumerated in this capturable content
+    /// may have been closed before it is used to open a stream, and creating a stream for that window will result in an error.
+    pub async fn new(filter: CapturableContentFilter) -> Result<Self, CapturableContentError> {
+        Ok(Self {
+            impl_capturable_content: ImplCapturableContent::new(filter).await?
+        })
+    }
+
+    /// Get an iterator over the capturable windows
+    pub fn windows<'a>(&'a self) -> CapturableWindowIterator<'a> {
+        CapturableWindowIterator { content: self, i: 0 }
+    }
+
+    /// Get an iterator over the capturable displays
+    pub fn displays<'a>(&'a self) -> CapturableDisplayIterator<'a> {
+        CapturableDisplayIterator { content: self, i: 0 }
+    }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) enum Capturable {
+    Window(CapturableWindow),
+    Display(CapturableDisplay),
+}
+
+/// Represents a capturable application window
+#[derive(Debug, Clone)]
+pub struct CapturableWindow {
+    pub(crate) impl_capturable_window: ImplCapturableWindow
+}
+
+impl CapturableWindow {
+    /// Gets the title of the window
+    pub fn title(&self) -> String {
+        self.impl_capturable_window.title()
+    }
+
+    /// Gets the virtual screen rectangle of the window
+    pub fn rect(&self) -> Rect {
+        self.impl_capturable_window.rect()
+    }
+
+    /// Gets the application that owns this window
+    pub fn application(&self) -> CapturableApplication {
+        CapturableApplication {
+            impl_capturable_application: self.impl_capturable_window.application()
+        }
+    }
+}
+
+/// Represents a capturable display
+#[derive(Debug, Clone)]
+pub struct CapturableDisplay {
+    pub(crate) impl_capturable_display: ImplCapturableDisplay
+}
+
+impl CapturableDisplay {
+    /// Gets the virtual screen rectangle of this display
+    /// 
+    /// Note: Currently on windows, this is only evaluated at the time of display enumeration
+    pub fn rect(&self) -> Rect {
+        self.impl_capturable_display.rect()
+    }
+}
+
+pub struct CapturableApplication {
+    impl_capturable_application: ImplCapturableApplication
+}
+
+impl CapturableApplication {
+    /// Gets the "identifier" of the application
+    /// 
+    /// On Macos, this is the application bundle, and on windows, this is the application file name
+    pub fn identifier(&self) -> String {
+        self.impl_capturable_application.identifier()
+    }
+}
+
\ No newline at end of file diff --git a/docs/windows_docs/src/crabgrab/capture_stream.rs.html b/docs/windows_docs/src/crabgrab/capture_stream.rs.html new file mode 100644 index 00000000..aa5a14cc --- /dev/null +++ b/docs/windows_docs/src/crabgrab/capture_stream.rs.html @@ -0,0 +1,533 @@ +capture_stream.rs - source +
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
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+
use std::fmt::Debug;
+use std::{error::Error, fmt::Display};
+
+use crate::platform::platform_impl::{ImplAudioCaptureConfig, ImplCaptureConfig, ImplCaptureStream};
+use crate::capturable_content::Capturable;
+use crate::prelude::{AudioChannelCount, AudioFrame, AudioSampleRate, CapturableDisplay, CapturableWindow, VideoFrame};
+use crate::util::{Point, Rect, Size};
+
+/// Represents an event in a capture stream
+#[derive(Debug)]
+pub enum StreamEvent {
+    /// This event is produced when the stream receives a new audio packet
+    Audio(AudioFrame),
+    /// This event is produced when the stream receives a new video frame
+    Video(VideoFrame),
+    /// This event is produced when the stream goes idle - IE when no new frames are expected for some time, like when a window minimizes
+    Idle,
+    /// This event is produced once at the end of the stream
+    End,
+}
+
+/// This represents an error during a stream, for example a failure to retreive a video or audio frame
+#[derive(Debug, Clone)]
+pub enum StreamError {
+    Other(String),
+}
+
+impl Display for StreamError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Other(message) => f.write_fmt(format_args!("StreamError::Other(\"{}\")", message))
+        }
+    }
+}
+
+impl Error for StreamError {
+    fn source(&self) -> Option<&(dyn Error + 'static)> {
+        None
+    }
+
+    fn description(&self) -> &str {
+        "description() is deprecated; use Display"
+    }
+
+    fn cause(&self) -> Option<&dyn Error> {
+        self.source()
+    }
+}
+
+/// This represents an error when creating a capture stream
+#[derive(Debug, Clone)]
+pub enum StreamCreateError {
+    Other(String),
+    /// The supplied pixel format is unsupported by the implementation
+    UnsupportedPixelFormat,
+    //GpuLost,
+}
+
+unsafe impl Send for StreamCreateError {}
+unsafe impl Sync for StreamCreateError {}
+
+/// This represents an error while stopping a stream
+#[derive(Debug)]
+pub enum StreamStopError {
+    Other(String),
+    /// The stream was already stopped
+    AlreadyStopped,
+    //GpuLost,
+}
+
+unsafe impl Send for StreamStopError {}
+unsafe impl Sync for StreamStopError {}
+
+impl Display for StreamCreateError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Other(message) => f.write_fmt(format_args!("StreamCreateError::Other(\"{}\")", message)),
+            Self::UnsupportedPixelFormat => f.write_fmt(format_args!("SteamCreateError::UnsupportedPixelFormat")),
+        }
+    }
+}
+
+impl Error for StreamCreateError {
+    fn source(&self) -> Option<&(dyn Error + 'static)> {
+        None
+    }
+
+    fn description(&self) -> &str {
+        "description() is deprecated; use Display"
+    }
+
+    fn cause(&self) -> Option<&dyn Error> {
+        self.source()
+    }
+}
+
+/// Configuration settings for audio streams
+#[derive(Clone, Debug)]
+pub struct AudioCaptureConfig {
+    pub(crate)  sample_rate: AudioSampleRate, 
+    pub(crate)  channel_count: AudioChannelCount,
+    pub(crate)  impl_capture_audio_config: ImplAudioCaptureConfig,
+}
+
+impl AudioCaptureConfig {
+    /// Creates a new audio capture config with default settings:
+    /// * 24000 hz
+    /// * Mono
+    pub fn new() -> Self {
+        Self {
+            sample_rate: AudioSampleRate::Hz24000,
+            channel_count: AudioChannelCount::Mono,
+            impl_capture_audio_config: ImplAudioCaptureConfig::new()
+        }
+    }
+}
+
+/// The pixel format of returned video frames
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[non_exhaustive]
+pub enum CapturePixelFormat {
+    /// One plane, 4 channels, 8 bits per channel: {b: u8, g: u8, r: u8, a: u8}, full range: [0, 255]
+    Bgra8888,
+    /// One plane, 4 channels, 10 bits per color channel, two bits for alpha: {a: u2, r: u10, g: u10, b: u10}, rgb range: [0, 1023], alpha range: [0, 3]
+    Argb2101010,
+    /// Two planes:
+    /// * 1 channel, luminance, 8 bits per pixel, video range: [16, 240]
+    /// * 2 channels, chroma (cb, cr) 8 bits bits per channel per two pixels vertically, range: [0, 255]
+    V420,
+    /// Two planes:
+    /// * 1 channel, luminance, 8 bits per pixel, full range: [0, 255]
+    /// * 2 channels, chroma (cb, cr) 8 bits bits per channel per two pixels vertically, range: [0, 255]
+    F420,
+}
+
+/// Configuration settings for a capture stream
+#[derive(Clone, Debug)]
+pub struct CaptureConfig {
+    pub(crate) target: Capturable,
+    pub(crate) source_rect: Rect,
+    pub(crate) output_size: Size,
+    pub(crate) show_cursor: bool,
+    pub(crate) pixel_format: CapturePixelFormat,
+    pub(crate) capture_audio: Option<AudioCaptureConfig>,
+    pub(crate) impl_capture_config: ImplCaptureConfig,
+    pub(crate) buffer_count: usize,
+}
+
+/// Represents an error creating the capture config
+#[derive(Debug, Clone)]
+pub enum CaptureConfigError {
+    /// The pixel format is unsupported by the implementation
+    UnsupportedPixelFormat,
+    /// The buffer count is out of the valid range for the implementation
+    InvalidBufferCount,
+}
+
+impl CaptureConfig {
+    /// Create a capture configuration for a given capturable window
+    pub fn with_window(window: CapturableWindow, pixel_format: CapturePixelFormat) -> Result<CaptureConfig, CaptureConfigError> {
+        let rect = window.rect();
+        Ok(CaptureConfig {
+            target: Capturable::Window(window),
+            pixel_format,
+            source_rect: Rect {
+                origin: Point {
+                    x: 0.0,
+                    y: 0.0,
+                },
+                size: rect.size
+            },
+            output_size: rect.size,
+            show_cursor: false,
+            impl_capture_config: ImplCaptureConfig::new(),
+            capture_audio: None,
+            buffer_count: 3,
+        })
+    }
+
+    /// Create a capture configuration for a given capturable display
+    pub fn with_display(display: CapturableDisplay, pixel_format: CapturePixelFormat) -> CaptureConfig {
+        let rect = display.rect();
+        CaptureConfig {
+            target: Capturable::Display(display),
+            pixel_format,
+            source_rect: Rect {
+                origin: Point {
+                    x: 0.0,
+                    y: 0.0,
+                },
+                size: rect.size
+            },
+            output_size: rect.size,
+            show_cursor: false,
+            impl_capture_config: ImplCaptureConfig::new(),
+            capture_audio: None,
+            buffer_count: 3,
+        }
+    }
+
+    /// Configure the buffer count - the number of frames in the capture queue.
+    /// 
+    /// Higher numbers mean higher latency, but smoother performance
+    pub fn with_buffer_count(self, buffer_count: usize) -> Self {
+        Self {
+            buffer_count,
+            ..self
+        }
+    }
+
+    /// Configure whether the cursor is visible in the capture
+    pub fn with_show_cursor(self, show_cursor: bool) -> Self {
+        Self {
+            show_cursor,
+            ..self
+        }
+    }
+
+    /// Configure the output texture size - by default, this will match the captured content at the time of enumeration
+    pub fn set_output_size(self, output_size: Size) -> Self {
+        Self {
+            output_size,
+            ..self
+        }
+    }
+}
+
+/// Represents an active capture stream
+pub struct CaptureStream {
+    pub(crate) impl_capture_stream: ImplCaptureStream,
+}
+
+impl CaptureStream {
+    /// Test whether the calling application has permission to capture content
+    pub fn test_access(borderless: bool) -> bool {
+        ImplCaptureStream::check_access(borderless)
+    }
+
+    /// Prompt the user for permission to capture content
+    pub async fn request_access(borderless: bool) -> bool {
+        ImplCaptureStream::request_access(borderless).await
+    }
+
+    /// Gets the implementation's supported pixel formats
+    /// 
+    /// Note that the returned formats may not work for all capture modalities (eg, window vs display)
+    pub fn supported_pixel_formats() -> &'static [CapturePixelFormat] {
+        ImplCaptureStream::supported_pixel_formats()
+    }
+
+    /// Start a new capture stream with the given stream callback
+    pub fn new(config: CaptureConfig, callback: impl FnMut(Result<StreamEvent, StreamError>) + Send + 'static) -> Result<Self, StreamCreateError> {
+        let boxed_callback = Box::new(callback);
+        Ok(Self {
+            impl_capture_stream: ImplCaptureStream::new(config, boxed_callback)?
+        })
+    }
+
+    /// Stop the capture
+    pub fn stop(&mut self) -> Result<(), StreamStopError> {
+        self.impl_capture_stream.stop()
+    }
+}
+
+
+
\ No newline at end of file diff --git a/docs/windows_docs/src/crabgrab/feature/dx11/mod.rs.html b/docs/windows_docs/src/crabgrab/feature/dx11/mod.rs.html new file mode 100644 index 00000000..49048909 --- /dev/null +++ b/docs/windows_docs/src/crabgrab/feature/dx11/mod.rs.html @@ -0,0 +1,125 @@ +mod.rs - source +
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
+
#![cfg(target_os = "windows")]
+#![cfg(feature = "dx11")]
+
+use futures::lock::Mutex;
+use windows::{Graphics::DirectX::{Direct3D11::IDirect3DSurface, DirectXPixelFormat}, Win32::Graphics::Direct3D11::ID3D11Device};
+
+use std::error::Error;
+use std::fmt::Display;
+
+use crate::prelude::{CaptureStream, VideoFrame};
+
+#[derive(Debug, Clone)]
+pub enum WindowsDx11VideoFrameError {
+    Other(String),
+}
+
+impl Display for WindowsDx11VideoFrameError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Other(error) => f.write_fmt(format_args!("WindowsDx11VideoFrameError::Other(\"{}\")", error)),
+        }
+    }
+}
+
+impl Error for WindowsDx11VideoFrameError {
+    fn source(&self) -> Option<&(dyn Error + 'static)> {
+        None
+    }
+
+    fn description(&self) -> &str {
+        "description() is deprecated; use Display"
+    }
+
+    fn cause(&self) -> Option<&dyn Error> {
+        self.source()
+    }
+}
+
+pub trait WindowsDx11VideoFrame {
+    /// Get the DX11 surface representing the video frame's texture memory, as well as the pixel format
+    fn get_dx11_surface(&self) -> Result<(IDirect3DSurface, DirectXPixelFormat), WindowsDx11VideoFrameError>;
+}
+
+impl WindowsDx11VideoFrame for VideoFrame {
+    fn get_dx11_surface(&self) -> Result<(IDirect3DSurface, DirectXPixelFormat), WindowsDx11VideoFrameError> {
+        self.impl_video_frame.frame.Surface()
+            .map_err(|e| WindowsDx11VideoFrameError::Other(format!("Failed to get frame surface: {}", e.to_string())))
+            .map(|surface| (surface, self.impl_video_frame.pixel_format))
+    }
+}
+
+pub trait WindowsDx11CaptureStream {
+    /// Get the underlying D3D11 device used for frame capture
+    fn get_dx11_device(&self) -> ID3D11Device;
+}
+
+impl WindowsDx11CaptureStream for CaptureStream {
+    fn get_dx11_device(&self) -> ID3D11Device {
+        self.impl_capture_stream.d3d11_device.clone()
+    }
+}
+
\ No newline at end of file diff --git a/docs/windows_docs/src/crabgrab/feature/dxgi/mod.rs.html b/docs/windows_docs/src/crabgrab/feature/dxgi/mod.rs.html new file mode 100644 index 00000000..3dd11664 --- /dev/null +++ b/docs/windows_docs/src/crabgrab/feature/dxgi/mod.rs.html @@ -0,0 +1,153 @@ +mod.rs - source +
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
+
#![cfg(target_os = "windows")]
+#![cfg(feature = "dxgi")]
+
+use crate::prelude::{CaptureStream, VideoFrame};
+
+use std::error::Error;
+use std::fmt::Display;
+
+use windows::core::ComInterface;
+use windows::Graphics::DirectX::DirectXPixelFormat;
+use windows::Win32::System::WinRT::Direct3D11::IDirect3DDxgiInterfaceAccess;
+use windows::Win32::Graphics::Direct3D11::ID3D11Texture2D;
+
+#[derive(Debug, Clone)]
+pub enum WindowsDxgiVideoFrameError {
+    Other(String),
+}
+
+impl Display for WindowsDxgiVideoFrameError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Other(error) => f.write_fmt(format_args!("WindowsDxgiVideoFrameError::Other(\"{}\")", error)),
+        }
+    }
+}
+
+impl Error for WindowsDxgiVideoFrameError {
+    fn source(&self) -> Option<&(dyn Error + 'static)> {
+        None
+    }
+
+    fn description(&self) -> &str {
+        "description() is deprecated; use Display"
+    }
+
+    fn cause(&self) -> Option<&dyn Error> {
+        self.source()
+    }
+}
+
+pub trait WindowsDxgiVideoFrame {
+    /// Get the surface texture for this video frame
+    fn get_dxgi_surface(&self) -> Result<(windows::Win32::Graphics::Dxgi::IDXGISurface, DirectXPixelFormat), WindowsDxgiVideoFrameError>; 
+}
+
+impl WindowsDxgiVideoFrame for VideoFrame {
+    fn get_dxgi_surface(&self) -> Result<(windows::Win32::Graphics::Dxgi::IDXGISurface, DirectXPixelFormat), WindowsDxgiVideoFrameError> {
+        let d3d11_surface = self.impl_video_frame.frame.Surface()
+            .map_err(|e| WindowsDxgiVideoFrameError::Other(format!("Failed to get frame surface: {}", e.to_string())))?;
+        let interface_access: IDirect3DDxgiInterfaceAccess = d3d11_surface.cast()
+            .map_err(|e| WindowsDxgiVideoFrameError::Other(format!("Failed to cast d3d11 surface to dxgi interface access: {}", e.to_string())))?;
+        let d3d11_texture: ID3D11Texture2D = unsafe {
+            interface_access.GetInterface::<ID3D11Texture2D>()
+        }.map_err(|e| WindowsDxgiVideoFrameError::Other(format!("Failed to get ID3D11Texture2D interface from to IDirect3DSurface(IDirect3DDxgiInterfaceAccess): {}", e.to_string())))?;
+        d3d11_texture.cast().map_err(|e| WindowsDxgiVideoFrameError::Other(format!("Failed to cast ID3D11Texture2D to IDXGISurface: {}", e.to_string())))
+            .map(|texture| (texture, self.impl_video_frame.pixel_format))
+    }
+}
+
+pub trait WindowsDxgiCaptureStream {
+    /// Get the dxgi adapter used by the capture stream for frame generation
+    fn get_dxgi_adapter(&self) -> windows::Win32::Graphics::Dxgi::IDXGIAdapter;
+    /// Get the dxgi device used by the capture stream for frame generation
+    fn get_dxgi_device(&self) -> windows::Win32::Graphics::Dxgi::IDXGIDevice;
+}
+
+impl WindowsDxgiCaptureStream for CaptureStream {
+    fn get_dxgi_adapter(&self) -> windows::Win32::Graphics::Dxgi::IDXGIAdapter {
+        self.impl_capture_stream.dxgi_adapter.clone()
+    }
+
+    fn get_dxgi_device(&self) -> windows::Win32::Graphics::Dxgi::IDXGIDevice {
+        self.impl_capture_stream.dxgi_device.clone()
+    }
+}
+
\ No newline at end of file diff --git a/docs/windows_docs/src/crabgrab/feature/mod.rs.html b/docs/windows_docs/src/crabgrab/feature/mod.rs.html new file mode 100644 index 00000000..6b057219 --- /dev/null +++ b/docs/windows_docs/src/crabgrab/feature/mod.rs.html @@ -0,0 +1,34 @@ +mod.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+
#[cfg(feature = "metal")]
+#[cfg(target_os="macos")]
+pub mod metal;
+#[cfg(feature = "dxgi")]
+#[cfg(target_os="windows")]
+pub mod dxgi;
+#[cfg(feature = "dx11")]
+#[cfg(target_os="windows")]
+pub mod dx11;
+#[cfg(feature = "iosurface")]
+#[cfg(target_os="macos")]
+pub mod iosurface;
+#[cfg(feature = "bitmap")]
+pub mod bitmap;
+#[cfg(feature = "wgpu")]
+pub mod wgpu;
\ No newline at end of file diff --git a/docs/windows_docs/src/crabgrab/frame.rs.html b/docs/windows_docs/src/crabgrab/frame.rs.html new file mode 100644 index 00000000..bbcb9cb5 --- /dev/null +++ b/docs/windows_docs/src/crabgrab/frame.rs.html @@ -0,0 +1,323 @@ +frame.rs - source +
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
+
#![allow(unused)]
+use std::{marker::PhantomData, time::{Duration, Instant}, fmt::Debug};
+
+use crate::{platform::platform_impl::{ImplAudioFrame, ImplVideoFrame}, util::*};
+
+/// The rate to capture audio samples
+#[derive(Copy, Clone, Debug)]
+pub enum AudioSampleRate {
+    Hz8000,
+    Hz16000,
+    Hz24000,
+    Hz48000,
+}
+
+/// The number of audio channels to capture
+#[derive(Copy, Clone, Debug)]
+pub enum AudioChannelCount {
+    Mono,
+    Stereo
+}
+
+/// Represents audio channel data in an audio frame
+pub enum AudioChannelData<'data> {
+    F32(AudioChannelDataSamples<'data, f32>),
+    I32(AudioChannelDataSamples<'data, i32>),
+    I16(AudioChannelDataSamples<'data, i16>),
+}
+
+pub struct AudioChannelDataSamples<'data, T> {
+    pub(crate) data: *const u8,
+    pub(crate) stride: usize,
+    pub(crate) length: usize,
+    pub(crate) phantom_lifetime: PhantomData<&'data T>,
+}
+
+impl<T: Copy> AudioChannelDataSamples<'_, T> {
+    fn get(&self, i: usize) -> T {
+        let ptr = self.data.wrapping_add(self.stride * i);
+        unsafe { *(ptr as *const T) }
+    }
+
+    fn length(&self) -> usize {
+        self.length
+    }
+}
+
+/// Represents an error getting the data for an audio channel
+pub enum AudioBufferError {
+    UnsupportedFormat,
+    InvalidChannel,
+    Other(String)
+}
+
+pub(crate) trait AudioCaptureFrame {
+    fn sample_rate(&self) -> AudioSampleRate;
+    fn channel_count(&self) -> AudioChannelCount;
+    fn audio_channel_buffer(&mut self, channel: usize) -> Result<AudioChannelData<'_>, AudioBufferError>;
+    fn duration(&self) -> Duration;
+    fn origin_time(&self) -> Duration;
+    fn frame_id(&self) -> u64;
+}
+
+/// A frame of captured audio
+pub struct AudioFrame {
+    pub(crate) impl_audio_frame: ImplAudioFrame,
+}
+
+impl Debug for AudioFrame {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("AudioFrame").finish()
+    }
+}
+
+impl AudioFrame {
+    /// Get the sample rate of the captured audio
+    pub fn sample_rate(&self) -> AudioSampleRate {
+        self.impl_audio_frame.sample_rate()
+    }
+
+    /// Get the channel count of the captured audio
+    pub fn channel_count(&self) -> AudioChannelCount {
+        self.impl_audio_frame.channel_count()
+    }
+
+    /// Get the data buffer for the captured audio channel
+    pub fn audio_channel_buffer(&mut self, channel: usize) -> Result<AudioChannelData<'_>, AudioBufferError> {
+        self.impl_audio_frame.audio_channel_buffer(channel)
+    }
+
+    /// Get the duration of this audio frames
+    pub fn duration(&self) -> Duration {
+        self.impl_audio_frame.duration()
+    }
+
+    /// Get the time since the start of the stream that this audio frame begins at
+    pub fn origin_time(&self) -> Duration {
+        self.impl_audio_frame.duration()
+    }
+
+    /// Get the sequence id of this frame (monotonically increasing)
+    /// 
+    /// Note: This is separate from video frame ids
+    pub fn frame_id(&self) -> u64 {
+        self.impl_audio_frame.frame_id()
+    }
+}
+
+pub(crate) trait VideoCaptureFrame {
+    fn size(&self) -> Size;
+    fn dpi(&self) -> f64;
+    fn duration(&self) -> Duration;
+    fn origin_time(&self) -> Duration;
+    fn capture_time(&self) -> Instant;
+    fn frame_id(&self) -> u64;
+}
+
+/// A frame of captured video
+pub struct VideoFrame {
+    pub(crate) impl_video_frame: ImplVideoFrame,
+}
+
+unsafe impl Send for VideoFrame {}
+unsafe impl Sync for VideoFrame {}
+
+impl VideoFrame {
+    /// Get the sequence id of this video frame (monotonically increasing)
+    /// 
+    /// Note: This is separate from audio frame ids
+    pub fn frame_id(&self) -> u64 {
+        self.impl_video_frame.frame_id()
+    }
+
+    /// Get the Instant that this frame was delivered to the application
+    pub fn capture_time(&self) -> Instant {
+        self.impl_video_frame.capture_time()
+    }
+
+    /// Get the time since the start of the stream that this frame was generated
+    pub fn origin_time(&self) -> Duration {
+        self.impl_video_frame.origin_time()
+    }
+
+    /// Get the raw size of the frame
+    /// 
+    /// For planar image formats, this is the size of the largest plane
+    pub fn size(&self) -> Size {
+        self.impl_video_frame.size()
+    }
+
+    /// Get the dpi of the contents of the frame (accounting for capture scaling)
+    pub fn dpi(&self) -> f64 {
+        self.impl_video_frame.dpi()
+    }
+}
+
+impl Debug for VideoFrame {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("VideoFrame").finish()
+    }
+}
+
\ No newline at end of file diff --git a/docs/windows_docs/src/crabgrab/lib.rs.html b/docs/windows_docs/src/crabgrab/lib.rs.html new file mode 100644 index 00000000..0fe0d525 --- /dev/null +++ b/docs/windows_docs/src/crabgrab/lib.rs.html @@ -0,0 +1,178 @@ +lib.rs - source +
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
+
//! A cross-platform screen/window/audio capture library
+//! 
+//! ## Feature flags
+//! 
+//! ### GPU Interop
+//! 
+//! - **`dx11`** - enables retreiving the surface of a video frame and getting the dx11 device instance for the stream (windows only)
+//! - **`dxgi`** - enables retreiving the surface of a video frame and getting the dxgi device instance for the stream (windows only)
+//! - **`metal`** - enabels retreiving the metal textures for a video frame and getting the metal device instance for the stream (macos only)
+//! - **`iosurface`** - enables retreiving the iosurface for a video frame (macos only)
+//! 
+//! ### Bitmap output
+//! 
+//! - **`bitmap`** - enables creating raw bitmap copies of frames in system memory
+//! 
+//! ## Example
+//! 
+//! ```
+//! use std::time::Duration;
+//! use crabgrab::prelude::*;
+//! 
+//! // spin up the async runtime
+//! let runtime = tokio::runtime::Builder::new_multi_thread().build().unwrap();
+//! // run our capture code in an async context
+//! let future = runtime.spawn(async {
+//!     // ensure we have priveleges to capture content
+//!     if !CaptureStream::test_access(false) {
+//!         CaptureStream::request_access(false).await;
+//!         println!("Approve access and run again!");
+//!     }
+//!     // create a filter for the windows we're interested in capturing
+//!     let window_filter = CapturableWindowFilter {
+//!         desktop_windows: false,
+//!         onscreen_only: true,
+//!     };
+//!     // create an overall content filter
+//!     let filter = CapturableContentFilter { windows: Some(window_filter), displays: false };
+//!     // get capturable content matching the filter
+//!     let content = CapturableContent::new(filter).await.unwrap();
+//!     // find the window we want to capture
+//!     let window = content.windows().filter(|window| {
+//!         let app_identifier = window.application().identifier();
+//!         app_identifier.to_lowercase().contains("finder") || app_identifier.to_lowercase().contains("explorer")
+//!     }).next();
+//!     match window {
+//!         Some(window) => {
+//!             println!("capturing window: {}", window.title()); 
+//!             // create a captuere config using the first supported pixel format
+//!             let config = CaptureConfig::with_window(window, CaptureStream::supported_pixel_formats()[0]).unwrap();
+//!             // create a capture stream with an event handler callback
+//!             let mut stream = CaptureStream::new(config, |stream_event| {
+//!                 match stream_event {
+//!                     Ok(event) => {
+//!                         match event {
+//!                             StreamEvent::Video(frame) => {
+//!                                 println!("Got frame: {}", frame.frame_id());
+//!                             },
+//!                             _ => {}
+//!                         }
+//!                     },
+//!                     Err(error) => {
+//!                         println!("Stream error: {:?}", error);
+//!                     }
+//!                 }
+//!             }).unwrap();
+//!             // wait for a while to capture some frames
+//!             tokio::task::block_in_place(|| std::thread::sleep(Duration::from_millis(4000)));
+//!             stream.stop().unwrap();
+//!         },
+//!         None => { println!("Failed to find window"); }
+//!     }
+//! });
+//! // wait for the future to complete
+//! runtime.block_on(future).unwrap();
+//! // shutdown the async runtime
+//! runtime.shutdown_timeout(Duration::from_millis(10000));
+//! ````
+//! 
+
+pub mod platform;
+pub mod feature;
+
+pub mod util;
+pub mod frame;
+pub mod capture_stream;
+pub mod capturable_content;
+
+pub mod prelude;
\ No newline at end of file diff --git a/docs/windows_docs/src/crabgrab/platform/mod.rs.html b/docs/windows_docs/src/crabgrab/platform/mod.rs.html new file mode 100644 index 00000000..32fa1c58 --- /dev/null +++ b/docs/windows_docs/src/crabgrab/platform/mod.rs.html @@ -0,0 +1,29 @@ +mod.rs - source +
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+
#[cfg(target_os = "macos")]
+pub mod macos;
+
+#[cfg(target_os = "macos")]
+pub use macos as platform_impl;
+
+#[cfg(target_os = "windows")]
+pub mod windows;
+
+#[cfg(target_os = "windows")]
+pub use windows as platform_impl;
+
+
+
\ No newline at end of file diff --git a/docs/windows_docs/src/crabgrab/platform/windows/audio_capture_stream.rs.html b/docs/windows_docs/src/crabgrab/platform/windows/audio_capture_stream.rs.html new file mode 100644 index 00000000..00a5d32b --- /dev/null +++ b/docs/windows_docs/src/crabgrab/platform/windows/audio_capture_stream.rs.html @@ -0,0 +1,355 @@ +audio_capture_stream.rs - source +
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
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+
use std::{ffi::c_void, sync::{atomic::{self, AtomicBool}, Arc}, time::Duration};
+
+use windows::{core::Interface, Win32::{Media::Audio::{eConsole, eRender, IAudioCaptureClient, IAudioClient, IMMDeviceEnumerator, MMDeviceEnumerator, AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, WAVEFORMATEX, WAVE_FORMAT_PCM}, System::Com::{CoCreateInstance, CoInitializeEx, CoUninitialize, CLSCTX_ALL, COINIT_MULTITHREADED}}};
+
+use crate::prelude::{AudioCaptureConfig, AudioChannelCount, AudioSampleRate};
+
+use super::capture_stream::SharedHandlerData;
+
+pub struct WindowsAudioCaptureStream {
+    should_couninit: bool,
+    audio_client: IAudioClient,
+}
+
+pub enum WindowsAudioCaptureStreamCreateError {
+    Other(String),
+    EndpointEnumerationFailed,
+    AudioClientActivationFailed,
+    AudioClientInitializeFailed,
+    AudioCaptureCreationFailed,
+    StreamStartFailed,
+}
+
+pub enum WindowsAudioCaptureStreamError {
+    Other(String),
+    GetBufferFailed,
+}
+
+pub struct WindowsAudioCaptureStreamPacket<'a> {
+    pub(crate) data: &'a [i16],
+    pub(crate) channel_count: u32,
+    pub(crate) origin_time: Duration,
+    pub(crate) duration: Duration,
+    pub(crate) sample_index: u64,
+}
+
+struct SendCaptureClient(*mut c_void);
+
+unsafe impl Send for SendCaptureClient {}
+unsafe impl Sync for SendCaptureClient {}
+
+impl SendCaptureClient {
+    fn from_iaudiocaptureclient(client: IAudioCaptureClient) -> Self {
+        SendCaptureClient(client.into_raw())
+    }
+
+    fn into_iaudiocaptureclient(self) -> IAudioCaptureClient {
+        unsafe { IAudioCaptureClient::from_raw(self.0) }
+    }
+}
+
+impl WindowsAudioCaptureStream {
+    pub fn new(config: AudioCaptureConfig, mut callback: Box<dyn for <'a> FnMut(Result<WindowsAudioCaptureStreamPacket<'a>, WindowsAudioCaptureStreamError>) + Send + 'static>) -> Result<Self, WindowsAudioCaptureStreamCreateError> {
+        unsafe {
+            let should_couninit = CoInitializeEx(None, COINIT_MULTITHREADED).is_ok();
+
+            let mm_device_enumerator: IMMDeviceEnumerator = CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL)
+                .map_err(|e| WindowsAudioCaptureStreamCreateError::Other(format!("Failed to create MMDeviceEnumerator: {}", e.to_string())))?;
+            let device = mm_device_enumerator.GetDefaultAudioEndpoint(eRender, eConsole)
+                .map_err(|_| WindowsAudioCaptureStreamCreateError::EndpointEnumerationFailed)?;
+            
+            let audio_client: IAudioClient = device.Activate(CLSCTX_ALL, None)
+                .map_err(|e| WindowsAudioCaptureStreamCreateError::AudioClientActivationFailed)?;
+
+            let mut format = WAVEFORMATEX::default();
+            format.wFormatTag = WAVE_FORMAT_PCM as u16;
+            format.nSamplesPerSec = match config.sample_rate {
+                AudioSampleRate::Hz8000  =>  8000,
+                AudioSampleRate::Hz16000 => 16000,
+                AudioSampleRate::Hz24000 => 24000,
+                AudioSampleRate::Hz48000 => 48000,
+            };
+            format.wBitsPerSample = 16;
+            format.nChannels = match config.channel_count {
+                AudioChannelCount::Mono   => 1,
+                AudioChannelCount::Stereo => 2,
+            };
+            format.nBlockAlign = format.nChannels * 2;
+            format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign as u32;
+            format.cbSize = 0;
+
+            let callback_format = format.clone();
+
+            let buffer_size = 512;
+            let buffer_time = buffer_size as i64 * 10000000i64 / format.nSamplesPerSec as i64;
+
+            let buffer_duration = Duration::from_nanos(buffer_time as u64 * 100);
+            let half_buffer_duration = buffer_duration / 2;
+
+            audio_client.Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, buffer_time, buffer_time, &format as *const _, None)
+                .map_err(|_| WindowsAudioCaptureStreamCreateError::AudioClientInitializeFailed)?;
+
+            let capture_client : IAudioCaptureClient = audio_client.GetService()
+                .map_err(|_| WindowsAudioCaptureStreamCreateError::AudioCaptureCreationFailed)?;
+
+            let capture_client_send = SendCaptureClient::from_iaudiocaptureclient(capture_client);
+
+            std::thread::spawn(move || {
+                unsafe {
+                    let should_couninit = CoInitializeEx(None, COINIT_MULTITHREADED).is_ok();
+
+                    let mut last_device_position = 0u64;
+                    let mut sample_count = 0u64;
+
+                    let capture_client = capture_client_send.into_iaudiocaptureclient();
+                    loop {
+                        std::thread::sleep(half_buffer_duration);
+
+                        let buffered_count = match capture_client.GetNextPacketSize() {
+                            Ok(count) => count,
+                            Err(_) => {
+                                (callback)(Err(WindowsAudioCaptureStreamError::Other(format!("Stream failed - couldn't fetch packet size"))));
+                                break;
+                            }
+                        };
+
+                        let mut data_ptr: *mut u8 = std::ptr::null_mut();
+
+                        let mut num_frames = 0u32;
+                        let mut flags = 0u32;
+                        let mut device_position = 0u64;
+
+                        match capture_client.GetBuffer(&mut data_ptr as *mut _, &mut num_frames as *mut _, &mut flags as *mut _, Some(&mut device_position as *mut _), None) {
+                            Ok(_) => {
+                                let packet = WindowsAudioCaptureStreamPacket {
+                                    data: std::slice::from_raw_parts(data_ptr as *const i16, num_frames as usize * 2),
+                                    channel_count: callback_format.nChannels as u32,
+                                    origin_time: Duration::from_nanos(device_position as u64 * 100),
+                                    duration: Duration::from_nanos((device_position - last_device_position) as u64),
+                                    sample_index: sample_count
+                                };
+                                (callback)(Ok(packet));
+                                let _ = capture_client.ReleaseBuffer(num_frames);
+                                last_device_position = device_position;
+                                sample_count += num_frames as u64;
+                            },
+                            Err(_) => {
+                                (callback)(Err(WindowsAudioCaptureStreamError::GetBufferFailed));
+                                break;
+                            }
+                        }
+
+                    }
+
+                    if should_couninit {
+                        CoUninitialize();
+                    }
+                }
+            });
+
+            audio_client.Start()
+                .map_err(|_| WindowsAudioCaptureStreamCreateError::StreamStartFailed)?;
+
+            Ok(WindowsAudioCaptureStream {
+                should_couninit,
+                audio_client
+            })
+        }
+    }
+
+    pub fn stop(&mut self) {
+        unsafe {
+            let _ = self.audio_client.Stop();
+        }
+    }
+}
+
+impl Drop for WindowsAudioCaptureStream {
+    fn drop(&mut self) {
+        unsafe {
+            let _ = self.audio_client.Stop();
+            if self.should_couninit {
+                CoUninitialize();
+            }
+        }
+    }
+}
+
\ No newline at end of file diff --git a/docs/windows_docs/src/crabgrab/platform/windows/capturable_content.rs.html b/docs/windows_docs/src/crabgrab/platform/windows/capturable_content.rs.html new file mode 100644 index 00000000..8a9a10e3 --- /dev/null +++ b/docs/windows_docs/src/crabgrab/platform/windows/capturable_content.rs.html @@ -0,0 +1,360 @@ +capturable_content.rs - source +
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
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+
use std::{ffi::OsString, os::{raw::c_void, windows::ffi::OsStringExt}};
+
+use windows::Win32::{Foundation::{BOOL, FALSE, HANDLE, HWND, LPARAM, RECT, TRUE}, Graphics::Gdi::{EnumDisplayMonitors, GetMonitorInfoA, HDC, HMONITOR, MONITORINFO}, System::{ProcessStatus::GetModuleFileNameExW, Threading::{GetProcessId, OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_VM_READ}}, UI::WindowsAndMessaging::{EnumWindows, GetWindowRect, GetWindowTextA, GetWindowTextLengthA, GetWindowThreadProcessId, IsWindow, IsWindowVisible}};
+
+use crate::{prelude::{CapturableContentError, CapturableContentFilter}, util::{Point, Rect, Size}};
+
+use super::AutoHandle;
+
+#[derive(Debug, Clone)]
+pub struct WindowsCapturableWindow(pub(crate) HWND);
+
+fn hwnd_process(hwnd: HWND) -> HANDLE {
+    unsafe {
+        let mut pid = 0u32;
+        GetWindowThreadProcessId(hwnd, Some(&mut pid as *mut _));
+        OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid).unwrap()
+    }
+}
+
+impl WindowsCapturableWindow {
+    pub fn from_impl(hwnd: HWND) -> Self {
+        Self(hwnd)
+    }
+
+    pub fn title(&self) -> String {
+        unsafe {
+            let text_length = GetWindowTextLengthA(self.0);
+            if text_length == 0 {
+                return "".into();
+            }
+            let mut text_buffer = vec![0u8; text_length as usize + 1];
+            let text_length = GetWindowTextA(self.0, &mut text_buffer[..]);
+            if (text_length as usize) < text_buffer.len() {
+                text_buffer.truncate(text_length as usize);
+            }
+            String::from_utf8_lossy(&text_buffer).to_string()
+        }
+    }
+
+    pub fn rect(&self) -> Rect {
+        unsafe {
+            let mut rect = RECT::default();
+            let _ = GetWindowRect(self.0, &mut rect);
+            Rect {
+                origin: Point {
+                    x: rect.left as f64,
+                    y: rect.top as f64
+                },
+                size: Size {
+                    width: (rect.right - rect.left) as f64,
+                    height: (rect.bottom - rect.top) as f64,
+                }
+            }
+        }
+    }
+
+    pub fn application(&self) -> WindowsCapturableApplication {
+        WindowsCapturableApplication(hwnd_process(self.0))
+    }
+}
+
+#[derive(Clone, Debug)]
+pub struct WindowsCapturableDisplay(pub(crate) HMONITOR, pub(crate) RECT);
+
+impl WindowsCapturableDisplay {
+    pub fn from_impl(monitor: (HMONITOR, RECT)) -> Self {
+        Self(monitor.0, monitor.1)
+    }
+
+    pub fn rect(&self) -> Rect {
+        Rect {
+            origin: Point {
+                x: self.1.left as f64,
+                y: self.1.top as f64
+            },
+            size: Size {
+                width: (self.1.right - self.1.left) as f64,
+                height: (self.1.bottom - self.1.top) as f64
+            }
+        }
+    }
+}
+
+#[derive(Clone, Debug)]
+pub struct WindowsCapturableApplication(pub(crate) HANDLE);
+
+impl WindowsCapturableApplication {
+    pub fn from_impl(handle: HANDLE) -> Self {
+        Self(handle)
+    }
+
+    pub fn identifier(&self) -> String {
+        unsafe {
+            let pid = GetProcessId(self.0);
+            let process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid);
+            if process.is_err() {
+                return "".into();
+            }
+            let process = process.unwrap();
+            // TODO: If OpenProcess fails we could fall back to GetProcessHandleFromHwnd, in oleacc.dll
+            //       Alternatively, it might be better to use the accessibility APIs.
+            let process = AutoHandle(process);
+            let mut process_name = vec![0u16; 64];
+            let mut len = GetModuleFileNameExW (process.0, None, process_name.as_mut_slice()) as usize;
+            while len == process_name.len() - 1 {
+                process_name = vec![0u16; process_name.len() * 2];
+                len = GetModuleFileNameExW (process.0, None, process_name.as_mut_slice()) as usize;
+            }
+
+            if len == 0 {
+                return "".into();
+            }
+
+            let os_string = OsString::from_wide(&process_name[..len as usize]);
+            let path = std::path::Path::new(&os_string);
+            let file_name = path.file_name();
+
+            if let Some(file_name) = file_name {
+                if let Some(name_str) = file_name.to_str() {
+                    return name_str.to_string()
+                }
+            }
+
+            let result = String::from_utf16(&process_name[..len as usize]);
+            result.unwrap_or("".into())
+        }
+    }
+}
+
+pub struct WindowsCapturableContent {
+    pub(crate) windows: Vec<HWND>,
+    pub(crate) displays: Vec<(HMONITOR, RECT)>,
+    pub(crate) applications: Vec<HANDLE>,
+}
+
+unsafe extern "system" fn enum_windows_callback(window: HWND, windows_ptr_raw: LPARAM) -> BOOL {
+    let windows: &mut Vec<HWND> = &mut *(windows_ptr_raw.0 as *mut c_void as *mut _);
+    windows.push(window);
+    TRUE
+}
+
+unsafe extern "system" fn enum_monitors_callback(monitor: HMONITOR, _: HDC, rect: *mut RECT, monitors_ptr_raw: LPARAM) -> BOOL {
+    let monitors: &mut Vec<(HMONITOR, RECT)> = &mut *(monitors_ptr_raw.0 as *mut c_void as *mut _);
+    monitors.push((monitor, *rect));
+    TRUE
+}
+
+impl WindowsCapturableContent {
+    pub async fn new(filter: CapturableContentFilter) -> Result<Self, CapturableContentError> {
+        let mut displays = Vec::<(HMONITOR, RECT)>::new();
+        let mut windows = Vec::<HWND>::new();
+        unsafe {
+            if filter.displays {
+                EnumDisplayMonitors(HDC(0), None, Some(enum_monitors_callback), LPARAM(&mut displays as *mut _ as *mut c_void as isize));
+            }
+            if let Some(window_filter) = filter.windows {
+                let _ = EnumWindows(Some(enum_windows_callback), LPARAM(&mut windows as *mut _ as *mut c_void as isize));
+                windows = windows.iter().filter(|hwnd| {
+                    if !IsWindow(**hwnd).as_bool() {
+                        return false;
+                    }
+                    if window_filter.onscreen_only && !IsWindowVisible(**hwnd).as_bool() {
+                        return false;
+                    }
+                    // TODO: filter desktop windows
+                    true
+                }).map(|hwnd| *hwnd).collect();
+            }
+        }
+        let applications = windows.iter().map(|hwnd| {
+            hwnd_process(*hwnd)
+        }).collect();
+        Ok(WindowsCapturableContent {
+            windows,
+            displays,
+            applications,
+        })
+    }
+}
\ No newline at end of file diff --git a/docs/windows_docs/src/crabgrab/platform/windows/capture_stream.rs.html b/docs/windows_docs/src/crabgrab/platform/windows/capture_stream.rs.html new file mode 100644 index 00000000..6f6aeede --- /dev/null +++ b/docs/windows_docs/src/crabgrab/platform/windows/capture_stream.rs.html @@ -0,0 +1,773 @@ +capture_stream.rs - source +
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
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+
use std::{sync::{atomic::{self, AtomicBool, AtomicU64}, Arc}, time::{Duration, Instant}};
+
+use crate::{prelude::{AudioChannelCount, AudioFrame, Capturable, CaptureConfig, CapturePixelFormat, StreamCreateError, StreamError, StreamEvent, StreamStopError, VideoFrame}, util::Rect};
+
+use parking_lot::Mutex;
+use windows::{core::{ComInterface, IInspectable, HSTRING}, Foundation::TypedEventHandler, Graphics::{Capture::{Direct3D11CaptureFramePool, GraphicsCaptureAccess, GraphicsCaptureAccessKind, GraphicsCaptureItem, GraphicsCaptureSession}, DirectX::{Direct3D11::IDirect3DDevice, DirectXPixelFormat}, SizeInt32}, Security::Authorization::AppCapabilityAccess::{AppCapability, AppCapabilityAccessStatus}, Win32::{Graphics::{Direct3D::{D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_UNKNOWN, D3D_FEATURE_LEVEL_11_0}, Direct3D11::{D3D11CreateDevice, ID3D11Device, D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_SDK_VERSION}, Dxgi::{CreateDXGIFactory, IDXGIAdapter, IDXGIDevice, IDXGIFactory}}, System::{Com::{CoInitializeEx, CoUninitialize, COINIT_MULTITHREADED}, WinRT::{Direct3D11::CreateDirect3D11DeviceFromDXGIDevice, Graphics::Capture::IGraphicsCaptureItemInterop}}, UI::HiDpi::{GetDpiForMonitor, GetDpiForWindow, MDT_RAW_DPI}}};
+
+use super::{audio_capture_stream::{WindowsAudioCaptureStream, WindowsAudioCaptureStreamError, WindowsAudioCaptureStreamPacket}, frame::WindowsVideoFrame, frame::WindowsAudioFrame};
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum WindowsPixelFormat {
+    Bgra8888,
+}
+
+#[derive(Clone, Debug)]
+pub struct WindowsAudioCaptureConfig {}
+
+impl WindowsAudioCaptureConfig {
+    pub fn new() -> Self {
+        Self {
+        }
+    }
+}
+
+pub trait WindowsAudioCaptureConfigExt {
+
+}
+
+impl WindowsAudioCaptureConfigExt for CaptureConfig {
+
+}
+
+#[derive(Clone, Debug)]
+pub struct WindowsCaptureConfig {
+    dxgi_adapter: Option<IDXGIAdapter>,
+    d3d11_device: Option<ID3D11Device>,
+}
+
+impl WindowsCaptureConfig {
+    pub fn new() -> Self {
+        Self {
+            dxgi_adapter: None,
+            d3d11_device: None,
+        }
+    }
+}
+
+pub trait WindowsCaptureConfigExt {
+    fn with_dxgi_adapter(self, dxgi_adapter: IDXGIAdapter) -> Self;
+    fn with_d3d11_device(self, d3d11_device: ID3D11Device) -> Self;
+}
+
+impl WindowsCaptureConfigExt for CaptureConfig {
+    fn with_dxgi_adapter(self, dxgi_adapter: IDXGIAdapter) -> Self {
+        Self {
+            impl_capture_config: WindowsCaptureConfig {
+                dxgi_adapter: Some(dxgi_adapter),
+                ..self.impl_capture_config
+            },
+            ..self
+        }
+    }
+
+    fn with_d3d11_device(self, d3d11_device: ID3D11Device) -> Self {
+        Self {
+            impl_capture_config: WindowsCaptureConfig {
+                d3d11_device: Some(d3d11_device),
+                ..self.impl_capture_config
+            },
+            ..self
+        }
+    }
+}
+
+pub struct WindowsCaptureStream {
+    pub(crate) dxgi_adapter: IDXGIAdapter,
+    pub(crate) dxgi_device: IDXGIDevice,
+    pub(crate) d3d11_device: ID3D11Device,
+    pub(crate) frame_pool: Direct3D11CaptureFramePool,
+    pub(crate) capture_session: GraphicsCaptureSession,
+    should_couninit: bool,
+    shared_handler_data: Arc<SharedHandlerData>,
+    audio_stream: Option<WindowsAudioCaptureStream>,
+}
+
+pub(crate) struct SharedHandlerData {
+    callback: Mutex<Box<dyn FnMut(Result<StreamEvent, StreamError>) + Send + 'static>>,
+    closed: AtomicBool,
+    frame_id_counter: AtomicU64,
+    audio_frame_id_counter: AtomicU64,
+}
+
+impl WindowsCaptureStream {
+    pub fn supported_pixel_formats() -> &'static [CapturePixelFormat] {
+        &[
+            CapturePixelFormat::Bgra8888,
+            CapturePixelFormat::Argb2101010,
+        ]
+    }
+
+    pub fn check_access(borderless: bool) -> bool {
+        let graphics_capture_capability = HSTRING::from("graphicsCaptureProgrammatic");
+        let programmatic_access = AppCapability::Create(&graphics_capture_capability).map(|capability| {
+            match capability.CheckAccess() {
+                Ok(AppCapabilityAccessStatus::Allowed) => true,
+                _ => false,
+            }
+        }).unwrap_or(true);
+        let borderless_graphics_capture_capability = HSTRING::from("graphicsCaptureWithoutBorder");
+        let borderless_access = AppCapability::Create(&borderless_graphics_capture_capability).map(|capability| {
+            match capability.CheckAccess() {
+                Ok(AppCapabilityAccessStatus::Allowed) => true,
+                _ => false,
+            }
+        }).unwrap_or(true);
+        programmatic_access && (!borderless || borderless_access)
+    }
+
+    pub async fn request_access(borderless: bool) -> bool {
+        let access_kind = if borderless {
+            GraphicsCaptureAccessKind::Borderless
+        } else {
+            GraphicsCaptureAccessKind::Programmatic
+        };
+        match GraphicsCaptureAccess::RequestAccessAsync(access_kind) {
+            Ok(access_future) => match access_future.await {
+                Ok(AppCapabilityAccessStatus::Allowed) => true,
+                _ => false
+            },
+            _ => false,
+        }
+    }
+
+    fn create_d3d11_device(dxgi_adapter: IDXGIAdapter) -> Result<(IDXGIAdapter, ID3D11Device), StreamCreateError> {
+        unsafe {
+            let mut d3d11_device = None;
+            let d3d11_device_result = D3D11CreateDevice(
+                Some(&dxgi_adapter),
+                D3D_DRIVER_TYPE_UNKNOWN,
+                None,
+                D3D11_CREATE_DEVICE_BGRA_SUPPORT,
+                Some(&[D3D_FEATURE_LEVEL_11_0]),
+                D3D11_SDK_VERSION,
+                Some(&mut d3d11_device as *mut _),
+                None,
+                None
+            );
+            match d3d11_device_result {
+                Ok(_) => d3d11_device.map_or_else(|| Err(StreamCreateError::Other("Failed to create ID3D11Device".into())), |x| Ok((dxgi_adapter, x))),
+                Err(e) => Err(StreamCreateError::Other(format!("Failed to create d3d11 device")))
+                ,
+            }
+        }
+    }
+
+    pub fn new(config: CaptureConfig, callback: Box<impl FnMut(Result<StreamEvent, StreamError>) + Send + 'static>) -> Result<Self, StreamCreateError> {
+        let should_couninit = unsafe {
+            CoInitializeEx(None, COINIT_MULTITHREADED).is_ok()
+        };
+        
+        let pixel_format = match config.pixel_format {
+            CapturePixelFormat::Bgra8888 => DirectXPixelFormat::B8G8R8A8UIntNormalized,
+            CapturePixelFormat::Argb2101010 => DirectXPixelFormat::R10G10B10A2UIntNormalized,
+            _ => return Err(StreamCreateError::UnsupportedPixelFormat),
+        };
+
+        let interop: IGraphicsCaptureItemInterop = windows::core::factory::<GraphicsCaptureItem, IGraphicsCaptureItemInterop>()
+            .map_err(|_| StreamCreateError::Other("Failed to create IGraphicsCaptureInterop factory".into()))?;
+
+        let callback_target = config.target.clone();
+
+        let graphics_capture_item: GraphicsCaptureItem = unsafe {
+            match config.target {
+                Capturable::Window(window) =>
+                    interop.CreateForWindow(window.impl_capturable_window.0)
+                        .map_err(|_| StreamCreateError::Other("Failed to create graphics capture item from HWND".into()))?,
+                Capturable::Display(display) => 
+                    interop.CreateForMonitor(display.impl_capturable_display.0)
+                        .map_err(|_| StreamCreateError::Other("Failed to create graphics capture item from HMONITOR".into()))?,
+            }
+        };
+
+        let (dxgi_adapter, d3d11_device) = match (config.impl_capture_config.dxgi_adapter, config.impl_capture_config.d3d11_device) {
+            (_, Some(d3d11_device)) => {
+                let dxgi_adapter = d3d11_device.cast().map_err(|_| StreamCreateError::Other("Failed to create IDXGIAdapter from ID3D11Device".into()))?;
+                (dxgi_adapter, d3d11_device)
+            },
+            (Some(dxgi_adapter), None) => Self::create_d3d11_device(dxgi_adapter)?,
+            (None, None) => {
+                let dxgi_factory: IDXGIFactory = unsafe { CreateDXGIFactory()
+                    .map_err(|_| StreamCreateError::Other("Failed to create IDXGIAdapter factory".into())) }?;
+                let dxgi_adapter = unsafe { dxgi_factory.EnumAdapters(0) }
+                    .map_err(|_| StreamCreateError::Other("Failed to enumerate IDXGIAdapter".into()))?;
+                Self::create_d3d11_device(dxgi_adapter)?
+            }
+        };
+
+        let dxgi_device: IDXGIDevice = d3d11_device.clone().cast()
+            .map_err(|_| StreamCreateError::Other("Failed to cast ID3D11Device to IDXGIDevice".into()))?;
+        let direct3d_device_iinspectible = unsafe { CreateDirect3D11DeviceFromDXGIDevice(&dxgi_device) }
+            .map_err(|_| StreamCreateError::Other("Failed to create IDirect3DDevice from IDXGIDevice".into()))?;
+        let direct3d_device: IDirect3DDevice = direct3d_device_iinspectible.cast()
+            .map_err(|_| StreamCreateError::Other("Failed to cast IInspectible to IDirect3DDevice".into()))?;
+
+        let callback_direct3d_device = d3d11_device.clone();
+
+        let (width, height) = ((config.output_size.width + 0.1) as usize, (config.output_size.height + 0.1) as usize);
+
+        let frame_pool = Direct3D11CaptureFramePool::CreateFreeThreaded(
+            &direct3d_device,
+            pixel_format,
+            config.buffer_count as i32,
+            SizeInt32 { Width: width as i32, Height: height as i32 },
+        ).map_err(|e| StreamCreateError::Other(format!("Failed to create Direct3D11CaptureFramePool: {}", e.to_string())))?;
+
+        let shared_handler_data = Arc::new(
+            SharedHandlerData {
+                callback: Mutex::new(callback),
+                closed: AtomicBool::new(false),
+                frame_id_counter: AtomicU64::new(0),
+                audio_frame_id_counter: AtomicU64::new(0),
+            }
+        );
+
+        let close_handler_data = shared_handler_data.clone();
+        let frame_handler_data = shared_handler_data.clone();
+        let audio_handler_data = shared_handler_data.clone();
+
+        let close_handler = TypedEventHandler::new(move |_, _| {
+            let alread_closed = close_handler_data.closed.fetch_and(true, atomic::Ordering::AcqRel);
+            if !alread_closed {
+                let mut callback = close_handler_data.callback.lock();
+                (*callback)(Ok(StreamEvent::End));
+            }
+            Ok(())
+        });
+
+        let mut t_first_frame = None;
+        let mut t_last_frame = None;
+
+        let frame_handler = TypedEventHandler::new(move |frame_pool: &Option<Direct3D11CaptureFramePool>, _: &Option<IInspectable>| {
+            if frame_pool.is_none() {
+                return Ok(());
+            }
+            let frame_pool = frame_pool.as_ref().unwrap();
+            if frame_handler_data.closed.load(atomic::Ordering::Acquire) {
+                return Ok(());
+            }
+            let t_capture = Instant::now();
+            let t_origin = match t_first_frame {
+                Some(t_first_frame) => t_capture - t_first_frame,
+                None => {
+                    t_first_frame = Some(t_capture);
+                    Duration::ZERO
+                }
+            };
+            let duration = match t_last_frame {
+                Some(t_last_frame) => t_capture - t_last_frame,
+                None => {
+                    t_last_frame = Some(t_capture);
+                    Duration::ZERO
+                }
+            };
+            let dpi = unsafe { 
+                match &callback_target {
+                    Capturable::Window(window) => GetDpiForWindow(window.impl_capturable_window.0),
+                    Capturable::Display(display) => {
+                        let mut dpi_x = 0u32;
+                        let mut dpi_y = 0u32;
+                        let _ = GetDpiForMonitor(display.impl_capturable_display.0, MDT_RAW_DPI, &mut dpi_x as *mut _, &mut dpi_y as *mut _);
+                        dpi_x.min(dpi_y)
+                    }
+                }
+            };
+            let mut callback = frame_handler_data.callback.lock();
+            //let window_rect = RECT::default();
+            let frame = match frame_pool.TryGetNextFrame() {
+                Ok(frame) => frame,
+                Err(e) => {
+                    (*callback)(Err(StreamError::Other(format!("Failed to capture frame: {}", e.to_string()))));
+                    return Ok(());
+                }
+            };
+
+            let frame_id = frame_handler_data.frame_id_counter.fetch_add(1, atomic::Ordering::AcqRel);
+            let impl_video_frame = WindowsVideoFrame {
+                device: callback_direct3d_device.clone(),
+                frame,
+                frame_id,
+                frame_size: (width, height),
+                pixel_format,
+                dpi,
+                t_capture,
+                t_origin,
+                duration,
+            };
+            let video_frame = VideoFrame {
+                impl_video_frame
+            };
+            (*callback)(Ok(StreamEvent::Video(video_frame)));
+            Ok(())
+        });
+
+        frame_pool.FrameArrived(&frame_handler).map_err(|_| StreamCreateError::Other("Failed to listen to FrameArrived event".into()))?;
+        graphics_capture_item.Closed(&close_handler).map_err(|_| StreamCreateError::Other("Failed to listen to Closed event".into()))?;
+
+        let capture_session = frame_pool.CreateCaptureSession(&graphics_capture_item)
+            .map_err(|_| StreamCreateError::Other("Failed to create GraphicsCaptureSession".into()))?;
+
+        let audio_stream = if let Some(audio_config) = config.capture_audio {
+            let handler_config = audio_config.clone();
+            let audio_handler = Box::new(move |audio_result: Result<WindowsAudioCaptureStreamPacket<'_>, WindowsAudioCaptureStreamError>| {
+                if audio_handler_data.closed.load(atomic::Ordering::Acquire) {
+                    return;
+                }
+                match audio_result {
+                    Ok(packet) => {
+                        let audio_frame_id = audio_handler_data.audio_frame_id_counter.fetch_add(1, atomic::Ordering::AcqRel);
+                        let event = StreamEvent::Audio(AudioFrame {
+                            impl_audio_frame: WindowsAudioFrame {
+                                data: packet.data.to_owned().into_boxed_slice(),
+                                channel_count: handler_config.channel_count,
+                                sample_rate: handler_config.sample_rate,
+                                duration: packet.duration,
+                                origin_time: packet.origin_time,
+                                frame_id: audio_frame_id
+                            }
+                        });
+                        (*audio_handler_data.callback.lock())(Ok(event));
+                    },
+                    Err(e) => {
+                        (*audio_handler_data.callback.lock())(Err(StreamError::Other("Audio stream error".to_string())));
+                    }
+                }
+            });
+
+            match WindowsAudioCaptureStream::new(audio_config, audio_handler) {
+                Ok(audio_stream) => {
+                    Some(audio_stream)
+                },
+                Err(_) => {
+                    return Err(StreamCreateError::Other("Failed to create audio stream".into()))
+                }
+            }
+        } else {
+            None
+        };
+
+        capture_session.StartCapture().map_err(|_| StreamCreateError::Other("Failed to start capture".into()))?;
+
+        let stream = WindowsCaptureStream {
+            dxgi_adapter,
+            dxgi_device,
+            d3d11_device,
+            frame_pool,
+            capture_session,
+            should_couninit,
+            shared_handler_data,
+            audio_stream
+        };
+
+        Ok(stream)
+    }
+
+    pub fn stop(&self) -> Result<(), StreamStopError> {
+        let already_closed = self.shared_handler_data.closed.fetch_and(true, atomic::Ordering::AcqRel);
+        if !already_closed {
+            (*self.shared_handler_data.callback.lock())(Ok(StreamEvent::End));
+        }
+        self.capture_session.Close().map_err(|_| StreamStopError::Other("Failed to close capture session".into()))?;
+        Ok(())
+    }
+}
+
+impl Drop for WindowsCaptureStream {
+    fn drop(&mut self) {
+        let _ = self.stop();
+        if let Some(audio_stream) = &mut self.audio_stream {
+            audio_stream.stop();
+        }
+        if self.should_couninit {
+            unsafe { CoUninitialize(); }
+        }
+    }
+}
+
\ No newline at end of file diff --git a/docs/windows_docs/src/crabgrab/platform/windows/frame.rs.html b/docs/windows_docs/src/crabgrab/platform/windows/frame.rs.html new file mode 100644 index 00000000..5029a30a --- /dev/null +++ b/docs/windows_docs/src/crabgrab/platform/windows/frame.rs.html @@ -0,0 +1,209 @@ +frame.rs - source +
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
+
use std::{marker::PhantomData, time::Duration};
+
+use windows::{Graphics::{Capture::Direct3D11CaptureFrame, DirectX::DirectXPixelFormat, SizeInt32}, Win32::Graphics::Direct3D11::ID3D11Device};
+
+use crate::{prelude::{AudioBufferError, AudioCaptureFrame, AudioChannelCount, AudioChannelDataSamples, AudioSampleRate, VideoCaptureFrame}, util::Size};
+
+pub struct WindowsVideoFrame {
+    pub(crate) device       : ID3D11Device,
+    pub(crate) frame        : Direct3D11CaptureFrame,
+    pub(crate) frame_size   : (usize, usize),
+    pub(crate) pixel_format : DirectXPixelFormat,
+    pub(crate) frame_id     : u64,
+    pub(crate) dpi          : u32,
+    pub(crate) t_capture    : std::time::Instant,
+    pub(crate) t_origin     : std::time::Duration,
+    pub(crate) duration     : std::time::Duration,
+}
+
+impl VideoCaptureFrame for WindowsVideoFrame {
+    fn size(&self) -> Size {
+        let size = self.frame.ContentSize().unwrap_or(SizeInt32::default());
+        Size {
+            width: size.Width as f64,
+            height: size.Height as f64,
+        }
+    }
+
+    fn dpi(&self) -> f64 {
+        self.dpi as f64
+    }
+
+    fn duration(&self) -> std::time::Duration {
+        self.duration
+    }
+
+    fn origin_time(&self) -> std::time::Duration {
+        self.t_origin
+    }
+
+    fn capture_time(&self) -> std::time::Instant {
+        self.t_capture
+    }
+
+    fn frame_id(&self) -> u64 {
+        self.frame_id
+    }
+}
+
+pub struct WindowsAudioFrame {
+    pub(crate) data: Box<[i16]>,
+    pub(crate) channel_count: AudioChannelCount,
+    pub(crate) sample_rate: AudioSampleRate,
+    pub(crate) duration: Duration,
+    pub(crate) origin_time: Duration,
+    pub(crate) frame_id: u64,
+}
+
+impl AudioCaptureFrame for WindowsAudioFrame {
+    fn sample_rate(&self) -> crate::prelude::AudioSampleRate {
+        self.sample_rate
+    }
+
+    fn channel_count(&self) -> crate::prelude::AudioChannelCount {
+        self.channel_count
+    }
+
+    fn audio_channel_buffer(&mut self, channel: usize) -> Result<crate::prelude::AudioChannelData<'_>, crate::prelude::AudioBufferError> {
+        let element_stride = match self.channel_count {
+            AudioChannelCount::Mono => {
+                if channel != 0 {
+                    return Err(AudioBufferError::InvalidChannel)
+                }
+                0
+            },
+            AudioChannelCount::Stereo => {
+                if channel > 1 {
+                    return Err(AudioBufferError::InvalidChannel)
+                }
+                channel
+            },
+        };
+        let data = &self.data[element_stride] as *const i16 as *const u8;
+        Ok(crate::prelude::AudioChannelData::I16(AudioChannelDataSamples {
+            data,
+            stride: element_stride / 2,
+            length: self.data.len() / element_stride,
+            phantom_lifetime: PhantomData
+        }))
+    }
+
+    fn duration(&self) -> std::time::Duration {
+        self.duration
+    }
+
+    fn origin_time(&self) -> std::time::Duration {
+        self.origin_time
+    }
+
+    fn frame_id(&self) -> u64 {
+        self.frame_id
+    }
+}
+
+
\ No newline at end of file diff --git a/docs/windows_docs/src/crabgrab/platform/windows/mod.rs.html b/docs/windows_docs/src/crabgrab/platform/windows/mod.rs.html new file mode 100644 index 00000000..4b400948 --- /dev/null +++ b/docs/windows_docs/src/crabgrab/platform/windows/mod.rs.html @@ -0,0 +1,57 @@ +mod.rs - source +
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
+
use windows::Win32::Foundation::CloseHandle;
+use windows::Win32::Foundation::HANDLE;
+
+mod capture_stream;
+mod capturable_content;
+mod audio_capture_stream;
+pub(crate) mod frame;
+
+pub(crate) struct AutoHandle(HANDLE);
+impl Drop for AutoHandle {
+    fn drop(&mut self) {
+        unsafe { let _ = CloseHandle(self.0); }
+    }
+}
+
+pub use capturable_content::WindowsCapturableApplication as ImplCapturableApplication;
+pub use capturable_content::WindowsCapturableDisplay as ImplCapturableDisplay;
+pub use capturable_content::WindowsCapturableWindow as ImplCapturableWindow;
+pub use capturable_content::WindowsCapturableContent as ImplCapturableContent;
+
+pub use capture_stream::WindowsCaptureStream as ImplCaptureStream;
+pub use capture_stream::WindowsCaptureConfig as ImplCaptureConfig;
+pub use capture_stream::WindowsAudioCaptureConfig as ImplAudioCaptureConfig;
+pub use capture_stream::WindowsPixelFormat as ImplPixelFormat;
+
+pub use frame::WindowsVideoFrame as ImplVideoFrame;
+pub use frame::WindowsAudioFrame as ImplAudioFrame;
+
\ No newline at end of file diff --git a/docs/windows_docs/src/crabgrab/prelude.rs.html b/docs/windows_docs/src/crabgrab/prelude.rs.html new file mode 100644 index 00000000..cb618079 --- /dev/null +++ b/docs/windows_docs/src/crabgrab/prelude.rs.html @@ -0,0 +1,11 @@ +prelude.rs - source +
1
+2
+3
+4
+
pub use crate::capturable_content::*;
+pub use crate::frame::*;
+pub use crate::capture_stream::*;
+pub use crate::util::*;
+
\ No newline at end of file diff --git a/docs/windows_docs/src/crabgrab/util.rs.html b/docs/windows_docs/src/crabgrab/util.rs.html new file mode 100644 index 00000000..0d944a46 --- /dev/null +++ b/docs/windows_docs/src/crabgrab/util.rs.html @@ -0,0 +1,163 @@ +util.rs - source +
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
+
/// Represents a 2d size
+#[derive(Debug, Copy, Clone)]
+pub struct Size {
+    pub width: f64,
+    pub height: f64,
+}
+
+impl Size {
+    /// scale the size uniformly by some value
+    pub fn scaled(&self, scale: f64) -> Self {
+        Self {
+            width: self.width * scale,
+            height: self.height * scale
+        }
+    }
+
+    /// scale the size non-uniformly in x and y
+    pub fn scaled_2d(&self, scale: (f64, f64)) -> Self {
+        Self {
+            width: self.width * scale.0,
+            height: self.height * scale.1
+        }
+    }
+}
+
+/// Represents a 2d point
+#[derive(Debug, Copy, Clone)]
+pub struct Point {
+    pub x: f64,
+    pub y: f64,
+}
+
+impl Point {
+    /// The point at (0, 0)
+    pub const ZERO: Point = Point {
+        x: 0.0, 
+        y: 0.0
+    };
+
+    /// Scale the point uniformly by some value
+    pub fn scaled(&self, scale: f64) -> Self {
+        Self {
+            x: self.x * scale,
+            y: self.y * scale
+        }
+    }
+
+    /// Scale the point non-uniformly in x and y
+    pub fn scaled_2d(&self, scale: (f64, f64)) -> Self {
+        Self {
+            x: self.x * scale.0,
+            y: self.y * scale.1
+        }
+    }
+}
+
+/// Represents an axis-aligned rectangle
+#[derive(Debug, Copy, Clone)]
+pub struct Rect {
+    pub origin: Point,
+    pub size: Size,
+}
+
+impl Rect {
+    /// Scale the rectangle uniformly
+    pub fn scaled(&self, scale: f64) -> Self {
+        Self {
+            origin: self.origin.scaled(scale),
+            size: self.size.scaled(scale)
+        }
+    }
+
+    /// Scale the rectangle non-uniformly in x and y
+    pub fn scaled_2d(&self, scale: (f64, f64)) -> Self {
+        Self {
+            origin: self.origin.scaled_2d(scale),
+            size: self.size.scaled_2d(scale)
+        }
+    }
+}
+
\ No newline at end of file diff --git a/docs/windows_docs/static.files/COPYRIGHT-23e9bde6c69aea69.txt b/docs/windows_docs/static.files/COPYRIGHT-23e9bde6c69aea69.txt new file mode 100644 index 00000000..1447df79 --- /dev/null +++ b/docs/windows_docs/static.files/COPYRIGHT-23e9bde6c69aea69.txt @@ -0,0 +1,50 @@ +# REUSE-IgnoreStart + +These documentation pages include resources by third parties. This copyright +file applies only to those resources. The following third party resources are +included, and carry their own copyright notices and license terms: + +* Fira Sans (FiraSans-Regular.woff2, FiraSans-Medium.woff2): + + Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ + with Reserved Font Name Fira Sans. + + Copyright (c) 2014, Telefonica S.A. + + Licensed under the SIL Open Font License, Version 1.1. + See FiraSans-LICENSE.txt. + +* rustdoc.css, main.js, and playpen.js: + + Copyright 2015 The Rust Developers. + Licensed under the Apache License, Version 2.0 (see LICENSE-APACHE.txt) or + the MIT license (LICENSE-MIT.txt) at your option. + +* normalize.css: + + Copyright (c) Nicolas Gallagher and Jonathan Neal. + Licensed under the MIT license (see LICENSE-MIT.txt). + +* Source Code Pro (SourceCodePro-Regular.ttf.woff2, + SourceCodePro-Semibold.ttf.woff2, SourceCodePro-It.ttf.woff2): + + Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), + with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark + of Adobe Systems Incorporated in the United States and/or other countries. + + Licensed under the SIL Open Font License, Version 1.1. + See SourceCodePro-LICENSE.txt. + +* Source Serif 4 (SourceSerif4-Regular.ttf.woff2, SourceSerif4-Bold.ttf.woff2, + SourceSerif4-It.ttf.woff2): + + Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name + 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United + States and/or other countries. + + Licensed under the SIL Open Font License, Version 1.1. + See SourceSerif4-LICENSE.md. + +This copyright file is intended to be distributed with rustdoc output. + +# REUSE-IgnoreEnd diff --git a/docs/windows_docs/static.files/FiraSans-LICENSE-db4b642586e02d97.txt b/docs/windows_docs/static.files/FiraSans-LICENSE-db4b642586e02d97.txt new file mode 100644 index 00000000..d7e9c149 --- /dev/null +++ b/docs/windows_docs/static.files/FiraSans-LICENSE-db4b642586e02d97.txt @@ -0,0 +1,98 @@ +// REUSE-IgnoreStart + +Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. +with Reserved Font Name < Fira >, + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/docs/windows_docs/static.files/FiraSans-Medium-8f9a781e4970d388.woff2 b/docs/windows_docs/static.files/FiraSans-Medium-8f9a781e4970d388.woff2 new file mode 100644 index 00000000..7a1e5fc5 Binary files /dev/null and b/docs/windows_docs/static.files/FiraSans-Medium-8f9a781e4970d388.woff2 differ diff --git a/docs/windows_docs/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 b/docs/windows_docs/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 new file mode 100644 index 00000000..e766e06c Binary files /dev/null and b/docs/windows_docs/static.files/FiraSans-Regular-018c141bf0843ffd.woff2 differ diff --git a/docs/windows_docs/static.files/LICENSE-APACHE-b91fa81cba47b86a.txt b/docs/windows_docs/static.files/LICENSE-APACHE-b91fa81cba47b86a.txt new file mode 100644 index 00000000..16fe87b0 --- /dev/null +++ b/docs/windows_docs/static.files/LICENSE-APACHE-b91fa81cba47b86a.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/docs/windows_docs/static.files/LICENSE-MIT-65090b722b3f6c56.txt b/docs/windows_docs/static.files/LICENSE-MIT-65090b722b3f6c56.txt new file mode 100644 index 00000000..31aa7938 --- /dev/null +++ b/docs/windows_docs/static.files/LICENSE-MIT-65090b722b3f6c56.txt @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/docs/windows_docs/static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 b/docs/windows_docs/static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 new file mode 100644 index 00000000..1866ad4b Binary files /dev/null and b/docs/windows_docs/static.files/NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2 differ diff --git a/docs/windows_docs/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt b/docs/windows_docs/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt new file mode 100644 index 00000000..4b3edc29 --- /dev/null +++ b/docs/windows_docs/static.files/NanumBarunGothic-LICENSE-18c5adf4b52b4041.txt @@ -0,0 +1,103 @@ +// REUSE-IgnoreStart + +Copyright (c) 2010, NAVER Corporation (https://www.navercorp.com/), + +with Reserved Font Name Nanum, Naver Nanum, NanumGothic, Naver NanumGothic, +NanumMyeongjo, Naver NanumMyeongjo, NanumBrush, Naver NanumBrush, NanumPen, +Naver NanumPen, Naver NanumGothicEco, NanumGothicEco, Naver NanumMyeongjoEco, +NanumMyeongjoEco, Naver NanumGothicLight, NanumGothicLight, NanumBarunGothic, +Naver NanumBarunGothic, NanumSquareRound, NanumBarunPen, MaruBuri + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/docs/windows_docs/static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 b/docs/windows_docs/static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 new file mode 100644 index 00000000..462c34ef Binary files /dev/null and b/docs/windows_docs/static.files/SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2 differ diff --git a/docs/windows_docs/static.files/SourceCodePro-LICENSE-d180d465a756484a.txt b/docs/windows_docs/static.files/SourceCodePro-LICENSE-d180d465a756484a.txt new file mode 100644 index 00000000..0d2941e1 --- /dev/null +++ b/docs/windows_docs/static.files/SourceCodePro-LICENSE-d180d465a756484a.txt @@ -0,0 +1,97 @@ +// REUSE-IgnoreStart + +Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +// REUSE-IgnoreEnd diff --git a/docs/windows_docs/static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 b/docs/windows_docs/static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 new file mode 100644 index 00000000..10b558e0 Binary files /dev/null and b/docs/windows_docs/static.files/SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2 differ diff --git a/docs/windows_docs/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 b/docs/windows_docs/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 new file mode 100644 index 00000000..5ec64eef Binary files /dev/null and b/docs/windows_docs/static.files/SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2 differ diff --git a/docs/windows_docs/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 b/docs/windows_docs/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 new file mode 100644 index 00000000..181a07f6 Binary files /dev/null and b/docs/windows_docs/static.files/SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2 differ diff --git a/docs/windows_docs/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 b/docs/windows_docs/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 new file mode 100644 index 00000000..2ae08a7b Binary files /dev/null and b/docs/windows_docs/static.files/SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2 differ diff --git a/docs/windows_docs/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md b/docs/windows_docs/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md new file mode 100644 index 00000000..175fa4f4 --- /dev/null +++ b/docs/windows_docs/static.files/SourceSerif4-LICENSE-3bb119e13b1258b7.md @@ -0,0 +1,98 @@ + + +Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. +Copyright 2014 - 2023 Adobe (http://www.adobe.com/), with Reserved Font Name ‘Source’. All Rights Reserved. Source is a trademark of Adobe in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + + diff --git a/docs/windows_docs/static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 b/docs/windows_docs/static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 new file mode 100644 index 00000000..0263fc30 Binary files /dev/null and b/docs/windows_docs/static.files/SourceSerif4-Regular-46f98efaafac5295.ttf.woff2 differ diff --git a/docs/windows_docs/static.files/clipboard-7571035ce49a181d.svg b/docs/windows_docs/static.files/clipboard-7571035ce49a181d.svg new file mode 100644 index 00000000..8adbd996 --- /dev/null +++ b/docs/windows_docs/static.files/clipboard-7571035ce49a181d.svg @@ -0,0 +1 @@ + diff --git a/docs/windows_docs/static.files/favicon-16x16-8b506e7a72182f1c.png b/docs/windows_docs/static.files/favicon-16x16-8b506e7a72182f1c.png new file mode 100644 index 00000000..ea4b45ca Binary files /dev/null and b/docs/windows_docs/static.files/favicon-16x16-8b506e7a72182f1c.png differ diff --git a/docs/windows_docs/static.files/favicon-2c020d218678b618.svg b/docs/windows_docs/static.files/favicon-2c020d218678b618.svg new file mode 100644 index 00000000..8b34b511 --- /dev/null +++ b/docs/windows_docs/static.files/favicon-2c020d218678b618.svg @@ -0,0 +1,24 @@ + + + + + diff --git a/docs/windows_docs/static.files/favicon-32x32-422f7d1d52889060.png b/docs/windows_docs/static.files/favicon-32x32-422f7d1d52889060.png new file mode 100644 index 00000000..69b8613c Binary files /dev/null and b/docs/windows_docs/static.files/favicon-32x32-422f7d1d52889060.png differ diff --git a/docs/windows_docs/static.files/main-48f368f3872407c8.js b/docs/windows_docs/static.files/main-48f368f3872407c8.js new file mode 100644 index 00000000..987fae42 --- /dev/null +++ b/docs/windows_docs/static.files/main-48f368f3872407c8.js @@ -0,0 +1,11 @@ +"use strict";window.RUSTDOC_TOOLTIP_HOVER_MS=300;window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS=450;function resourcePath(basename,extension){return getVar("root-path")+basename+getVar("resource-suffix")+extension}function hideMain(){addClass(document.getElementById(MAIN_ID),"hidden")}function showMain(){removeClass(document.getElementById(MAIN_ID),"hidden")}function blurHandler(event,parentElem,hideCallback){if(!parentElem.contains(document.activeElement)&&!parentElem.contains(event.relatedTarget)){hideCallback()}}window.rootPath=getVar("root-path");window.currentCrate=getVar("current-crate");function setMobileTopbar(){const mobileTopbar=document.querySelector(".mobile-topbar");const locationTitle=document.querySelector(".sidebar h2.location");if(mobileTopbar){const mobileTitle=document.createElement("h2");mobileTitle.className="location";if(hasClass(document.querySelector(".rustdoc"),"crate")){mobileTitle.innerText=`Crate ${window.currentCrate}`}else if(locationTitle){mobileTitle.innerHTML=locationTitle.innerHTML}mobileTopbar.appendChild(mobileTitle)}}function getVirtualKey(ev){if("key"in ev&&typeof ev.key!=="undefined"){return ev.key}const c=ev.charCode||ev.keyCode;if(c===27){return"Escape"}return String.fromCharCode(c)}const MAIN_ID="main-content";const SETTINGS_BUTTON_ID="settings-menu";const ALTERNATIVE_DISPLAY_ID="alternative-display";const NOT_DISPLAYED_ID="not-displayed";const HELP_BUTTON_ID="help-button";function getSettingsButton(){return document.getElementById(SETTINGS_BUTTON_ID)}function getHelpButton(){return document.getElementById(HELP_BUTTON_ID)}function getNakedUrl(){return window.location.href.split("?")[0].split("#")[0]}function insertAfter(newNode,referenceNode){referenceNode.parentNode.insertBefore(newNode,referenceNode.nextSibling)}function getOrCreateSection(id,classes){let el=document.getElementById(id);if(!el){el=document.createElement("section");el.id=id;el.className=classes;insertAfter(el,document.getElementById(MAIN_ID))}return el}function getAlternativeDisplayElem(){return getOrCreateSection(ALTERNATIVE_DISPLAY_ID,"content hidden")}function getNotDisplayedElem(){return getOrCreateSection(NOT_DISPLAYED_ID,"hidden")}function switchDisplayedElement(elemToDisplay){const el=getAlternativeDisplayElem();if(el.children.length>0){getNotDisplayedElem().appendChild(el.firstElementChild)}if(elemToDisplay===null){addClass(el,"hidden");showMain();return}el.appendChild(elemToDisplay);hideMain();removeClass(el,"hidden")}function browserSupportsHistoryApi(){return window.history&&typeof window.history.pushState==="function"}function preLoadCss(cssUrl){const link=document.createElement("link");link.href=cssUrl;link.rel="preload";link.as="style";document.getElementsByTagName("head")[0].appendChild(link)}(function(){const isHelpPage=window.location.pathname.endsWith("/help.html");function loadScript(url){const script=document.createElement("script");script.src=url;document.head.append(script)}getSettingsButton().onclick=event=>{if(event.ctrlKey||event.altKey||event.metaKey){return}window.hideAllModals(false);addClass(getSettingsButton(),"rotate");event.preventDefault();loadScript(getVar("static-root-path")+getVar("settings-js"));setTimeout(()=>{const themes=getVar("themes").split(",");for(const theme of themes){if(theme!==""){preLoadCss(getVar("root-path")+theme+".css")}}},0)};window.searchState={loadingText:"Loading search results...",input:document.getElementsByClassName("search-input")[0],outputElement:()=>{let el=document.getElementById("search");if(!el){el=document.createElement("section");el.id="search";getNotDisplayedElem().appendChild(el)}return el},title:document.title,titleBeforeSearch:document.title,timeout:null,currentTab:0,focusedByTab:[null,null,null],clearInputTimeout:()=>{if(searchState.timeout!==null){clearTimeout(searchState.timeout);searchState.timeout=null}},isDisplayed:()=>searchState.outputElement().parentElement.id===ALTERNATIVE_DISPLAY_ID,focus:()=>{searchState.input.focus()},defocus:()=>{searchState.input.blur()},showResults:search=>{if(search===null||typeof search==="undefined"){search=searchState.outputElement()}switchDisplayedElement(search);searchState.mouseMovedAfterSearch=false;document.title=searchState.title},removeQueryParameters:()=>{document.title=searchState.titleBeforeSearch;if(browserSupportsHistoryApi()){history.replaceState(null,"",getNakedUrl()+window.location.hash)}},hideResults:()=>{switchDisplayedElement(null);searchState.removeQueryParameters()},getQueryStringParams:()=>{const params={};window.location.search.substring(1).split("&").map(s=>{const pair=s.split("=").map(x=>x.replace(/\+/g," "));params[decodeURIComponent(pair[0])]=typeof pair[1]==="undefined"?null:decodeURIComponent(pair[1])});return params},setup:()=>{const search_input=searchState.input;if(!searchState.input){return}let searchLoaded=false;function loadSearch(){if(!searchLoaded){searchLoaded=true;loadScript(getVar("static-root-path")+getVar("search-js"));loadScript(resourcePath("search-index",".js"))}}search_input.addEventListener("focus",()=>{search_input.origPlaceholder=search_input.placeholder;search_input.placeholder="Type your search here.";loadSearch()});if(search_input.value!==""){loadSearch()}const params=searchState.getQueryStringParams();if(params.search!==undefined){searchState.setLoadingSearch();loadSearch()}},setLoadingSearch:()=>{const search=searchState.outputElement();search.innerHTML="

"+searchState.loadingText+"

";searchState.showResults(search)},};const toggleAllDocsId="toggle-all-docs";let savedHash="";function handleHashes(ev){if(ev!==null&&searchState.isDisplayed()&&ev.newURL){switchDisplayedElement(null);const hash=ev.newURL.slice(ev.newURL.indexOf("#")+1);if(browserSupportsHistoryApi()){history.replaceState(null,"",getNakedUrl()+window.location.search+"#"+hash)}const elem=document.getElementById(hash);if(elem){elem.scrollIntoView()}}const pageId=window.location.hash.replace(/^#/,"");if(savedHash!==pageId){savedHash=pageId;if(pageId!==""){expandSection(pageId)}}if(savedHash.startsWith("impl-")){const splitAt=savedHash.indexOf("/");if(splitAt!==-1){const implId=savedHash.slice(0,splitAt);const assocId=savedHash.slice(splitAt+1);const implElem=document.getElementById(implId);if(implElem&&implElem.parentElement.tagName==="SUMMARY"&&implElem.parentElement.parentElement.tagName==="DETAILS"){onEachLazy(implElem.parentElement.parentElement.querySelectorAll(`[id^="${assocId}"]`),item=>{const numbered=/([^-]+)-([0-9]+)/.exec(item.id);if(item.id===assocId||(numbered&&numbered[1]===assocId)){openParentDetails(item);item.scrollIntoView();setTimeout(()=>{window.location.replace("#"+item.id)},0)}})}}}}function onHashChange(ev){hideSidebar();handleHashes(ev)}function openParentDetails(elem){while(elem){if(elem.tagName==="DETAILS"){elem.open=true}elem=elem.parentNode}}function expandSection(id){openParentDetails(document.getElementById(id))}function handleEscape(ev){searchState.clearInputTimeout();searchState.hideResults();ev.preventDefault();searchState.defocus();window.hideAllModals(true)}function handleShortcut(ev){const disableShortcuts=getSettingValue("disable-shortcuts")==="true";if(ev.ctrlKey||ev.altKey||ev.metaKey||disableShortcuts){return}if(document.activeElement.tagName==="INPUT"&&document.activeElement.type!=="checkbox"&&document.activeElement.type!=="radio"){switch(getVirtualKey(ev)){case"Escape":handleEscape(ev);break}}else{switch(getVirtualKey(ev)){case"Escape":handleEscape(ev);break;case"s":case"S":ev.preventDefault();searchState.focus();break;case"+":ev.preventDefault();expandAllDocs();break;case"-":ev.preventDefault();collapseAllDocs();break;case"?":showHelp();break;default:break}}}document.addEventListener("keypress",handleShortcut);document.addEventListener("keydown",handleShortcut);function addSidebarItems(){if(!window.SIDEBAR_ITEMS){return}const sidebar=document.getElementsByClassName("sidebar-elems")[0];function block(shortty,id,longty){const filtered=window.SIDEBAR_ITEMS[shortty];if(!filtered){return}const modpath=hasClass(document.querySelector(".rustdoc"),"mod")?"../":"";const h3=document.createElement("h3");h3.innerHTML=`${longty}`;const ul=document.createElement("ul");ul.className="block "+shortty;for(const name of filtered){let path;if(shortty==="mod"){path=`${modpath}${name}/index.html`}else{path=`${modpath}${shortty}.${name}.html`}let current_page=document.location.href.toString();if(current_page.endsWith("/")){current_page+="index.html"}const link=document.createElement("a");link.href=path;if(path===current_page){link.className="current"}link.textContent=name;const li=document.createElement("li");li.appendChild(link);ul.appendChild(li)}sidebar.appendChild(h3);sidebar.appendChild(ul)}if(sidebar){block("primitive","primitives","Primitive Types");block("mod","modules","Modules");block("macro","macros","Macros");block("struct","structs","Structs");block("enum","enums","Enums");block("constant","constants","Constants");block("static","static","Statics");block("trait","traits","Traits");block("fn","functions","Functions");block("type","types","Type Aliases");block("union","unions","Unions");block("foreigntype","foreign-types","Foreign Types");block("keyword","keywords","Keywords");block("opaque","opaque-types","Opaque Types");block("attr","attributes","Attribute Macros");block("derive","derives","Derive Macros");block("traitalias","trait-aliases","Trait Aliases")}}window.register_implementors=imp=>{const implementors=document.getElementById("implementors-list");const synthetic_implementors=document.getElementById("synthetic-implementors-list");const inlined_types=new Set();const TEXT_IDX=0;const SYNTHETIC_IDX=1;const TYPES_IDX=2;if(synthetic_implementors){onEachLazy(synthetic_implementors.getElementsByClassName("impl"),el=>{const aliases=el.getAttribute("data-aliases");if(!aliases){return}aliases.split(",").forEach(alias=>{inlined_types.add(alias)})})}let currentNbImpls=implementors.getElementsByClassName("impl").length;const traitName=document.querySelector(".main-heading h1 > .trait").textContent;const baseIdName="impl-"+traitName+"-";const libs=Object.getOwnPropertyNames(imp);const script=document.querySelector("script[data-ignore-extern-crates]");const ignoreExternCrates=new Set((script?script.getAttribute("data-ignore-extern-crates"):"").split(","));for(const lib of libs){if(lib===window.currentCrate||ignoreExternCrates.has(lib)){continue}const structs=imp[lib];struct_loop:for(const struct of structs){const list=struct[SYNTHETIC_IDX]?synthetic_implementors:implementors;if(struct[SYNTHETIC_IDX]){for(const struct_type of struct[TYPES_IDX]){if(inlined_types.has(struct_type)){continue struct_loop}inlined_types.add(struct_type)}}const code=document.createElement("h3");code.innerHTML=struct[TEXT_IDX];addClass(code,"code-header");onEachLazy(code.getElementsByTagName("a"),elem=>{const href=elem.getAttribute("href");if(href&&!href.startsWith("#")&&!/^(?:[a-z+]+:)?\/\//.test(href)){elem.setAttribute("href",window.rootPath+href)}});const currentId=baseIdName+currentNbImpls;const anchor=document.createElement("a");anchor.href="#"+currentId;addClass(anchor,"anchor");const display=document.createElement("div");display.id=currentId;addClass(display,"impl");display.appendChild(anchor);display.appendChild(code);list.appendChild(display);currentNbImpls+=1}}};if(window.pending_implementors){window.register_implementors(window.pending_implementors)}window.register_type_impls=imp=>{if(!imp||!imp[window.currentCrate]){return}window.pending_type_impls=null;const idMap=new Map();let implementations=document.getElementById("implementations-list");let trait_implementations=document.getElementById("trait-implementations-list");let trait_implementations_header=document.getElementById("trait-implementations");const script=document.querySelector("script[data-self-path]");const selfPath=script?script.getAttribute("data-self-path"):null;const mainContent=document.querySelector("#main-content");const sidebarSection=document.querySelector(".sidebar section");let methods=document.querySelector(".sidebar .block.method");let associatedTypes=document.querySelector(".sidebar .block.associatedtype");let associatedConstants=document.querySelector(".sidebar .block.associatedconstant");let sidebarTraitList=document.querySelector(".sidebar .block.trait-implementation");for(const impList of imp[window.currentCrate]){const types=impList.slice(2);const text=impList[0];const isTrait=impList[1]!==0;const traitName=impList[1];if(types.indexOf(selfPath)===-1){continue}let outputList=isTrait?trait_implementations:implementations;if(outputList===null){const outputListName=isTrait?"Trait Implementations":"Implementations";const outputListId=isTrait?"trait-implementations-list":"implementations-list";const outputListHeaderId=isTrait?"trait-implementations":"implementations";const outputListHeader=document.createElement("h2");outputListHeader.id=outputListHeaderId;outputListHeader.innerText=outputListName;outputList=document.createElement("div");outputList.id=outputListId;if(isTrait){const link=document.createElement("a");link.href=`#${outputListHeaderId}`;link.innerText="Trait Implementations";const h=document.createElement("h3");h.appendChild(link);trait_implementations=outputList;trait_implementations_header=outputListHeader;sidebarSection.appendChild(h);sidebarTraitList=document.createElement("ul");sidebarTraitList.className="block trait-implementation";sidebarSection.appendChild(sidebarTraitList);mainContent.appendChild(outputListHeader);mainContent.appendChild(outputList)}else{implementations=outputList;if(trait_implementations){mainContent.insertBefore(outputListHeader,trait_implementations_header);mainContent.insertBefore(outputList,trait_implementations_header)}else{const mainContent=document.querySelector("#main-content");mainContent.appendChild(outputListHeader);mainContent.appendChild(outputList)}}}const template=document.createElement("template");template.innerHTML=text;onEachLazy(template.content.querySelectorAll("a"),elem=>{const href=elem.getAttribute("href");if(href&&!href.startsWith("#")&&!/^(?:[a-z+]+:)?\/\//.test(href)){elem.setAttribute("href",window.rootPath+href)}});onEachLazy(template.content.querySelectorAll("[id]"),el=>{let i=0;if(idMap.has(el.id)){i=idMap.get(el.id)}else if(document.getElementById(el.id)){i=1;while(document.getElementById(`${el.id}-${2 * i}`)){i=2*i}while(document.getElementById(`${el.id}-${i}`)){i+=1}}if(i!==0){const oldHref=`#${el.id}`;const newHref=`#${el.id}-${i}`;el.id=`${el.id}-${i}`;onEachLazy(template.content.querySelectorAll("a[href]"),link=>{if(link.getAttribute("href")===oldHref){link.href=newHref}})}idMap.set(el.id,i+1)});const templateAssocItems=template.content.querySelectorAll("section.tymethod, "+"section.method, section.associatedtype, section.associatedconstant");if(isTrait){const li=document.createElement("li");const a=document.createElement("a");a.href=`#${template.content.querySelector(".impl").id}`;a.textContent=traitName;li.appendChild(a);sidebarTraitList.append(li)}else{onEachLazy(templateAssocItems,item=>{let block=hasClass(item,"associatedtype")?associatedTypes:(hasClass(item,"associatedconstant")?associatedConstants:(methods));if(!block){const blockTitle=hasClass(item,"associatedtype")?"Associated Types":(hasClass(item,"associatedconstant")?"Associated Constants":("Methods"));const blockClass=hasClass(item,"associatedtype")?"associatedtype":(hasClass(item,"associatedconstant")?"associatedconstant":("method"));const blockHeader=document.createElement("h3");const blockLink=document.createElement("a");blockLink.href="#implementations";blockLink.innerText=blockTitle;blockHeader.appendChild(blockLink);block=document.createElement("ul");block.className=`block ${blockClass}`;const insertionReference=methods||sidebarTraitList;if(insertionReference){const insertionReferenceH=insertionReference.previousElementSibling;sidebarSection.insertBefore(blockHeader,insertionReferenceH);sidebarSection.insertBefore(block,insertionReferenceH)}else{sidebarSection.appendChild(blockHeader);sidebarSection.appendChild(block)}if(hasClass(item,"associatedtype")){associatedTypes=block}else if(hasClass(item,"associatedconstant")){associatedConstants=block}else{methods=block}}const li=document.createElement("li");const a=document.createElement("a");a.innerText=item.id.split("-")[0].split(".")[1];a.href=`#${item.id}`;li.appendChild(a);block.appendChild(li)})}outputList.appendChild(template.content)}for(const list of[methods,associatedTypes,associatedConstants,sidebarTraitList]){if(!list){continue}const newChildren=Array.prototype.slice.call(list.children);newChildren.sort((a,b)=>{const aI=a.innerText;const bI=b.innerText;return aIbI?1:0});list.replaceChildren(...newChildren)}};if(window.pending_type_impls){window.register_type_impls(window.pending_type_impls)}function addSidebarCrates(){if(!window.ALL_CRATES){return}const sidebarElems=document.getElementsByClassName("sidebar-elems")[0];if(!sidebarElems){return}const h3=document.createElement("h3");h3.innerHTML="Crates";const ul=document.createElement("ul");ul.className="block crate";for(const crate of window.ALL_CRATES){const link=document.createElement("a");link.href=window.rootPath+crate+"/index.html";link.textContent=crate;const li=document.createElement("li");if(window.rootPath!=="./"&&crate===window.currentCrate){li.className="current"}li.appendChild(link);ul.appendChild(li)}sidebarElems.appendChild(h3);sidebarElems.appendChild(ul)}function expandAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);removeClass(innerToggle,"will-expand");onEachLazy(document.getElementsByClassName("toggle"),e=>{if(!hasClass(e,"type-contents-toggle")&&!hasClass(e,"more-examples-toggle")){e.open=true}});innerToggle.title="collapse all docs";innerToggle.children[0].innerText="\u2212"}function collapseAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);addClass(innerToggle,"will-expand");onEachLazy(document.getElementsByClassName("toggle"),e=>{if(e.parentNode.id!=="implementations-list"||(!hasClass(e,"implementors-toggle")&&!hasClass(e,"type-contents-toggle"))){e.open=false}});innerToggle.title="expand all docs";innerToggle.children[0].innerText="+"}function toggleAllDocs(){const innerToggle=document.getElementById(toggleAllDocsId);if(!innerToggle){return}if(hasClass(innerToggle,"will-expand")){expandAllDocs()}else{collapseAllDocs()}}(function(){const toggles=document.getElementById(toggleAllDocsId);if(toggles){toggles.onclick=toggleAllDocs}const hideMethodDocs=getSettingValue("auto-hide-method-docs")==="true";const hideImplementations=getSettingValue("auto-hide-trait-implementations")==="true";const hideLargeItemContents=getSettingValue("auto-hide-large-items")!=="false";function setImplementorsTogglesOpen(id,open){const list=document.getElementById(id);if(list!==null){onEachLazy(list.getElementsByClassName("implementors-toggle"),e=>{e.open=open})}}if(hideImplementations){setImplementorsTogglesOpen("trait-implementations-list",false);setImplementorsTogglesOpen("blanket-implementations-list",false)}onEachLazy(document.getElementsByClassName("toggle"),e=>{if(!hideLargeItemContents&&hasClass(e,"type-contents-toggle")){e.open=true}if(hideMethodDocs&&hasClass(e,"method-toggle")){e.open=false}})}());window.rustdoc_add_line_numbers_to_examples=()=>{onEachLazy(document.getElementsByClassName("rust-example-rendered"),x=>{const parent=x.parentNode;const line_numbers=parent.querySelectorAll(".example-line-numbers");if(line_numbers.length>0){return}const count=x.textContent.split("\n").length;const elems=[];for(let i=0;i{onEachLazy(document.getElementsByClassName("rust-example-rendered"),x=>{const parent=x.parentNode;const line_numbers=parent.querySelectorAll(".example-line-numbers");for(const node of line_numbers){parent.removeChild(node)}})};if(getSettingValue("line-numbers")==="true"){window.rustdoc_add_line_numbers_to_examples()}function showSidebar(){window.hideAllModals(false);const sidebar=document.getElementsByClassName("sidebar")[0];addClass(sidebar,"shown")}function hideSidebar(){const sidebar=document.getElementsByClassName("sidebar")[0];removeClass(sidebar,"shown")}window.addEventListener("resize",()=>{if(window.CURRENT_TOOLTIP_ELEMENT){const base=window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE;const force_visible=base.TOOLTIP_FORCE_VISIBLE;hideTooltip(false);if(force_visible){showTooltip(base);base.TOOLTIP_FORCE_VISIBLE=true}}});const mainElem=document.getElementById(MAIN_ID);if(mainElem){mainElem.addEventListener("click",hideSidebar)}onEachLazy(document.querySelectorAll("a[href^='#']"),el=>{el.addEventListener("click",()=>{expandSection(el.hash.slice(1));hideSidebar()})});onEachLazy(document.querySelectorAll(".toggle > summary:not(.hideme)"),el=>{el.addEventListener("click",e=>{if(e.target.tagName!=="SUMMARY"&&e.target.tagName!=="A"){e.preventDefault()}})});function showTooltip(e){const notable_ty=e.getAttribute("data-notable-ty");if(!window.NOTABLE_TRAITS&¬able_ty){const data=document.getElementById("notable-traits-data");if(data){window.NOTABLE_TRAITS=JSON.parse(data.innerText)}else{throw new Error("showTooltip() called with notable without any notable traits!")}}if(window.CURRENT_TOOLTIP_ELEMENT&&window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE===e){clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);return}window.hideAllModals(false);const wrapper=document.createElement("div");if(notable_ty){wrapper.innerHTML="
"+window.NOTABLE_TRAITS[notable_ty]+"
"}else{if(e.getAttribute("title")!==null){e.setAttribute("data-title",e.getAttribute("title"));e.removeAttribute("title")}if(e.getAttribute("data-title")!==null){const titleContent=document.createElement("div");titleContent.className="content";titleContent.appendChild(document.createTextNode(e.getAttribute("data-title")));wrapper.appendChild(titleContent)}}wrapper.className="tooltip popover";const focusCatcher=document.createElement("div");focusCatcher.setAttribute("tabindex","0");focusCatcher.onfocus=hideTooltip;wrapper.appendChild(focusCatcher);const pos=e.getBoundingClientRect();wrapper.style.top=(pos.top+window.scrollY+pos.height)+"px";wrapper.style.left=0;wrapper.style.right="auto";wrapper.style.visibility="hidden";const body=document.getElementsByTagName("body")[0];body.appendChild(wrapper);const wrapperPos=wrapper.getBoundingClientRect();const finalPos=pos.left+window.scrollX-wrapperPos.width+24;if(finalPos>0){wrapper.style.left=finalPos+"px"}else{wrapper.style.setProperty("--popover-arrow-offset",(wrapperPos.right-pos.right+4)+"px")}wrapper.style.visibility="";window.CURRENT_TOOLTIP_ELEMENT=wrapper;window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE=e;clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);wrapper.onpointerenter=ev=>{if(ev.pointerType!=="mouse"){return}clearTooltipHoverTimeout(e)};wrapper.onpointerleave=ev=>{if(ev.pointerType!=="mouse"){return}if(!e.TOOLTIP_FORCE_VISIBLE&&!e.contains(ev.relatedTarget)){setTooltipHoverTimeout(e,false);addClass(wrapper,"fade-out")}}}function setTooltipHoverTimeout(element,show){clearTooltipHoverTimeout(element);if(!show&&!window.CURRENT_TOOLTIP_ELEMENT){return}if(show&&window.CURRENT_TOOLTIP_ELEMENT){return}if(window.CURRENT_TOOLTIP_ELEMENT&&window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE!==element){return}element.TOOLTIP_HOVER_TIMEOUT=setTimeout(()=>{if(show){showTooltip(element)}else if(!element.TOOLTIP_FORCE_VISIBLE){hideTooltip(false)}},show?window.RUSTDOC_TOOLTIP_HOVER_MS:window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS)}function clearTooltipHoverTimeout(element){if(element.TOOLTIP_HOVER_TIMEOUT!==undefined){removeClass(window.CURRENT_TOOLTIP_ELEMENT,"fade-out");clearTimeout(element.TOOLTIP_HOVER_TIMEOUT);delete element.TOOLTIP_HOVER_TIMEOUT}}function tooltipBlurHandler(event){if(window.CURRENT_TOOLTIP_ELEMENT&&!window.CURRENT_TOOLTIP_ELEMENT.contains(document.activeElement)&&!window.CURRENT_TOOLTIP_ELEMENT.contains(event.relatedTarget)&&!window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.contains(document.activeElement)&&!window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.contains(event.relatedTarget)){setTimeout(()=>hideTooltip(false),0)}}function hideTooltip(focus){if(window.CURRENT_TOOLTIP_ELEMENT){if(window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE){if(focus){window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.focus()}window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE=false}const body=document.getElementsByTagName("body")[0];body.removeChild(window.CURRENT_TOOLTIP_ELEMENT);clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT);window.CURRENT_TOOLTIP_ELEMENT=null}}onEachLazy(document.getElementsByClassName("tooltip"),e=>{e.onclick=()=>{e.TOOLTIP_FORCE_VISIBLE=e.TOOLTIP_FORCE_VISIBLE?false:true;if(window.CURRENT_TOOLTIP_ELEMENT&&!e.TOOLTIP_FORCE_VISIBLE){hideTooltip(true)}else{showTooltip(e);window.CURRENT_TOOLTIP_ELEMENT.setAttribute("tabindex","0");window.CURRENT_TOOLTIP_ELEMENT.focus();window.CURRENT_TOOLTIP_ELEMENT.onblur=tooltipBlurHandler}return false};e.onpointerenter=ev=>{if(ev.pointerType!=="mouse"){return}setTooltipHoverTimeout(e,true)};e.onpointermove=ev=>{if(ev.pointerType!=="mouse"){return}setTooltipHoverTimeout(e,true)};e.onpointerleave=ev=>{if(ev.pointerType!=="mouse"){return}if(!e.TOOLTIP_FORCE_VISIBLE&&window.CURRENT_TOOLTIP_ELEMENT&&!window.CURRENT_TOOLTIP_ELEMENT.contains(ev.relatedTarget)){setTooltipHoverTimeout(e,false);addClass(window.CURRENT_TOOLTIP_ELEMENT,"fade-out")}}});const sidebar_menu_toggle=document.getElementsByClassName("sidebar-menu-toggle")[0];if(sidebar_menu_toggle){sidebar_menu_toggle.addEventListener("click",()=>{const sidebar=document.getElementsByClassName("sidebar")[0];if(!hasClass(sidebar,"shown")){showSidebar()}else{hideSidebar()}})}function helpBlurHandler(event){blurHandler(event,getHelpButton(),window.hidePopoverMenus)}function buildHelpMenu(){const book_info=document.createElement("span");const channel=getVar("channel");book_info.className="top";book_info.innerHTML=`You can find more information in \ +the rustdoc book.`;const shortcuts=[["?","Show this help dialog"],["S","Focus the search field"],["↑","Move up in search results"],["↓","Move down in search results"],["← / →","Switch result tab (when results focused)"],["⏎","Go to active search result"],["+","Expand all sections"],["-","Collapse all sections"],].map(x=>"
"+x[0].split(" ").map((y,index)=>((index&1)===0?""+y+"":" "+y+" ")).join("")+"
"+x[1]+"
").join("");const div_shortcuts=document.createElement("div");addClass(div_shortcuts,"shortcuts");div_shortcuts.innerHTML="

Keyboard Shortcuts

"+shortcuts+"
";const infos=[`For a full list of all search features, take a look here.`,"Prefix searches with a type followed by a colon (e.g., fn:) to \ + restrict the search to a given item kind.","Accepted kinds are: fn, mod, struct, \ + enum, trait, type, macro, \ + and const.","Search functions by type signature (e.g., vec -> usize or \ + -> vec or String, enum:Cow -> bool)","You can look for items with an exact name by putting double quotes around \ + your request: \"string\"","Look for functions that accept or return \ + slices and \ + arrays by writing \ + square brackets (e.g., -> [u8] or [] -> Option)","Look for items inside another one by searching for a path: vec::Vec",].map(x=>"

"+x+"

").join("");const div_infos=document.createElement("div");addClass(div_infos,"infos");div_infos.innerHTML="

Search Tricks

"+infos;const rustdoc_version=document.createElement("span");rustdoc_version.className="bottom";const rustdoc_version_code=document.createElement("code");rustdoc_version_code.innerText="rustdoc "+getVar("rustdoc-version");rustdoc_version.appendChild(rustdoc_version_code);const container=document.createElement("div");if(!isHelpPage){container.className="popover"}container.id="help";container.style.display="none";const side_by_side=document.createElement("div");side_by_side.className="side-by-side";side_by_side.appendChild(div_shortcuts);side_by_side.appendChild(div_infos);container.appendChild(book_info);container.appendChild(side_by_side);container.appendChild(rustdoc_version);if(isHelpPage){const help_section=document.createElement("section");help_section.appendChild(container);document.getElementById("main-content").appendChild(help_section);container.style.display="block"}else{const help_button=getHelpButton();help_button.appendChild(container);container.onblur=helpBlurHandler;help_button.onblur=helpBlurHandler;help_button.children[0].onblur=helpBlurHandler}return container}window.hideAllModals=switchFocus=>{hideSidebar();window.hidePopoverMenus();hideTooltip(switchFocus)};window.hidePopoverMenus=()=>{onEachLazy(document.querySelectorAll(".search-form .popover"),elem=>{elem.style.display="none"})};function getHelpMenu(buildNeeded){let menu=getHelpButton().querySelector(".popover");if(!menu&&buildNeeded){menu=buildHelpMenu()}return menu}function showHelp(){getHelpButton().querySelector("a").focus();const menu=getHelpMenu(true);if(menu.style.display==="none"){window.hideAllModals();menu.style.display=""}}if(isHelpPage){showHelp();document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click",event=>{const target=event.target;if(target.tagName!=="A"||target.parentElement.id!==HELP_BUTTON_ID||event.ctrlKey||event.altKey||event.metaKey){return}event.preventDefault()})}else{document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click",event=>{const target=event.target;if(target.tagName!=="A"||target.parentElement.id!==HELP_BUTTON_ID||event.ctrlKey||event.altKey||event.metaKey){return}event.preventDefault();const menu=getHelpMenu(true);const shouldShowHelp=menu.style.display==="none";if(shouldShowHelp){showHelp()}else{window.hidePopoverMenus()}})}setMobileTopbar();addSidebarItems();addSidebarCrates();onHashChange(null);window.addEventListener("hashchange",onHashChange);searchState.setup()}());(function(){const SIDEBAR_MIN=100;const SIDEBAR_MAX=500;const RUSTDOC_MOBILE_BREAKPOINT=700;const BODY_MIN=400;const SIDEBAR_VANISH_THRESHOLD=SIDEBAR_MIN/2;const sidebarButton=document.getElementById("sidebar-button");if(sidebarButton){sidebarButton.addEventListener("click",e=>{removeClass(document.documentElement,"hide-sidebar");updateLocalStorage("hide-sidebar","false");if(document.querySelector(".rustdoc.src")){window.rustdocToggleSrcSidebar()}e.preventDefault()})}let currentPointerId=null;let desiredSidebarSize=null;let pendingSidebarResizingFrame=false;const resizer=document.querySelector(".sidebar-resizer");const sidebar=document.querySelector(".sidebar");if(!resizer||!sidebar){return}const isSrcPage=hasClass(document.body,"src");function hideSidebar(){if(isSrcPage){window.rustdocCloseSourceSidebar();updateLocalStorage("src-sidebar-width",null);document.documentElement.style.removeProperty("--src-sidebar-width");sidebar.style.removeProperty("--src-sidebar-width");resizer.style.removeProperty("--src-sidebar-width")}else{addClass(document.documentElement,"hide-sidebar");updateLocalStorage("hide-sidebar","true");updateLocalStorage("desktop-sidebar-width",null);document.documentElement.style.removeProperty("--desktop-sidebar-width");sidebar.style.removeProperty("--desktop-sidebar-width");resizer.style.removeProperty("--desktop-sidebar-width")}}function showSidebar(){if(isSrcPage){window.rustdocShowSourceSidebar()}else{removeClass(document.documentElement,"hide-sidebar");updateLocalStorage("hide-sidebar","false")}}function changeSidebarSize(size){if(isSrcPage){updateLocalStorage("src-sidebar-width",size);sidebar.style.setProperty("--src-sidebar-width",size+"px");resizer.style.setProperty("--src-sidebar-width",size+"px")}else{updateLocalStorage("desktop-sidebar-width",size);sidebar.style.setProperty("--desktop-sidebar-width",size+"px");resizer.style.setProperty("--desktop-sidebar-width",size+"px")}}function isSidebarHidden(){return isSrcPage?!hasClass(document.documentElement,"src-sidebar-expanded"):hasClass(document.documentElement,"hide-sidebar")}function resize(e){if(currentPointerId===null||currentPointerId!==e.pointerId){return}e.preventDefault();const pos=e.clientX-3;if(pos=SIDEBAR_MIN){if(isSidebarHidden()){showSidebar()}const constrainedPos=Math.min(pos,window.innerWidth-BODY_MIN,SIDEBAR_MAX);changeSidebarSize(constrainedPos);desiredSidebarSize=constrainedPos;if(pendingSidebarResizingFrame!==false){clearTimeout(pendingSidebarResizingFrame)}pendingSidebarResizingFrame=setTimeout(()=>{if(currentPointerId===null||pendingSidebarResizingFrame===false){return}pendingSidebarResizingFrame=false;document.documentElement.style.setProperty("--resizing-sidebar-width",desiredSidebarSize+"px")},100)}}window.addEventListener("resize",()=>{if(window.innerWidth=(window.innerWidth-BODY_MIN)){changeSidebarSize(window.innerWidth-BODY_MIN)}else if(desiredSidebarSize!==null&&desiredSidebarSize>SIDEBAR_MIN){changeSidebarSize(desiredSidebarSize)}});function stopResize(e){if(currentPointerId===null){return}if(e){e.preventDefault()}desiredSidebarSize=sidebar.getBoundingClientRect().width;removeClass(resizer,"active");window.removeEventListener("pointermove",resize,false);window.removeEventListener("pointerup",stopResize,false);removeClass(document.documentElement,"sidebar-resizing");document.documentElement.style.removeProperty("--resizing-sidebar-width");if(resizer.releasePointerCapture){resizer.releasePointerCapture(currentPointerId);currentPointerId=null}}function initResize(e){if(currentPointerId!==null||e.altKey||e.ctrlKey||e.metaKey||e.button!==0){return}if(resizer.setPointerCapture){resizer.setPointerCapture(e.pointerId);if(!resizer.hasPointerCapture(e.pointerId)){resizer.releasePointerCapture(e.pointerId);return}currentPointerId=e.pointerId}window.hideAllModals(false);e.preventDefault();window.addEventListener("pointermove",resize,false);window.addEventListener("pointercancel",stopResize,false);window.addEventListener("pointerup",stopResize,false);addClass(resizer,"active");addClass(document.documentElement,"sidebar-resizing");const pos=e.clientX-sidebar.offsetLeft-3;document.documentElement.style.setProperty("--resizing-sidebar-width",pos+"px");desiredSidebarSize=null}resizer.addEventListener("pointerdown",initResize,false)}());(function(){let reset_button_timeout=null;const but=document.getElementById("copy-path");if(!but){return}but.onclick=()=>{const parent=but.parentElement;const path=[];onEach(parent.childNodes,child=>{if(child.tagName==="A"){path.push(child.textContent)}});const el=document.createElement("textarea");el.value=path.join("::");el.setAttribute("readonly","");el.style.position="absolute";el.style.left="-9999px";document.body.appendChild(el);el.select();document.execCommand("copy");document.body.removeChild(el);but.children[0].style.display="none";let tmp;if(but.childNodes.length<2){tmp=document.createTextNode("✓");but.appendChild(tmp)}else{onEachLazy(but.childNodes,e=>{if(e.nodeType===Node.TEXT_NODE){tmp=e;return true}});tmp.textContent="✓"}if(reset_button_timeout!==null){window.clearTimeout(reset_button_timeout)}function reset_button(){tmp.textContent="";reset_button_timeout=null;but.children[0].style.display=""}reset_button_timeout=window.setTimeout(reset_button,1000)}}()) \ No newline at end of file diff --git a/docs/windows_docs/static.files/normalize-76eba96aa4d2e634.css b/docs/windows_docs/static.files/normalize-76eba96aa4d2e634.css new file mode 100644 index 00000000..469959f1 --- /dev/null +++ b/docs/windows_docs/static.files/normalize-76eba96aa4d2e634.css @@ -0,0 +1,2 @@ + /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ +html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:0.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type="button"],[type="reset"],[type="submit"],button{-webkit-appearance:button}[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:0.35em 0.75em 0.625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none} \ No newline at end of file diff --git a/docs/windows_docs/static.files/noscript-04d5337699b92874.css b/docs/windows_docs/static.files/noscript-04d5337699b92874.css new file mode 100644 index 00000000..fbd55f57 --- /dev/null +++ b/docs/windows_docs/static.files/noscript-04d5337699b92874.css @@ -0,0 +1 @@ + #main-content .attributes{margin-left:0 !important;}#copy-path,#sidebar-button,.sidebar-resizer{display:none !important;}nav.sub{display:none;}.src .sidebar{display:none;}.notable-traits{display:none;}:root{--main-background-color:white;--main-color:black;--settings-input-color:#2196f3;--settings-input-border-color:#717171;--settings-button-color:#000;--settings-button-border-focus:#717171;--sidebar-background-color:#f5f5f5;--sidebar-background-color-hover:#e0e0e0;--code-block-background-color:#f5f5f5;--scrollbar-track-background-color:#dcdcdc;--scrollbar-thumb-background-color:rgba(36,37,39,0.6);--scrollbar-color:rgba(36,37,39,0.6) #d9d9d9;--headings-border-bottom-color:#ddd;--border-color:#e0e0e0;--button-background-color:#fff;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:none;--mobile-sidebar-menu-filter:none;--search-input-focused-border-color:#66afe9;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(35%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ad378a;--trait-link-color:#6e4fc9;--assoc-item-link-color:#3873ad;--function-link-color:#ad7c37;--macro-link-color:#068000;--keyword-link-color:#3873ad;--mod-link-color:#3873ad;--link-color:#3873ad;--sidebar-link-color:#356da4;--sidebar-current-link-background-color:#fff;--search-result-link-focus-background-color:#ccc;--search-result-border-color:#aaa3;--search-color:#000;--search-error-code-background-color:#d0cccc;--search-results-alias-color:#000;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#e6e6e6;--search-tab-button-not-selected-background:#e6e6e6;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#fff;--stab-background-color:#fff5d6;--stab-code-color:#000;--code-highlight-kw-color:#8959a8;--code-highlight-kw-2-color:#4271ae;--code-highlight-lifetime-color:#b76514;--code-highlight-prelude-color:#4271ae;--code-highlight-prelude-val-color:#c82829;--code-highlight-number-color:#718c00;--code-highlight-string-color:#718c00;--code-highlight-literal-color:#c82829;--code-highlight-attribute-color:#c82829;--code-highlight-self-color:#c82829;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8e908c;--code-highlight-doc-comment-color:#4d4d4c;--src-line-numbers-span-color:#c67e2d;--src-line-number-highlighted-background-color:#fdffd3;--test-arrow-color:#f5f5f5;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#f5f5f5;--test-arrow-hover-background-color:rgb(78,139,202);--target-background-color:#fdffd3;--target-border-color:#ad7c37;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:initial;--crate-search-div-filter:invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) brightness(114%) contrast(76%);--crate-search-div-hover-filter:invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) brightness(96%) contrast(93%);--crate-search-hover-border:#717171;--src-sidebar-background-selected:#fff;--src-sidebar-background-hover:#e0e0e0;--table-alt-row-background-color:#f5f5f5;--codeblock-link-background:#eee;--scrape-example-toggle-line-background:#ccc;--scrape-example-toggle-line-hover-background:#999;--scrape-example-code-line-highlight:#fcffd6;--scrape-example-code-line-highlight-focus:#f6fdb0;--scrape-example-help-border-color:#555;--scrape-example-help-color:#333;--scrape-example-help-hover-border-color:#000;--scrape-example-help-hover-color:#000;--scrape-example-code-wrapper-background-start:rgba(255,255,255,1);--scrape-example-code-wrapper-background-end:rgba(255,255,255,0);--sidebar-resizer-hover:hsl(207,90%,66%);--sidebar-resizer-active:hsl(207,90%,54%);}@media (prefers-color-scheme:dark){:root{--main-background-color:#353535;--main-color:#ddd;--settings-input-color:#2196f3;--settings-input-border-color:#999;--settings-button-color:#000;--settings-button-border-focus:#ffb900;--sidebar-background-color:#505050;--sidebar-background-color-hover:#676767;--code-block-background-color:#2A2A2A;--scrollbar-track-background-color:#717171;--scrollbar-thumb-background-color:rgba(32,34,37,.6);--scrollbar-color:rgba(32,34,37,.6) #5a5a5a;--headings-border-bottom-color:#d2d2d2;--border-color:#e0e0e0;--button-background-color:#f0f0f0;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--mobile-sidebar-menu-filter:invert(100%);--search-input-focused-border-color:#008dfd;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(65%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#2dbfb8;--trait-link-color:#b78cf2;--assoc-item-link-color:#d2991d;--function-link-color:#2bab63;--macro-link-color:#09bd00;--keyword-link-color:#d2991d;--mod-link-color:#d2991d;--link-color:#d2991d;--sidebar-link-color:#fdbf35;--sidebar-current-link-background-color:#444;--search-result-link-focus-background-color:#616161;--search-result-border-color:#aaa3;--search-color:#111;--search-error-code-background-color:#484848;--search-results-alias-color:#fff;--search-results-grey-color:#ccc;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#252525;--search-tab-button-not-selected-background:#252525;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#353535;--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ab8ac1;--code-highlight-kw-2-color:#769acb;--code-highlight-lifetime-color:#d97f26;--code-highlight-prelude-color:#769acb;--code-highlight-prelude-val-color:#ee6868;--code-highlight-number-color:#83a300;--code-highlight-string-color:#83a300;--code-highlight-literal-color:#ee6868;--code-highlight-attribute-color:#ee6868;--code-highlight-self-color:#ee6868;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8d8d8b;--code-highlight-doc-comment-color:#8ca375;--src-line-numbers-span-color:#3b91e2;--src-line-number-highlighted-background-color:#0a042f;--test-arrow-color:#dedede;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#dedede;--test-arrow-hover-background-color:#4e8bca;--target-background-color:#494a3d;--target-border-color:#bb7410;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) brightness(90%) contrast(90%);--crate-search-div-hover-filter:invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) brightness(100%) contrast(91%);--crate-search-hover-border:#2196f3;--src-sidebar-background-selected:#333;--src-sidebar-background-hover:#444;--table-alt-row-background-color:#2a2a2a;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(53,53,53,1);--scrape-example-code-wrapper-background-end:rgba(53,53,53,0);--sidebar-resizer-hover:hsl(207,30%,54%);--sidebar-resizer-active:hsl(207,90%,54%);}} \ No newline at end of file diff --git a/docs/windows_docs/static.files/rust-logo-151179464ae7ed46.svg b/docs/windows_docs/static.files/rust-logo-151179464ae7ed46.svg new file mode 100644 index 00000000..62424d8f --- /dev/null +++ b/docs/windows_docs/static.files/rust-logo-151179464ae7ed46.svg @@ -0,0 +1,61 @@ + + + diff --git a/docs/windows_docs/static.files/rustdoc-5bc39a1768837dd0.css b/docs/windows_docs/static.files/rustdoc-5bc39a1768837dd0.css new file mode 100644 index 00000000..175164ef --- /dev/null +++ b/docs/windows_docs/static.files/rustdoc-5bc39a1768837dd0.css @@ -0,0 +1,24 @@ + :root{--nav-sub-mobile-padding:8px;--search-typename-width:6.75rem;--desktop-sidebar-width:200px;--src-sidebar-width:300px;--desktop-sidebar-z-index:100;}@font-face {font-family:'Fira Sans';font-style:normal;font-weight:400;src:local('Fira Sans'),url("FiraSans-Regular-018c141bf0843ffd.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Fira Sans';font-style:normal;font-weight:500;src:local('Fira Sans Medium'),url("FiraSans-Medium-8f9a781e4970d388.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:normal;font-weight:400;src:local('Source Serif 4'),url("SourceSerif4-Regular-46f98efaafac5295.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:italic;font-weight:400;src:local('Source Serif 4 Italic'),url("SourceSerif4-It-acdfaf1a8af734b1.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Serif 4';font-style:normal;font-weight:700;src:local('Source Serif 4 Bold'),url("SourceSerif4-Bold-a2c9cd1067f8b328.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:normal;font-weight:400;src:url("SourceCodePro-Regular-562dcc5011b6de7d.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:italic;font-weight:400;src:url("SourceCodePro-It-1cc31594bf4f1f79.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'Source Code Pro';font-style:normal;font-weight:600;src:url("SourceCodePro-Semibold-d899c5a5c4aeb14a.ttf.woff2") format("woff2");font-display:swap;}@font-face {font-family:'NanumBarunGothic';src:url("NanumBarunGothic-0f09457c7a19b7c6.ttf.woff2") format("woff2");font-display:swap;unicode-range:U+AC00-D7AF,U+1100-11FF,U+3130-318F,U+A960-A97F,U+D7B0-D7FF;}*{box-sizing:border-box;}body{font:1rem/1.5 "Source Serif 4",NanumBarunGothic,serif;margin:0;position:relative;overflow-wrap:break-word;overflow-wrap:anywhere;font-feature-settings:"kern","liga";background-color:var(--main-background-color);color:var(--main-color);}h1{font-size:1.5rem;}h2{font-size:1.375rem;}h3{font-size:1.25rem;}h1,h2,h3,h4,h5,h6{font-weight:500;}h1,h2,h3,h4{margin:25px 0 15px 0;padding-bottom:6px;}.docblock h3,.docblock h4,h5,h6{margin:15px 0 5px 0;}.docblock>h2:first-child,.docblock>h3:first-child,.docblock>h4:first-child,.docblock>h5:first-child,.docblock>h6:first-child{margin-top:0;}.main-heading h1{margin:0;padding:0;flex-grow:1;overflow-wrap:break-word;overflow-wrap:anywhere;}.main-heading{display:flex;flex-wrap:wrap;padding-bottom:6px;margin-bottom:15px;}.content h2,.top-doc .docblock>h3,.top-doc .docblock>h4{border-bottom:1px solid var(--headings-border-bottom-color);}h1,h2{line-height:1.25;padding-top:3px;padding-bottom:9px;}h3.code-header{font-size:1.125rem;}h4.code-header{font-size:1rem;}.code-header{font-weight:600;margin:0;padding:0;white-space:pre-wrap;}#crate-search,h1,h2,h3,h4,h5,h6,.sidebar,.mobile-topbar,.search-input,.search-results .result-name,.item-name>a,.out-of-band,span.since,a.src,#help-button>a,summary.hideme,.scraped-example-list,ul.all-items{font-family:"Fira Sans",Arial,NanumBarunGothic,sans-serif;}#toggle-all-docs,a.anchor,.section-header a,#src-sidebar a,.rust a,.sidebar h2 a,.sidebar h3 a,.mobile-topbar h2 a,h1 a,.search-results a,.stab,.result-name i{color:var(--main-color);}span.enum,a.enum,span.struct,a.struct,span.union,a.union,span.primitive,a.primitive,span.type,a.type,span.foreigntype,a.foreigntype{color:var(--type-link-color);}span.trait,a.trait,span.traitalias,a.traitalias{color:var(--trait-link-color);}span.associatedtype,a.associatedtype,span.constant,a.constant,span.static,a.static{color:var(--assoc-item-link-color);}span.fn,a.fn,span.method,a.method,span.tymethod,a.tymethod{color:var(--function-link-color);}span.attr,a.attr,span.derive,a.derive,span.macro,a.macro{color:var(--macro-link-color);}span.mod,a.mod{color:var(--mod-link-color);}span.keyword,a.keyword{color:var(--keyword-link-color);}a{color:var(--link-color);text-decoration:none;}ol,ul{padding-left:24px;}ul ul,ol ul,ul ol,ol ol{margin-bottom:.625em;}p,.docblock>.warning{margin:0 0 .75em 0;}p:last-child,.docblock>.warning:last-child{margin:0;}button{padding:1px 6px;cursor:pointer;}button#toggle-all-docs{padding:0;background:none;border:none;-webkit-appearance:none;opacity:1;}.rustdoc{display:flex;flex-direction:row;flex-wrap:nowrap;}main{position:relative;flex-grow:1;padding:10px 15px 40px 45px;min-width:0;}.src main{padding:15px;}.width-limiter{max-width:960px;margin-right:auto;}details:not(.toggle) summary{margin-bottom:.6em;}code,pre,a.test-arrow,.code-header{font-family:"Source Code Pro",monospace;}.docblock code,.docblock-short code{border-radius:3px;padding:0 0.125em;}.docblock pre code,.docblock-short pre code{padding:0;}pre{padding:14px;line-height:1.5;}pre.item-decl{overflow-x:auto;}.item-decl .type-contents-toggle{contain:initial;}.src .content pre{padding:20px;}.rustdoc.src .example-wrap pre.src-line-numbers{padding:20px 0 20px 4px;}img{max-width:100%;}.logo-container{line-height:0;display:block;}.rust-logo{filter:var(--rust-logo-filter);}.sidebar{font-size:0.875rem;flex:0 0 var(--desktop-sidebar-width);width:var(--desktop-sidebar-width);overflow-y:scroll;overscroll-behavior:contain;position:sticky;height:100vh;top:0;left:0;z-index:var(--desktop-sidebar-z-index);}.rustdoc.src .sidebar{flex-basis:50px;width:50px;border-right:1px solid;overflow-x:hidden;overflow-y:hidden;}.hide-sidebar .sidebar,.hide-sidebar .sidebar-resizer{display:none;}.sidebar-resizer{touch-action:none;width:9px;cursor:col-resize;z-index:calc(var(--desktop-sidebar-z-index) + 1);position:fixed;height:100%;left:calc(var(--desktop-sidebar-width) + 1px);}.rustdoc.src .sidebar-resizer{left:49px;}.src-sidebar-expanded .src .sidebar-resizer{left:var(--src-sidebar-width);}.sidebar-resizing{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none;}.sidebar-resizing*{cursor:col-resize !important;}.sidebar-resizing .sidebar{position:fixed;}.sidebar-resizing>body{padding-left:var(--resizing-sidebar-width);}.sidebar-resizer:hover,.sidebar-resizer:active,.sidebar-resizer:focus,.sidebar-resizer.active{width:10px;margin:0;left:var(--desktop-sidebar-width);border-left:solid 1px var(--sidebar-resizer-hover);}.src-sidebar-expanded .rustdoc.src .sidebar-resizer:hover,.src-sidebar-expanded .rustdoc.src .sidebar-resizer:active,.src-sidebar-expanded .rustdoc.src .sidebar-resizer:focus,.src-sidebar-expanded .rustdoc.src .sidebar-resizer.active{left:calc(var(--src-sidebar-width) - 1px);}@media (pointer:coarse){.sidebar-resizer{display:none !important;}}.sidebar-resizer.active{padding:0 140px;width:2px;margin-left:-140px;border-left:none;}.sidebar-resizer.active:before{border-left:solid 2px var(--sidebar-resizer-active);display:block;height:100%;content:"";}.sidebar,.mobile-topbar,.sidebar-menu-toggle,#src-sidebar{background-color:var(--sidebar-background-color);}.src .sidebar>*{visibility:hidden;}.src-sidebar-expanded .src .sidebar{overflow-y:auto;flex-basis:var(--src-sidebar-width);width:var(--src-sidebar-width);}.src-sidebar-expanded .src .sidebar>*{visibility:visible;}#all-types{margin-top:1em;}*{scrollbar-width:initial;scrollbar-color:var(--scrollbar-color);}.sidebar{scrollbar-width:thin;scrollbar-color:var(--scrollbar-color);}::-webkit-scrollbar{width:12px;}.sidebar::-webkit-scrollbar{width:8px;}::-webkit-scrollbar-track{-webkit-box-shadow:inset 0;background-color:var(--scrollbar-track-background-color);}.sidebar::-webkit-scrollbar-track{background-color:var(--scrollbar-track-background-color);}::-webkit-scrollbar-thumb,.sidebar::-webkit-scrollbar-thumb{background-color:var(--scrollbar-thumb-background-color);}.hidden{display:none !important;}.logo-container>img{height:48px;width:48px;}ul.block,.block li{padding:0;margin:0;list-style:none;}.sidebar-elems a,.sidebar>h2 a{display:block;padding:0.25rem;margin-left:-0.25rem;margin-right:0.25rem;}.sidebar h2{overflow-wrap:anywhere;padding:0;margin:0.7rem 0;}.sidebar h3{font-size:1.125rem;padding:0;margin:0;}.sidebar-elems,.sidebar>.version,.sidebar>h2{padding-left:24px;}.sidebar a{color:var(--sidebar-link-color);}.sidebar .current,.sidebar .current a,.sidebar-crate a.logo-container:hover+h2 a,.sidebar a:hover:not(.logo-container){background-color:var(--sidebar-current-link-background-color);}.sidebar-elems .block{margin-bottom:2em;}.sidebar-elems .block li a{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;}.sidebar-crate{display:flex;align-items:center;justify-content:center;margin:14px 32px 1rem;row-gap:10px;column-gap:32px;flex-wrap:wrap;}.sidebar-crate h2{flex-grow:1;margin:0 -8px;align-self:start;}.sidebar-crate .logo-container{margin:0 -16px 0 -16px;text-align:center;}.sidebar-crate h2 a{display:block;margin:0 calc(-24px + 0.25rem) 0 -0.2rem;padding:calc((16px - 0.57rem ) / 2 ) 0.25rem;padding-left:0.2rem;}.sidebar-crate h2 .version{display:block;font-weight:normal;font-size:1rem;overflow-wrap:break-word;}.sidebar-crate+.version{margin-top:-1rem;margin-bottom:1rem;}.mobile-topbar{display:none;}.rustdoc .example-wrap{display:flex;position:relative;margin-bottom:10px;}.rustdoc .example-wrap:last-child{margin-bottom:0px;}.rustdoc .example-wrap pre{margin:0;flex-grow:1;}.rustdoc:not(.src) .example-wrap pre{overflow:auto hidden;}.rustdoc .example-wrap pre.example-line-numbers,.rustdoc .example-wrap pre.src-line-numbers{flex-grow:0;min-width:fit-content;overflow:initial;text-align:right;-webkit-user-select:none;user-select:none;padding:14px 8px;color:var(--src-line-numbers-span-color);}.rustdoc .example-wrap pre.src-line-numbers{padding:14px 0;}.src-line-numbers a,.src-line-numbers span{color:var(--src-line-numbers-span-color);padding:0 8px;}.src-line-numbers :target{background-color:transparent;border-right:none;padding:0 8px;}.src-line-numbers .line-highlighted{background-color:var(--src-line-number-highlighted-background-color);}.search-loading{text-align:center;}.docblock-short{overflow-wrap:break-word;overflow-wrap:anywhere;}.docblock :not(pre)>code,.docblock-short code{white-space:pre-wrap;}.top-doc .docblock h2{font-size:1.375rem;}.top-doc .docblock h3{font-size:1.25rem;}.top-doc .docblock h4,.top-doc .docblock h5{font-size:1.125rem;}.top-doc .docblock h6{font-size:1rem;}.docblock h5{font-size:1rem;}.docblock h6{font-size:0.875rem;}.docblock{margin-left:24px;position:relative;}.docblock>:not(.more-examples-toggle):not(.example-wrap){max-width:100%;overflow-x:auto;}.out-of-band{flex-grow:0;font-size:1.125rem;}.docblock code,.docblock-short code,pre,.rustdoc.src .example-wrap{background-color:var(--code-block-background-color);}#main-content{position:relative;}.docblock table{margin:.5em 0;border-collapse:collapse;}.docblock table td,.docblock table th{padding:.5em;border:1px solid var(--border-color);}.docblock table tbody tr:nth-child(2n){background:var(--table-alt-row-background-color);}div.where{white-space:pre-wrap;font-size:0.875rem;}.item-info{display:block;margin-left:24px;}.item-info code{font-size:0.875rem;}#main-content>.item-info{margin-left:0;}nav.sub{flex-grow:1;flex-flow:row nowrap;margin:4px 0 25px 0;display:flex;align-items:center;}.search-form{position:relative;display:flex;height:34px;flex-grow:1;}.src nav.sub{margin:0 0 15px 0;}.section-header{display:block;position:relative;}.section-header:hover>.anchor,.impl:hover>.anchor,.trait-impl:hover>.anchor,.variant:hover>.anchor{display:initial;}.anchor{display:none;position:absolute;left:-0.5em;background:none !important;}.anchor.field{left:-5px;}.section-header>.anchor{left:-15px;padding-right:8px;}h2.section-header>.anchor{padding-right:6px;}a.doc-anchor{color:var(--main-color);display:none;position:absolute;left:-17px;padding-right:5px;padding-left:3px;}*:hover>.doc-anchor{display:block;}.top-doc>.docblock>*:first-child>.doc-anchor{display:none !important;}.main-heading a:hover,.example-wrap .rust a:hover,.all-items a:hover,.docblock a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover:not(.doc-anchor),.docblock-short a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover,.item-info a{text-decoration:underline;}.crate.block li.current a{font-weight:500;}table,.item-table{overflow-wrap:break-word;}.item-table{display:table;padding:0;margin:0;}.item-table>li{display:table-row;}.item-table>li>div{display:table-cell;}.item-table>li>.item-name{padding-right:1.25rem;}.search-results-title{margin-top:0;white-space:nowrap;display:flex;align-items:baseline;}#crate-search-div{position:relative;min-width:5em;}#crate-search{min-width:115px;padding:0 23px 0 4px;max-width:100%;text-overflow:ellipsis;border:1px solid var(--border-color);border-radius:4px;outline:none;cursor:pointer;-moz-appearance:none;-webkit-appearance:none;text-indent:0.01px;background-color:var(--main-background-color);color:inherit;line-height:1.5;font-weight:500;}#crate-search:hover,#crate-search:focus{border-color:var(--crate-search-hover-border);}#crate-search-div::after{pointer-events:none;width:100%;height:100%;position:absolute;top:0;left:0;content:"";background-repeat:no-repeat;background-size:20px;background-position:calc(100% - 2px) 56%;background-image:url('data:image/svg+xml, \ + ');filter:var(--crate-search-div-filter);}#crate-search-div:hover::after,#crate-search-div:focus-within::after{filter:var(--crate-search-div-hover-filter);}#crate-search>option{font-size:1rem;}.search-input{-webkit-appearance:none;outline:none;border:1px solid var(--border-color);border-radius:2px;padding:8px;font-size:1rem;flex-grow:1;background-color:var(--button-background-color);color:var(--search-color);}.search-input:focus{border-color:var(--search-input-focused-border-color);}.search-results{display:none;}.search-results.active{display:block;}.search-results>a{display:flex;margin-left:2px;margin-right:2px;border-bottom:1px solid var(--search-result-border-color);gap:1em;}.search-results>a>div.desc{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;flex:2;}.search-results a:hover,.search-results a:focus{background-color:var(--search-result-link-focus-background-color);}.search-results .result-name{display:flex;align-items:center;justify-content:start;flex:3;}.search-results .result-name .alias{color:var(--search-results-alias-color);}.search-results .result-name .grey{color:var(--search-results-grey-color);}.search-results .result-name .typename{color:var(--search-results-grey-color);font-size:0.875rem;width:var(--search-typename-width);}.search-results .result-name .path{word-break:break-all;max-width:calc(100% - var(--search-typename-width));display:inline-block;}.search-results .result-name .path>*{display:inline;}.popover{position:absolute;top:100%;right:0;z-index:calc(var(--desktop-sidebar-z-index) + 1);margin-top:7px;border-radius:3px;border:1px solid var(--border-color);background-color:var(--main-background-color);color:var(--main-color);--popover-arrow-offset:11px;}.popover::before{content:'';position:absolute;right:var(--popover-arrow-offset);border:solid var(--border-color);border-width:1px 1px 0 0;background-color:var(--main-background-color);padding:4px;transform:rotate(-45deg);top:-5px;}.setting-line{margin:1.2em 0.6em;}.setting-radio input,.setting-check input{margin-right:0.3em;height:1.2rem;width:1.2rem;border:2px solid var(--settings-input-border-color);outline:none;-webkit-appearance:none;cursor:pointer;}.setting-radio input{border-radius:50%;}.setting-radio span,.setting-check span{padding-bottom:1px;}.setting-radio{margin-top:0.1em;margin-bottom:0.1em;min-width:3.8em;padding:0.3em;display:inline-flex;align-items:center;cursor:pointer;}.setting-radio+.setting-radio{margin-left:0.5em;}.setting-check{margin-right:20px;display:flex;align-items:center;cursor:pointer;}.setting-radio input:checked{box-shadow:inset 0 0 0 3px var(--main-background-color);background-color:var(--settings-input-color);}.setting-check input:checked{background-color:var(--settings-input-color);border-width:1px;content:url('data:image/svg+xml,\ + \ + ');}.setting-radio input:focus,.setting-check input:focus{box-shadow:0 0 1px 1px var(--settings-input-color);}.setting-radio input:checked:focus{box-shadow:inset 0 0 0 3px var(--main-background-color),0 0 2px 2px var(--settings-input-color);}.setting-radio input:hover,.setting-check input:hover{border-color:var(--settings-input-color) !important;}#help.popover{max-width:600px;--popover-arrow-offset:48px;}#help dt{float:left;clear:left;margin-right:0.5rem;}#help span.top,#help span.bottom{text-align:center;display:block;font-size:1.125rem;}#help span.top{margin:10px 0;border-bottom:1px solid var(--border-color);padding-bottom:4px;margin-bottom:6px;}#help span.bottom{clear:both;border-top:1px solid var(--border-color);}.side-by-side>div{width:50%;float:left;padding:0 20px 20px 17px;}.item-info .stab{display:block;padding:3px;margin-bottom:5px;}.item-name .stab{margin-left:0.3125em;}.stab{padding:0 2px;font-size:0.875rem;font-weight:normal;color:var(--main-color);background-color:var(--stab-background-color);width:fit-content;white-space:pre-wrap;border-radius:3px;display:inline;vertical-align:baseline;}.stab.portability>code{background:none;color:var(--stab-code-color);}.stab .emoji,.item-info .stab::before{font-size:1.25rem;}.stab .emoji{margin-right:0.3rem;}.item-info .stab::before{content:"\0";width:0;display:inline-block;color:transparent;}.emoji{text-shadow:1px 0 0 black,-1px 0 0 black,0 1px 0 black,0 -1px 0 black;}.since{font-weight:normal;font-size:initial;}.rightside{padding-left:12px;float:right;}.rightside:not(a),.out-of-band{color:var(--right-side-color);}pre.rust{tab-size:4;-moz-tab-size:4;}pre.rust .kw{color:var(--code-highlight-kw-color);}pre.rust .kw-2{color:var(--code-highlight-kw-2-color);}pre.rust .lifetime{color:var(--code-highlight-lifetime-color);}pre.rust .prelude-ty{color:var(--code-highlight-prelude-color);}pre.rust .prelude-val{color:var(--code-highlight-prelude-val-color);}pre.rust .string{color:var(--code-highlight-string-color);}pre.rust .number{color:var(--code-highlight-number-color);}pre.rust .bool-val{color:var(--code-highlight-literal-color);}pre.rust .self{color:var(--code-highlight-self-color);}pre.rust .attr{color:var(--code-highlight-attribute-color);}pre.rust .macro,pre.rust .macro-nonterminal{color:var(--code-highlight-macro-color);}pre.rust .question-mark{font-weight:bold;color:var(--code-highlight-question-mark-color);}pre.rust .comment{color:var(--code-highlight-comment-color);}pre.rust .doccomment{color:var(--code-highlight-doc-comment-color);}.rustdoc.src .example-wrap pre.rust a{background:var(--codeblock-link-background);}.example-wrap.compile_fail,.example-wrap.should_panic{border-left:2px solid var(--codeblock-error-color);}.ignore.example-wrap{border-left:2px solid var(--codeblock-ignore-color);}.example-wrap.compile_fail:hover,.example-wrap.should_panic:hover{border-left:2px solid var(--codeblock-error-hover-color);}.example-wrap.ignore:hover{border-left:2px solid var(--codeblock-ignore-hover-color);}.example-wrap.compile_fail .tooltip,.example-wrap.should_panic .tooltip{color:var(--codeblock-error-color);}.example-wrap.ignore .tooltip{color:var(--codeblock-ignore-color);}.example-wrap.compile_fail:hover .tooltip,.example-wrap.should_panic:hover .tooltip{color:var(--codeblock-error-hover-color);}.example-wrap.ignore:hover .tooltip{color:var(--codeblock-ignore-hover-color);}.example-wrap .tooltip{position:absolute;display:block;left:-25px;top:5px;margin:0;line-height:1;}.example-wrap.compile_fail .tooltip,.example-wrap.should_panic .tooltip,.example-wrap.ignore .tooltip{font-weight:bold;font-size:1.25rem;}.content .docblock .warning{border-left:2px solid var(--warning-border-color);padding:14px;position:relative;overflow-x:visible !important;}.content .docblock .warning::before{color:var(--warning-border-color);content:"ⓘ";position:absolute;left:-25px;top:5px;font-weight:bold;font-size:1.25rem;}.top-doc>.docblock>.warning:first-child::before{top:20px;}a.test-arrow{visibility:hidden;position:absolute;padding:5px 10px 5px 10px;border-radius:5px;font-size:1.375rem;top:5px;right:5px;z-index:1;color:var(--test-arrow-color);background-color:var(--test-arrow-background-color);}a.test-arrow:hover{color:var(--test-arrow-hover-color);background-color:var(--test-arrow-hover-background-color);}.example-wrap:hover .test-arrow{visibility:visible;}.code-attribute{font-weight:300;color:var(--code-attribute-color);}.item-spacer{width:100%;height:12px;display:block;}.out-of-band>span.since{font-size:1.25rem;}.sub-variant h4{font-size:1rem;font-weight:400;margin-top:0;margin-bottom:0;}.sub-variant{margin-left:24px;margin-bottom:40px;}.sub-variant>.sub-variant-field{margin-left:24px;}:target{padding-right:3px;background-color:var(--target-background-color);border-right:3px solid var(--target-border-color);}.code-header a.tooltip{color:inherit;margin-right:15px;position:relative;}.code-header a.tooltip:hover{color:var(--link-color);}a.tooltip:hover::after{position:absolute;top:calc(100% - 10px);left:-15px;right:-15px;height:20px;content:"\00a0";}.fade-out{opacity:0;transition:opacity 0.45s cubic-bezier(0,0,0.1,1.0);}.popover.tooltip .content{margin:0.25em 0.5em;}.popover.tooltip .content pre,.popover.tooltip .content code{background:transparent;margin:0;padding:0;font-size:1.25rem;white-space:pre-wrap;}.popover.tooltip .content>h3:first-child{margin:0 0 5px 0;}.search-failed{text-align:center;margin-top:20px;display:none;}.search-failed.active{display:block;}.search-failed>ul{text-align:left;max-width:570px;margin-left:auto;margin-right:auto;}#search-tabs{display:flex;flex-direction:row;gap:1px;margin-bottom:4px;}#search-tabs button{text-align:center;font-size:1.125rem;border:0;border-top:2px solid;flex:1;line-height:1.5;color:inherit;}#search-tabs button:not(.selected){background-color:var(--search-tab-button-not-selected-background);border-top-color:var(--search-tab-button-not-selected-border-top-color);}#search-tabs button:hover,#search-tabs button.selected{background-color:var(--search-tab-button-selected-background);border-top-color:var(--search-tab-button-selected-border-top-color);}#search-tabs .count{font-size:1rem;font-variant-numeric:tabular-nums;color:var(--search-tab-title-count-color);}#search .error code{border-radius:3px;background-color:var(--search-error-code-background-color);}.search-corrections{font-weight:normal;}#src-sidebar{width:100%;overflow:auto;}#src-sidebar div.files>a:hover,details.dir-entry summary:hover,#src-sidebar div.files>a:focus,details.dir-entry summary:focus{background-color:var(--src-sidebar-background-hover);}#src-sidebar div.files>a.selected{background-color:var(--src-sidebar-background-selected);}.src-sidebar-title{position:sticky;top:0;display:flex;padding:8px 8px 0 48px;margin-bottom:7px;background:var(--sidebar-background-color);border-bottom:1px solid var(--border-color);}#settings-menu,#help-button{margin-left:4px;display:flex;}#sidebar-button{display:none;line-height:0;}.hide-sidebar #sidebar-button,.src #sidebar-button{display:flex;margin-right:4px;position:fixed;left:6px;height:34px;width:34px;background-color:var(--main-background-color);z-index:1;}.src #sidebar-button{left:8px;z-index:calc(var(--desktop-sidebar-z-index) + 1);}.hide-sidebar .src #sidebar-button{position:static;}#settings-menu>a,#help-button>a,#sidebar-button>a{display:flex;align-items:center;justify-content:center;background-color:var(--button-background-color);border:1px solid var(--border-color);border-radius:2px;color:var(--settings-button-color);font-size:20px;width:33px;}#settings-menu>a:hover,#settings-menu>a:focus,#help-button>a:hover,#help-button>a:focus,#sidebar-button>a:hover,#sidebar-button>a:focus{border-color:var(--settings-button-border-focus);}#sidebar-button>a:before{content:url('data:image/svg+xml,\ + \ + \ + ');width:22px;height:22px;}#copy-path{color:var(--copy-path-button-color);background:var(--main-background-color);height:34px;margin-left:10px;padding:0;padding-left:2px;border:0;width:33px;}#copy-path>img{filter:var(--copy-path-img-filter);}#copy-path:hover>img{filter:var(--copy-path-img-hover-filter);}@keyframes rotating{from{transform:rotate(0deg);}to{transform:rotate(360deg);}}#settings-menu.rotate>a img{animation:rotating 2s linear infinite;}kbd{display:inline-block;padding:3px 5px;font:15px monospace;line-height:10px;vertical-align:middle;border:solid 1px var(--border-color);border-radius:3px;color:var(--kbd-color);background-color:var(--kbd-background);box-shadow:inset 0 -1px 0 var(--kbd-box-shadow-color);}ul.all-items>li{list-style:none;}details.dir-entry{padding-left:4px;}details.dir-entry>summary{margin:0 0 0 -4px;padding:0 0 0 4px;cursor:pointer;}details.dir-entry div.folders,details.dir-entry div.files{padding-left:23px;}details.dir-entry a{display:block;}details.toggle{contain:layout;position:relative;}details.toggle>summary.hideme{cursor:pointer;font-size:1rem;}details.toggle>summary{list-style:none;outline:none;}details.toggle>summary::-webkit-details-marker,details.toggle>summary::marker{display:none;}details.toggle>summary.hideme>span{margin-left:9px;}details.toggle>summary::before{background:url('data:image/svg+xml,') no-repeat top left;content:"";cursor:pointer;width:16px;height:16px;display:inline-block;vertical-align:middle;opacity:.5;filter:var(--toggle-filter);}details.toggle>summary.hideme>span,.more-examples-toggle summary,.more-examples-toggle .hide-more{color:var(--toggles-color);}details.toggle>summary::after{content:"Expand";overflow:hidden;width:0;height:0;position:absolute;}details.toggle>summary.hideme::after{content:"";}details.toggle>summary:focus::before,details.toggle>summary:hover::before{opacity:1;}details.toggle>summary:focus-visible::before{outline:1px dotted #000;outline-offset:1px;}details.non-exhaustive{margin-bottom:8px;}details.toggle>summary.hideme::before{position:relative;}details.toggle>summary:not(.hideme)::before{position:absolute;left:-24px;top:4px;}.impl-items>details.toggle>summary:not(.hideme)::before{position:absolute;left:-24px;}details.toggle[open] >summary.hideme{position:absolute;}details.toggle[open] >summary.hideme>span{display:none;}details.toggle[open] >summary::before{background:url('data:image/svg+xml,') no-repeat top left;}details.toggle[open] >summary::after{content:"Collapse";}.docblock summary>*{display:inline-block;}.docblock>.example-wrap:first-child .tooltip{margin-top:16px;}.src #sidebar-button>a:before,.sidebar-menu-toggle:before{content:url('data:image/svg+xml,\ + ');opacity:0.75;}.sidebar-menu-toggle:hover:before,.sidebar-menu-toggle:active:before,.sidebar-menu-toggle:focus:before{opacity:1;}.src #sidebar-button>a:before{content:url('data:image/svg+xml,\ + \ + \ + ');opacity:0.75;}@media (max-width:850px){#search-tabs .count{display:block;}}@media (max-width:700px){*[id]{scroll-margin-top:45px;}.rustdoc{display:block;}main{padding-left:15px;padding-top:0px;}.main-heading{flex-direction:column;}.out-of-band{text-align:left;margin-left:initial;padding:initial;}.out-of-band .since::before{content:"Since ";}.sidebar .logo-container,.sidebar .location,.sidebar-resizer{display:none;}.sidebar{position:fixed;top:45px;left:-1000px;z-index:11;height:calc(100vh - 45px);width:200px;}.src main,.rustdoc.src .sidebar{top:0;padding:0;height:100vh;border:0;}.src .search-form{margin-left:40px;}.hide-sidebar .search-form{margin-left:32px;}.hide-sidebar .src .search-form{margin-left:0;}.sidebar.shown,.src-sidebar-expanded .src .sidebar,.rustdoc:not(.src) .sidebar:focus-within{left:0;}.mobile-topbar h2{padding-bottom:0;margin:auto 0.5em auto auto;overflow:hidden;font-size:24px;}.mobile-topbar h2 a{display:block;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;}.mobile-topbar .logo-container>img{max-width:35px;max-height:35px;margin:5px 0 5px 20px;}.mobile-topbar{display:flex;flex-direction:row;position:sticky;z-index:10;font-size:2rem;height:45px;width:100%;left:0;top:0;}.hide-sidebar .mobile-topbar{display:none;}.sidebar-menu-toggle{width:45px;border:none;line-height:0;}.hide-sidebar .sidebar-menu-toggle{display:none;}.sidebar-elems{margin-top:1em;}.anchor{display:none !important;}#main-content>details.toggle>summary::before,#main-content>div>details.toggle>summary::before{left:-11px;}#copy-path,#help-button{display:none;}#sidebar-button>a:before{content:url('data:image/svg+xml,\ + \ + \ + ');width:22px;height:22px;}.sidebar-menu-toggle:before{filter:var(--mobile-sidebar-menu-filter);}.sidebar-menu-toggle:hover{background:var(--main-background-color);}.item-table,.item-row,.item-table>li,.item-table>li>div,.search-results>a,.search-results>a>div{display:block;}.search-results>a{padding:5px 0px;}.search-results>a>div.desc,.item-table>li>div.desc{padding-left:2em;}.search-results .result-name{display:block;}.search-results .result-name .typename{width:initial;margin-right:0;}.search-results .result-name .typename,.search-results .result-name .path{display:inline;}.src-sidebar-expanded .src .sidebar{position:fixed;max-width:100vw;width:100vw;}.src .src-sidebar-title{padding-top:0;}details.toggle:not(.top-doc)>summary{margin-left:10px;}.impl-items>details.toggle>summary:not(.hideme)::before,#main-content>details.toggle:not(.top-doc)>summary::before,#main-content>div>details.toggle>summary::before{left:-11px;}.impl-items>.item-info{margin-left:34px;}.src nav.sub{margin:0;padding:var(--nav-sub-mobile-padding);}}@media (min-width:701px){.scraped-example-title{position:absolute;z-index:10;background:var(--main-background-color);bottom:8px;right:5px;padding:2px 4px;box-shadow:0 0 4px var(--main-background-color);}}@media print{nav.sidebar,nav.sub,.out-of-band,a.src,#copy-path,details.toggle[open] >summary::before,details.toggle>summary::before,details.toggle.top-doc>summary{display:none;}.docblock{margin-left:0;}main{padding:10px;}}@media (max-width:464px){.docblock{margin-left:12px;}.docblock code{overflow-wrap:break-word;overflow-wrap:anywhere;}nav.sub{flex-direction:column;}.search-form{align-self:stretch;}}.variant,.implementors-toggle>summary,.impl,#implementors-list>.docblock,.impl-items>section,.impl-items>.toggle>summary,.methods>section,.methods>.toggle>summary{margin-bottom:0.75em;}.variants>.docblock,.implementors-toggle>.docblock,.impl-items>.toggle[open]:not(:last-child),.methods>.toggle[open]:not(:last-child),.implementors-toggle[open]:not(:last-child){margin-bottom:2em;}#trait-implementations-list .impl-items>.toggle:not(:last-child),#synthetic-implementations-list .impl-items>.toggle:not(:last-child),#blanket-implementations-list .impl-items>.toggle:not(:last-child){margin-bottom:1em;}.scraped-example-list .scrape-help{margin-left:10px;padding:0 4px;font-weight:normal;font-size:12px;position:relative;bottom:1px;border:1px solid var(--scrape-example-help-border-color);border-radius:50px;color:var(--scrape-example-help-color);}.scraped-example-list .scrape-help:hover{border-color:var(--scrape-example-help-hover-border-color);color:var(--scrape-example-help-hover-color);}.scraped-example{position:relative;}.scraped-example .code-wrapper{position:relative;display:flex;flex-direction:row;flex-wrap:wrap;width:100%;}.scraped-example:not(.expanded) .code-wrapper{max-height:calc(1.5em * 5 + 10px);}.scraped-example:not(.expanded) .code-wrapper pre{overflow-y:hidden;padding-bottom:0;max-height:calc(1.5em * 5 + 10px);}.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper,.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper pre{max-height:calc(1.5em * 10 + 10px);}.scraped-example .code-wrapper .next,.scraped-example .code-wrapper .prev,.scraped-example .code-wrapper .expand{color:var(--main-color);position:absolute;top:0.25em;z-index:1;padding:0;background:none;border:none;-webkit-appearance:none;opacity:1;}.scraped-example .code-wrapper .prev{right:2.25em;}.scraped-example .code-wrapper .next{right:1.25em;}.scraped-example .code-wrapper .expand{right:0.25em;}.scraped-example:not(.expanded) .code-wrapper::before,.scraped-example:not(.expanded) .code-wrapper::after{content:" ";width:100%;height:5px;position:absolute;z-index:1;}.scraped-example:not(.expanded) .code-wrapper::before{top:0;background:linear-gradient(to bottom,var(--scrape-example-code-wrapper-background-start),var(--scrape-example-code-wrapper-background-end));}.scraped-example:not(.expanded) .code-wrapper::after{bottom:0;background:linear-gradient(to top,var(--scrape-example-code-wrapper-background-start),var(--scrape-example-code-wrapper-background-end));}.scraped-example .code-wrapper .example-wrap{width:100%;overflow-y:hidden;margin-bottom:0;}.scraped-example:not(.expanded) .code-wrapper .example-wrap{overflow-x:hidden;}.scraped-example .example-wrap .rust span.highlight{background:var(--scrape-example-code-line-highlight);}.scraped-example .example-wrap .rust span.highlight.focus{background:var(--scrape-example-code-line-highlight-focus);}.more-examples-toggle{max-width:calc(100% + 25px);margin-top:10px;margin-left:-25px;}.more-examples-toggle .hide-more{margin-left:25px;cursor:pointer;}.more-scraped-examples{margin-left:25px;position:relative;}.toggle-line{position:absolute;top:5px;bottom:0;right:calc(100% + 10px);padding:0 4px;cursor:pointer;}.toggle-line-inner{min-width:2px;height:100%;background:var(--scrape-example-toggle-line-background);}.toggle-line:hover .toggle-line-inner{background:var(--scrape-example-toggle-line-hover-background);}.more-scraped-examples .scraped-example,.example-links{margin-top:20px;}.more-scraped-examples .scraped-example:first-child{margin-top:5px;}.example-links ul{margin-bottom:0;}:root[data-theme="light"]{--main-background-color:white;--main-color:black;--settings-input-color:#2196f3;--settings-input-border-color:#717171;--settings-button-color:#000;--settings-button-border-focus:#717171;--sidebar-background-color:#f5f5f5;--sidebar-background-color-hover:#e0e0e0;--code-block-background-color:#f5f5f5;--scrollbar-track-background-color:#dcdcdc;--scrollbar-thumb-background-color:rgba(36,37,39,0.6);--scrollbar-color:rgba(36,37,39,0.6) #d9d9d9;--headings-border-bottom-color:#ddd;--border-color:#e0e0e0;--button-background-color:#fff;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:none;--mobile-sidebar-menu-filter:none;--search-input-focused-border-color:#66afe9;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(35%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ad378a;--trait-link-color:#6e4fc9;--assoc-item-link-color:#3873ad;--function-link-color:#ad7c37;--macro-link-color:#068000;--keyword-link-color:#3873ad;--mod-link-color:#3873ad;--link-color:#3873ad;--sidebar-link-color:#356da4;--sidebar-current-link-background-color:#fff;--search-result-link-focus-background-color:#ccc;--search-result-border-color:#aaa3;--search-color:#000;--search-error-code-background-color:#d0cccc;--search-results-alias-color:#000;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#e6e6e6;--search-tab-button-not-selected-background:#e6e6e6;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#fff;--stab-background-color:#fff5d6;--stab-code-color:#000;--code-highlight-kw-color:#8959a8;--code-highlight-kw-2-color:#4271ae;--code-highlight-lifetime-color:#b76514;--code-highlight-prelude-color:#4271ae;--code-highlight-prelude-val-color:#c82829;--code-highlight-number-color:#718c00;--code-highlight-string-color:#718c00;--code-highlight-literal-color:#c82829;--code-highlight-attribute-color:#c82829;--code-highlight-self-color:#c82829;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8e908c;--code-highlight-doc-comment-color:#4d4d4c;--src-line-numbers-span-color:#c67e2d;--src-line-number-highlighted-background-color:#fdffd3;--test-arrow-color:#f5f5f5;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#f5f5f5;--test-arrow-hover-background-color:rgb(78,139,202);--target-background-color:#fdffd3;--target-border-color:#ad7c37;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:initial;--crate-search-div-filter:invert(100%) sepia(0%) saturate(4223%) hue-rotate(289deg) brightness(114%) contrast(76%);--crate-search-div-hover-filter:invert(44%) sepia(18%) saturate(23%) hue-rotate(317deg) brightness(96%) contrast(93%);--crate-search-hover-border:#717171;--src-sidebar-background-selected:#fff;--src-sidebar-background-hover:#e0e0e0;--table-alt-row-background-color:#f5f5f5;--codeblock-link-background:#eee;--scrape-example-toggle-line-background:#ccc;--scrape-example-toggle-line-hover-background:#999;--scrape-example-code-line-highlight:#fcffd6;--scrape-example-code-line-highlight-focus:#f6fdb0;--scrape-example-help-border-color:#555;--scrape-example-help-color:#333;--scrape-example-help-hover-border-color:#000;--scrape-example-help-hover-color:#000;--scrape-example-code-wrapper-background-start:rgba(255,255,255,1);--scrape-example-code-wrapper-background-end:rgba(255,255,255,0);--sidebar-resizer-hover:hsl(207,90%,66%);--sidebar-resizer-active:hsl(207,90%,54%);}:root[data-theme="dark"]{--main-background-color:#353535;--main-color:#ddd;--settings-input-color:#2196f3;--settings-input-border-color:#999;--settings-button-color:#000;--settings-button-border-focus:#ffb900;--sidebar-background-color:#505050;--sidebar-background-color-hover:#676767;--code-block-background-color:#2A2A2A;--scrollbar-track-background-color:#717171;--scrollbar-thumb-background-color:rgba(32,34,37,.6);--scrollbar-color:rgba(32,34,37,.6) #5a5a5a;--headings-border-bottom-color:#d2d2d2;--border-color:#e0e0e0;--button-background-color:#f0f0f0;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--mobile-sidebar-menu-filter:invert(100%);--search-input-focused-border-color:#008dfd;--copy-path-button-color:#999;--copy-path-img-filter:invert(50%);--copy-path-img-hover-filter:invert(65%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#2dbfb8;--trait-link-color:#b78cf2;--assoc-item-link-color:#d2991d;--function-link-color:#2bab63;--macro-link-color:#09bd00;--keyword-link-color:#d2991d;--mod-link-color:#d2991d;--link-color:#d2991d;--sidebar-link-color:#fdbf35;--sidebar-current-link-background-color:#444;--search-result-link-focus-background-color:#616161;--search-result-border-color:#aaa3;--search-color:#111;--search-error-code-background-color:#484848;--search-results-alias-color:#fff;--search-results-grey-color:#ccc;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:#252525;--search-tab-button-not-selected-background:#252525;--search-tab-button-selected-border-top-color:#0089ff;--search-tab-button-selected-background:#353535;--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ab8ac1;--code-highlight-kw-2-color:#769acb;--code-highlight-lifetime-color:#d97f26;--code-highlight-prelude-color:#769acb;--code-highlight-prelude-val-color:#ee6868;--code-highlight-number-color:#83a300;--code-highlight-string-color:#83a300;--code-highlight-literal-color:#ee6868;--code-highlight-attribute-color:#ee6868;--code-highlight-self-color:#ee6868;--code-highlight-macro-color:#3e999f;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#8d8d8b;--code-highlight-doc-comment-color:#8ca375;--src-line-numbers-span-color:#3b91e2;--src-line-number-highlighted-background-color:#0a042f;--test-arrow-color:#dedede;--test-arrow-background-color:rgba(78,139,202,0.2);--test-arrow-hover-color:#dedede;--test-arrow-hover-background-color:#4e8bca;--target-background-color:#494a3d;--target-border-color:#bb7410;--kbd-color:#000;--kbd-background:#fafbfc;--kbd-box-shadow-color:#c6cbd1;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(94%) sepia(0%) saturate(721%) hue-rotate(255deg) brightness(90%) contrast(90%);--crate-search-div-hover-filter:invert(69%) sepia(60%) saturate(6613%) hue-rotate(184deg) brightness(100%) contrast(91%);--crate-search-hover-border:#2196f3;--src-sidebar-background-selected:#333;--src-sidebar-background-hover:#444;--table-alt-row-background-color:#2a2a2a;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(53,53,53,1);--scrape-example-code-wrapper-background-end:rgba(53,53,53,0);--sidebar-resizer-hover:hsl(207,30%,54%);--sidebar-resizer-active:hsl(207,90%,54%);}:root[data-theme="ayu"]{--main-background-color:#0f1419;--main-color:#c5c5c5;--settings-input-color:#ffb454;--settings-input-border-color:#999;--settings-button-color:#fff;--settings-button-border-focus:#e0e0e0;--sidebar-background-color:#14191f;--sidebar-background-color-hover:rgba(70,70,70,0.33);--code-block-background-color:#191f26;--scrollbar-track-background-color:transparent;--scrollbar-thumb-background-color:#5c6773;--scrollbar-color:#5c6773 #24292f;--headings-border-bottom-color:#5c6773;--border-color:#5c6773;--button-background-color:#141920;--right-side-color:grey;--code-attribute-color:#999;--toggles-color:#999;--toggle-filter:invert(100%);--mobile-sidebar-menu-filter:invert(100%);--search-input-focused-border-color:#5c6773;--copy-path-button-color:#fff;--copy-path-img-filter:invert(70%);--copy-path-img-hover-filter:invert(100%);--codeblock-error-hover-color:rgb(255,0,0);--codeblock-error-color:rgba(255,0,0,.5);--codeblock-ignore-hover-color:rgb(255,142,0);--codeblock-ignore-color:rgba(255,142,0,.6);--warning-border-color:#ff8e00;--type-link-color:#ffa0a5;--trait-link-color:#39afd7;--assoc-item-link-color:#39afd7;--function-link-color:#fdd687;--macro-link-color:#a37acc;--keyword-link-color:#39afd7;--mod-link-color:#39afd7;--link-color:#39afd7;--sidebar-link-color:#53b1db;--sidebar-current-link-background-color:transparent;--search-result-link-focus-background-color:#3c3c3c;--search-result-border-color:#aaa3;--search-color:#fff;--search-error-code-background-color:#4f4c4c;--search-results-alias-color:#c5c5c5;--search-results-grey-color:#999;--search-tab-title-count-color:#888;--search-tab-button-not-selected-border-top-color:none;--search-tab-button-not-selected-background:transparent !important;--search-tab-button-selected-border-top-color:none;--search-tab-button-selected-background:#141920 !important;--stab-background-color:#314559;--stab-code-color:#e6e1cf;--code-highlight-kw-color:#ff7733;--code-highlight-kw-2-color:#ff7733;--code-highlight-lifetime-color:#ff7733;--code-highlight-prelude-color:#69f2df;--code-highlight-prelude-val-color:#ff7733;--code-highlight-number-color:#b8cc52;--code-highlight-string-color:#b8cc52;--code-highlight-literal-color:#ff7733;--code-highlight-attribute-color:#e6e1cf;--code-highlight-self-color:#36a3d9;--code-highlight-macro-color:#a37acc;--code-highlight-question-mark-color:#ff9011;--code-highlight-comment-color:#788797;--code-highlight-doc-comment-color:#a1ac88;--src-line-numbers-span-color:#5c6773;--src-line-number-highlighted-background-color:rgba(255,236,164,0.06);--test-arrow-color:#788797;--test-arrow-background-color:rgba(57,175,215,0.09);--test-arrow-hover-color:#c5c5c5;--test-arrow-hover-background-color:rgba(57,175,215,0.368);--target-background-color:rgba(255,236,164,0.06);--target-border-color:rgba(255,180,76,0.85);--kbd-color:#c5c5c5;--kbd-background:#314559;--kbd-box-shadow-color:#5c6773;--rust-logo-filter:drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff);--crate-search-div-filter:invert(41%) sepia(12%) saturate(487%) hue-rotate(171deg) brightness(94%) contrast(94%);--crate-search-div-hover-filter:invert(98%) sepia(12%) saturate(81%) hue-rotate(343deg) brightness(113%) contrast(76%);--crate-search-hover-border:#e0e0e0;--src-sidebar-background-selected:#14191f;--src-sidebar-background-hover:#14191f;--table-alt-row-background-color:#191f26;--codeblock-link-background:#333;--scrape-example-toggle-line-background:#999;--scrape-example-toggle-line-hover-background:#c5c5c5;--scrape-example-code-line-highlight:#5b3b01;--scrape-example-code-line-highlight-focus:#7c4b0f;--scrape-example-help-border-color:#aaa;--scrape-example-help-color:#eee;--scrape-example-help-hover-border-color:#fff;--scrape-example-help-hover-color:#fff;--scrape-example-code-wrapper-background-start:rgba(15,20,25,1);--scrape-example-code-wrapper-background-end:rgba(15,20,25,0);--sidebar-resizer-hover:hsl(34,50%,33%);--sidebar-resizer-active:hsl(34,100%,66%);}:root[data-theme="ayu"] h1,:root[data-theme="ayu"] h2,:root[data-theme="ayu"] h3,:root[data-theme="ayu"] h4,:where(:root[data-theme="ayu"]) h1 a,:root[data-theme="ayu"] .sidebar h2 a,:root[data-theme="ayu"] .sidebar h3 a{color:#fff;}:root[data-theme="ayu"] .docblock code{color:#ffb454;}:root[data-theme="ayu"] .docblock a>code{color:#39AFD7 !important;}:root[data-theme="ayu"] .code-header,:root[data-theme="ayu"] .docblock pre>code,:root[data-theme="ayu"] pre,:root[data-theme="ayu"] pre>code,:root[data-theme="ayu"] .item-info code,:root[data-theme="ayu"] .rustdoc.source .example-wrap{color:#e6e1cf;}:root[data-theme="ayu"] .sidebar .current,:root[data-theme="ayu"] .sidebar .current a,:root[data-theme="ayu"] .sidebar a:hover,:root[data-theme="ayu"] #src-sidebar div.files>a:hover,:root[data-theme="ayu"] details.dir-entry summary:hover,:root[data-theme="ayu"] #src-sidebar div.files>a:focus,:root[data-theme="ayu"] details.dir-entry summary:focus,:root[data-theme="ayu"] #src-sidebar div.files>a.selected{color:#ffb44c;}:root[data-theme="ayu"] .sidebar-elems .location{color:#ff7733;}:root[data-theme="ayu"] .src-line-numbers .line-highlighted{color:#708090;padding-right:7px;border-right:1px solid #ffb44c;}:root[data-theme="ayu"] .search-results a:hover,:root[data-theme="ayu"] .search-results a:focus{color:#fff !important;background-color:#3c3c3c;}:root[data-theme="ayu"] .search-results a{color:#0096cf;}:root[data-theme="ayu"] .search-results a div.desc{color:#c5c5c5;}:root[data-theme="ayu"] .result-name .primitive>i,:root[data-theme="ayu"] .result-name .keyword>i{color:#788797;}:root[data-theme="ayu"] #search-tabs>button.selected{border-bottom:1px solid #ffb44c !important;border-top:none;}:root[data-theme="ayu"] #search-tabs>button:not(.selected){border:none;background-color:transparent !important;}:root[data-theme="ayu"] #search-tabs>button:hover{border-bottom:1px solid rgba(242,151,24,0.3);}:root[data-theme="ayu"] #settings-menu>a img,:root[data-theme="ayu"] #sidebar-button>a:before{filter:invert(100);} \ No newline at end of file diff --git a/docs/windows_docs/static.files/scrape-examples-ef1e698c1d417c0c.js b/docs/windows_docs/static.files/scrape-examples-ef1e698c1d417c0c.js new file mode 100644 index 00000000..ba830e37 --- /dev/null +++ b/docs/windows_docs/static.files/scrape-examples-ef1e698c1d417c0c.js @@ -0,0 +1 @@ +"use strict";(function(){const DEFAULT_MAX_LINES=5;const HIDDEN_MAX_LINES=10;function scrollToLoc(elt,loc,isHidden){const lines=elt.querySelector(".src-line-numbers");let scrollOffset;const maxLines=isHidden?HIDDEN_MAX_LINES:DEFAULT_MAX_LINES;if(loc[1]-loc[0]>maxLines){const line=Math.max(0,loc[0]-1);scrollOffset=lines.children[line].offsetTop}else{const wrapper=elt.querySelector(".code-wrapper");const halfHeight=wrapper.offsetHeight/2;const offsetTop=lines.children[loc[0]].offsetTop;const lastLine=lines.children[loc[1]];const offsetBot=lastLine.offsetTop+lastLine.offsetHeight;const offsetMid=(offsetTop+offsetBot)/2;scrollOffset=offsetMid-halfHeight}lines.scrollTo(0,scrollOffset);elt.querySelector(".rust").scrollTo(0,scrollOffset)}function updateScrapedExample(example,isHidden){const locs=JSON.parse(example.attributes.getNamedItem("data-locs").textContent);let locIndex=0;const highlights=Array.prototype.slice.call(example.querySelectorAll(".highlight"));const link=example.querySelector(".scraped-example-title a");if(locs.length>1){const onChangeLoc=changeIndex=>{removeClass(highlights[locIndex],"focus");changeIndex();scrollToLoc(example,locs[locIndex][0],isHidden);addClass(highlights[locIndex],"focus");const url=locs[locIndex][1];const title=locs[locIndex][2];link.href=url;link.innerHTML=title};example.querySelector(".prev").addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex-1+locs.length)%locs.length})});example.querySelector(".next").addEventListener("click",()=>{onChangeLoc(()=>{locIndex=(locIndex+1)%locs.length})})}const expandButton=example.querySelector(".expand");if(expandButton){expandButton.addEventListener("click",()=>{if(hasClass(example,"expanded")){removeClass(example,"expanded");scrollToLoc(example,locs[0][0],isHidden)}else{addClass(example,"expanded")}})}scrollToLoc(example,locs[0][0],isHidden)}const firstExamples=document.querySelectorAll(".scraped-example-list > .scraped-example");onEachLazy(firstExamples,el=>updateScrapedExample(el,false));onEachLazy(document.querySelectorAll(".more-examples-toggle"),toggle=>{onEachLazy(toggle.querySelectorAll(".toggle-line, .hide-more"),button=>{button.addEventListener("click",()=>{toggle.open=false})});const moreExamples=toggle.querySelectorAll(".scraped-example");toggle.querySelector("summary").addEventListener("click",()=>{setTimeout(()=>{onEachLazy(moreExamples,el=>updateScrapedExample(el,true))})},{once:true})})})() \ No newline at end of file diff --git a/docs/windows_docs/static.files/search-dd67cee4cfa65049.js b/docs/windows_docs/static.files/search-dd67cee4cfa65049.js new file mode 100644 index 00000000..ef8bf865 --- /dev/null +++ b/docs/windows_docs/static.files/search-dd67cee4cfa65049.js @@ -0,0 +1,5 @@ +"use strict";if(!Array.prototype.toSpliced){Array.prototype.toSpliced=function(){const me=this.slice();Array.prototype.splice.apply(me,arguments);return me}}(function(){const itemTypes=["keyword","primitive","mod","externcrate","import","struct","enum","fn","type","static","trait","impl","tymethod","method","structfield","variant","macro","associatedtype","constant","associatedconstant","union","foreigntype","existential","attr","derive","traitalias","generic",];const longItemTypes=["keyword","primitive type","module","extern crate","re-export","struct","enum","function","type alias","static","trait","","trait method","method","struct field","enum variant","macro","assoc type","constant","assoc const","union","foreign type","existential type","attribute macro","derive macro","trait alias",];const TY_GENERIC=itemTypes.indexOf("generic");const ROOT_PATH=typeof window!=="undefined"?window.rootPath:"../";function printTab(nb){let iter=0;let foundCurrentTab=false;let foundCurrentResultSet=false;onEachLazy(document.getElementById("search-tabs").childNodes,elem=>{if(nb===iter){addClass(elem,"selected");foundCurrentTab=true}else{removeClass(elem,"selected")}iter+=1});const isTypeSearch=(nb>0||iter===1);iter=0;onEachLazy(document.getElementById("results").childNodes,elem=>{if(nb===iter){addClass(elem,"active");foundCurrentResultSet=true}else{removeClass(elem,"active")}iter+=1});if(foundCurrentTab&&foundCurrentResultSet){searchState.currentTab=nb;const correctionsElem=document.getElementsByClassName("search-corrections");if(isTypeSearch){removeClass(correctionsElem[0],"hidden")}else{addClass(correctionsElem[0],"hidden")}}else if(nb!==0){printTab(0)}}const editDistanceState={current:[],prev:[],prevPrev:[],calculate:function calculate(a,b,limit){if(a.lengthlimit){return limit+1}while(b.length>0&&b[0]===a[0]){a=a.substring(1);b=b.substring(1)}while(b.length>0&&b[b.length-1]===a[a.length-1]){a=a.substring(0,a.length-1);b=b.substring(0,b.length-1)}if(b.length===0){return minDist}const aLength=a.length;const bLength=b.length;for(let i=0;i<=bLength;++i){this.current[i]=0;this.prev[i]=i;this.prevPrev[i]=Number.MAX_VALUE}for(let i=1;i<=aLength;++i){this.current[0]=i;const aIdx=i-1;for(let j=1;j<=bLength;++j){const bIdx=j-1;const substitutionCost=a[aIdx]===b[bIdx]?0:1;this.current[j]=Math.min(this.prev[j]+1,this.current[j-1]+1,this.prev[j-1]+substitutionCost);if((i>1)&&(j>1)&&(a[aIdx]===b[bIdx-1])&&(a[aIdx-1]===b[bIdx])){this.current[j]=Math.min(this.current[j],this.prevPrev[j-2]+1)}}const prevPrevTmp=this.prevPrev;this.prevPrev=this.prev;this.prev=this.current;this.current=prevPrevTmp}const distance=this.prev[bLength];return distance<=limit?distance:(limit+1)},};function editDistance(a,b,limit){return editDistanceState.calculate(a,b,limit)}function initSearch(rawSearchIndex){const MAX_RESULTS=200;const NO_TYPE_FILTER=-1;let searchIndex;let functionTypeFingerprint;let currentResults;let typeNameIdMap;const ALIASES=new Map();let typeNameIdOfArray;let typeNameIdOfSlice;let typeNameIdOfArrayOrSlice;let typeNameIdOfTuple;let typeNameIdOfUnit;let typeNameIdOfTupleOrUnit;function buildTypeMapIndex(name,isAssocType){if(name===""||name===null){return null}if(typeNameIdMap.has(name)){const obj=typeNameIdMap.get(name);obj.assocOnly=isAssocType&&obj.assocOnly;return obj.id}else{const id=typeNameIdMap.size;typeNameIdMap.set(name,{id,assocOnly:isAssocType});return id}}function isSpecialStartCharacter(c){return"<\"".indexOf(c)!==-1}function isEndCharacter(c){return"=,>-])".indexOf(c)!==-1}function itemTypeFromName(typename){const index=itemTypes.findIndex(i=>i===typename);if(index<0){throw["Unknown type filter ",typename]}return index}function getStringElem(query,parserState,isInGenerics){if(isInGenerics){throw["Unexpected ","\""," in generics"]}else if(query.literalSearch){throw["Cannot have more than one literal search element"]}else if(parserState.totalElems-parserState.genericsElems>0){throw["Cannot use literal search when there is more than one element"]}parserState.pos+=1;const start=parserState.pos;const end=getIdentEndPosition(parserState);if(parserState.pos>=parserState.length){throw["Unclosed ","\""]}else if(parserState.userQuery[end]!=="\""){throw["Unexpected ",parserState.userQuery[end]," in a string element"]}else if(start===end){throw["Cannot have empty string element"]}parserState.pos+=1;query.literalSearch=true}function isPathStart(parserState){return parserState.userQuery.slice(parserState.pos,parserState.pos+2)==="::"}function isReturnArrow(parserState){return parserState.userQuery.slice(parserState.pos,parserState.pos+2)==="->"}function isIdentCharacter(c){return(c==="_"||(c>="0"&&c<="9")||(c>="a"&&c<="z")||(c>="A"&&c<="Z"))}function isSeparatorCharacter(c){return c===","||c==="="}function isPathSeparator(c){return c===":"||c===" "}function prevIs(parserState,lookingFor){let pos=parserState.pos;while(pos>0){const c=parserState.userQuery[pos-1];if(c===lookingFor){return true}else if(c!==" "){break}pos-=1}return false}function isLastElemGeneric(elems,parserState){return(elems.length>0&&elems[elems.length-1].generics.length>0)||prevIs(parserState,">")}function skipWhitespace(parserState){while(parserState.pos0){throw["Cannot have more than one element if you use quotes"]}const typeFilter=parserState.typeFilter;parserState.typeFilter=null;if(name==="!"){if(typeFilter!==null&&typeFilter!=="primitive"){throw["Invalid search type: primitive never type ","!"," and ",typeFilter," both specified",]}if(generics.length!==0){throw["Never type ","!"," does not accept generic parameters",]}const bindingName=parserState.isInBinding;parserState.isInBinding=null;return{name:"never",id:null,fullPath:["never"],pathWithoutLast:[],pathLast:"never",normalizedPathLast:"never",generics:[],bindings:new Map(),typeFilter:"primitive",bindingName,}}const quadcolon=/::\s*::/.exec(path);if(path.startsWith("::")){throw["Paths cannot start with ","::"]}else if(path.endsWith("::")){throw["Paths cannot end with ","::"]}else if(quadcolon!==null){throw["Unexpected ",quadcolon[0]]}const pathSegments=path.split(/(?:::\s*)|(?:\s+(?:::\s*)?)/);if(pathSegments.length===0||(pathSegments.length===1&&pathSegments[0]==="")){if(generics.length>0||prevIs(parserState,">")){throw["Found generics without a path"]}else{throw["Unexpected ",parserState.userQuery[parserState.pos]]}}for(const[i,pathSegment]of pathSegments.entries()){if(pathSegment==="!"){if(i!==0){throw["Never type ","!"," is not associated item"]}pathSegments[i]="never"}}parserState.totalElems+=1;if(isInGenerics){parserState.genericsElems+=1}const bindingName=parserState.isInBinding;parserState.isInBinding=null;const bindings=new Map();const pathLast=pathSegments[pathSegments.length-1];return{name:name.trim(),id:null,fullPath:pathSegments,pathWithoutLast:pathSegments.slice(0,pathSegments.length-1),pathLast,normalizedPathLast:pathLast.replace(/_/g,""),generics:generics.filter(gen=>{if(gen.bindingName!==null){bindings.set(gen.bindingName.name,[gen,...gen.bindingName.generics]);return false}return true}),bindings,typeFilter,bindingName,}}function getIdentEndPosition(parserState){const start=parserState.pos;let end=parserState.pos;let foundExclamation=-1;while(parserState.pos0){throw["Unexpected ",c," after ",parserState.userQuery[parserState.pos-1]]}else{throw["Unexpected ",c]}}parserState.pos+=1;end=parserState.pos}if(foundExclamation!==-1&&foundExclamation!==start&&isIdentCharacter(parserState.userQuery[foundExclamation-1])){if(parserState.typeFilter===null){parserState.typeFilter="macro"}else if(parserState.typeFilter!=="macro"){throw["Invalid search type: macro ","!"," and ",parserState.typeFilter," both specified",]}end=foundExclamation}return end}function getNextElem(query,parserState,elems,isInGenerics){const generics=[];skipWhitespace(parserState);let start=parserState.pos;let end;if("[(".indexOf(parserState.userQuery[parserState.pos])!==-1){let endChar=")";let name="()";let friendlyName="tuple";if(parserState.userQuery[parserState.pos]==="["){endChar="]";name="[]";friendlyName="slice"}parserState.pos+=1;const{foundSeparator}=getItemsBefore(query,parserState,generics,endChar);const typeFilter=parserState.typeFilter;const isInBinding=parserState.isInBinding;if(typeFilter!==null&&typeFilter!=="primitive"){throw["Invalid search type: primitive ",name," and ",typeFilter," both specified",]}parserState.typeFilter=null;parserState.isInBinding=null;for(const gen of generics){if(gen.bindingName!==null){throw["Type parameter ","=",` cannot be within ${friendlyName} `,name]}}if(name==="()"&&!foundSeparator&&generics.length===1&&typeFilter===null){elems.push(generics[0])}else{parserState.totalElems+=1;if(isInGenerics){parserState.genericsElems+=1}elems.push({name:name,id:null,fullPath:[name],pathWithoutLast:[],pathLast:name,normalizedPathLast:name,generics,bindings:new Map(),typeFilter:"primitive",bindingName:isInBinding,})}}else{const isStringElem=parserState.userQuery[start]==="\"";if(isStringElem){start+=1;getStringElem(query,parserState,isInGenerics);end=parserState.pos-1}else{end=getIdentEndPosition(parserState)}if(parserState.pos=end){throw["Found generics without a path"]}parserState.pos+=1;getItemsBefore(query,parserState,generics,">")}if(isStringElem){skipWhitespace(parserState)}if(start>=end&&generics.length===0){return}if(parserState.userQuery[parserState.pos]==="="){if(parserState.isInBinding){throw["Cannot write ","="," twice in a binding"]}if(!isInGenerics){throw["Type parameter ","="," must be within generics list"]}const name=parserState.userQuery.slice(start,end).trim();if(name==="!"){throw["Type parameter ","="," key cannot be ","!"," never type"]}if(name.includes("!")){throw["Type parameter ","="," key cannot be ","!"," macro"]}if(name.includes("::")){throw["Type parameter ","="," key cannot contain ","::"," path"]}if(name.includes(":")){throw["Type parameter ","="," key cannot contain ",":"," type"]}parserState.isInBinding={name,generics}}else{elems.push(createQueryElement(query,parserState,parserState.userQuery.slice(start,end),generics,isInGenerics))}}}function getItemsBefore(query,parserState,elems,endChar){let foundStopChar=true;let foundSeparator=false;let start=parserState.pos;const oldTypeFilter=parserState.typeFilter;parserState.typeFilter=null;const oldIsInBinding=parserState.isInBinding;parserState.isInBinding=null;let extra="";if(endChar===">"){extra="<"}else if(endChar==="]"){extra="["}else if(endChar===")"){extra="("}else if(endChar===""){extra="->"}else{extra=endChar}while(parserState.pos"]}else if(prevIs(parserState,"\"")){throw["Cannot have more than one element if you use quotes"]}if(endChar!==""){throw["Expected ",",",", ","=",", or ",endChar,...extra,", found ",c,]}throw["Expected ",","," or ","=",...extra,", found ",c,]}const posBefore=parserState.pos;start=parserState.pos;getNextElem(query,parserState,elems,endChar!=="");if(endChar!==""&&parserState.pos>=parserState.length){throw["Unclosed ",extra]}if(posBefore===parserState.pos){parserState.pos+=1}foundStopChar=false}if(parserState.pos>=parserState.length&&endChar!==""){throw["Unclosed ",extra]}parserState.pos+=1;parserState.typeFilter=oldTypeFilter;parserState.isInBinding=oldIsInBinding;return{foundSeparator}}function checkExtraTypeFilterCharacters(start,parserState){const query=parserState.userQuery.slice(start,parserState.pos).trim();for(const c in query){if(!isIdentCharacter(query[c])){throw["Unexpected ",query[c]," in type filter (before ",":",")",]}}}function parseInput(query,parserState){let foundStopChar=true;let start=parserState.pos;while(parserState.pos"){if(isReturnArrow(parserState)){break}throw["Unexpected ",c," (did you mean ","->","?)"]}else if(parserState.pos>0){throw["Unexpected ",c," after ",parserState.userQuery[parserState.pos-1]]}throw["Unexpected ",c]}else if(c===":"&&!isPathStart(parserState)){if(parserState.typeFilter!==null){throw["Unexpected ",":"," (expected path after type filter ",parserState.typeFilter+":",")",]}else if(query.elems.length===0){throw["Expected type filter before ",":"]}else if(query.literalSearch){throw["Cannot use quotes on type filter"]}const typeFilterElem=query.elems.pop();checkExtraTypeFilterCharacters(start,parserState);parserState.typeFilter=typeFilterElem.name;parserState.pos+=1;parserState.totalElems-=1;query.literalSearch=false;foundStopChar=true;continue}else if(c===" "){skipWhitespace(parserState);continue}if(!foundStopChar){let extra="";if(isLastElemGeneric(query.elems,parserState)){extra=[" after ",">"]}else if(prevIs(parserState,"\"")){throw["Cannot have more than one element if you use quotes"]}if(parserState.typeFilter!==null){throw["Expected ",","," or ","->",...extra,", found ",c,]}throw["Expected ",",",", ",":"," or ","->",...extra,", found ",c,]}const before=query.elems.length;start=parserState.pos;getNextElem(query,parserState,query.elems,false);if(query.elems.length===before){parserState.pos+=1}foundStopChar=false}if(parserState.typeFilter!==null){throw["Unexpected ",":"," (expected path after type filter ",parserState.typeFilter+":",")",]}while(parserState.pos"]}break}else{parserState.pos+=1}}}function newParsedQuery(userQuery){return{original:userQuery,userQuery:userQuery.toLowerCase(),elems:[],returned:[],foundElems:0,totalElems:0,literalSearch:false,error:null,correction:null,proposeCorrectionFrom:null,proposeCorrectionTo:null,typeFingerprint:new Uint32Array(4),}}function buildUrl(search,filterCrates){let extra="?search="+encodeURIComponent(search);if(filterCrates!==null){extra+="&filter-crate="+encodeURIComponent(filterCrates)}return getNakedUrl()+extra+window.location.hash}function getFilterCrates(){const elem=document.getElementById("crate-search");if(elem&&elem.value!=="all crates"&&rawSearchIndex.has(elem.value)){return elem.value}return null}function parseQuery(userQuery){function convertTypeFilterOnElem(elem){if(elem.typeFilter!==null){let typeFilter=elem.typeFilter;if(typeFilter==="const"){typeFilter="constant"}elem.typeFilter=itemTypeFromName(typeFilter)}else{elem.typeFilter=NO_TYPE_FILTER}for(const elem2 of elem.generics){convertTypeFilterOnElem(elem2)}for(const constraints of elem.bindings.values()){for(const constraint of constraints){convertTypeFilterOnElem(constraint)}}}userQuery=userQuery.trim().replace(/\r|\n|\t/g," ");const parserState={length:userQuery.length,pos:0,totalElems:0,genericsElems:0,typeFilter:null,isInBinding:null,userQuery:userQuery.toLowerCase(),};let query=newParsedQuery(userQuery);try{parseInput(query,parserState);for(const elem of query.elems){convertTypeFilterOnElem(elem)}for(const elem of query.returned){convertTypeFilterOnElem(elem)}}catch(err){query=newParsedQuery(userQuery);query.error=err;return query}if(!query.literalSearch){query.literalSearch=parserState.totalElems>1}query.foundElems=query.elems.length+query.returned.length;query.totalElems=parserState.totalElems;return query}function createQueryResults(results_in_args,results_returned,results_others,parsedQuery){return{"in_args":results_in_args,"returned":results_returned,"others":results_others,"query":parsedQuery,}}function execQuery(parsedQuery,filterCrates,currentCrate){const results_others=new Map(),results_in_args=new Map(),results_returned=new Map();function transformResults(results){const duplicates=new Set();const out=[];for(const result of results){if(result.id!==-1){const obj=searchIndex[result.id];obj.dist=result.dist;const res=buildHrefAndPath(obj);obj.displayPath=pathSplitter(res[0]);obj.fullPath=obj.displayPath+obj.name;obj.fullPath+="|"+obj.ty;if(duplicates.has(obj.fullPath)){continue}duplicates.add(obj.fullPath);obj.href=res[1];out.push(obj);if(out.length>=MAX_RESULTS){break}}}return out}function sortResults(results,isType,preferredCrate){if(results.size===0){return[]}const userQuery=parsedQuery.userQuery;const result_list=[];for(const result of results.values()){result.item=searchIndex[result.id];result.word=searchIndex[result.id].word;result_list.push(result)}result_list.sort((aaa,bbb)=>{let a,b;a=(aaa.word!==userQuery);b=(bbb.word!==userQuery);if(a!==b){return a-b}a=(aaa.index<0);b=(bbb.index<0);if(a!==b){return a-b}a=aaa.path_dist;b=bbb.path_dist;if(a!==b){return a-b}a=aaa.index;b=bbb.index;if(a!==b){return a-b}a=(aaa.dist);b=(bbb.dist);if(a!==b){return a-b}a=aaa.item.deprecated;b=bbb.item.deprecated;if(a!==b){return a-b}a=(aaa.item.crate!==preferredCrate);b=(bbb.item.crate!==preferredCrate);if(a!==b){return a-b}a=aaa.word.length;b=bbb.word.length;if(a!==b){return a-b}a=aaa.word;b=bbb.word;if(a!==b){return(a>b?+1:-1)}a=(aaa.item.desc==="");b=(bbb.item.desc==="");if(a!==b){return a-b}a=aaa.item.ty;b=bbb.item.ty;if(a!==b){return a-b}a=aaa.item.path;b=bbb.item.path;if(a!==b){return(a>b?+1:-1)}return 0});return transformResults(result_list)}function unifyFunctionTypes(fnTypesIn,queryElems,whereClause,mgensIn,solutionCb){const mgens=mgensIn===null?null:new Map(mgensIn);if(queryElems.length===0){return!solutionCb||solutionCb(mgens)}if(!fnTypesIn||fnTypesIn.length===0){return false}const ql=queryElems.length;const fl=fnTypesIn.length;if(ql===1&&queryElems[0].generics.length===0&&queryElems[0].bindings.size===0){const queryElem=queryElems[0];for(const fnType of fnTypesIn){if(!unifyFunctionTypeIsMatchCandidate(fnType,queryElem,whereClause,mgens)){continue}if(fnType.id<0&&queryElem.id<0){if(mgens&&mgens.has(fnType.id)&&mgens.get(fnType.id)!==queryElem.id){continue}const mgensScratch=new Map(mgens);mgensScratch.set(fnType.id,queryElem.id);if(!solutionCb||solutionCb(mgensScratch)){return true}}else if(!solutionCb||solutionCb(mgens?new Map(mgens):null)){return true}}for(const fnType of fnTypesIn){if(!unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens)){continue}if(fnType.id<0){if(mgens&&mgens.has(fnType.id)&&mgens.get(fnType.id)!==0){continue}const mgensScratch=new Map(mgens);mgensScratch.set(fnType.id,0);if(unifyFunctionTypes(whereClause[(-fnType.id)-1],queryElems,whereClause,mgensScratch,solutionCb)){return true}}else if(unifyFunctionTypes([...fnType.generics,...Array.from(fnType.bindings.values()).flat()],queryElems,whereClause,mgens?new Map(mgens):null,solutionCb)){return true}}return false}const fnTypes=fnTypesIn.slice();const flast=fl-1;const qlast=ql-1;const queryElem=queryElems[qlast];let queryElemsTmp=null;for(let i=flast;i>=0;i-=1){const fnType=fnTypes[i];if(!unifyFunctionTypeIsMatchCandidate(fnType,queryElem,whereClause,mgens)){continue}let mgensScratch;if(fnType.id<0){mgensScratch=new Map(mgens);if(mgensScratch.has(fnType.id)&&mgensScratch.get(fnType.id)!==queryElem.id){continue}mgensScratch.set(fnType.id,queryElem.id)}else{mgensScratch=mgens}fnTypes[i]=fnTypes[flast];fnTypes.length=flast;if(!queryElemsTmp){queryElemsTmp=queryElems.slice(0,qlast)}const passesUnification=unifyFunctionTypes(fnTypes,queryElemsTmp,whereClause,mgensScratch,mgensScratch=>{if(fnType.generics.length===0&&queryElem.generics.length===0&&fnType.bindings.size===0&&queryElem.bindings.size===0){return!solutionCb||solutionCb(mgensScratch)}const solution=unifyFunctionTypeCheckBindings(fnType,queryElem,whereClause,mgensScratch);if(!solution){return false}const simplifiedGenerics=solution.simplifiedGenerics;for(const simplifiedMgens of solution.mgens){const passesUnification=unifyFunctionTypes(simplifiedGenerics,queryElem.generics,whereClause,simplifiedMgens,solutionCb);if(passesUnification){return true}}return false});if(passesUnification){return true}fnTypes[flast]=fnTypes[i];fnTypes[i]=fnType;fnTypes.length=fl}for(let i=flast;i>=0;i-=1){const fnType=fnTypes[i];if(!unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens)){continue}let mgensScratch;if(fnType.id<0){mgensScratch=new Map(mgens);if(mgensScratch.has(fnType.id)&&mgensScratch.get(fnType.id)!==0){continue}mgensScratch.set(fnType.id,0)}else{mgensScratch=mgens}const generics=fnType.id<0?whereClause[(-fnType.id)-1]:fnType.generics;const bindings=fnType.bindings?Array.from(fnType.bindings.values()).flat():[];const passesUnification=unifyFunctionTypes(fnTypes.toSpliced(i,1,...generics,...bindings),queryElems,whereClause,mgensScratch,solutionCb);if(passesUnification){return true}}return false}function unifyFunctionTypeIsMatchCandidate(fnType,queryElem,whereClause,mgensIn){if(!typePassesFilter(queryElem.typeFilter,fnType.ty)){return false}if(fnType.id<0&&queryElem.id<0){if(mgensIn){if(mgensIn.has(fnType.id)&&mgensIn.get(fnType.id)!==queryElem.id){return false}for(const[fid,qid]of mgensIn.entries()){if(fnType.id!==fid&&queryElem.id===qid){return false}if(fnType.id===fid&&queryElem.id!==qid){return false}}}return true}else{if(queryElem.id===typeNameIdOfArrayOrSlice&&(fnType.id===typeNameIdOfSlice||fnType.id===typeNameIdOfArray)){}else if(queryElem.id===typeNameIdOfTupleOrUnit&&(fnType.id===typeNameIdOfTuple||fnType.id===typeNameIdOfUnit)){}else if(fnType.id!==queryElem.id||queryElem.id===null){return false}if((fnType.generics.length+fnType.bindings.size)===0&&queryElem.generics.length!==0){return false}if(fnType.bindings.size0){const fnTypePath=fnType.path!==undefined&&fnType.path!==null?fnType.path.split("::"):[];if(queryElemPathLength>fnTypePath.length){return false}let i=0;for(const path of fnTypePath){if(path===queryElem.pathWithoutLast[i]){i+=1;if(i>=queryElemPathLength){break}}}if(i0){let mgensSolutionSet=[mgensIn];for(const[name,constraints]of queryElem.bindings.entries()){if(mgensSolutionSet.length===0){return false}if(!fnType.bindings.has(name)){return false}const fnTypeBindings=fnType.bindings.get(name);mgensSolutionSet=mgensSolutionSet.flatMap(mgens=>{const newSolutions=[];unifyFunctionTypes(fnTypeBindings,constraints,whereClause,mgens,newMgens=>{newSolutions.push(newMgens);return false});return newSolutions})}if(mgensSolutionSet.length===0){return false}const binds=Array.from(fnType.bindings.entries()).flatMap(entry=>{const[name,constraints]=entry;if(queryElem.bindings.has(name)){return[]}else{return constraints}});if(simplifiedGenerics.length>0){simplifiedGenerics=[...simplifiedGenerics,...binds]}else{simplifiedGenerics=binds}return{simplifiedGenerics,mgens:mgensSolutionSet}}return{simplifiedGenerics,mgens:[mgensIn]}}function unifyFunctionTypeIsUnboxCandidate(fnType,queryElem,whereClause,mgens){if(fnType.id<0&&queryElem.id>=0){if(!whereClause){return false}if(mgens&&mgens.has(fnType.id)&&mgens.get(fnType.id)!==0){return false}const mgensTmp=new Map(mgens);mgensTmp.set(fnType.id,null);return checkIfInList(whereClause[(-fnType.id)-1],queryElem,whereClause,mgensTmp)}else if(fnType.generics.length>0||fnType.bindings.size>0){const simplifiedGenerics=[...fnType.generics,...Array.from(fnType.bindings.values()).flat(),];return checkIfInList(simplifiedGenerics,queryElem,whereClause,mgens)}return false}function checkIfInList(list,elem,whereClause,mgens){for(const entry of list){if(checkType(entry,elem,whereClause,mgens)){return true}}return false}function checkType(row,elem,whereClause,mgens){if(row.bindings.size===0&&elem.bindings.size===0){if(elem.id<0){return row.id<0||checkIfInList(row.generics,elem,whereClause,mgens)}if(row.id>0&&elem.id>0&&elem.pathWithoutLast.length===0&&typePassesFilter(elem.typeFilter,row.ty)&&elem.generics.length===0&&elem.id!==typeNameIdOfArrayOrSlice&&elem.id!==typeNameIdOfTupleOrUnit){return row.id===elem.id||checkIfInList(row.generics,elem,whereClause,mgens)}}return unifyFunctionTypes([row],[elem],whereClause,mgens)}function checkPath(contains,ty){if(contains.length===0){return 0}const maxPathEditDistance=Math.floor(contains.reduce((acc,next)=>acc+next.length,0)/3);let ret_dist=maxPathEditDistance+1;const path=ty.path.split("::");if(ty.parent&&ty.parent.name){path.push(ty.parent.name.toLowerCase())}const length=path.length;const clength=contains.length;pathiter:for(let i=length-clength;i>=0;i-=1){let dist_total=0;for(let x=0;xmaxPathEditDistance){continue pathiter}dist_total+=dist}}ret_dist=Math.min(ret_dist,Math.round(dist_total/clength))}return ret_dist>maxPathEditDistance?null:ret_dist}function typePassesFilter(filter,type){if(filter<=NO_TYPE_FILTER||filter===type)return true;const name=itemTypes[type];switch(itemTypes[filter]){case"constant":return name==="associatedconstant";case"fn":return name==="method"||name==="tymethod";case"type":return name==="primitive"||name==="associatedtype";case"trait":return name==="traitalias"}return false}function createAliasFromItem(item){return{crate:item.crate,name:item.name,path:item.path,desc:item.desc,ty:item.ty,parent:item.parent,type:item.type,is_alias:true,deprecated:item.deprecated,implDisambiguator:item.implDisambiguator,}}function handleAliases(ret,query,filterCrates,currentCrate){const lowerQuery=query.toLowerCase();const aliases=[];const crateAliases=[];if(filterCrates!==null){if(ALIASES.has(filterCrates)&&ALIASES.get(filterCrates).has(lowerQuery)){const query_aliases=ALIASES.get(filterCrates).get(lowerQuery);for(const alias of query_aliases){aliases.push(createAliasFromItem(searchIndex[alias]))}}}else{for(const[crate,crateAliasesIndex]of ALIASES){if(crateAliasesIndex.has(lowerQuery)){const pushTo=crate===currentCrate?crateAliases:aliases;const query_aliases=crateAliasesIndex.get(lowerQuery);for(const alias of query_aliases){pushTo.push(createAliasFromItem(searchIndex[alias]))}}}}const sortFunc=(aaa,bbb)=>{if(aaa.path{alias.alias=query;const res=buildHrefAndPath(alias);alias.displayPath=pathSplitter(res[0]);alias.fullPath=alias.displayPath+alias.name;alias.href=res[1];ret.others.unshift(alias);if(ret.others.length>MAX_RESULTS){ret.others.pop()}};aliases.forEach(pushFunc);crateAliases.forEach(pushFunc)}function addIntoResults(results,fullId,id,index,dist,path_dist,maxEditDistance){if(dist<=maxEditDistance||index!==-1){if(results.has(fullId)){const result=results.get(fullId);if(result.dontValidate||result.dist<=dist){return}}results.set(fullId,{id:id,index:index,dontValidate:parsedQuery.literalSearch,dist:dist,path_dist:path_dist,})}}function handleSingleArg(row,pos,elem,results_others,results_in_args,results_returned,maxEditDistance){if(!row||(filterCrates!==null&&row.crate!==filterCrates)){return}let path_dist=0;const fullId=row.id;const tfpDist=compareTypeFingerprints(fullId,parsedQuery.typeFingerprint);if(tfpDist!==null){const in_args=row.type&&row.type.inputs&&checkIfInList(row.type.inputs,elem,row.type.where_clause);const returned=row.type&&row.type.output&&checkIfInList(row.type.output,elem,row.type.where_clause);if(in_args){results_in_args.max_dist=Math.max(results_in_args.max_dist||0,tfpDist);const maxDist=results_in_args.sizenormalizedIndex&&normalizedIndex!==-1)){index=normalizedIndex}if(elem.fullPath.length>1){path_dist=checkPath(elem.pathWithoutLast,row);if(path_dist===null){return}}if(parsedQuery.literalSearch){if(row.word===elem.pathLast){addIntoResults(results_others,fullId,pos,index,0,path_dist)}return}const dist=editDistance(row.normalizedName,elem.normalizedPathLast,maxEditDistance);if(index===-1&&dist>maxEditDistance){return}addIntoResults(results_others,fullId,pos,index,dist,path_dist,maxEditDistance)}function handleArgs(row,pos,results){if(!row||(filterCrates!==null&&row.crate!==filterCrates)||!row.type){return}const tfpDist=compareTypeFingerprints(row.id,parsedQuery.typeFingerprint);if(tfpDist===null){return}if(results.size>=MAX_RESULTS&&tfpDist>results.max_dist){return}if(!unifyFunctionTypes(row.type.inputs,parsedQuery.elems,row.type.where_clause,null,mgens=>{return unifyFunctionTypes(row.type.output,parsedQuery.returned,row.type.where_clause,mgens)})){return}results.max_dist=Math.max(results.max_dist||0,tfpDist);addIntoResults(results,row.id,pos,0,tfpDist,0,Number.MAX_VALUE)}function innerRunQuery(){const queryLen=parsedQuery.elems.reduce((acc,next)=>acc+next.pathLast.length,0)+parsedQuery.returned.reduce((acc,next)=>acc+next.pathLast.length,0);const maxEditDistance=Math.floor(queryLen/3);const genericSymbols=new Map();function convertNameToId(elem,isAssocType){if(typeNameIdMap.has(elem.normalizedPathLast)&&(isAssocType||!typeNameIdMap.get(elem.normalizedPathLast).assocOnly)){elem.id=typeNameIdMap.get(elem.normalizedPathLast).id}else if(!parsedQuery.literalSearch){let match=null;let matchDist=maxEditDistance+1;let matchName="";for(const[name,{id,assocOnly}]of typeNameIdMap){const dist=editDistance(name,elem.normalizedPathLast,maxEditDistance);if(dist<=matchDist&&dist<=maxEditDistance&&(isAssocType||!assocOnly)){if(dist===matchDist&&matchName>name){continue}match=id;matchDist=dist;matchName=name}}if(match!==null){parsedQuery.correction=matchName}elem.id=match}if((elem.id===null&&parsedQuery.totalElems>1&&elem.typeFilter===-1&&elem.generics.length===0&&elem.bindings.size===0)||elem.typeFilter===TY_GENERIC){if(genericSymbols.has(elem.name)){elem.id=genericSymbols.get(elem.name)}else{elem.id=-(genericSymbols.size+1);genericSymbols.set(elem.name,elem.id)}if(elem.typeFilter===-1&&elem.name.length>=3){const maxPartDistance=Math.floor(elem.name.length/3);let matchDist=maxPartDistance+1;let matchName="";for(const name of typeNameIdMap.keys()){const dist=editDistance(name,elem.name,maxPartDistance);if(dist<=matchDist&&dist<=maxPartDistance){if(dist===matchDist&&matchName>name){continue}matchDist=dist;matchName=name}}if(matchName!==""){parsedQuery.proposeCorrectionFrom=elem.name;parsedQuery.proposeCorrectionTo=matchName}}elem.typeFilter=TY_GENERIC}if(elem.generics.length>0&&elem.typeFilter===TY_GENERIC){parsedQuery.error=["Generic type parameter ",elem.name," does not accept generic parameters",]}for(const elem2 of elem.generics){convertNameToId(elem2)}elem.bindings=new Map(Array.from(elem.bindings.entries()).map(entry=>{const[name,constraints]=entry;if(!typeNameIdMap.has(name)){parsedQuery.error=["Type parameter ",name," does not exist",];return[null,[]]}for(const elem2 of constraints){convertNameToId(elem2)}return[typeNameIdMap.get(name).id,constraints]}))}const fps=new Set();for(const elem of parsedQuery.elems){convertNameToId(elem);buildFunctionTypeFingerprint(elem,parsedQuery.typeFingerprint,fps)}for(const elem of parsedQuery.returned){convertNameToId(elem);buildFunctionTypeFingerprint(elem,parsedQuery.typeFingerprint,fps)}if(parsedQuery.foundElems===1&&parsedQuery.returned.length===0){if(parsedQuery.elems.length===1){const elem=parsedQuery.elems[0];for(let i=0,nSearchIndex=searchIndex.length;i0){const sortQ=(a,b)=>{const ag=a.generics.length===0&&a.bindings.size===0;const bg=b.generics.length===0&&b.bindings.size===0;if(ag!==bg){return ag-bg}const ai=a.id>0;const bi=b.id>0;return ai-bi};parsedQuery.elems.sort(sortQ);parsedQuery.returned.sort(sortQ);for(let i=0,nSearchIndex=searchIndex.length;i");if(tmp.endsWith("")){return tmp.slice(0,tmp.length-6)}return tmp}function addTab(array,query,display){const extraClass=display?" active":"";const output=document.createElement("div");if(array.length>0){output.className="search-results "+extraClass;array.forEach(item=>{const name=item.name;const type=itemTypes[item.ty];const longType=longItemTypes[item.ty];const typeName=longType.length!==0?`${longType}`:"?";const link=document.createElement("a");link.className="result-"+type;link.href=item.href;const resultName=document.createElement("div");resultName.className="result-name";resultName.insertAdjacentHTML("beforeend",`${typeName}`);link.appendChild(resultName);let alias=" ";if(item.is_alias){alias=`
\ +${item.alias} - see \ +
`}resultName.insertAdjacentHTML("beforeend",`
${alias}\ +${item.displayPath}${name}\ +
`);const description=document.createElement("div");description.className="desc";description.insertAdjacentHTML("beforeend",item.desc);link.appendChild(description);output.appendChild(link)})}else if(query.error===null){output.className="search-failed"+extraClass;output.innerHTML="No results :(
"+"Try on DuckDuckGo?

"+"Or try looking in one of these:"}return[output,array.length]}function makeTabHeader(tabNb,text,nbElems){const fmtNbElems=nbElems<10?`\u{2007}(${nbElems})\u{2007}\u{2007}`:nbElems<100?`\u{2007}(${nbElems})\u{2007}`:`\u{2007}(${nbElems})`;if(searchState.currentTab===tabNb){return""}return""}function showResults(results,go_to_first,filterCrates){const search=searchState.outputElement();if(go_to_first||(results.others.length===1&&getSettingValue("go-to-only-result")==="true")){window.onunload=()=>{};searchState.removeQueryParameters();const elem=document.createElement("a");elem.href=results.others[0].href;removeClass(elem,"active");document.body.appendChild(elem);elem.click();return}if(results.query===undefined){results.query=parseQuery(searchState.input.value)}currentResults=results.query.userQuery;const ret_others=addTab(results.others,results.query,true);const ret_in_args=addTab(results.in_args,results.query,false);const ret_returned=addTab(results.returned,results.query,false);let currentTab=searchState.currentTab;if((currentTab===0&&ret_others[1]===0)||(currentTab===1&&ret_in_args[1]===0)||(currentTab===2&&ret_returned[1]===0)){if(ret_others[1]!==0){currentTab=0}else if(ret_in_args[1]!==0){currentTab=1}else if(ret_returned[1]!==0){currentTab=2}}let crates="";if(rawSearchIndex.size>1){crates=" in 
"}let output=`

Results${crates}

`;if(results.query.error!==null){const error=results.query.error;error.forEach((value,index)=>{value=value.split("<").join("<").split(">").join(">");if(index%2!==0){error[index]=`${value.replaceAll(" ", " ")}`}else{error[index]=value}});output+=`

Query parser error: "${error.join("")}".

`;output+="
"+makeTabHeader(0,"In Names",ret_others[1])+"
";currentTab=0}else if(results.query.foundElems<=1&&results.query.returned.length===0){output+="
"+makeTabHeader(0,"In Names",ret_others[1])+makeTabHeader(1,"In Parameters",ret_in_args[1])+makeTabHeader(2,"In Return Types",ret_returned[1])+"
"}else{const signatureTabTitle=results.query.elems.length===0?"In Function Return Types":results.query.returned.length===0?"In Function Parameters":"In Function Signatures";output+="
"+makeTabHeader(0,signatureTabTitle,ret_others[1])+"
";currentTab=0}if(results.query.correction!==null){const orig=results.query.returned.length>0?results.query.returned[0].name:results.query.elems[0].name;output+="

"+`Type "${orig}" not found. `+"Showing results for closest type name "+`"${results.query.correction}" instead.

`}if(results.query.proposeCorrectionFrom!==null){const orig=results.query.proposeCorrectionFrom;const targ=results.query.proposeCorrectionTo;output+="

"+`Type "${orig}" not found and used as generic parameter. `+`Consider searching for "${targ}" instead.

`}const resultsElem=document.createElement("div");resultsElem.id="results";resultsElem.appendChild(ret_others[0]);resultsElem.appendChild(ret_in_args[0]);resultsElem.appendChild(ret_returned[0]);search.innerHTML=output;const crateSearch=document.getElementById("crate-search");if(crateSearch){crateSearch.addEventListener("input",updateCrate)}search.appendChild(resultsElem);searchState.showResults(search);const elems=document.getElementById("search-tabs").childNodes;searchState.focusedByTab=[];let i=0;for(const elem of elems){const j=i;elem.onclick=()=>printTab(j);searchState.focusedByTab.push(null);i+=1}printTab(currentTab)}function updateSearchHistory(url){if(!browserSupportsHistoryApi()){return}const params=searchState.getQueryStringParams();if(!history.state&&!params.search){history.pushState(null,"",url)}else{history.replaceState(null,"",url)}}function search(forced){const query=parseQuery(searchState.input.value.trim());let filterCrates=getFilterCrates();if(!forced&&query.userQuery===currentResults){if(query.userQuery.length>0){putBackSearch()}return}searchState.setLoadingSearch();const params=searchState.getQueryStringParams();if(filterCrates===null&¶ms["filter-crate"]!==undefined){filterCrates=params["filter-crate"]}searchState.title="Results for "+query.original+" - Rust";updateSearchHistory(buildUrl(query.original,filterCrates));showResults(execQuery(query,filterCrates,window.currentCrate),params.go_to_first,filterCrates)}function buildItemSearchTypeAll(types,lowercasePaths){return types.length>0?types.map(type=>buildItemSearchType(type,lowercasePaths)):EMPTY_GENERICS_ARRAY}const EMPTY_BINDINGS_MAP=new Map();const EMPTY_GENERICS_ARRAY=[];let TYPES_POOL=new Map();function buildItemSearchType(type,lowercasePaths,isAssocType){const PATH_INDEX_DATA=0;const GENERICS_DATA=1;const BINDINGS_DATA=2;let pathIndex,generics,bindings;if(typeof type==="number"){pathIndex=type;generics=EMPTY_GENERICS_ARRAY;bindings=EMPTY_BINDINGS_MAP}else{pathIndex=type[PATH_INDEX_DATA];generics=buildItemSearchTypeAll(type[GENERICS_DATA],lowercasePaths);if(type.length>BINDINGS_DATA&&type[BINDINGS_DATA].length>0){bindings=new Map(type[BINDINGS_DATA].map(binding=>{const[assocType,constraints]=binding;return[buildItemSearchType(assocType,lowercasePaths,true).id,buildItemSearchTypeAll(constraints,lowercasePaths),]}))}else{bindings=EMPTY_BINDINGS_MAP}}let result;if(pathIndex<0){result={id:pathIndex,ty:TY_GENERIC,path:null,generics,bindings,}}else if(pathIndex===0){result={id:null,ty:null,path:null,generics,bindings,}}else{const item=lowercasePaths[pathIndex-1];result={id:buildTypeMapIndex(item.name,isAssocType),ty:item.ty,path:item.path,generics,bindings,}}const cr=TYPES_POOL.get(result.id);if(cr){if(cr.generics.length===result.generics.length&&cr.generics!==result.generics&&cr.generics.every((x,i)=>result.generics[i]===x)){result.generics=cr.generics}if(cr.bindings.size===result.bindings.size&&cr.bindings!==result.bindings){let ok=true;for(const[k,v]of cr.bindings.entries()){const v2=result.bindings.get(v);if(!v2){ok=false;break}if(v!==v2&&v.length===v2.length&&v.every((x,i)=>v2[i]===x)){result.bindings.set(k,v)}else if(v!==v2){ok=false;break}}if(ok){result.bindings=cr.bindings}}if(cr.ty===result.ty&&cr.path===result.path&&cr.bindings===result.bindings&&cr.generics===result.generics&&cr.ty===result.ty){return cr}}TYPES_POOL.set(result.id,result);return result}function buildFunctionSearchType(itemFunctionDecoder,lowercasePaths){const c=itemFunctionDecoder.string.charCodeAt(itemFunctionDecoder.offset);itemFunctionDecoder.offset+=1;const[zero,ua,la,ob,cb]=["0","@","`","{","}"].map(c=>c.charCodeAt(0));if(c===la){return null}if(c>=zero&&c>1];itemFunctionDecoder.offset+=1;return sign?-value:value}const functionSearchType=decodeList();const INPUTS_DATA=0;const OUTPUT_DATA=1;let inputs,output;if(typeof functionSearchType[INPUTS_DATA]==="number"){inputs=[buildItemSearchType(functionSearchType[INPUTS_DATA],lowercasePaths)]}else{inputs=buildItemSearchTypeAll(functionSearchType[INPUTS_DATA],lowercasePaths)}if(functionSearchType.length>1){if(typeof functionSearchType[OUTPUT_DATA]==="number"){output=[buildItemSearchType(functionSearchType[OUTPUT_DATA],lowercasePaths)]}else{output=buildItemSearchTypeAll(functionSearchType[OUTPUT_DATA],lowercasePaths)}}else{output=[]}const where_clause=[];const l=functionSearchType.length;for(let i=2;i16){itemFunctionDecoder.backrefQueue.pop()}return ret}function buildFunctionTypeFingerprint(type,output,fps){let input=type.id;if(input===typeNameIdOfArray||input===typeNameIdOfSlice){input=typeNameIdOfArrayOrSlice}if(input===typeNameIdOfTuple||input===typeNameIdOfUnit){input=typeNameIdOfTupleOrUnit}const hashint1=k=>{k=(~~k+0x7ed55d16)+(k<<12);k=(k ^ 0xc761c23c)^(k>>>19);k=(~~k+0x165667b1)+(k<<5);k=(~~k+0xd3a2646c)^(k<<9);k=(~~k+0xfd7046c5)+(k<<3);return(k ^ 0xb55a4f09)^(k>>>16)};const hashint2=k=>{k=~k+(k<<15);k ^=k>>>12;k+=k<<2;k ^=k>>>4;k=Math.imul(k,2057);return k ^(k>>16)};if(input!==null){const h0a=hashint1(input);const h0b=hashint2(input);const h1a=~~(h0a+Math.imul(h0b,2));const h1b=~~(h0a+Math.imul(h0b,3));const h2a=~~(h0a+Math.imul(h0b,4));const h2b=~~(h0a+Math.imul(h0b,5));output[0]|=(1<<(h0a%32))|(1<<(h1b%32));output[1]|=(1<<(h1a%32))|(1<<(h2b%32));output[2]|=(1<<(h2a%32))|(1<<(h0b%32));fps.add(input)}for(const g of type.generics){buildFunctionTypeFingerprint(g,output,fps)}const fb={id:null,ty:0,generics:EMPTY_GENERICS_ARRAY,bindings:EMPTY_BINDINGS_MAP,};for(const[k,v]of type.bindings.entries()){fb.id=k;fb.generics=v;buildFunctionTypeFingerprint(fb,output,fps)}output[3]=fps.size}function compareTypeFingerprints(fullId,queryFingerprint){const fh0=functionTypeFingerprint[fullId*4];const fh1=functionTypeFingerprint[(fullId*4)+1];const fh2=functionTypeFingerprint[(fullId*4)+2];const[qh0,qh1,qh2]=queryFingerprint;const[in0,in1,in2]=[fh0&qh0,fh1&qh1,fh2&qh2];if((in0 ^ qh0)||(in1 ^ qh1)||(in2 ^ qh2)){return null}return functionTypeFingerprint[(fullId*4)+3]}function buildIndex(rawSearchIndex){searchIndex=[];typeNameIdMap=new Map();const charA="A".charCodeAt(0);let currentIndex=0;let id=0;typeNameIdOfArray=buildTypeMapIndex("array");typeNameIdOfSlice=buildTypeMapIndex("slice");typeNameIdOfTuple=buildTypeMapIndex("tuple");typeNameIdOfUnit=buildTypeMapIndex("unit");typeNameIdOfArrayOrSlice=buildTypeMapIndex("[]");typeNameIdOfTupleOrUnit=buildTypeMapIndex("()");for(const crate of rawSearchIndex.values()){id+=crate.t.length+1}functionTypeFingerprint=new Uint32Array((id+1)*4);id=0;for(const[crate,crateCorpus]of rawSearchIndex){const crateRow={crate:crate,ty:3,name:crate,path:"",desc:crateCorpus.doc,parent:undefined,type:null,id:id,word:crate,normalizedName:crate.indexOf("_")===-1?crate:crate.replace(/_/g,""),deprecated:null,implDisambiguator:null,};id+=1;searchIndex.push(crateRow);currentIndex+=1;const itemTypes=crateCorpus.t;const itemNames=crateCorpus.n;const itemPaths=new Map(crateCorpus.q);const itemDescs=crateCorpus.d;const itemParentIdxs=crateCorpus.i;const itemFunctionDecoder={string:crateCorpus.f,offset:0,backrefQueue:[],};const deprecatedItems=new Set(crateCorpus.c);const implDisambiguator=new Map(crateCorpus.b);const paths=crateCorpus.p;const aliases=crateCorpus.a;const lowercasePaths=[];let len=paths.length;let lastPath=itemPaths.get(0);for(let i=0;i2){path=itemPaths.has(elem[2])?itemPaths.get(elem[2]):lastPath;lastPath=path}lowercasePaths.push({ty:ty,name:name.toLowerCase(),path:path});paths[i]={ty:ty,name:name,path:path}}lastPath="";len=itemTypes.length;for(let i=0;i0?paths[itemParentIdxs[i]-1]:undefined,type,id:id,word,normalizedName:word.indexOf("_")===-1?word:word.replace(/_/g,""),deprecated:deprecatedItems.has(i),implDisambiguator:implDisambiguator.has(i)?implDisambiguator.get(i):null,};id+=1;searchIndex.push(row);lastPath=row.path}if(aliases){const currentCrateAliases=new Map();ALIASES.set(crate,currentCrateAliases);for(const alias_name in aliases){if(!Object.prototype.hasOwnProperty.call(aliases,alias_name)){continue}let currentNameAliases;if(currentCrateAliases.has(alias_name)){currentNameAliases=currentCrateAliases.get(alias_name)}else{currentNameAliases=[];currentCrateAliases.set(alias_name,currentNameAliases)}for(const local_alias of aliases[alias_name]){currentNameAliases.push(local_alias+currentIndex)}}}currentIndex+=itemTypes.length}TYPES_POOL=new Map()}function onSearchSubmit(e){e.preventDefault();searchState.clearInputTimeout();search()}function putBackSearch(){const search_input=searchState.input;if(!searchState.input){return}if(search_input.value!==""&&!searchState.isDisplayed()){searchState.showResults();if(browserSupportsHistoryApi()){history.replaceState(null,"",buildUrl(search_input.value,getFilterCrates()))}document.title=searchState.title}}function registerSearchEvents(){const params=searchState.getQueryStringParams();if(searchState.input.value===""){searchState.input.value=params.search||""}const searchAfter500ms=()=>{searchState.clearInputTimeout();if(searchState.input.value.length===0){searchState.hideResults()}else{searchState.timeout=setTimeout(search,500)}};searchState.input.onkeyup=searchAfter500ms;searchState.input.oninput=searchAfter500ms;document.getElementsByClassName("search-form")[0].onsubmit=onSearchSubmit;searchState.input.onchange=e=>{if(e.target!==document.activeElement){return}searchState.clearInputTimeout();setTimeout(search,0)};searchState.input.onpaste=searchState.input.onchange;searchState.outputElement().addEventListener("keydown",e=>{if(e.altKey||e.ctrlKey||e.shiftKey||e.metaKey){return}if(e.which===38){const previous=document.activeElement.previousElementSibling;if(previous){previous.focus()}else{searchState.focus()}e.preventDefault()}else if(e.which===40){const next=document.activeElement.nextElementSibling;if(next){next.focus()}const rect=document.activeElement.getBoundingClientRect();if(window.innerHeight-rect.bottom{if(e.which===40){focusSearchResult();e.preventDefault()}});searchState.input.addEventListener("focus",()=>{putBackSearch()});searchState.input.addEventListener("blur",()=>{searchState.input.placeholder=searchState.input.origPlaceholder});if(browserSupportsHistoryApi()){const previousTitle=document.title;window.addEventListener("popstate",e=>{const params=searchState.getQueryStringParams();document.title=previousTitle;currentResults=null;if(params.search&¶ms.search.length>0){searchState.input.value=params.search;e.preventDefault();search()}else{searchState.input.value="";searchState.hideResults()}})}window.onpageshow=()=>{const qSearch=searchState.getQueryStringParams().search;if(searchState.input.value===""&&qSearch){searchState.input.value=qSearch}search()}}function updateCrate(ev){if(ev.target.value==="all crates"){const query=searchState.input.value.trim();updateSearchHistory(buildUrl(query,null))}currentResults=null;search(true)}buildIndex(rawSearchIndex);if(typeof window!=="undefined"){registerSearchEvents();if(window.searchState.getQueryStringParams().search){search()}}if(typeof exports!=="undefined"){exports.initSearch=initSearch;exports.execQuery=execQuery;exports.parseQuery=parseQuery}}if(typeof window!=="undefined"){window.initSearch=initSearch;if(window.searchIndex!==undefined){initSearch(window.searchIndex)}}else{initSearch(new Map())}})() \ No newline at end of file diff --git a/docs/windows_docs/static.files/settings-4313503d2e1961c2.js b/docs/windows_docs/static.files/settings-4313503d2e1961c2.js new file mode 100644 index 00000000..ab425fe4 --- /dev/null +++ b/docs/windows_docs/static.files/settings-4313503d2e1961c2.js @@ -0,0 +1,17 @@ +"use strict";(function(){const isSettingsPage=window.location.pathname.endsWith("/settings.html");function changeSetting(settingName,value){if(settingName==="theme"){const useSystem=value==="system preference"?"true":"false";updateLocalStorage("use-system-theme",useSystem)}updateLocalStorage(settingName,value);switch(settingName){case"theme":case"preferred-dark-theme":case"preferred-light-theme":updateTheme();updateLightAndDark();break;case"line-numbers":if(value===true){window.rustdoc_add_line_numbers_to_examples()}else{window.rustdoc_remove_line_numbers_from_examples()}break;case"hide-sidebar":if(value===true){addClass(document.documentElement,"hide-sidebar")}else{removeClass(document.documentElement,"hide-sidebar")}break}}function showLightAndDark(){removeClass(document.getElementById("preferred-light-theme"),"hidden");removeClass(document.getElementById("preferred-dark-theme"),"hidden")}function hideLightAndDark(){addClass(document.getElementById("preferred-light-theme"),"hidden");addClass(document.getElementById("preferred-dark-theme"),"hidden")}function updateLightAndDark(){const useSystem=getSettingValue("use-system-theme");if(useSystem==="true"||(useSystem===null&&getSettingValue("theme")===null)){showLightAndDark()}else{hideLightAndDark()}}function setEvents(settingsElement){updateLightAndDark();onEachLazy(settingsElement.querySelectorAll("input[type=\"checkbox\"]"),toggle=>{const settingId=toggle.id;const settingValue=getSettingValue(settingId);if(settingValue!==null){toggle.checked=settingValue==="true"}toggle.onchange=()=>{changeSetting(toggle.id,toggle.checked)}});onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"),elem=>{const settingId=elem.name;let settingValue=getSettingValue(settingId);if(settingId==="theme"){const useSystem=getSettingValue("use-system-theme");if(useSystem==="true"||settingValue===null){settingValue=useSystem==="false"?"light":"system preference"}}if(settingValue!==null&&settingValue!=="null"){elem.checked=settingValue===elem.value}elem.addEventListener("change",ev=>{changeSetting(ev.target.name,ev.target.value)})})}function buildSettingsPageSections(settings){let output="";for(const setting of settings){const js_data_name=setting["js_name"];const setting_name=setting["name"];if(setting["options"]!==undefined){output+=`\ +
+
${setting_name}
+
`;onEach(setting["options"],option=>{const checked=option===setting["default"]?" checked":"";const full=`${js_data_name}-${option.replace(/ /g,"-")}`;output+=`\ + `});output+=`\ +
+
`}else{const checked=setting["default"]===true?" checked":"";output+=`\ +
\ + \ +
`}}return output}function buildSettingsPage(){const theme_names=getVar("themes").split(",").filter(t=>t);theme_names.push("light","dark","ayu");const settings=[{"name":"Theme","js_name":"theme","default":"system preference","options":theme_names.concat("system preference"),},{"name":"Preferred light theme","js_name":"preferred-light-theme","default":"light","options":theme_names,},{"name":"Preferred dark theme","js_name":"preferred-dark-theme","default":"dark","options":theme_names,},{"name":"Auto-hide item contents for large items","js_name":"auto-hide-large-items","default":true,},{"name":"Auto-hide item methods' documentation","js_name":"auto-hide-method-docs","default":false,},{"name":"Auto-hide trait implementation documentation","js_name":"auto-hide-trait-implementations","default":false,},{"name":"Directly go to item in search if there is only one result","js_name":"go-to-only-result","default":false,},{"name":"Show line numbers on code examples","js_name":"line-numbers","default":false,},{"name":"Hide persistent navigation bar","js_name":"hide-sidebar","default":false,},{"name":"Disable keyboard shortcuts","js_name":"disable-shortcuts","default":false,},];const elementKind=isSettingsPage?"section":"div";const innerHTML=`
${buildSettingsPageSections(settings)}
`;const el=document.createElement(elementKind);el.id="settings";if(!isSettingsPage){el.className="popover"}el.innerHTML=innerHTML;if(isSettingsPage){document.getElementById(MAIN_ID).appendChild(el)}else{el.setAttribute("tabindex","-1");getSettingsButton().appendChild(el)}return el}const settingsMenu=buildSettingsPage();function displaySettings(){settingsMenu.style.display="";onEachLazy(settingsMenu.querySelectorAll("input[type='checkbox']"),el=>{const val=getSettingValue(el.id);const checked=val==="true";if(checked!==el.checked&&val!==null){el.checked=checked}})}function settingsBlurHandler(event){blurHandler(event,getSettingsButton(),window.hidePopoverMenus)}if(isSettingsPage){getSettingsButton().onclick=event=>{event.preventDefault()}}else{const settingsButton=getSettingsButton();const settingsMenu=document.getElementById("settings");settingsButton.onclick=event=>{if(settingsMenu.contains(event.target)){return}event.preventDefault();const shouldDisplaySettings=settingsMenu.style.display==="none";window.hideAllModals();if(shouldDisplaySettings){displaySettings()}};settingsButton.onblur=settingsBlurHandler;settingsButton.querySelector("a").onblur=settingsBlurHandler;onEachLazy(settingsMenu.querySelectorAll("input"),el=>{el.onblur=settingsBlurHandler});settingsMenu.onblur=settingsBlurHandler}setTimeout(()=>{setEvents(settingsMenu);if(!isSettingsPage){displaySettings()}removeClass(getSettingsButton(),"rotate")},0)})() \ No newline at end of file diff --git a/docs/windows_docs/static.files/src-script-e66d777a5a92e9b2.js b/docs/windows_docs/static.files/src-script-e66d777a5a92e9b2.js new file mode 100644 index 00000000..d0aebb85 --- /dev/null +++ b/docs/windows_docs/static.files/src-script-e66d777a5a92e9b2.js @@ -0,0 +1 @@ +"use strict";(function(){const rootPath=getVar("root-path");const NAME_OFFSET=0;const DIRS_OFFSET=1;const FILES_OFFSET=2;const RUSTDOC_MOBILE_BREAKPOINT=700;function closeSidebarIfMobile(){if(window.innerWidth{removeClass(document.documentElement,"src-sidebar-expanded");updateLocalStorage("source-sidebar-show","false")};window.rustdocShowSourceSidebar=()=>{addClass(document.documentElement,"src-sidebar-expanded");updateLocalStorage("source-sidebar-show","true")};window.rustdocToggleSrcSidebar=()=>{if(document.documentElement.classList.contains("src-sidebar-expanded")){window.rustdocCloseSourceSidebar()}else{window.rustdocShowSourceSidebar()}};function createSrcSidebar(){const container=document.querySelector("nav.sidebar");const sidebar=document.createElement("div");sidebar.id="src-sidebar";let hasFoundFile=false;for(const[key,source]of srcIndex){source[NAME_OFFSET]=key;hasFoundFile=createDirEntry(source,sidebar,"",hasFoundFile)}container.appendChild(sidebar);const selected_elem=sidebar.getElementsByClassName("selected")[0];if(typeof selected_elem!=="undefined"){selected_elem.focus()}}function highlightSrcLines(){const match=window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);if(!match){return}let from=parseInt(match[1],10);let to=from;if(typeof match[2]!=="undefined"){to=parseInt(match[2],10)}if(to{onEachLazy(e.getElementsByTagName("a"),i_e=>{removeClass(i_e,"line-highlighted")})});for(let i=from;i<=to;++i){elem=document.getElementById(i);if(!elem){break}addClass(elem,"line-highlighted")}}const handleSrcHighlight=(function(){let prev_line_id=0;const set_fragment=name=>{const x=window.scrollX,y=window.scrollY;if(browserSupportsHistoryApi()){history.replaceState(null,null,"#"+name);highlightSrcLines()}else{location.replace("#"+name)}window.scrollTo(x,y)};return ev=>{let cur_line_id=parseInt(ev.target.id,10);if(isNaN(cur_line_id)||ev.ctrlKey||ev.altKey||ev.metaKey){return}ev.preventDefault();if(ev.shiftKey&&prev_line_id){if(prev_line_id>cur_line_id){const tmp=prev_line_id;prev_line_id=cur_line_id;cur_line_id=tmp}set_fragment(prev_line_id+"-"+cur_line_id)}else{prev_line_id=cur_line_id;set_fragment(cur_line_id)}}}());window.addEventListener("hashchange",highlightSrcLines);onEachLazy(document.getElementsByClassName("src-line-numbers"),el=>{el.addEventListener("click",handleSrcHighlight)});highlightSrcLines();window.createSrcSidebar=createSrcSidebar})() \ No newline at end of file diff --git a/docs/windows_docs/static.files/storage-4c98445ec4002617.js b/docs/windows_docs/static.files/storage-4c98445ec4002617.js new file mode 100644 index 00000000..b378b856 --- /dev/null +++ b/docs/windows_docs/static.files/storage-4c98445ec4002617.js @@ -0,0 +1 @@ +"use strict";const builtinThemes=["light","dark","ayu"];const darkThemes=["dark","ayu"];window.currentTheme=document.getElementById("themeStyle");const settingsDataset=(function(){const settingsElement=document.getElementById("default-settings");return settingsElement&&settingsElement.dataset?settingsElement.dataset:null})();function getSettingValue(settingName){const current=getCurrentValue(settingName);if(current===null&&settingsDataset!==null){const def=settingsDataset[settingName.replace(/-/g,"_")];if(def!==undefined){return def}}return current}const localStoredTheme=getSettingValue("theme");function hasClass(elem,className){return elem&&elem.classList&&elem.classList.contains(className)}function addClass(elem,className){if(elem&&elem.classList){elem.classList.add(className)}}function removeClass(elem,className){if(elem&&elem.classList){elem.classList.remove(className)}}function onEach(arr,func){for(const elem of arr){if(func(elem)){return true}}return false}function onEachLazy(lazyArray,func){return onEach(Array.prototype.slice.call(lazyArray),func)}function updateLocalStorage(name,value){try{window.localStorage.setItem("rustdoc-"+name,value)}catch(e){}}function getCurrentValue(name){try{return window.localStorage.getItem("rustdoc-"+name)}catch(e){return null}}const getVar=(function getVar(name){const el=document.querySelector("head > meta[name='rustdoc-vars']");return el?el.attributes["data-"+name].value:null});function switchTheme(newThemeName,saveTheme){const themeNames=getVar("themes").split(",").filter(t=>t);themeNames.push(...builtinThemes);if(themeNames.indexOf(newThemeName)===-1){return}if(saveTheme){updateLocalStorage("theme",newThemeName)}document.documentElement.setAttribute("data-theme",newThemeName);if(builtinThemes.indexOf(newThemeName)!==-1){if(window.currentTheme){window.currentTheme.parentNode.removeChild(window.currentTheme);window.currentTheme=null}}else{const newHref=getVar("root-path")+encodeURIComponent(newThemeName)+getVar("resource-suffix")+".css";if(!window.currentTheme){if(document.readyState==="loading"){document.write(``);window.currentTheme=document.getElementById("themeStyle")}else{window.currentTheme=document.createElement("link");window.currentTheme.rel="stylesheet";window.currentTheme.id="themeStyle";window.currentTheme.href=newHref;document.documentElement.appendChild(window.currentTheme)}}else if(newHref!==window.currentTheme.href){window.currentTheme.href=newHref}}}const updateTheme=(function(){const mql=window.matchMedia("(prefers-color-scheme: dark)");function updateTheme(){if(getSettingValue("use-system-theme")!=="false"){const lightTheme=getSettingValue("preferred-light-theme")||"light";const darkTheme=getSettingValue("preferred-dark-theme")||"dark";updateLocalStorage("use-system-theme","true");switchTheme(mql.matches?darkTheme:lightTheme,true)}else{switchTheme(getSettingValue("theme"),false)}}mql.addEventListener("change",updateTheme);return updateTheme})();if(getSettingValue("use-system-theme")!=="false"&&window.matchMedia){if(getSettingValue("use-system-theme")===null&&getSettingValue("preferred-dark-theme")===null&&darkThemes.indexOf(localStoredTheme)>=0){updateLocalStorage("preferred-dark-theme",localStoredTheme)}}updateTheme();if(getSettingValue("source-sidebar-show")==="true"){addClass(document.documentElement,"src-sidebar-expanded")}if(getSettingValue("hide-sidebar")==="true"){addClass(document.documentElement,"hide-sidebar")}function updateSidebarWidth(){const desktopSidebarWidth=getSettingValue("desktop-sidebar-width");if(desktopSidebarWidth&&desktopSidebarWidth!=="null"){document.documentElement.style.setProperty("--desktop-sidebar-width",desktopSidebarWidth+"px")}const srcSidebarWidth=getSettingValue("src-sidebar-width");if(srcSidebarWidth&&srcSidebarWidth!=="null"){document.documentElement.style.setProperty("--src-sidebar-width",srcSidebarWidth+"px")}}updateSidebarWidth();window.addEventListener("pageshow",ev=>{if(ev.persisted){setTimeout(updateTheme,0);setTimeout(updateSidebarWidth,0)}}) \ No newline at end of file diff --git a/docs/windows_docs/static.files/wheel-7b819b6101059cd0.svg b/docs/windows_docs/static.files/wheel-7b819b6101059cd0.svg new file mode 100644 index 00000000..83c07f63 --- /dev/null +++ b/docs/windows_docs/static.files/wheel-7b819b6101059cd0.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/core/clone/trait.Clone.js b/docs/windows_docs/trait.impl/core/clone/trait.Clone.js new file mode 100644 index 00000000..70f71ace --- /dev/null +++ b/docs/windows_docs/trait.impl/core/clone/trait.Clone.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Clone for WindowsDxgiVideoFrameError"],["impl Clone for CapturableDisplay"],["impl Clone for StreamCreateError"],["impl Clone for AudioCaptureConfig"],["impl Clone for StreamError"],["impl Clone for CapturableContentError"],["impl Clone for WindowsCaptureConfig"],["impl Clone for WindowsPixelFormat"],["impl Clone for CapturableWindow"],["impl Clone for WindowsCapturableDisplay"],["impl Clone for WindowsDx11VideoFrameError"],["impl Clone for WindowsCapturableWindow"],["impl Clone for AudioChannelCount"],["impl Clone for CaptureConfig"],["impl Clone for WindowsAudioCaptureConfig"],["impl Clone for Rect"],["impl Clone for AudioSampleRate"],["impl Clone for Point"],["impl Clone for CapturePixelFormat"],["impl Clone for CaptureConfigError"],["impl Clone for Size"],["impl Clone for WindowsCapturableApplication"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/core/cmp/trait.Eq.js b/docs/windows_docs/trait.impl/core/cmp/trait.Eq.js new file mode 100644 index 00000000..dd171586 --- /dev/null +++ b/docs/windows_docs/trait.impl/core/cmp/trait.Eq.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Eq for CapturePixelFormat"],["impl Eq for WindowsPixelFormat"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/core/cmp/trait.PartialEq.js b/docs/windows_docs/trait.impl/core/cmp/trait.PartialEq.js new file mode 100644 index 00000000..6173eebf --- /dev/null +++ b/docs/windows_docs/trait.impl/core/cmp/trait.PartialEq.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl PartialEq for CapturePixelFormat"],["impl PartialEq for WindowsPixelFormat"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/core/default/trait.Default.js b/docs/windows_docs/trait.impl/core/default/trait.Default.js new file mode 100644 index 00000000..b59409b9 --- /dev/null +++ b/docs/windows_docs/trait.impl/core/default/trait.Default.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Default for CapturableWindowFilter"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/core/error/trait.Error.js b/docs/windows_docs/trait.impl/core/error/trait.Error.js new file mode 100644 index 00000000..01084883 --- /dev/null +++ b/docs/windows_docs/trait.impl/core/error/trait.Error.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Error for CapturableContentError"],["impl Error for WindowsDx11VideoFrameError"],["impl Error for StreamCreateError"],["impl Error for WindowsDxgiVideoFrameError"],["impl Error for StreamError"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/core/fmt/trait.Debug.js b/docs/windows_docs/trait.impl/core/fmt/trait.Debug.js new file mode 100644 index 00000000..dc0ef1ec --- /dev/null +++ b/docs/windows_docs/trait.impl/core/fmt/trait.Debug.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Debug for Point"],["impl Debug for CaptureConfig"],["impl Debug for Size"],["impl Debug for Rect"],["impl Debug for StreamEvent"],["impl Debug for WindowsCapturableWindow"],["impl Debug for WindowsCaptureConfig"],["impl Debug for VideoFrame"],["impl Debug for StreamCreateError"],["impl Debug for CapturableWindow"],["impl Debug for WindowsCapturableApplication"],["impl Debug for AudioChannelCount"],["impl Debug for WindowsDx11VideoFrameError"],["impl Debug for WindowsPixelFormat"],["impl Debug for WindowsAudioCaptureConfig"],["impl Debug for AudioCaptureConfig"],["impl Debug for StreamError"],["impl Debug for WindowsDxgiVideoFrameError"],["impl Debug for AudioSampleRate"],["impl Debug for CaptureConfigError"],["impl Debug for CapturableContentError"],["impl Debug for CapturePixelFormat"],["impl Debug for AudioFrame"],["impl Debug for StreamStopError"],["impl Debug for CapturableDisplay"],["impl Debug for WindowsCapturableDisplay"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/core/fmt/trait.Display.js b/docs/windows_docs/trait.impl/core/fmt/trait.Display.js new file mode 100644 index 00000000..6cc569bd --- /dev/null +++ b/docs/windows_docs/trait.impl/core/fmt/trait.Display.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Display for StreamError"],["impl Display for WindowsDxgiVideoFrameError"],["impl Display for CapturableContentError"],["impl Display for WindowsDx11VideoFrameError"],["impl Display for StreamCreateError"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/core/iter/traits/exact_size/trait.ExactSizeIterator.js b/docs/windows_docs/trait.impl/core/iter/traits/exact_size/trait.ExactSizeIterator.js new file mode 100644 index 00000000..2c080477 --- /dev/null +++ b/docs/windows_docs/trait.impl/core/iter/traits/exact_size/trait.ExactSizeIterator.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl ExactSizeIterator for CapturableWindowIterator<'_>"],["impl ExactSizeIterator for CapturableDisplayIterator<'_>"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/core/iter/traits/iterator/trait.Iterator.js b/docs/windows_docs/trait.impl/core/iter/traits/iterator/trait.Iterator.js new file mode 100644 index 00000000..262a72ad --- /dev/null +++ b/docs/windows_docs/trait.impl/core/iter/traits/iterator/trait.Iterator.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Iterator for CapturableWindowIterator<'_>"],["impl Iterator for CapturableDisplayIterator<'_>"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/core/marker/trait.Copy.js b/docs/windows_docs/trait.impl/core/marker/trait.Copy.js new file mode 100644 index 00000000..4ac60004 --- /dev/null +++ b/docs/windows_docs/trait.impl/core/marker/trait.Copy.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Copy for CapturePixelFormat"],["impl Copy for AudioSampleRate"],["impl Copy for Rect"],["impl Copy for AudioChannelCount"],["impl Copy for WindowsPixelFormat"],["impl Copy for Size"],["impl Copy for Point"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/core/marker/trait.Freeze.js b/docs/windows_docs/trait.impl/core/marker/trait.Freeze.js new file mode 100644 index 00000000..6aa38326 --- /dev/null +++ b/docs/windows_docs/trait.impl/core/marker/trait.Freeze.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Freeze for WindowsPixelFormat",1,["crabgrab::platform::windows::capture_stream::WindowsPixelFormat"]],["impl Freeze for WindowsAudioCaptureConfig",1,["crabgrab::platform::windows::capture_stream::WindowsAudioCaptureConfig"]],["impl Freeze for WindowsCaptureConfig",1,["crabgrab::platform::windows::capture_stream::WindowsCaptureConfig"]],["impl Freeze for WindowsCaptureStream",1,["crabgrab::platform::windows::capture_stream::WindowsCaptureStream"]],["impl Freeze for WindowsCapturableWindow",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableWindow"]],["impl Freeze for WindowsCapturableDisplay",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableDisplay"]],["impl Freeze for WindowsCapturableApplication",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableApplication"]],["impl Freeze for WindowsCapturableContent",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableContent"]],["impl Freeze for WindowsVideoFrame",1,["crabgrab::platform::windows::frame::WindowsVideoFrame"]],["impl Freeze for WindowsAudioFrame",1,["crabgrab::platform::windows::frame::WindowsAudioFrame"]],["impl Freeze for WindowsDxgiVideoFrameError",1,["crabgrab::feature::dxgi::WindowsDxgiVideoFrameError"]],["impl Freeze for WindowsDx11VideoFrameError",1,["crabgrab::feature::dx11::WindowsDx11VideoFrameError"]],["impl Freeze for Size",1,["crabgrab::util::Size"]],["impl Freeze for Point",1,["crabgrab::util::Point"]],["impl Freeze for Rect",1,["crabgrab::util::Rect"]],["impl Freeze for AudioSampleRate",1,["crabgrab::frame::AudioSampleRate"]],["impl Freeze for AudioChannelCount",1,["crabgrab::frame::AudioChannelCount"]],["impl<'data> Freeze for AudioChannelData<'data>",1,["crabgrab::frame::AudioChannelData"]],["impl<'data, T> Freeze for AudioChannelDataSamples<'data, T>",1,["crabgrab::frame::AudioChannelDataSamples"]],["impl Freeze for AudioBufferError",1,["crabgrab::frame::AudioBufferError"]],["impl Freeze for AudioFrame",1,["crabgrab::frame::AudioFrame"]],["impl Freeze for VideoFrame",1,["crabgrab::frame::VideoFrame"]],["impl Freeze for StreamEvent",1,["crabgrab::capture_stream::StreamEvent"]],["impl Freeze for StreamError",1,["crabgrab::capture_stream::StreamError"]],["impl Freeze for StreamCreateError",1,["crabgrab::capture_stream::StreamCreateError"]],["impl Freeze for StreamStopError",1,["crabgrab::capture_stream::StreamStopError"]],["impl Freeze for AudioCaptureConfig",1,["crabgrab::capture_stream::AudioCaptureConfig"]],["impl Freeze for CapturePixelFormat",1,["crabgrab::capture_stream::CapturePixelFormat"]],["impl Freeze for CaptureConfig",1,["crabgrab::capture_stream::CaptureConfig"]],["impl Freeze for CaptureConfigError",1,["crabgrab::capture_stream::CaptureConfigError"]],["impl Freeze for CaptureStream",1,["crabgrab::capture_stream::CaptureStream"]],["impl Freeze for CapturableContentError",1,["crabgrab::capturable_content::CapturableContentError"]],["impl Freeze for CapturableWindowFilter",1,["crabgrab::capturable_content::CapturableWindowFilter"]],["impl Freeze for CapturableContentFilter",1,["crabgrab::capturable_content::CapturableContentFilter"]],["impl Freeze for CapturableContent",1,["crabgrab::capturable_content::CapturableContent"]],["impl<'content> Freeze for CapturableWindowIterator<'content>",1,["crabgrab::capturable_content::CapturableWindowIterator"]],["impl<'content> Freeze for CapturableDisplayIterator<'content>",1,["crabgrab::capturable_content::CapturableDisplayIterator"]],["impl Freeze for CapturableWindow",1,["crabgrab::capturable_content::CapturableWindow"]],["impl Freeze for CapturableDisplay",1,["crabgrab::capturable_content::CapturableDisplay"]],["impl Freeze for CapturableApplication",1,["crabgrab::capturable_content::CapturableApplication"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/core/marker/trait.Send.js b/docs/windows_docs/trait.impl/core/marker/trait.Send.js new file mode 100644 index 00000000..ac8db72d --- /dev/null +++ b/docs/windows_docs/trait.impl/core/marker/trait.Send.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Send for WindowsPixelFormat",1,["crabgrab::platform::windows::capture_stream::WindowsPixelFormat"]],["impl Send for WindowsAudioCaptureConfig",1,["crabgrab::platform::windows::capture_stream::WindowsAudioCaptureConfig"]],["impl Send for WindowsCaptureConfig",1,["crabgrab::platform::windows::capture_stream::WindowsCaptureConfig"]],["impl !Send for WindowsCaptureStream",1,["crabgrab::platform::windows::capture_stream::WindowsCaptureStream"]],["impl Send for WindowsCapturableWindow",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableWindow"]],["impl Send for WindowsCapturableDisplay",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableDisplay"]],["impl Send for WindowsCapturableApplication",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableApplication"]],["impl Send for WindowsCapturableContent",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableContent"]],["impl Send for WindowsVideoFrame",1,["crabgrab::platform::windows::frame::WindowsVideoFrame"]],["impl Send for WindowsAudioFrame",1,["crabgrab::platform::windows::frame::WindowsAudioFrame"]],["impl Send for WindowsDxgiVideoFrameError",1,["crabgrab::feature::dxgi::WindowsDxgiVideoFrameError"]],["impl Send for WindowsDx11VideoFrameError",1,["crabgrab::feature::dx11::WindowsDx11VideoFrameError"]],["impl Send for Size",1,["crabgrab::util::Size"]],["impl Send for Point",1,["crabgrab::util::Point"]],["impl Send for Rect",1,["crabgrab::util::Rect"]],["impl Send for AudioSampleRate",1,["crabgrab::frame::AudioSampleRate"]],["impl Send for AudioChannelCount",1,["crabgrab::frame::AudioChannelCount"]],["impl<'data> !Send for AudioChannelData<'data>",1,["crabgrab::frame::AudioChannelData"]],["impl<'data, T> !Send for AudioChannelDataSamples<'data, T>",1,["crabgrab::frame::AudioChannelDataSamples"]],["impl Send for AudioBufferError",1,["crabgrab::frame::AudioBufferError"]],["impl Send for AudioFrame",1,["crabgrab::frame::AudioFrame"]],["impl Send for StreamEvent",1,["crabgrab::capture_stream::StreamEvent"]],["impl Send for StreamError",1,["crabgrab::capture_stream::StreamError"]],["impl Send for AudioCaptureConfig",1,["crabgrab::capture_stream::AudioCaptureConfig"]],["impl Send for CapturePixelFormat",1,["crabgrab::capture_stream::CapturePixelFormat"]],["impl Send for CaptureConfig",1,["crabgrab::capture_stream::CaptureConfig"]],["impl Send for CaptureConfigError",1,["crabgrab::capture_stream::CaptureConfigError"]],["impl !Send for CaptureStream",1,["crabgrab::capture_stream::CaptureStream"]],["impl Send for CapturableContentError",1,["crabgrab::capturable_content::CapturableContentError"]],["impl Send for CapturableWindowFilter",1,["crabgrab::capturable_content::CapturableWindowFilter"]],["impl Send for CapturableContentFilter",1,["crabgrab::capturable_content::CapturableContentFilter"]],["impl Send for CapturableContent",1,["crabgrab::capturable_content::CapturableContent"]],["impl<'content> Send for CapturableWindowIterator<'content>",1,["crabgrab::capturable_content::CapturableWindowIterator"]],["impl<'content> Send for CapturableDisplayIterator<'content>",1,["crabgrab::capturable_content::CapturableDisplayIterator"]],["impl Send for CapturableWindow",1,["crabgrab::capturable_content::CapturableWindow"]],["impl Send for CapturableDisplay",1,["crabgrab::capturable_content::CapturableDisplay"]],["impl Send for CapturableApplication",1,["crabgrab::capturable_content::CapturableApplication"]],["impl Send for StreamCreateError"],["impl Send for StreamStopError"],["impl Send for VideoFrame"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/core/marker/trait.StructuralPartialEq.js b/docs/windows_docs/trait.impl/core/marker/trait.StructuralPartialEq.js new file mode 100644 index 00000000..ffe19ccf --- /dev/null +++ b/docs/windows_docs/trait.impl/core/marker/trait.StructuralPartialEq.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl StructuralPartialEq for CapturePixelFormat"],["impl StructuralPartialEq for WindowsPixelFormat"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/core/marker/trait.Sync.js b/docs/windows_docs/trait.impl/core/marker/trait.Sync.js new file mode 100644 index 00000000..761bf755 --- /dev/null +++ b/docs/windows_docs/trait.impl/core/marker/trait.Sync.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Sync for WindowsPixelFormat",1,["crabgrab::platform::windows::capture_stream::WindowsPixelFormat"]],["impl Sync for WindowsAudioCaptureConfig",1,["crabgrab::platform::windows::capture_stream::WindowsAudioCaptureConfig"]],["impl Sync for WindowsCaptureConfig",1,["crabgrab::platform::windows::capture_stream::WindowsCaptureConfig"]],["impl !Sync for WindowsCaptureStream",1,["crabgrab::platform::windows::capture_stream::WindowsCaptureStream"]],["impl Sync for WindowsCapturableWindow",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableWindow"]],["impl Sync for WindowsCapturableDisplay",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableDisplay"]],["impl Sync for WindowsCapturableApplication",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableApplication"]],["impl Sync for WindowsCapturableContent",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableContent"]],["impl Sync for WindowsVideoFrame",1,["crabgrab::platform::windows::frame::WindowsVideoFrame"]],["impl Sync for WindowsAudioFrame",1,["crabgrab::platform::windows::frame::WindowsAudioFrame"]],["impl Sync for WindowsDxgiVideoFrameError",1,["crabgrab::feature::dxgi::WindowsDxgiVideoFrameError"]],["impl Sync for WindowsDx11VideoFrameError",1,["crabgrab::feature::dx11::WindowsDx11VideoFrameError"]],["impl Sync for Size",1,["crabgrab::util::Size"]],["impl Sync for Point",1,["crabgrab::util::Point"]],["impl Sync for Rect",1,["crabgrab::util::Rect"]],["impl Sync for AudioSampleRate",1,["crabgrab::frame::AudioSampleRate"]],["impl Sync for AudioChannelCount",1,["crabgrab::frame::AudioChannelCount"]],["impl<'data> !Sync for AudioChannelData<'data>",1,["crabgrab::frame::AudioChannelData"]],["impl<'data, T> !Sync for AudioChannelDataSamples<'data, T>",1,["crabgrab::frame::AudioChannelDataSamples"]],["impl Sync for AudioBufferError",1,["crabgrab::frame::AudioBufferError"]],["impl Sync for AudioFrame",1,["crabgrab::frame::AudioFrame"]],["impl Sync for StreamEvent",1,["crabgrab::capture_stream::StreamEvent"]],["impl Sync for StreamError",1,["crabgrab::capture_stream::StreamError"]],["impl Sync for AudioCaptureConfig",1,["crabgrab::capture_stream::AudioCaptureConfig"]],["impl Sync for CapturePixelFormat",1,["crabgrab::capture_stream::CapturePixelFormat"]],["impl Sync for CaptureConfig",1,["crabgrab::capture_stream::CaptureConfig"]],["impl Sync for CaptureConfigError",1,["crabgrab::capture_stream::CaptureConfigError"]],["impl !Sync for CaptureStream",1,["crabgrab::capture_stream::CaptureStream"]],["impl Sync for CapturableContentError",1,["crabgrab::capturable_content::CapturableContentError"]],["impl Sync for CapturableWindowFilter",1,["crabgrab::capturable_content::CapturableWindowFilter"]],["impl Sync for CapturableContentFilter",1,["crabgrab::capturable_content::CapturableContentFilter"]],["impl Sync for CapturableContent",1,["crabgrab::capturable_content::CapturableContent"]],["impl<'content> Sync for CapturableWindowIterator<'content>",1,["crabgrab::capturable_content::CapturableWindowIterator"]],["impl<'content> Sync for CapturableDisplayIterator<'content>",1,["crabgrab::capturable_content::CapturableDisplayIterator"]],["impl Sync for CapturableWindow",1,["crabgrab::capturable_content::CapturableWindow"]],["impl Sync for CapturableDisplay",1,["crabgrab::capturable_content::CapturableDisplay"]],["impl Sync for CapturableApplication",1,["crabgrab::capturable_content::CapturableApplication"]],["impl Sync for StreamStopError"],["impl Sync for VideoFrame"],["impl Sync for StreamCreateError"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/core/marker/trait.Unpin.js b/docs/windows_docs/trait.impl/core/marker/trait.Unpin.js new file mode 100644 index 00000000..7bbd5feb --- /dev/null +++ b/docs/windows_docs/trait.impl/core/marker/trait.Unpin.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Unpin for WindowsPixelFormat",1,["crabgrab::platform::windows::capture_stream::WindowsPixelFormat"]],["impl Unpin for WindowsAudioCaptureConfig",1,["crabgrab::platform::windows::capture_stream::WindowsAudioCaptureConfig"]],["impl Unpin for WindowsCaptureConfig",1,["crabgrab::platform::windows::capture_stream::WindowsCaptureConfig"]],["impl Unpin for WindowsCaptureStream",1,["crabgrab::platform::windows::capture_stream::WindowsCaptureStream"]],["impl Unpin for WindowsCapturableWindow",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableWindow"]],["impl Unpin for WindowsCapturableDisplay",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableDisplay"]],["impl Unpin for WindowsCapturableApplication",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableApplication"]],["impl Unpin for WindowsCapturableContent",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableContent"]],["impl Unpin for WindowsVideoFrame",1,["crabgrab::platform::windows::frame::WindowsVideoFrame"]],["impl Unpin for WindowsAudioFrame",1,["crabgrab::platform::windows::frame::WindowsAudioFrame"]],["impl Unpin for WindowsDxgiVideoFrameError",1,["crabgrab::feature::dxgi::WindowsDxgiVideoFrameError"]],["impl Unpin for WindowsDx11VideoFrameError",1,["crabgrab::feature::dx11::WindowsDx11VideoFrameError"]],["impl Unpin for Size",1,["crabgrab::util::Size"]],["impl Unpin for Point",1,["crabgrab::util::Point"]],["impl Unpin for Rect",1,["crabgrab::util::Rect"]],["impl Unpin for AudioSampleRate",1,["crabgrab::frame::AudioSampleRate"]],["impl Unpin for AudioChannelCount",1,["crabgrab::frame::AudioChannelCount"]],["impl<'data> Unpin for AudioChannelData<'data>",1,["crabgrab::frame::AudioChannelData"]],["impl<'data, T> Unpin for AudioChannelDataSamples<'data, T>",1,["crabgrab::frame::AudioChannelDataSamples"]],["impl Unpin for AudioBufferError",1,["crabgrab::frame::AudioBufferError"]],["impl Unpin for AudioFrame",1,["crabgrab::frame::AudioFrame"]],["impl Unpin for VideoFrame",1,["crabgrab::frame::VideoFrame"]],["impl Unpin for StreamEvent",1,["crabgrab::capture_stream::StreamEvent"]],["impl Unpin for StreamError",1,["crabgrab::capture_stream::StreamError"]],["impl Unpin for StreamCreateError",1,["crabgrab::capture_stream::StreamCreateError"]],["impl Unpin for StreamStopError",1,["crabgrab::capture_stream::StreamStopError"]],["impl Unpin for AudioCaptureConfig",1,["crabgrab::capture_stream::AudioCaptureConfig"]],["impl Unpin for CapturePixelFormat",1,["crabgrab::capture_stream::CapturePixelFormat"]],["impl Unpin for CaptureConfig",1,["crabgrab::capture_stream::CaptureConfig"]],["impl Unpin for CaptureConfigError",1,["crabgrab::capture_stream::CaptureConfigError"]],["impl Unpin for CaptureStream",1,["crabgrab::capture_stream::CaptureStream"]],["impl Unpin for CapturableContentError",1,["crabgrab::capturable_content::CapturableContentError"]],["impl Unpin for CapturableWindowFilter",1,["crabgrab::capturable_content::CapturableWindowFilter"]],["impl Unpin for CapturableContentFilter",1,["crabgrab::capturable_content::CapturableContentFilter"]],["impl Unpin for CapturableContent",1,["crabgrab::capturable_content::CapturableContent"]],["impl<'content> Unpin for CapturableWindowIterator<'content>",1,["crabgrab::capturable_content::CapturableWindowIterator"]],["impl<'content> Unpin for CapturableDisplayIterator<'content>",1,["crabgrab::capturable_content::CapturableDisplayIterator"]],["impl Unpin for CapturableWindow",1,["crabgrab::capturable_content::CapturableWindow"]],["impl Unpin for CapturableDisplay",1,["crabgrab::capturable_content::CapturableDisplay"]],["impl Unpin for CapturableApplication",1,["crabgrab::capturable_content::CapturableApplication"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/core/ops/drop/trait.Drop.js b/docs/windows_docs/trait.impl/core/ops/drop/trait.Drop.js new file mode 100644 index 00000000..9d2c6d43 --- /dev/null +++ b/docs/windows_docs/trait.impl/core/ops/drop/trait.Drop.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl Drop for WindowsCaptureStream"]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js b/docs/windows_docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js new file mode 100644 index 00000000..92a5a884 --- /dev/null +++ b/docs/windows_docs/trait.impl/core/panic/unwind_safe/trait.RefUnwindSafe.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl RefUnwindSafe for WindowsPixelFormat",1,["crabgrab::platform::windows::capture_stream::WindowsPixelFormat"]],["impl RefUnwindSafe for WindowsAudioCaptureConfig",1,["crabgrab::platform::windows::capture_stream::WindowsAudioCaptureConfig"]],["impl RefUnwindSafe for WindowsCaptureConfig",1,["crabgrab::platform::windows::capture_stream::WindowsCaptureConfig"]],["impl !RefUnwindSafe for WindowsCaptureStream",1,["crabgrab::platform::windows::capture_stream::WindowsCaptureStream"]],["impl RefUnwindSafe for WindowsCapturableWindow",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableWindow"]],["impl RefUnwindSafe for WindowsCapturableDisplay",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableDisplay"]],["impl RefUnwindSafe for WindowsCapturableApplication",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableApplication"]],["impl RefUnwindSafe for WindowsCapturableContent",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableContent"]],["impl RefUnwindSafe for WindowsVideoFrame",1,["crabgrab::platform::windows::frame::WindowsVideoFrame"]],["impl RefUnwindSafe for WindowsAudioFrame",1,["crabgrab::platform::windows::frame::WindowsAudioFrame"]],["impl RefUnwindSafe for WindowsDxgiVideoFrameError",1,["crabgrab::feature::dxgi::WindowsDxgiVideoFrameError"]],["impl RefUnwindSafe for WindowsDx11VideoFrameError",1,["crabgrab::feature::dx11::WindowsDx11VideoFrameError"]],["impl RefUnwindSafe for Size",1,["crabgrab::util::Size"]],["impl RefUnwindSafe for Point",1,["crabgrab::util::Point"]],["impl RefUnwindSafe for Rect",1,["crabgrab::util::Rect"]],["impl RefUnwindSafe for AudioSampleRate",1,["crabgrab::frame::AudioSampleRate"]],["impl RefUnwindSafe for AudioChannelCount",1,["crabgrab::frame::AudioChannelCount"]],["impl<'data> RefUnwindSafe for AudioChannelData<'data>",1,["crabgrab::frame::AudioChannelData"]],["impl<'data, T> RefUnwindSafe for AudioChannelDataSamples<'data, T>
where\n T: RefUnwindSafe,
",1,["crabgrab::frame::AudioChannelDataSamples"]],["impl RefUnwindSafe for AudioBufferError",1,["crabgrab::frame::AudioBufferError"]],["impl RefUnwindSafe for AudioFrame",1,["crabgrab::frame::AudioFrame"]],["impl RefUnwindSafe for VideoFrame",1,["crabgrab::frame::VideoFrame"]],["impl RefUnwindSafe for StreamEvent",1,["crabgrab::capture_stream::StreamEvent"]],["impl RefUnwindSafe for StreamError",1,["crabgrab::capture_stream::StreamError"]],["impl RefUnwindSafe for StreamCreateError",1,["crabgrab::capture_stream::StreamCreateError"]],["impl RefUnwindSafe for StreamStopError",1,["crabgrab::capture_stream::StreamStopError"]],["impl RefUnwindSafe for AudioCaptureConfig",1,["crabgrab::capture_stream::AudioCaptureConfig"]],["impl RefUnwindSafe for CapturePixelFormat",1,["crabgrab::capture_stream::CapturePixelFormat"]],["impl RefUnwindSafe for CaptureConfig",1,["crabgrab::capture_stream::CaptureConfig"]],["impl RefUnwindSafe for CaptureConfigError",1,["crabgrab::capture_stream::CaptureConfigError"]],["impl !RefUnwindSafe for CaptureStream",1,["crabgrab::capture_stream::CaptureStream"]],["impl RefUnwindSafe for CapturableContentError",1,["crabgrab::capturable_content::CapturableContentError"]],["impl RefUnwindSafe for CapturableWindowFilter",1,["crabgrab::capturable_content::CapturableWindowFilter"]],["impl RefUnwindSafe for CapturableContentFilter",1,["crabgrab::capturable_content::CapturableContentFilter"]],["impl RefUnwindSafe for CapturableContent",1,["crabgrab::capturable_content::CapturableContent"]],["impl<'content> RefUnwindSafe for CapturableWindowIterator<'content>",1,["crabgrab::capturable_content::CapturableWindowIterator"]],["impl<'content> RefUnwindSafe for CapturableDisplayIterator<'content>",1,["crabgrab::capturable_content::CapturableDisplayIterator"]],["impl RefUnwindSafe for CapturableWindow",1,["crabgrab::capturable_content::CapturableWindow"]],["impl RefUnwindSafe for CapturableDisplay",1,["crabgrab::capturable_content::CapturableDisplay"]],["impl RefUnwindSafe for CapturableApplication",1,["crabgrab::capturable_content::CapturableApplication"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js b/docs/windows_docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js new file mode 100644 index 00000000..1c4512f3 --- /dev/null +++ b/docs/windows_docs/trait.impl/core/panic/unwind_safe/trait.UnwindSafe.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[["impl UnwindSafe for WindowsPixelFormat",1,["crabgrab::platform::windows::capture_stream::WindowsPixelFormat"]],["impl UnwindSafe for WindowsAudioCaptureConfig",1,["crabgrab::platform::windows::capture_stream::WindowsAudioCaptureConfig"]],["impl UnwindSafe for WindowsCaptureConfig",1,["crabgrab::platform::windows::capture_stream::WindowsCaptureConfig"]],["impl !UnwindSafe for WindowsCaptureStream",1,["crabgrab::platform::windows::capture_stream::WindowsCaptureStream"]],["impl UnwindSafe for WindowsCapturableWindow",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableWindow"]],["impl UnwindSafe for WindowsCapturableDisplay",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableDisplay"]],["impl UnwindSafe for WindowsCapturableApplication",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableApplication"]],["impl UnwindSafe for WindowsCapturableContent",1,["crabgrab::platform::windows::capturable_content::WindowsCapturableContent"]],["impl UnwindSafe for WindowsVideoFrame",1,["crabgrab::platform::windows::frame::WindowsVideoFrame"]],["impl UnwindSafe for WindowsAudioFrame",1,["crabgrab::platform::windows::frame::WindowsAudioFrame"]],["impl UnwindSafe for WindowsDxgiVideoFrameError",1,["crabgrab::feature::dxgi::WindowsDxgiVideoFrameError"]],["impl UnwindSafe for WindowsDx11VideoFrameError",1,["crabgrab::feature::dx11::WindowsDx11VideoFrameError"]],["impl UnwindSafe for Size",1,["crabgrab::util::Size"]],["impl UnwindSafe for Point",1,["crabgrab::util::Point"]],["impl UnwindSafe for Rect",1,["crabgrab::util::Rect"]],["impl UnwindSafe for AudioSampleRate",1,["crabgrab::frame::AudioSampleRate"]],["impl UnwindSafe for AudioChannelCount",1,["crabgrab::frame::AudioChannelCount"]],["impl<'data> UnwindSafe for AudioChannelData<'data>",1,["crabgrab::frame::AudioChannelData"]],["impl<'data, T> UnwindSafe for AudioChannelDataSamples<'data, T>
where\n T: RefUnwindSafe,
",1,["crabgrab::frame::AudioChannelDataSamples"]],["impl UnwindSafe for AudioBufferError",1,["crabgrab::frame::AudioBufferError"]],["impl UnwindSafe for AudioFrame",1,["crabgrab::frame::AudioFrame"]],["impl UnwindSafe for VideoFrame",1,["crabgrab::frame::VideoFrame"]],["impl UnwindSafe for StreamEvent",1,["crabgrab::capture_stream::StreamEvent"]],["impl UnwindSafe for StreamError",1,["crabgrab::capture_stream::StreamError"]],["impl UnwindSafe for StreamCreateError",1,["crabgrab::capture_stream::StreamCreateError"]],["impl UnwindSafe for StreamStopError",1,["crabgrab::capture_stream::StreamStopError"]],["impl UnwindSafe for AudioCaptureConfig",1,["crabgrab::capture_stream::AudioCaptureConfig"]],["impl UnwindSafe for CapturePixelFormat",1,["crabgrab::capture_stream::CapturePixelFormat"]],["impl UnwindSafe for CaptureConfig",1,["crabgrab::capture_stream::CaptureConfig"]],["impl UnwindSafe for CaptureConfigError",1,["crabgrab::capture_stream::CaptureConfigError"]],["impl !UnwindSafe for CaptureStream",1,["crabgrab::capture_stream::CaptureStream"]],["impl UnwindSafe for CapturableContentError",1,["crabgrab::capturable_content::CapturableContentError"]],["impl UnwindSafe for CapturableWindowFilter",1,["crabgrab::capturable_content::CapturableWindowFilter"]],["impl UnwindSafe for CapturableContentFilter",1,["crabgrab::capturable_content::CapturableContentFilter"]],["impl UnwindSafe for CapturableContent",1,["crabgrab::capturable_content::CapturableContent"]],["impl<'content> UnwindSafe for CapturableWindowIterator<'content>",1,["crabgrab::capturable_content::CapturableWindowIterator"]],["impl<'content> UnwindSafe for CapturableDisplayIterator<'content>",1,["crabgrab::capturable_content::CapturableDisplayIterator"]],["impl UnwindSafe for CapturableWindow",1,["crabgrab::capturable_content::CapturableWindow"]],["impl UnwindSafe for CapturableDisplay",1,["crabgrab::capturable_content::CapturableDisplay"]],["impl UnwindSafe for CapturableApplication",1,["crabgrab::capturable_content::CapturableApplication"]]] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/crabgrab/feature/dx11/trait.WindowsDx11CaptureStream.js b/docs/windows_docs/trait.impl/crabgrab/feature/dx11/trait.WindowsDx11CaptureStream.js new file mode 100644 index 00000000..d7ba5ecd --- /dev/null +++ b/docs/windows_docs/trait.impl/crabgrab/feature/dx11/trait.WindowsDx11CaptureStream.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/crabgrab/feature/dx11/trait.WindowsDx11VideoFrame.js b/docs/windows_docs/trait.impl/crabgrab/feature/dx11/trait.WindowsDx11VideoFrame.js new file mode 100644 index 00000000..d7ba5ecd --- /dev/null +++ b/docs/windows_docs/trait.impl/crabgrab/feature/dx11/trait.WindowsDx11VideoFrame.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/crabgrab/feature/dxgi/trait.WindowsDxgiCaptureStream.js b/docs/windows_docs/trait.impl/crabgrab/feature/dxgi/trait.WindowsDxgiCaptureStream.js new file mode 100644 index 00000000..d7ba5ecd --- /dev/null +++ b/docs/windows_docs/trait.impl/crabgrab/feature/dxgi/trait.WindowsDxgiCaptureStream.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/docs/windows_docs/trait.impl/crabgrab/feature/dxgi/trait.WindowsDxgiVideoFrame.js b/docs/windows_docs/trait.impl/crabgrab/feature/dxgi/trait.WindowsDxgiVideoFrame.js new file mode 100644 index 00000000..d7ba5ecd --- /dev/null +++ b/docs/windows_docs/trait.impl/crabgrab/feature/dxgi/trait.WindowsDxgiVideoFrame.js @@ -0,0 +1,3 @@ +(function() {var implementors = { +"crabgrab":[] +};if (window.register_implementors) {window.register_implementors(implementors);} else {window.pending_implementors = implementors;}})() \ No newline at end of file diff --git a/examples/capture_display.rs b/examples/capture_display.rs new file mode 100644 index 00000000..d54015f3 --- /dev/null +++ b/examples/capture_display.rs @@ -0,0 +1,24 @@ +use std::time::Duration; + +use crabgrab::prelude::*; + +#[tokio::main] +async fn main() { + if !CaptureStream::test_access(false) { + if !CaptureStream::request_access(false).await { + println!("Failed to get access!"); + return; + }; + } + let filter = CapturableContentFilter { windows: None, displays: true }; + let content = CapturableContent::new(filter).await.unwrap(); + let config = CaptureConfig::with_display(content.displays().next().unwrap(), CapturePixelFormat::Bgra8888); + + let mut stream = CaptureStream::new(config, |result| { + println!("result: {:?}", result); + }).unwrap(); + + std::thread::sleep(Duration::from_millis(2000)); + + stream.stop().unwrap(); +} diff --git a/examples/capture_window.rs b/examples/capture_window.rs new file mode 100644 index 00000000..80fe2a3d --- /dev/null +++ b/examples/capture_window.rs @@ -0,0 +1,51 @@ +use std::time::Duration; + +use crabgrab::prelude::*; + +fn main() { + let runtime = tokio::runtime::Builder::new_multi_thread() + .build().unwrap(); + let future = runtime.spawn(async { + if !CaptureStream::test_access(false) { + CaptureStream::request_access(false).await; + println!("Approve access and run again!"); + } + let window_filter = CapturableWindowFilter { + desktop_windows: false, + onscreen_only: true, + }; + let filter = CapturableContentFilter { windows: Some(window_filter), displays: false }; + let content = CapturableContent::new(filter).await.unwrap(); + let window = content.windows().filter(|window| { + let app_identifier = window.application().identifier(); + app_identifier.to_lowercase().contains("finder") || app_identifier.to_lowercase().contains("explorer") + }).next(); + match window { + Some(window) => { + println!("capturing window: {}", window.title()); + let config = CaptureConfig::with_window(window, CaptureStream::supported_pixel_formats()[3]).unwrap(); + let mut stream = CaptureStream::new(config, |stream_event| { + match stream_event { + Ok(event) => { + match event { + StreamEvent::Video(frame) => { + println!("Got frame: {}", frame.frame_id()); + }, + _ => {} + } + }, + Err(error) => { + println!("Stream error: {:?}", error); + } + } + }).unwrap(); + println!("stream created!"); + tokio::task::block_in_place(|| std::thread::sleep(Duration::from_millis(4000))); + stream.stop().unwrap(); + }, + None => { println!("Failed to find window"); } + } + }); + runtime.block_on(future).unwrap(); + runtime.shutdown_timeout(Duration::from_millis(10000)); +} diff --git a/examples/feature_bitmap.rs b/examples/feature_bitmap.rs new file mode 100644 index 00000000..882992a7 --- /dev/null +++ b/examples/feature_bitmap.rs @@ -0,0 +1,64 @@ +use std::time::Duration; + +use crabgrab::{feature::bitmap::VideoFrameBitmap, prelude::*}; + +fn main() { + let runtime = tokio::runtime::Builder::new_multi_thread() + .build().unwrap(); + let future = runtime.spawn(async { + if !CaptureStream::test_access(false) { + CaptureStream::request_access(false).await; + println!("Approve access and run again!"); + } + let window_filter = CapturableWindowFilter { + desktop_windows: false, + onscreen_only: true, + }; + let filter = CapturableContentFilter { windows: Some(window_filter), displays: false }; + let content = CapturableContent::new(filter).await.unwrap(); + let window = content.windows().filter(|window| { + let app_identifier = window.application().identifier(); + app_identifier.to_lowercase().contains("firefox") + }).next(); + match window { + Some(window) => { + println!("capturing window: {}", window.title()); + let config = CaptureConfig::with_window(window, CaptureStream::supported_pixel_formats()[0]).unwrap(); + let mut stream = CaptureStream::new(config, |stream_event| { + match stream_event { + Ok(event) => { + match event { + StreamEvent::Video(frame) => { + println!("Got frame: {}", frame.frame_id()); + match frame.get_bitmap() { + Ok(bitmap) => { + match bitmap { + crabgrab::feature::bitmap::FrameBitmap::BgraUnorm8x4(_) => println!("format: BgraUnorm8x4"), + crabgrab::feature::bitmap::FrameBitmap::RgbaUnormPacked1010102(_) => println!("format: RgbaUnormPacked1010102"), + crabgrab::feature::bitmap::FrameBitmap::RgbaF16x4(_) => println!("format: RgbaF16x4"), + crabgrab::feature::bitmap::FrameBitmap::YCbCr(_) => println!("format: YCbCr"), + } + }, + Err(e) => { + println!("Bitmap error: {:?}", e); + } + } + }, + _ => {} + } + }, + Err(error) => { + println!("Stream error: {:?}", error); + } + } + }).unwrap(); + println!("stream created!"); + tokio::task::block_in_place(|| std::thread::sleep(Duration::from_millis(40000))); + stream.stop().unwrap(); + }, + None => { println!("Failed to find window"); } + } + }); + runtime.block_on(future).unwrap(); + runtime.shutdown_timeout(Duration::from_millis(100000)); +} diff --git a/src/capturable_content.rs b/src/capturable_content.rs new file mode 100644 index 00000000..7481bf7e --- /dev/null +++ b/src/capturable_content.rs @@ -0,0 +1,205 @@ +use std::{error::Error, fmt::{Debug, Display}}; + +use crate::{platform::platform_impl::{ImplCapturableApplication, ImplCapturableContent, ImplCapturableDisplay, ImplCapturableWindow}, util::Rect}; + +/// Represents an error that occured when enumerating capturable content +#[derive(Debug, Clone)] +pub enum CapturableContentError { + Other(String) +} + +impl Display for CapturableContentError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Other(message) => f.write_fmt(format_args!("CapturableContentError::Other(\"{}\")", message)) + } + } +} + +impl Error for CapturableContentError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } + + fn description(&self) -> &str { + "description() is deprecated; use Display" + } + + fn cause(&self) -> Option<&dyn Error> { + self.source() + } +} + +/// Selects the kind of windows to enumerate for capture +pub struct CapturableWindowFilter { + /// Desktop windows are elements of the desktop environment, E.G. the dock on macos or the start bar on windows. + pub desktop_windows: bool, + /// Whether to restrict to onscreen windows + pub onscreen_only: bool, +} + +impl Default for CapturableWindowFilter { + fn default() -> Self { + Self { desktop_windows: false, onscreen_only: true } + } +} + +/// Selects the kind of capturable content to enumerate +pub struct CapturableContentFilter { + /// What kind of capturable windows, if Some, to enumerate + pub windows: Option, + /// Whether to enumerate capturable displays + pub displays: bool, +} + +impl CapturableContentFilter { + /// Whether this filter allows any capturable content + pub fn is_empty(&self) -> bool { + !( + self.windows.is_some() || + self.displays + ) + } +} + +pub struct CapturableContent { + impl_capturable_content: ImplCapturableContent +} + +/// An iterator over capturable windows +pub struct CapturableWindowIterator<'content> { + content: &'content CapturableContent, + i: usize +} + +impl Iterator for CapturableWindowIterator<'_> { + type Item = CapturableWindow; + + fn next(&mut self) -> Option { + if self.i < self.content.impl_capturable_content.windows.len() { + let i = self.i; + self.i += 1; + Some(CapturableWindow { impl_capturable_window: ImplCapturableWindow::from_impl(self.content.impl_capturable_content.windows[i].clone()) }) + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.i, Some(self.content.impl_capturable_content.windows.len())) + } +} + +impl ExactSizeIterator for CapturableWindowIterator<'_> { +} + +/// An iterator over capturable displays +pub struct CapturableDisplayIterator<'content> { + content: &'content CapturableContent, + i: usize +} + +impl Iterator for CapturableDisplayIterator<'_> { + type Item = CapturableDisplay; + + fn next(&mut self) -> Option { + if self.i < self.content.impl_capturable_content.displays.len() { + let i = self.i; + self.i += 1; + Some(CapturableDisplay { impl_capturable_display: ImplCapturableDisplay::from_impl(self.content.impl_capturable_content.displays[i].clone()) }) + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.i, Some(self.content.impl_capturable_content.displays.len())) + } +} + +impl ExactSizeIterator for CapturableDisplayIterator<'_> { + fn len(&self) -> usize { + self.content.impl_capturable_content.displays.len() + } +} + +impl CapturableContent { + /// Requests capturable content from the OS + /// + /// Note that the returned capturable content may be stale - for example, a window enumerated in this capturable content + /// may have been closed before it is used to open a stream, and creating a stream for that window will result in an error. + pub async fn new(filter: CapturableContentFilter) -> Result { + Ok(Self { + impl_capturable_content: ImplCapturableContent::new(filter).await? + }) + } + + /// Get an iterator over the capturable windows + pub fn windows<'a>(&'a self) -> CapturableWindowIterator<'a> { + CapturableWindowIterator { content: self, i: 0 } + } + + /// Get an iterator over the capturable displays + pub fn displays<'a>(&'a self) -> CapturableDisplayIterator<'a> { + CapturableDisplayIterator { content: self, i: 0 } + } +} + +#[derive(Clone, Debug)] +pub(crate) enum Capturable { + Window(CapturableWindow), + Display(CapturableDisplay), +} + +/// Represents a capturable application window +#[derive(Debug, Clone)] +pub struct CapturableWindow { + pub(crate) impl_capturable_window: ImplCapturableWindow +} + +impl CapturableWindow { + /// Gets the title of the window + pub fn title(&self) -> String { + self.impl_capturable_window.title() + } + + /// Gets the virtual screen rectangle of the window + pub fn rect(&self) -> Rect { + self.impl_capturable_window.rect() + } + + /// Gets the application that owns this window + pub fn application(&self) -> CapturableApplication { + CapturableApplication { + impl_capturable_application: self.impl_capturable_window.application() + } + } +} + +/// Represents a capturable display +#[derive(Debug, Clone)] +pub struct CapturableDisplay { + pub(crate) impl_capturable_display: ImplCapturableDisplay +} + +impl CapturableDisplay { + /// Gets the virtual screen rectangle of this display + /// + /// Note: Currently on windows, this is only evaluated at the time of display enumeration + pub fn rect(&self) -> Rect { + self.impl_capturable_display.rect() + } +} + +pub struct CapturableApplication { + impl_capturable_application: ImplCapturableApplication +} + +impl CapturableApplication { + /// Gets the "identifier" of the application + /// + /// On Macos, this is the application bundle, and on windows, this is the application file name + pub fn identifier(&self) -> String { + self.impl_capturable_application.identifier() + } +} diff --git a/src/capture_stream.rs b/src/capture_stream.rs new file mode 100644 index 00000000..a1c27c2a --- /dev/null +++ b/src/capture_stream.rs @@ -0,0 +1,265 @@ +use std::fmt::Debug; +use std::{error::Error, fmt::Display}; + +use crate::platform::platform_impl::{ImplAudioCaptureConfig, ImplCaptureConfig, ImplCaptureStream}; +use crate::capturable_content::Capturable; +use crate::prelude::{AudioChannelCount, AudioFrame, AudioSampleRate, CapturableDisplay, CapturableWindow, VideoFrame}; +use crate::util::{Point, Rect, Size}; + +/// Represents an event in a capture stream +#[derive(Debug)] +pub enum StreamEvent { + /// This event is produced when the stream receives a new audio packet + Audio(AudioFrame), + /// This event is produced when the stream receives a new video frame + Video(VideoFrame), + /// This event is produced when the stream goes idle - IE when no new frames are expected for some time, like when a window minimizes + Idle, + /// This event is produced once at the end of the stream + End, +} + +/// This represents an error during a stream, for example a failure to retreive a video or audio frame +#[derive(Debug, Clone)] +pub enum StreamError { + Other(String), +} + +impl Display for StreamError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Other(message) => f.write_fmt(format_args!("StreamError::Other(\"{}\")", message)) + } + } +} + +impl Error for StreamError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } + + fn description(&self) -> &str { + "description() is deprecated; use Display" + } + + fn cause(&self) -> Option<&dyn Error> { + self.source() + } +} + +/// This represents an error when creating a capture stream +#[derive(Debug, Clone)] +pub enum StreamCreateError { + Other(String), + /// The supplied pixel format is unsupported by the implementation + UnsupportedPixelFormat, + //GpuLost, +} + +unsafe impl Send for StreamCreateError {} +unsafe impl Sync for StreamCreateError {} + +/// This represents an error while stopping a stream +#[derive(Debug)] +pub enum StreamStopError { + Other(String), + /// The stream was already stopped + AlreadyStopped, + //GpuLost, +} + +unsafe impl Send for StreamStopError {} +unsafe impl Sync for StreamStopError {} + +impl Display for StreamCreateError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Other(message) => f.write_fmt(format_args!("StreamCreateError::Other(\"{}\")", message)), + Self::UnsupportedPixelFormat => f.write_fmt(format_args!("SteamCreateError::UnsupportedPixelFormat")), + } + } +} + +impl Error for StreamCreateError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } + + fn description(&self) -> &str { + "description() is deprecated; use Display" + } + + fn cause(&self) -> Option<&dyn Error> { + self.source() + } +} + +/// Configuration settings for audio streams +#[derive(Clone, Debug)] +pub struct AudioCaptureConfig { + pub(crate) sample_rate: AudioSampleRate, + pub(crate) channel_count: AudioChannelCount, + pub(crate) impl_capture_audio_config: ImplAudioCaptureConfig, +} + +impl AudioCaptureConfig { + /// Creates a new audio capture config with default settings: + /// * 24000 hz + /// * Mono + pub fn new() -> Self { + Self { + sample_rate: AudioSampleRate::Hz24000, + channel_count: AudioChannelCount::Mono, + impl_capture_audio_config: ImplAudioCaptureConfig::new() + } + } +} + +/// The pixel format of returned video frames +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum CapturePixelFormat { + /// One plane, 4 channels, 8 bits per channel: {b: u8, g: u8, r: u8, a: u8}, full range: [0, 255] + Bgra8888, + /// One plane, 4 channels, 10 bits per color channel, two bits for alpha: {a: u2, r: u10, g: u10, b: u10}, rgb range: [0, 1023], alpha range: [0, 3] + Argb2101010, + /// Two planes: + /// * 1 channel, luminance, 8 bits per pixel, video range: [16, 240] + /// * 2 channels, chroma (cb, cr) 8 bits bits per channel per two pixels vertically, range: [0, 255] + V420, + /// Two planes: + /// * 1 channel, luminance, 8 bits per pixel, full range: [0, 255] + /// * 2 channels, chroma (cb, cr) 8 bits bits per channel per two pixels vertically, range: [0, 255] + F420, +} + +/// Configuration settings for a capture stream +#[derive(Clone, Debug)] +pub struct CaptureConfig { + pub(crate) target: Capturable, + pub(crate) source_rect: Rect, + pub(crate) output_size: Size, + pub(crate) show_cursor: bool, + pub(crate) pixel_format: CapturePixelFormat, + pub(crate) capture_audio: Option, + pub(crate) impl_capture_config: ImplCaptureConfig, + pub(crate) buffer_count: usize, +} + +/// Represents an error creating the capture config +#[derive(Debug, Clone)] +pub enum CaptureConfigError { + /// The pixel format is unsupported by the implementation + UnsupportedPixelFormat, + /// The buffer count is out of the valid range for the implementation + InvalidBufferCount, +} + +impl CaptureConfig { + /// Create a capture configuration for a given capturable window + pub fn with_window(window: CapturableWindow, pixel_format: CapturePixelFormat) -> Result { + let rect = window.rect(); + Ok(CaptureConfig { + target: Capturable::Window(window), + pixel_format, + source_rect: Rect { + origin: Point { + x: 0.0, + y: 0.0, + }, + size: rect.size + }, + output_size: rect.size, + show_cursor: false, + impl_capture_config: ImplCaptureConfig::new(), + capture_audio: None, + buffer_count: 3, + }) + } + + /// Create a capture configuration for a given capturable display + pub fn with_display(display: CapturableDisplay, pixel_format: CapturePixelFormat) -> CaptureConfig { + let rect = display.rect(); + CaptureConfig { + target: Capturable::Display(display), + pixel_format, + source_rect: Rect { + origin: Point { + x: 0.0, + y: 0.0, + }, + size: rect.size + }, + output_size: rect.size, + show_cursor: false, + impl_capture_config: ImplCaptureConfig::new(), + capture_audio: None, + buffer_count: 3, + } + } + + /// Configure the buffer count - the number of frames in the capture queue. + /// + /// Higher numbers mean higher latency, but smoother performance + pub fn with_buffer_count(self, buffer_count: usize) -> Self { + Self { + buffer_count, + ..self + } + } + + /// Configure whether the cursor is visible in the capture + pub fn with_show_cursor(self, show_cursor: bool) -> Self { + Self { + show_cursor, + ..self + } + } + + /// Configure the output texture size - by default, this will match the captured content at the time of enumeration + pub fn set_output_size(self, output_size: Size) -> Self { + Self { + output_size, + ..self + } + } +} + +/// Represents an active capture stream +pub struct CaptureStream { + pub(crate) impl_capture_stream: ImplCaptureStream, +} + +impl CaptureStream { + /// Test whether the calling application has permission to capture content + pub fn test_access(borderless: bool) -> bool { + ImplCaptureStream::check_access(borderless) + } + + /// Prompt the user for permission to capture content + pub async fn request_access(borderless: bool) -> bool { + ImplCaptureStream::request_access(borderless).await + } + + /// Gets the implementation's supported pixel formats + /// + /// Note that the returned formats may not work for all capture modalities (eg, window vs display) + pub fn supported_pixel_formats() -> &'static [CapturePixelFormat] { + ImplCaptureStream::supported_pixel_formats() + } + + /// Start a new capture stream with the given stream callback + pub fn new(config: CaptureConfig, callback: impl FnMut(Result) + Send + 'static) -> Result { + let boxed_callback = Box::new(callback); + Ok(Self { + impl_capture_stream: ImplCaptureStream::new(config, boxed_callback)? + }) + } + + /// Stop the capture + pub fn stop(&mut self) -> Result<(), StreamStopError> { + self.impl_capture_stream.stop() + } +} + + diff --git a/src/feature/bitmap/mod.rs b/src/feature/bitmap/mod.rs new file mode 100644 index 00000000..656c2c42 --- /dev/null +++ b/src/feature/bitmap/mod.rs @@ -0,0 +1,272 @@ +#![cfg(feature = "bitmap")] + +use std::error::Error; +use std::fmt::Display; + +use half::f16; + +use crate::prelude::VideoFrame; +#[cfg(target_os = "macos")] +use crate::platform::macos::frame::MacosVideoFrame; +#[cfg(target_os = "macos")] +use crate::platform::platform_impl::objc_wrap::CVPixelFormat; + +#[cfg(target_os = "windows")] +use crate::feature::dx11::{WindowsDx11VideoFrame, WindowsDx11VideoFrameError}; +#[cfg(target_os = "windows")] +use windows::Win32::Graphics::Direct3D11::ID3D11Texture2D; +#[cfg(target_os = "windows")] +use windows::Graphics::DirectX::DirectXPixelFormat; +#[cfg(target_os = "windows")] +use windows::core::ComInterface; +#[cfg(target_os = "windows")] +use windows::Win32::Graphics::Direct3D11::{D3D11_BOX, D3D11_CPU_ACCESS_READ, D3D11_MAPPED_SUBRESOURCE, D3D11_MAP_READ, D3D11_TEXTURE2D_DESC, D3D11_USAGE_STAGING}; +#[cfg(target_os = "windows")] +use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_SAMPLE_DESC}; +#[cfg(target_os = "windows")] +use windows::Win32::System::WinRT::Direct3D11::IDirect3DDxgiInterfaceAccess; +#[cfg(target_os = "windows")] +use windows::Win32::Graphics::Direct3D11::{D3D11_STANDARD_MULTISAMPLE_QUALITY_LEVELS, D3D11_USAGE_DYNAMIC}; + +pub struct FrameBitmapBgraUnorm8x4 { + pub data: Box<[[u8; 4]]>, + pub width: usize, + pub height: usize, +} + +pub struct FrameBitmapRgbaUnormPacked1010102 { + pub data: Box<[u32]>, + pub width: usize, + pub height: usize, +} + +pub struct FrameBitmapRgbaF16x4 { + pub data: Box<[[f16; 4]]>, + pub width: usize, + pub height: usize, +} + +pub enum VideoRange { + Video, + Full, +} + +/// A YCbCr image, corresponding to either V420 or F420 pixel formats. +/// +/// Dual-planar, with luma (Y) in one plane, and chroma (CbCr) in another. +/// Note that each plane may have a different size, as with V420 format, where +/// the chroma plane is 2x2 blocks, but luma is per-pixel +pub struct FrameBitmapYCbCr { + pub luma_data: Box<[u8]>, + pub luma_width: usize, + pub luma_height: usize, + pub chroma_data: Box<[[u8; 2]]>, + pub chroma_width: usize, + pub chroma_height: usize, + pub range: VideoRange, +} + +/// A bitmap image +pub enum FrameBitmap { + BgraUnorm8x4(FrameBitmapBgraUnorm8x4), + RgbaUnormPacked1010102(FrameBitmapRgbaUnormPacked1010102), + RgbaF16x4(FrameBitmapRgbaF16x4), + YCbCr(FrameBitmapYCbCr), +} + +/// A video frame which can produce a bitmap +pub trait VideoFrameBitmap { + /// Create a bitmap image from this frame. This usually involves a memory transfer from VRAM to system RAM, + /// and is an expensive operation. + fn get_bitmap(&self) -> Result; +} + +#[derive(Clone, Debug)] +pub enum VideoFrameBitmapError { + Other(String), +} + +impl Display for VideoFrameBitmapError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Other(error) => f.write_fmt(format_args!("VideoFrameBitmapError::Other(\"{}\")", error)), + } + } +} + +impl Error for VideoFrameBitmapError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } + + fn description(&self) -> &str { + "description() is deprecated; use Display" + } + + fn cause(&self) -> Option<&dyn Error> { + self.source() + } +} + +impl VideoFrameBitmap for VideoFrame { + fn get_bitmap(&self) -> Result { + #[cfg(target_os = "windows")] + { + let (width, height) = self.impl_video_frame.frame_size; + match self.get_dx11_surface() { + Err(WindowsDx11VideoFrameError::Other(x)) => Err(VideoFrameBitmapError::Other(x)), + Ok((surface, pixel_format)) => { + let (pixel_size, dxgi_format) = match pixel_format { + DirectXPixelFormat::B8G8R8A8UIntNormalized => (4, DXGI_FORMAT_B8G8R8A8_UNORM), + DirectXPixelFormat::R10G10B10A2UIntNormalized => (4, DXGI_FORMAT_R10G10B10A2_UNORM), + _ => return Err(VideoFrameBitmapError::Other("Unknown or unsupported pixel format on DXGISurface".to_string())), + }; + + unsafe { + let surface_desc = surface.Description() + .map_err(|_| VideoFrameBitmapError::Other("Couldn't get description of frame surface".to_string()))?; + let mut new_texture_desc = D3D11_TEXTURE2D_DESC::default(); + new_texture_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ.0 as u32; + new_texture_desc.ArraySize = 1; + new_texture_desc.BindFlags = 0; + new_texture_desc.Width = surface_desc.Width as u32; + new_texture_desc.Height = surface_desc.Height as u32; + new_texture_desc.MipLevels = 1; + new_texture_desc.SampleDesc.Count = 1; + new_texture_desc.SampleDesc.Quality = 0; + new_texture_desc.Usage.0 = D3D11_USAGE_STAGING.0 | D3D11_USAGE_DYNAMIC.0; + new_texture_desc.Format = dxgi_format; + let mut staging_texture = Option::::None; + let staging_tex_result = self.impl_video_frame.device.CreateTexture2D(&new_texture_desc as *const _, None, Some(&mut staging_texture as *mut _)); + staging_tex_result.map_err(|error| VideoFrameBitmapError::Other(format!("Failed to create texture: {}", error.to_string())))?; + let dxgi_interfce_access: IDirect3DDxgiInterfaceAccess = surface.cast() + .map_err(|_| VideoFrameBitmapError::Other("Couldn't create surface interface access".to_string()))?; + let surface_texture: ID3D11Texture2D = dxgi_interfce_access.GetInterface() + .map_err(|_| VideoFrameBitmapError::Other("Couldn't create surface texture from surface IDirect3DDxgiInterfaceAccess".to_string()))?; + let device = self.impl_video_frame.device.GetImmediateContext() + .map_err(|_| VideoFrameBitmapError::Other("Couldn't get immediate d3d11 context".to_string()))?; + let staging_texture = staging_texture.unwrap(); + device.CopyResource(&staging_texture, &surface_texture); + let mut mapped_resource = D3D11_MAPPED_SUBRESOURCE::default(); + let map_result = device.Map(&staging_texture, 0, D3D11_MAP_READ, 0, Some(&mut mapped_resource as *mut _)); + map_result.map_err(|_| VideoFrameBitmapError::Other("Couldn't map staging texture".to_string()))?; + match pixel_format { + DirectXPixelFormat::B8G8R8A8UIntNormalized => { + let mut image_data = vec![[0u8; 4]; width * height]; + let bpr = mapped_resource.RowPitch as usize; + let surface_slice = std::slice::from_raw_parts(mapped_resource.pData as *const u8, bpr * height); + for y in 0..height { + let source_slice = bytemuck::cast_slice::<_, [u8; 4]>(&surface_slice[(bpr * y)..(bpr * y + 4 * width)]); + image_data[(width * y)..(width * y + width)].copy_from_slice(source_slice); + } + let _ = device.Unmap(&staging_texture, 0); + Ok(FrameBitmap::BgraUnorm8x4(FrameBitmapBgraUnorm8x4 { + data: image_data.into_boxed_slice(), + width, + height, + })) + }, + DirectXPixelFormat::R10G10B10A2UIntNormalized => { + let mut image_data = vec![0u32; width * height]; + let bpr = mapped_resource.RowPitch as usize; + let surface_slice = std::slice::from_raw_parts(mapped_resource.pData as *const u8, bpr * height); + for y in 0..height { + let source_slice = bytemuck::cast_slice::<_, u32>(&surface_slice[(bpr * y)..(bpr * y + 4 * width)]); + image_data[(width * y)..(width * y + width)].copy_from_slice(source_slice); + } + let _ = device.Unmap(&staging_texture, 0); + Ok(FrameBitmap::RgbaUnormPacked1010102(FrameBitmapRgbaUnormPacked1010102 { + data: image_data.into_boxed_slice(), + width, + height, + })) + }, + _ => { + Err(VideoFrameBitmapError::Other("Unknown or unsupported pixel format on DXGISurface".to_string())) + } + } + } + } + } + } + #[cfg(target_os = "macos")] + { + let iosurface = match &self.impl_video_frame { + MacosVideoFrame::SCStream(sc_frame) => { + match sc_frame.sample_buffer.get_image_buffer().map(|image_buffer| image_buffer.get_iosurface()).flatten() { + Some(iosurface) => iosurface, + None => return Err(VideoFrameBitmapError::Other("Failed to get iosurface".to_string())), + } + }, + MacosVideoFrame::CGDisplayStream(cg_display_frame) => { + cg_display_frame.io_surface.clone() + } + }; + if let Ok(lock_gaurd) = iosurface.lock(true, false) { + let pixel_format = iosurface.get_pixel_format(); + match pixel_format { + Some(CVPixelFormat::BGRA8888) => { + let bpr = iosurface.get_bytes_per_row(); + let height = iosurface.get_height(); + let width = iosurface.get_width(); + let mut image_data = vec![[0; 4]; width * height]; + let base_address = lock_gaurd.get_base_address().ok_or(VideoFrameBitmapError::Other("Failed to get base address of iosurface".into()))?; + let iosurface_slice = unsafe { std::slice::from_raw_parts(base_address as *const u8, bpr * height) }; + for y in 0..height { + let source_slice = bytemuck::cast_slice::<_, [u8; 4]>(&iosurface_slice[(bpr * y)..(bpr * y + 4 * width)]); + image_data[(width * y)..(width * y + width)].copy_from_slice(source_slice); + } + Ok(FrameBitmap::BgraUnorm8x4(FrameBitmapBgraUnorm8x4 { + data: image_data.into_boxed_slice(), + width, + height, + })) + }, + Some(CVPixelFormat::V420) | + Some(CVPixelFormat::F420) => { + + let luma_bpr = iosurface.get_bytes_per_row_of_plane(0); + let luma_height = iosurface.get_height_of_plane(0); + let luma_width = iosurface.get_width_of_plane(0); + + let mut luma_image_data = vec![0u8; luma_width * luma_height]; + let luma_base_address = lock_gaurd.get_base_address_of_plane(0).ok_or(VideoFrameBitmapError::Other("Failed to get base address of iosurface".into()))?; + let luma_iosurface_slice = unsafe { std::slice::from_raw_parts(luma_base_address as *const u8, luma_bpr * luma_height) }; + + for y in 0..luma_height { + let luma_source_slice = &luma_iosurface_slice[(luma_bpr * y)..(luma_bpr * y + luma_width)]; + luma_image_data[(luma_width * y)..(luma_width * y + luma_width)].copy_from_slice(luma_source_slice); + } + + let chroma_bpr = iosurface.get_bytes_per_row_of_plane(1); + let chroma_height = iosurface.get_height_of_plane(1); + let chroma_width = iosurface.get_width_of_plane(1); + let mut chroma_image_data = vec![[0u8; 2]; chroma_width * chroma_height]; + let chroma_base_address = lock_gaurd.get_base_address_of_plane(1).ok_or(VideoFrameBitmapError::Other("Failed to get base address of iosurface".into()))?; + let chroma_iosurface_slice = unsafe { std::slice::from_raw_parts(chroma_base_address as *const u8, chroma_bpr * chroma_height) }; + + for y in 0..chroma_height { + let chroma_source_slice = bytemuck::cast_slice::<_, [u8; 2]>(&chroma_iosurface_slice[(chroma_bpr * y)..(chroma_bpr * y + 2 * chroma_width)]); + chroma_image_data[(chroma_width * y)..(chroma_width * y + chroma_width)].copy_from_slice(chroma_source_slice); + } + + Ok(FrameBitmap::YCbCr(FrameBitmapYCbCr { + luma_data: luma_image_data.into_boxed_slice(), + chroma_data: chroma_image_data.into_boxed_slice(), + luma_width, + luma_height, + chroma_width, + chroma_height, + range: if pixel_format == Some(CVPixelFormat::F420) { VideoRange::Full } else { VideoRange::Video } + })) + }, + _ => Err(VideoFrameBitmapError::Other("Unknown pixel format on iosurface".to_string())) + } + } else { + Err(VideoFrameBitmapError::Other("Failed to lock iosurface".to_string())) + } + } + } +} + + diff --git a/src/feature/dx11/mod.rs b/src/feature/dx11/mod.rs new file mode 100644 index 00000000..f05abcb1 --- /dev/null +++ b/src/feature/dx11/mod.rs @@ -0,0 +1,61 @@ +#![cfg(target_os = "windows")] +#![cfg(feature = "dx11")] + +use futures::lock::Mutex; +use windows::{Graphics::DirectX::{Direct3D11::IDirect3DSurface, DirectXPixelFormat}, Win32::Graphics::Direct3D11::ID3D11Device}; + +use std::error::Error; +use std::fmt::Display; + +use crate::prelude::{CaptureStream, VideoFrame}; + +#[derive(Debug, Clone)] +pub enum WindowsDx11VideoFrameError { + Other(String), +} + +impl Display for WindowsDx11VideoFrameError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Other(error) => f.write_fmt(format_args!("WindowsDx11VideoFrameError::Other(\"{}\")", error)), + } + } +} + +impl Error for WindowsDx11VideoFrameError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } + + fn description(&self) -> &str { + "description() is deprecated; use Display" + } + + fn cause(&self) -> Option<&dyn Error> { + self.source() + } +} + +pub trait WindowsDx11VideoFrame { + /// Get the DX11 surface representing the video frame's texture memory, as well as the pixel format + fn get_dx11_surface(&self) -> Result<(IDirect3DSurface, DirectXPixelFormat), WindowsDx11VideoFrameError>; +} + +impl WindowsDx11VideoFrame for VideoFrame { + fn get_dx11_surface(&self) -> Result<(IDirect3DSurface, DirectXPixelFormat), WindowsDx11VideoFrameError> { + self.impl_video_frame.frame.Surface() + .map_err(|e| WindowsDx11VideoFrameError::Other(format!("Failed to get frame surface: {}", e.to_string()))) + .map(|surface| (surface, self.impl_video_frame.pixel_format)) + } +} + +pub trait WindowsDx11CaptureStream { + /// Get the underlying D3D11 device used for frame capture + fn get_dx11_device(&self) -> ID3D11Device; +} + +impl WindowsDx11CaptureStream for CaptureStream { + fn get_dx11_device(&self) -> ID3D11Device { + self.impl_capture_stream.d3d11_device.clone() + } +} diff --git a/src/feature/dxgi/mod.rs b/src/feature/dxgi/mod.rs new file mode 100644 index 00000000..d0b0d3b0 --- /dev/null +++ b/src/feature/dxgi/mod.rs @@ -0,0 +1,75 @@ +#![cfg(target_os = "windows")] +#![cfg(feature = "dxgi")] + +use crate::prelude::{CaptureStream, VideoFrame}; + +use std::error::Error; +use std::fmt::Display; + +use windows::core::ComInterface; +use windows::Graphics::DirectX::DirectXPixelFormat; +use windows::Win32::System::WinRT::Direct3D11::IDirect3DDxgiInterfaceAccess; +use windows::Win32::Graphics::Direct3D11::ID3D11Texture2D; + +#[derive(Debug, Clone)] +pub enum WindowsDxgiVideoFrameError { + Other(String), +} + +impl Display for WindowsDxgiVideoFrameError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Other(error) => f.write_fmt(format_args!("WindowsDxgiVideoFrameError::Other(\"{}\")", error)), + } + } +} + +impl Error for WindowsDxgiVideoFrameError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } + + fn description(&self) -> &str { + "description() is deprecated; use Display" + } + + fn cause(&self) -> Option<&dyn Error> { + self.source() + } +} + +pub trait WindowsDxgiVideoFrame { + /// Get the surface texture for this video frame + fn get_dxgi_surface(&self) -> Result<(windows::Win32::Graphics::Dxgi::IDXGISurface, DirectXPixelFormat), WindowsDxgiVideoFrameError>; +} + +impl WindowsDxgiVideoFrame for VideoFrame { + fn get_dxgi_surface(&self) -> Result<(windows::Win32::Graphics::Dxgi::IDXGISurface, DirectXPixelFormat), WindowsDxgiVideoFrameError> { + let d3d11_surface = self.impl_video_frame.frame.Surface() + .map_err(|e| WindowsDxgiVideoFrameError::Other(format!("Failed to get frame surface: {}", e.to_string())))?; + let interface_access: IDirect3DDxgiInterfaceAccess = d3d11_surface.cast() + .map_err(|e| WindowsDxgiVideoFrameError::Other(format!("Failed to cast d3d11 surface to dxgi interface access: {}", e.to_string())))?; + let d3d11_texture: ID3D11Texture2D = unsafe { + interface_access.GetInterface::() + }.map_err(|e| WindowsDxgiVideoFrameError::Other(format!("Failed to get ID3D11Texture2D interface from to IDirect3DSurface(IDirect3DDxgiInterfaceAccess): {}", e.to_string())))?; + d3d11_texture.cast().map_err(|e| WindowsDxgiVideoFrameError::Other(format!("Failed to cast ID3D11Texture2D to IDXGISurface: {}", e.to_string()))) + .map(|texture| (texture, self.impl_video_frame.pixel_format)) + } +} + +pub trait WindowsDxgiCaptureStream { + /// Get the dxgi adapter used by the capture stream for frame generation + fn get_dxgi_adapter(&self) -> windows::Win32::Graphics::Dxgi::IDXGIAdapter; + /// Get the dxgi device used by the capture stream for frame generation + fn get_dxgi_device(&self) -> windows::Win32::Graphics::Dxgi::IDXGIDevice; +} + +impl WindowsDxgiCaptureStream for CaptureStream { + fn get_dxgi_adapter(&self) -> windows::Win32::Graphics::Dxgi::IDXGIAdapter { + self.impl_capture_stream.dxgi_adapter.clone() + } + + fn get_dxgi_device(&self) -> windows::Win32::Graphics::Dxgi::IDXGIDevice { + self.impl_capture_stream.dxgi_device.clone() + } +} diff --git a/src/feature/iosurface/mod.rs b/src/feature/iosurface/mod.rs new file mode 100644 index 00000000..ae0b00dd --- /dev/null +++ b/src/feature/iosurface/mod.rs @@ -0,0 +1,94 @@ +#![cfg(target_os = "macos")] +#![cfg(feature = "iosurface")] + +use std::os::raw::c_void; + +use std::error::Error; +use std::fmt::Display; + +use crate::{platform::{macos::{frame::MacosVideoFrame, objc_wrap::IOSurfaceRef}, platform_impl::objc_wrap::{IOSurfaceDecrementUseCount, IOSurfaceIncrementUseCount}}, prelude::VideoFrame}; + +/// A Macos IOSurface instance +pub struct IoSurface(IOSurfaceRef); + +impl IoSurface { + /// Gets the raw IOSurfaceRef + pub fn get_raw(&self) -> *const c_void { + self.0 + } + + pub(crate) fn from_ref_unretained(r: IOSurfaceRef) -> Self { + unsafe { IOSurfaceIncrementUseCount(r); } + IoSurface(r) + } +} + +impl Clone for IoSurface { + fn clone(&self) -> Self { + unsafe { IOSurfaceIncrementUseCount(self.0); } + IoSurface(self.0) + } +} + +impl Drop for IoSurface { + fn drop(&mut self) { + unsafe { IOSurfaceDecrementUseCount(self.0); } + } +} + +pub trait MacosIoSurfaceVideoFrame { + /// Get the iosurface representing the video frame's texture + fn get_iosurface(&self) -> Result; +} + +#[derive(Debug)] +pub enum GetIoSurfaceError{ + NoImageBuffer, + NoIoSurface +} + +impl Display for GetIoSurfaceError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::NoImageBuffer => f.write_str("GetIoSurfaceError::NoImageBuffer"), + Self::NoIoSurface => f.write_str("GetIoSurfaceError::NoIoSurface"), + } + } +} + +impl Error for GetIoSurfaceError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } + + fn description(&self) -> &str { + "description() is deprecated; use Display" + } + + fn cause(&self) -> Option<&dyn Error> { + self.source() + } +} + +impl MacosIoSurfaceVideoFrame for VideoFrame { + fn get_iosurface(&self) -> Result { + match &self.impl_video_frame { + MacosVideoFrame::SCStream(frame) => { + match frame.sample_buffer.get_image_buffer() { + Some(image_buffer) => { + match image_buffer.get_iosurface_ptr() { + Some(ptr) => { + Ok(IoSurface::from_ref_unretained(ptr)) + }, + None => Err(GetIoSurfaceError::NoIoSurface) + } + }, + None => Err(GetIoSurfaceError::NoImageBuffer) + } + }, + MacosVideoFrame::CGDisplayStream(frame) => { + Ok(IoSurface::from_ref_unretained(frame.io_surface.0)) + } + } + } +} diff --git a/src/feature/metal/mod.rs b/src/feature/metal/mod.rs new file mode 100644 index 00000000..33058232 --- /dev/null +++ b/src/feature/metal/mod.rs @@ -0,0 +1,153 @@ +#![cfg(target_os = "macos")] +#![cfg(feature = "metal")] + +use metal::foreign_types::ForeignType; +use objc::msg_send; +use objc::runtime::Object; +use objc::sel; +use objc::sel_impl; + +use crate::platform::platform_impl::objc_wrap::CVPixelFormat; +use crate::prelude::{CaptureStream, VideoFrame}; + +use std::error::Error; +use std::fmt::Display; + +use crate::platform::macos::frame::MacosVideoFrame; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum MetalVideoFramePlaneTexture { + Rgba, + Luminance, + Chroma +} + +/// Represents an error getting the texture from a video frame +#[derive(Clone, Debug)] +pub enum MacosVideoFrameError { + NoIoSurface, + NoImageBuffer, + InvalidVideoPlaneTexture, + Other(String) +} + + +impl Display for MacosVideoFrameError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::NoIoSurface => f.write_str("MacosVideoFrameError::NoIoSurface"), + Self::NoImageBuffer => f.write_str("MacosVideoFrameError::NoImageBuffer"), + Self::InvalidVideoPlaneTexture => f.write_str("MacosVideoFrameError::InvalidVideoPlaneTexture"), + Self::Other(error) => f.write_fmt(format_args!("MacosVideoFrameError::Other(\"{}\")", error)), + } + } +} + +impl Error for MacosVideoFrameError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + None + } + + fn description(&self) -> &str { + "description() is deprecated; use Display" + } + + fn cause(&self) -> Option<&dyn Error> { + self.source() + } +} + +pub trait MetalVideoFrame { + /// Get the texture for the given plane of the video frame + fn get_texture(&self, plane: MetalVideoFramePlaneTexture) -> Result; +} + +#[cfg(feature="metal")] +impl MetalVideoFrame for VideoFrame { + fn get_texture(&self, plane: MetalVideoFramePlaneTexture) -> Result { + let iosurface_and_metal_device = match &self.impl_video_frame { + MacosVideoFrame::SCStream(frame) => { + match frame.sample_buffer.get_image_buffer() { + Some(image_buffer) => { + match image_buffer.get_iosurface() { + Some(iosurface) => { + Ok((iosurface, frame.metal_device.clone())) + }, + None => Err(MacosVideoFrameError::NoIoSurface) + } + }, + None => Err(MacosVideoFrameError::NoImageBuffer) + } + }, + MacosVideoFrame::CGDisplayStream(frame) => { + Ok((frame.io_surface.clone(), frame.metal_device.clone())) + } + }?; + let (iosurface, metal_device) = iosurface_and_metal_device; + let pixel_format = match iosurface.get_pixel_format() { + None => return Err(MacosVideoFrameError::Other("Unable to get pixel format from iosurface".to_string())), + Some(format) => format + }; + match pixel_format { + CVPixelFormat::BGRA8888 => { + match plane { + MetalVideoFramePlaneTexture::Rgba => {}, + _ => return Err(MacosVideoFrameError::InvalidVideoPlaneTexture), + } + unsafe { + let device_ref = metal_device.as_ref(); + let texture_descriptor = metal::TextureDescriptor::new(); + texture_descriptor.set_texture_type(metal::MTLTextureType::D2); + texture_descriptor.set_pixel_format(metal::MTLPixelFormat::RGBA8Unorm); + texture_descriptor.set_width(iosurface.get_width() as u64); + texture_descriptor.set_height(iosurface.get_height() as u64); + texture_descriptor.set_sample_count(1); + texture_descriptor.set_mipmap_level_count(1); + texture_descriptor.set_storage_mode(metal::MTLStorageMode::Shared); + let texture_ptr: *mut Object = msg_send![device_ref, newTextureWithDescriptor: texture_descriptor.as_ptr() iosurface: iosurface.0 plane: 0]; + if texture_ptr.is_null() { + Err(MacosVideoFrameError::Other("Failed to create metal texture".to_string())) + } else { + Ok((metal::Texture::from_ptr(texture_ptr as *mut metal::MTLTexture)).to_owned()) + } + } + }, + CVPixelFormat::V420 | CVPixelFormat::F420 => { + let (plane, texture_index) = match plane { + MetalVideoFramePlaneTexture::Luminance => (0, metal::MTLPixelFormat::R8Uint), + MetalVideoFramePlaneTexture::Chroma => (1, metal::MTLPixelFormat::RG8Uint), + _ => return Err(MacosVideoFrameError::InvalidVideoPlaneTexture), + }; + unsafe { + let device_ref = metal_device.as_ref(); + let texture_descriptor = metal::TextureDescriptor::new(); + texture_descriptor.set_texture_type(metal::MTLTextureType::D2); + texture_descriptor.set_pixel_format(texture_index); + texture_descriptor.set_width(iosurface.get_width() as u64); + texture_descriptor.set_height(iosurface.get_height_of_plane(plane) as u64); + texture_descriptor.set_sample_count(1); + texture_descriptor.set_mipmap_level_count(1); + texture_descriptor.set_storage_mode(metal::MTLStorageMode::Shared); + let texture_ptr: *mut Object = msg_send![device_ref, newTextureWithDescriptor: texture_descriptor.as_ptr() iosurface: iosurface.0 plane: plane]; + if texture_ptr.is_null() { + Err(MacosVideoFrameError::Other("Failed to create metal texture".to_string())) + } else { + Ok((metal::Texture::from_ptr(texture_ptr as *mut metal::MTLTexture)).to_owned()) + } + } + }, + _ => Err(MacosVideoFrameError::Other("Unknown pixel format on iosurface".to_string())), + } + } +} + +pub trait MetalCaptureStream { + /// Get the metal device used for frame capture + fn get_metal_device(&self) -> metal::Device; +} + +impl MetalCaptureStream for CaptureStream { + fn get_metal_device(&self) -> metal::Device { + self.impl_capture_stream.metal_device.clone() + } +} diff --git a/src/feature/mod.rs b/src/feature/mod.rs new file mode 100644 index 00000000..aefb95da --- /dev/null +++ b/src/feature/mod.rs @@ -0,0 +1,16 @@ +#[cfg(feature = "metal")] +#[cfg(target_os="macos")] +pub mod metal; +#[cfg(feature = "dxgi")] +#[cfg(target_os="windows")] +pub mod dxgi; +#[cfg(feature = "dx11")] +#[cfg(target_os="windows")] +pub mod dx11; +#[cfg(feature = "iosurface")] +#[cfg(target_os="macos")] +pub mod iosurface; +#[cfg(feature = "bitmap")] +pub mod bitmap; +#[cfg(feature = "wgpu")] +pub mod wgpu; \ No newline at end of file diff --git a/src/feature/wgpu/mod.rs b/src/feature/wgpu/mod.rs new file mode 100644 index 00000000..0ffdd02f --- /dev/null +++ b/src/feature/wgpu/mod.rs @@ -0,0 +1 @@ +// TODO \ No newline at end of file diff --git a/src/frame.rs b/src/frame.rs new file mode 100644 index 00000000..901ffe10 --- /dev/null +++ b/src/frame.rs @@ -0,0 +1,160 @@ +#![allow(unused)] +use std::{marker::PhantomData, time::{Duration, Instant}, fmt::Debug}; + +use crate::{platform::platform_impl::{ImplAudioFrame, ImplVideoFrame}, util::*}; + +/// The rate to capture audio samples +#[derive(Copy, Clone, Debug)] +pub enum AudioSampleRate { + Hz8000, + Hz16000, + Hz24000, + Hz48000, +} + +/// The number of audio channels to capture +#[derive(Copy, Clone, Debug)] +pub enum AudioChannelCount { + Mono, + Stereo +} + +/// Represents audio channel data in an audio frame +pub enum AudioChannelData<'data> { + F32(AudioChannelDataSamples<'data, f32>), + I32(AudioChannelDataSamples<'data, i32>), + I16(AudioChannelDataSamples<'data, i16>), +} + +pub struct AudioChannelDataSamples<'data, T> { + pub(crate) data: *const u8, + pub(crate) stride: usize, + pub(crate) length: usize, + pub(crate) phantom_lifetime: PhantomData<&'data T>, +} + +impl AudioChannelDataSamples<'_, T> { + fn get(&self, i: usize) -> T { + let ptr = self.data.wrapping_add(self.stride * i); + unsafe { *(ptr as *const T) } + } + + fn length(&self) -> usize { + self.length + } +} + +/// Represents an error getting the data for an audio channel +pub enum AudioBufferError { + UnsupportedFormat, + InvalidChannel, + Other(String) +} + +pub(crate) trait AudioCaptureFrame { + fn sample_rate(&self) -> AudioSampleRate; + fn channel_count(&self) -> AudioChannelCount; + fn audio_channel_buffer(&mut self, channel: usize) -> Result, AudioBufferError>; + fn duration(&self) -> Duration; + fn origin_time(&self) -> Duration; + fn frame_id(&self) -> u64; +} + +/// A frame of captured audio +pub struct AudioFrame { + pub(crate) impl_audio_frame: ImplAudioFrame, +} + +impl Debug for AudioFrame { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("AudioFrame").finish() + } +} + +impl AudioFrame { + /// Get the sample rate of the captured audio + pub fn sample_rate(&self) -> AudioSampleRate { + self.impl_audio_frame.sample_rate() + } + + /// Get the channel count of the captured audio + pub fn channel_count(&self) -> AudioChannelCount { + self.impl_audio_frame.channel_count() + } + + /// Get the data buffer for the captured audio channel + pub fn audio_channel_buffer(&mut self, channel: usize) -> Result, AudioBufferError> { + self.impl_audio_frame.audio_channel_buffer(channel) + } + + /// Get the duration of this audio frames + pub fn duration(&self) -> Duration { + self.impl_audio_frame.duration() + } + + /// Get the time since the start of the stream that this audio frame begins at + pub fn origin_time(&self) -> Duration { + self.impl_audio_frame.duration() + } + + /// Get the sequence id of this frame (monotonically increasing) + /// + /// Note: This is separate from video frame ids + pub fn frame_id(&self) -> u64 { + self.impl_audio_frame.frame_id() + } +} + +pub(crate) trait VideoCaptureFrame { + fn size(&self) -> Size; + fn dpi(&self) -> f64; + fn duration(&self) -> Duration; + fn origin_time(&self) -> Duration; + fn capture_time(&self) -> Instant; + fn frame_id(&self) -> u64; +} + +/// A frame of captured video +pub struct VideoFrame { + pub(crate) impl_video_frame: ImplVideoFrame, +} + +unsafe impl Send for VideoFrame {} +unsafe impl Sync for VideoFrame {} + +impl VideoFrame { + /// Get the sequence id of this video frame (monotonically increasing) + /// + /// Note: This is separate from audio frame ids + pub fn frame_id(&self) -> u64 { + self.impl_video_frame.frame_id() + } + + /// Get the Instant that this frame was delivered to the application + pub fn capture_time(&self) -> Instant { + self.impl_video_frame.capture_time() + } + + /// Get the time since the start of the stream that this frame was generated + pub fn origin_time(&self) -> Duration { + self.impl_video_frame.origin_time() + } + + /// Get the raw size of the frame + /// + /// For planar image formats, this is the size of the largest plane + pub fn size(&self) -> Size { + self.impl_video_frame.size() + } + + /// Get the dpi of the contents of the frame (accounting for capture scaling) + pub fn dpi(&self) -> f64 { + self.impl_video_frame.dpi() + } +} + +impl Debug for VideoFrame { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("VideoFrame").finish() + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000..8507d128 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,88 @@ +//! A cross-platform screen/window/audio capture library +//! +//! ## Feature flags +//! +//! ### GPU Interop +//! +//! - **`dx11`** - enables retreiving the surface of a video frame and getting the dx11 device instance for the stream (windows only) +//! - **`dxgi`** - enables retreiving the surface of a video frame and getting the dxgi device instance for the stream (windows only) +//! - **`metal`** - enabels retreiving the metal textures for a video frame and getting the metal device instance for the stream (macos only) +//! - **`iosurface`** - enables retreiving the iosurface for a video frame (macos only) +//! +//! ### Bitmap output +//! +//! - **`bitmap`** - enables creating raw bitmap copies of frames in system memory +//! +//! ## Example +//! +//! ``` +//! use std::time::Duration; +//! use crabgrab::prelude::*; +//! +//! // spin up the async runtime +//! let runtime = tokio::runtime::Builder::new_multi_thread().build().unwrap(); +//! // run our capture code in an async context +//! let future = runtime.spawn(async { +//! // ensure we have priveleges to capture content +//! if !CaptureStream::test_access(false) { +//! CaptureStream::request_access(false).await; +//! println!("Approve access and run again!"); +//! } +//! // create a filter for the windows we're interested in capturing +//! let window_filter = CapturableWindowFilter { +//! desktop_windows: false, +//! onscreen_only: true, +//! }; +//! // create an overall content filter +//! let filter = CapturableContentFilter { windows: Some(window_filter), displays: false }; +//! // get capturable content matching the filter +//! let content = CapturableContent::new(filter).await.unwrap(); +//! // find the window we want to capture +//! let window = content.windows().filter(|window| { +//! let app_identifier = window.application().identifier(); +//! app_identifier.to_lowercase().contains("finder") || app_identifier.to_lowercase().contains("explorer") +//! }).next(); +//! match window { +//! Some(window) => { +//! println!("capturing window: {}", window.title()); +//! // create a captuere config using the first supported pixel format +//! let config = CaptureConfig::with_window(window, CaptureStream::supported_pixel_formats()[0]).unwrap(); +//! // create a capture stream with an event handler callback +//! let mut stream = CaptureStream::new(config, |stream_event| { +//! match stream_event { +//! Ok(event) => { +//! match event { +//! StreamEvent::Video(frame) => { +//! println!("Got frame: {}", frame.frame_id()); +//! }, +//! _ => {} +//! } +//! }, +//! Err(error) => { +//! println!("Stream error: {:?}", error); +//! } +//! } +//! }).unwrap(); +//! // wait for a while to capture some frames +//! tokio::task::block_in_place(|| std::thread::sleep(Duration::from_millis(4000))); +//! stream.stop().unwrap(); +//! }, +//! None => { println!("Failed to find window"); } +//! } +//! }); +//! // wait for the future to complete +//! runtime.block_on(future).unwrap(); +//! // shutdown the async runtime +//! runtime.shutdown_timeout(Duration::from_millis(10000)); +//! ```` +//! + +pub mod platform; +pub mod feature; + +pub mod util; +pub mod frame; +pub mod capture_stream; +pub mod capturable_content; + +pub mod prelude; \ No newline at end of file diff --git a/src/platform/macos/capturable_content.rs b/src/platform/macos/capturable_content.rs new file mode 100644 index 00000000..0fe0b54d --- /dev/null +++ b/src/platform/macos/capturable_content.rs @@ -0,0 +1,128 @@ +use std::{cell::Cell, fmt::Debug}; + +use futures::channel::oneshot; +use libc::getpid; + +use crate::{capturable_content::{CapturableContentFilter, CapturableContentError}, util::{Rect, Point, Size}}; + +use super::objc_wrap::{SCDisplay, SCRunningApplication, SCShareableContent, SCWindow}; + +pub struct MacosCapturableContent { + pub windows: Vec, + pub displays: Vec, +} + +impl MacosCapturableContent { + pub async fn new(filter: CapturableContentFilter) -> Result { + let (exclude_desktop, onscreen_only) = filter.windows.map_or((false, true), |filter| (!filter.desktop_windows, filter.onscreen_only)); + let (tx, rx) = oneshot::channel(); + let tx = Cell::new(Some(tx)); + SCShareableContent::get_shareable_content_with_completion_handler(exclude_desktop, onscreen_only, move |result| { + if let Some(tx) = tx.take() { + let _ = tx.send(result); + } + }); + + match rx.await { + Ok(Ok(content)) => { + let windows = content.windows(); + let displays = content.displays(); + Ok(Self { + windows, + displays, + }) + }, + Ok(Err(error)) => { + Err(CapturableContentError::Other(format!("SCShareableContent returned error code: {}", error.code()))) + } + Err(_) => Err(CapturableContentError::Other("Failed to receive SCSharableContent result from completion handler future".into())), + } + } +} + +#[derive(Clone)] +pub struct MacosCapturableWindow { + pub(crate) window: SCWindow +} + +impl MacosCapturableWindow { + pub fn from_impl(window: SCWindow) -> Self { + Self { + window + } + } + + pub fn title(&self) -> String { + self.window.title() + } + + pub fn rect(&self) -> Rect { + let frame = self.window.frame(); + Rect { + origin: Point { + x: frame.origin.x, + y: frame.origin.y, + }, + size: Size { + width: frame.size.x, + height: frame.size.y + } + } + } + + pub fn application(&self) -> MacosCapturableApplication { + MacosCapturableApplication { + running_application: self.window.owning_application() + } + } +} + +impl Debug for MacosCapturableWindow { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MacosCapturableWindow").field("window", &self.window.title()).finish() + } +} + +#[derive(Clone)] +pub struct MacosCapturableDisplay { + pub(crate) display: SCDisplay +} + +impl MacosCapturableDisplay { + pub fn from_impl(display: SCDisplay) -> Self { + Self { + display + } + } + + pub fn rect(&self) -> Rect { + let frame = self.display.frame(); + Rect { + origin: Point { + x: frame.origin.x, + y: frame.origin.y, + }, + size: Size { + width: frame.size.x, + height: frame.size.y + } + } + } +} + +impl Debug for MacosCapturableDisplay { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MacosCapturableDisplay").field("display", &self.display.raw_id()).finish() + } +} + +#[derive()] +pub struct MacosCapturableApplication { + pub(crate) running_application: SCRunningApplication, +} + +impl MacosCapturableApplication { + pub fn identifier(&self) -> String { + self.running_application.bundle_identifier() + } +} diff --git a/src/platform/macos/capture_stream.rs b/src/platform/macos/capture_stream.rs new file mode 100644 index 00000000..5da34c4b --- /dev/null +++ b/src/platform/macos/capture_stream.rs @@ -0,0 +1,342 @@ +use std::{borrow::{Borrow, BorrowMut}, cell::{Cell, RefCell}, sync::{atomic::{self, AtomicBool, AtomicU64}, Arc}, time::{Duration, Instant}}; + +use futures::executor::block_on; +use objc::runtime::Object; +use parking_lot::Mutex; + +use crate::{capture_stream::{CaptureConfig, StreamCreateError, StreamError, StreamEvent}, platform::platform_impl::{frame::MacosSCStreamVideoFrame, objc_wrap::NSNumber}, prelude::{AudioCaptureConfig, AudioFrame, Capturable, CaptureConfigError, CapturePixelFormat, StreamStopError, VideoFrame}, util::{Rect, Size}}; +use super::{frame::{MacosAudioFrame, MacosCGDisplayStreamVideoFrame, MacosVideoFrame}, objc_wrap::{kCFBooleanFalse, kCFBooleanTrue, kCGDisplayStreamDestinationRect, kCGDisplayStreamMinimumFrameTime, kCGDisplayStreamPreserveAspectRatio, kCGDisplayStreamQueueDepth, kCGDisplayStreamShowCursor, kCGDisplayStreamSourceRect, CFNumber, CGDisplayStream, CGDisplayStreamFrameStatus, CGPoint, CGRect, CGSize, CMSampleBuffer, CMTime, DispatchQueue, NSArray, NSDictionary, NSString, SCContentFilter, SCFrameStatus, SCStream, SCStreamCallbackError, SCStreamColorMatrix, SCStreamConfiguration, SCStreamFrameInfoStatus, SCStreamHandler, SCStreamOutputType, SCStreamPixelFormat, SCStreamSampleRate}}; + +pub type MacosPixelFormat = SCStreamPixelFormat; + +impl TryFrom for SCStreamPixelFormat { + type Error = StreamCreateError; + + fn try_from(value: CapturePixelFormat) -> Result { + match value { + CapturePixelFormat::Bgra8888 => Ok(SCStreamPixelFormat::BGRA8888), + CapturePixelFormat::Argb2101010 => Ok(SCStreamPixelFormat::L10R), + CapturePixelFormat::F420 => Ok(SCStreamPixelFormat::F420), + CapturePixelFormat::V420 => Ok(SCStreamPixelFormat::V420), + _ => Err(StreamCreateError::UnsupportedPixelFormat) + } + } +} + +enum MacosCaptureStreamInternal { + Window(SCStream), + Display(CGDisplayStream), +} + +pub(crate) struct MacosCaptureStream { + stream: MacosCaptureStreamInternal, + stopped_flag: Arc, + shared_callback: Arc) + Send + 'static>>>, + #[cfg(feature = "metal")] + pub(crate) metal_device: metal::Device, +} + +pub trait MacosCaptureConfigExt { + fn with_scale_to_fit(self, scale_to_fit: bool) -> Self; + fn with_maximum_fps(self, maximum_fps: Option) -> Self; + #[cfg(feature = "metal")] + fn with_metal_device(self, metal_device: metal::Device) -> Self; +} + +#[derive(Clone, Debug)] +pub(crate) struct MacosCaptureConfig { + scale_to_fit: bool, + maximum_fps: Option, + #[cfg(feature = "metal")] + metal_device: Option, +} + +impl MacosCaptureConfig { + pub fn new() -> Self { + Self { + scale_to_fit: true, + maximum_fps: None, + #[cfg(feature = "metal")] + metal_device: None, + } + } +} + +impl MacosCaptureConfigExt for CaptureConfig { + fn with_scale_to_fit(self, scale_to_fit: bool) -> Self { + Self { + impl_capture_config: MacosCaptureConfig { + scale_to_fit, + ..self.impl_capture_config + }, + ..self + } + } + + fn with_maximum_fps(self, maximum_fps: Option) -> Self { + Self { + impl_capture_config: MacosCaptureConfig { + maximum_fps, + ..self.impl_capture_config + }, + ..self + } + } + + #[cfg(feature = "metal")] + fn with_metal_device(self, metal_device: metal::Device) -> Self { + Self { + impl_capture_config: MacosCaptureConfig { + metal_device: Some(metal_device), + ..self.impl_capture_config + }, + ..self + } + } +} + +pub trait MacosAudioCaptureConfigExt { + fn set_exclude_current_process_audio(self, exclude_current_process_audio: bool) -> Self; +} + +#[derive(Copy, Clone, Debug)] +pub(crate) struct MacosAudioCaptureConfig { + exclude_current_process_audio: bool, +} + +impl MacosAudioCaptureConfig { + pub fn new() -> Self { + Self { + exclude_current_process_audio: false, + } + } +} + +impl MacosAudioCaptureConfigExt for AudioCaptureConfig { + fn set_exclude_current_process_audio(self, exclude_current_process_audio: bool) -> Self { + Self { + impl_capture_audio_config: MacosAudioCaptureConfig { + exclude_current_process_audio, + ..self.impl_capture_audio_config + }, + ..self + } + } +} + +impl MacosCaptureStream { + pub fn supported_pixel_formats() -> &'static [CapturePixelFormat] { + &[ + CapturePixelFormat::V420, + CapturePixelFormat::F420, + CapturePixelFormat::Bgra8888, + CapturePixelFormat::Argb2101010, + ] + } + + pub fn check_access(_borderless: bool) -> bool { + return SCStream::preflight_access() + } + + pub async fn request_access(_borderless: bool) -> bool { + SCStream::request_access().await + } + + pub fn new(capture_config: CaptureConfig, mut callback: Box) + Send + 'static>) -> Result { + let shared_callback = Arc::new(Mutex::new(callback as Box) + Send + 'static>)); + let stream_shared_callback = shared_callback.clone(); + match capture_config.target { + Capturable::Window(window) => { + let mut config = SCStreamConfiguration::new(); + config.set_size(CGSize { + x: window.rect().size.width * 2.0, + y: window.rect().size.height * 2.0, + }); + config.set_scales_to_fit(false); + let (pixel_format, set_color_matrix) = match capture_config.pixel_format { + CapturePixelFormat::Bgra8888 => (SCStreamPixelFormat::BGRA8888, false), + CapturePixelFormat::Argb2101010 => (SCStreamPixelFormat::L10R, false), + CapturePixelFormat::V420 => (SCStreamPixelFormat::V420, true), + CapturePixelFormat::F420 => (SCStreamPixelFormat::F420, true), + }; + if set_color_matrix { + config.set_color_matrix(SCStreamColorMatrix::ItuR709_2); + } + config.set_pixel_format(pixel_format); + config.set_minimum_time_interval(CMTime::new_with_seconds(capture_config.impl_capture_config.maximum_fps.map(|x| 1.0 / x).unwrap_or(1.0 / 120.0) as f64, 240)); + config.set_source_rect(CGRect { + origin: CGPoint { + x: capture_config.source_rect.origin.x, + y: capture_config.source_rect.origin.y, + }, + size: CGSize { + x: capture_config.source_rect.size.width, + y: capture_config.source_rect.size.height + } + }); + config.set_size(CGSize { + x: capture_config.output_size.width, + y: capture_config.output_size.height, + }); + config.set_queue_depth(capture_config.buffer_count as isize); + config.set_show_cursor(capture_config.show_cursor); + match capture_config.capture_audio { + Some(audio_config) => { + config.set_capture_audio(true); + let channel_count = match audio_config.channel_count { + crate::prelude::AudioChannelCount::Mono => 1, + crate::prelude::AudioChannelCount::Stereo => 2, + }; + config.set_channel_count(channel_count); + config.set_exclude_current_process_audio(audio_config.impl_capture_audio_config.exclude_current_process_audio); + let sample_rate = match audio_config.sample_rate { + crate::prelude::AudioSampleRate::Hz8000 => SCStreamSampleRate::R8000, + crate::prelude::AudioSampleRate::Hz16000 => SCStreamSampleRate::R16000, + crate::prelude::AudioSampleRate::Hz24000 => SCStreamSampleRate::R24000, + crate::prelude::AudioSampleRate::Hz48000 => SCStreamSampleRate::R48000, + }; + config.set_sample_rate(sample_rate); + }, + None => { + config.set_capture_audio(false); + } + } + + let filter = SCContentFilter::new_with_desktop_independent_window(&window.impl_capturable_window.window); + + let handler_queue = DispatchQueue::make_concurrent("com.augmend.crabgrab.window_capture".into()); + + let mut audio_frame_id_counter = AtomicU64::new(0); + let mut video_frame_id_counter = AtomicU64::new(0); + + let stopped_flag = Arc::new(AtomicBool::new(false)); + let callback_stopped_flag = stopped_flag.clone(); + + #[cfg(feature = "metal")] + let mut metal_device = match capture_config.impl_capture_config.metal_device { + Some(metal_device) => metal_device, + None => { + match metal::Device::system_default() { + Some(device) => device, + None => return Err(StreamCreateError::Other("Failed to create system default metal device".into())) + } + } + }; + #[cfg(feature = "metal")] + let callback_metal_device = metal_device.clone(); + + let handler = SCStreamHandler::new(Box::new(move |stream_result: Result<(CMSampleBuffer, SCStreamOutputType), SCStreamCallbackError>| { + let mut callback = stream_shared_callback.lock(); + let capture_time = Instant::now(); + match stream_result { + Ok((sample_buffer, output_type)) => { + match output_type { + SCStreamOutputType::Audio => { + let frame_id = audio_frame_id_counter.fetch_add(1, atomic::Ordering::AcqRel); + // TODO... + }, + SCStreamOutputType::Screen => { + let attachments = sample_buffer.get_sample_attachment_array(); + if attachments.len() == 0 { + return; + } + let status_nsnumber_ptr = unsafe { attachments[0].get_value(SCStreamFrameInfoStatus) }; + if status_nsnumber_ptr.is_null() { + return; + } + let status_i32 = unsafe { NSNumber::from_id_unretained(status_nsnumber_ptr as *mut Object).as_i32() }; + let status_opt = SCFrameStatus::from_i32(status_i32); + if status_opt.is_none() { + return; + } + match status_opt.unwrap() { + SCFrameStatus::Complete => { + if callback_stopped_flag.load(atomic::Ordering::Acquire) { + return; + } + let frame_id = video_frame_id_counter.fetch_add(1, atomic::Ordering::AcqRel); + let video_frame = VideoFrame { + impl_video_frame: MacosVideoFrame::SCStream(MacosSCStreamVideoFrame { + sample_buffer, + capture_time, + dictionary: RefCell::new(None), + frame_id, + #[cfg(feature = "metal")] + metal_device: callback_metal_device.clone() + }) + }; + (callback)(Ok(StreamEvent::Video(video_frame))); + }, + SCFrameStatus::Suspended | + SCFrameStatus::Idle => { + if callback_stopped_flag.load(atomic::Ordering::Acquire) { + return; + } + (callback)(Ok(StreamEvent::Idle)); + }, + SCFrameStatus::Stopped => { + if callback_stopped_flag.fetch_and(true, atomic::Ordering::AcqRel) { + return; + } + (callback)(Ok(StreamEvent::End)); + } + _ => {} + } + + + }, + } + }, + Err(err) => { + let event = match err { + SCStreamCallbackError::StreamStopped => { + if callback_stopped_flag.fetch_and(true, atomic::Ordering::AcqRel) { + return; + } + Ok(StreamEvent::End) + }, + SCStreamCallbackError::SampleBufferCopyFailed => Err(StreamError::Other("Failed to copy sample buffer".into())), + SCStreamCallbackError::Other(e) => Err(StreamError::Other(format!("Internal stream failure: [description: {}, reason: {}, code: {}, domain: {}]", e.description(), e.reason(), e.code(), e.domain()))), + }; + (callback)(event); + } + } + })); + + let mut sc_stream = SCStream::new(filter, config, handler_queue, handler) + .map_err(|error| StreamCreateError::Other(error))?; + + sc_stream.start(); + + Ok(MacosCaptureStream { + stopped_flag, + shared_callback, + stream: MacosCaptureStreamInternal::Window(sc_stream), + #[cfg(feature = "metal")] + metal_device + }) + }, + Capturable::Display(_) => Err(StreamCreateError::Other("Macos display capture unimplemented".into())) + } + + } + + pub(crate) fn stop(&mut self) -> Result<(), StreamStopError> { + { + let mut callback = self.shared_callback.lock(); + if !self.stopped_flag.fetch_and(true, atomic::Ordering::AcqRel) { + (callback)(Ok(StreamEvent::End)); + } + } + match &mut self.stream { + MacosCaptureStreamInternal::Window(stream) => { stream.stop(); Ok(()) }, + MacosCaptureStreamInternal::Display(stream) => stream.stop().map_err(|_| StreamStopError::Other("Unkown".into())), + } + } +} + +impl Drop for MacosCaptureStream { + fn drop(&mut self) { + self.stop(); + } +} diff --git a/src/platform/macos/frame.rs b/src/platform/macos/frame.rs new file mode 100644 index 00000000..d1134b56 --- /dev/null +++ b/src/platform/macos/frame.rs @@ -0,0 +1,194 @@ +use std::{cell::{Ref, RefCell}, marker::PhantomData, time::{Duration, Instant}}; + +use objc::runtime::Object; + +use crate::{frame::{AudioCaptureFrame, VideoCaptureFrame}, prelude::{AudioBufferError, AudioChannelCount, AudioChannelData, AudioChannelDataSamples, AudioSampleRate}, util::{Rect, Size}}; + +use super::objc_wrap::{kAudioFormatFlagIsBigEndian, kAudioFormatFlagIsPacked, kAudioFormatFlagsCanonical, kAudioFormatNativeEndian, AVAudioFormat, AVAudioPCMBuffer, AudioBufferList, AudioStreamBasicDescription, CFDictionary, CGRect, CGRectMakeWithDictionaryRepresentation, CMBlockBuffer, CMSampleBuffer, IOSurface, NSDictionary, NSNumber, NSScreen, SCStreamFrameInfoScaleFactor, SCStreamFrameInfoScreenRect}; + +pub(crate) struct MacosSCStreamVideoFrame { + pub(crate) sample_buffer: CMSampleBuffer, + pub(crate) capture_time: Instant, + pub(crate) dictionary: RefCell>, + pub(crate) frame_id: u64, + #[cfg(feature = "metal")] + pub(crate) metal_device: metal::Device, +} + +pub(crate) struct MacosCGDisplayStreamVideoFrame { + pub(crate) io_surface: IOSurface, + pub(crate) duration: Duration, + pub(crate) capture_time: Duration, + pub(crate) capture_timestamp: Instant, + pub(crate) frame_id: u64, + pub(crate) source_rect: Rect, + pub(crate) dest_size: Size, + #[cfg(feature = "metal")] + pub(crate) metal_device: metal::Device, +} + +impl MacosSCStreamVideoFrame { + fn get_info_dict(&self) -> Ref<'_, CFDictionary> { + let needs_dict = { self.dictionary.borrow().is_none() }; + if needs_dict { + let mut dict_opt_mut = self.dictionary.borrow_mut(); + *dict_opt_mut = Some(self.sample_buffer.get_sample_attachment_array()[0].clone()); + } + Ref::map(self.dictionary.borrow(), |x| x.as_ref().unwrap()) + } +} + +pub(crate) enum MacosVideoFrame { + SCStream(MacosSCStreamVideoFrame), + CGDisplayStream(MacosCGDisplayStreamVideoFrame), +} + +impl VideoCaptureFrame for MacosVideoFrame { + fn size(&self) -> Size { + match self { + MacosVideoFrame::SCStream(sc_frame) => { + sc_frame.sample_buffer.get_image_buffer().map(|image_buffer| { + Size { + width: image_buffer.get_width() as f64, + height: image_buffer.get_height() as f64, + } + }).unwrap_or(Size { width: 0.0, height: 0.0}) + } + MacosVideoFrame::CGDisplayStream(cgd_frame) => cgd_frame.dest_size + } + } + + fn dpi(&self) -> f64 { + match self { + MacosVideoFrame::SCStream(sc_frame) => { + let info_dict = sc_frame.get_info_dict(); + let scale_factor_ptr = unsafe { info_dict.get_value(SCStreamFrameInfoScaleFactor) }; + let scale_factor = unsafe { NSNumber::from_id_unretained(scale_factor_ptr as *mut Object).as_f64() }; + let screen_rect_ptr = unsafe { info_dict.get_value(SCStreamFrameInfoScreenRect) }; + let screen_rect_dict = unsafe { NSDictionary::from_id_unretained(screen_rect_ptr as *mut Object) }; + let frame_screen_rect = unsafe { CGRect::create_from_dictionary_representation(&screen_rect_dict) }; + let mut dpi = 72.0f64; + for screen in NSScreen::screens() { + let screen_rect = screen.frame(); + if screen_rect.contains(frame_screen_rect.origin) { + dpi = screen.dpi(); + break; + } + } + dpi + }, + MacosVideoFrame::CGDisplayStream(cgd_frame) => todo!() + } + } + + fn duration(&self) -> Duration { + match self { + MacosVideoFrame::SCStream(sc_frame) => std::time::Duration::from_secs_f64(sc_frame.sample_buffer.get_duration().seconds_f64()), + MacosVideoFrame::CGDisplayStream(cgd_frame) => cgd_frame.duration + } + } + + fn origin_time(&self) -> Duration { + match self { + MacosVideoFrame::SCStream(sc_frame) => std::time::Duration::from_secs_f64(sc_frame.sample_buffer.get_presentation_timestamp().seconds_f64()), + MacosVideoFrame::CGDisplayStream(cgd_frame) => cgd_frame.capture_time + } + } + + fn capture_time(&self) -> Instant { + match self { + MacosVideoFrame::SCStream(sc_frame) => sc_frame.capture_time, + MacosVideoFrame::CGDisplayStream(cgd_frame) => cgd_frame.capture_timestamp + } + } + + fn frame_id(&self) -> u64 { + match self { + MacosVideoFrame::SCStream(sc_frame) => sc_frame.frame_id, + MacosVideoFrame::CGDisplayStream(cgd_frame) => cgd_frame.frame_id + } + } +} + +pub struct MacosAudioFrame { + pub(crate) sample_buffer: CMSampleBuffer, + pub(crate) audio_format_description: AudioStreamBasicDescription, + pub(crate) pcm_audio_buffer: Option, + pub(crate) block_buffer: Option, + pub(crate) buffer_list: Option, + pub(crate) capture_time: Instant, + pub(crate) frame_id: u64, +} + +impl AudioCaptureFrame for MacosAudioFrame { + fn sample_rate(&self) -> crate::prelude::AudioSampleRate { + if self.audio_format_description.sample_rate >= 15500.0 && self.audio_format_description.sample_rate <= 16500.0 { + AudioSampleRate::Hz16000 + } else if self.audio_format_description.sample_rate >= 23500.0 && self.audio_format_description.sample_rate <= 24500.0 { + AudioSampleRate::Hz24000 + } else if self.audio_format_description.sample_rate >= 47500.0 && self.audio_format_description.sample_rate <= 48500.0 { + AudioSampleRate::Hz48000 + } else { + AudioSampleRate::Hz8000 + } + } + + fn channel_count(&self) -> crate::prelude::AudioChannelCount { + if self.audio_format_description.channels_per_frame == 1 { + AudioChannelCount::Mono + } else { + AudioChannelCount::Stereo + } + } + + fn audio_channel_buffer(&mut self, channel: usize) -> Result, AudioBufferError> { + let pcm_audio_buffer_ref = if self.pcm_audio_buffer.is_some() { + self.pcm_audio_buffer.as_ref().unwrap() + } else { + if self.audio_format_description.format_flags == kAudioFormatFlagsCanonical { + let (audio_buffer_list, block_buffer) = match unsafe { self.sample_buffer.get_audio_buffer_list_with_block_buffer() } { + Ok(x) => x, + Err(()) => return Err(AudioBufferError::Other("CMSampleBuffer::get_audio_buffer_list_with_block_buffer() failed".into())) + }; + self.buffer_list = Some(audio_buffer_list); + self.block_buffer = Some(block_buffer); + let audio_buffer_list = self.buffer_list.as_ref().unwrap(); + let av_audio_format = AVAudioFormat::new_with_standard_format_sample_rate_channels(self.audio_format_description.sample_rate, self.audio_format_description.channels_per_frame); + if let Ok(pcm_audio_buffer) = AVAudioPCMBuffer::new_with_format_buffer_list_no_copy_deallocator(av_audio_format, audio_buffer_list as *const _) { + self.pcm_audio_buffer = Some(pcm_audio_buffer); + self.pcm_audio_buffer.as_ref().unwrap() + } else { + return Err(AudioBufferError::Other("Failed to build PCM audio buffer".into())); + } + } else { + return Err(AudioBufferError::UnsupportedFormat); + } + }; + if channel >= pcm_audio_buffer_ref.channel_count() { + return Err(AudioBufferError::InvalidChannel); + } + let stride = pcm_audio_buffer_ref.stride(); + if let Some(f32_ptr) = pcm_audio_buffer_ref.f32_buffer(channel) { + let data_samples = AudioChannelDataSamples { + data: f32_ptr as *const u8, + stride, + length: pcm_audio_buffer_ref.frame_capacity(), + phantom_lifetime: PhantomData + }; + return Ok(AudioChannelData::F32(data_samples)); + } + return Err(AudioBufferError::Other("Failed to get audio buffer".into())) + } + + fn duration(&self) -> std::time::Duration { + std::time::Duration::from_secs_f64(self.sample_buffer.get_duration().seconds_f64()) + } + + fn origin_time(&self) -> std::time::Duration { + std::time::Duration::from_secs_f64(self.sample_buffer.get_presentation_timestamp().seconds_f64()) + } + + fn frame_id(&self) -> u64 { + self.frame_id + } +} diff --git a/src/platform/macos/mod.rs b/src/platform/macos/mod.rs new file mode 100644 index 00000000..eb00d6f4 --- /dev/null +++ b/src/platform/macos/mod.rs @@ -0,0 +1,20 @@ +#![allow(unused)] + +pub(crate) mod capture_stream; +pub(crate) mod frame; +pub(crate) mod capturable_content; +pub(crate) mod objc_wrap; + +pub(crate) use capture_stream::MacosCaptureStream as ImplCaptureStream; +pub(crate) use capture_stream::MacosAudioCaptureConfig as ImplAudioCaptureConfig; +pub(crate) use capture_stream::MacosCaptureConfig as ImplCaptureConfig; +pub(crate) use frame::MacosAudioFrame as ImplAudioFrame; +pub(crate) use frame::MacosVideoFrame as ImplVideoFrame; +pub(crate) use capturable_content::MacosCapturableContent as ImplCapturableContent; +pub(crate) use capturable_content::MacosCapturableWindow as ImplCapturableWindow; +pub(crate) use capturable_content::MacosCapturableDisplay as ImplCapturableDisplay; +pub(crate) use capture_stream::MacosPixelFormat as ImplPixelFormat; +pub(crate) use capturable_content::MacosCapturableApplication as ImplCapturableApplication; + +pub use capture_stream::MacosAudioCaptureConfigExt; +pub use capture_stream::MacosCaptureConfigExt; diff --git a/src/platform/macos/objc_wrap.rs b/src/platform/macos/objc_wrap.rs new file mode 100644 index 00000000..cd3116a0 --- /dev/null +++ b/src/platform/macos/objc_wrap.rs @@ -0,0 +1,2586 @@ +#![allow(unused)] +#![allow(non_upper_case_globals)] + +#[link(name = "ScreenCaptureKit", kind = "framework")] +#[link(name = "CoreGraphics", kind = "framework")] +#[link(name = "CoreMedia", kind = "framework")] +#[link(name = "CoreVideo", kind = "framework")] +#[link(name = "IOSurface", kind = "framework")] +#[link(name = "System", kind = "dylib")] +#[link(name = "Foundation", kind = "framework")] +#[link(name = "AppKit", kind = "framework")] +#[link(name = "ApplicationServices", kind = "framework")] +#[link(name = "AVFoundation", kind = "framework")] +extern "C" {} + +use std::{cell::RefCell, ffi::CString, ops::{Add, Mul, Sub}, ptr::{null, null_mut}, sync::Arc, time::{Duration, Instant}}; + +use block::{Block, ConcreteBlock, RcBlock}; +use libc::{c_void, strlen}; +use objc::{class, declare::MethodImplementation, msg_send, runtime::{objc_copyProtocolList, objc_getProtocol, Class, Object, Protocol, Sel, BOOL}, sel, sel_impl, Encode, Encoding, Message}; +use objc2::runtime::Bool; +use mach2::mach_time::{mach_timebase_info, mach_timebase_info_data_t}; + +use crate::{prelude::{AudioSampleRate, StreamCreateError, StreamError, StreamEvent, StreamStopError}}; + +use lazy_static::lazy_static; +use parking_lot::Mutex; + +use super::ImplPixelFormat; + +type CFTypeRef = *const c_void; +type CFStringRef = CFTypeRef; +type CMSampleBufferRef = CFTypeRef; +type CFAllocatorRef = CFTypeRef; +type CFDictionaryRef = CFTypeRef; +type CMFormatDescriptionRef = CFTypeRef; +type CMBlockBufferRef = CFTypeRef; +type CFArrayRef = CFTypeRef; +type OSStatus = i32; +type CGDisplayStreamRef = CFTypeRef; +type CGDisplayStreamUpdateRef = CFTypeRef; +pub(crate) type IOSurfaceRef = CFTypeRef; +type CGDictionaryRef = CFTypeRef; +type CFBooleanRef = CFTypeRef; +type CFNumberRef = CFTypeRef; +type CVPixelBufferRef = CFTypeRef; + +#[allow(unused)] +fn debug_objc_class(name: &str) { + let class_name_cstring = CString::new(name).unwrap(); + let class = unsafe { &*objc::runtime::objc_getClass(class_name_cstring.as_ptr()) }; + println!("instance methods: "); + for method in class.instance_methods().iter() { + print!("METHOD {}::{}(", class.name(), method.name().name()); + for i in 0 .. method.arguments_count() { + if i + 1 == method.arguments_count() { + println!("{}) -> {}", method.argument_type(i).unwrap().as_str(), method.return_type().as_str()); + } else { + print!("{}, ", method.argument_type(i).unwrap().as_str()); + } + } + } + println!("instance variables: "); + for ivar in class.instance_variables().iter() { + println!("IVAR {}::{}: {}", class.name(), ivar.name(), ivar.type_encoding().as_str()); + } + let metaclass = class.metaclass(); + let metaclass_ptr = metaclass as *const _; + println!("metaclass ptr: {:?}", metaclass_ptr); + println!("class methods: "); + for method in metaclass.instance_methods().iter() { + print!("CLASS METHOD {}::{}(", class.name(), method.name().name()); + for i in 0 .. method.arguments_count() { + if i + 1 == method.arguments_count() { + println!("{}) -> {}", method.argument_type(i).unwrap().as_str(), method.return_type().as_str()); + } else { + print!("{}, ", method.argument_type(i).unwrap().as_str()); + } + } + } + + println!("class ivars: "); + for ivar in metaclass.instance_variables().iter() { + println!("CLASS IVAR {}::{}: {}", class.name(), ivar.name(), ivar.type_encoding().as_str()); + } + + println!("protocols: "); + for protocol in class.adopted_protocols().iter() { + println!("PROTOCOL {}", protocol.name()); + } + println!("end"); +} + +pub(crate) fn debug_objc_object(obj: *mut Object) { + if (obj.is_null()) { + println!("debug_objc_object: nil"); + return; + } else { + println!("debug_objc_object: {:?}", obj); + } + unsafe { + let class_ptr = objc::runtime::object_getClass(obj); + if class_ptr.is_null() { + println!(" * class: nil"); + return; + } + let class = &*class_ptr; + println!(" * class: {}", class.name()); + debug_objc_class(class.name()); + } +} + +extern "C" { + + static kCFAllocatorNull: CFTypeRef; + static kCFAllocatorDefault: CFTypeRef; + + fn CFRetain(x: CFTypeRef) -> CFTypeRef; + fn CFRelease(x: CFTypeRef); + + pub(crate) static kCFBooleanTrue: CFBooleanRef; + pub(crate) static kCFBooleanFalse: CFBooleanRef; + + //CFNumberRef CFNumberCreate(CFAllocatorRef allocator, CFNumberType theType, const void *valuePtr); + fn CFNumberCreate(allocator: CFAllocatorRef, the_type: isize, value_ptr: *const c_void) -> CFNumberRef; + + fn CGColorGetConstantColor(color_name: CFStringRef) -> CGColorRef; + + pub(crate) fn CGRectMakeWithDictionaryRepresentation(dictionary: CGDictionaryRef, rect: *mut CGRect) -> bool; + + static kCGColorBlack: CFStringRef; + static kCGColorWhite: CFStringRef; + static kCGColorClear: CFStringRef; + + fn CMTimeMake(value: i64, timescale: i32) -> CMTime; + fn CMTimeMakeWithEpoch(value: i64, timescale: i32, epoch: i64) -> CMTime; + fn CMTimeMakeWithSeconds(seconds: f64, preferred_timescale: i32) -> CMTime; + fn CMTimeGetSeconds(time: CMTime) -> f64; + + fn CMTimeAdd(lhs: CMTime, rhs: CMTime) -> CMTime; + fn CMTimeSubtract(lhs: CMTime, rhs: CMTime) -> CMTime; + fn CMTimeMultiply(lhs: CMTime, rhs: i32) -> CMTime; + fn CMTimeMultiplyByFloat64(time: CMTime, multiplier: f64) -> CMTime; + fn CMTimeMultiplyByRatio(time: CMTime, multiplier: i32, divisor: i32) -> CMTime; + fn CMTimeConvertScale(time: CMTime, new_timescale: i32, rounding_method: u32) -> CMTime; + fn CMTimeCompare(time1: CMTime, time2: CMTime) -> i32; + + static kCMTimeInvalid: CMTime; + static kCMTimeIndefinite: CMTime; + static kCMTimePositiveInfinity: CMTime; + static kCMTimeNegativeInfinity: CMTime; + static kCMTimeZero: CMTime; + + fn CMSampleBufferCreateCopy(allocator: CFAllocatorRef, original: CMSampleBufferRef, new: *mut CMSampleBufferRef) -> OSStatus; + fn CMSampleBufferIsValid(sbuf: CMSampleBufferRef) -> Bool; + fn CMSampleBufferGetNumSamples(sbuf: CMSampleBufferRef) -> isize; + fn CMSampleBufferGetPresentationTimeStamp(sbuf: CMSampleBufferRef) -> CMTime; + fn CMSampleBufferGetDuration(sbuf: CMSampleBufferRef) -> CMTime; + fn CMSampleBufferGetFormatDescription(sbuf: CMSampleBufferRef) -> CMFormatDescriptionRef; + fn CMSampleBufferGetSampleAttachmentsArray(sbuf: CMSampleBufferRef, create_if_necessary: Bool) -> CFArrayRef; + + fn CMFormatDescriptionGetMediaType(fdesc: CMFormatDescriptionRef) -> OSType; + fn CMAudioFormatDescriptionGetStreamBasicDescription(afdesc: CMFormatDescriptionRef) -> *const AudioStreamBasicDescription; + fn CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sbuf: CMSampleBufferRef, buffer_list_size_needed_out: *mut usize, buffer_list_out: *mut AudioBufferList, buffer_list_size: usize, block_buffer_structure_allocator: CFAllocatorRef, block_buffer_block_allocator: CFAllocatorRef, flags: u32, block_buffer_out: *mut CMBlockBufferRef) -> OSStatus; + fn CMSampleBufferGetImageBuffer(sbuffer: CMSampleBufferRef) -> CVPixelBufferRef; + fn CVPixelBufferGetIOSurface(pixel_buffer: CVPixelBufferRef) -> IOSurfaceRef; + fn CVPixelBufferGetWidth(pixel_buffer: CVPixelBufferRef) -> usize; + fn CVPixelBufferGetHeight(pixel_buffer: CVPixelBufferRef) -> usize; + fn CVBufferRetain(buffer: CVPixelBufferRef) -> CVPixelBufferRef; + fn CVBufferRelease(buffer: CVPixelBufferRef) -> CVPixelBufferRef; + + fn CFArrayGetCount(array: CFArrayRef) -> i32; + fn CFArrayGetValueAtIndex(array: CFArrayRef, index: i32) -> CFTypeRef; + + fn CFStringCreateWithBytes(allocator: CFTypeRef, bytes: *const u8, byte_count: isize, encoding: u32, contains_byte_order_marker: bool) -> CFStringRef; + + fn CFDictionaryGetValue(dict: CFDictionaryRef, value: CFTypeRef) -> CFTypeRef; + + fn CGPreflightScreenCaptureAccess() -> bool; + fn CGRequestScreenCaptureAccess() -> bool; + + //CGDisplayStreamRef CGDisplayStreamCreateWithDispatchQueue(CGDirectDisplayID display, size_t outputWidth, size_t outputHeight, int32_t pixelFormat, CFDictionaryRef properties, dispatch_queue_t queue, CGDisplayStreamFrameAvailableHandler handler); + fn CGDisplayStreamCreateWithDispatchQueue(display_id: u32, output_width: usize, output_height: usize, pixel_format: i32, properties: CFDictionaryRef, dispatch_queue: *mut Object, handler: *const c_void) -> CGDisplayStreamRef; + fn CGDisplayStreamStart(stream: CGDisplayStreamRef) -> i32; + fn CGDisplayStreamStop(stream: CGDisplayStreamRef) -> i32; + + fn CGMainDisplayID() -> u32; + + fn CGDisplayScreenSize(display: u32) -> CGSize; + + fn CGRectCreateDictionaryRepresentation(rect: CGRect) -> CFDictionaryRef; + + pub(crate) fn IOSurfaceIncrementUseCount(r: IOSurfaceRef); + pub(crate) fn IOSurfaceDecrementUseCount(r: IOSurfaceRef); + + fn IOSurfaceGetPixelFormat(surface: IOSurfaceRef) -> OSType; + fn IOSurfaceGetPlaneCount(surface: IOSurfaceRef) -> usize; + + fn IOSurfaceGetWidth(surface: IOSurfaceRef) -> usize; + fn IOSurfaceGetHeight(surface: IOSurfaceRef) -> usize; + + fn IOSurfaceLock(surface: IOSurfaceRef, options: u32, seed: *mut u32) -> i32; + fn IOSurfaceUnlock(surface: IOSurfaceRef, options: u32, seed: *mut u32) -> i32; + + fn IOSurfaceGetBaseAddressOfPlane(surface: IOSurfaceRef, plane: usize) -> *mut c_void; + fn IOSurfaceGetBaseAddress(surface: IOSurfaceRef) -> *mut c_void; + + fn IOSurfaceGetBytesPerRow(surface: IOSurfaceRef) -> usize; + fn IOSurfaceGetBytesPerRowOfPlane(surface: IOSurfaceRef, plane: usize) -> usize; + + fn IOSurfaceGetHeightOfPlane(surface: IOSurfaceRef, plane: usize) -> usize; + fn IOSurfaceGetWidthOfPlane(surface: IOSurfaceRef, plane: usize) -> usize; + + fn IOSurfaceGetElementWidthOfPlane(surface: IOSurfaceRef, plane: usize) -> usize; + fn IOSurfaceGetBytesPerElementOfPlane(surface: IOSurfaceRef, plane: usize) -> usize; + fn IOSurfaceGetNumberOfComponentsOfPlane(surface: IOSurfaceRef, plane: usize) -> usize; + + static mut _dispatch_queue_attr_concurrent: c_void; + + fn dispatch_queue_create(label: *const std::ffi::c_char, attr: DispatchQueueAttr) -> DispatchQueue; + fn dispatch_retain(object: *mut Object); + fn dispatch_release(object: *mut Object); + + pub(crate) static SCStreamFrameInfoStatus : CFStringRef; + pub(crate) static SCStreamFrameInfoDisplayTime : CFStringRef; + pub(crate) static SCStreamFrameInfoScaleFactor : CFStringRef; + pub(crate) static SCStreamFrameInfoContentScale : CFStringRef; + pub(crate) static SCStreamFrameInfoContentRect : CFStringRef; + pub(crate) static SCStreamFrameInfoBoundingRect : CFStringRef; + pub(crate) static SCStreamFrameInfoScreenRect : CFStringRef; + pub(crate) static SCStreamFrameInfoDirtyRects : CFStringRef; + + pub(crate) static kCGDisplayStreamSourceRect : CFStringRef; + pub(crate) static kCGDisplayStreamDestinationRect : CFStringRef; + pub(crate) static kCGDisplayStreamPreserveAspectRatio : CFStringRef; + pub(crate) static kCGDisplayStreamColorSpace : CFStringRef; + pub(crate) static kCGDisplayStreamMinimumFrameTime : CFStringRef; + pub(crate) static kCGDisplayStreamShowCursor : CFStringRef; + pub(crate) static kCGDisplayStreamQueueDepth : CFStringRef; + pub(crate) static kCGDisplayStreamYCbCrMatrix : CFStringRef; + + static kCGDisplayStreamYCbCrMatrix_ITU_R_709_2 : CFStringRef; + static kCGDisplayStreamYCbCrMatrix_ITU_R_601_4 : CFStringRef; + static kCGDisplayStreamYCbCrMatrix_SMPTE_240M_1995 : CFStringRef; + + static NSDeviceSize: CFStringRef; +} + +const SCSTREAM_ERROR_CODE_USER_STOPPED: isize = -3817; + +pub const kAudioFormatFlagIsFloat : u32 = 1 << 0; +pub const kAudioFormatFlagIsBigEndian : u32 = 1 << 1; +pub const kAudioFormatFlagIsPacked : u32 = 1 << 3; +pub const kAudioFormatFlagIsNonInterleaved : u32 = 1 << 5; +#[cfg(target_endian = "big")] +pub const kAudioFormatNativeEndian : u32 = kAudioFormatFlagIsBigEndian; +#[cfg(target_endian = "little")] +pub const kAudioFormatNativeEndian : u32 = 0; + +pub const kAudioFormatFlagsCanonical : u32 = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatNativeEndian; + +pub const kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment: u32 = 1 << 0; + +pub const kCMSampleBufferError_ArrayTooSmall: i32 = -12737; + +pub const kCFStringEncodingUTF8: u32 = 0x08000100; + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub(crate) struct CGColorRef(CFTypeRef); + +unsafe impl Encode for CGColorRef { + fn encode() -> Encoding { + unsafe { Encoding::from_str("^{CGColor=}") } + } +} + +#[repr(C)] +pub(crate) struct NSString(pub(crate) *mut Object); + +impl NSString { + pub(crate) fn new(s: &str) -> Self { + unsafe { + let bytes = s.as_bytes(); + let instance = CFStringCreateWithBytes(std::ptr::null(), bytes.as_ptr(), bytes.len() as isize, kCFStringEncodingUTF8, false); + NSString(instance as *mut Object) + } + } + + pub(crate) fn from_ref_unretained(r: CFStringRef) -> Self { + unsafe { CFRetain(r); } + Self(r as *mut Object) + } + + pub(crate) fn from_ref_retained(r: CFStringRef) -> Self { + Self(r as *mut Object) + } + + pub(crate) fn from_id_unretained(id: *mut Object) -> Self { + unsafe { + let _: () = msg_send![id, retain]; + Self(id) + } + } + + pub(crate) fn as_string(&self) -> String { + unsafe { + let c_str: *const i8 = msg_send![self.0, UTF8String]; + let len = strlen(c_str); + let bytes = std::slice::from_raw_parts(c_str as *const u8, len); + String::from_utf8_lossy(bytes).into_owned() + } + } +} + +unsafe impl Encode for NSString { + fn encode() -> Encoding { + unsafe { Encoding::from_str("^@\"NSString\"") } + } +} + +#[repr(C)] +pub(crate) struct NSError(*mut Object); +unsafe impl Send for NSError {} + +impl NSError { + pub(crate) fn from_id_unretained(id: *mut Object) -> Self { + unsafe { let _: () = msg_send![id, retain]; } + Self(id) + } + + pub(crate) fn from_id_retained(id: *mut Object) -> Self { + Self(id) + } + + pub(crate) fn code(&self) -> isize { + unsafe { msg_send![self.0, code] } + } + + pub(crate) fn domain(&self) -> String { + unsafe { + let domain_cfstringref: CFStringRef = msg_send![self.0, domain]; + NSString::from_ref_retained(domain_cfstringref).as_string() + } + } + + pub fn description(&self) -> String { + unsafe { NSString::from_id_unretained(msg_send![self.0, localizedDescription]).as_string() } + } + + pub fn reason(&self) -> String { + unsafe { NSString::from_id_unretained(msg_send![self.0, localizedFailureReason]).as_string() } + } + + //pub(crate) fn user_info(&self) -> +} + +unsafe impl Encode for NSError { + fn encode() -> Encoding { + unsafe { + Encoding::from_str("@\"NSError\"") + } + } +} + +impl Drop for NSError { + fn drop(&mut self) { + unsafe { let _: () = msg_send![self.0, release]; }; + } +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub(crate) struct NSArrayRef(*mut Object); + +impl NSArrayRef { + pub(crate) fn is_null(&self) -> bool { + self.0.is_null() + } +} + +unsafe impl Encode for NSArrayRef { + fn encode() -> objc::Encoding { + unsafe { Encoding::from_str("@\"NSArray\"") } + } +} + +#[repr(C)] +pub(crate) struct NSArray(*mut Object); + +impl NSArray { + pub(crate) fn new() -> Self { + unsafe { + let id: *mut Object = msg_send![class!(NSArray), new]; + Self::from_id_retained(id) + } + } + + pub(crate) fn new_mutable() -> Self { + unsafe { + let id: *mut Object = msg_send![class!(NSMutableArray), alloc]; + let id: *mut Object = msg_send![id, init]; + Self::from_id_retained(id) + } + } + + pub(crate) fn from_ref(r: NSArrayRef) -> Self { + Self::from_id_unretained(r.0) + } + + pub(crate) fn from_id_unretained(id: *mut Object) -> Self { + unsafe { let _: () = msg_send![id, retain]; } + Self(id) + } + + pub(crate) fn from_id_retained(id: *mut Object) -> Self { + Self(id) + } + + pub(crate) fn count(&self) -> usize { + unsafe { msg_send![self.0, count] } + } + + pub(crate) fn add_object(&mut self, object: T) { + unsafe { + let _: () = msg_send![self.0, addObject: object]; + } + } + + pub(crate) fn obj_at_index(&self, i: usize) -> T { + unsafe { msg_send![self.0, objectAtIndex: i] } + } +} + +impl Drop for NSArray { + fn drop(&mut self) { + unsafe { let _: () = msg_send![self.0, release]; } + } +} + + +#[repr(C)] +#[derive(Copy, Clone)] +pub(crate) struct NSDictionaryEncoded(*mut Object); + +impl NSDictionaryEncoded { + pub(crate) fn is_null(&self) -> bool { + self.0.is_null() + } +} + +unsafe impl Encode for NSDictionaryEncoded { + fn encode() -> objc::Encoding { + unsafe { Encoding::from_str("@\"NSDictionary\"") } + } +} + + +#[repr(C)] +pub(crate) struct NSDictionary(pub(crate) *mut Object); + +impl NSDictionary { + pub(crate) fn new() -> Self { + unsafe { + let id: *mut Object = msg_send![class!(NSDictionary), new]; + Self::from_id_retained(id) + } + } + + pub(crate) fn new_mutable() -> Self { + unsafe { + let id: *mut Object = msg_send![class!(NSMutableDictionary), new]; + Self::from_id_retained(id) + } + } + + pub(crate) fn from_ref_unretained(r: CGDictionaryRef) -> Self { + Self::from_id_unretained(r as *mut Object) + } + + pub(crate) fn from_encoded(e: NSDictionaryEncoded) -> Self { + Self::from_id_unretained(e.0) + } + + pub(crate) fn from_id_unretained(id: *mut Object) -> Self { + unsafe { let _: () = msg_send![id, retain]; } + Self(id) + } + + pub(crate) fn from_id_retained(id: *mut Object) -> Self { + Self(id) + } + + pub(crate) fn count(&self) -> usize { + unsafe { msg_send![self.0, count] } + } + + pub(crate) fn all_keys(&self) -> NSArray { + unsafe { + let keys: *mut Object = msg_send![self.0, allKeys]; + NSArray::from_id_retained(keys) + } + } + + pub(crate) fn value_for_key(&self, key: CFStringRef) -> *mut Object { + unsafe { + msg_send![self.0, valueForKey: key] + } + } + + pub(crate) fn set_object_for_key(&mut self, object: *mut Object, key: *mut Object) { + unsafe { + let _: () = msg_send![self.0, setObject: object forKey: key]; + } + } +} + +impl Drop for NSDictionary { + fn drop(&mut self) { + unsafe { let _: () = msg_send![self.0, release]; } + } +} + +impl Clone for NSDictionary { + fn clone(&self) -> Self { + Self::from_id_unretained(self.0) + } +} + +type CGFloat = f64; + +#[repr(C)] +#[derive(Copy, Clone, Debug, Default)] +pub(crate) struct CGPoint { + pub(crate) x: CGFloat, + pub(crate) y: CGFloat, +} + +impl CGPoint { + pub(crate) const ZERO: CGPoint = CGPoint { x: 0.0, y: 0.0 }; + pub(crate) const INF: CGPoint = CGPoint { x: std::f64::INFINITY, y: std::f64::INFINITY }; +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, Default)] +pub(crate) struct CGSize { + pub(crate) x: CGFloat, + pub(crate) y: CGFloat, +} + +impl CGSize { + pub(crate) const ZERO: CGSize = CGSize { x: 0.0, y: 0.0 }; +} + +unsafe impl Encode for CGSize { + fn encode() -> Encoding { + unsafe { Encoding::from_str("{CGSize=\"width\"d\"height\"d}") } + } +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, Default)] +pub(crate) struct CGRect { + pub(crate) origin: CGPoint, + pub(crate) size: CGSize, +} + +impl CGRect { + pub(crate) const ZERO: CGRect = CGRect { + origin: CGPoint::ZERO, + size: CGSize::ZERO + }; + + pub(crate) const NULL: CGRect = CGRect { + origin: CGPoint::INF, + size: CGSize::ZERO + }; + + pub(crate) fn contains(&self, p: CGPoint) -> bool { + p.x >= self.origin.x && + p.y >= self.origin.y && + p.x <= (self.origin.x + self.size.x) && + p.y <= (self.origin.y + self.size.y) + } + + pub(crate) fn create_dicitonary_representation(&self) -> NSDictionary { + unsafe { + NSDictionary::from_ref_unretained(CGRectCreateDictionaryRepresentation(*self)) + } + } + + pub(crate) fn create_from_dictionary_representation(dictionary: &NSDictionary) -> Self { + unsafe { + let mut rect = CGRect::default(); + CGRectMakeWithDictionaryRepresentation(dictionary.0 as *const c_void, &mut rect as *mut _); + return rect; + } + } +} + +unsafe impl Encode for CGRect { + fn encode() -> Encoding { + unsafe { Encoding::from_str("{CGRect=\"origin\"{CGPoint=\"x\"d\"y\"d}\"size\"{CGSize=\"width\"d\"height\"d}}") } + } +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, PartialEq)] +pub(crate) struct CGWindowID(pub(crate) u32); + +impl CGWindowID { + pub(crate) fn raw(&self) -> u32 { + self.0 + } +} + +unsafe impl Send for CGWindowID {} + +#[repr(C)] +pub(crate) struct SCWindow(*mut Object); +unsafe impl Send for SCWindow {} + +impl SCWindow { + pub(crate) fn from_id_unretained(id: *mut Object) -> Self { + unsafe { let _: () = msg_send![id, retain]; } + Self(id) + } + + pub(crate) fn from_id_retained(id: *mut Object) -> Self { + Self(id) + } + + pub(crate) fn id(&self) -> CGWindowID { + unsafe { + let id: u32 = msg_send![self.0, windowID]; + CGWindowID(id) + } + } + + pub(crate) fn title(&self) -> String { + unsafe { + let title_cfstringref: CFStringRef = msg_send![self.0, title]; + NSString::from_ref_unretained(title_cfstringref).as_string() + } + } + + pub(crate) fn frame(&self) -> CGRect { + unsafe { + *(*self.0).get_ivar("_frame") + } + } + + pub(crate) fn owning_application(&self) -> SCRunningApplication { + unsafe { + let scra_id: *mut Object = msg_send![self.0, owningApplication]; + SCRunningApplication::from_id_unretained(scra_id) + } + } +} + +unsafe impl Encode for SCWindow { + fn encode() -> Encoding { + unsafe { Encoding::from_str("^\"@SCWindow\"") } + } +} + +impl Clone for SCWindow { + fn clone(&self) -> Self { + unsafe { let _: () = msg_send![self.0, retain]; } + SCWindow(self.0) + } +} + +impl Drop for SCWindow { + fn drop(&mut self) { + unsafe { let _: () = msg_send![self.0, release]; } + } +} + +#[repr(C)] +pub(crate) struct SCDisplay(*mut Object); +unsafe impl Send for SCDisplay {} + +impl SCDisplay { + pub(crate) fn from_id_unretained(id: *mut Object) -> Self { + unsafe { let _: () = msg_send![id, retain]; } + Self(id) + } + + pub(crate) fn from_id_retained(id: *mut Object) -> Self { + Self(id) + } + + pub(crate) fn frame(&self) -> CGRect { + unsafe { + *(*self.0).get_ivar("_frame") + } + } + + pub(crate) fn raw_id(&self) -> u32 { + unsafe { + *(*self.0).get_ivar("_displayID") + } + } +} + +impl Clone for SCDisplay { + fn clone(&self) -> Self { + unsafe { let _: () = msg_send![self.0, retain]; } + SCDisplay(self.0) + } +} + +impl Drop for SCDisplay { + fn drop(&mut self) { + unsafe { let _: () = msg_send![self.0, release]; } + } +} + +#[repr(C)] +pub(crate) struct SCShareableContent(*mut Object); +unsafe impl Send for SCShareableContent {} +unsafe impl Sync for SCShareableContent {} + +impl SCShareableContent { + pub(crate) fn get_shareable_content_with_completion_handler( + excluding_desktop_windows: bool, + onscreen_windows_only: bool, + completion_handler: impl Fn(Result) + Send + 'static, + ) { + let completion_handler = Box::new(completion_handler); + let handler_block = ConcreteBlock::new(move |sc_shareable_content: *mut Object, error: *mut Object| { + if !error.is_null() { + let error = NSError::from_id_retained(error); + (completion_handler)(Err(error)); + } else { + unsafe { let _:() = msg_send![sc_shareable_content, retain]; } + let sc_shareable_content = SCShareableContent(sc_shareable_content); + (completion_handler)(Ok(sc_shareable_content)); + } + }).copy(); + unsafe { + let _: () = msg_send![ + class!(SCShareableContent), + getShareableContentExcludingDesktopWindows: Bool::from_raw(excluding_desktop_windows) + onScreenWindowsOnly: Bool::from_raw(onscreen_windows_only) + completionHandler: handler_block + ]; + } + } + + pub(crate) fn windows(&self) -> Vec { + let mut windows = Vec::new(); + unsafe { + let windows_nsarray_ref: NSArrayRef = *(*self.0).get_ivar("_windows"); + if !windows_nsarray_ref.is_null() { + let windows_ns_array = NSArray::from_ref(windows_nsarray_ref); + let count = windows_ns_array.count(); + for i in 0..count { + let window_id: *mut Object = windows_ns_array.obj_at_index(i); + windows.push(SCWindow::from_id_unretained(window_id)); + } + } + } + windows + } + + pub(crate) fn displays(&self) -> Vec { + let mut displays = Vec::new(); + unsafe { + let displays_ref: NSArrayRef = *(*self.0).get_ivar("_displays"); + if !displays_ref.is_null() { + let displays_ns_array = NSArray::from_ref(displays_ref); + let count = displays_ns_array.count(); + for i in 0..count { + let display_id: *mut Object = displays_ns_array.obj_at_index(i); + displays.push(SCDisplay::from_id_unretained(display_id)); + } + } + } + displays + } + + pub(crate) fn applications(&self) -> Vec { + let mut applications = Vec::new(); + unsafe { + let applicaitons_ref: NSArrayRef = *(*self.0).get_ivar("_applications"); + if !applicaitons_ref.is_null() { + let applications_array = NSArray::from_ref(applicaitons_ref); + let count = applications_array.count(); + for i in 0..count { + let application_id: *mut Object = applications_array.obj_at_index(i); + applications.push(SCRunningApplication::from_id_unretained(application_id)); + } + } + } + applications + } +} + +impl Drop for SCShareableContent { + fn drop(&mut self) { + unsafe { let _: () = msg_send![self.0, release]; } + } +} + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub(crate) struct OSType([u8; 4]); + +impl OSType { + pub fn as_i32(&self) -> i32 { + unsafe { std::mem::transmute(self.0) } + } + + pub fn as_u32(&self) -> u32 { + unsafe { std::mem::transmute(self.0) } + } +} + +unsafe impl Encode for OSType { + fn encode() -> Encoding { + unsafe { Encoding::from_str("I") } + } +} + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub(crate) enum SCStreamPixelFormat { + BGRA8888, + L10R, + V420, + F420, +} + +impl SCStreamPixelFormat { + pub(crate) fn to_ostype(&self) -> OSType { + match self { + Self::BGRA8888 => OSType(['A' as u8, 'R' as u8, 'G' as u8, 'B' as u8]), + Self::L10R => OSType(['r' as u8, '0' as u8, '1' as u8, 'l' as u8]), + Self::V420 => OSType(['v' as u8, '0' as u8, '2' as u8, '4' as u8]), + Self::F420 => OSType(['f' as u8, '0' as u8, '2' as u8, '4' as u8]), + } + } +} + + +#[derive(Copy, Clone, Debug)] +pub(crate) enum SCStreamBackgroundColor { + Black, + White, + Clear, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum SCStreamColorMatrix { + ItuR709_2, + ItuR601_4, + Smpte240M1995, +} + +impl SCStreamColorMatrix { + pub(crate) fn to_cfstringref(&self) -> CFStringRef { + unsafe { + match self { + Self::ItuR709_2 => kCGDisplayStreamYCbCrMatrix_ITU_R_709_2, + Self::ItuR601_4 => kCGDisplayStreamYCbCrMatrix_ITU_R_709_2, + Self::Smpte240M1995 => kCGDisplayStreamYCbCrMatrix_SMPTE_240M_1995, + } + } + } +} + +#[repr(C)] +pub(crate) struct SCStreamConfiguration(pub(crate) *mut Object); +unsafe impl Send for SCStreamConfiguration {} +unsafe impl Sync for SCStreamConfiguration {} + +#[test] +fn test_sc_stream_config_properties() { + debug_objc_class("SCStreamConfiguration"); +} + +impl SCStreamConfiguration { + pub(crate) fn new() -> Self { + unsafe { + let instance: *mut Object = msg_send![class!(SCStreamConfiguration), alloc]; + let instance: *mut Object = msg_send![instance, init]; + Self(instance) + } + } + + pub(crate) fn set_size(&mut self, size: CGSize) { + let dest_rect = CGRect { + size, + origin: CGPoint::ZERO, + }; + unsafe { + let _: () = msg_send![self.0, setDestinationRect: dest_rect]; + } + } + + pub(crate) fn set_source_rect(&mut self, source_rect: CGRect) { + unsafe { + let _: () = msg_send![self.0, setSourceRect: source_rect]; + } + } + + pub(crate) fn set_scales_to_fit(&mut self, scale_to_fit: bool) { + unsafe { + let _: () = msg_send![self.0, setScalesToFit: scale_to_fit]; + } + } + + pub(crate) fn set_pixel_format(&mut self, format: SCStreamPixelFormat) { + unsafe { + let old_pf: OSType = *(*self.0).get_ivar("_pixelFormat"); + println!("old pixel format: {:?}", old_pf); + (*self.0).set_ivar("_pixelFormat", format.to_ostype()); + } + } + + pub(crate) fn set_color_matrix(&mut self, color_matrix: SCStreamColorMatrix) { + unsafe { + let _: () = msg_send![self.0, setColorMatrix: color_matrix.to_cfstringref()]; + } + } + + pub(crate) fn set_background_color(&mut self, bg_color: SCStreamBackgroundColor) { + unsafe { + let bg_color_name = match bg_color { + SCStreamBackgroundColor::Black => kCGColorBlack, + SCStreamBackgroundColor::White => kCGColorWhite, + SCStreamBackgroundColor::Clear => kCGColorClear, + }; + let bg_color = CGColorGetConstantColor(bg_color_name); + (*self.0).set_ivar("_backgroundColor", bg_color); + } + } + + pub(crate) fn set_queue_depth(&mut self, queue_depth: isize) { + unsafe { + let _: () = msg_send![self.0, setQueueDepth: queue_depth]; + } + } + + pub(crate) fn set_minimum_time_interval(&mut self, interval: CMTime) { + unsafe { + (*self.0).set_ivar("_minimumFrameInterval", interval); + } + } + + pub(crate) fn set_sample_rate(&mut self, sample_rate: SCStreamSampleRate) { + unsafe { + (*self.0).set_ivar("_sampleRate", sample_rate.to_isize()); + } + } + + pub(crate) fn set_show_cursor(&mut self, show_cursor: bool) { + unsafe { + (*self.0).set_ivar("_showsCursor", show_cursor); + } + } + + pub(crate) fn set_capture_audio(&mut self, capture_audio: bool) { + unsafe { + (*self.0).set_ivar("_capturesAudio", BOOL::from(capture_audio)); + } + } + + pub(crate) fn set_channel_count(&mut self, channel_count: isize) { + unsafe { + (*self.0).set_ivar("_channelCount", channel_count); + } + } + + pub(crate) fn set_exclude_current_process_audio(&mut self, exclude_current_process_audio: bool) { + unsafe { + (*self.0).set_ivar("_excludesCurrentProcessAudio", exclude_current_process_audio); + } + } +} + +impl Clone for SCStreamConfiguration { + fn clone(&self) -> Self { + unsafe { let _: () = msg_send![self.0, retain]; } + SCStreamConfiguration(self.0) + } +} + +impl Drop for SCStreamConfiguration { + fn drop(&mut self) { + unsafe { let _: () = msg_send![self.0, release]; } + } +} + + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub(crate) struct CMTime { + value: i64, + scale: i32, + epoch: i64, + flags: u32, +} + +pub(crate) const K_CMTIME_FLAGS_VALID : u32 = 1 << 0; +pub(crate) const K_CMTIME_FLAGS_HAS_BEEN_ROUNDED : u32 = 1 << 0; +pub(crate) const K_CMTIME_FLAGS_POSITIVE_INFINITY : u32 = 1 << 0; +pub(crate) const K_CMTIME_FLAGS_NEGATIVE_INFINITY : u32 = 1 << 0; +pub(crate) const K_CMTIME_FLAGS_INDEFINITE : u32 = 1 << 0; +pub(crate) const K_CMTIME_FLAGS_IMPLIED_VALUE_FLAG_MASK : u32 = + K_CMTIME_FLAGS_VALID | + K_CMTIME_FLAGS_HAS_BEEN_ROUNDED | + K_CMTIME_FLAGS_POSITIVE_INFINITY | + K_CMTIME_FLAGS_NEGATIVE_INFINITY | + K_CMTIME_FLAGS_INDEFINITE + ; + +const K_CMTIME_ROUNDING_METHOD_ROUND_HALF_AWAY_FROM_ZERO: u32 = 1; +const K_CMTIME_ROUNDING_METHOD_ROUND_TOWARD_ZERO: u32 = 2; +const K_CMTIME_ROUNDING_METHOD_ROUND_AWAY_FROM_ZERO: u32 = 3; +const K_CMTIME_ROUNDING_METHOD_QUICKTIME: u32 = 4; +const K_CMTIME_ROUNDING_METHOD_TOWARD_POSITIVE_INFINITY: u32 = 5; +const K_CMTIME_ROUNDING_METHOD_TOWARD_NEGATIVE_INFINITY: u32 = 6; + +#[derive(Copy, Clone, Debug)] +pub(crate) enum CMTimeRoundingMethod { + HalfAwayFromZero, + TowardZero, + AwayFromZero, + QuickTime, + TowardPositiveInfinity, + TowardNegativeInfinity, +} + +impl CMTimeRoundingMethod { + pub(crate) fn to_u32(&self) -> u32 { + match self { + Self::HalfAwayFromZero => K_CMTIME_ROUNDING_METHOD_ROUND_HALF_AWAY_FROM_ZERO, + Self::TowardZero => K_CMTIME_ROUNDING_METHOD_ROUND_TOWARD_ZERO, + Self::AwayFromZero => K_CMTIME_ROUNDING_METHOD_ROUND_AWAY_FROM_ZERO, + Self::QuickTime => K_CMTIME_ROUNDING_METHOD_QUICKTIME, + Self::TowardPositiveInfinity => K_CMTIME_ROUNDING_METHOD_TOWARD_POSITIVE_INFINITY, + Self::TowardNegativeInfinity => K_CMTIME_ROUNDING_METHOD_TOWARD_NEGATIVE_INFINITY, + } + } +} + +impl Default for CMTimeRoundingMethod { + fn default() -> Self { + Self::HalfAwayFromZero + } +} + +impl Add for CMTime { + type Output = CMTime; + + fn add(self, rhs: Self) -> Self { + unsafe { CMTimeAdd(self, rhs) } + } +} + +impl Sub for CMTime { + type Output = CMTime; + + fn sub(self, rhs: Self) -> Self { + unsafe { CMTimeSubtract(self, rhs) } + } +} + +impl Mul for CMTime { + type Output = Self; + + fn mul(self, rhs: i32) -> Self { + unsafe { CMTimeMultiply(self, rhs) } + } +} + +impl Mul for CMTime { + type Output = Self; + + fn mul(self, rhs: f64) -> Self { + unsafe { CMTimeMultiplyByFloat64(self, rhs) } + } +} + +impl PartialEq for CMTime { + fn eq(&self, other: &Self) -> bool { + unsafe { + CMTimeCompare(*self, *other) == 0 + } + } +} + +impl PartialOrd for CMTime { + fn partial_cmp(&self, other: &Self) -> Option { + unsafe { + let self_op_other = CMTimeCompare(*self, *other); + match self_op_other { + -1 => Some(std::cmp::Ordering::Less), + 0 => Some(std::cmp::Ordering::Equal), + 1 => Some(std::cmp::Ordering::Greater), + _ => None + } + } + } +} + +unsafe impl Encode for CMTime { + fn encode() -> Encoding { + unsafe { Encoding::from_str("{?=\"value\"q\"timescale\"i\"flags\"I\"epoch\"q}") } + } +} + +impl CMTime { + pub(crate) fn new_with_seconds(seconds: f64, timescale: i32) -> Self { + unsafe { CMTimeMakeWithSeconds(seconds, timescale) } + } + + pub(crate) const fn is_valid(&self) -> bool { + self.flags & K_CMTIME_FLAGS_VALID != 0 + } + + pub(crate) const fn is_invalid(&self) -> bool { + ! self.is_valid() + } + + pub(crate) const fn is_indefinite(&self) -> bool { + self.is_valid() && + (self.flags & K_CMTIME_FLAGS_INDEFINITE != 0) + } + + pub(crate) const fn is_positive_infinity(&self) -> bool { + self.is_valid() && + (self.flags & K_CMTIME_FLAGS_POSITIVE_INFINITY != 0) + } + + pub(crate) const fn is_negative_infinity(&self) -> bool { + self.is_valid() && + (self.flags & K_CMTIME_FLAGS_NEGATIVE_INFINITY != 0) + } + + pub(crate) const fn is_numeric(&self) -> bool { + self.is_valid() && + ! self.is_indefinite() && + ! self.is_positive_infinity() && + ! self.is_negative_infinity() + } + + pub(crate) const fn has_been_rounded(&self) -> bool { + self.flags & K_CMTIME_FLAGS_HAS_BEEN_ROUNDED != 0 + } + + pub(crate) fn convert_timescale(&self, new_timescale: i32, rounding_method: CMTimeRoundingMethod) -> Self { + unsafe { CMTimeConvertScale(*self, new_timescale, rounding_method.to_u32()) } + } + + pub(crate) fn multiply_by_ratio(&self, multiplier: i32, divisor: i32) -> Self { + unsafe { CMTimeMultiplyByRatio(*self, multiplier, divisor) } + } + + pub(crate) fn seconds_f64(&self) -> f64 { + unsafe { CMTimeGetSeconds(*self) } + } +} + +#[derive(Copy, Clone, Debug)] +pub(crate) enum SCStreamSampleRate { + R8000, + R16000, + R24000, + R48000, +} + +impl SCStreamSampleRate { + pub(crate) fn to_isize(&self) -> isize { + match self { + Self::R8000 => 8000, + Self::R16000 => 16000, + Self::R24000 => 24000, + Self::R48000 => 48000, + } + } +} + +#[repr(C)] +pub(crate) struct SCContentFilter(pub(crate) *mut Object); + +unsafe impl Send for SCContentFilter {} +unsafe impl Sync for SCContentFilter {} + +impl SCContentFilter { + pub(crate) fn new_with_desktop_independent_window(window: &SCWindow) -> Self { + unsafe { + let id: *mut Object = msg_send![class!(SCContentFilter), alloc]; + let _: *mut Object = msg_send![id, initWithDesktopIndependentWindow: window.clone()]; + Self(id) + } + } + + pub(crate) fn new_with_display_excluding_apps_excepting_windows(display: SCDisplay, excluded_applications: NSArray, excepting_windows: NSArray) -> Self { + unsafe { + let id: *mut Object = msg_send![class!(SCContentFilter), alloc]; + let id: *mut Object = msg_send![id, initWithDisplay: display.0 excludingApplications: excluded_applications.0 exceptingWindows: excepting_windows.0]; + Self(id) + } + } +} + +unsafe impl Encode for SCContentFilter { + fn encode() -> Encoding { + unsafe { Encoding::from_str("@\"SCContentFilter\"") } + } +} + +impl Clone for SCContentFilter { + fn clone(&self) -> Self { + unsafe { let _: () = msg_send![self.0, retain]; } + SCContentFilter(self.0) + } +} + +impl Drop for SCContentFilter { + fn drop(&mut self) { + unsafe { let _: () = msg_send![self.0, release]; } + } +} + +pub(crate) enum SCStreamCallbackError { + SampleBufferCopyFailed, + StreamStopped, + Other(NSError) +} + +#[repr(C)] +struct SCStreamCallbackContainer { + callback: Box) + Send + 'static> +} + +impl SCStreamCallbackContainer { + pub fn new(callback: impl FnMut(Result<(CMSampleBuffer, SCStreamOutputType), SCStreamCallbackError>) + Send + 'static) -> Self { + Self { + callback: Box::new(callback) + } + } + + pub fn call_output(&mut self, sample_buffer: CMSampleBuffer, output_type: SCStreamOutputType) { + (self.callback)(Ok((sample_buffer, output_type))); + } + + pub fn call_error(&mut self, error: SCStreamCallbackError) { + (self.callback)(Err(error)); + } +} + +#[derive(Copy, Clone, Debug)] +pub enum SCStreamOutputType { + Screen, + Audio, +} + +impl SCStreamOutputType { + fn to_encoded(&self) -> SCStreamOutputTypeEncoded { + SCStreamOutputTypeEncoded(match *self { + Self::Screen => 0, + Self::Audio => 1, + }) + } + + fn from_encoded(x: usize) -> Option { + match x { + 0 => Some(Self::Screen), + 1 => Some(Self::Audio), + _ => None + } + } +} + +#[repr(C)] +struct SCStreamOutputTypeEncoded(usize); + +unsafe impl Encode for SCStreamOutputTypeEncoded { + fn encode() -> Encoding { + unsafe { Encoding::from_str("q") } + } +} + +#[repr(C)] +struct SCStreamEncoded(*mut Object); + +unsafe impl Message for SCStreamEncoded {} + +unsafe impl Encode for SCStreamEncoded { + fn encode() -> Encoding { + unsafe { + Encoding::from_str("@\"SCStream\"") + } + } +} + +extern fn sc_stream_output_did_output_sample_buffer_of_type(this: &Object, _sel: Sel, stream: SCStream, buffer: CMSampleBufferRef, output_type: SCStreamOutputTypeEncoded) { + unsafe { + let callback_container: &mut SCStreamCallbackContainer = &mut *(*this.get_ivar::<*mut c_void>("callback_container_ptr") as *mut SCStreamCallbackContainer); + if let Ok(sample_buffer) = CMSampleBuffer::copy_from_ref(buffer) { + let output_type = SCStreamOutputType::from_encoded(output_type.0).unwrap(); + callback_container.call_output(sample_buffer, output_type); + } else { + callback_container.call_error(SCStreamCallbackError::SampleBufferCopyFailed); + } + std::mem::forget(stream); + } +} + +extern fn sc_stream_handler_did_stop_with_error(this: &Object, _sel: Sel, stream: SCStream, error: NSError) -> () { + unsafe { + let callback_container: &mut SCStreamCallbackContainer = &mut *(*this.get_ivar::<*mut c_void>("callback_container_ptr") as *mut SCStreamCallbackContainer); + callback_container.call_error(SCStreamCallbackError::StreamStopped); + std::mem::forget(error); + std::mem::forget(stream); + } +} + +extern fn sc_stream_handler_dealloc(this: &mut Object, _sel: Sel) { + unsafe { + let callback_container: Box = Box::from_raw(*this.get_ivar::<*mut c_void>("callback_container_ptr") as *mut SCStreamCallbackContainer); + drop(callback_container); + } +} + +#[repr(C)] +pub(crate) struct SCStreamHandler(*mut Object); + +unsafe impl Message for SCStreamHandler {} + +unsafe impl Encode for SCStreamHandler { + fn encode() -> Encoding { + unsafe { Encoding::from_str("@\"\"") } + } +} + +impl SCStreamHandler { + pub fn new(callback: impl FnMut(Result<(CMSampleBuffer, SCStreamOutputType), SCStreamCallbackError>) + Send + 'static) -> Self { + let class = Self::get_class(); + let callback_container_ptr = Box::leak(Box::new(SCStreamCallbackContainer::new(callback))); + unsafe { + let instance: *mut Object = msg_send![class!(SCStreamHandler), alloc]; + let instance: *mut Object = msg_send![instance, init]; + (*instance).set_ivar("callback_container_ptr", callback_container_ptr as *mut _ as *mut c_void); + Self(instance) + } + } + + fn get_class() -> &'static Class { + unsafe { + let class_name = CString::new("SCStreamHandler").unwrap(); + let class_ptr = objc::runtime::objc_getClass(class_name.as_ptr()); + if !class_ptr.is_null() { + &*class_ptr + } else if let Some(mut class) = objc::declare::ClassDecl::new("SCStreamHandler", class!(NSObject)) { + class.add_method(sel!(stream:didOutputSampleBuffer:ofType:), sc_stream_output_did_output_sample_buffer_of_type as extern fn (&Object, Sel, SCStream, CMSampleBufferRef, SCStreamOutputTypeEncoded)); + class.add_method(sel!(stream:didStopWithError:), sc_stream_handler_did_stop_with_error as extern fn(&Object, Sel, SCStream, NSError)); + class.add_method(sel!(dealloc), sc_stream_handler_dealloc as extern fn(&mut Object, Sel)); + + //let sc_stream_delegate_name = CString::new("SCStreamDelegate").unwrap(); + //class.add_protocol(&*objc::runtime::objc_getProtocol(sc_stream_delegate_name.as_ptr())); + + //let sc_stream_output_name = CString::new("SCStreamOutput").unwrap(); + //class.add_protocol(&*objc::runtime::objc_getProtocol(sc_stream_output_name.as_ptr())); + + class.add_ivar::<*mut c_void>("callback_container_ptr"); + + class.register() + } else { + panic!("Failed to register SCStreamHandler"); + } + } + } +} + + +#[repr(C)] +pub struct SCStream(*mut Object); + +unsafe impl Sync for SCStream {} +unsafe impl Send for SCStream {} + +unsafe impl Encode for SCStream { + fn encode() -> Encoding { + unsafe { Encoding::from_str("@\"SCStream\"") } + } +} + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +struct SCStreamDelegate(*mut Object); + +unsafe impl Encode for SCStreamDelegate { + fn encode() -> Encoding { + unsafe { Encoding::from_str("@\"\"") } + } +} + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +struct SCStreamOutput(*mut Object); + +unsafe impl Encode for SCStreamOutput { + fn encode() -> Encoding { + unsafe { Encoding::from_str("@\"\"") } + } +} + +impl SCStream { + pub fn preflight_access() -> bool { + unsafe { CGPreflightScreenCaptureAccess() } + } + + pub async fn request_access() -> bool { + async { + unsafe { CGRequestScreenCaptureAccess() } + }.await + } + + pub fn from_id(id: *mut Object) -> Self { + unsafe { let _: () = msg_send![id, retain]; } + Self(id) + } + + pub fn is_nil(&self) -> bool { + self.0.is_null() + } + + pub fn new(filter: SCContentFilter, config: SCStreamConfiguration, handler_queue: DispatchQueue, handler: SCStreamHandler) -> Result { + unsafe { + let instance: *mut Object = msg_send![class!(SCStream), alloc]; + let instance: *mut Object = msg_send![instance, initWithFilter: filter.0 configuration: config.0 delegate: SCStreamDelegate(handler.0)]; + println!("SCStream instance: {:?}", instance); + let mut error: *mut Object = std::ptr::null_mut(); + let result: bool = msg_send![instance, addStreamOutput: SCStreamOutput(handler.0) type: SCStreamOutputType::Screen.to_encoded() sampleHandlerQueue: handler_queue error: &mut error as *mut _]; + println!("addStreamOutput result: {}", result); + if !error.is_null() { + let error = NSError::from_id_retained(error); + println!("error: {}, reason: {}", error.description(), error.reason()); + } + Ok(SCStream(instance)) + } + } + + pub fn start(&mut self) { + unsafe { + let _: () = msg_send![self.0, startCaptureWithCompletionHandler: ConcreteBlock::new(Box::new( + |error: *mut Object| { + if !error.is_null() { + let error = NSError::from_id_unretained(error); + println!("startCaptureWithCompletionHandler error: {:?}, reason: {:?}", error.description(), error.reason()); + } else { + println!("startCaptureWithCompletionHandler success!"); + } + } + )).copy()]; + } + } + + pub fn stop(&mut self) { + } +} + +#[repr(C)] +pub(crate) struct CMSampleBuffer(CMSampleBufferRef); + +impl CMSampleBuffer { + pub(crate) fn copy_from_ref(r: CMSampleBufferRef) -> Result { + unsafe { + let mut new_ref: CMSampleBufferRef = std::ptr::null(); + let status = CMSampleBufferCreateCopy(kCFAllocatorDefault, r, &mut new_ref as *mut _); + if status != 0 { + Err(()) + } else { + Ok(CMSampleBuffer(new_ref)) + } + } + } + + pub(crate) fn get_presentation_timestamp(&self) -> CMTime { + unsafe { CMSampleBufferGetPresentationTimeStamp(self.0) } + } + + pub(crate) fn get_duration(&self) -> CMTime { + unsafe { CMSampleBufferGetDuration(self.0) } + } + + pub(crate) fn get_format_description(&self) -> CMFormatDescription { + let format_desc_ref = unsafe { CMSampleBufferGetFormatDescription(self.0) }; + CMFormatDescription::from_ref_unretained(format_desc_ref) + } + + // CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer + pub(crate) unsafe fn get_audio_buffer_list_with_block_buffer(&self) -> Result<(AudioBufferList, CMBlockBuffer), ()> { + let mut audio_buffer_list = AudioBufferList::default(); + let mut block_buffer: CMBlockBufferRef = std::ptr::null(); + let status = CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer( + self.0, + std::ptr::null_mut(), + &mut audio_buffer_list as *mut _, + std::mem::size_of::(), + kCFAllocatorNull, + kCFAllocatorNull, + kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, + &mut block_buffer as *mut _ + ); + if status != 0 { + println!("CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(...) failed: {}", status); + return Err(()); + } + Ok((audio_buffer_list, CMBlockBuffer::from_ref_retained(block_buffer))) + } + + pub(crate) fn get_sample_attachment_array(&self) -> Vec { + unsafe { + let attachment_array_ref = CMSampleBufferGetSampleAttachmentsArray(self.0, false.into()); + if attachment_array_ref.is_null() { + return vec![]; + } + let attachments_array = CFArray::from_ref_unretained(attachment_array_ref); + let mut attachments = Vec::new(); + for i in 0..attachments_array.get_count() { + attachments.push(CFDictionary::from_ref_unretained(attachments_array.get_value_at_index(i))); + } + attachments + } + } + + pub(crate) fn get_image_buffer(&self) -> Option { + unsafe { + let buffer_ref = CMSampleBufferGetImageBuffer(self.0); + if buffer_ref.is_null() { + None + } else { + Some(CVPixelBuffer::from_ref_unretained(buffer_ref)) + } + } + } + +} + +impl Clone for CMSampleBuffer { + fn clone(&self) -> Self { + unsafe { CFRetain(self.0); } + Self(self.0) + } +} + +impl Drop for CMSampleBuffer { + fn drop(&mut self) { + unsafe { CFRelease(self.0); } + } +} + +#[repr(C)] +pub(crate) struct CFDictionary(CFTypeRef); + +impl CFDictionary { + pub(crate) fn from_ref_retained(r: CFDictionaryRef) -> Self { + Self(r) + } + + pub(crate) fn from_ref_unretained(r: CFDictionaryRef) -> Self { + unsafe { CFRetain(r); } + Self(r) + } + + pub(crate) fn get_value(&self, key: CFTypeRef) -> CFTypeRef { + unsafe { CFDictionaryGetValue(self.0, key) } + } +} + +impl Clone for CFDictionary { + fn clone(&self) -> Self { + Self::from_ref_unretained(self.0) + } +} + +impl Drop for CFDictionary { + fn drop(&mut self) { + unsafe { CFRelease(self.0); } + } +} + +pub(crate) enum CMMediaType { + Audio, + Video, +} + +impl CMMediaType { + pub(crate) fn from_ostype(ostype: OSType) -> Option { + match ostype.0.map(|x| x as char) { + ['v', 'i', 'd', 'e'] => Some(Self::Video), + ['s', 'o', 'u', 'n'] => Some(Self::Audio), + _ => None, + } + } +} + +#[repr(C)] +pub(crate) struct CMFormatDescription(CMFormatDescriptionRef); + +impl CMFormatDescription { + pub(crate) fn from_ref_retained(r: CMFormatDescriptionRef) -> Self { + Self(r) + } + + pub(crate) fn from_ref_unretained(r: CMFormatDescriptionRef) -> Self { + unsafe { CFRetain(r); } + Self(r) + } + + pub(crate) fn get_media_type(&self) -> OSType { + unsafe { CMFormatDescriptionGetMediaType(self.0) } + } + + pub(crate) fn as_audio_format_description(&self) -> Option { + match CMMediaType::from_ostype(self.get_media_type()) { + Some(CMMediaType::Audio) => { + Some(CMAudioFormatDescription::from_ref_unretained(self.0)) + }, + _ => None + } + } +} + +impl Drop for CMFormatDescription { + fn drop(&mut self) { + unsafe { CFRelease(self.0); } + } +} + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub(crate) struct AudioStreamBasicDescription { + pub sample_rate: f64, + pub format_id: u32, + pub format_flags: u32, + pub bytes_per_packet: u32, + pub frames_per_packet: u32, + pub bytes_per_frame: u32, + pub channels_per_frame: u32, + pub bits_per_channel: u32, + pub reserved: u32, +} + +#[repr(C)] +pub(crate) struct CMAudioFormatDescription(CMFormatDescriptionRef); + +impl CMAudioFormatDescription { + pub(crate) fn from_ref_retained(r: CMFormatDescriptionRef) -> Self { + Self(r) + } + + pub(crate) fn from_ref_unretained(r: CMFormatDescriptionRef) -> Self { + unsafe { CFRetain(r); } + Self(r) + } + + pub(crate) fn get_basic_stream_description(&self) -> &'_ AudioStreamBasicDescription { + unsafe { &*CMAudioFormatDescriptionGetStreamBasicDescription(self.0) as &_ } + } +} + +impl Drop for CMAudioFormatDescription { + fn drop(&mut self) { + unsafe { CFRelease(self.0); } + } +} + +pub(crate) struct AVAudioFormat(*mut Object); + +impl AVAudioFormat { + pub fn new_with_standard_format_sample_rate_channels(sample_rate: f64, channel_count: u32) -> Self { + unsafe { + let id: *mut Object = msg_send![class!(AVAudioFormat), alloc]; + let _: *mut Object = msg_send![id, initStandardFormatWithSampleRate: sample_rate channels: channel_count]; + Self(id) + } + } +} + +impl Drop for AVAudioFormat { + fn drop(&mut self) { + unsafe { let _: () = msg_send![self.0, release]; } + } +} + +#[repr(C)] +pub(crate) struct AudioBuffer { + number_channels: u32, + data_byte_size: u32, + data: *mut c_void, +} + +#[repr(C)] +pub(crate) struct AudioBufferList { + number_buffers: u32, + buffers: *mut AudioBuffer, +} + +impl Default for AudioBufferList { + fn default() -> Self { + Self { + number_buffers: 0, + buffers: std::ptr::null_mut() + } + } +} + +#[repr(C)] +pub(crate) struct CMBlockBuffer(CMBlockBufferRef); + + +impl CMBlockBuffer { + pub(crate) fn from_ref_retained(r: CMBlockBufferRef) -> Self { + Self(r) + } + + pub(crate) fn from_ref_unretained(r: CMBlockBufferRef) -> Self { + unsafe { CFRetain(r); } + Self(r) + } +} + +impl Drop for CMBlockBuffer { + fn drop(&mut self) { + unsafe { CFRelease(self.0); } + } +} + +pub(crate) struct AVAudioPCMBuffer(*mut Object); + +impl AVAudioPCMBuffer { + pub fn new_with_format_buffer_list_no_copy_deallocator(format: AVAudioFormat, buffer_list_no_copy: *const AudioBufferList) -> Result { + unsafe { + let id: *mut Object = msg_send![class!(AVAudioPCMBuffer), alloc]; + let result: *mut Object = msg_send![id, initWithPCMFormat: format bufferListNoCopy: buffer_list_no_copy]; + if result.is_null() { + let _: () = msg_send![id, release]; + Err(()) + } else { + Ok(Self(id)) + } + } + } + + pub fn stride(&self) -> usize { + unsafe { msg_send![self.0, stride] } + } + + pub fn frame_capacity(&self) -> usize { + unsafe { msg_send![self.0, frameCapacity] } + } + + pub fn channel_count(&self) -> usize { + unsafe { msg_send![self.0, stride] } + } + + pub fn f32_buffer(&self, channel: usize) -> Option<*const f32> { + let channel_count = self.stride(); + if channel >= channel_count { + return None; + } + unsafe { + let all_channels_data_ptr: *const *const f32 = msg_send![self.0, floatChannelData]; + if all_channels_data_ptr.is_null() { + return None; + } + let all_channels_data = std::slice::from_raw_parts(all_channels_data_ptr, channel_count); + let channel_data = all_channels_data[channel]; + if channel_data.is_null() { + None + } else { + Some(channel_data) + } + } + } + + pub fn i32_buffer(&self, channel: usize) -> Option<*const i32> { + let channel_count = self.stride(); + if channel >= channel_count { + return None; + } + unsafe { + let all_channels_data_ptr: *const *const i32 = msg_send![self.0, int32ChannelData]; + if all_channels_data_ptr.is_null() { + return None; + } + let all_channels_data = std::slice::from_raw_parts(all_channels_data_ptr, channel_count); + let channel_data = all_channels_data[channel]; + if channel_data.is_null() { + None + } else { + Some(channel_data) + } + } + } + + pub fn i16_buffer(&self, channel: usize) -> Option<*const i16> { + let channel_count = self.stride(); + if channel >= channel_count { + return None; + } + unsafe { + let all_channels_data_ptr: *const *const i16 = msg_send![self.0, int32ChannelData]; + if all_channels_data_ptr.is_null() { + return None; + } + let all_channels_data = std::slice::from_raw_parts(all_channels_data_ptr, channel_count); + let channel_data = all_channels_data[channel]; + if channel_data.is_null() { + None + } else { + Some(channel_data) + } + } + } +} + +#[repr(C)] +struct CFArray(CFArrayRef); + +impl CFArray { + pub(crate) fn from_ref_unretained(r: CFStringRef) -> Self { + unsafe { CFRetain(r); } + Self(r) + } + + pub(crate) fn from_ref_retained(r: CFStringRef) -> Self { + Self(r) + } + + pub(crate) fn get_count(&self) -> i32 { + unsafe { CFArrayGetCount(self.0) } + } + + pub(crate) fn get_value_at_index(&self, index: i32) -> *const c_void { + unsafe { CFArrayGetValueAtIndex(self.0, index) } + } +} + +impl Clone for CFArray { + fn clone(&self) -> Self { + CFArray::from_ref_unretained(self.0) + } +} + +impl Drop for CFArray { + fn drop(&mut self) { + unsafe { CFRelease(self.0); } + } +} + + + + +#[repr(C)] +#[derive(Debug)] +pub struct DispatchQueue(*mut Object); + +impl DispatchQueue { + pub fn make_concurrent(name: String) -> Self { + let cstring_name = CString::new(name.as_str()).unwrap(); + unsafe { dispatch_queue_create(cstring_name.as_ptr(), DispatchQueueAttr(&mut _dispatch_queue_attr_concurrent as *mut c_void)) } + } + + pub fn make_serial(name: String) -> Self { + let cstring_name = CString::new(name.as_str()).unwrap(); + unsafe { dispatch_queue_create(cstring_name.as_ptr(), DispatchQueueAttr(0 as *mut c_void)) } + } + + pub fn make_null() -> Self { + DispatchQueue(std::ptr::null_mut()) + } +} + +impl Drop for DispatchQueue { + fn drop(&mut self) { + if self.0.is_null() { + return; + } + unsafe { dispatch_release(self.0) }; + } +} + +impl Clone for DispatchQueue { + fn clone(&self) -> Self { + if self.0.is_null() { + return Self(std::ptr::null_mut()); + } + unsafe { dispatch_retain(self.0); } + Self(self.0) + } +} + +unsafe impl Encode for DispatchQueue { + fn encode() -> Encoding { + unsafe { Encoding::from_str("@\"NSObject\"") } + } +} + +#[repr(C)] +struct DispatchQueueAttr(*mut c_void); + +pub(crate) struct SCRunningApplication(pub(crate) *mut Object); + +impl SCRunningApplication { + pub(crate) fn from_id_unretained(id: *mut Object) -> Self { + unsafe { let _: () = msg_send![id, retain]; } + Self(id) + } + + pub(crate) fn from_id_retained(id: *mut Object) -> Self { + Self(id) + } + + pub(crate) fn pid(&self) -> i32 { + unsafe { + msg_send![self.0, processID] + } + } + + pub(crate) fn application_name(&self) -> String { + unsafe { + let app_name_cfstringref: CFStringRef = msg_send![self.0, applicationName]; + NSString::from_ref_unretained(app_name_cfstringref).as_string() + } + } + + pub(crate) fn bundle_identifier(&self) -> String { + unsafe { + let bundle_id_cfstringref: CFStringRef = msg_send![self.0, bundleIdentifier]; + NSString::from_ref_unretained(bundle_id_cfstringref).as_string() + } + } +} + +impl Clone for SCRunningApplication { + fn clone(&self) -> Self { + Self::from_id_unretained(self.0) + } +} + +impl Drop for SCRunningApplication { + fn drop(&mut self) { + unsafe { let _: () = msg_send![self.0, release]; } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum CGDisplayStreamFrameStatus { + Complete, + Idle, + Blank, + Stopped, +} + +impl CGDisplayStreamFrameStatus { + pub fn from_i32(x: i32) -> Option { + match x { + 0 => Some(Self::Complete), + 1 => Some(Self::Idle), + 2 => Some(Self::Blank), + 3 => Some(Self::Stopped), + _ => None + } + } +} + +pub(crate) struct CGDisplayStream{ + stream_ref: CGDisplayStreamRef, + callback_block: RcBlock<(i32, u64, IOSurfaceRef, CGDisplayStreamUpdateRef), ()>, +} + +impl CGDisplayStream { + pub fn new(callback: impl Fn(CGDisplayStreamFrameStatus, Duration, IOSurface) + 'static, display_id: u32, size: (usize, usize), pixel_format: SCStreamPixelFormat, options_dict: NSDictionary, dispatch_queue: DispatchQueue) -> Self { + println!("CGDisplayStream::new(..)"); + let absolute_time_start = RefCell::new(None); + let callback_block = ConcreteBlock::new(move |status: i32, display_time: u64, iosurface_ref: IOSurfaceRef, stream_update_ref: CGDisplayStreamUpdateRef| { + println!("CGDisplayStream callback_block"); + if let Some(status) = CGDisplayStreamFrameStatus::from_i32(status) { + let relative_time = if let Some(absolute_time_start) = *absolute_time_start.borrow() { + display_time - absolute_time_start + } else { + *absolute_time_start.borrow_mut() = Some(display_time); + 0 + }; + unsafe { + let mut timebase_info: mach_timebase_info_data_t = Default::default(); + mach_timebase_info(&mut timebase_info as *mut _); + let time_ns = ((relative_time as u128 * timebase_info.numer as u128) / timebase_info.denom as u128); + let time = Duration::from_nanos(time_ns as u64); + let io_surface = IOSurface::from_ref_unretained(iosurface_ref); + (callback)(status, time, io_surface); + } + } + }).copy(); + unsafe { + let pixel_format = pixel_format.to_ostype(); + let display_id = CGMainDisplayID(); + let stream_ref = CGDisplayStreamCreateWithDispatchQueue(display_id, size.0, size.1, pixel_format.as_i32(), std::ptr::null_mut(), dispatch_queue.0, &*callback_block as *const _ as *const c_void); + println!("CGDisplayStreamCreateWithDispatchQueue(display_id: {}, output_width: {}, output_height: {}, pixel_format: {:?}, options_dict: {:?}, dispatch_queue: {:?}, callback_block: {:?}): return value: {:?}", display_id, size.0, size.1, pixel_format, options_dict.0, dispatch_queue.0, &callback_block as *const _, stream_ref); + Self { + stream_ref, + callback_block + } + } + } + + pub fn start(&self) -> Result<(), ()> { + let error_code = unsafe { CGDisplayStreamStart(self.stream_ref) }; + println!("CGDisplayStreamStart({:?}) return value: {:?}", self.stream_ref, error_code); + if error_code == 0 { + Ok(()) + } else { + Err(()) + } + } + + pub fn stop(&self) -> Result<(), ()> { + let error_code = unsafe { CGDisplayStreamStop(self.stream_ref) }; + if error_code == 0 { + Ok(()) + } else { + Err(()) + } + } +} + +impl Clone for CGDisplayStream { + fn clone(&self) -> Self { + unsafe { + CFRetain(self.stream_ref); + } + CGDisplayStream { + stream_ref: self.stream_ref, + callback_block: self.callback_block.clone() + } + } +} + +impl Drop for CGDisplayStream { + fn drop(&mut self) { + unsafe { + CFRelease(self.stream_ref); + } + } +} + + + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum CVPixelFormat { + RGB888, + BGR888, + ARGB8888, + BGRA8888, + ABGR8888, + RGBA8888, + V420, + F420, + Other, +} + +impl CVPixelFormat { + pub(crate) fn from_ostype(ostype: &OSType) -> Option { + unsafe { + let value = std::mem::transmute::<_, u32>(*ostype); + Some(match value { + 0x00000018 => Self::RGB888, + 0x32344247 => Self::BGR888, + 0x00000020 => Self::ARGB8888, + 0x42475241 => Self::BGRA8888, + 0x41424752 => Self::ABGR8888, + 0x52474241 => Self::RGBA8888, + 0x34323076 => Self::V420, + _ => { + return None; + } + }) + } + } +} + +pub(crate) struct IOSurface(pub(crate) IOSurfaceRef); + +const IOSURFACELOCK_READONLY : u32 = 1; +const IOSURFACELOCK_AVOIDSYNC : u32 = 2; + +const SYS_IOKIT : i32 = 0x38 << 26; +const SUB_IOKIT_COMMON : i32 = 0; +const KIO_RETURN_CANNOT_LOCK : i32 = SYS_IOKIT | SUB_IOKIT_COMMON | 0x2cc; + +pub enum IOSurfaceLockError { + CannotLock, + Other +} + +pub struct IOSurfaceLockGaurd(IOSurfaceRef, u32); + +impl IOSurfaceLockGaurd { + pub(crate) fn get_base_address_of_plane(&self, plane: usize) -> Option<*const c_void> { + unsafe { + let ptr = IOSurfaceGetBaseAddressOfPlane(self.0, plane); + if ptr.is_null() { + None + } else { + Some(ptr) + } + } + } + + pub(crate) fn get_base_address(&self) -> Option<*const c_void> { + unsafe { + let ptr = IOSurfaceGetBaseAddress(self.0); + if ptr.is_null() { + None + } else { + Some(ptr) + } + } + } +} + +impl Drop for IOSurfaceLockGaurd { + fn drop(&mut self) { + unsafe { + let mut seed = 0u32; + IOSurfaceUnlock(self.0, self.1, &mut seed as *mut _); + } + } +} + +impl IOSurface { + fn from_ref_unretained(r: IOSurfaceRef) -> Self { + unsafe { IOSurfaceIncrementUseCount(r); } + Self(r) + } + + pub(crate) fn get_pixel_format(&self) -> Option { + unsafe { + let pixel_format_ostype = IOSurfaceGetPixelFormat(self.0); + CVPixelFormat::from_ostype(&pixel_format_ostype) + } + } + + pub(crate) fn get_bytes_per_row(&self) -> usize { + unsafe { + IOSurfaceGetBytesPerRow(self.0) + } + } + + pub(crate) fn get_bytes_per_row_of_plane(&self, plane: usize) -> usize { + unsafe { + IOSurfaceGetBytesPerRowOfPlane(self.0, plane) + } + } + + pub(crate) fn get_width(&self) -> usize { + unsafe { + IOSurfaceGetWidth(self.0) + } + } + + pub(crate) fn get_height(&self) -> usize { + unsafe { + IOSurfaceGetHeight(self.0) + } + } + + pub(crate) fn get_height_of_plane(&self, plane: usize) -> usize { + unsafe { + IOSurfaceGetHeightOfPlane(self.0, plane) + } + } + + pub(crate) fn get_width_of_plane(&self, plane: usize) -> usize { + unsafe { + IOSurfaceGetWidthOfPlane(self.0, plane) + } + } + + pub(crate) fn lock(&self, read_only: bool, avoid_sync: bool) -> Result { + unsafe { + let options = + if read_only { IOSURFACELOCK_READONLY } else { 0 } | + if avoid_sync { IOSURFACELOCK_AVOIDSYNC } else { 0 } + ; + match IOSurfaceLock(self.0, options, std::ptr::null_mut()) { + 0 => Ok(IOSurfaceLockGaurd(self.0, options)), + KIO_RETURN_CANNOT_LOCK => Err(IOSurfaceLockError::CannotLock), + _ => Err(IOSurfaceLockError::Other) + } + } + } +} + +impl Clone for IOSurface { + fn clone(&self) -> Self { + Self::from_ref_unretained(self.0) + } +} + +impl Drop for IOSurface { + fn drop(&mut self) { + unsafe { + IOSurfaceDecrementUseCount(self.0); + } + } +} + +/* +kCFNumberSInt8Type = 1, +kCFNumberSInt16Type = 2, +kCFNumberSInt32Type = 3, +kCFNumberSInt64Type = 4, +kCFNumberFloat32Type = 5, +kCFNumberFloat64Type = 6, /* 64-bit IEEE 754 */ +/* Basic C types */ +kCFNumberCharType = 7, +kCFNumberShortType = 8, +kCFNumberIntType = 9, +kCFNumberLongType = 10, +kCFNumberLongLongType = 11, +kCFNumberFloatType = 12, +kCFNumberDoubleType = 13, +/* Other */ +kCFNumberCFIndexType = 14, +*/ + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum CFNumberType { + I8, + I16, + I32, + I64, + F32, + F64, + Char, + Short, + Int, + Long, + LongLong, + Float, + Double, + CFIndex, + NSInteger, + CGFloat, +} + +impl CFNumberType { + pub(crate) fn to_isize(self) -> isize { + match self { + Self::I8 => 1, + Self::I16 => 2, + Self::I32 => 3, + Self::I64 => 4, + Self::F32 => 5, + Self::F64 => 6, + Self::Char => 7, + Self::Short => 8, + Self::Int => 9, + Self::Long => 10, + Self::LongLong => 11, + Self::Float => 12, + Self::Double => 13, + Self::CFIndex => 14, + Self::NSInteger => 15, + Self::CGFloat => 16, + } + } + + pub(crate) fn from_i32(x: i32) -> Option { + Some(match x { + 1 => Self::I8, + 2 => Self::I16, + 3 => Self::I32, + 4 => Self::I64, + 5 => Self::F32, + 6 => Self::F64, + 7 => Self::Char, + 8 => Self::Short, + 9 => Self::Int, + 10 => Self::Long, + 11 => Self::LongLong, + 12 => Self::Float, + 13 => Self::Double, + 14 => Self::CFIndex, + 15 => Self::NSInteger, + 16 => Self::CGFloat, + _ => return None, + }) + } +} + +#[repr(C)] +pub(crate) struct CFNumber(pub(crate) CFNumberRef); + +impl CFNumber { + pub fn new_f32(x: f32) -> Self { + unsafe { + let r = CFNumberCreate(kCFAllocatorNull, CFNumberType::F32.to_isize(), &x as *const f32 as *const c_void); + Self(r) + } + } + + pub fn new_i32(x: i32) -> Self { + unsafe { + let r = CFNumberCreate(kCFAllocatorNull, CFNumberType::I32.to_isize(), &x as *const i32 as *const c_void); + Self(r) + } + } +} + +impl Clone for CFNumber { + fn clone(&self) -> Self { + unsafe { CFRetain(self.0); } + CFNumber(self.0) + } +} + +impl Drop for CFNumber { + fn drop(&mut self) { + unsafe { + CFRelease(self.0); + } + } +} + +pub struct NSNumber(pub(crate) *mut Object); + +impl NSNumber { + pub(crate) fn from_id_unretained(id: *mut Object) -> Self { + unsafe { let _: () = msg_send![id, retain]; } + Self(id) + } + + pub(crate) fn from_id_retained(id: *mut Object) -> Self { + Self(id) + } + + pub(crate) fn new_isize(x: isize) -> Self { + unsafe { + let id: *mut Object = msg_send![class!(NSNumber), numberWithInteger: x]; + Self(id) + } + } + + pub(crate) fn new_f32(x: f32) -> Self { + unsafe { + let id: *mut Object = msg_send![class!(NSNumber), numberWithFloat: x]; + Self(id) + } + } + + pub(crate) fn as_i32(&self) -> i32 { + unsafe { + msg_send![self.0, intValue] + } + } + + pub(crate) fn as_f64(&self) -> f64 { + unsafe { + msg_send![self.0, doubleValue] + } + } + +} + +impl Clone for NSNumber { + fn clone(&self) -> Self { + Self::from_id_unretained(self.0) + } +} + +impl Drop for NSNumber { + fn drop(&mut self) { + unsafe { let _: () = msg_send![self.0, release]; } + } +} + +pub struct NSApplication(*mut Object); + +impl NSApplication { + fn from_id_unretained(id: *mut Object) -> Self { + unsafe { let _: () = msg_send![id, retain]; } + Self(id) + } + + fn from_id_retained(id: *mut Object) -> Self { + Self(id) + } + + pub fn shared() -> Self { + unsafe { + let id: *mut Object = msg_send![class!(NSApplication), sharedApplication]; + Self::from_id_unretained(id) + } + } + + pub fn run(&self) { + unsafe { + let _: () = msg_send![self.0, run]; + } + } +} + +impl Clone for NSApplication { + fn clone(&self) -> Self { + Self::from_id_unretained(self.0) + } +} + +impl Drop for NSApplication { + fn drop(&mut self) { + unsafe { let _: () = msg_send![self.0, release]; } + } +} + +pub enum SCFrameStatus { + Complete, + Idle, + Blank, + Started, + Suspended, + Stopped, +} + +impl SCFrameStatus { + pub fn from_i32(x: i32) -> Option { + match x { + 0 => Some(Self::Complete), + 1 => Some(Self::Idle), + 2 => Some(Self::Blank), + 3 => Some(Self::Suspended), + 4 => Some(Self::Started), + 5 => Some(Self::Stopped), + _ => None, + } + } +} + +pub struct CVPixelBuffer(CVPixelBufferRef); + +impl CVPixelBuffer { + pub fn from_ref_unretained(r: CVPixelBufferRef) -> Self { + unsafe { CFRetain(r); } + Self(r) + } + + pub fn from_ref_retained(r: CVPixelBufferRef) -> Self { + Self(r) + } + + pub fn get_iosurface_ptr(&self) -> Option<*const c_void> { + unsafe { + let iosurface_ptr = CVPixelBufferGetIOSurface(self.0); + if iosurface_ptr.is_null() { + None + } else { + Some(iosurface_ptr) + } + } + } + + pub fn get_iosurface(&self) -> Option { + unsafe { + let iosurface_ptr = CVPixelBufferGetIOSurface(self.0); + if iosurface_ptr.is_null() { + None + } else { + Some(IOSurface::from_ref_unretained(iosurface_ptr)) + } + } + } + + pub fn get_width(&self) -> usize { + unsafe { + CVPixelBufferGetWidth(self.0) + } + } + + pub fn get_height(&self) -> usize { + unsafe { + CVPixelBufferGetHeight(self.0) + } + } +} + +impl Clone for CVPixelBuffer { + fn clone(&self) -> Self { + Self::from_ref_unretained(self.0) + } +} + +impl Drop for CVPixelBuffer { + fn drop(&mut self) { + unsafe { CFRelease(self.0); } + } +} + + +#[repr(C)] +struct NSValue(*mut Object); + +#[allow(unused)] +impl NSValue { + fn from_id(id: *mut Object) -> Self { + Self(id) + } + + fn size_value(&self) -> CGSize { + unsafe { + msg_send![self.0, sizeValue] + } + } + + fn unsigned_int_value(&self) -> u32 { + unsafe { + msg_send![self.0, unsignedIntValue] + } + } +} + +impl Drop for NSValue { + fn drop(&mut self) { + unsafe { let _: () = msg_send![self.0, release]; } + } +} + +#[repr(C)] +pub struct NSScreen(*mut Object); + +impl NSScreen { + pub(crate) fn screens() -> Vec { + unsafe { + let screens_ns_array = NSArray::from_id_retained(msg_send![class!(NSScreen), screens]); + let mut screens_out = Vec::new(); + for i in 0..screens_ns_array.count() { + let screen: *mut Object = screens_ns_array.obj_at_index(i); + screens_out.push(NSScreen(screen)); + } + return screens_out; + } + } + + fn device_description(&self) -> NSDictionary { + unsafe { NSDictionary::from_id_retained(msg_send![self.0, deviceDescription]) } + } + + pub(crate) fn dpi(&self) -> f64 { + let ns_screen_number_string = NSString::new("NSScreenNumber"); + let device_description = self.device_description(); + let pixel_size_value = NSValue(unsafe { device_description.value_for_key(NSDeviceSize) }); + if pixel_size_value.0.is_null() { + std::mem::forget(pixel_size_value); + return 72.0; + } + let pixel_size = pixel_size_value.size_value(); + let screen_number_ptr = device_description.value_for_key(ns_screen_number_string.0 as CFStringRef); + if screen_number_ptr.is_null() { + return 72.0; + } + let screen_number_num = NSNumber::from_id_unretained(screen_number_ptr); + let screen_number = screen_number_num.as_i32() as u32; + let physical_size = unsafe { CGDisplayScreenSize(screen_number) }; + let mut backing_scale_factor: f32 = unsafe { msg_send![self.0, backingScaleFactor] }; + if backing_scale_factor == 0.0 { + backing_scale_factor = 1.0; + } + std::mem::forget(pixel_size_value); + std::mem::forget(screen_number_num); + std::mem::forget(device_description); + (pixel_size.x / physical_size.x) * 25.4 * backing_scale_factor as f64 + } + + pub(crate) fn frame(&self) -> CGRect { + unsafe { msg_send![self.0, frame] } + } +} diff --git a/src/platform/mod.rs b/src/platform/mod.rs new file mode 100644 index 00000000..7a530630 --- /dev/null +++ b/src/platform/mod.rs @@ -0,0 +1,13 @@ +#[cfg(target_os = "macos")] +pub mod macos; + +#[cfg(target_os = "macos")] +pub use macos as platform_impl; + +#[cfg(target_os = "windows")] +pub mod windows; + +#[cfg(target_os = "windows")] +pub use windows as platform_impl; + + diff --git a/src/platform/windows/audio_capture_stream.rs b/src/platform/windows/audio_capture_stream.rs new file mode 100644 index 00000000..d3525e83 --- /dev/null +++ b/src/platform/windows/audio_capture_stream.rs @@ -0,0 +1,176 @@ +use std::{ffi::c_void, sync::{atomic::{self, AtomicBool}, Arc}, time::Duration}; + +use windows::{core::Interface, Win32::{Media::Audio::{eConsole, eRender, IAudioCaptureClient, IAudioClient, IMMDeviceEnumerator, MMDeviceEnumerator, AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, WAVEFORMATEX, WAVE_FORMAT_PCM}, System::Com::{CoCreateInstance, CoInitializeEx, CoUninitialize, CLSCTX_ALL, COINIT_MULTITHREADED}}}; + +use crate::prelude::{AudioCaptureConfig, AudioChannelCount, AudioSampleRate}; + +use super::capture_stream::SharedHandlerData; + +pub struct WindowsAudioCaptureStream { + should_couninit: bool, + audio_client: IAudioClient, +} + +pub enum WindowsAudioCaptureStreamCreateError { + Other(String), + EndpointEnumerationFailed, + AudioClientActivationFailed, + AudioClientInitializeFailed, + AudioCaptureCreationFailed, + StreamStartFailed, +} + +pub enum WindowsAudioCaptureStreamError { + Other(String), + GetBufferFailed, +} + +pub struct WindowsAudioCaptureStreamPacket<'a> { + pub(crate) data: &'a [i16], + pub(crate) channel_count: u32, + pub(crate) origin_time: Duration, + pub(crate) duration: Duration, + pub(crate) sample_index: u64, +} + +struct SendCaptureClient(*mut c_void); + +unsafe impl Send for SendCaptureClient {} +unsafe impl Sync for SendCaptureClient {} + +impl SendCaptureClient { + fn from_iaudiocaptureclient(client: IAudioCaptureClient) -> Self { + SendCaptureClient(client.into_raw()) + } + + fn into_iaudiocaptureclient(self) -> IAudioCaptureClient { + unsafe { IAudioCaptureClient::from_raw(self.0) } + } +} + +impl WindowsAudioCaptureStream { + pub fn new(config: AudioCaptureConfig, mut callback: Box FnMut(Result, WindowsAudioCaptureStreamError>) + Send + 'static>) -> Result { + unsafe { + let should_couninit = CoInitializeEx(None, COINIT_MULTITHREADED).is_ok(); + + let mm_device_enumerator: IMMDeviceEnumerator = CoCreateInstance(&MMDeviceEnumerator, None, CLSCTX_ALL) + .map_err(|e| WindowsAudioCaptureStreamCreateError::Other(format!("Failed to create MMDeviceEnumerator: {}", e.to_string())))?; + let device = mm_device_enumerator.GetDefaultAudioEndpoint(eRender, eConsole) + .map_err(|_| WindowsAudioCaptureStreamCreateError::EndpointEnumerationFailed)?; + + let audio_client: IAudioClient = device.Activate(CLSCTX_ALL, None) + .map_err(|e| WindowsAudioCaptureStreamCreateError::AudioClientActivationFailed)?; + + let mut format = WAVEFORMATEX::default(); + format.wFormatTag = WAVE_FORMAT_PCM as u16; + format.nSamplesPerSec = match config.sample_rate { + AudioSampleRate::Hz8000 => 8000, + AudioSampleRate::Hz16000 => 16000, + AudioSampleRate::Hz24000 => 24000, + AudioSampleRate::Hz48000 => 48000, + }; + format.wBitsPerSample = 16; + format.nChannels = match config.channel_count { + AudioChannelCount::Mono => 1, + AudioChannelCount::Stereo => 2, + }; + format.nBlockAlign = format.nChannels * 2; + format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign as u32; + format.cbSize = 0; + + let callback_format = format.clone(); + + let buffer_size = 512; + let buffer_time = buffer_size as i64 * 10000000i64 / format.nSamplesPerSec as i64; + + let buffer_duration = Duration::from_nanos(buffer_time as u64 * 100); + let half_buffer_duration = buffer_duration / 2; + + audio_client.Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, buffer_time, buffer_time, &format as *const _, None) + .map_err(|_| WindowsAudioCaptureStreamCreateError::AudioClientInitializeFailed)?; + + let capture_client : IAudioCaptureClient = audio_client.GetService() + .map_err(|_| WindowsAudioCaptureStreamCreateError::AudioCaptureCreationFailed)?; + + let capture_client_send = SendCaptureClient::from_iaudiocaptureclient(capture_client); + + std::thread::spawn(move || { + unsafe { + let should_couninit = CoInitializeEx(None, COINIT_MULTITHREADED).is_ok(); + + let mut last_device_position = 0u64; + let mut sample_count = 0u64; + + let capture_client = capture_client_send.into_iaudiocaptureclient(); + loop { + std::thread::sleep(half_buffer_duration); + + let buffered_count = match capture_client.GetNextPacketSize() { + Ok(count) => count, + Err(_) => { + (callback)(Err(WindowsAudioCaptureStreamError::Other(format!("Stream failed - couldn't fetch packet size")))); + break; + } + }; + + let mut data_ptr: *mut u8 = std::ptr::null_mut(); + + let mut num_frames = 0u32; + let mut flags = 0u32; + let mut device_position = 0u64; + + match capture_client.GetBuffer(&mut data_ptr as *mut _, &mut num_frames as *mut _, &mut flags as *mut _, Some(&mut device_position as *mut _), None) { + Ok(_) => { + let packet = WindowsAudioCaptureStreamPacket { + data: std::slice::from_raw_parts(data_ptr as *const i16, num_frames as usize * 2), + channel_count: callback_format.nChannels as u32, + origin_time: Duration::from_nanos(device_position as u64 * 100), + duration: Duration::from_nanos((device_position - last_device_position) as u64), + sample_index: sample_count + }; + (callback)(Ok(packet)); + let _ = capture_client.ReleaseBuffer(num_frames); + last_device_position = device_position; + sample_count += num_frames as u64; + }, + Err(_) => { + (callback)(Err(WindowsAudioCaptureStreamError::GetBufferFailed)); + break; + } + } + + } + + if should_couninit { + CoUninitialize(); + } + } + }); + + audio_client.Start() + .map_err(|_| WindowsAudioCaptureStreamCreateError::StreamStartFailed)?; + + Ok(WindowsAudioCaptureStream { + should_couninit, + audio_client + }) + } + } + + pub fn stop(&mut self) { + unsafe { + let _ = self.audio_client.Stop(); + } + } +} + +impl Drop for WindowsAudioCaptureStream { + fn drop(&mut self) { + unsafe { + let _ = self.audio_client.Stop(); + if self.should_couninit { + CoUninitialize(); + } + } + } +} diff --git a/src/platform/windows/capturable_content.rs b/src/platform/windows/capturable_content.rs new file mode 100644 index 00000000..048b8b5d --- /dev/null +++ b/src/platform/windows/capturable_content.rs @@ -0,0 +1,179 @@ +use std::{ffi::OsString, os::{raw::c_void, windows::ffi::OsStringExt}}; + +use windows::Win32::{Foundation::{BOOL, FALSE, HANDLE, HWND, LPARAM, RECT, TRUE}, Graphics::Gdi::{EnumDisplayMonitors, GetMonitorInfoA, HDC, HMONITOR, MONITORINFO}, System::{ProcessStatus::GetModuleFileNameExW, Threading::{GetProcessId, OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_VM_READ}}, UI::WindowsAndMessaging::{EnumWindows, GetWindowRect, GetWindowTextA, GetWindowTextLengthA, GetWindowThreadProcessId, IsWindow, IsWindowVisible}}; + +use crate::{prelude::{CapturableContentError, CapturableContentFilter}, util::{Point, Rect, Size}}; + +use super::AutoHandle; + +#[derive(Debug, Clone)] +pub struct WindowsCapturableWindow(pub(crate) HWND); + +fn hwnd_process(hwnd: HWND) -> HANDLE { + unsafe { + let mut pid = 0u32; + GetWindowThreadProcessId(hwnd, Some(&mut pid as *mut _)); + OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid).unwrap() + } +} + +impl WindowsCapturableWindow { + pub fn from_impl(hwnd: HWND) -> Self { + Self(hwnd) + } + + pub fn title(&self) -> String { + unsafe { + let text_length = GetWindowTextLengthA(self.0); + if text_length == 0 { + return "".into(); + } + let mut text_buffer = vec![0u8; text_length as usize + 1]; + let text_length = GetWindowTextA(self.0, &mut text_buffer[..]); + if (text_length as usize) < text_buffer.len() { + text_buffer.truncate(text_length as usize); + } + String::from_utf8_lossy(&text_buffer).to_string() + } + } + + pub fn rect(&self) -> Rect { + unsafe { + let mut rect = RECT::default(); + let _ = GetWindowRect(self.0, &mut rect); + Rect { + origin: Point { + x: rect.left as f64, + y: rect.top as f64 + }, + size: Size { + width: (rect.right - rect.left) as f64, + height: (rect.bottom - rect.top) as f64, + } + } + } + } + + pub fn application(&self) -> WindowsCapturableApplication { + WindowsCapturableApplication(hwnd_process(self.0)) + } +} + +#[derive(Clone, Debug)] +pub struct WindowsCapturableDisplay(pub(crate) HMONITOR, pub(crate) RECT); + +impl WindowsCapturableDisplay { + pub fn from_impl(monitor: (HMONITOR, RECT)) -> Self { + Self(monitor.0, monitor.1) + } + + pub fn rect(&self) -> Rect { + Rect { + origin: Point { + x: self.1.left as f64, + y: self.1.top as f64 + }, + size: Size { + width: (self.1.right - self.1.left) as f64, + height: (self.1.bottom - self.1.top) as f64 + } + } + } +} + +#[derive(Clone, Debug)] +pub struct WindowsCapturableApplication(pub(crate) HANDLE); + +impl WindowsCapturableApplication { + pub fn from_impl(handle: HANDLE) -> Self { + Self(handle) + } + + pub fn identifier(&self) -> String { + unsafe { + let pid = GetProcessId(self.0); + let process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid); + if process.is_err() { + return "".into(); + } + let process = process.unwrap(); + // TODO: If OpenProcess fails we could fall back to GetProcessHandleFromHwnd, in oleacc.dll + // Alternatively, it might be better to use the accessibility APIs. + let process = AutoHandle(process); + let mut process_name = vec![0u16; 64]; + let mut len = GetModuleFileNameExW (process.0, None, process_name.as_mut_slice()) as usize; + while len == process_name.len() - 1 { + process_name = vec![0u16; process_name.len() * 2]; + len = GetModuleFileNameExW (process.0, None, process_name.as_mut_slice()) as usize; + } + + if len == 0 { + return "".into(); + } + + let os_string = OsString::from_wide(&process_name[..len as usize]); + let path = std::path::Path::new(&os_string); + let file_name = path.file_name(); + + if let Some(file_name) = file_name { + if let Some(name_str) = file_name.to_str() { + return name_str.to_string() + } + } + + let result = String::from_utf16(&process_name[..len as usize]); + result.unwrap_or("".into()) + } + } +} + +pub struct WindowsCapturableContent { + pub(crate) windows: Vec, + pub(crate) displays: Vec<(HMONITOR, RECT)>, + pub(crate) applications: Vec, +} + +unsafe extern "system" fn enum_windows_callback(window: HWND, windows_ptr_raw: LPARAM) -> BOOL { + let windows: &mut Vec = &mut *(windows_ptr_raw.0 as *mut c_void as *mut _); + windows.push(window); + TRUE +} + +unsafe extern "system" fn enum_monitors_callback(monitor: HMONITOR, _: HDC, rect: *mut RECT, monitors_ptr_raw: LPARAM) -> BOOL { + let monitors: &mut Vec<(HMONITOR, RECT)> = &mut *(monitors_ptr_raw.0 as *mut c_void as *mut _); + monitors.push((monitor, *rect)); + TRUE +} + +impl WindowsCapturableContent { + pub async fn new(filter: CapturableContentFilter) -> Result { + let mut displays = Vec::<(HMONITOR, RECT)>::new(); + let mut windows = Vec::::new(); + unsafe { + if filter.displays { + EnumDisplayMonitors(HDC(0), None, Some(enum_monitors_callback), LPARAM(&mut displays as *mut _ as *mut c_void as isize)); + } + if let Some(window_filter) = filter.windows { + let _ = EnumWindows(Some(enum_windows_callback), LPARAM(&mut windows as *mut _ as *mut c_void as isize)); + windows = windows.iter().filter(|hwnd| { + if !IsWindow(**hwnd).as_bool() { + return false; + } + if window_filter.onscreen_only && !IsWindowVisible(**hwnd).as_bool() { + return false; + } + // TODO: filter desktop windows + true + }).map(|hwnd| *hwnd).collect(); + } + } + let applications = windows.iter().map(|hwnd| { + hwnd_process(*hwnd) + }).collect(); + Ok(WindowsCapturableContent { + windows, + displays, + applications, + }) + } +} \ No newline at end of file diff --git a/src/platform/windows/capture_stream.rs b/src/platform/windows/capture_stream.rs new file mode 100644 index 00000000..172d77dc --- /dev/null +++ b/src/platform/windows/capture_stream.rs @@ -0,0 +1,385 @@ +use std::{sync::{atomic::{self, AtomicBool, AtomicU64}, Arc}, time::{Duration, Instant}}; + +use crate::{prelude::{AudioChannelCount, AudioFrame, Capturable, CaptureConfig, CapturePixelFormat, StreamCreateError, StreamError, StreamEvent, StreamStopError, VideoFrame}, util::Rect}; + +use parking_lot::Mutex; +use windows::{core::{ComInterface, IInspectable, HSTRING}, Foundation::TypedEventHandler, Graphics::{Capture::{Direct3D11CaptureFramePool, GraphicsCaptureAccess, GraphicsCaptureAccessKind, GraphicsCaptureItem, GraphicsCaptureSession}, DirectX::{Direct3D11::IDirect3DDevice, DirectXPixelFormat}, SizeInt32}, Security::Authorization::AppCapabilityAccess::{AppCapability, AppCapabilityAccessStatus}, Win32::{Graphics::{Direct3D::{D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_UNKNOWN, D3D_FEATURE_LEVEL_11_0}, Direct3D11::{D3D11CreateDevice, ID3D11Device, D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_SDK_VERSION}, Dxgi::{CreateDXGIFactory, IDXGIAdapter, IDXGIDevice, IDXGIFactory}}, System::{Com::{CoInitializeEx, CoUninitialize, COINIT_MULTITHREADED}, WinRT::{Direct3D11::CreateDirect3D11DeviceFromDXGIDevice, Graphics::Capture::IGraphicsCaptureItemInterop}}, UI::HiDpi::{GetDpiForMonitor, GetDpiForWindow, MDT_RAW_DPI}}}; + +use super::{audio_capture_stream::{WindowsAudioCaptureStream, WindowsAudioCaptureStreamError, WindowsAudioCaptureStreamPacket}, frame::WindowsVideoFrame, frame::WindowsAudioFrame}; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum WindowsPixelFormat { + Bgra8888, +} + +#[derive(Clone, Debug)] +pub struct WindowsAudioCaptureConfig {} + +impl WindowsAudioCaptureConfig { + pub fn new() -> Self { + Self { + } + } +} + +pub trait WindowsAudioCaptureConfigExt { + +} + +impl WindowsAudioCaptureConfigExt for CaptureConfig { + +} + +#[derive(Clone, Debug)] +pub struct WindowsCaptureConfig { + dxgi_adapter: Option, + d3d11_device: Option, +} + +impl WindowsCaptureConfig { + pub fn new() -> Self { + Self { + dxgi_adapter: None, + d3d11_device: None, + } + } +} + +pub trait WindowsCaptureConfigExt { + fn with_dxgi_adapter(self, dxgi_adapter: IDXGIAdapter) -> Self; + fn with_d3d11_device(self, d3d11_device: ID3D11Device) -> Self; +} + +impl WindowsCaptureConfigExt for CaptureConfig { + fn with_dxgi_adapter(self, dxgi_adapter: IDXGIAdapter) -> Self { + Self { + impl_capture_config: WindowsCaptureConfig { + dxgi_adapter: Some(dxgi_adapter), + ..self.impl_capture_config + }, + ..self + } + } + + fn with_d3d11_device(self, d3d11_device: ID3D11Device) -> Self { + Self { + impl_capture_config: WindowsCaptureConfig { + d3d11_device: Some(d3d11_device), + ..self.impl_capture_config + }, + ..self + } + } +} + +pub struct WindowsCaptureStream { + pub(crate) dxgi_adapter: IDXGIAdapter, + pub(crate) dxgi_device: IDXGIDevice, + pub(crate) d3d11_device: ID3D11Device, + pub(crate) frame_pool: Direct3D11CaptureFramePool, + pub(crate) capture_session: GraphicsCaptureSession, + should_couninit: bool, + shared_handler_data: Arc, + audio_stream: Option, +} + +pub(crate) struct SharedHandlerData { + callback: Mutex) + Send + 'static>>, + closed: AtomicBool, + frame_id_counter: AtomicU64, + audio_frame_id_counter: AtomicU64, +} + +impl WindowsCaptureStream { + pub fn supported_pixel_formats() -> &'static [CapturePixelFormat] { + &[ + CapturePixelFormat::Bgra8888, + CapturePixelFormat::Argb2101010, + ] + } + + pub fn check_access(borderless: bool) -> bool { + let graphics_capture_capability = HSTRING::from("graphicsCaptureProgrammatic"); + let programmatic_access = AppCapability::Create(&graphics_capture_capability).map(|capability| { + match capability.CheckAccess() { + Ok(AppCapabilityAccessStatus::Allowed) => true, + _ => false, + } + }).unwrap_or(true); + let borderless_graphics_capture_capability = HSTRING::from("graphicsCaptureWithoutBorder"); + let borderless_access = AppCapability::Create(&borderless_graphics_capture_capability).map(|capability| { + match capability.CheckAccess() { + Ok(AppCapabilityAccessStatus::Allowed) => true, + _ => false, + } + }).unwrap_or(true); + programmatic_access && (!borderless || borderless_access) + } + + pub async fn request_access(borderless: bool) -> bool { + let access_kind = if borderless { + GraphicsCaptureAccessKind::Borderless + } else { + GraphicsCaptureAccessKind::Programmatic + }; + match GraphicsCaptureAccess::RequestAccessAsync(access_kind) { + Ok(access_future) => match access_future.await { + Ok(AppCapabilityAccessStatus::Allowed) => true, + _ => false + }, + _ => false, + } + } + + fn create_d3d11_device(dxgi_adapter: IDXGIAdapter) -> Result<(IDXGIAdapter, ID3D11Device), StreamCreateError> { + unsafe { + let mut d3d11_device = None; + let d3d11_device_result = D3D11CreateDevice( + Some(&dxgi_adapter), + D3D_DRIVER_TYPE_UNKNOWN, + None, + D3D11_CREATE_DEVICE_BGRA_SUPPORT, + Some(&[D3D_FEATURE_LEVEL_11_0]), + D3D11_SDK_VERSION, + Some(&mut d3d11_device as *mut _), + None, + None + ); + match d3d11_device_result { + Ok(_) => d3d11_device.map_or_else(|| Err(StreamCreateError::Other("Failed to create ID3D11Device".into())), |x| Ok((dxgi_adapter, x))), + Err(e) => Err(StreamCreateError::Other(format!("Failed to create d3d11 device"))) + , + } + } + } + + pub fn new(config: CaptureConfig, callback: Box) + Send + 'static>) -> Result { + let should_couninit = unsafe { + CoInitializeEx(None, COINIT_MULTITHREADED).is_ok() + }; + + let pixel_format = match config.pixel_format { + CapturePixelFormat::Bgra8888 => DirectXPixelFormat::B8G8R8A8UIntNormalized, + CapturePixelFormat::Argb2101010 => DirectXPixelFormat::R10G10B10A2UIntNormalized, + _ => return Err(StreamCreateError::UnsupportedPixelFormat), + }; + + let interop: IGraphicsCaptureItemInterop = windows::core::factory::() + .map_err(|_| StreamCreateError::Other("Failed to create IGraphicsCaptureInterop factory".into()))?; + + let callback_target = config.target.clone(); + + let graphics_capture_item: GraphicsCaptureItem = unsafe { + match config.target { + Capturable::Window(window) => + interop.CreateForWindow(window.impl_capturable_window.0) + .map_err(|_| StreamCreateError::Other("Failed to create graphics capture item from HWND".into()))?, + Capturable::Display(display) => + interop.CreateForMonitor(display.impl_capturable_display.0) + .map_err(|_| StreamCreateError::Other("Failed to create graphics capture item from HMONITOR".into()))?, + } + }; + + let (dxgi_adapter, d3d11_device) = match (config.impl_capture_config.dxgi_adapter, config.impl_capture_config.d3d11_device) { + (_, Some(d3d11_device)) => { + let dxgi_adapter = d3d11_device.cast().map_err(|_| StreamCreateError::Other("Failed to create IDXGIAdapter from ID3D11Device".into()))?; + (dxgi_adapter, d3d11_device) + }, + (Some(dxgi_adapter), None) => Self::create_d3d11_device(dxgi_adapter)?, + (None, None) => { + let dxgi_factory: IDXGIFactory = unsafe { CreateDXGIFactory() + .map_err(|_| StreamCreateError::Other("Failed to create IDXGIAdapter factory".into())) }?; + let dxgi_adapter = unsafe { dxgi_factory.EnumAdapters(0) } + .map_err(|_| StreamCreateError::Other("Failed to enumerate IDXGIAdapter".into()))?; + Self::create_d3d11_device(dxgi_adapter)? + } + }; + + let dxgi_device: IDXGIDevice = d3d11_device.clone().cast() + .map_err(|_| StreamCreateError::Other("Failed to cast ID3D11Device to IDXGIDevice".into()))?; + let direct3d_device_iinspectible = unsafe { CreateDirect3D11DeviceFromDXGIDevice(&dxgi_device) } + .map_err(|_| StreamCreateError::Other("Failed to create IDirect3DDevice from IDXGIDevice".into()))?; + let direct3d_device: IDirect3DDevice = direct3d_device_iinspectible.cast() + .map_err(|_| StreamCreateError::Other("Failed to cast IInspectible to IDirect3DDevice".into()))?; + + let callback_direct3d_device = d3d11_device.clone(); + + let (width, height) = ((config.output_size.width + 0.1) as usize, (config.output_size.height + 0.1) as usize); + + let frame_pool = Direct3D11CaptureFramePool::CreateFreeThreaded( + &direct3d_device, + pixel_format, + config.buffer_count as i32, + SizeInt32 { Width: width as i32, Height: height as i32 }, + ).map_err(|e| StreamCreateError::Other(format!("Failed to create Direct3D11CaptureFramePool: {}", e.to_string())))?; + + let shared_handler_data = Arc::new( + SharedHandlerData { + callback: Mutex::new(callback), + closed: AtomicBool::new(false), + frame_id_counter: AtomicU64::new(0), + audio_frame_id_counter: AtomicU64::new(0), + } + ); + + let close_handler_data = shared_handler_data.clone(); + let frame_handler_data = shared_handler_data.clone(); + let audio_handler_data = shared_handler_data.clone(); + + let close_handler = TypedEventHandler::new(move |_, _| { + let alread_closed = close_handler_data.closed.fetch_and(true, atomic::Ordering::AcqRel); + if !alread_closed { + let mut callback = close_handler_data.callback.lock(); + (*callback)(Ok(StreamEvent::End)); + } + Ok(()) + }); + + let mut t_first_frame = None; + let mut t_last_frame = None; + + let frame_handler = TypedEventHandler::new(move |frame_pool: &Option, _: &Option| { + if frame_pool.is_none() { + return Ok(()); + } + let frame_pool = frame_pool.as_ref().unwrap(); + if frame_handler_data.closed.load(atomic::Ordering::Acquire) { + return Ok(()); + } + let t_capture = Instant::now(); + let t_origin = match t_first_frame { + Some(t_first_frame) => t_capture - t_first_frame, + None => { + t_first_frame = Some(t_capture); + Duration::ZERO + } + }; + let duration = match t_last_frame { + Some(t_last_frame) => t_capture - t_last_frame, + None => { + t_last_frame = Some(t_capture); + Duration::ZERO + } + }; + let dpi = unsafe { + match &callback_target { + Capturable::Window(window) => GetDpiForWindow(window.impl_capturable_window.0), + Capturable::Display(display) => { + let mut dpi_x = 0u32; + let mut dpi_y = 0u32; + let _ = GetDpiForMonitor(display.impl_capturable_display.0, MDT_RAW_DPI, &mut dpi_x as *mut _, &mut dpi_y as *mut _); + dpi_x.min(dpi_y) + } + } + }; + let mut callback = frame_handler_data.callback.lock(); + //let window_rect = RECT::default(); + let frame = match frame_pool.TryGetNextFrame() { + Ok(frame) => frame, + Err(e) => { + (*callback)(Err(StreamError::Other(format!("Failed to capture frame: {}", e.to_string())))); + return Ok(()); + } + }; + + let frame_id = frame_handler_data.frame_id_counter.fetch_add(1, atomic::Ordering::AcqRel); + let impl_video_frame = WindowsVideoFrame { + device: callback_direct3d_device.clone(), + frame, + frame_id, + frame_size: (width, height), + pixel_format, + dpi, + t_capture, + t_origin, + duration, + }; + let video_frame = VideoFrame { + impl_video_frame + }; + (*callback)(Ok(StreamEvent::Video(video_frame))); + Ok(()) + }); + + frame_pool.FrameArrived(&frame_handler).map_err(|_| StreamCreateError::Other("Failed to listen to FrameArrived event".into()))?; + graphics_capture_item.Closed(&close_handler).map_err(|_| StreamCreateError::Other("Failed to listen to Closed event".into()))?; + + let capture_session = frame_pool.CreateCaptureSession(&graphics_capture_item) + .map_err(|_| StreamCreateError::Other("Failed to create GraphicsCaptureSession".into()))?; + + let audio_stream = if let Some(audio_config) = config.capture_audio { + let handler_config = audio_config.clone(); + let audio_handler = Box::new(move |audio_result: Result, WindowsAudioCaptureStreamError>| { + if audio_handler_data.closed.load(atomic::Ordering::Acquire) { + return; + } + match audio_result { + Ok(packet) => { + let audio_frame_id = audio_handler_data.audio_frame_id_counter.fetch_add(1, atomic::Ordering::AcqRel); + let event = StreamEvent::Audio(AudioFrame { + impl_audio_frame: WindowsAudioFrame { + data: packet.data.to_owned().into_boxed_slice(), + channel_count: handler_config.channel_count, + sample_rate: handler_config.sample_rate, + duration: packet.duration, + origin_time: packet.origin_time, + frame_id: audio_frame_id + } + }); + (*audio_handler_data.callback.lock())(Ok(event)); + }, + Err(e) => { + (*audio_handler_data.callback.lock())(Err(StreamError::Other("Audio stream error".to_string()))); + } + } + }); + + match WindowsAudioCaptureStream::new(audio_config, audio_handler) { + Ok(audio_stream) => { + Some(audio_stream) + }, + Err(_) => { + return Err(StreamCreateError::Other("Failed to create audio stream".into())) + } + } + } else { + None + }; + + capture_session.StartCapture().map_err(|_| StreamCreateError::Other("Failed to start capture".into()))?; + + let stream = WindowsCaptureStream { + dxgi_adapter, + dxgi_device, + d3d11_device, + frame_pool, + capture_session, + should_couninit, + shared_handler_data, + audio_stream + }; + + Ok(stream) + } + + pub fn stop(&self) -> Result<(), StreamStopError> { + let already_closed = self.shared_handler_data.closed.fetch_and(true, atomic::Ordering::AcqRel); + if !already_closed { + (*self.shared_handler_data.callback.lock())(Ok(StreamEvent::End)); + } + self.capture_session.Close().map_err(|_| StreamStopError::Other("Failed to close capture session".into()))?; + Ok(()) + } +} + +impl Drop for WindowsCaptureStream { + fn drop(&mut self) { + let _ = self.stop(); + if let Some(audio_stream) = &mut self.audio_stream { + audio_stream.stop(); + } + if self.should_couninit { + unsafe { CoUninitialize(); } + } + } +} diff --git a/src/platform/windows/frame.rs b/src/platform/windows/frame.rs new file mode 100644 index 00000000..72745c19 --- /dev/null +++ b/src/platform/windows/frame.rs @@ -0,0 +1,103 @@ +use std::{marker::PhantomData, time::Duration}; + +use windows::{Graphics::{Capture::Direct3D11CaptureFrame, DirectX::DirectXPixelFormat, SizeInt32}, Win32::Graphics::Direct3D11::ID3D11Device}; + +use crate::{prelude::{AudioBufferError, AudioCaptureFrame, AudioChannelCount, AudioChannelDataSamples, AudioSampleRate, VideoCaptureFrame}, util::Size}; + +pub struct WindowsVideoFrame { + pub(crate) device : ID3D11Device, + pub(crate) frame : Direct3D11CaptureFrame, + pub(crate) frame_size : (usize, usize), + pub(crate) pixel_format : DirectXPixelFormat, + pub(crate) frame_id : u64, + pub(crate) dpi : u32, + pub(crate) t_capture : std::time::Instant, + pub(crate) t_origin : std::time::Duration, + pub(crate) duration : std::time::Duration, +} + +impl VideoCaptureFrame for WindowsVideoFrame { + fn size(&self) -> Size { + let size = self.frame.ContentSize().unwrap_or(SizeInt32::default()); + Size { + width: size.Width as f64, + height: size.Height as f64, + } + } + + fn dpi(&self) -> f64 { + self.dpi as f64 + } + + fn duration(&self) -> std::time::Duration { + self.duration + } + + fn origin_time(&self) -> std::time::Duration { + self.t_origin + } + + fn capture_time(&self) -> std::time::Instant { + self.t_capture + } + + fn frame_id(&self) -> u64 { + self.frame_id + } +} + +pub struct WindowsAudioFrame { + pub(crate) data: Box<[i16]>, + pub(crate) channel_count: AudioChannelCount, + pub(crate) sample_rate: AudioSampleRate, + pub(crate) duration: Duration, + pub(crate) origin_time: Duration, + pub(crate) frame_id: u64, +} + +impl AudioCaptureFrame for WindowsAudioFrame { + fn sample_rate(&self) -> crate::prelude::AudioSampleRate { + self.sample_rate + } + + fn channel_count(&self) -> crate::prelude::AudioChannelCount { + self.channel_count + } + + fn audio_channel_buffer(&mut self, channel: usize) -> Result, crate::prelude::AudioBufferError> { + let element_stride = match self.channel_count { + AudioChannelCount::Mono => { + if channel != 0 { + return Err(AudioBufferError::InvalidChannel) + } + 0 + }, + AudioChannelCount::Stereo => { + if channel > 1 { + return Err(AudioBufferError::InvalidChannel) + } + channel + }, + }; + let data = &self.data[element_stride] as *const i16 as *const u8; + Ok(crate::prelude::AudioChannelData::I16(AudioChannelDataSamples { + data, + stride: element_stride / 2, + length: self.data.len() / element_stride, + phantom_lifetime: PhantomData + })) + } + + fn duration(&self) -> std::time::Duration { + self.duration + } + + fn origin_time(&self) -> std::time::Duration { + self.origin_time + } + + fn frame_id(&self) -> u64 { + self.frame_id + } +} + diff --git a/src/platform/windows/mod.rs b/src/platform/windows/mod.rs new file mode 100644 index 00000000..a3b53c34 --- /dev/null +++ b/src/platform/windows/mod.rs @@ -0,0 +1,27 @@ +use windows::Win32::Foundation::CloseHandle; +use windows::Win32::Foundation::HANDLE; + +mod capture_stream; +mod capturable_content; +mod audio_capture_stream; +pub(crate) mod frame; + +pub(crate) struct AutoHandle(HANDLE); +impl Drop for AutoHandle { + fn drop(&mut self) { + unsafe { let _ = CloseHandle(self.0); } + } +} + +pub use capturable_content::WindowsCapturableApplication as ImplCapturableApplication; +pub use capturable_content::WindowsCapturableDisplay as ImplCapturableDisplay; +pub use capturable_content::WindowsCapturableWindow as ImplCapturableWindow; +pub use capturable_content::WindowsCapturableContent as ImplCapturableContent; + +pub use capture_stream::WindowsCaptureStream as ImplCaptureStream; +pub use capture_stream::WindowsCaptureConfig as ImplCaptureConfig; +pub use capture_stream::WindowsAudioCaptureConfig as ImplAudioCaptureConfig; +pub use capture_stream::WindowsPixelFormat as ImplPixelFormat; + +pub use frame::WindowsVideoFrame as ImplVideoFrame; +pub use frame::WindowsAudioFrame as ImplAudioFrame; diff --git a/src/prelude.rs b/src/prelude.rs new file mode 100644 index 00000000..63228b77 --- /dev/null +++ b/src/prelude.rs @@ -0,0 +1,4 @@ +pub use crate::capturable_content::*; +pub use crate::frame::*; +pub use crate::capture_stream::*; +pub use crate::util::*; diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 00000000..26cfeb80 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,80 @@ +/// Represents a 2d size +#[derive(Debug, Copy, Clone)] +pub struct Size { + pub width: f64, + pub height: f64, +} + +impl Size { + /// scale the size uniformly by some value + pub fn scaled(&self, scale: f64) -> Self { + Self { + width: self.width * scale, + height: self.height * scale + } + } + + /// scale the size non-uniformly in x and y + pub fn scaled_2d(&self, scale: (f64, f64)) -> Self { + Self { + width: self.width * scale.0, + height: self.height * scale.1 + } + } +} + +/// Represents a 2d point +#[derive(Debug, Copy, Clone)] +pub struct Point { + pub x: f64, + pub y: f64, +} + +impl Point { + /// The point at (0, 0) + pub const ZERO: Point = Point { + x: 0.0, + y: 0.0 + }; + + /// Scale the point uniformly by some value + pub fn scaled(&self, scale: f64) -> Self { + Self { + x: self.x * scale, + y: self.y * scale + } + } + + /// Scale the point non-uniformly in x and y + pub fn scaled_2d(&self, scale: (f64, f64)) -> Self { + Self { + x: self.x * scale.0, + y: self.y * scale.1 + } + } +} + +/// Represents an axis-aligned rectangle +#[derive(Debug, Copy, Clone)] +pub struct Rect { + pub origin: Point, + pub size: Size, +} + +impl Rect { + /// Scale the rectangle uniformly + pub fn scaled(&self, scale: f64) -> Self { + Self { + origin: self.origin.scaled(scale), + size: self.size.scaled(scale) + } + } + + /// Scale the rectangle non-uniformly in x and y + pub fn scaled_2d(&self, scale: (f64, f64)) -> Self { + Self { + origin: self.origin.scaled_2d(scale), + size: self.size.scaled_2d(scale) + } + } +} diff --git a/tests/capturable_content.rs b/tests/capturable_content.rs new file mode 100644 index 00000000..725d59fb --- /dev/null +++ b/tests/capturable_content.rs @@ -0,0 +1,20 @@ +use crabgrab::capturable_content::*; +use futures::executor::block_on; + +#[test] +fn find_capturable_windows() { + let content_filter = CapturableContentFilter { + windows: Some(CapturableWindowFilter { + desktop_windows: true, + onscreen_only: true, + }), + displays: false + }; + let content_result = block_on(CapturableContent::new(content_filter)); + assert!(content_result.is_ok()); + println!("capturable windows: "); + let content = content_result.unwrap(); + for window in content.windows() { + println!(" * {}", window.title()); + } +} diff --git a/tests/capture_display.rs b/tests/capture_display.rs new file mode 100644 index 00000000..154a536b --- /dev/null +++ b/tests/capture_display.rs @@ -0,0 +1,54 @@ +use std::sync::Arc; + +use crabgrab::prelude::*; +use futures::channel::oneshot; +use parking_lot::Mutex; + +#[tokio::test] +async fn capture_display() { + if !CaptureStream::test_access(false) { + let has_permission = CaptureStream::request_access(false).await; + assert!(has_permission); + } + let content_filter = CapturableContentFilter { + windows: None, + displays: true + }; + let content = CapturableContent::new(content_filter).await; + assert!(content.is_ok()); + let content = content.unwrap(); + let mut displays = content.displays(); + println!("display count: {}", displays.len()); + let display_opt = displays.next(); + assert!(display_opt.is_some()); + let display = display_opt.unwrap(); + println!("display: {:?}", display.rect()); + //let size = display.rect().size; + let config = CaptureConfig::with_display(display, CapturePixelFormat::Bgra8888); + let (tx, rx) = oneshot::channel(); + let tx = Arc::new(Mutex::new(Some(tx))); + let new_stream_result = CaptureStream::new(config, move |result| { + println!("stream result: {:?}", result); + if let Some(tx) = tx.lock().take() { + match result { + Ok(event) => { + match event { + StreamEvent::Video(_frame) => { + tx.send(true).unwrap(); + }, + _ => {} + } + }, + Err(_) => tx.send(false).unwrap(), + }; + } + }); + if let Err(e) = &new_stream_result { + println!("Stream create error: {:?}", e); + } else { + println!("Stream created!"); + } + assert!(new_stream_result.is_ok()); + let stream_callback_result = rx.await.unwrap(); + assert!(stream_callback_result); +} diff --git a/tests/capture_window.rs b/tests/capture_window.rs new file mode 100644 index 00000000..e69de29b diff --git a/update_doc_copy.ps1 b/update_doc_copy.ps1 new file mode 100644 index 00000000..09c6bb20 --- /dev/null +++ b/update_doc_copy.ps1 @@ -0,0 +1,3 @@ +mkdir docs/windows_docs -Force +cargo doc --no-deps --features dxgi,dx11 +Copy-Item -Path ".\target\doc\*" -Destination "docs/windows_docs" -Recurse -Force diff --git a/update_doc_copy.sh b/update_doc_copy.sh new file mode 100755 index 00000000..37942b28 --- /dev/null +++ b/update_doc_copy.sh @@ -0,0 +1,3 @@ +mkdir -p docs/macos_docs +cargo doc --no-deps --features metal,iosurface,bitmap +cp -rf target/doc/** docs/macos_docs