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 <mikayla@zed.dev>
This commit is contained in:
Dzmitry Malyshau 2024-02-14 12:24:12 -08:00 committed by GitHub
parent 181f556269
commit 8f7a26f397
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 78 additions and 23 deletions

View File

@ -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" }

View File

@ -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;
}

View File

@ -61,6 +61,7 @@
#![deny(missing_docs)]
#![allow(clippy::type_complexity)]
#![allow(clippy::collapsible_else_if)]
#[macro_use]
mod action;

View File

@ -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::*;

View File

@ -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);

View File

@ -41,6 +41,11 @@ impl X11Client {
}),
}
}
fn get_window(&self, win: x::Window) -> Rc<X11WindowState> {
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<dyn PlatformWindow> {
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()

View File

@ -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 {

View File

@ -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"

View File

@ -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<Room>) -> 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<Sid> {
//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<Sid> {
//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<Self>) -> impl Future<Output = Result<Vec<MacOSDisplay>>> {
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())
}
}