diff --git a/.all-contributorsrc b/.all-contributorsrc
index 292a480..5de3337 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -44,6 +44,15 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "krzykro2",
+ "name": "Kris Krolak",
+ "avatar_url": "https://avatars.githubusercontent.com/u/6817875?v=4",
+ "profile": "https://github.com/krzykro2",
+ "contributions": [
+ "code"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 87cc9bb..2bdf462 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -11,7 +11,7 @@ env:
jobs:
build:
- runs-on: macos-13
+ runs-on: macos-14
steps:
- uses: actions/checkout@v3
- name: Update rust
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e114cee..c751107 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,14 +4,24 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [0.2.6] - 2024-02-06
+
+### Fixed
+
+- [#37](https://github.com/svtlabs/screencapturekit-rs/issues/37) Fix error handling
+- [#35](https://github.com/svtlabs/screencapturekit-rs/issues/35) Make safe StreamConfiguration defaults equal to unsafe
+
## [0.2.5] - 2024-01-25
### Fixed
+
- 0.2.4 had an issue with the configuration. This is fixed now.
## [0.2.4] - 2024-01-23
### Fixed
+
- [#34](https://github.com/svtlabs/screencapturekit-rs/issues/34) minimum_frame_interval not working as expected
- [#33](https://github.com/svtlabs/screencapturekit-rs/issues/33) Can no longer import SCFrameStatus
@@ -50,13 +60,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [0.1.0] - 2023-08-21
-
-
### Added
- Initial commit with prototype version
-[unreleased]: https://github.com/svtlabs/screencapturekit-rs/compare/v0.2.5...HEAD
+[unreleased]: https://github.com/svtlabs/screencapturekit-rs/compare/v0.2.6...HEAD
+[0.2.6]: https://github.com/svtlabs/screencapturekit-rs/compare/v0.2.5...v0.2.6
[0.2.5]: https://github.com/svtlabs/screencapturekit-rs/compare/v0.2.4...v0.2.5
[0.2.4]: https://github.com/svtlabs/screencapturekit-rs/compare/v0.2.3...v0.2.4
[0.2.3]: https://github.com/svtlabs/screencapturekit-rs/compare/v0.2.2...v0.2.3
diff --git a/Cargo.toml b/Cargo.toml
index 5b89525..e034e0b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,9 @@
[package]
-name = "screencapturekit"
+
+keywords = ["screencapture", "screencapturekit", "macos"]
description = "Rust wrapper for apple's ScreenCaptureKit"
+name = "screencapturekit"
categories = [
"external-ffi-bindings",
"multimedia",
@@ -14,7 +16,6 @@ homepage = "https://github.com/svtlabs"
edition = "2021"
rust-version = "1.72"
version = "0.3.0"
-keywords = ["screencapture", "screencapturekit", "macos"]
license = "MIT OR Apache-2.0"
[lib]
@@ -30,6 +31,3 @@ dispatch = "0.2"
once_cell = "1"
core-foundation = { version = "0.9", features = ["mac_os_10_8_features"] }
core-graphics = { version = "0.23" }
-
-[[example]]
-name = "test_fps"
diff --git a/README.md b/README.md
index 26a10a1..aca9804 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# screencapturekit-rs
-[](#contributors-)
+[](#contributors-)
## Introduction
@@ -87,6 +87,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
 Tokuhiro Matsuno 💻 |
 bigduu 💻 |
 Pranav Joglekar 💻 |
+  Kris Krolak 💻 |
diff --git a/screencapturekit-sys/src/as_ptr.rs b/screencapturekit-sys/src/as_ptr.rs
new file mode 100644
index 0000000..0799429
--- /dev/null
+++ b/screencapturekit-sys/src/as_ptr.rs
@@ -0,0 +1,13 @@
+pub trait AsPtr {
+ fn as_ptr(&self) -> *const Self {
+ self as *const Self
+ }
+}
+pub trait AsMutPtr {
+ fn as_mut_ptr(&self) -> *mut Self {
+ self as *const _ as *mut Self
+ }
+}
+
+impl AsPtr for T {}
+impl AsMutPtr for T {}
diff --git a/screencapturekit-sys/src/lib.rs b/screencapturekit-sys/src/lib.rs
new file mode 100644
index 0000000..ec1d285
--- /dev/null
+++ b/screencapturekit-sys/src/lib.rs
@@ -0,0 +1,16 @@
+pub mod as_ptr;
+pub mod audio_buffer;
+pub mod cm_block_buffer_ref;
+pub mod cm_format_description_ref;
+pub mod cm_sample_buffer_ref;
+pub mod content_filter;
+pub mod cv_image_buffer_ref;
+pub mod cv_pixel_buffer_ref;
+pub mod macros;
+pub mod os_types;
+pub mod sc_stream_frame_info;
+pub mod shareable_content;
+pub mod stream;
+pub mod stream_configuration;
+pub mod stream_error_handler;
+pub mod stream_output_handler;
diff --git a/screencapturekit-sys/src/os_types.rs b/screencapturekit-sys/src/os_types.rs
new file mode 100644
index 0000000..e8c6437
--- /dev/null
+++ b/screencapturekit-sys/src/os_types.rs
@@ -0,0 +1,5 @@
+pub mod base;
+pub mod four_char_code;
+pub mod geometry;
+pub mod graphics;
+pub mod rc;
diff --git a/screencapturekit-sys/src/os_types/four_char_code.rs b/screencapturekit-sys/src/os_types/four_char_code.rs
new file mode 100644
index 0000000..62ee96b
--- /dev/null
+++ b/screencapturekit-sys/src/os_types/four_char_code.rs
@@ -0,0 +1,78 @@
+use std::{
+ ascii,
+ fmt::{self},
+};
+
+#[repr(transparent)]
+#[derive(Copy, Clone, Default, Ord, PartialOrd, Eq, PartialEq, Hash)]
+pub struct FourCharCode(u32);
+
+impl fmt::Display for FourCharCode {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(&self.format())
+ }
+}
+
+impl fmt::Debug for FourCharCode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "\"")?;
+ f.write_str(&self.format())?;
+ write!(f, "\"")
+ }
+}
+
+impl FourCharCode {
+ #[inline]
+ fn format(&self) -> String {
+ // Format as escaped ASCII string.
+
+ let raw = self
+ .into_chars()
+ .into_iter()
+ .flat_map(ascii::escape_default)
+ .collect::>();
+
+ String::from_utf8(raw).unwrap()
+ }
+ /// Returns an instance from the integer value.
+ #[inline]
+ pub const fn from_int(int: u32) -> Self {
+ Self(int)
+ }
+
+ /// Returns an instance from the 4-character code.
+ #[inline]
+ pub const fn from_chars(chars: [u8; 4]) -> Self {
+ Self(u32::from_be_bytes(chars))
+ }
+
+ /// Returns this descriptor's integer value.
+ #[inline]
+ pub const fn into_int(self) -> u32 {
+ self.0
+ }
+
+ /// Returns this descriptor's 4-character code.
+ #[inline]
+ pub const fn into_chars(self) -> [u8; 4] {
+ self.0.to_be_bytes()
+ }
+
+ /// Returns `true` if all of the characters in `self` are ASCII.
+ #[inline]
+ pub const fn is_ascii(&self) -> bool {
+ const NON_ASCII: u32 = u32::from_be_bytes([128; 4]);
+
+ self.0 & NON_ASCII == 0
+ }
+
+ /// Returns `true` if all of the characters in `self` are ASCII graphic
+ /// characters: U+0021 '!' ..= U+007E '~'.
+ #[inline]
+ pub const fn is_ascii_graphic(&self) -> bool {
+ matches!(
+ self.into_chars(),
+ [b'!'..=b'~', b'!'..=b'~', b'!'..=b'~', b'!'..=b'~'],
+ )
+ }
+}
diff --git a/screencapturekit-sys/src/stream_error_handler.rs b/screencapturekit-sys/src/stream_error_handler.rs
new file mode 100644
index 0000000..b7bc091
--- /dev/null
+++ b/screencapturekit-sys/src/stream_error_handler.rs
@@ -0,0 +1,112 @@
+use std::sync::Once;
+
+use objc::{
+ class,
+ declare::ClassDecl,
+ runtime::{Class, Object, Sel},
+ Message, *,
+};
+use objc_foundation::INSObject;
+use objc_id::Id;
+
+pub trait UnsafeSCStreamError: Send + Sync + 'static {
+ fn handle_error(&self);
+}
+
+#[repr(C)]
+pub(crate) struct UnsafeSCStreamErrorHandler {}
+
+unsafe impl Message for UnsafeSCStreamErrorHandler {}
+
+use once_cell::sync::Lazy;
+use std::collections::HashMap;
+use std::sync::RwLock;
+static ERROR_HANDLERS: Lazy>>> =
+ Lazy::new(|| RwLock::new(HashMap::new()));
+
+impl INSObject for UnsafeSCStreamErrorHandler {
+ fn class() -> &'static Class {
+ static REGISTER_UNSAFE_SC_ERROR_HANDLER: Once = Once::new();
+ REGISTER_UNSAFE_SC_ERROR_HANDLER.call_once(|| {
+ let mut decl = ClassDecl::new("SCStreamErrorHandler", class!(NSObject)).unwrap();
+ decl.add_ivar::("_hash");
+
+ extern "C" fn stream_error(
+ this: &mut Object,
+ _cmd: Sel,
+ _stream: *mut Object,
+ _error: *mut Object,
+ ) {
+ unsafe {
+ let hash = this.get_ivar::("_hash");
+ let lookup = ERROR_HANDLERS.read().unwrap();
+ let error_handler = lookup.get(hash).unwrap();
+ error_handler.handle_error();
+ };
+ }
+ unsafe {
+ let stream_error_method: extern "C" fn(&mut Object, Sel, *mut Object, *mut Object) =
+ stream_error;
+
+ decl.add_method(sel!(stream:didStopWithError:), stream_error_method);
+ }
+
+ decl.register();
+ });
+ class!(SCStreamErrorHandler)
+ }
+}
+
+impl UnsafeSCStreamErrorHandler {
+ fn store_error_handler(&mut self, error_handler: impl UnsafeSCStreamError) {
+ unsafe {
+ let obj = &mut *(self as *mut _ as *mut Object);
+ let hash = self.hash_code();
+ ERROR_HANDLERS
+ .write()
+ .unwrap()
+ .insert(hash, Box::new(error_handler));
+ obj.set_ivar("_hash", hash);
+ }
+ }
+ // Error handlers passed into here will currently live forever inside the statically
+ // allocated map.
+ // TODO: Remove the handler from the HashMap whenever the associated stream is dropped.
+ pub fn init(error_handler: impl UnsafeSCStreamError) -> Id {
+ let mut handle = Self::new();
+ handle.store_error_handler(error_handler);
+ handle
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::ptr;
+ use std::sync::mpsc::{sync_channel, SyncSender};
+
+ use super::*;
+
+ struct TestHandler {
+ error_tx: SyncSender<()>,
+ }
+ impl UnsafeSCStreamError for TestHandler {
+ fn handle_error(&self) {
+ eprintln!("ERROR!");
+ if let Err(e) = self.error_tx.send(()) {
+ panic!("can't send error message back on the channel: {:?}", e);
+ }
+ }
+ }
+
+ #[test]
+ fn test_sc_stream_error_handler() {
+ let (error_tx, error_rx) = sync_channel(1);
+ let handle = UnsafeSCStreamErrorHandler::init(TestHandler { error_tx });
+ unsafe {
+ msg_send![handle, stream: ptr::null_mut::