WIP: Start pushing native surface to Scene

Antonio Scandurra created

This is segfaulting for some reason, so that's the next step to figure out.

Change summary

Cargo.lock                 |  3 +
crates/capture/Cargo.toml  |  3 +
crates/capture/src/main.rs | 98 +++++++++++++++++++++++++++++----------
3 files changed, 79 insertions(+), 25 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -761,10 +761,13 @@ dependencies = [
  "core-foundation",
  "core-graphics",
  "foreign-types",
+ "futures",
  "gpui",
  "io_surface",
  "log",
  "objc",
+ "parking_lot 0.11.2",
+ "postage",
  "simplelog",
 ]
 

crates/capture/Cargo.toml 🔗

@@ -17,8 +17,11 @@ cocoa = "0.24"
 core-foundation = "0.9.3"
 core-graphics = "0.22.3"
 foreign-types = "0.3"
+futures = "0.3"
 log = { version = "0.4.16", features = ["kv_unstable_serde"] }
 objc = "0.2"
+parking_lot = "0.11.1"
+postage = { version = "0.4.1", features = ["futures-traits"] }
 simplelog = "0.9"
 
 [build-dependencies]

crates/capture/src/main.rs 🔗

@@ -8,8 +8,15 @@ use cocoa::{
 };
 use core_foundation::{base::TCFType, number::CFNumberRef, string::CFStringRef};
 use core_media::{CMSampleBuffer, CMSampleBufferRef};
-use gpui::elements::Canvas;
-use gpui::{actions, elements::*, keymap::Binding, Menu, MenuItem};
+use futures::StreamExt;
+use gpui::{
+    actions,
+    elements::{Canvas, *},
+    keymap::Binding,
+    platform::current::Surface,
+    Menu, MenuItem, ViewContext,
+};
+use io_surface::IOSurface;
 use log::LevelFilter;
 use objc::{
     class,
@@ -18,8 +25,9 @@ use objc::{
     runtime::{Object, Sel},
     sel, sel_impl,
 };
+use parking_lot::Mutex;
 use simplelog::SimpleLogger;
-use std::{ffi::c_void, slice, str};
+use std::{cell::RefCell, ffi::c_void, slice, str};
 
 #[allow(non_upper_case_globals)]
 const NSUTF8StringEncoding: NSUInteger = 4;
@@ -42,10 +50,29 @@ fn main() {
             }],
         }]);
 
+        cx.add_window(Default::default(), |cx| ScreenCaptureView::new(cx));
+    });
+}
+
+struct ScreenCaptureView {
+    surface: Option<io_surface::IOSurface>,
+}
+
+impl gpui::Entity for ScreenCaptureView {
+    type Event = ();
+}
+
+impl ScreenCaptureView {
+    pub fn new(cx: &mut ViewContext<Self>) -> Self {
+        let (surface_tx, mut surface_rx) = postage::watch::channel::<Option<IOSurface>>();
+        let surface_tx = RefCell::new(surface_tx);
         unsafe {
             let block = ConcreteBlock::new(move |content: id, error: id| {
                 if !error.is_null() {
-                    println!("ERROR {}", string_from_objc(msg_send![error, localizedDescription]));
+                    println!(
+                        "ERROR {}",
+                        string_from_objc(msg_send![error, localizedDescription])
+                    );
                     return;
                 }
 
@@ -57,11 +84,16 @@ fn main() {
 
                 let mut decl = ClassDecl::new("CaptureOutput", class!(NSObject)).unwrap();
                 decl.add_ivar::<*mut c_void>("callback");
-                decl.add_method(sel!(stream:didOutputSampleBuffer:ofType:), sample_output as extern "C" fn(&Object, Sel, id, id, SCStreamOutputType));
+                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];
+
+                // TODO: we could probably move this into a background queue.
                 let callback = Box::new(|buffer: CMSampleBufferRef| {
                     let buffer = CMSampleBuffer::wrap_under_get_rule(buffer);
                     let attachments = buffer.attachments();
@@ -79,8 +111,11 @@ fn main() {
                     }
 
                     let image_buffer = buffer.image_buffer();
-                    dbg!(image_buffer.width(), image_buffer.height());
                     let io_surface = image_buffer.io_surface();
+                    println!("before segfault");
+                    *surface_tx.borrow_mut().borrow_mut() = Some(io_surface);
+
+                    println!("!!!!!!!!!!!!!!!!!");
                 }) as Box<dyn FnMut(CMSampleBufferRef)>;
                 let callback = Box::into_raw(Box::new(callback));
                 (*output).set_ivar("callback", callback as *mut c_void);
@@ -109,7 +144,10 @@ fn main() {
 
                 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]));
+                        println!(
+                            "error starting capture... error? {}",
+                            string_from_objc(msg_send![error, localizedDescription])
+                        );
                         return;
                     }
 
@@ -117,8 +155,10 @@ fn main() {
                 });
 
                 assert!(!stream.is_null());
-                let _: () = msg_send![stream, startCaptureWithCompletionHandler: start_capture_completion];
-
+                let _: () = msg_send![
+                    stream,
+                    startCaptureWithCompletionHandler: start_capture_completion
+                ];
             });
 
             let _: id = msg_send![
@@ -127,31 +167,39 @@ fn main() {
             ];
         }
 
-        // cx.add_window(Default::default(), |_| ScreenCaptureView);
-    });
-}
-
-struct ScreenCaptureView {
-    surface: io_surface::IOSurface,
-}
+        cx.spawn_weak(|this, mut cx| async move {
+            while let Some(surface) = surface_rx.next().await {
+                if let Some(this) = this.upgrade(&cx) {
+                    this.update(&mut cx, |this, cx| {
+                        this.surface = surface;
+                        cx.notify();
+                    })
+                } else {
+                    break;
+                }
+            }
+        })
+        .detach();
 
-impl gpui::Entity for ScreenCaptureView {
-    type Event = ();
+        Self { surface: None }
+    }
 }
 
-// impl ScreenCaptureView {
-//     pub fn new() -> Self {}
-// }
-
 impl gpui::View for ScreenCaptureView {
     fn ui_name() -> &'static str {
         "View"
     }
 
     fn render(&mut self, _: &mut gpui::RenderContext<Self>) -> gpui::ElementBox {
-        Canvas::new(|bounds, _, cx| {
-
-            // cx.scene.push_surface(surface)
+        let surface = self.surface.clone();
+        Canvas::new(move |bounds, _, cx| {
+            if let Some(native_surface) = surface.clone() {
+                dbg!("!!!!");
+                cx.scene.push_surface(Surface {
+                    bounds,
+                    native_surface,
+                });
+            }
         })
         .boxed()
     }