shared_screen.rs

  1#[cfg(any(
  2    all(
  3        target_os = "macos",
  4        feature = "livekit-cross-platform",
  5        not(feature = "livekit-macos"),
  6    ),
  7    all(not(target_os = "macos"), feature = "livekit-cross-platform"),
  8))]
  9mod cross_platform {
 10    use crate::{
 11        item::{Item, ItemEvent},
 12        ItemNavHistory, WorkspaceId,
 13    };
 14    use call::{RemoteVideoTrack, RemoteVideoTrackView};
 15    use client::{proto::PeerId, User};
 16    use gpui::{
 17        div, AppContext, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
 18        ParentElement, Render, SharedString, Styled, View, ViewContext, VisualContext,
 19        WindowContext,
 20    };
 21    use std::sync::Arc;
 22    use ui::{prelude::*, Icon, IconName};
 23
 24    pub enum Event {
 25        Close,
 26    }
 27
 28    pub struct SharedScreen {
 29        pub peer_id: PeerId,
 30        user: Arc<User>,
 31        nav_history: Option<ItemNavHistory>,
 32        view: View<RemoteVideoTrackView>,
 33        focus: FocusHandle,
 34    }
 35
 36    impl SharedScreen {
 37        pub fn new(
 38            track: RemoteVideoTrack,
 39            peer_id: PeerId,
 40            user: Arc<User>,
 41            cx: &mut ViewContext<Self>,
 42        ) -> Self {
 43            let view = cx.new_view(|cx| RemoteVideoTrackView::new(track.clone(), cx));
 44            cx.subscribe(&view, |_, _, ev, cx| match ev {
 45                call::RemoteVideoTrackViewEvent::Close => cx.emit(Event::Close),
 46            })
 47            .detach();
 48            Self {
 49                view,
 50                peer_id,
 51                user,
 52                nav_history: Default::default(),
 53                focus: cx.focus_handle(),
 54            }
 55        }
 56    }
 57
 58    impl EventEmitter<Event> for SharedScreen {}
 59
 60    impl FocusableView for SharedScreen {
 61        fn focus_handle(&self, _: &AppContext) -> FocusHandle {
 62            self.focus.clone()
 63        }
 64    }
 65    impl Render for SharedScreen {
 66        fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
 67            div()
 68                .bg(cx.theme().colors().editor_background)
 69                .track_focus(&self.focus)
 70                .key_context("SharedScreen")
 71                .size_full()
 72                .child(self.view.clone())
 73        }
 74    }
 75
 76    impl Item for SharedScreen {
 77        type Event = Event;
 78
 79        fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
 80            Some(format!("{}'s screen", self.user.github_login).into())
 81        }
 82
 83        fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
 84            if let Some(nav_history) = self.nav_history.as_mut() {
 85                nav_history.push::<()>(None, cx);
 86            }
 87        }
 88
 89        fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
 90            Some(Icon::new(IconName::Screen))
 91        }
 92
 93        fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
 94            Some(format!("{}'s screen", self.user.github_login).into())
 95        }
 96
 97        fn telemetry_event_text(&self) -> Option<&'static str> {
 98            None
 99        }
