main.rs

  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}