1use futures::StreamExt;
2use gpui::{
3 actions,
4 elements::{Canvas, *},
5 keymap::Binding,
6 platform::current::Surface,
7 Menu, MenuItem, ViewContext,
8};
9use live_kit_client::{LocalVideoTrack, Room};
10use log::LevelFilter;
11use media::core_video::CVImageBuffer;
12use postage::watch;
13use simplelog::SimpleLogger;
14use std::sync::Arc;
15
16actions!(capture, [Quit]);
17
18fn main() {
19 SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
20
21 gpui::App::new(()).unwrap().run(|cx| {
22 cx.platform().activate(true);
23 cx.add_global_action(quit);
24
25 cx.add_bindings([Binding::new("cmd-q", Quit, None)]);
26 cx.set_menus(vec![Menu {
27 name: "Zed",
28 items: vec![MenuItem::Action {
29 name: "Quit",
30 action: Box::new(Quit),
31 }],
32 }]);
33
34 let live_kit_url = std::env::var("LIVE_KIT_URL").unwrap();
35 let live_kit_key = std::env::var("LIVE_KIT_KEY").unwrap();
36 let live_kit_secret = std::env::var("LIVE_KIT_SECRET").unwrap();
37
38 cx.spawn(|mut cx| async move {
39 let user1_token = live_kit_server::token::create(
40 &live_kit_key,
41 &live_kit_secret,
42 "test-room",
43 "test-participant-1",
44 )
45 .unwrap();
46 let room1 = Room::new();
47 room1.connect(&live_kit_url, &user1_token).await.unwrap();
48
49 let user2_token = live_kit_server::token::create(
50 &live_kit_key,
51 &live_kit_secret,
52 "test-room",
53 "test-participant-2",
54 )
55 .unwrap();
56 let room2 = Room::new();
57 room2.connect(&live_kit_url, &user2_token).await.unwrap();
58 cx.add_window(Default::default(), |cx| ScreenCaptureView::new(room2, cx));
59
60 let display_sources = live_kit_client::display_sources().await.unwrap();
61 let track = LocalVideoTrack::screen_share_for_display(display_sources.first().unwrap());
62 room1.publish_video_track(&track).await.unwrap();
63 })
64 .detach();
65 });
66}
67
68struct ScreenCaptureView {
69 image_buffer: Option<CVImageBuffer>,
70 _room: Arc<Room>,
71}
72
73impl gpui::Entity for ScreenCaptureView {
74 type Event = ();
75}
76
77impl ScreenCaptureView {
78 pub fn new(room: Arc<Room>, cx: &mut ViewContext<Self>) -> Self {
79 let mut remote_video_tracks = room.remote_video_tracks();
80 cx.spawn_weak(|this, mut cx| async move {
81 if let Some(video_track) = remote_video_tracks.next().await {
82 let (mut frames_tx, mut frames_rx) = watch::channel_with(None);
83 video_track.add_renderer(move |frame| *frames_tx.borrow_mut() = Some(frame));
84
85 while let Some(frame) = frames_rx.next().await {
86 if let Some(this) = this.upgrade(&cx) {
87 this.update(&mut cx, |this, cx| {
88 this.image_buffer = frame;
89 cx.notify();
90 });
91 } else {
92 break;
93 }
94 }
95 }
96 })
97 .detach();
98
99 Self {
100 image_buffer: None,
101 _room: room,
102 }
103 }
104}
105
106impl gpui::View for ScreenCaptureView {
107 fn ui_name() -> &'static str {
108 "View"
109 }
110
111 fn render(&mut self, _: &mut gpui::RenderContext<Self>) -> gpui::ElementBox {
112 let image_buffer = self.image_buffer.clone();
113 let canvas = Canvas::new(move |bounds, _, cx| {
114 if let Some(image_buffer) = image_buffer.clone() {
115 cx.scene.push_surface(Surface {
116 bounds,
117 image_buffer,
118 });
119 }
120 });
121
122 if let Some(image_buffer) = self.image_buffer.as_ref() {
123 canvas
124 .constrained()
125 .with_width(image_buffer.width() as f32)
126 .with_height(image_buffer.height() as f32)
127 .aligned()
128 .boxed()
129 } else {
130 canvas.boxed()
131 }
132 }
133}
134
135fn quit(_: &Quit, cx: &mut gpui::MutableAppContext) {
136 cx.platform().quit();
137}