diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index bde4dfb4fa..f2ad5b9a86 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -13,6 +13,7 @@ use util::ResultExt; use x11rb::connection::{Connection, RequestConnection}; use x11rb::cursor; use x11rb::errors::ConnectionError; +use x11rb::protocol::randr::ConnectionExt as _; use x11rb::protocol::xinput::ConnectionExt; use x11rb::protocol::xkb::ConnectionExt as _; use x11rb::protocol::xproto::{ChangeWindowAttributesAux, ConnectionExt as _}; @@ -45,7 +46,7 @@ use crate::platform::linux::xdg_desktop_portal::{Event as XDPEvent, XDPEventSour pub(super) const XINPUT_MASTER_DEVICE: u16 = 1; pub(crate) struct WindowRef { - pub window: X11WindowStatePtr, + window: X11WindowStatePtr, refresh_event_token: RegistrationToken, } @@ -297,33 +298,7 @@ impl X11Client { { let xcb_connection = xcb_connection.clone(); move |_readiness, _, client| { - let windows = client - .0 - .borrow() - .windows - .values() - .map(|window_ref| window_ref.window.clone()) - .collect::>(); - while let Some(event) = xcb_connection.poll_for_event()? { - for window in &windows { - let last_render_at; - let refresh_rate; - { - let window_state = window.state.borrow(); - last_render_at = window_state.last_render_at; - refresh_rate = window_state.refresh_rate; - } - - if let Some(last_render_at) = last_render_at { - if last_render_at.elapsed() >= refresh_rate { - window.refresh(); - } - } else { - window.refresh(); - } - } - let mut state = client.0.borrow_mut(); if state.ximc.is_none() || state.xim_handler.is_none() { drop(state); @@ -980,18 +955,60 @@ impl LinuxClient for X11Client { state.common.appearance, )?; + let screen_resources = state + .xcb_connection + .randr_get_screen_resources(x_window) + .unwrap() + .reply() + .expect("Could not find available screens"); + + let mode = screen_resources + .crtcs + .iter() + .find_map(|crtc| { + let crtc_info = state + .xcb_connection + .randr_get_crtc_info(*crtc, x11rb::CURRENT_TIME) + .ok()? + .reply() + .ok()?; + + screen_resources + .modes + .iter() + .find(|m| m.id == crtc_info.mode) + }) + .expect("Unable to find screen refresh rate"); + let refresh_event_token = state .loop_handle .insert_source(calloop::timer::Timer::immediate(), { - let window = window.0.clone(); - let refresh_rate = window.state.borrow().refresh_rate; - move |mut instant, (), _| { - window.refresh(); - + let refresh_duration = mode_refresh_rate(mode); + move |mut instant, (), client| { + let state = client.0.borrow_mut(); + state + .xcb_connection + .send_event( + false, + x_window, + xproto::EventMask::EXPOSURE, + xproto::ExposeEvent { + response_type: xproto::EXPOSE_EVENT, + sequence: 0, + window: x_window, + x: 0, + y: 0, + width: 0, + height: 0, + count: 1, + }, + ) + .unwrap(); + let _ = state.xcb_connection.flush().unwrap(); // Take into account that some frames have been skipped let now = Instant::now(); while instant < now { - instant += refresh_rate; + instant += refresh_duration; } calloop::timer::TimeoutAction::ToInstant(instant) } @@ -1129,6 +1146,15 @@ impl LinuxClient for X11Client { } } +// Adatpted from: +// https://docs.rs/winit/0.29.11/src/winit/platform_impl/linux/x11/monitor.rs.html#103-111 +pub fn mode_refresh_rate(mode: &randr::ModeInfo) -> Duration { + let millihertz = mode.dot_clock as u64 * 1_000 / (mode.htotal as u64 * mode.vtotal as u64); + let micros = 1_000_000_000 / millihertz; + log::info!("Refreshing at {} micros", micros); + Duration::from_micros(micros) +} + fn fp3232_to_f32(value: xinput::Fp3232) -> f32 { value.integral as f32 + value.frac as f32 / u32::MAX as f32 } diff --git a/crates/gpui/src/platform/linux/x11/window.rs b/crates/gpui/src/platform/linux/x11/window.rs index 8f9bf5910e..4dea09393d 100644 --- a/crates/gpui/src/platform/linux/x11/window.rs +++ b/crates/gpui/src/platform/linux/x11/window.rs @@ -14,7 +14,6 @@ use util::{maybe, ResultExt}; use x11rb::{ connection::Connection, protocol::{ - randr::{self, ConnectionExt as _}, xinput::{self, ConnectionExt as _}, xproto::{ self, ClientMessageEvent, ConnectionExt as _, EventMask, TranslateCoordinatesReply, @@ -32,7 +31,6 @@ use std::{ ptr::NonNull, rc::Rc, sync::{self, Arc}, - time::{Duration, Instant}, }; use super::{X11Display, XINPUT_MASTER_DEVICE}; @@ -161,8 +159,6 @@ pub struct Callbacks { pub struct X11WindowState { pub destroyed: bool, - pub last_render_at: Option, - pub refresh_rate: Duration, client: X11ClientStatePtr, executor: ForegroundExecutor, atoms: XcbAtoms, @@ -401,31 +397,6 @@ impl X11WindowState { }; xcb_connection.map_window(x_window).unwrap(); - let screen_resources = xcb_connection - .randr_get_screen_resources(x_window) - .unwrap() - .reply() - .expect("Could not find available screens"); - - let mode = screen_resources - .crtcs - .iter() - .find_map(|crtc| { - let crtc_info = xcb_connection - .randr_get_crtc_info(*crtc, x11rb::CURRENT_TIME) - .ok()? - .reply() - .ok()?; - - screen_resources - .modes - .iter() - .find(|m| m.id == crtc_info.mode) - }) - .expect("Unable to find screen refresh rate"); - - let refresh_rate = mode_refresh_rate(mode); - Ok(Self { client, executor, @@ -442,8 +413,6 @@ impl X11WindowState { appearance, handle, destroyed: false, - last_render_at: None, - refresh_rate, }) } @@ -613,11 +582,6 @@ impl X11WindowStatePtr { let mut cb = self.callbacks.borrow_mut(); if let Some(ref mut fun) = cb.request_frame { fun(); - - self.state - .borrow_mut() - .last_render_at - .replace(Instant::now()); } } @@ -1064,12 +1028,3 @@ impl PlatformWindow for X11Window { false } } - -// Adatpted from: -// https://docs.rs/winit/0.29.11/src/winit/platform_impl/linux/x11/monitor.rs.html#103-111 -pub fn mode_refresh_rate(mode: &randr::ModeInfo) -> Duration { - let millihertz = mode.dot_clock as u64 * 1_000 / (mode.htotal as u64 * mode.vtotal as u64); - let micros = 1_000_000_000 / millihertz; - log::info!("Refreshing at {} micros", micros); - Duration::from_micros(micros) -}