summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/event_loop.rs73
-rw-r--r--src/main.rs118
2 files changed, 118 insertions, 73 deletions
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<D> {
+ pollfds: Vec<libc::pollfd>,
+ cbs: HashMap<RawFd, Box<dyn FnMut(&mut D) -> Result<Action>>>,
+}
+
+pub enum Action {
+ Keep,
+ Unregister,
+}
+
+impl<D> EventLoop<D> {
+ pub fn new() -> Self {
+ Self {
+ pollfds: Vec::new(),
+ cbs: HashMap::new(),
+ }
+ }
+
+ pub fn register_with_fd<F>(&mut self, fd: RawFd, cb: F)
+ where
+ F: FnMut(&mut D) -> Result<Action> + '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>, 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),
}
+ Ok(event_loop::Action::Keep)
+ });
- 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)?;
- }
+ 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<RawFd>) -> io::Result<Self> {
- 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)?;
}
}