diff --git a/crates/capture/build.rs b/crates/capture/build.rs index 6ab5a40e0f8df1a95e648ff96f7a6d51b5af3750..51ff36f705a1f969005c1bafc86fd560f446017f 100644 --- a/crates/capture/build.rs +++ b/crates/capture/build.rs @@ -17,15 +17,13 @@ fn main() { .unwrap(); let sdk_path = sdk_path.trim_end(); + println!("cargo:rerun-if-changed=src/bindings.h"); let bindings = bindgen::Builder::default() .header("src/bindings.h") .clang_arg(format!("-isysroot{}", sdk_path)) .clang_arg("-xobjective-c") .allowlist_function("CMTimeMake") - .allowlist_type("CMSampleBufferRef") .allowlist_type("SCStreamOutputType") - .allowlist_var("_dispatch_main_q") - .allowlist_function("dispatch_async_f") .allowlist_function("dispatch_queue_create") .parse_callbacks(Box::new(bindgen::CargoCallbacks)) .layout_tests(false) diff --git a/crates/capture/src/bindings.rs b/crates/capture/src/bindings.rs index afdef6d1dafb6b7572a18774961429fe21b1600a..a111bede4de059f0c0157775778bcc56ebdd4079 100644 --- a/crates/capture/src/bindings.rs +++ b/crates/capture/src/bindings.rs @@ -5,7 +5,3 @@ use objc::*; include!(concat!(env!("OUT_DIR"), "/bindings.rs")); - -pub fn dispatch_get_main_queue() -> dispatch_queue_t { - unsafe { std::mem::transmute(&_dispatch_main_q) } -} diff --git a/crates/capture/src/dummy.m b/crates/capture/src/dummy.m index 9e3a343ce5490d1facb7a987566c9fc6a9eb1456..0ae2bda6a7646d48c3485a1e811ffc2c5ecb8888 100644 --- a/crates/capture/src/dummy.m +++ b/crates/capture/src/dummy.m @@ -4,19 +4,4 @@ @end @implementation MyClass - -- (void)stream:(SCStream *)stream - didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer - ofType:(SCStreamOutputType)type { - printf("dummy capture handler called"); -} - -- (void)stream:(SCStream *)stream didStopWithError:(NSError *)error { - printf("dummy did stop with error called"); -} - -int main() { - [[MyClass alloc] init]; -} - @end diff --git a/crates/capture/src/main.rs b/crates/capture/src/main.rs index 70a8ca62b24dce463dc56b0598e04273cf4fd29d..c1c017c3599927f9eb6b823865e373b02491b44a 100644 --- a/crates/capture/src/main.rs +++ b/crates/capture/src/main.rs @@ -1,18 +1,22 @@ mod bindings; -use std::{slice, str, ptr::{self}}; - +use crate::bindings::{dispatch_queue_create, NSObject, SCStreamOutputType}; use block::ConcreteBlock; use cocoa::{ - base::{id, nil}, - foundation::{NSArray, NSString, NSUInteger, NSInteger}, + base::{id, nil, YES}, + foundation::{NSArray, NSBundle, NSString, NSUInteger}, }; use gpui::{actions, elements::*, keymap::Binding, Menu, MenuItem}; use log::LevelFilter; -use objc::{class, msg_send, sel, sel_impl, declare::ClassDecl, runtime::{Protocol, Object, Sel}}; +use objc::{ + class, + declare::ClassDecl, + msg_send, + runtime::{Object, Protocol, Sel}, + sel, sel_impl, +}; use simplelog::SimpleLogger; - -use crate::bindings::{dispatch_get_main_queue, dispatch_queue_create, NSObject, CMSampleBufferRef}; +use std::{ptr, slice, str}; #[allow(non_upper_case_globals)] const NSUTF8StringEncoding: NSUInteger = 4; @@ -41,72 +45,51 @@ fn main() { println!("ERROR {}", string_from_objc(msg_send![error, localizedDescription])); return; } - + + let applications: id = msg_send![content, applications]; let displays: id = msg_send![content, displays]; - - if let Some(display) = (0..displays.count()) - .map(|ix| displays.objectAtIndex(ix)) - .next() - { - - let display_id: u32 = msg_send![display, displayID]; - println!("display id {:?}", display_id); - - // let mut decl = ClassDecl::new("CaptureOutput", class!(NSObject)).unwrap(); - // decl.add_protocol(Protocol::get("SCStreamOutput").unwrap()); - // decl.add_protocol(Protocol::get("SCStreamDelegate").unwrap()); - // decl.add_method(sel!(stream:didOutputSampleBuffer:ofType:), sample_output as extern "C" fn(&Object, Sel, id, id, NSInteger)); - // decl.add_method(sel!(stream:didStopWithError:), did_stop_with_error as extern "C" fn(&Object, Sel, id, id)); - // let capture_output_class = decl.register(); - - // let output: id = msg_send![capture_output_class, alloc]; - // let output: id = msg_send![output, init]; - - let output: id = msg_send![class!(MyClass), alloc]; - let output: id = msg_send![output, init]; - - // Do we conform to the protocol? - let conforms: bool = msg_send![output, conformsToProtocol: Protocol::get("SCStreamOutput").unwrap()]; - dbg!(conforms); - assert!(conforms, "expect CaptureOutput instance to conform to SCStreamOutput protocol"); - - // Confirm we can send the protocol message to the object - let _: () = msg_send![output, stream:NSObject(ptr::null_mut()) didOutputSampleBuffer:NSObject(ptr::null_mut()) ofType:0]; - - let excluded_windows: id = msg_send![class!(NSArray), array]; - let filter: id = msg_send![class!(SCContentFilter), alloc]; - let filter: id = msg_send![filter, initWithDisplay: display excludingWindows: excluded_windows]; - let config: id = msg_send![class!(SCStreamConfiguration), alloc]; - let config: id = msg_send![config, init]; - // Configure the display content width and height. - let _: () = msg_send![config, setWidth: 800]; - let _: () = msg_send![config, setHeight: 600]; - let _: () = msg_send![config, setMinimumFrameInterval: bindings::CMTimeMake(1, 60)]; - let _: () = msg_send![config, setQueueDepth: 5]; - - let stream: id = msg_send![class!(SCStream), alloc]; - let stream: id = msg_send![stream, initWithFilter: filter configuration: config delegate: output]; - let error: id = nil; - let queue = dispatch_queue_create(ptr::null(), NSObject(ptr::null_mut())); - - let _: () = msg_send![stream, - addStreamOutput: output type: bindings::SCStreamOutputType_SCStreamOutputTypeScreen - sampleHandlerQueue: queue - error: &error - ]; - - let start_capture_completion = ConcreteBlock::new(move |error: id| { - if !error.is_null() { - println!("error starting capture... error? {}", string_from_objc(msg_send![error, localizedDescription])); - return; - } - - println!("starting capture"); - }); - - assert!(!stream.is_null()); - let _: () = msg_send![stream, startCaptureWithCompletionHandler: start_capture_completion]; - } + let display: id = displays.objectAtIndex(0); + + let mut decl = ClassDecl::new("CaptureOutput", class!(NSObject)).unwrap(); + decl.add_protocol(Protocol::get("SCStreamOutput").unwrap()); + decl.add_method(sel!(stream:didOutputSampleBuffer:ofType:), sample_output as extern "C" fn(&Object, Sel, id, id, SCStreamOutputType)); + let capture_output_class = decl.register(); + + let output: id = msg_send![capture_output_class, alloc]; + let output: id = msg_send![output, init]; + + let filter: id = msg_send![class!(SCContentFilter), alloc]; + let filter: id = msg_send![filter, initWithDisplay: display includingApplications: applications exceptingWindows: nil]; + // let filter: id = msg_send![filter, initWithDesktopIndependentWindow: window]; + let config: id = msg_send![class!(SCStreamConfiguration), alloc]; + let config: id = msg_send![config, init]; + let _: () = msg_send![config, setMinimumFrameInterval: bindings::CMTimeMake(1, 60)]; + let _: () = msg_send![config, setQueueDepth: 6]; + let _: () = msg_send![config, setShowsCursor: YES]; + + let stream: id = msg_send![class!(SCStream), alloc]; + let stream: id = msg_send![stream, initWithFilter: filter configuration: config delegate: output]; + let error: id = nil; + let queue = dispatch_queue_create(ptr::null(), NSObject(ptr::null_mut())); + + let _: () = msg_send![stream, + addStreamOutput: output type: bindings::SCStreamOutputType_SCStreamOutputTypeScreen + sampleHandlerQueue: queue + error: &error + ]; + + let start_capture_completion = ConcreteBlock::new(move |error: id| { + if !error.is_null() { + println!("error starting capture... error? {}", string_from_objc(msg_send![error, localizedDescription])); + return; + } + + println!("starting capture"); + }); + + assert!(!stream.is_null()); + let _: () = msg_send![stream, startCaptureWithCompletionHandler: start_capture_completion]; + }); let _: id = msg_send![ @@ -136,18 +119,24 @@ impl gpui::View for ScreenCaptureView { } pub unsafe fn string_from_objc(string: id) -> String { - let len = msg_send![string, lengthOfBytesUsingEncoding: NSUTF8StringEncoding]; - let bytes = string.UTF8String() as *const u8; - str::from_utf8(slice::from_raw_parts(bytes, len)) - .unwrap() - .to_string() -} - -extern "C" fn did_stop_with_error(this: &Object, _: Sel, stream: id, error: id) { - println!("did_stop_with_error"); + if string.is_null() { + Default::default() + } else { + let len = msg_send![string, lengthOfBytesUsingEncoding: NSUTF8StringEncoding]; + let bytes = string.UTF8String() as *const u8; + str::from_utf8(slice::from_raw_parts(bytes, len)) + .unwrap() + .to_string() + } } -extern "C" fn sample_output(this: &Object, _: Sel, stream: id, buffer: id, kind: NSInteger) { +extern "C" fn sample_output( + _: &Object, + _: Sel, + _stream: id, + _buffer: id, + _kind: SCStreamOutputType, +) { println!("sample output"); }