windows: Make collab run on Windows (#23117)
I’ve also updated the documentation in `development\local-collaboration.md` and `docs\src\development\windows.md`. Testing collab on my Windows machine:  Release Notes: - N/A --------- Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
This commit is contained in:
parent
b1375ab946
commit
70db427fc8
14 changed files with 176 additions and 197 deletions
|
@ -19,6 +19,10 @@ rustflags = ["-C", "link-args=-Objc -all_load"]
|
|||
[target.x86_64-apple-darwin]
|
||||
rustflags = ["-C", "link-args=-Objc -all_load"]
|
||||
|
||||
# This cfg will reduce the size of `windows::core::Error` from 16 bytes to 4 bytes
|
||||
[target.'cfg(target_os = "windows")']
|
||||
rustflags = ["--cfg", "windows_slim_errors"]
|
||||
rustflags = [
|
||||
"--cfg",
|
||||
"windows_slim_errors", # This cfg will reduce the size of `windows::core::Error` from 16 bytes to 4 bytes
|
||||
"-C",
|
||||
"target-feature=+crt-static", # This fixes the linking issue when compiling livekit on Windows
|
||||
]
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![cfg_attr(target_os = "windows", allow(unused))]
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use client::{proto, ParticipantIndex, User};
|
||||
use collections::HashMap;
|
||||
|
@ -8,7 +6,6 @@ use livekit_client::AudioStream;
|
|||
use project::Project;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub use livekit_client::id::TrackSid;
|
||||
pub use livekit_client::track::{RemoteAudioTrack, RemoteVideoTrack};
|
||||
|
||||
|
@ -52,17 +49,12 @@ pub struct RemoteParticipant {
|
|||
pub participant_index: ParticipantIndex,
|
||||
pub muted: bool,
|
||||
pub speaking: bool,
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub video_tracks: HashMap<TrackSid, RemoteVideoTrack>,
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub audio_tracks: HashMap<TrackSid, (RemoteAudioTrack, AudioStream)>,
|
||||
}
|
||||
|
||||
impl RemoteParticipant {
|
||||
pub fn has_video_tracks(&self) -> bool {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
return !self.video_tracks.is_empty();
|
||||
#[cfg(target_os = "windows")]
|
||||
return false;
|
||||
!self.video_tracks.is_empty()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![cfg_attr(target_os = "windows", allow(unused))]
|
||||
|
||||
use crate::{
|
||||
call_settings::CallSettings,
|
||||
participant::{LocalParticipant, ParticipantLocation, RemoteParticipant},
|
||||
|
@ -17,7 +15,6 @@ use gpui::{
|
|||
AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Task, WeakModel,
|
||||
};
|
||||
use language::LanguageRegistry;
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
use livekit::{
|
||||
capture_local_audio_track, capture_local_video_track,
|
||||
id::ParticipantIdentity,
|
||||
|
@ -27,8 +24,6 @@ use livekit::{
|
|||
track::{TrackKind, TrackSource},
|
||||
RoomEvent, RoomOptions,
|
||||
};
|
||||
#[cfg(target_os = "windows")]
|
||||
use livekit::{publication::LocalTrackPublication, RoomEvent};
|
||||
use livekit_client as livekit;
|
||||
use postage::{sink::Sink, stream::Stream, watch};
|
||||
use project::Project;
|
||||
|
@ -106,7 +101,7 @@ impl Room {
|
|||
!self.shared_projects.is_empty()
|
||||
}
|
||||
|
||||
#[cfg(all(any(test, feature = "test-support"), not(target_os = "windows")))]
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn is_connected(&self) -> bool {
|
||||
if let Some(live_kit) = self.live_kit.as_ref() {
|
||||
live_kit.room.connection_state() == livekit::ConnectionState::Connected
|
||||
|
@ -671,16 +666,6 @@ impl Room {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn start_room_connection(
|
||||
&self,
|
||||
mut room: proto::Room,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<()> {
|
||||
Task::ready(())
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn start_room_connection(
|
||||
&self,
|
||||
mut room: proto::Room,
|
||||
|
@ -837,7 +822,6 @@ impl Room {
|
|||
muted: true,
|
||||
speaking: false,
|
||||
video_tracks: Default::default(),
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
audio_tracks: Default::default(),
|
||||
},
|
||||
);
|
||||
|
@ -944,7 +928,6 @@ impl Room {
|
|||
);
|
||||
|
||||
match event {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
RoomEvent::TrackSubscribed {
|
||||
track,
|
||||
participant,
|
||||
|
@ -979,7 +962,6 @@ impl Room {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
RoomEvent::TrackUnsubscribed {
|
||||
track, participant, ..
|
||||
} => {
|
||||
|
@ -1007,7 +989,6 @@ impl Room {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
RoomEvent::ActiveSpeakersChanged { speakers } => {
|
||||
let mut speaker_ids = speakers
|
||||
.into_iter()
|
||||
|
@ -1024,7 +1005,6 @@ impl Room {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
RoomEvent::TrackMuted {
|
||||
participant,
|
||||
publication,
|
||||
|
@ -1049,7 +1029,6 @@ impl Room {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
RoomEvent::LocalTrackUnpublished { publication, .. } => {
|
||||
log::info!("unpublished track {}", publication.sid());
|
||||
if let Some(room) = &mut self.live_kit {
|
||||
|
@ -1072,12 +1051,10 @@ impl Room {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
RoomEvent::LocalTrackPublished { publication, .. } => {
|
||||
log::info!("published track {:?}", publication.sid());
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
RoomEvent::Disconnected { reason } => {
|
||||
log::info!("disconnected from room: {reason:?}");
|
||||
self.leave(cx).detach_and_log_err(cx);
|
||||
|
@ -1307,13 +1284,6 @@ impl Room {
|
|||
pub fn can_use_microphone(&self) -> bool {
|
||||
use proto::ChannelRole::*;
|
||||
|
||||
#[cfg(not(any(test, feature = "test-support")))]
|
||||
{
|
||||
if cfg!(target_os = "windows") {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
match self.local_participant.role {
|
||||
Admin | Member | Talker => true,
|
||||
Guest | Banned => false,
|
||||
|
@ -1328,12 +1298,6 @@ impl Room {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn share_microphone(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
|
||||
Task::ready(Err(anyhow!("Windows is not supported yet")))
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[track_caller]
|
||||
pub fn share_microphone(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
|
||||
if self.status.is_offline() {
|
||||
|
@ -1411,12 +1375,6 @@ impl Room {
|
|||
})
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn share_screen(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
|
||||
Task::ready(Err(anyhow!("Windows is not supported yet")))
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn share_screen(&mut self, cx: &mut ModelContext<Self>) -> Task<Result<()>> {
|
||||
if self.status.is_offline() {
|
||||
return Task::ready(Err(anyhow!("room is offline")));
|
||||
|
@ -1564,15 +1522,13 @@ impl Room {
|
|||
LocalTrack::Published {
|
||||
track_publication, ..
|
||||
} => {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let local_participant = live_kit.room.local_participant();
|
||||
let sid = track_publication.sid();
|
||||
cx.background_executor()
|
||||
.spawn(async move { local_participant.unpublish_track(&sid).await })
|
||||
.detach_and_log_err(cx);
|
||||
cx.notify();
|
||||
}
|
||||
let local_participant = live_kit.room.local_participant();
|
||||
let sid = track_publication.sid();
|
||||
cx.background_executor()
|
||||
.spawn(async move { local_participant.unpublish_track(&sid).await })
|
||||
.detach_and_log_err(cx);
|
||||
cx.notify();
|
||||
|
||||
Audio::play_sound(Sound::StopScreenshare, cx);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1580,15 +1536,12 @@ impl Room {
|
|||
}
|
||||
|
||||
fn set_deafened(&mut self, deafened: bool, cx: &mut ModelContext<Self>) -> Option<()> {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
let live_kit = self.live_kit.as_mut()?;
|
||||
cx.notify();
|
||||
for (_, participant) in live_kit.room.remote_participants() {
|
||||
for (_, publication) in participant.track_publications() {
|
||||
if publication.kind() == TrackKind::Audio {
|
||||
publication.set_enabled(!deafened);
|
||||
}
|
||||
let live_kit = self.live_kit.as_mut()?;
|
||||
cx.notify();
|
||||
for (_, participant) in live_kit.room.remote_participants() {
|
||||
for (_, publication) in participant.track_publications() {
|
||||
if publication.kind() == TrackKind::Audio {
|
||||
publication.set_enabled(!deafened);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1622,28 +1575,18 @@ impl Room {
|
|||
LocalTrack::Published {
|
||||
track_publication, ..
|
||||
} => {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
if should_mute {
|
||||
track_publication.mute()
|
||||
} else {
|
||||
track_publication.unmute()
|
||||
}
|
||||
if should_mute {
|
||||
track_publication.mute()
|
||||
} else {
|
||||
track_publication.unmute()
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn spawn_room_connection(
|
||||
livekit_connection_info: Option<proto::LiveKitConnectionInfo>,
|
||||
cx: &mut ModelContext<'_, Room>,
|
||||
) {
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn spawn_room_connection(
|
||||
livekit_connection_info: Option<proto::LiveKitConnectionInfo>,
|
||||
cx: &mut ModelContext<'_, Room>,
|
||||
|
@ -1708,10 +1651,6 @@ struct LiveKitRoom {
|
|||
}
|
||||
|
||||
impl LiveKitRoom {
|
||||
#[cfg(target_os = "windows")]
|
||||
fn stop_publishing(&mut self, _cx: &mut ModelContext<Room>) {}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn stop_publishing(&mut self, cx: &mut ModelContext<Room>) {
|
||||
let mut tracks_to_unpublish = Vec::new();
|
||||
if let LocalTrack::Published {
|
||||
|
|
|
@ -2,7 +2,11 @@ use collab::env::get_dotenv_vars;
|
|||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
for (key, value) in get_dotenv_vars(".")? {
|
||||
println!("export {}=\"{}\"", key, value);
|
||||
if option_env!("POWERSHELL").is_some() {
|
||||
println!("$env:{}=\"{}\"", key, value);
|
||||
} else {
|
||||
println!("export {}=\"{}\"", key, value);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -18,11 +18,7 @@ name = "test_app"
|
|||
|
||||
[features]
|
||||
no-webrtc = []
|
||||
test-support = [
|
||||
"collections/test-support",
|
||||
"gpui/test-support",
|
||||
"nanoid",
|
||||
]
|
||||
test-support = ["collections/test-support", "gpui/test-support", "nanoid"]
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
|
@ -32,10 +28,11 @@ cpal = "0.15"
|
|||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
http_2 = { package = "http", version = "0.2.1" }
|
||||
livekit.workspace = true
|
||||
livekit_server.workspace = true
|
||||
log.workspace = true
|
||||
media.workspace = true
|
||||
nanoid = { workspace = true, optional = true}
|
||||
nanoid = { workspace = true, optional = true }
|
||||
parking_lot.workspace = true
|
||||
postage.workspace = true
|
||||
util.workspace = true
|
||||
|
@ -43,9 +40,6 @@ http_client.workspace = true
|
|||
smallvec.workspace = true
|
||||
image.workspace = true
|
||||
|
||||
[target.'cfg(not(target_os = "windows"))'.dependencies]
|
||||
livekit.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
core-foundation.workspace = true
|
||||
coreaudio-rs = "0.12.1"
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#![cfg_attr(target_os = "windows", allow(unused))]
|
||||
|
||||
mod remote_video_track_view;
|
||||
#[cfg(any(test, feature = "test-support", target_os = "windows"))]
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub mod test;
|
||||
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
|
@ -13,7 +11,6 @@ use gpui::{
|
|||
use parking_lot::Mutex;
|
||||
use std::{borrow::Cow, collections::VecDeque, future::Future, pin::Pin, sync::Arc, thread};
|
||||
use util::{debug_panic, ResultExt as _};
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
use webrtc::{
|
||||
audio_frame::AudioFrame,
|
||||
audio_source::{native::NativeAudioSource, AudioSourceOptions, RtcAudioSource},
|
||||
|
@ -23,13 +20,13 @@ use webrtc::{
|
|||
video_stream::native::NativeVideoStream,
|
||||
};
|
||||
|
||||
#[cfg(all(not(any(test, feature = "test-support")), not(target_os = "windows")))]
|
||||
#[cfg(not(any(test, feature = "test-support")))]
|
||||
use livekit::track::RemoteAudioTrack;
|
||||
#[cfg(all(not(any(test, feature = "test-support")), not(target_os = "windows")))]
|
||||
#[cfg(not(any(test, feature = "test-support")))]
|
||||
pub use livekit::*;
|
||||
#[cfg(any(test, feature = "test-support", target_os = "windows"))]
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
use test::track::RemoteAudioTrack;
|
||||
#[cfg(any(test, feature = "test-support", target_os = "windows"))]
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub use test::*;
|
||||
|
||||
pub use remote_video_track_view::{RemoteVideoTrackView, RemoteVideoTrackViewEvent};
|
||||
|
@ -46,7 +43,6 @@ pub enum AudioStream {
|
|||
|
||||
struct Dispatcher(Arc<dyn gpui::PlatformDispatcher>);
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
impl livekit::dispatcher::Dispatcher for Dispatcher {
|
||||
fn dispatch(&self, runnable: livekit::dispatcher::Runnable) {
|
||||
self.0.dispatch(runnable, None);
|
||||
|
@ -68,7 +64,6 @@ fn http_2_status(status: http_client::http::StatusCode) -> http_2::StatusCode {
|
|||
.expect("valid status code to status code conversion")
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
impl livekit::dispatcher::HttpClient for HttpClientAdapter {
|
||||
fn get(
|
||||
&self,
|
||||
|
@ -123,14 +118,6 @@ impl livekit::dispatcher::HttpClient for HttpClientAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn init(
|
||||
dispatcher: Arc<dyn gpui::PlatformDispatcher>,
|
||||
http_client: Arc<dyn http_client::HttpClient>,
|
||||
) {
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn init(
|
||||
dispatcher: Arc<dyn gpui::PlatformDispatcher>,
|
||||
http_client: Arc<dyn http_client::HttpClient>,
|
||||
|
@ -139,7 +126,6 @@ pub fn init(
|
|||
livekit::dispatcher::set_http_client(HttpClientAdapter(http_client));
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub async fn capture_local_video_track(
|
||||
capture_source: &dyn ScreenCaptureSource,
|
||||
) -> Result<(track::LocalVideoTrack, Box<dyn ScreenCaptureStream>)> {
|
||||
|
@ -173,7 +159,6 @@ pub async fn capture_local_video_track(
|
|||
))
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn capture_local_audio_track(
|
||||
background_executor: &BackgroundExecutor,
|
||||
) -> Result<Task<(track::LocalAudioTrack, AudioStream)>> {
|
||||
|
@ -265,7 +250,6 @@ pub fn capture_local_audio_track(
|
|||
}))
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn play_remote_audio_track(
|
||||
track: &RemoteAudioTrack,
|
||||
background_executor: &BackgroundExecutor,
|
||||
|
@ -336,7 +320,6 @@ fn default_device(input: bool) -> anyhow::Result<(cpal::Device, cpal::SupportedS
|
|||
Ok((device, config))
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn get_default_output() -> anyhow::Result<(cpal::Device, cpal::SupportedStreamConfig)> {
|
||||
let host = cpal::default_host();
|
||||
let output_device = host
|
||||
|
@ -346,7 +329,6 @@ fn get_default_output() -> anyhow::Result<(cpal::Device, cpal::SupportedStreamCo
|
|||
Ok((output_device, output_config))
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn start_output_stream(
|
||||
output_config: cpal::SupportedStreamConfig,
|
||||
output_device: cpal::Device,
|
||||
|
@ -431,14 +413,6 @@ fn start_output_stream(
|
|||
(receive_task, thread)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn play_remote_video_track(
|
||||
track: &track::RemoteVideoTrack,
|
||||
) -> impl Stream<Item = RemoteVideoFrame> {
|
||||
futures::stream::empty()
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn play_remote_video_track(
|
||||
track: &track::RemoteVideoTrack,
|
||||
) -> impl Stream<Item = RemoteVideoFrame> {
|
||||
|
@ -466,7 +440,7 @@ fn video_frame_buffer_from_webrtc(buffer: Box<dyn VideoBuffer>) -> Option<Remote
|
|||
#[cfg(not(target_os = "macos"))]
|
||||
pub type RemoteVideoFrame = Arc<gpui::RenderImage>;
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
fn video_frame_buffer_from_webrtc(buffer: Box<dyn VideoBuffer>) -> Option<RemoteVideoFrame> {
|
||||
use gpui::RenderImage;
|
||||
use image::{Frame, RgbaImage};
|
||||
|
@ -517,7 +491,7 @@ fn video_frame_buffer_to_webrtc(frame: ScreenCaptureFrame) -> Option<impl AsRef<
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows")))]
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
fn video_frame_buffer_to_webrtc(_frame: ScreenCaptureFrame) -> Option<impl AsRef<dyn VideoBuffer>> {
|
||||
None as Option<Box<dyn VideoBuffer>>
|
||||
}
|
||||
|
|
|
@ -1,18 +1,14 @@
|
|||
pub mod participant;
|
||||
pub mod publication;
|
||||
pub mod track;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub mod webrtc;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
use self::id::*;
|
||||
use self::{participant::*, publication::*, track::*};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use collections::{btree_map::Entry as BTreeEntry, hash_map::Entry, BTreeMap, HashMap, HashSet};
|
||||
use gpui::BackgroundExecutor;
|
||||
#[cfg(not(windows))]
|
||||
use livekit::options::TrackPublishOptions;
|
||||
use livekit_server::{proto, token};
|
||||
use parking_lot::Mutex;
|
||||
|
@ -22,7 +18,6 @@ use std::sync::{
|
|||
Arc, Weak,
|
||||
};
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub use livekit::{id, options, ConnectionState, DisconnectReason, RoomOptions};
|
||||
|
||||
static SERVERS: Mutex<BTreeMap<String, Arc<TestServer>>> = Mutex::new(BTreeMap::new());
|
||||
|
@ -31,12 +26,10 @@ pub struct TestServer {
|
|||
pub url: String,
|
||||
pub api_key: String,
|
||||
pub secret_key: String,
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
rooms: Mutex<HashMap<String, TestServerRoom>>,
|
||||
executor: BackgroundExecutor,
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
impl TestServer {
|
||||
pub fn create(
|
||||
url: String,
|
||||
|
@ -534,7 +527,6 @@ impl TestServer {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[derive(Default, Debug)]
|
||||
struct TestServerRoom {
|
||||
client_rooms: HashMap<ParticipantIdentity, Room>,
|
||||
|
@ -543,7 +535,6 @@ struct TestServerRoom {
|
|||
participant_permissions: HashMap<ParticipantIdentity, proto::ParticipantPermission>,
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[derive(Debug)]
|
||||
struct TestServerVideoTrack {
|
||||
sid: TrackSid,
|
||||
|
@ -551,7 +542,6 @@ struct TestServerVideoTrack {
|
|||
// frames_rx: async_broadcast::Receiver<Frame>,
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[derive(Debug)]
|
||||
struct TestServerAudioTrack {
|
||||
sid: TrackSid,
|
||||
|
@ -590,7 +580,6 @@ pub enum RoomEvent {
|
|||
TrackSubscriptionFailed {
|
||||
participant: RemoteParticipant,
|
||||
error: String,
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
track_sid: TrackSid,
|
||||
},
|
||||
TrackPublished {
|
||||
|
@ -626,12 +615,10 @@ pub enum RoomEvent {
|
|||
ActiveSpeakersChanged {
|
||||
speakers: Vec<Participant>,
|
||||
},
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
ConnectionStateChanged(ConnectionState),
|
||||
Connected {
|
||||
participants_with_tracks: Vec<(RemoteParticipant, Vec<RemoteTrackPublication>)>,
|
||||
},
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
Disconnected {
|
||||
reason: DisconnectReason,
|
||||
},
|
||||
|
@ -639,7 +626,6 @@ pub enum RoomEvent {
|
|||
Reconnected,
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[async_trait]
|
||||
impl livekit_server::api::Client for TestApiClient {
|
||||
fn url(&self) -> &str {
|
||||
|
@ -703,11 +689,8 @@ impl livekit_server::api::Client for TestApiClient {
|
|||
struct RoomState {
|
||||
url: String,
|
||||
token: String,
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
local_identity: ParticipantIdentity,
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
connection_state: ConnectionState,
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
paused_audio_tracks: HashSet<TrackSid>,
|
||||
updates_tx: mpsc::Sender<RoomEvent>,
|
||||
}
|
||||
|
@ -718,7 +701,6 @@ pub struct Room(Arc<Mutex<RoomState>>);
|
|||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct WeakRoom(Weak<Mutex<RoomState>>);
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
impl std::fmt::Debug for RoomState {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Room")
|
||||
|
@ -731,17 +713,6 @@ impl std::fmt::Debug for RoomState {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
impl std::fmt::Debug for RoomState {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Room")
|
||||
.field("url", &self.url)
|
||||
.field("token", &self.token)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
impl Room {
|
||||
fn downgrade(&self) -> WeakRoom {
|
||||
WeakRoom(Arc::downgrade(&self.0))
|
||||
|
@ -803,7 +774,6 @@ impl Room {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
impl Drop for RoomState {
|
||||
fn drop(&mut self) {
|
||||
if self.connection_state == ConnectionState::Connected {
|
||||
|
|
|
@ -8,19 +8,16 @@ pub enum Participant {
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LocalParticipant {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub(super) identity: ParticipantIdentity,
|
||||
pub(super) room: Room,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RemoteParticipant {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub(super) identity: ParticipantIdentity,
|
||||
pub(super) room: WeakRoom,
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
impl Participant {
|
||||
pub fn identity(&self) -> ParticipantIdentity {
|
||||
match self {
|
||||
|
@ -30,7 +27,6 @@ impl Participant {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
impl LocalParticipant {
|
||||
pub async fn unpublish_track(&self, track: &TrackSid) -> Result<()> {
|
||||
self.room
|
||||
|
@ -64,7 +60,6 @@ impl LocalParticipant {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
impl RemoteParticipant {
|
||||
pub fn track_publications(&self) -> HashMap<TrackSid, RemoteTrackPublication> {
|
||||
if let Some(room) = self.room.upgrade() {
|
||||
|
|
|
@ -8,20 +8,17 @@ pub enum TrackPublication {
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LocalTrackPublication {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub(crate) sid: TrackSid,
|
||||
pub(crate) room: WeakRoom,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RemoteTrackPublication {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub(crate) sid: TrackSid,
|
||||
pub(crate) room: WeakRoom,
|
||||
pub(crate) track: RemoteTrack,
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
impl TrackPublication {
|
||||
pub fn sid(&self) -> TrackSid {
|
||||
match self {
|
||||
|
@ -38,7 +35,6 @@ impl TrackPublication {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
impl LocalTrackPublication {
|
||||
pub fn sid(&self) -> TrackSid {
|
||||
self.sid.clone()
|
||||
|
@ -71,7 +67,6 @@ impl LocalTrackPublication {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
impl RemoteTrackPublication {
|
||||
pub fn sid(&self) -> TrackSid {
|
||||
self.sid.clone()
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
use super::*;
|
||||
#[cfg(not(windows))]
|
||||
use webrtc::{audio_source::RtcAudioSource, video_source::RtcVideoSource};
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub use livekit::track::{TrackKind, TrackSource};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -25,14 +23,12 @@ pub struct LocalAudioTrack {}
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RemoteVideoTrack {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub(super) server_track: Arc<TestServerVideoTrack>,
|
||||
pub(super) _room: WeakRoom,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RemoteAudioTrack {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub(super) server_track: Arc<TestServerAudioTrack>,
|
||||
pub(super) room: WeakRoom,
|
||||
}
|
||||
|
@ -43,17 +39,14 @@ pub enum RtcTrack {
|
|||
}
|
||||
|
||||
pub struct RtcAudioTrack {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub(super) server_track: Arc<TestServerAudioTrack>,
|
||||
pub(super) room: WeakRoom,
|
||||
}
|
||||
|
||||
pub struct RtcVideoTrack {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub(super) _server_track: Arc<TestServerVideoTrack>,
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
impl RemoteTrack {
|
||||
pub fn sid(&self) -> TrackSid {
|
||||
match self {
|
||||
|
@ -84,21 +77,18 @@ impl RemoteTrack {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
impl LocalVideoTrack {
|
||||
pub fn create_video_track(_name: &str, _source: RtcVideoSource) -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
impl LocalAudioTrack {
|
||||
pub fn create_audio_track(_name: &str, _source: RtcAudioSource) -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
impl RemoteAudioTrack {
|
||||
pub fn sid(&self) -> TrackSid {
|
||||
self.server_track.sid.clone()
|
||||
|
@ -134,7 +124,6 @@ impl RemoteAudioTrack {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
impl RemoteVideoTrack {
|
||||
pub fn sid(&self) -> TrackSid {
|
||||
self.server_track.sid.clone()
|
||||
|
@ -151,7 +140,6 @@ impl RemoteVideoTrack {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
impl RtcTrack {
|
||||
pub fn enabled(&self) -> bool {
|
||||
match self {
|
||||
|
@ -168,7 +156,6 @@ impl RtcTrack {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
impl RtcAudioTrack {
|
||||
pub fn set_enabled(&self, enabled: bool) {
|
||||
if let Some(room) = self.room.upgrade() {
|
||||
|
|
|
@ -10,6 +10,8 @@ First, make sure you've installed Zed's backend dependencies for your platform:
|
|||
|
||||
Before you can run the `collab` server locally, you'll need to set up a `zed` Postgres database.
|
||||
|
||||
### On macOS and Linux
|
||||
|
||||
```sh
|
||||
script/bootstrap
|
||||
```
|
||||
|
@ -31,8 +33,16 @@ To use a different set of admin users, you can create your own version of that j
|
|||
}
|
||||
```
|
||||
|
||||
### On Windows
|
||||
|
||||
```powershell
|
||||
.\script\bootstrap.ps1
|
||||
```
|
||||
|
||||
## Testing collaborative features locally
|
||||
|
||||
### On macOS and Linux
|
||||
|
||||
Ensure that Postgres is configured and running, then run Zed's collaboration server and the `livekit` dev server:
|
||||
|
||||
```sh
|
||||
|
@ -41,7 +51,7 @@ foreman start
|
|||
cargo run -p collab -- serve all
|
||||
```
|
||||
|
||||
In a second terminal, run two or more instances of Zed.
|
||||
In a new terminal, run two or more instances of Zed.
|
||||
|
||||
```sh
|
||||
script/zed-local -2
|
||||
|
@ -49,6 +59,34 @@ script/zed-local -2
|
|||
|
||||
This script starts one to four instances of Zed, depending on the `-2`, `-3` or `-4` flags. Each instance will be connected to the local `collab` server, signed in as a different user from `.admins.json` or `.admins.default.json`.
|
||||
|
||||
### On Windows
|
||||
|
||||
Since `foreman` is not available on Windows, you can run the following commands in separate terminals:
|
||||
|
||||
```powershell
|
||||
cargo run --package=collab -- serve all
|
||||
```
|
||||
|
||||
If you have added the `livekit-server` binary to your `PATH`, you can run:
|
||||
|
||||
```powershell
|
||||
livekit-server --dev
|
||||
```
|
||||
|
||||
Otherwise,
|
||||
|
||||
```powershell
|
||||
.\path\to\livekit-serve.exe --dev
|
||||
```
|
||||
|
||||
In a new terminal, run two or more instances of Zed.
|
||||
|
||||
```powershell
|
||||
node .\script\zed-local -2
|
||||
```
|
||||
|
||||
Note that this requires `node.exe` to be in your `PATH`.
|
||||
|
||||
## Running a local collab server
|
||||
|
||||
If you want to run your own version of the zed collaboration service, you can, but note that this is still under development, and there is no good support for authentication nor extensions.
|
||||
|
|
|
@ -12,7 +12,31 @@ Clone down the [Zed repository](https://github.com/zed-industries/zed).
|
|||
|
||||
- Install [Visual Studio](https://visualstudio.microsoft.com/downloads/) with the optional components `MSVC v*** - VS YYYY C++ x64/x86 build tools` and `MSVC v*** - VS YYYY C++ x64/x86 Spectre-mitigated libs (latest)` (`v***` is your VS version and `YYYY` is year when your VS was released. Pay attention to the architecture and change it to yours if needed.)
|
||||
- Install Windows 11 or 10 SDK depending on your system, but ensure that at least `Windows 10 SDK version 2104 (10.0.20348.0)` is installed on your machine. You can download it from the [Windows SDK Archive](https://developer.microsoft.com/windows/downloads/windows-sdk/)
|
||||
- Install [CMake](https://cmake.org/download) (required by [a dependency](https://docs.rs/wasmtime-c-api-impl/latest/wasmtime_c_api/))
|
||||
- Install [CMake](https://cmake.org/download) (required by [a dependency](https://docs.rs/wasmtime-c-api-impl/latest/wasmtime_c_api/)). Or you can install it through Visual Studio Installer, then manually add the `bin` directory to your `PATH`, for example: `C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin`.
|
||||
|
||||
If you can't compile Zed, make sure that you have at least the following components installed:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.0",
|
||||
"components": [
|
||||
"Microsoft.VisualStudio.Component.CoreEditor",
|
||||
"Microsoft.VisualStudio.Workload.CoreEditor",
|
||||
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
|
||||
"Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions.CMake",
|
||||
"Microsoft.VisualStudio.Component.VC.CMake.Project",
|
||||
"Microsoft.VisualStudio.Component.Windows11SDK.26100",
|
||||
"Microsoft.VisualStudio.Component.VC.Runtimes.x86.x64.Spectre"
|
||||
],
|
||||
"extensions": []
|
||||
}
|
||||
```
|
||||
|
||||
The list can be obtained as follows:
|
||||
|
||||
- Open the Visual Studio Installer
|
||||
- Click on `More` in the `Installed` tab
|
||||
- Click on `Export configuration`
|
||||
|
||||
## Backend dependencies
|
||||
|
||||
|
@ -21,7 +45,7 @@ Clone down the [Zed repository](https://github.com/zed-industries/zed).
|
|||
If you are developing collaborative features of Zed, you'll need to install the dependencies of zed's `collab` server:
|
||||
|
||||
- Install [Postgres](https://www.postgresql.org/download/windows/)
|
||||
- Install [Livekit](https://github.com/livekit/livekit-cli) and [Foreman](https://theforeman.org/manuals/3.9/quickstart_guide.html)
|
||||
- Install [Livekit](https://github.com/livekit/livekit), optionally you can add the `livekit-server` binary to your `PATH`.
|
||||
|
||||
Alternatively, if you have [Docker](https://www.docker.com/) installed you can bring up all the `collab` dependencies using Docker Compose:
|
||||
|
||||
|
@ -29,6 +53,26 @@ Alternatively, if you have [Docker](https://www.docker.com/) installed you can b
|
|||
docker compose up -d
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
You should modify the `pg_hba.conf` file in the `data` directory to use `trust` instead of `scram-sha-256` for the `host` method. Otherwise, the connection will fail with the error `password authentication failed`. The `pg_hba.conf` file typically locates at `C:\Program Files\PostgreSQL\17\data\pg_hba.conf`. After the modification, the file should look like this:
|
||||
|
||||
```conf
|
||||
# IPv4 local connections:
|
||||
host all all 127.0.0.1/32 trust
|
||||
# IPv6 local connections:
|
||||
host all all ::1/128 trust
|
||||
```
|
||||
|
||||
Also, if you are using a non-latin Windows version, you must modify the`lc_messages` parameter in the `postgresql.conf` file in the `data` directory to `English_United States.1252` (or whatever UTF8-compatible encoding you have). Otherwise, the database will panic. The `postgresql.conf` file should look like this:
|
||||
|
||||
```conf
|
||||
# lc_messages = 'Chinese (Simplified)_China.936' # locale for system error message strings
|
||||
lc_messages = 'English_United States.1252'
|
||||
```
|
||||
|
||||
After this, you should restart the `postgresql` service. Press the `win` key + `R` to launch the `Run` window. Type the `services.msc` and hit the `OK` button to open the Services Manager. Then, find the `postgresql-x64-XX` service, right-click on it, and select `Restart`.
|
||||
|
||||
## Building from source
|
||||
|
||||
Once you have the dependencies installed, you can build Zed using [Cargo](https://doc.rust-lang.org/cargo/).
|
||||
|
|
21
script/bootstrap.ps1
Normal file
21
script/bootstrap.ps1
Normal file
|
@ -0,0 +1,21 @@
|
|||
$ErrorActionPreference = 'Stop'
|
||||
$PSNativeCommandUseErrorActionPreference = $true
|
||||
|
||||
$env:POWERSHELL = $true
|
||||
|
||||
if (!(Get-Command sqlx -ErrorAction SilentlyContinue) -or (sqlx --version) -notlike "sqlx-cli 0.7.2") {
|
||||
Write-Output "sqlx-cli not found or not the required version, installing version 0.7.2..."
|
||||
cargo install sqlx-cli --version 0.7.2
|
||||
}
|
||||
|
||||
Set-Location .\crates\collab
|
||||
|
||||
# Export contents of .env.toml
|
||||
$env = (cargo run --bin dotenv) -join "`n";
|
||||
Invoke-Expression $env
|
||||
|
||||
Set-Location ../..
|
||||
|
||||
Write-Output "creating databases..."
|
||||
sqlx database create --database-url "$env:DATABASE_URL"
|
||||
sqlx database create --database-url "$env:LLM_DATABASE_URL"
|
|
@ -112,9 +112,23 @@ if (platform === "darwin") {
|
|||
console.log(err);
|
||||
throw new Error("Could not get screen resolution");
|
||||
}
|
||||
} else if (platform === "win32") {
|
||||
// windows
|
||||
try {
|
||||
const resolutionOutput = execSync(
|
||||
`powershell -Command "& {Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea.Size}"`,
|
||||
{ encoding: "utf8" }
|
||||
).trim();
|
||||
[screenWidth, screenHeight] = resolutionOutput.match(/\d+/g).map(Number);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
throw new Error("Could not get screen resolution on Windows");
|
||||
}
|
||||
}
|
||||
|
||||
screenHeight -= titleBarHeight;
|
||||
if (platform !== "win32") {
|
||||
screenHeight -= titleBarHeight;
|
||||
}
|
||||
|
||||
if (isTop) {
|
||||
screenHeight = Math.floor(screenHeight / 2);
|
||||
|
@ -168,10 +182,18 @@ setTimeout(() => {
|
|||
for (let i = 0; i < instanceCount; i++) {
|
||||
const row = Math.floor(i / columns);
|
||||
const column = i % columns;
|
||||
const position = [
|
||||
column * instanceWidth,
|
||||
row * instanceHeight + titleBarHeight,
|
||||
].join(",");
|
||||
let position;
|
||||
if (platform == "win32") {
|
||||
position = [
|
||||
column * instanceWidth,
|
||||
row * instanceHeight,
|
||||
].join(",");
|
||||
} else {
|
||||
position = [
|
||||
column * instanceWidth,
|
||||
row * instanceHeight + titleBarHeight,
|
||||
].join(",");
|
||||
}
|
||||
const size = [instanceWidth, instanceHeight].join(",");
|
||||
let binaryPath = zedBinary;
|
||||
if (i != 0 && othersOnStable) {
|
||||
|
@ -186,8 +208,8 @@ setTimeout(() => {
|
|||
ZED_WINDOW_POSITION: position,
|
||||
ZED_STATELESS: isStateful && i == 0 ? "" : "1",
|
||||
ZED_ALWAYS_ACTIVE: "1",
|
||||
ZED_SERVER_URL: "http://localhost:3000",
|
||||
ZED_RPC_URL: "http://localhost:8080/rpc",
|
||||
ZED_SERVER_URL: "http://127.0.0.1:3000", // On windows, the http_client could not parse localhost
|
||||
ZED_RPC_URL: "http://127.0.0.1:8080/rpc",
|
||||
ZED_ADMIN_API_TOKEN: "secret",
|
||||
ZED_WINDOW_SIZE: size,
|
||||
ZED_CLIENT_CHECKSUM_SEED: "development-checksum-seed",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue