Skip to content
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

Use the polling crate in xclock_utc example #812

Merged
merged 1 commit into from
Apr 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions x11rb/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ winapi-wsapoll = "0.1.1"
version = "0.3"
features = ["winsock2"]

[dev-dependencies]
polling = "2.7.0"

[features]
# Without this feature, all uses of `unsafe` in the crate are forbidden via
# #![deny(unsafe_code)]. This has the effect of disabling the XCB FFI bindings.
Expand Down
106 changes: 34 additions & 72 deletions x11rb/examples/xclock_utc.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::convert::TryFrom;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};

use polling::{Event as PollingEvent, Poller};

use x11rb::atom_manager;
use x11rb::connection::Connection;
use x11rb::errors::{ConnectionError, ReplyOrIdError};
Expand Down Expand Up @@ -166,87 +167,39 @@ fn redraw(
Ok(())
}

#[cfg(unix)]
fn poll_with_timeout(
poller: &Poller,
conn: &RustConnection,
timeout: Duration,
) -> Result<(), Box<dyn std::error::Error>> {
use std::os::raw::c_int;
use std::os::unix::io::AsRawFd;
// Add interest in the connection's stream.
poller.add(conn.stream(), PollingEvent::readable(1))?;

use nix::poll::{poll, PollFd, PollFlags};
// Remove it if we time out.
let _guard = CallOnDrop(|| {
poller.delete(conn.stream()).ok();
});

let start_instant = Instant::now();
let fd = conn.stream().as_raw_fd();
let mut poll_fds = [PollFd::new(fd, PollFlags::POLLIN)];
// Wait for events.
let mut event = Vec::with_capacity(1);
let target = Instant::now() + timeout;
loop {
let timeout_millis = timeout
.checked_sub(start_instant.elapsed())
.map(|remaining| c_int::try_from(remaining.as_millis()).unwrap_or(c_int::max_value()))
.unwrap_or(0);
match poll(&mut poll_fds, timeout_millis) {
Ok(_) => {
if poll_fds[0]
.revents()
.unwrap_or_else(PollFlags::empty)
.contains(PollFlags::POLLIN)
{
break;
}
}
// try again
Err(nix::Error::EINTR) => {}
Err(e) => return Err(e.into()),
}
if start_instant.elapsed() >= timeout {
break;
}
}
// We do not really care about the result of poll. Either there was a timeout, in which case we
// try to handle events (there are none) and then redraw. Or there was an event, in which case
// we handle it and then still redraw.
Ok(())
}
let remaining = target.saturating_duration_since(Instant::now());
poller.wait(&mut event, Some(remaining))?;

#[cfg(windows)]
fn poll_with_timeout(
conn: &RustConnection,
timeout: Duration,
) -> Result<(), Box<dyn std::error::Error>> {
use std::os::windows::io::AsRawSocket;

use winapi::shared::minwindef::INT;
use winapi::um::winsock2::{POLLRDNORM, SOCKET, WSAPOLLFD};
use winapi_wsapoll::wsa_poll;

let start_instant = Instant::now();
let raw_socket = conn.stream().as_raw_socket();
let mut poll_fds = [WSAPOLLFD {
fd: raw_socket as SOCKET,
events: POLLRDNORM,
revents: 0,
}];
loop {
let timeout_millis = timeout
.checked_sub(start_instant.elapsed())
.map(|remaining| INT::try_from(remaining.as_millis()).unwrap_or(INT::max_value()))
.unwrap_or(0);
match wsa_poll(&mut poll_fds, timeout_millis) {
Ok(_) => {
if (poll_fds[0].revents & POLLRDNORM) != 0 {
break;
}
}
Err(e) => return Err(e.into()),
// If we received an event, we're done.
if event.contains(&PollingEvent::readable(1)) {
return Ok(());
}
if start_instant.elapsed() >= timeout {
break;

// If our timeout expired, we're done.
if Instant::now() >= target {
// We do not really care about the result of poll. Either there was a timeout, in which case we
// try to handle events (there are none) and then redraw. Or there was an event, in which case
// we handle it and then still redraw.
return Ok(());
}
}
// We do not really care about the result of poll. Either there was a timeout, in which case we
// try to handle events (there are none) and then redraw. Or there was an event, in which case
// we handle it and then still redraw.
Ok(())
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
Expand All @@ -255,6 +208,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
// The following is only needed for start_timeout_thread(), which is used for 'tests'
let conn1 = std::sync::Arc::new(conn);
let conn = &*conn1;
let poller = Poller::new()?;

let screen = &conn.setup().roots[screen_num];
let atoms = Atoms::new(conn)?.reply()?;
Expand All @@ -270,7 +224,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
conn.flush()?;

loop {
poll_with_timeout(conn, Duration::from_millis(1_000))?;
poll_with_timeout(&poller, conn, Duration::from_millis(1_000))?;
while let Some(event) = conn.poll_for_event()? {
println!("{:?})", event);
match event {
Expand Down Expand Up @@ -313,3 +267,11 @@ fn get_time() -> (u8, u8, u8) {
}

include!("integration_test_util/util.rs");

struct CallOnDrop<F: FnMut()>(F);

impl<F: FnMut()> Drop for CallOnDrop<F> {
fn drop(&mut self) {
(self.0)();
}
}