100
101        fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
102            self.nav_history = Some(history);
103        }
104
105        fn clone_on_split(
106            &self,
107            _workspace_id: Option<WorkspaceId>,
108            cx: &mut ViewContext<Self>,
109        ) -> Option<View<Self>> {
110            Some(cx.new_view(|cx| Self {
111                view: self.view.update(cx, |view, cx| view.clone(cx)),
112                peer_id: self.peer_id,
113                user: self.user.clone(),
114                nav_history: Default::default(),
115                focus: cx.focus_handle(),
116            }))
117        }
118
119        fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
120            match event {
121                Event::Close => f(ItemEvent::CloseItem),
122            }
123        }
124    }
125}
126
127#[cfg(any(
128    all(
129        target_os = "macos",
130        feature = "livekit-cross-platform",
131        not(feature = "livekit-macos"),
132    ),
133    all(not(target_os = "macos"), feature = "livekit-cross-platform"),
134))]
135pub use cross_platform::*;
136
137#[cfg(any(
138    all(target_os = "macos", feature = "livekit-macos"),
139    all(
140        not(target_os = "macos"),
141        feature = "livekit-macos",
142        not(feature = "livekit-cross-platform")
143    )
144))]
145mod macos {
146    use crate::{
147        item::{Item, ItemEvent},
148        ItemNavHistory, WorkspaceId,
149    };
150    use anyhow::Result;
151    use call::participant::{Frame, RemoteVideoTrack};
152    use client::{proto::PeerId, User};
153    use futures::StreamExt;
154    use gpui::{
155        div, surface, AppContext, EventEmitter, FocusHandle, FocusableView, InteractiveElement,
156        ParentElement, Render, SharedString, Styled, Task, View, ViewContext, VisualContext,
157        WindowContext,
158    };
159    use std::sync::{Arc, Weak};
160    use ui::{prelude::*, Icon, IconName};
161
162    pub enum Event {
163        Close,
164    }
165
166    pub struct SharedScreen {
167        track: Weak<RemoteVideoTrack>,
168        frame: Option<Frame>,
169        pub peer_id: PeerId,
170        user: Arc<User>,
171        nav_history: Option<ItemNavHistory>,
172        _maintain_frame: Task<Result<()>>,
173        focus: FocusHandle,
174    }
175
176    impl SharedScreen {
177        pub fn new(
178            track: Arc<RemoteVideoTrack>,
179            peer_id: PeerId,
180            user: Arc<User>,
181            cx: &mut ViewContext<Self>,
182        ) -> Self {
183            cx.focus_handle();
184            let mut frames = track.frames();
185            Self {
186                track: Arc::downgrade(&track),
187                frame: None,
188                peer_id,
189                user,
190                nav_history: Default::default(),
191                _maintain_frame: cx.spawn(|this, mut cx| async move {
192                    while let Some(frame) = frames.next().await {
193                        this.update(&mut cx, |this, cx| {
194                            this.frame = Some(frame);
195                            cx.notify();
196                        })?;
197                    }
198                    this.update(&mut cx, |_, cx| cx.emit(Event::Close))?;
199                    Ok(())
200                }),
201                focus: cx.focus_handle(),
202            }
203        }
204    }
205
206    impl EventEmitter<Event> for SharedScreen {}
207
208    impl FocusableView for SharedScreen {
209        fn focus_handle(&self, _: &AppContext) -> FocusHandle {
210            self.focus.clone()
211        }
212    }
213    impl Render for SharedScreen {
214        fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
215            div()
216                .bg(cx.theme().colors().editor_background)
217                .track_focus(&self.focus)
218                .key_context("SharedScreen")
219                .size_full()
220                .children(
221                    self.frame
222                        .as_ref()
223                        .map(|frame| surface(frame.image()).size_full()),
224                )
225        }
226    }
227
228    impl Item for SharedScreen {
229        type Event = Event;
230
231        fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
232            Some(format!("{}'s screen", self.user.github_login).into())
233        }
234
235        fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
236            if let Some(nav_history) = self.nav_history.as_mut() {
237                nav_history.push::<()>(None, cx);
238            }
239        }
240
241        fn tab_icon(&self, _cx: &WindowContext) -> Option<Icon> {
242            Some(Icon::new(IconName::Screen))
243        }
244
245        fn tab_content_text(&self, _cx: &WindowContext) -> Option<SharedString> {
246            Some(format!("{}'s screen", self.user.github_login).into())
247        }
248
249        fn telemetry_event_text(&self) -> Option<&'static str> {
250            None
251        }
252
253        fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
254            self.nav_history = Some(history);
255        }
256
257        fn clone_on_split(
258            &self,
259            _workspace_id: Option<WorkspaceId>,
260            cx: &mut ViewContext<Self>,
261        ) -> Option<View<Self>> {
262            let track = self.track.upgrade()?;
263            Some(cx.new_view(|cx| Self::new(track, self.peer_id, self.user.clone(), cx)))
264        }
265
266        fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
267            match event {
268                Event::Close => f(ItemEvent::CloseItem),
269            }
270        }
271    }
272}
273
274#[cfg(any(
275    all(target_os = "macos", feature = "livekit-macos"),
276    all(
277        not(target_os = "macos"),
278        feature = "livekit-macos",
279        not(feature = "livekit-cross-platform")
280    )
281))]
282pub use macos::*;