Mute mics by default

Fix bug when file ends in line with 1 more digit displayed than previous lines
Remove stale UI elements from voice call development
This commit is contained in:
Mikayla Maki 2023-07-19 12:34:24 -07:00
parent 07dc82409b
commit 5ceb258b3e
No known key found for this signature in database
13 changed files with 147 additions and 51 deletions

6
Cargo.lock generated
View File

@ -1052,6 +1052,10 @@ dependencies = [
"media",
"postage",
"project",
"schemars",
"serde",
"serde_derive",
"serde_json",
"settings",
"util",
]
@ -6470,7 +6474,7 @@ name = "search"
version = "0.1.0"
dependencies = [
"anyhow",
"bitflags 1.3.2",
"bitflags",
"client",
"collections",
"editor",

View File

@ -66,6 +66,11 @@
// 3. Draw all invisible symbols:
// "all"
"show_whitespaces": "selection",
// Settings related to calls in Zed
"calls": {
// Join calls with the microphone muted by default
"mute_on_join": true
},
// Scrollbar related settings
"scrollbar": {
// When to show the scrollbar in the editor.

View File

@ -36,6 +36,10 @@ anyhow.workspace = true
async-broadcast = "0.4"
futures.workspace = true
postage.workspace = true
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
serde_derive.workspace = true
[dev-dependencies]
client = { path = "../client", features = ["test-support"] }

View File

@ -1,9 +1,11 @@
pub mod participant;
pub mod room;
pub mod call_settings;
use std::sync::Arc;
use anyhow::{anyhow, Result};
use call_settings::CallSettings;
use client::{proto, ClickhouseEvent, Client, TelemetrySettings, TypedEnvelope, User, UserStore};
use collections::HashSet;
use futures::{future::Shared, FutureExt};
@ -19,6 +21,8 @@ pub use participant::ParticipantLocation;
pub use room::Room;
pub fn init(client: Arc<Client>, user_store: ModelHandle<UserStore>, cx: &mut AppContext) {
settings::register::<CallSettings>(cx);
let active_call = cx.add_model(|cx| ActiveCall::new(client, user_store, cx));
cx.set_global(active_call);
}

View File

@ -0,0 +1,27 @@
use schemars::JsonSchema;
use serde_derive::{Serialize, Deserialize};
use settings::Setting;
#[derive(Deserialize, Debug)]
pub struct CallSettings {
pub mute_on_join: bool,
}
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
pub struct CallSettingsContent {
pub mute_on_join: Option<bool>,
}
impl Setting for CallSettings {
const KEY: Option<&'static str> = Some("calls");
type FileContent = CallSettingsContent;
fn load(
default_value: &Self::FileContent,
user_values: &[&Self::FileContent],
_: &gpui::AppContext,
) -> anyhow::Result<Self> {
Self::load_via_json_merge(default_value, user_values)
}
}

View File

@ -1,4 +1,5 @@
use crate::{
call_settings::CallSettings,
participant::{LocalParticipant, ParticipantLocation, RemoteParticipant, RemoteVideoTrack},
IncomingCall,
};
@ -19,7 +20,7 @@ use live_kit_client::{
};
use postage::stream::Stream;
use project::Project;
use std::{future::Future, mem, pin::Pin, sync::Arc, time::Duration};
use std::{future::Future, mem, pin::Pin, sync::Arc, time::Duration, panic::Location};
use util::{post_inc, ResultExt, TryFutureExt};
pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30);
@ -153,8 +154,10 @@ impl Room {
cx.spawn(|this, mut cx| async move {
connect.await?;
this.update(&mut cx, |this, cx| this.share_microphone(cx))
.await?;
if !cx.read(|cx| settings::get::<CallSettings>(cx).mute_on_join) {
this.update(&mut cx, |this, cx| this.share_microphone(cx))
.await?;
}
anyhow::Ok(())
})
@ -656,7 +659,7 @@ impl Room {
peer_id,
projects: participant.projects,
location,
muted: false,
muted: true,
speaking: false,
video_tracks: Default::default(),
audio_tracks: Default::default(),
@ -670,6 +673,10 @@ impl Room {
live_kit.room.remote_video_tracks(&user.id.to_string());
let audio_tracks =
live_kit.room.remote_audio_tracks(&user.id.to_string());
let publications = live_kit
.room
.remote_audio_track_publications(&user.id.to_string());
for track in video_tracks {
this.remote_video_track_updated(
RemoteVideoTrackUpdate::Subscribed(track),
@ -677,9 +684,15 @@ impl Room {
)
.log_err();
}
for track in audio_tracks {
for (track, publication) in
audio_tracks.iter().zip(publications.iter())
{
this.remote_audio_track_updated(
RemoteAudioTrackUpdate::Subscribed(track),
RemoteAudioTrackUpdate::Subscribed(
track.clone(),
publication.clone(),
),
cx,
)
.log_err();
@ -819,8 +832,8 @@ impl Room {
cx.notify();
}
RemoteAudioTrackUpdate::MuteChanged { track_id, muted } => {
let mut found = false;
for participant in &mut self.remote_participants.values_mut() {
let mut found = false;
for track in participant.audio_tracks.values() {
if track.sid() == track_id {
found = true;
@ -832,16 +845,20 @@ impl Room {
break;
}
}
cx.notify();
}
RemoteAudioTrackUpdate::Subscribed(track) => {
RemoteAudioTrackUpdate::Subscribed(track, publication) => {
let user_id = track.publisher_id().parse()?;
let track_id = track.sid().to_string();
let participant = self
.remote_participants
.get_mut(&user_id)
.ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?;
participant.audio_tracks.insert(track_id.clone(), track);
participant.muted = publication.is_muted();
cx.emit(Event::RemoteAudioTracksChanged {
participant_id: participant.peer_id,
});
@ -1053,7 +1070,7 @@ impl Room {
self.live_kit
.as_ref()
.and_then(|live_kit| match &live_kit.microphone_track {
LocalTrack::None => None,
LocalTrack::None => Some(true),
LocalTrack::Pending { muted, .. } => Some(*muted),
LocalTrack::Published { muted, .. } => Some(*muted),
})
@ -1070,7 +1087,9 @@ impl Room {
self.live_kit.as_ref().map(|live_kit| live_kit.deafened)
}
#[track_caller]
pub fn share_microphone(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
dbg!(Location::caller());
if self.status.is_offline() {
return Task::ready(Err(anyhow!("room is offline")));
} else if self.is_sharing_mic() {
@ -1244,6 +1263,10 @@ impl Room {
pub fn toggle_mute(&mut self, cx: &mut ModelContext<Self>) -> Result<Task<Result<()>>> {
let should_mute = !self.is_muted();
if let Some(live_kit) = self.live_kit.as_mut() {
if matches!(live_kit.microphone_track, LocalTrack::None) {
return Ok(self.share_microphone(cx));
}
let (ret_task, old_muted) = live_kit.set_mute(should_mute, cx)?;
live_kit.muted_by_user = should_mute;

View File

@ -652,10 +652,10 @@ impl CollabTitlebarItem {
let is_muted = room.read(cx).is_muted();
if is_muted {
icon = "icons/radix/mic-mute.svg";
tooltip = "Unmute microphone\nRight click for options";
tooltip = "Unmute microphone";
} else {
icon = "icons/radix/mic.svg";
tooltip = "Mute microphone\nRight click for options";
tooltip = "Mute microphone";
}
let titlebar = &theme.titlebar;
@ -705,10 +705,10 @@ impl CollabTitlebarItem {
let is_deafened = room.read(cx).is_deafened().unwrap_or(false);
if is_deafened {
icon = "icons/radix/speaker-off.svg";
tooltip = "Unmute speakers\nRight click for options";
tooltip = "Unmute speakers";
} else {
icon = "icons/radix/speaker-loud.svg";
tooltip = "Mute speakers\nRight click for options";
tooltip = "Mute speakers";
}
let titlebar = &theme.titlebar;

View File

@ -18,13 +18,7 @@ use workspace::AppState;
actions!(
collab,
[
ToggleScreenSharing,
ToggleMute,
ToggleDeafen,
LeaveCall,
ShareMicrophone
]
[ToggleScreenSharing, ToggleMute, ToggleDeafen, LeaveCall]
);
pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
@ -40,7 +34,6 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut AppContext) {
cx.add_global_action(toggle_screen_sharing);
cx.add_global_action(toggle_mute);
cx.add_global_action(toggle_deafen);
cx.add_global_action(share_microphone);
}
pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) {
@ -85,10 +78,3 @@ pub fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) {
.log_err();
}
}
pub fn share_microphone(_: &ShareMicrophone, cx: &mut AppContext) {
if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
room.update(cx, Room::share_microphone)
.detach_and_log_err(cx)
}
}

View File

@ -1311,7 +1311,7 @@ impl EditorElement {
}
fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &ViewContext<Editor>) -> f32 {
let digit_count = (snapshot.max_buffer_row() as f32).log10().floor() as usize + 1;
let digit_count = (snapshot.max_buffer_row() as f32 + 1.).log10().floor() as usize + 1;
let style = &self.style;
cx.text_layout_cache()

View File

@ -6,7 +6,7 @@ import ScreenCaptureKit
class LKRoomDelegate: RoomDelegate {
var data: UnsafeRawPointer
var onDidDisconnect: @convention(c) (UnsafeRawPointer) -> Void
var onDidSubscribeToRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void
var onDidSubscribeToRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer, UnsafeRawPointer) -> Void
var onDidUnsubscribeFromRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void
var onMuteChangedFromRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void
var onActiveSpeakersChanged: @convention(c) (UnsafeRawPointer, CFArray) -> Void
@ -16,7 +16,7 @@ class LKRoomDelegate: RoomDelegate {
init(
data: UnsafeRawPointer,
onDidDisconnect: @escaping @convention(c) (UnsafeRawPointer) -> Void,
onDidSubscribeToRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void,
onDidSubscribeToRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer, UnsafeRawPointer) -> Void,
onDidUnsubscribeFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void,
onMuteChangedFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void,
onActiveSpeakersChanged: @convention(c) (UnsafeRawPointer, CFArray) -> Void,
@ -43,7 +43,7 @@ class LKRoomDelegate: RoomDelegate {
if track.kind == .video {
self.onDidSubscribeToRemoteVideoTrack(self.data, participant.identity as CFString, track.sid! as CFString, Unmanaged.passUnretained(track).toOpaque())
} else if track.kind == .audio {
self.onDidSubscribeToRemoteAudioTrack(self.data, participant.identity as CFString, track.sid! as CFString, Unmanaged.passUnretained(track).toOpaque())
self.onDidSubscribeToRemoteAudioTrack(self.data, participant.identity as CFString, track.sid! as CFString, Unmanaged.passUnretained(track).toOpaque(), Unmanaged.passUnretained(publication).toOpaque())
}
}
@ -52,12 +52,12 @@ class LKRoomDelegate: RoomDelegate {
self.onMuteChangedFromRemoteAudioTrack(self.data, publication.sid as CFString, muted)
}
}
func room(_ room: Room, didUpdate speakers: [Participant]) {
guard let speaker_ids = speakers.compactMap({ $0.identity as CFString }) as CFArray? else { return }
self.onActiveSpeakersChanged(self.data, speaker_ids)
}
func room(_ room: Room, participant: RemoteParticipant, didUnsubscribe publication: RemoteTrackPublication, track: Track) {
if track.kind == .video {
self.onDidUnsubscribeFromRemoteVideoTrack(self.data, participant.identity as CFString, track.sid! as CFString)
@ -104,7 +104,7 @@ class LKVideoRenderer: NSObject, VideoRenderer {
public func LKRoomDelegateCreate(
data: UnsafeRawPointer,
onDidDisconnect: @escaping @convention(c) (UnsafeRawPointer) -> Void,
onDidSubscribeToRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void,
onDidSubscribeToRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer, UnsafeRawPointer) -> Void,
onDidUnsubscribeFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void,
onMuteChangedFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void,
onActiveSpeakerChanged: @escaping @convention(c) (UnsafeRawPointer, CFArray) -> Void,
@ -180,39 +180,39 @@ public func LKRoomUnpublishTrack(room: UnsafeRawPointer, publication: UnsafeRawP
@_cdecl("LKRoomAudioTracksForRemoteParticipant")
public func LKRoomAudioTracksForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? {
let room = Unmanaged<Room>.fromOpaque(room).takeUnretainedValue()
for (_, participant) in room.remoteParticipants {
if participant.identity == participantId as String {
return participant.audioTracks.compactMap { $0.track as? RemoteAudioTrack } as CFArray?
}
}
return nil;
}
@_cdecl("LKRoomAudioTrackPublicationsForRemoteParticipant")
public func LKRoomAudioTrackPublicationsForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? {
let room = Unmanaged<Room>.fromOpaque(room).takeUnretainedValue()
for (_, participant) in room.remoteParticipants {
if participant.identity == participantId as String {
return participant.audioTracks.compactMap { $0 as? RemoteTrackPublication } as CFArray?
}
}
return nil;
}
@_cdecl("LKRoomVideoTracksForRemoteParticipant")
public func LKRoomVideoTracksForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? {
let room = Unmanaged<Room>.fromOpaque(room).takeUnretainedValue()
for (_, participant) in room.remoteParticipants {
if participant.identity == participantId as String {
return participant.videoTracks.compactMap { $0.track as? RemoteVideoTrack } as CFArray?
}
}
return nil;
}
@ -222,7 +222,7 @@ public func LKLocalAudioTrackCreateTrack() -> UnsafeMutableRawPointer {
echoCancellation: true,
noiseSuppression: true
))
return Unmanaged.passRetained(track).toOpaque()
}
@ -276,7 +276,7 @@ public func LKLocalTrackPublicationSetMute(
callback_data: UnsafeRawPointer
) {
let publication = Unmanaged<LocalTrackPublication>.fromOpaque(publication).takeUnretainedValue()
if muted {
publication.mute().then {
on_complete(callback_data, nil)
@ -307,3 +307,21 @@ public func LKRemoteTrackPublicationSetEnabled(
on_complete(callback_data, error.localizedDescription as CFString)
}
}
@_cdecl("LKRemoteTrackPublicationIsMuted")
public func LKRemoteTrackPublicationIsMuted(
publication: UnsafeRawPointer
) -> Bool {
let publication = Unmanaged<RemoteTrackPublication>.fromOpaque(publication).takeUnretainedValue()
return publication.muted
}
@_cdecl("LKRemoteTrackPublicationGetSid")
public func LKRemoteTrackPublicationGetSid(
publication: UnsafeRawPointer
) -> CFString {
let publication = Unmanaged<RemoteTrackPublication>.fromOpaque(publication).takeUnretainedValue()
return publication.sid as CFString
}

View File

@ -63,7 +63,7 @@ fn main() {
let audio_track = LocalAudioTrack::create();
let audio_track_publication = room_a.publish_audio_track(&audio_track).await.unwrap();
if let RemoteAudioTrackUpdate::Subscribed(track) =
if let RemoteAudioTrackUpdate::Subscribed(track, _) =
audio_track_updates.next().await.unwrap()
{
let remote_tracks = room_b.remote_audio_tracks("test-participant-1");

View File

@ -26,6 +26,7 @@ extern "C" {
publisher_id: CFStringRef,
track_id: CFStringRef,
remote_track: *const c_void,
remote_publication: *const c_void,
),
on_did_unsubscribe_from_remote_audio_track: extern "C" fn(
callback_data: *mut c_void,
@ -125,6 +126,9 @@ extern "C" {
on_complete: extern "C" fn(callback_data: *mut c_void, error: CFStringRef),
callback_data: *mut c_void,
);
fn LKRemoteTrackPublicationIsMuted(publication: *const c_void) -> bool;
fn LKRemoteTrackPublicationGetSid(publication: *const c_void) -> CFStringRef;
}
pub type Sid = String;
@ -372,10 +376,11 @@ impl Room {
rx
}
fn did_subscribe_to_remote_audio_track(&self, track: RemoteAudioTrack) {
fn did_subscribe_to_remote_audio_track(&self, track: RemoteAudioTrack, publication: RemoteTrackPublication) {
let track = Arc::new(track);
let publication = Arc::new(publication);
self.remote_audio_track_subscribers.lock().retain(|tx| {
tx.unbounded_send(RemoteAudioTrackUpdate::Subscribed(track.clone()))
tx.unbounded_send(RemoteAudioTrackUpdate::Subscribed(track.clone(), publication.clone()))
.is_ok()
});
}
@ -501,13 +506,15 @@ impl RoomDelegate {
publisher_id: CFStringRef,
track_id: CFStringRef,
track: *const c_void,
publication: *const c_void,
) {
let room = unsafe { Weak::from_raw(room as *mut Room) };
let publisher_id = unsafe { CFString::wrap_under_get_rule(publisher_id).to_string() };
let track_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() };
let track = RemoteAudioTrack::new(track, track_id, publisher_id);
let publication = RemoteTrackPublication::new(publication);
if let Some(room) = room.upgrade() {
room.did_subscribe_to_remote_audio_track(track);
room.did_subscribe_to_remote_audio_track(track, publication);
}
let _ = Weak::into_raw(room);
}
@ -682,6 +689,14 @@ impl RemoteTrackPublication {
Self(native_track_publication)
}
pub fn sid(&self) -> String {
unsafe { CFString::wrap_under_get_rule(LKRemoteTrackPublicationGetSid(self.0)).to_string() }
}
pub fn is_muted(&self) -> bool {
unsafe { LKRemoteTrackPublicationIsMuted(self.0) }
}
pub fn set_enabled(&self, enabled: bool) -> impl Future<Output = Result<()>> {
let (tx, rx) = futures::channel::oneshot::channel();
@ -832,7 +847,7 @@ pub enum RemoteVideoTrackUpdate {
pub enum RemoteAudioTrackUpdate {
ActiveSpeakersChanged { speakers: Vec<Sid> },
MuteChanged { track_id: Sid, muted: bool },
Subscribed(Arc<RemoteAudioTrack>),
Subscribed(Arc<RemoteAudioTrack>, Arc<RemoteTrackPublication>),
Unsubscribed { publisher_id: Sid, track_id: Sid },
}

View File

@ -216,6 +216,8 @@ impl TestServer {
publisher_id: identity.clone(),
});
let publication = Arc::new(RemoteTrackPublication);
room.audio_tracks.push(track.clone());
for (id, client_room) in &room.client_rooms {
@ -225,7 +227,7 @@ impl TestServer {
.lock()
.audio_track_updates
.0
.try_broadcast(RemoteAudioTrackUpdate::Subscribed(track.clone()))
.try_broadcast(RemoteAudioTrackUpdate::Subscribed(track.clone(), publication.clone()))
.unwrap();
}
}
@ -501,6 +503,14 @@ impl RemoteTrackPublication {
pub fn set_enabled(&self, _enabled: bool) -> impl Future<Output = Result<()>> {
async { Ok(()) }
}
pub fn is_muted(&self) -> bool {
false
}
pub fn sid(&self) -> String {
"".to_string()
}
}
#[derive(Clone)]
@ -579,7 +589,7 @@ pub enum RemoteVideoTrackUpdate {
pub enum RemoteAudioTrackUpdate {
ActiveSpeakersChanged { speakers: Vec<Sid> },
MuteChanged { track_id: Sid, muted: bool },
Subscribed(Arc<RemoteAudioTrack>),
Subscribed(Arc<RemoteAudioTrack>, Arc<RemoteTrackPublication>),
Unsubscribed { publisher_id: Sid, track_id: Sid },
}