Add capture example to GPUI

Nathan Sobo created

Added a linker arg to the GPUI build script. Not sure if we'll want to bake
this into GPUI or do it via another crate, but this is convenient for
exploration for now.

Change summary

crates/gpui/Cargo.toml          |  6 ++
crates/gpui/build.rs            |  3 +
crates/gpui/examples/capture.rs | 78 +++++++++++++++++++++++++++++++++++
crates/gpui/script/capture      |  4 +
4 files changed, 91 insertions(+)

Detailed changes

crates/gpui/Cargo.toml 🔗

@@ -3,6 +3,7 @@ authors = ["Nathan Sobo <nathansobo@gmail.com>"]
 edition = "2021"
 name = "gpui"
 version = "0.1.0"
+description = "A GPU-accelerated UI framework"
 
 [lib]
 path = "src/gpui.rs"
@@ -11,6 +12,11 @@ doctest = false
 [features]
 test-support = ["backtrace", "dhat", "env_logger", "collections/test-support"]
 
+[package.metadata.bundle.example.capture]
+name = "Capture"
+identifier = "rs.gpui.examples.Capture"
+description = "An example of screen capture"
+
 [dependencies]
 collections = { path = "../collections" }
 gpui_macros = { path = "../gpui_macros" }

crates/gpui/build.rs 🔗

@@ -9,6 +9,9 @@ fn main() {
     compile_context_predicate_parser();
     compile_metal_shaders();
     generate_shader_bindings();
+
+    // Support screen capture
+    println!("cargo:rustc-link-lib=framework=ScreenCaptureKit");
 }
 
 fn generate_dispatch_bindings() {

crates/gpui/examples/capture.rs 🔗

@@ -0,0 +1,78 @@
+use std::{slice, str};
+
+use block::ConcreteBlock;
+use cocoa::{
+    base::id,
+    foundation::{NSString, NSUInteger},
+};
+use gpui::{actions, elements::*, keymap::Binding, Menu, MenuItem};
+use log::LevelFilter;
+use objc::{class, msg_send, sel, sel_impl};
+use simplelog::SimpleLogger;
+
+#[allow(non_upper_case_globals)]
+const NSUTF8StringEncoding: NSUInteger = 4;
+
+actions!(capture, [Quit]);
+
+fn main() {
+    SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
+
+    gpui::App::new(()).unwrap().run(|cx| {
+        cx.platform().activate(true);
+        cx.add_global_action(quit);
+
+        cx.add_bindings([Binding::new("cmd-q", Quit, None)]);
+        cx.set_menus(vec![Menu {
+            name: "Zed",
+            items: vec![MenuItem::Action {
+                name: "Quit",
+                action: Box::new(Quit),
+            }],
+        }]);
+
+        unsafe {
+            let block = ConcreteBlock::new(move |content: id, error: id| {
+                println!("got response with shareable content");
+                dbg!(content);
+                dbg!(error);
+                dbg!(string_from_objc(msg_send![error, localizedDescription]));
+            });
+
+            let _: id = msg_send![
+                class!(SCShareableContent),
+                getShareableContentWithCompletionHandler: block
+            ];
+        }
+
+        // cx.add_window(Default::default(), |_| ScreenCaptureView);
+    });
+}
+
+struct ScreenCaptureView;
+
+impl gpui::Entity for ScreenCaptureView {
+    type Event = ();
+}
+
+impl gpui::View for ScreenCaptureView {
+    fn ui_name() -> &'static str {
+        "View"
+    }
+
+    fn render(&mut self, _: &mut gpui::RenderContext<Self>) -> gpui::ElementBox {
+        Empty::new().boxed()
+    }
+}
+
+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()
+}
+
+fn quit(_: &Quit, cx: &mut gpui::MutableAppContext) {
+    cx.platform().quit();
+}

crates/gpui/script/capture 🔗

@@ -0,0 +1,4 @@
+#!/bin/bash
+
+cargo bundle --example capture
+open ../../target/debug/examples/bundle/osx/Capture.app