1mod bindings;
2
3use std::{slice, str, ptr::{self}};
4
5use block::ConcreteBlock;
6use cocoa::{
7 base::{id, nil},
8 foundation::{NSArray, NSString, NSUInteger, NSInteger},
9};
10use gpui::{actions, elements::*, keymap::Binding, Menu, MenuItem};
11use log::LevelFilter;
12use objc::{class, msg_send, sel, sel_impl, declare::ClassDecl, runtime::{Protocol, Object, Sel}};
13use simplelog::SimpleLogger;
14
15use crate::bindings::{dispatch_get_main_queue, dispatch_queue_create, NSObject, CMSampleBufferRef};
16
17#[allow(non_upper_case_globals)]
18const NSUTF8StringEncoding: NSUInteger = 4;
19
20actions!(capture, [Quit]);
21
22fn main() {
23 SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
24
25 gpui::App::new(()).unwrap().run(|cx| {
26 cx.platform().activate(true);
27 cx.add_global_action(quit);
28
29 cx.add_bindings([Binding::new("cmd-q", Quit, None)]);
30 cx.set_menus(vec![Menu {
31 name: "Zed",
32 items: vec![MenuItem::Action {
33 name: "Quit",
34 action: Box::new(Quit),
35 }],
36 }]);
37
38 unsafe {
39 let block = ConcreteBlock::new(move |content: id, error: id| {
40 if !error.is_null() {
41 println!("ERROR {}", string_from_objc(msg_send![error, localizedDescription]));
42 return;
43 }
44
45 let displays: id = msg_send![content, displays];
46
47 if let Some(display) = (0..displays.count())
48 .map(|ix| displays.objectAtIndex(ix))
49 .next()
50 {
51
52 let display_id: u32 = msg_send![display, displayID];
53 println!("display id {:?}", display_id);
54
55 // let mut decl = ClassDecl::new("CaptureOutput", class!(NSObject)).unwrap();
56 // decl.add_protocol(Protocol::get("SCStreamOutput").unwrap());
57 // decl.add_protocol(Protocol::get("SCStreamDelegate").unwrap());
58 // decl.add_method(sel!(stream:didOutputSampleBuffer:ofType:), sample_output as extern "C" fn(&Object, Sel, id, id, NSInteger));
59 // decl.add_method(sel!(stream:didStopWithError:), did_stop_with_error as extern "C" fn(&Object, Sel, id, id));
60 // let capture_output_class = decl.register();
61
62 // let output: id = msg_send![capture_output_class, alloc];
63 // let output: id = msg_send![output, init];
64
65 let output: id = msg_send![class!(MyClass), alloc];
66 let output: id = msg_send![output, init];
67
68 // Do we conform to the protocol?
69 let conforms: bool = msg_send![output, conformsToProtocol: Protocol::get("SCStreamOutput").unwrap()];
70 dbg!(conforms);
71 assert!(conforms, "expect CaptureOutput instance to conform to SCStreamOutput protocol");
72
73 // Confirm we can send the protocol message to the object
74 let _: () = msg_send![output, stream:NSObject(ptr::null_mut()) didOutputSampleBuffer:NSObject(ptr::null_mut()) ofType:0];
75
76 let excluded_windows: id = msg_send![class!(NSArray), array];
77 let filter: id = msg_send![class!(SCContentFilter), alloc];
78 let filter: id = msg_send![filter, initWithDisplay: display excludingWindows: excluded_windows];
79 let config: id = msg_send![class!(SCStreamConfiguration), alloc];
80 let config: id = msg_send![config, init];
81 // Configure the display content width and height.
82 let _: () = msg_send![config, setWidth: 800];
83 let _: () = msg_send![config, setHeight: 600];
84 let _: () = msg_send![config, setMinimumFrameInterval: bindings::CMTimeMake(1, 60)];
85 let _: () = msg_send![config, setQueueDepth: 5];
86
87 let stream: id = msg_send![class!(SCStream), alloc];
88 let stream: id = msg_send![stream, initWithFilter: filter configuration: config delegate: output];
89 let error: id = nil;
90 let queue = dispatch_queue_create(ptr::null(), NSObject(ptr::null_mut()));
91
92 let _: () = msg_send![stream,
93 addStreamOutput: output type: bindings::SCStreamOutputType_SCStreamOutputTypeScreen
94 sampleHandlerQueue: queue
95 error: &error
96 ];
97
98 let start_capture_completion = ConcreteBlock::new(move |error: id| {
99 if !error.is_null() {
100 println!("error starting capture... error? {}", string_from_objc(msg_send![error, localizedDescription]));
101 return;
102 }
103
104 println!("starting capture");
105 });
106
107 assert!(!stream.is_null());
108 let _: () = msg_send![stream, startCaptureWithCompletionHandler: start_capture_completion];
109 }
110 });
111
112 let _: id = msg_send![
113 class!(SCShareableContent),
114 getShareableContentWithCompletionHandler: block
115 ];
116 }
117
118 // cx.add_window(Default::default(), |_| ScreenCaptureView);
119 });
120}
121
122struct ScreenCaptureView;
123
124impl gpui::Entity for ScreenCaptureView {
125 type Event = ();
126}
127
128impl gpui::View for ScreenCaptureView {
129 fn ui_name() -> &'static str {
130 "View"
131 }
132
133 fn render(&mut self, _: &mut gpui::RenderContext<Self>) -> gpui::ElementBox {
134 Empty::new().boxed()
135 }
136}
137
138pub unsafe fn string_from_objc(string: id) -> String {
139 let len = msg_send![string, lengthOfBytesUsingEncoding: NSUTF8StringEncoding];
140 let bytes = string.UTF8String() as *const u8;
141 str::from_utf8(slice::from_raw_parts(bytes, len))
142 .unwrap()
143 .to_string()
144}
145
146extern "C" fn did_stop_with_error(this: &Object, _: Sel, stream: id, error: id) {
147 println!("did_stop_with_error");
148}
149
150extern "C" fn sample_output(this: &Object, _: Sel, stream: id, buffer: id, kind: NSInteger) {
151 println!("sample output");
152}
153
154fn quit(_: &Quit, cx: &mut gpui::MutableAppContext) {
155 cx.platform().quit();
156}