Detailed changes
@@ -4888,7 +4888,6 @@ dependencies = [
"log",
"media",
"metal",
- "mio 1.0.0",
"num_cpus",
"objc",
"oo7",
@@ -5689,7 +5688,7 @@ dependencies = [
"fnv",
"lazy_static",
"libc",
- "mio 0.8.11",
+ "mio",
"rand 0.8.5",
"serde",
"tempfile",
@@ -6619,19 +6618,6 @@ dependencies = [
"windows-sys 0.48.0",
]
-[[package]]
-name = "mio"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4929e1f84c5e54c3ec6141cd5d8b5a5c055f031f80cf78f2072920173cb4d880"
-dependencies = [
- "hermit-abi 0.3.9",
- "libc",
- "log",
- "wasi 0.11.0+wasi-snapshot-preview1",
- "windows-sys 0.52.0",
-]
-
[[package]]
name = "miow"
version = "0.6.0"
@@ -6870,7 +6856,7 @@ dependencies = [
"kqueue",
"libc",
"log",
- "mio 0.8.11",
+ "mio",
"walkdir",
"windows-sys 0.48.0",
]
@@ -11080,7 +11066,7 @@ dependencies = [
"backtrace",
"bytes 1.5.0",
"libc",
- "mio 0.8.11",
+ "mio",
"num_cpus",
"parking_lot",
"pin-project-lite",
@@ -141,7 +141,6 @@ xim = { git = "https://github.com/npmania/xim-rs", rev = "27132caffc5b9bc9c432ca
] }
font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "5a5c4d4", features = ["source-fontconfig-dlopen"] }
x11-clipboard = "0.9.2"
-mio = { version = "1.0.0", features = ["os-poll", "os-ext"] }
[target.'cfg(windows)'.dependencies]
windows.workspace = true
@@ -5,11 +5,9 @@ use calloop::{
timer::TimeoutAction,
EventLoop,
};
-use mio::Waker;
use parking::{Parker, Unparker};
use parking_lot::Mutex;
use std::{
- sync::Arc,
thread,
time::{Duration, Instant},
};
@@ -23,7 +21,6 @@ struct TimerAfter {
pub(crate) struct LinuxDispatcher {
parker: Mutex<Parker>,
main_sender: Sender<Runnable>,
- main_waker: Option<Arc<Waker>>,
timer_sender: Sender<TimerAfter>,
background_sender: flume::Sender<Runnable>,
_background_threads: Vec<thread::JoinHandle<()>>,
@@ -31,7 +28,7 @@ pub(crate) struct LinuxDispatcher {
}
impl LinuxDispatcher {
- pub fn new(main_sender: Sender<Runnable>, main_waker: Option<Arc<Waker>>) -> Self {
+ pub fn new(main_sender: Sender<Runnable>) -> Self {
let (background_sender, background_receiver) = flume::unbounded::<Runnable>();
let thread_count = std::thread::available_parallelism()
.map(|i| i.get())
@@ -91,7 +88,6 @@ impl LinuxDispatcher {
Self {
parker: Mutex::new(Parker::new()),
main_sender,
- main_waker,
timer_sender,
background_sender,
_background_threads: background_threads,
@@ -111,9 +107,6 @@ impl PlatformDispatcher for LinuxDispatcher {
fn dispatch_on_main_thread(&self, runnable: Runnable) {
self.main_sender.send(runnable).ok();
- if let Some(main_waker) = self.main_waker.as_ref() {
- main_waker.wake().ok();
- }
}
fn dispatch_after(&self, duration: Duration, runnable: Runnable) {
@@ -22,7 +22,7 @@ impl HeadlessClient {
pub(crate) fn new() -> Self {
let event_loop = EventLoop::try_new().unwrap();
- let (common, main_receiver) = LinuxCommon::new(Box::new(event_loop.get_signal()), None);
+ let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal());
let handle = event_loop.handle();
@@ -28,7 +28,6 @@ use calloop::{EventLoop, LoopHandle, LoopSignal};
use filedescriptor::FileDescriptor;
use flume::{Receiver, Sender};
use futures::channel::oneshot;
-use mio::Waker;
use parking_lot::Mutex;
use time::UtcOffset;
use util::ResultExt;
@@ -88,16 +87,6 @@ pub(crate) struct PlatformHandlers {
pub(crate) validate_app_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>,
}
-pub trait QuitSignal {
- fn quit(&mut self);
-}
-
-impl QuitSignal for LoopSignal {
- fn quit(&mut self) {
- self.stop();
- }
-}
-
pub(crate) struct LinuxCommon {
pub(crate) background_executor: BackgroundExecutor,
pub(crate) foreground_executor: ForegroundExecutor,
@@ -105,20 +94,17 @@ pub(crate) struct LinuxCommon {
pub(crate) appearance: WindowAppearance,
pub(crate) auto_hide_scrollbars: bool,
pub(crate) callbacks: PlatformHandlers,
- pub(crate) quit_signal: Box<dyn QuitSignal>,
+ pub(crate) signal: LoopSignal,
pub(crate) menus: Vec<OwnedMenu>,
}
impl LinuxCommon {
- pub fn new(
- quit_signal: Box<dyn QuitSignal>,
- main_waker: Option<Arc<Waker>>,
- ) -> (Self, Channel<Runnable>) {
+ pub fn new(signal: LoopSignal) -> (Self, Channel<Runnable>) {
let (main_sender, main_receiver) = calloop::channel::channel::<Runnable>();
let text_system = Arc::new(CosmicTextSystem::new());
let callbacks = PlatformHandlers::default();
- let dispatcher = Arc::new(LinuxDispatcher::new(main_sender.clone(), main_waker));
+ let dispatcher = Arc::new(LinuxDispatcher::new(main_sender.clone()));
let background_executor = BackgroundExecutor::new(dispatcher.clone());
@@ -129,7 +115,7 @@ impl LinuxCommon {
appearance: WindowAppearance::Light,
auto_hide_scrollbars: false,
callbacks,
- quit_signal,
+ signal,
menus: Vec::new(),
};
@@ -163,7 +149,7 @@ impl<P: LinuxClient + 'static> Platform for P {
}
fn quit(&self) {
- self.with_common(|common| common.quit_signal.quit());
+ self.with_common(|common| common.signal.stop());
}
fn compositor_name(&self) -> &'static str {
@@ -326,7 +326,7 @@ impl WaylandClientStatePtr {
}
}
if state.windows.is_empty() {
- state.common.quit_signal.quit();
+ state.common.signal.stop();
}
}
}
@@ -422,7 +422,7 @@ impl WaylandClient {
let event_loop = EventLoop::<WaylandClientStatePtr>::try_new().unwrap();
- let (common, main_receiver) = LinuxCommon::new(Box::new(event_loop.get_signal()), None);
+ let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal());
let handle = event_loop.handle();
handle
@@ -459,7 +459,7 @@ impl WaylandClient {
let mut cursor = Cursor::new(&conn, &globals, 24);
handle
- .insert_source(XDPEventSource::new(&common.background_executor, None), {
+ .insert_source(XDPEventSource::new(&common.background_executor), {
move |event, _, client| match event {
XDPEvent::WindowAppearance(appearance) => {
if let Some(client) = client.0.upgrade() {
@@ -1,27 +1,23 @@
use std::cell::RefCell;
use std::collections::HashSet;
use std::ops::Deref;
-use std::os::fd::AsRawFd;
use std::path::PathBuf;
use std::rc::{Rc, Weak};
-use std::sync::Arc;
use std::time::{Duration, Instant};
-use anyhow::Context;
-use async_task::Runnable;
-use calloop::channel::Channel;
+use calloop::generic::{FdWrapper, Generic};
+use calloop::{EventLoop, LoopHandle, RegistrationToken};
use collections::HashMap;
-
-use futures::channel::oneshot;
-use mio::{Interest, Token, Waker};
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 _};
+use x11rb::protocol::xproto::{ChangeWindowAttributesAux, ConnectionExt as _, KeyPressEvent};
use x11rb::protocol::{randr, render, xinput, xkb, xproto, Event};
use x11rb::resource_manager::Database;
use x11rb::xcb_ffi::XCBConnection;
@@ -35,7 +31,7 @@ use crate::platform::{LinuxCommon, PlatformWindow};
use crate::{
modifiers_from_xinput_info, point, px, AnyWindowHandle, Bounds, ClipboardItem, CursorStyle,
DisplayId, Keystroke, Modifiers, ModifiersChangedEvent, Pixels, Platform, PlatformDisplay,
- PlatformInput, Point, QuitSignal, ScrollDelta, Size, TouchPhase, WindowParams, X11Window,
+ PlatformInput, Point, ScrollDelta, Size, TouchPhase, WindowParams, X11Window,
};
use super::{button_of_key, modifiers_from_state, pressed_button_from_mask};
@@ -51,6 +47,7 @@ pub(super) const XINPUT_MASTER_DEVICE: u16 = 1;
pub(crate) struct WindowRef {
window: X11WindowStatePtr,
+ refresh_event_token: RegistrationToken,
}
impl WindowRef {
@@ -98,18 +95,15 @@ impl From<xim::ClientError> for EventHandlerError {
}
pub struct X11ClientState {
- /// poll is in an Option so we can take it out in `run()` without
- /// mutating self.
- poll: Option<mio::Poll>,
- quit_signal_rx: oneshot::Receiver<()>,
- runnables: Channel<Runnable>,
- xdp_event_source: XDPEventSource,
+ pub(crate) loop_handle: LoopHandle<'static, X11Client>,
+ pub(crate) event_loop: Option<calloop::EventLoop<'static, X11Client>>,
pub(crate) last_click: Instant,
pub(crate) last_location: Point<Pixels>,
pub(crate) current_count: usize,
pub(crate) scale_factor: f32,
+
pub(crate) xcb_connection: Rc<XCBConnection>,
pub(crate) x_root_index: usize,
pub(crate) _resource_database: Database,
@@ -146,11 +140,8 @@ impl X11ClientStatePtr {
let client = X11Client(self.0.upgrade().expect("client already dropped"));
let mut state = client.0.borrow_mut();
- if state.windows.remove(&x_window).is_none() {
- log::warn!(
- "failed to remove X window {} from client state, does not exist",
- x_window
- );
+ if let Some(window_ref) = state.windows.remove(&x_window) {
+ state.loop_handle.remove(window_ref.refresh_event_token);
}
if state.mouse_focused_window == Some(x_window) {
state.mouse_focused_window = None;
@@ -161,36 +152,7 @@ impl X11ClientStatePtr {
state.cursor_styles.remove(&x_window);
if state.windows.is_empty() {
- state.common.quit_signal.quit();
- }
- }
-}
-
-struct ChannelQuitSignal {
- tx: Option<oneshot::Sender<()>>,
- waker: Option<Arc<Waker>>,
-}
-
-impl ChannelQuitSignal {
- fn new(waker: Option<Arc<Waker>>) -> (Self, oneshot::Receiver<()>) {
- let (tx, rx) = oneshot::channel::<()>();
-
- let quit_signal = ChannelQuitSignal {
- tx: Some(tx),
- waker,
- };
-
- (quit_signal, rx)
- }
-}
-
-impl QuitSignal for ChannelQuitSignal {
- fn quit(&mut self) {
- if let Some(tx) = self.tx.take() {
- tx.send(()).log_err();
- if let Some(waker) = self.waker.as_ref() {
- waker.wake().ok();
- }
+ state.common.signal.stop();
}
}
}
@@ -200,12 +162,27 @@ pub(crate) struct X11Client(Rc<RefCell<X11ClientState>>);
impl X11Client {
pub(crate) fn new() -> Self {
- let mut poll = mio::Poll::new().unwrap();
-
- let waker = Arc::new(Waker::new(poll.registry(), WAKER_TOKEN).unwrap());
-
- let (quit_signal, quit_signal_rx) = ChannelQuitSignal::new(Some(waker.clone()));
- let (common, runnables) = LinuxCommon::new(Box::new(quit_signal), Some(waker.clone()));
+ let event_loop = EventLoop::try_new().unwrap();
+
+ let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal());
+
+ let handle = event_loop.handle();
+
+ handle
+ .insert_source(main_receiver, {
+ let handle = handle.clone();
+ move |event, _, _: &mut X11Client| {
+ if let calloop::channel::Event::Msg(runnable) = event {
+ // Insert the runnables as idle callbacks, so we make sure that user-input and X11
+ // events have higher priority and runnables are only worked off after the event
+ // callbacks.
+ handle.insert_idle(|_| {
+ runnable.run();
+ });
+ }
+ }
+ })
+ .unwrap();
let (xcb_connection, x_root_index) = XCBConnection::connect(None).unwrap();
xcb_connection
@@ -304,18 +281,47 @@ impl X11Client {
None
};
- let xdp_event_source =
- XDPEventSource::new(&common.background_executor, Some(waker.clone()));
-
- X11Client(Rc::new(RefCell::new(X11ClientState {
- poll: Some(poll),
- runnables,
+ // Safety: Safe if xcb::Connection always returns a valid fd
+ let fd = unsafe { FdWrapper::new(Rc::clone(&xcb_connection)) };
+
+ handle
+ .insert_source(
+ Generic::new_with_error::<EventHandlerError>(
+ fd,
+ calloop::Interest::READ,
+ calloop::Mode::Level,
+ ),
+ {
+ let xcb_connection = xcb_connection.clone();
+ move |_readiness, _, client| {
+ client.process_x11_events(&xcb_connection)?;
+ Ok(calloop::PostAction::Continue)
+ }
+ },
+ )
+ .expect("Failed to initialize x11 event source");
- xdp_event_source,
- quit_signal_rx,
- common,
+ handle
+ .insert_source(XDPEventSource::new(&common.background_executor), {
+ move |event, _, client| match event {
+ XDPEvent::WindowAppearance(appearance) => {
+ client.with_common(|common| common.appearance = appearance);
+ for (_, window) in &mut client.0.borrow_mut().windows {
+ window.window.set_appearance(appearance);
+ }
+ }
+ XDPEvent::CursorTheme(_) | XDPEvent::CursorSize(_) => {
+ // noop, X11 manages this for us.
+ }
+ }
+ })
+ .unwrap();
+ X11Client(Rc::new(RefCell::new(X11ClientState {
modifiers: Modifiers::default(),
+ event_loop: Some(event_loop),
+ loop_handle: handle,
+ common,
last_click: Instant::now(),
last_location: Point::new(px(0.0), px(0.0)),
current_count: 0,
@@ -349,6 +355,125 @@ impl X11Client {
})))
}
+ pub fn process_x11_events(
+ &self,
+ xcb_connection: &XCBConnection,
+ ) -> Result<(), EventHandlerError> {
+ loop {
+ let mut events = Vec::new();
+ let mut windows_to_refresh = HashSet::new();
+
+ let mut last_key_release = None;
+ let mut last_key_press: Option<KeyPressEvent> = None;
+
+ loop {
+ match xcb_connection.poll_for_event() {
+ Ok(Some(event)) => {
+ match event {
+ Event::Expose(expose_event) => {
+ windows_to_refresh.insert(expose_event.window);
+ }
+ Event::KeyRelease(_) => {
+ last_key_release = Some(event);
+ }
+ Event::KeyPress(key_press) => {
+ if let Some(last_press) = last_key_press.as_ref() {
+ if last_press.detail == key_press.detail {
+ continue;
+ }
+ }
+
+ if let Some(Event::KeyRelease(key_release)) =
+ last_key_release.take()
+ {
+ // We ignore that last KeyRelease if it's too close to this KeyPress,
+ // suggesting that it's auto-generated by X11 as a key-repeat event.
+ if key_release.detail != key_press.detail
+ || key_press.time.saturating_sub(key_release.time) > 20
+ {
+ events.push(Event::KeyRelease(key_release));
+ }
+ }
+ events.push(Event::KeyPress(key_press));
+ last_key_press = Some(key_press);
+ }
+ _ => {
+ if let Some(release_event) = last_key_release.take() {
+ events.push(release_event);
+ }
+ events.push(event);
+ }
+ }
+ }
+ Ok(None) => {
+ // Add any remaining stored KeyRelease event
+ if let Some(release_event) = last_key_release.take() {
+ events.push(release_event);
+ }
+ break;
+ }
+ Err(e) => {
+ log::warn!("error polling for X11 events: {e:?}");
+ break;
+ }
+ }
+ }
+
+ if events.is_empty() && windows_to_refresh.is_empty() {
+ break;
+ }
+
+ for window in windows_to_refresh.into_iter() {
+ if let Some(window) = self.get_window(window) {
+ window.refresh();
+ }
+ }
+
+ for event in events.into_iter() {
+ let mut state = self.0.borrow_mut();
+ if state.ximc.is_none() || state.xim_handler.is_none() {
+ drop(state);
+ self.handle_event(event);
+ continue;
+ }
+
+ let mut ximc = state.ximc.take().unwrap();
+ let mut xim_handler = state.xim_handler.take().unwrap();
+ let xim_connected = xim_handler.connected;
+ drop(state);
+
+ let xim_filtered = match ximc.filter_event(&event, &mut xim_handler) {
+ Ok(handled) => handled,
+ Err(err) => {
+ log::error!("XIMClientError: {}", err);
+ false
+ }
+ };
+ let xim_callback_event = xim_handler.last_callback_event.take();
+
+ let mut state = self.0.borrow_mut();
+ state.ximc = Some(ximc);
+ state.xim_handler = Some(xim_handler);
+ drop(state);
+
+ if let Some(event) = xim_callback_event {
+ self.handle_xim_callback_event(event);
+ }
+
+ if xim_filtered {
+ continue;
+ }
+
+ if xim_connected {
+ self.xim_handle_event(event);
+ } else {
+ self.handle_event(event);
+ }
+ }
+ }
+ Ok(())
+ }
+
pub fn enable_ime(&self) {
let mut state = self.0.borrow_mut();
if state.ximc.is_none() {
@@ -411,117 +536,6 @@ impl X11Client {
.map(|window_reference| window_reference.window.clone())
}
- fn read_x11_events(&self) -> (HashSet<u32>, Vec<Event>) {
- let mut events = Vec::new();
- let mut windows_to_refresh = HashSet::new();
- let mut state = self.0.borrow_mut();
-
- let mut last_key_release: Option<Event> = None;
-
- loop {
- match state.xcb_connection.poll_for_event() {
- Ok(Some(event)) => {
- if let Event::Expose(expose_event) = event {
- windows_to_refresh.insert(expose_event.window);
- } else {
- match event {
- Event::KeyRelease(_) => {
- last_key_release = Some(event);
- }
- Event::KeyPress(key_press) => {
- if let Some(Event::KeyRelease(key_release)) =
- last_key_release.take()
- {
- // We ignore that last KeyRelease if it's too close to this KeyPress,
- // suggesting that it's auto-generated by X11 as a key-repeat event.
- if key_release.detail != key_press.detail
- || key_press.time.wrapping_sub(key_release.time) > 20
- {
- events.push(Event::KeyRelease(key_release));
- }
- }
- events.push(Event::KeyPress(key_press));
- }
- _ => {
- if let Some(release_event) = last_key_release.take() {
- events.push(release_event);
- }
- events.push(event);
- }
- }
- }
- }
- Ok(None) => {
- // Add any remaining stored KeyRelease event
- if let Some(release_event) = last_key_release.take() {
- events.push(release_event);
- }
- break;
- }
- Err(e) => {
- log::warn!("error polling for X11 events: {e:?}");
- break;
- }
- }
- }
-
- (windows_to_refresh, events)
- }
-
- fn process_x11_events(&self, events: Vec<Event>) {
- log::trace!(
- "main thread: processing X11 events. events: {}",
- events.len()
- );
-
- for event in events.into_iter() {
- log::trace!("main thread: processing X11 event: {:?}", event);
-
- let mut state = self.0.borrow_mut();
- if state.ximc.is_none() || state.xim_handler.is_none() {
- drop(state);
- self.handle_event(event);
- continue;
- }
-
- let mut ximc = state.ximc.take().unwrap();
- let mut xim_handler = state.xim_handler.take().unwrap();
- let xim_connected = xim_handler.connected;
- drop(state);
-
- // let xim_filtered = false;
- let xim_filtered = match ximc.filter_event(&event, &mut xim_handler) {
- Ok(handled) => handled,
- Err(err) => {
- log::error!("XIMClientError: {}", err);
- false
- }
- };
- let xim_callback_event = xim_handler.last_callback_event.take();
-
- let mut state = self.0.borrow_mut();
- state.ximc = Some(ximc);
- state.xim_handler = Some(xim_handler);
-
- if let Some(event) = xim_callback_event {
- drop(state);
- self.handle_xim_callback_event(event);
- } else {
- drop(state);
- }
-
- if xim_filtered {
- continue;
- }
-
- if xim_connected {
- self.xim_handle_event(event);
- } else {
- self.handle_event(event);
- }
- }
- }
-
fn handle_event(&self, event: Event) -> Option<()> {
match event {
Event::ClientMessage(event) => {
@@ -561,10 +575,6 @@ impl X11Client {
let window = self.get_window(event.window)?;
window.property_notify(event);
}
- Event::Expose(event) => {
- let window = self.get_window(event.window)?;
- window.refresh();
- }
Event::FocusIn(event) if event.mode == xproto::NotifyMode::NORMAL => {
let window = self.get_window(event.event)?;
window.set_focused(true);
@@ -979,13 +989,11 @@ impl X11Client {
}
}
-const XCB_CONNECTION_TOKEN: Token = Token(0);
-const WAKER_TOKEN: Token = Token(1);
-
impl LinuxClient for X11Client {
fn compositor_name(&self) -> &'static str {
"X11"
}
+
fn with_common<R>(&self, f: impl FnOnce(&mut LinuxCommon) -> R) -> R {
f(&mut self.0.borrow_mut().common)
}
@@ -1051,8 +1059,61 @@ 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 refresh_duration = mode_refresh_rate(mode);
+ move |mut instant, (), client| {
+ let xcb_connection = {
+ let state = client.0.borrow_mut();
+ let xcb_connection = state.xcb_connection.clone();
+ if let Some(window) = state.windows.get(&x_window) {
+ let window = window.window.clone();
+ drop(state);
+ window.refresh();
+ }
+ xcb_connection
+ };
+ client.process_x11_events(&xcb_connection).log_err();
+
+ // Take into account that some frames have been skipped
+ let now = Instant::now();
+ while instant < now {
+ instant += refresh_duration;
+ }
+ calloop::timer::TimeoutAction::ToInstant(instant)
+ }
+ })
+ .expect("Failed to initialize refresh timer");
+
let window_ref = WindowRef {
window: window.0.clone(),
+ refresh_event_token,
};
state.windows.insert(x_window, window_ref);
@@ -1181,134 +1242,14 @@ impl LinuxClient for X11Client {
}
fn run(&self) {
- let mut poll = self
+ let mut event_loop = self
.0
.borrow_mut()
- .poll
+ .event_loop
.take()
- .context("no poll set on X11Client. calling run more than once is not possible")
- .unwrap();
-
- let xcb_fd = self.0.borrow().xcb_connection.as_raw_fd();
- let mut xcb_source = mio::unix::SourceFd(&xcb_fd);
- poll.registry()
- .register(&mut xcb_source, XCB_CONNECTION_TOKEN, Interest::READABLE)
- .unwrap();
-
- let mut events = mio::Events::with_capacity(1024);
- let mut next_refresh_needed = Instant::now();
-
- 'run_loop: loop {
- let poll_timeout = next_refresh_needed - Instant::now();
- // We rounding the poll_timeout down so `mio` doesn't round it up to the next higher milliseconds
- let poll_timeout = Duration::from_millis(poll_timeout.as_millis() as u64);
-
- if poll_timeout >= Duration::from_millis(1) {
- let _ = poll.poll(&mut events, Some(poll_timeout));
- };
-
- let mut state = self.0.borrow_mut();
-
- // Check if we need to quit
- if let Ok(Some(())) = state.quit_signal_rx.try_recv() {
- return;
- }
-
- // Redraw windows
- let now = Instant::now();
- if now > next_refresh_needed {
- // This will be pulled down to 16ms (or less) if a window is open
- let mut frame_length = Duration::from_millis(100);
-
- let mut windows = vec![];
- for (_, window_ref) in state.windows.iter() {
- if !window_ref.window.state.borrow().destroyed {
- frame_length = frame_length.min(window_ref.window.refresh_rate());
- windows.push(window_ref.window.clone());
- }
- }
-
- drop(state);
-
- for window in windows {
- window.refresh();
- }
-
- state = self.0.borrow_mut();
-
- // In the case that we're looping a bit too fast, slow down
- next_refresh_needed = now.max(next_refresh_needed) + frame_length;
- }
-
- // X11 events
- drop(state);
-
- loop {
- let (x_windows, events) = self.read_x11_events();
- for x_window in x_windows {
- if let Some(window) = self.get_window(x_window) {
- log::trace!(
- "main thread: refreshing window {} due expose event",
- window.x_window
- );
- window.refresh();
- }
- }
-
- if events.len() == 0 {
- break;
- }
- self.process_x11_events(events);
-
- // When X11 is sending us events faster than we can handle we'll
- // let the frame rate drop to 10fps to try and avoid getting too behind.
- if Instant::now() > next_refresh_needed + Duration::from_millis(80) {
- continue 'run_loop;
- }
- }
-
- state = self.0.borrow_mut();
-
- // Runnables
- while let Ok(runnable) = state.runnables.try_recv() {
- drop(state);
-
- let start = Instant::now();
-
- runnable.run();
-
- log::trace!("main thread: ran runnable. took: {:?}", start.elapsed());
-
- state = self.0.borrow_mut();
+ .expect("App is already running");
- if Instant::now() + Duration::from_millis(1) >= next_refresh_needed {
- continue 'run_loop;
- }
- }
-
- // XDG events
- if let Ok(event) = state.xdp_event_source.try_recv() {
- log::trace!("main thread: XDG event");
- match event {
- XDPEvent::WindowAppearance(appearance) => {
- let mut windows = state
- .windows
- .values()
- .map(|window| window.window.clone())
- .collect::<Vec<_>>();
- drop(state);
-
- self.with_common(|common| common.appearance = appearance);
- for mut window in windows {
- window.set_appearance(appearance);
- }
- }
- XDPEvent::CursorTheme(_) | XDPEvent::CursorSize(_) => {
- // noop, X11 manages this for us.
- }
- };
- };
- }
+ event_loop.run(None, &mut self.clone(), |_| {}).log_err();
}
fn active_window(&self) -> Option<AnyWindowHandle> {
@@ -1322,6 +1263,19 @@ 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 {
+ if mode.dot_clock == 0 || mode.htotal == 0 || mode.vtotal == 0 {
+ return Duration::from_millis(16);
+ }
+
+ 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
}
@@ -15,7 +15,6 @@ use util::{maybe, ResultExt};
use x11rb::{
connection::Connection,
protocol::{
- randr::{self, ConnectionExt as _},
sync,
xinput::{self, ConnectionExt as _},
xproto::{self, ClientMessageEvent, ConnectionExt, EventMask, TranslateCoordinatesReply},
@@ -26,7 +25,7 @@ use x11rb::{
use std::{
cell::RefCell, ffi::c_void, mem::size_of, num::NonZeroU32, ops::Div, ptr::NonNull, rc::Rc,
- sync::Arc, time::Duration,
+ sync::Arc,
};
use super::{X11Display, XINPUT_MASTER_DEVICE};
@@ -220,7 +219,6 @@ pub struct Callbacks {
pub struct X11WindowState {
pub destroyed: bool,
- refresh_rate: Duration,
client: X11ClientStatePtr,
executor: ForegroundExecutor,
atoms: XcbAtoms,
@@ -257,7 +255,7 @@ pub(crate) struct X11WindowStatePtr {
pub state: Rc<RefCell<X11WindowState>>,
pub(crate) callbacks: Rc<RefCell<Callbacks>>,
xcb_connection: Rc<XCBConnection>,
- pub x_window: xproto::Window,
+ x_window: xproto::Window,
}
impl rwh::HasWindowHandle for RawWindow {
@@ -493,31 +491,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,
@@ -545,7 +518,6 @@ impl X11WindowState {
edge_constraints: None,
counter_id: sync_request_counter,
last_sync_counter: None,
- refresh_rate,
})
}
@@ -953,10 +925,6 @@ impl X11WindowStatePtr {
(fun)()
}
}
-
- pub fn refresh_rate(&self) -> Duration {
- self.state.borrow().refresh_rate
- }
}
impl PlatformWindow for X11Window {
@@ -1369,16 +1337,3 @@ impl PlatformWindow for X11Window {
}
}
}
-
-// Adapted 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 {
- if mode.dot_clock == 0 || mode.htotal == 0 || mode.vtotal == 0 {
- return Duration::from_millis(16);
- }
-
- 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)
-}
@@ -2,13 +2,9 @@
//!
//! This module uses the [ashpd] crate
-use std::sync::Arc;
-
-use anyhow::anyhow;
use ashpd::desktop::settings::{ColorScheme, Settings};
-use calloop::channel::{Channel, Sender};
+use calloop::channel::Channel;
use calloop::{EventSource, Poll, PostAction, Readiness, Token, TokenFactory};
-use mio::Waker;
use smol::stream::StreamExt;
use crate::{BackgroundExecutor, WindowAppearance};
@@ -24,45 +20,31 @@ pub struct XDPEventSource {
}
impl XDPEventSource {
- pub fn new(executor: &BackgroundExecutor, waker: Option<Arc<Waker>>) -> Self {
+ pub fn new(executor: &BackgroundExecutor) -> Self {
let (sender, channel) = calloop::channel::channel();
let background = executor.clone();
executor
.spawn(async move {
- fn send_event<T>(
- sender: &Sender<T>,
- waker: &Option<Arc<Waker>>,
- event: T,
- ) -> Result<(), std::sync::mpsc::SendError<T>> {
- sender.send(event)?;
- if let Some(waker) = waker {
- waker.wake().ok();
- };
- Ok(())
- }
-
let settings = Settings::new().await?;
if let Ok(initial_appearance) = settings.color_scheme().await {
- send_event(
- &sender,
- &waker,
- Event::WindowAppearance(WindowAppearance::from_native(initial_appearance)),
- )?;
+ sender.send(Event::WindowAppearance(WindowAppearance::from_native(
+ initial_appearance,
+ )))?;
}
if let Ok(initial_theme) = settings
.read::<String>("org.gnome.desktop.interface", "cursor-theme")
.await
{
- send_event(&sender, &waker, Event::CursorTheme(initial_theme))?;
+ sender.send(Event::CursorTheme(initial_theme))?;
}
if let Ok(initial_size) = settings
.read::<u32>("org.gnome.desktop.interface", "cursor-size")
.await
{
- send_event(&sender, &waker, Event::CursorSize(initial_size))?;
+ sender.send(Event::CursorSize(initial_size))?;
}
if let Ok(mut cursor_theme_changed) = settings
@@ -73,12 +55,11 @@ impl XDPEventSource {
.await
{
let sender = sender.clone();
- let waker = waker.clone();
background
.spawn(async move {
while let Some(theme) = cursor_theme_changed.next().await {
let theme = theme?;
- send_event(&sender, &waker, Event::CursorTheme(theme))?;
+ sender.send(Event::CursorTheme(theme))?;
}
anyhow::Ok(())
})
@@ -93,12 +74,11 @@ impl XDPEventSource {
.await
{
let sender = sender.clone();
- let waker = waker.clone();
background
.spawn(async move {
while let Some(size) = cursor_size_changed.next().await {
let size = size?;
- send_event(&sender, &waker, Event::CursorSize(size))?;
+ sender.send(Event::CursorSize(size))?;
}
anyhow::Ok(())
})
@@ -107,11 +87,9 @@ impl XDPEventSource {
let mut appearance_changed = settings.receive_color_scheme_changed().await?;
while let Some(scheme) = appearance_changed.next().await {
- send_event(
- &sender,
- &waker,
- Event::WindowAppearance(WindowAppearance::from_native(scheme)),
- )?;
+ sender.send(Event::WindowAppearance(WindowAppearance::from_native(
+ scheme,
+ )))?;
}
anyhow::Ok(())
@@ -120,12 +98,6 @@ impl XDPEventSource {
Self { channel }
}
-
- pub fn try_recv(&self) -> anyhow::Result<Event> {
- self.channel
- .try_recv()
- .map_err(|error| anyhow!("{}", error))
- }
}
impl EventSource for XDPEventSource {