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