From eefff9a9cc2ee6f17eab3431cc808d5a5e7d31fc Mon Sep 17 00:00:00 2001 From: MaxVerevkin Date: Wed, 6 Sep 2023 20:51:24 +0300 Subject: implement simple event loop abstraction --- src/event_loop.rs | 73 +++++++++++++++++++++++++++++++++ src/main.rs | 120 +++++++++++++++++++++--------------------------------- 2 files changed, 119 insertions(+), 74 deletions(-) create mode 100644 src/event_loop.rs (limited to 'src') diff --git a/src/event_loop.rs b/src/event_loop.rs new file mode 100644 index 0000000..2300513 --- /dev/null +++ b/src/event_loop.rs @@ -0,0 +1,73 @@ +use std::collections::HashMap; +use std::io; +use std::os::fd::RawFd; + +use anyhow::Result; + +pub struct EventLoop { + pollfds: Vec, + cbs: HashMap Result>>, +} + +pub enum Action { + Keep, + Unregister, +} + +impl EventLoop { + pub fn new() -> Self { + Self { + pollfds: Vec::new(), + cbs: HashMap::new(), + } + } + + pub fn register_with_fd(&mut self, fd: RawFd, cb: F) + where + F: FnMut(&mut D) -> Result + 'static, + { + self.cbs.insert(fd, Box::new(cb)); + } + + pub fn run(&mut self, state: &mut D) -> Result<()> { + if self.cbs.is_empty() { + return Ok(()); + } + + self.pollfds.clear(); + for &fd in self.cbs.keys() { + self.pollfds.push(libc::pollfd { + fd, + events: libc::POLLIN, + revents: 0, + }); + } + + loop { + let result = + unsafe { libc::poll(self.pollfds.as_mut_ptr(), self.pollfds.len() as _, -1) }; + if result == -1 { + let err = io::Error::last_os_error(); + if err.kind() == io::ErrorKind::Interrupted { + continue; + } + return Err(err.into()); + } + break; + } + + for fd in &self.pollfds { + if fd.revents != 0 { + let mut cb = self.cbs.remove(&fd.fd).unwrap(); + match cb(state)? { + Action::Keep => { + self.cbs.insert(fd.fd, cb); + } + Action::Unregister => (), + } + } + } + + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs index fc525ad..54c1449 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ mod blocks_cache; mod button_manager; mod color; mod config; +mod event_loop; mod i3bar_protocol; mod output; mod pointer_btn; @@ -17,11 +18,12 @@ mod text; mod utils; mod wm_info_provider; -use std::io::{self, ErrorKind}; -use std::os::fd::{AsRawFd, RawFd}; +use std::io::ErrorKind; +use std::os::fd::AsRawFd; use std::path::PathBuf; use clap::Parser; +use event_loop::EventLoop; use nix::fcntl::OFlag; use signal_hook::consts::*; use wayrs_client::{Connection, IoMode}; @@ -43,86 +45,56 @@ fn main() -> anyhow::Result<()> { signal_hook::low_level::pipe::register(SIGUSR1, sig_write)?; let (mut conn, globals) = Connection::connect_and_collect_globals()?; - let mut state = State::new(&mut conn, &globals, args.config.as_deref()); + let state = State::new(&mut conn, &globals, args.config.as_deref()); conn.flush(IoMode::Blocking)?; - loop { - let poll = Poll::new(conn.as_raw_fd(), sig_read, state.status_cmd_fd())?; + let mut el = EventLoop::<(Connection, State)>::new(); - if poll.wayland { - match conn.recv_events(IoMode::NonBlocking) { - Ok(()) => { - conn.dispatch_events(&mut state); - conn.flush(IoMode::Blocking)?; - } - Err(e) if e.kind() == ErrorKind::WouldBlock => (), - Err(e) => bail!(e), - } - } + el.register_with_fd(sig_read, move |(conn, state)| { + nix::unistd::read(sig_read, &mut [0; 1])?; + state.toggle_visibility(conn); + Ok(event_loop::Action::Keep) + }); - if poll.signal { - nix::unistd::read(sig_read.as_raw_fd(), &mut [0; 1])?; - state.toggle_visibility(&mut conn); - conn.flush(IoMode::Blocking)?; + el.register_with_fd(conn.as_raw_fd(), |(conn, state)| { + match conn.recv_events(IoMode::NonBlocking) { + Ok(()) => { + conn.dispatch_events(state); + conn.flush(IoMode::Blocking)?; + } + Err(e) if e.kind() == ErrorKind::WouldBlock => (), + Err(e) => bail!(e), } - - if let Some(status_cmd) = &mut state.shared_state.status_cmd { - if poll.cmd { - match status_cmd.receive_blocks() { - Ok(None) => (), - Ok(Some(blocks)) => { - state.set_blocks(&mut conn, blocks); - conn.flush(IoMode::Blocking)?; - } - Err(e) => { - let _ = state.shared_state.status_cmd.take().unwrap().child.kill(); - state.set_error(&mut conn, e); - conn.flush(IoMode::Blocking)?; - } + Ok(event_loop::Action::Keep) + }); + + if let Some(fd) = state.status_cmd_fd() { + el.register_with_fd(fd, |(conn, state)| { + match state + .shared_state + .status_cmd + .as_mut() + .unwrap() + .receive_blocks() + { + Ok(None) => Ok(event_loop::Action::Keep), + Ok(Some(blocks)) => { + state.set_blocks(conn, blocks); + conn.flush(IoMode::Blocking)?; + Ok(event_loop::Action::Keep) + } + Err(e) => { + let _ = state.shared_state.status_cmd.take().unwrap().child.kill(); + state.set_error(conn, e); + conn.flush(IoMode::Blocking)?; + Ok(event_loop::Action::Unregister) } } - } + }); } -} - -struct Poll { - wayland: bool, - signal: bool, - cmd: bool, -} - -impl Poll { - fn new(wayland: RawFd, signal: RawFd, cmd: Option) -> io::Result { - let mut fds = [libc::pollfd { - fd: 0, - events: libc::POLLIN, - revents: 0, - }; 3]; - - fds[0].fd = wayland.as_raw_fd(); - fds[1].fd = signal.as_raw_fd(); - fds[2].fd = cmd.map_or(0, |cmd| cmd.as_raw_fd()); - loop { - // nix' (0.27) poll() implementation is hard to work with because of the lifetimes. In - // particular, it makes reusing the same buffer across poll()s very hard. It is better - // to just call directly into the libc at this point, it's not that hard. At least it is - // safer that trying to trick nix by using `BorrowedFd::borrow_raw()`. - let result = - unsafe { libc::poll(fds.as_mut_ptr(), if cmd.is_some() { 3 } else { 2 }, -1) }; - - if result == -1 { - let err = io::Error::last_os_error(); - if err.kind() != ErrorKind::Interrupted { - return Err(io::Error::last_os_error()); - } - } else { - return Ok(Self { - wayland: fds[0].revents & libc::POLLIN != 0, - signal: fds[1].revents & libc::POLLIN != 0, - cmd: fds[2].revents & libc::POLLIN != 0, - }); - } - } + let mut el_state = (conn, state); + loop { + el.run(&mut el_state)?; } } -- cgit v1.3.1