main.rs

  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}