From 773f569385fabae114fd83edbe1bfbd7d3cdb75c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 19 Oct 2022 10:19:20 +0200 Subject: [PATCH] Add control to toggle screen-sharing --- assets/icons/disable_screen_sharing_12.svg | 3 + assets/icons/enable_screen_sharing_12.svg | 3 + crates/call/src/call.rs | 2 - crates/call/src/room.rs | 47 +++++++++++--- crates/collab_ui/src/collab_titlebar_item.rs | 62 ++++++++++++++++++- .../Sources/LiveKitBridge/LiveKitBridge.swift | 7 +++ crates/live_kit_client/src/live_kit_client.rs | 7 +++ crates/theme/src/theme.rs | 1 + styles/src/styleTree/workspace.ts | 11 ++++ 9 files changed, 131 insertions(+), 12 deletions(-) create mode 100644 assets/icons/disable_screen_sharing_12.svg create mode 100644 assets/icons/enable_screen_sharing_12.svg diff --git a/assets/icons/disable_screen_sharing_12.svg b/assets/icons/disable_screen_sharing_12.svg new file mode 100644 index 0000000000..c2a4edd45b --- /dev/null +++ b/assets/icons/disable_screen_sharing_12.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/enable_screen_sharing_12.svg b/assets/icons/enable_screen_sharing_12.svg new file mode 100644 index 0000000000..6ae37649d2 --- /dev/null +++ b/assets/icons/enable_screen_sharing_12.svg @@ -0,0 +1,3 @@ + + + diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs index 056c6f1260..6b06d04375 100644 --- a/crates/call/src/call.rs +++ b/crates/call/src/call.rs @@ -132,8 +132,6 @@ impl ActiveCall { Room::create(recipient_user_id, initial_project, client, user_store, cx) }) .await?; - room.update(&mut cx, |room, cx| room.share_screen(cx)) - .await?; this.update(&mut cx, |this, cx| this.set_room(Some(room), cx)); }; diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 86946b7974..4553772095 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -606,6 +606,12 @@ impl Room { }) } + pub fn is_screen_sharing(&self) -> bool { + self.live_kit + .as_ref() + .map_or(false, |live_kit| live_kit.screen_track.is_some()) + } + pub fn share_screen(&mut self, cx: &mut ModelContext) -> Task> { if self.status.is_offline() { return Task::ready(Err(anyhow!("room is offline"))); @@ -617,22 +623,49 @@ impl Room { .first() .ok_or_else(|| anyhow!("no display found"))?; let track = LocalVideoTrack::screen_share_for_display(&display); - let publication = this - .upgrade(&cx)? + .upgrade(&cx) + .ok_or_else(|| anyhow!("room was dropped"))? .read_with(&cx, |this, _| { this.live_kit .as_ref() .map(|live_kit| live_kit.room.publish_video_track(&track)) - })? + }) + .ok_or_else(|| anyhow!("live-kit was not initialized"))? .await?; - this.upgrade(&cx)?.update(cx, |this, _| { - this.live_kit.as_mut()?.screen_track = Some(publication); - Some(()) - }) + this.upgrade(&cx) + .ok_or_else(|| anyhow!("room was dropped"))? + .update(&mut cx, |this, cx| { + let live_kit = this + .live_kit + .as_mut() + .ok_or_else(|| anyhow!("live-kit was not initialized"))?; + live_kit.screen_track = Some(publication); + cx.notify(); + Ok(()) + }) }) } + + pub fn unshare_screen(&mut self, cx: &mut ModelContext) -> Result<()> { + if self.status.is_offline() { + return Err(anyhow!("room is offline")); + } + + let live_kit = self + .live_kit + .as_mut() + .ok_or_else(|| anyhow!("live-kit was not initialized"))?; + let track = live_kit + .screen_track + .take() + .ok_or_else(|| anyhow!("screen was not shared"))?; + live_kit.room.unpublish_track(track); + cx.notify(); + + Ok(()) + } } struct LiveKitRoom { diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index 1279d30437..43d9bc0dbf 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -10,17 +10,21 @@ use gpui::{ geometry::{rect::RectF, vector::vec2f, PathBuilder}, json::{self, ToJson}, Border, CursorStyle, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, - Subscription, View, ViewContext, ViewHandle, WeakViewHandle, + Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, }; use settings::Settings; use std::ops::Range; use theme::Theme; use workspace::{FollowNextCollaborator, JoinProject, ToggleFollow, Workspace}; -actions!(collab, [ToggleCollaborationMenu, ShareProject]); +actions!( + collab, + [ToggleCollaborationMenu, ToggleScreenSharing, ShareProject] +); pub fn init(cx: &mut MutableAppContext) { cx.add_action(CollabTitlebarItem::toggle_contacts_popover); + cx.add_action(CollabTitlebarItem::toggle_screen_sharing); cx.add_action(CollabTitlebarItem::share_project); } @@ -48,10 +52,12 @@ impl View for CollabTitlebarItem { }; let theme = cx.global::().theme.clone(); - let project = workspace.read(cx).project().read(cx); let mut container = Flex::row(); + container.add_children(self.render_toggle_screen_sharing_button(&theme, cx)); + if workspace.read(cx).client().status().borrow().is_connected() { + let project = workspace.read(cx).project().read(cx); if project.is_shared() || project.is_remote() || ActiveCall::global(cx).read(cx).room().is_none() @@ -169,6 +175,19 @@ impl CollabTitlebarItem { cx.notify(); } + pub fn toggle_screen_sharing(&mut self, _: &ToggleScreenSharing, cx: &mut ViewContext) { + if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() { + let toggle_screen_sharing = room.update(cx, |room, cx| { + if room.is_screen_sharing() { + Task::ready(room.unshare_screen(cx)) + } else { + room.share_screen(cx) + } + }); + toggle_screen_sharing.detach_and_log_err(cx); + } + } + fn render_toggle_contacts_button( &self, theme: &Theme, @@ -237,6 +256,43 @@ impl CollabTitlebarItem { .boxed() } + fn render_toggle_screen_sharing_button( + &self, + theme: &Theme, + cx: &mut RenderContext, + ) -> Option { + let active_call = ActiveCall::global(cx); + let room = active_call.read(cx).room().cloned()?; + let icon = if room.read(cx).is_screen_sharing() { + "icons/disable_screen_sharing_12.svg" + } else { + "icons/enable_screen_sharing_12.svg" + }; + let titlebar = &theme.workspace.titlebar; + Some( + MouseEventHandler::::new(0, cx, |state, _| { + let style = titlebar.call_control.style_for(state, false); + Svg::new(icon) + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .constrained() + .with_width(style.button_width) + .with_height(style.button_width) + .contained() + .with_style(style.container) + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_action(ToggleScreenSharing); + }) + .aligned() + .boxed(), + ) + } + fn render_share_button(&self, theme: &Theme, cx: &mut RenderContext) -> ElementBox { enum Share {} diff --git a/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift b/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift index 7488eb9444..8bfa98b522 100644 --- a/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift +++ b/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift @@ -95,6 +95,13 @@ public func LKRoomPublishVideoTrack(room: UnsafeRawPointer, track: UnsafeRawPoin } } +@_cdecl("LKRoomUnpublishTrack") +public func LKRoomUnpublishTrack(room: UnsafeRawPointer, publication: UnsafeRawPointer) { + let room = Unmanaged.fromOpaque(room).takeUnretainedValue() + let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue() + room.localParticipant?.unpublish(publication: publication) +} + @_cdecl("LKRoomVideoTracksForRemoteParticipant") public func LKRoomVideoTracksForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? { let room = Unmanaged.fromOpaque(room).takeUnretainedValue() diff --git a/crates/live_kit_client/src/live_kit_client.rs b/crates/live_kit_client/src/live_kit_client.rs index b3724e91db..6532ce110a 100644 --- a/crates/live_kit_client/src/live_kit_client.rs +++ b/crates/live_kit_client/src/live_kit_client.rs @@ -48,6 +48,7 @@ extern "C" { callback: extern "C" fn(*mut c_void, *mut c_void, CFStringRef), callback_data: *mut c_void, ); + fn LKRoomUnpublishTrack(room: *const c_void, publication: *const c_void); fn LKRoomVideoTracksForRemoteParticipant( room: *const c_void, participant_id: CFStringRef, @@ -134,6 +135,12 @@ impl Room { async { rx.await.unwrap().context("error publishing video track") } } + pub fn unpublish_track(&self, publication: LocalTrackPublication) { + unsafe { + LKRoomUnpublishTrack(self.native_room, publication.0); + } + } + pub fn remote_video_tracks(&self, participant_id: &str) -> Vec> { unsafe { let tracks = LKRoomVideoTracksForRemoteParticipant( diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 51aa3232d4..e517f8ced0 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -79,6 +79,7 @@ pub struct Titlebar { pub sign_in_prompt: Interactive, pub outdated_warning: ContainedText, pub share_button: Interactive, + pub call_control: Interactive, pub toggle_contacts_button: Interactive, pub toggle_contacts_badge: ContainerStyle, } diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index c8d32ae1a8..0defabbd7f 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -138,7 +138,18 @@ export default function workspace(theme: Theme) { }, cornerRadius: 6, }, + callControl: { + cornerRadius: 6, + color: iconColor(theme, "secondary"), + iconWidth: 12, + buttonWidth: 20, + hover: { + background: backgroundColor(theme, "on300", "hovered"), + color: iconColor(theme, "active"), + }, + }, toggleContactsButton: { + margin: { left: 6 }, cornerRadius: 6, color: iconColor(theme, "secondary"), iconWidth: 8,