1use crate::{
2 item::ItemEvent, persistence::model::ItemId, Item, ItemNavHistory, Pane, Workspace, WorkspaceId,
3};
4use anyhow::{anyhow, Result};
5use call::participant::{Frame, RemoteVideoTrack};
6use client::{proto::PeerId, User};
7use futures::StreamExt;
8use gpui::{
9 elements::*,
10 geometry::{rect::RectF, vector::vec2f},
11 AppContext, Entity, ModelHandle, MouseButton, RenderContext, Task, View, ViewContext,
12 ViewHandle, WeakViewHandle,
13};
14use project::Project;
15use settings::Settings;
16use std::{
17 path::PathBuf,
18 sync::{Arc, Weak},
19};
20
21pub enum Event {
22 Close,
23}
24
25pub struct SharedScreen {
26 track: Weak<RemoteVideoTrack>,
27 frame: Option<Frame>,
28 pub peer_id: PeerId,
29 user: Arc<User>,
30 nav_history: Option<ItemNavHistory>,
31 _maintain_frame: Task<()>,
32}
33
34impl SharedScreen {
35 pub fn new(
36 track: &Arc<RemoteVideoTrack>,
37 peer_id: PeerId,
38 user: Arc<User>,
39 cx: &mut ViewContext<Self>,
40 ) -> Self {
41 let mut frames = track.frames();
42 Self {
43 track: Arc::downgrade(track),
44 frame: None,
45 peer_id,
46 user,
47 nav_history: Default::default(),
48 _maintain_frame: cx.spawn(|this, mut cx| async move {
49 while let Some(frame) = frames.next().await {
50 this.update(&mut cx, |this, cx| {
51 this.frame = Some(frame);
52 cx.notify();
53 })
54 }
55 this.update(&mut cx, |_, cx| cx.emit(Event::Close));
56 }),
57 }
58 }
59}
60
61impl Entity for SharedScreen {
62 type Event = Event;
63}
64
65impl View for SharedScreen {
66 fn ui_name() -> &'static str {
67 "SharedScreen"
68 }
69
70 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
71 enum Focus {}
72
73 let frame = self.frame.clone();
74 MouseEventHandler::<Focus>::new(0, cx, |_, cx| {
75 Canvas::new(move |bounds, _, cx| {
76 if let Some(frame) = frame.clone() {
77 let size = constrain_size_preserving_aspect_ratio(
78 bounds.size(),
79 vec2f(frame.width() as f32, frame.height() as f32),
80 );
81 let origin = bounds.origin() + (bounds.size() / 2.) - size / 2.;
82 cx.scene.push_surface(gpui::mac::Surface {
83 bounds: RectF::new(origin, size),
84 image_buffer: frame.image(),
85 });
86 }
87 })
88 .contained()
89 .with_style(cx.global::<Settings>().theme.shared_screen)
90 .boxed()
91 })
92 .on_down(MouseButton::Left, |_, cx| cx.focus_parent_view())
93 .boxed()
94 }
95}
96
97impl Item for SharedScreen {
98 fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
99 if let Some(nav_history) = self.nav_history.as_ref() {
100 nav_history.push::<()>(None, cx);
101 }
102 }
103
104 fn tab_content(
105 &self,
106 _: Option<usize>,
107 style: &theme::Tab,
108 _: &AppContext,
109 ) -> gpui::ElementBox {
110 Flex::row()
111 .with_child(
112 Svg::new("icons/disable_screen_sharing_12.svg")
113 .with_color(style.label.text.color)
114 .constrained()
115 .with_width(style.icon_width)
116 .aligned()
117 .contained()
118 .with_margin_right(style.spacing)
119 .boxed(),
120 )
121 .with_child(
122 Label::new(
123 format!("{}'s screen", self.user.github_login),
124 style.label.clone(),
125 )
126 .aligned()
127 .boxed(),
128 )
129 .boxed()
130 }
131
132 fn for_each_project_item(&self, _: &AppContext, _: &mut dyn FnMut(usize, &dyn project::Item)) {}
133
134 fn is_singleton(&self, _: &AppContext) -> bool {
135 false
136 }
137
138 fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
139 self.nav_history = Some(history);
140 }
141
142 fn clone_on_split(
143 &self,
144 _workspace_id: WorkspaceId,
145 cx: &mut ViewContext<Self>,
146 ) -> Option<Self> {
147 let track = self.track.upgrade()?;
148 Some(Self::new(&track, self.peer_id, self.user.clone(), cx))
149 }
150
151 fn can_save(&self, _: &AppContext) -> bool {
152 false
153 }
154
155 fn save(
156 &mut self,
157 _: ModelHandle<project::Project>,
158 _: &mut ViewContext<Self>,
159 ) -> Task<Result<()>> {
160 Task::ready(Err(anyhow!("Item::save called on SharedScreen")))
161 }
162
163 fn save_as(
164 &mut self,
165 _: ModelHandle<project::Project>,
166 _: PathBuf,
167 _: &mut ViewContext<Self>,
168 ) -> Task<Result<()>> {
169 Task::ready(Err(anyhow!("Item::save_as called on SharedScreen")))
170 }
171
172 fn reload(
173 &mut self,
174 _: ModelHandle<project::Project>,
175 _: &mut ViewContext<Self>,
176 ) -> Task<Result<()>> {
177 Task::ready(Err(anyhow!("Item::reload called on SharedScreen")))
178 }
179
180 fn to_item_events(event: &Self::Event) -> Vec<ItemEvent> {
181 match event {
182 Event::Close => vec![ItemEvent::CloseItem],
183 }
184 }
185
186 fn serialized_item_kind() -> Option<&'static str> {
187 None
188 }
189
190 fn deserialize(
191 _project: ModelHandle<Project>,
192 _workspace: WeakViewHandle<Workspace>,
193 _workspace_id: WorkspaceId,
194 _item_id: ItemId,
195 _cx: &mut ViewContext<Pane>,
196 ) -> Task<Result<ViewHandle<Self>>> {
197 unreachable!("Shared screen can not be deserialized")
198 }
199}