-
Notifications
You must be signed in to change notification settings - Fork 570
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement timers for the X11 platform #1096
Changes from 7 commits
fca006d
518a451
605bbbf
17f49c4
50dd017
3bed70b
9afbbfc
4953e9b
ca9a7b6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -381,7 +381,18 @@ impl Application { | |
let timeout = Duration::from_millis((1000.0 / refresh_rate) as u64); | ||
let mut last_idle_time = Instant::now(); | ||
loop { | ||
// Figure out when the next wakeup needs to happen | ||
let next_timeout = if let Ok(state) = self.state.try_borrow() { | ||
state.windows | ||
.values() | ||
.filter_map(|w| w.next_timeout()) | ||
.min() | ||
} else { | ||
log::error!("Getting next timeout, application state already borrowed"); | ||
None | ||
}; | ||
let next_idle_time = last_idle_time + timeout; | ||
|
||
self.connection.flush()?; | ||
|
||
// Before we poll on the connection's file descriptor, check whether there are any | ||
|
@@ -390,7 +401,7 @@ impl Application { | |
let mut event = self.connection.poll_for_event()?; | ||
|
||
if event.is_none() { | ||
poll_with_timeout(&self.connection, self.idle_read, next_idle_time) | ||
poll_with_timeout(&self.connection, self.idle_read, next_timeout, next_idle_time) | ||
.context("Error while waiting for X11 connection")?; | ||
} | ||
|
||
|
@@ -409,6 +420,17 @@ impl Application { | |
} | ||
|
||
let now = Instant::now(); | ||
if let Some(timeout) = next_timeout { | ||
if timeout <= now { | ||
if let Ok(state) = self.state.try_borrow() { | ||
for w in state.windows.values() { | ||
w.run_timers(now); | ||
} | ||
} else { | ||
log::error!("In timer loop, application state already borrowed"); | ||
} | ||
} | ||
} | ||
if now >= next_idle_time { | ||
last_idle_time = now; | ||
drain_idle_pipe(self.idle_read)?; | ||
|
@@ -510,11 +532,13 @@ fn drain_idle_pipe(idle_read: RawFd) -> Result<(), Error> { | |
/// writing into our idle pipe and the `timeout` has passed. | ||
// This was taken, with minor modifications, from the xclock_utc example in the x11rb crate. | ||
// https://github.com/psychon/x11rb/blob/a6bd1453fd8e931394b9b1f2185fad48b7cca5fe/examples/xclock_utc.rs | ||
fn poll_with_timeout(conn: &Rc<XCBConnection>, idle: RawFd, timeout: Instant) -> Result<(), Error> { | ||
fn poll_with_timeout(conn: &Rc<XCBConnection>, idle: RawFd, timer_timeout: Option<Instant>, idle_timeout: Instant) -> Result<(), Error> { | ||
use nix::poll::{poll, PollFd, PollFlags}; | ||
use std::os::raw::c_int; | ||
use std::os::unix::io::AsRawFd; | ||
|
||
let mut now = Instant::now(); | ||
let earliest_timeout = idle_timeout.min(timer_timeout.unwrap_or(idle_timeout)); | ||
let fd = conn.as_raw_fd(); | ||
let mut both_poll_fds = [ | ||
PollFd::new(fd, PollFlags::POLLIN), | ||
|
@@ -525,36 +549,57 @@ fn poll_with_timeout(conn: &Rc<XCBConnection>, idle: RawFd, timeout: Instant) -> | |
|
||
// We start with no timeout in the poll call. If we get something from the idle handler, we'll | ||
// start setting one. | ||
let mut poll_timeout = -1; | ||
let mut honor_idle_timeout = false; | ||
loop { | ||
fn readable(p: PollFd) -> bool { | ||
p.revents() | ||
.unwrap_or_else(PollFlags::empty) | ||
.contains(PollFlags::POLLIN) | ||
}; | ||
|
||
// Compute the deadline for when poll() has to wakeup | ||
let deadline = if honor_idle_timeout { | ||
Some(earliest_timeout) | ||
} else { | ||
timer_timeout | ||
}; | ||
// ...and convert the deadline into an argument for poll() | ||
let poll_timeout = if let Some(deadline) = deadline { | ||
if deadline < now { | ||
0 | ||
} else { | ||
c_int::try_from(deadline.duration_since(now).as_millis()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess it would be best if this would round up instead of round down (in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think adding 1 ms is a good approach. |
||
.unwrap_or(c_int::max_value()) | ||
} | ||
} else { | ||
// No timeout | ||
-1 | ||
}; | ||
|
||
match poll(poll_fds, poll_timeout) { | ||
Ok(_) => { | ||
if readable(poll_fds[0]) { | ||
// There is an X11 event ready to be handled. | ||
break; | ||
} | ||
now = Instant::now(); | ||
if timer_timeout.is_some() && now >= timer_timeout.unwrap() { | ||
break; | ||
} | ||
if poll_fds.len() == 1 || readable(poll_fds[1]) { | ||
// Now that we got signalled, stop polling from the idle pipe and use a timeout | ||
// instead. | ||
poll_fds = &mut just_connection; | ||
|
||
let now = Instant::now(); | ||
if now >= timeout { | ||
honor_idle_timeout = true; | ||
if now >= idle_timeout { | ||
break; | ||
} else { | ||
poll_timeout = c_int::try_from(timeout.duration_since(now).as_millis()) | ||
.unwrap_or(c_int::max_value()) | ||
} | ||
} | ||
} | ||
|
||
Err(nix::Error::Sys(nix::errno::Errno::EINTR)) => {} | ||
Err(nix::Error::Sys(nix::errno::Errno::EINTR)) => { | ||
now = Instant::now(); | ||
} | ||
Err(e) => return Err(e.into()), | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just break here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A combination of "historic reasons" and "I do not follow understand the idle drawing code". The previous code definitely did not return early and might have went into a short busy loop. I think. I am not sure.
I'll just change it to a break (and also to
<=
instead of<
, but that shouldn't make much of a difference).