From 8f7a26f397a1c516cba83d2d38e170fa0f24b6f9 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Wed, 14 Feb 2024 12:24:12 -0800 Subject: [PATCH] X11: Continuous Presentation (#7762) Alternative to #7758, which doesn't involve adding a new trait method `request_draw`. Somehow, my whole screen goes blinking black with this when moving the window, so not ready for landing. Release Notes: - N/A --------- Co-authored-by: Mikayla Maki --- crates/gpui/Cargo.toml | 2 +- crates/gpui/src/app.rs | 4 ++- crates/gpui/src/gpui.rs | 1 + crates/gpui/src/platform/linux.rs | 1 - crates/gpui/src/platform/linux/platform.rs | 4 ++- crates/gpui/src/platform/linux/x11/client.rs | 28 ++++++++++-------- crates/gpui/src/platform/linux/x11/window.rs | 24 ++++++++++++++- crates/live_kit_client/Cargo.toml | 6 ++-- crates/live_kit_client/src/test.rs | 31 ++++++++++++++++++-- 9 files changed, 78 insertions(+), 23 deletions(-) diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 8a6c1597da..59c40cbaa2 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -96,7 +96,7 @@ objc = "0.2" [target.'cfg(target_os = "linux")'.dependencies] flume = "0.11" -xcb = { version = "1.3", features = ["as-raw-xcb-connection"] } +xcb = { version = "1.3", features = ["as-raw-xcb-connection", "present", "randr"] } as-raw-xcb-connection = "1" #TODO: use these on all platforms blade-graphics = { git = "https://github.com/kvark/blade", rev = "c4f951a88b345724cb952e920ad30e39851f7760" } diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 6478470cf3..6052f61358 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -111,6 +111,9 @@ impl App { /// Builds an app with the given asset source. #[allow(clippy::new_without_default)] pub fn new() -> Self { + #[cfg(any(test, feature = "test-support"))] + log::info!("GPUI was compiled in test mode"); + Self(AppContext::new( current_platform(), Arc::new(()), @@ -676,7 +679,6 @@ impl AppContext { self.update_window(window, |_, cx| cx.draw()).unwrap(); } - #[allow(clippy::collapsible_else_if)] if self.pending_effects.is_empty() { break; } diff --git a/crates/gpui/src/gpui.rs b/crates/gpui/src/gpui.rs index 4fa2f525a7..79115a8d78 100644 --- a/crates/gpui/src/gpui.rs +++ b/crates/gpui/src/gpui.rs @@ -61,6 +61,7 @@ #![deny(missing_docs)] #![allow(clippy::type_complexity)] +#![allow(clippy::collapsible_else_if)] #[macro_use] mod action; diff --git a/crates/gpui/src/platform/linux.rs b/crates/gpui/src/platform/linux.rs index fb81f7da8c..60caf428cd 100644 --- a/crates/gpui/src/platform/linux.rs +++ b/crates/gpui/src/platform/linux.rs @@ -12,7 +12,6 @@ pub(crate) use blade_atlas::*; pub(crate) use dispatcher::*; pub(crate) use platform::*; pub(crate) use text_system::*; -pub(crate) use x11::display::*; pub(crate) use x11::*; use blade_belt::*; diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index b83d0b9dd5..cd1bce6956 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -64,7 +64,9 @@ impl Default for LinuxPlatform { impl LinuxPlatform { pub(crate) fn new() -> Self { - let (xcb_connection, x_root_index) = xcb::Connection::connect(None).unwrap(); + let (xcb_connection, x_root_index) = + xcb::Connection::connect_with_extensions(None, &[xcb::Extension::Present], &[]) + .unwrap(); let atoms = XcbAtoms::intern_all(&xcb_connection).unwrap(); let xcb_connection = Arc::new(xcb_connection); diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index 0bfcfa9157..3d3842ced0 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -41,6 +41,11 @@ impl X11Client { }), } } + + fn get_window(&self, win: x::Window) -> Rc { + let state = self.state.lock(); + Rc::clone(&state.windows[&win]) + } } impl Client for X11Client { @@ -58,18 +63,14 @@ impl Client for X11Client { // window "x" button clicked by user, we gracefully exit let window = self.state.lock().windows.remove(&ev.window()).unwrap(); window.destroy(); - let mut state = self.state.lock(); + let state = self.state.lock(); self.platform_inner.state.lock().quit_requested |= state.windows.is_empty(); } } } xcb::Event::X(x::Event::Expose(ev)) => { - let window = { - let state = self.state.lock(); - Rc::clone(&state.windows[&ev.window()]) - }; - window.expose(); + self.get_window(ev.window()).refresh(); } xcb::Event::X(x::Event::ConfigureNotify(ev)) => { let bounds = Bounds { @@ -82,12 +83,14 @@ impl Client for X11Client { height: ev.height().into(), }, }; - let window = { - let state = self.state.lock(); - Rc::clone(&state.windows[&ev.window()]) - }; - window.configure(bounds) + self.get_window(ev.window()).configure(bounds) } + xcb::Event::Present(xcb::present::Event::CompleteNotify(ev)) => { + let window = self.get_window(ev.window()); + window.refresh(); + window.request_refresh(); + } + xcb::Event::Present(xcb::present::Event::IdleNotify(_ev)) => {} _ => {} } @@ -117,7 +120,7 @@ impl Client for X11Client { fn open_window( &self, - handle: AnyWindowHandle, + _handle: AnyWindowHandle, options: WindowOptions, ) -> Box { let x_window = self.xcb_connection.generate_id(); @@ -129,6 +132,7 @@ impl Client for X11Client { x_window, &self.atoms, )); + window_ptr.request_refresh(); self.state .lock() diff --git a/crates/gpui/src/platform/linux/x11/window.rs b/crates/gpui/src/platform/linux/x11/window.rs index e8bf2e0102..8853966a9f 100644 --- a/crates/gpui/src/platform/linux/x11/window.rs +++ b/crates/gpui/src/platform/linux/x11/window.rs @@ -202,6 +202,16 @@ impl X11WindowState { }) .unwrap(); + let fake_id = xcb_connection.generate_id(); + xcb_connection + .send_and_check_request(&xcb::present::SelectInput { + eid: fake_id, + window: x_window, + //Note: also consider `IDLE_NOTIFY` + event_mask: xcb::present::EventMask::COMPLETE_NOTIFY, + }) + .unwrap(); + xcb_connection.send_request(&x::MapWindow { window: x_window }); xcb_connection.flush().unwrap(); @@ -258,7 +268,7 @@ impl X11WindowState { self.xcb_connection.flush().unwrap(); } - pub fn expose(&self) { + pub fn refresh(&self) { let mut cb = self.callbacks.lock(); if let Some(ref mut fun) = cb.request_frame { fun(); @@ -291,6 +301,18 @@ impl X11WindowState { } } } + + pub fn request_refresh(&self) { + self.xcb_connection + .send_and_check_request(&xcb::present::NotifyMsc { + window: self.x_window, + serial: 0, + target_msc: 0, + divisor: 1, + remainder: 0, + }) + .unwrap(); + } } impl PlatformWindow for X11Window { diff --git a/crates/live_kit_client/Cargo.toml b/crates/live_kit_client/Cargo.toml index d004e18bc1..00a9496a16 100644 --- a/crates/live_kit_client/Cargo.toml +++ b/crates/live_kit_client/Cargo.toml @@ -39,10 +39,10 @@ postage.workspace = true [target.'cfg(target_os = "macos")'.dependencies] core-foundation = "0.9.3" -[target.'cfg(not(target_os = "macos"))'.dependencies] +[target.'cfg(all(not(target_os = "macos")))'.dependencies] async-trait = { workspace = true } -collections = { workspace = true, features = ["test-support"] } -gpui = { workspace = true, features = ["test-support"] } +collections = { workspace = true } +gpui = { workspace = true } live_kit_server.workspace = true nanoid = "0.4" diff --git a/crates/live_kit_client/src/test.rs b/crates/live_kit_client/src/test.rs index 677370c0b8..45f5ac1d1d 100644 --- a/crates/live_kit_client/src/test.rs +++ b/crates/live_kit_client/src/test.rs @@ -73,6 +73,8 @@ impl TestServer { } pub async fn create_room(&self, room: String) -> Result<()> { + //todo!(linux): Remove this once the cross-platform LiveKit implementation is merged + #[cfg(any(test, feature = "test-support"))] self.executor.simulate_random_delay().await; let mut server_rooms = self.rooms.lock(); if server_rooms.contains_key(&room) { @@ -85,6 +87,8 @@ impl TestServer { async fn delete_room(&self, room: String) -> Result<()> { // TODO: clear state associated with all `Room`s. + //todo!(linux): Remove this once the cross-platform LiveKit implementation is merged + #[cfg(any(test, feature = "test-support"))] self.executor.simulate_random_delay().await; let mut server_rooms = self.rooms.lock(); server_rooms @@ -94,7 +98,10 @@ impl TestServer { } async fn join_room(&self, token: String, client_room: Arc) -> Result<()> { + //todo!(linux): Remove this once the cross-platform LiveKit implementation is merged + #[cfg(any(test, feature = "test-support"))] self.executor.simulate_random_delay().await; + let claims = live_kit_server::token::validate(&token, &self.secret_key)?; let identity = claims.sub.unwrap().to_string(); let room_name = claims.video.room.unwrap(); @@ -140,6 +147,8 @@ impl TestServer { } async fn leave_room(&self, token: String) -> Result<()> { + //todo!(linux): Remove this once the cross-platform LiveKit implementation is merged + #[cfg(any(test, feature = "test-support"))] self.executor.simulate_random_delay().await; let claims = live_kit_server::token::validate(&token, &self.secret_key)?; let identity = claims.sub.unwrap().to_string(); @@ -160,8 +169,10 @@ impl TestServer { async fn remove_participant(&self, room_name: String, identity: String) -> Result<()> { // TODO: clear state associated with the `Room`. - + //todo!(linux): Remove this once the cross-platform LiveKit implementation is merged + #[cfg(any(test, feature = "test-support"))] self.executor.simulate_random_delay().await; + let mut server_rooms = self.rooms.lock(); let room = server_rooms .get_mut(&room_name) @@ -182,6 +193,8 @@ impl TestServer { identity: String, permission: proto::ParticipantPermission, ) -> Result<()> { + //todo!(linux): Remove this once the cross-platform LiveKit implementation is merged + #[cfg(any(test, feature = "test-support"))] self.executor.simulate_random_delay().await; let mut server_rooms = self.rooms.lock(); let room = server_rooms @@ -192,6 +205,8 @@ impl TestServer { } pub async fn disconnect_client(&self, client_identity: String) { + //todo!(linux): Remove this once the cross-platform LiveKit implementation is merged + #[cfg(any(test, feature = "test-support"))] self.executor.simulate_random_delay().await; let mut server_rooms = self.rooms.lock(); for room in server_rooms.values_mut() { @@ -206,6 +221,8 @@ impl TestServer { token: String, local_track: LocalVideoTrack, ) -> Result { + //todo!(linux): Remove this once the cross-platform LiveKit implementation is merged + #[cfg(any(test, feature = "test-support"))] self.executor.simulate_random_delay().await; let claims = live_kit_server::token::validate(&token, &self.secret_key)?; let identity = claims.sub.unwrap().to_string(); @@ -259,7 +276,10 @@ impl TestServer { token: String, _local_track: &LocalAudioTrack, ) -> Result { + //todo!(linux): Remove this once the cross-platform LiveKit implementation is merged + #[cfg(any(test, feature = "test-support"))] self.executor.simulate_random_delay().await; + let claims = live_kit_server::token::validate(&token, &self.secret_key)?; let identity = claims.sub.unwrap().to_string(); let room_name = claims.video.room.unwrap(); @@ -539,8 +559,13 @@ impl Room { pub fn display_sources(self: &Arc) -> impl Future>> { let this = self.clone(); async move { - let server = this.test_server(); - server.executor.simulate_random_delay().await; + //todo!(linux): Remove this once the cross-platform LiveKit implementation is merged + #[cfg(any(test, feature = "test-support"))] + { + let server = this.test_server(); + server.executor.simulate_random_delay().await; + } + Ok(this.0.lock().display_sources.clone()) } }