Provide all running applications to `SCContentFilter` to capture display

Antonio Scandurra created

Change summary

crates/capture/build.rs        |   4 
crates/capture/src/bindings.rs |   4 
crates/capture/src/dummy.m     |  15 ---
crates/capture/src/main.rs     | 153 ++++++++++++++++-------------------
4 files changed, 72 insertions(+), 104 deletions(-)

Detailed changes

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)

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) }
-}

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

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");
 }