diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.rs | 76 | ||||
| -rw-r--r-- | src/state.rs | 32 | ||||
| -rw-r--r-- | src/status_cmd.rs | 56 |
3 files changed, 94 insertions, 70 deletions
diff --git a/src/main.rs b/src/main.rs index c6cc525..befd750 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,42 +18,72 @@ mod utils; mod wm_info_provider; use signal_hook::consts::*; -use signal_hook_tokio::Signals; -use futures::stream::StreamExt; +use std::{io::ErrorKind, os::fd::AsRawFd}; use wayrs_client::connection::Connection; +use wayrs_client::IoMode; + +use nix::{ + errno::Errno, + fcntl::OFlag, + poll::{poll, PollFd, PollFlags}, +}; use state::State; -#[tokio::main(flavor = "current_thread")] -async fn main() -> anyhow::Result<()> { - let mut signals = Signals::new([SIGUSR1])?; +fn main() -> anyhow::Result<()> { + let (sig_read, sig_write) = nix::unistd::pipe2(OFlag::O_NONBLOCK | OFlag::O_CLOEXEC)?; + signal_hook::low_level::pipe::register(SIGUSR1, sig_write)?; let mut conn = Connection::connect()?; - let globals = conn.async_collect_initial_globals().await?; + let globals = conn.blocking_collect_initial_globals()?; let mut state = State::new(&mut conn, &globals); - conn.async_flush().await?; + conn.flush(IoMode::Blocking)?; + + let mut fds = Vec::with_capacity(3); + fds.push(PollFd::new(conn.as_raw_fd(), PollFlags::POLLIN)); + fds.push(PollFd::new(sig_read, PollFlags::POLLIN)); + if let Some(cmd_fd) = state.status_cmd_fd() { + fds.push(PollFd::new(cmd_fd, PollFlags::POLLIN)); + } loop { - tokio::select! { - recv_events = conn.async_recv_events() => { - recv_events?; - conn.dispatch_events(&mut state); - conn.async_flush().await?; - } - reat_res = state.status_cmd_read() => { - if let Err(e) = reat_res.and_then(|_| state.status_cmd_notify_available(&mut conn)) { - if let Some(mut status_cmd) = state.shared_state.status_cmd.take() { - let _ = status_cmd.child.kill(); - } - state.set_error(&mut conn, e.to_string()); + match poll(&mut fds, -1) { + Ok(_) => (), + Err(Errno::EINTR) => continue, + Err(e) => bail!(e), + } + + if fds[0].any().unwrap_or(false) { + match conn.recv_events(IoMode::NonBlocking) { + Ok(()) => { + conn.dispatch_events(&mut state); + conn.flush(IoMode::Blocking)?; } - conn.async_flush().await?; + Err(e) if e.kind() == ErrorKind::WouldBlock => (), + Err(e) => return Err(e.into()), } - Some(signal) = signals.next() => match signal { - SIGUSR1 => state.toggle_visibility(&mut conn), - _ => unreachable!(), + } + + if fds[1].any().unwrap_or(false) { + nix::unistd::read(sig_read, &mut [0; 1])?; + state.toggle_visibility(&mut conn); + } + + if fds.len() > 2 && fds[2].any().unwrap_or(false) { + match state.shared_state.status_cmd.as_mut().unwrap().read() { + 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(); + fds.pop().unwrap(); + state.set_error(&mut conn, e); + conn.flush(IoMode::Blocking)?; + } } } } diff --git a/src/state.rs b/src/state.rs index 53ef4ff..4741efa 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,7 +1,8 @@ use crate::protocol::*; use crate::wm_info_provider::*; -use std::future::pending; +use std::fmt::Display; +use std::os::fd::{AsRawFd, RawFd}; use wayrs_client::connection::Connection; use wayrs_client::global::{Global, GlobalExt, Globals, GlobalsExt}; @@ -108,11 +109,11 @@ impl State { self.draw_all(conn); } - pub fn set_error(&mut self, conn: &mut Connection<Self>, error: impl Into<String>) { + pub fn set_error(&mut self, conn: &mut Connection<Self>, error: impl Display) { self.set_blocks( conn, vec![Block { - full_text: error.into(), + full_text: error.to_string(), ..Default::default() }], ); @@ -124,26 +125,11 @@ impl State { } } - pub async fn status_cmd_read(&mut self) -> anyhow::Result<()> { - match &mut self.shared_state.status_cmd { - Some(cmd) => cmd.read().await, - None => { - pending::<()>().await; - unreachable!() - } - } - } - - pub fn status_cmd_notify_available( - &mut self, - conn: &mut Connection<Self>, - ) -> anyhow::Result<()> { - if let Some(cmd) = &mut self.shared_state.status_cmd { - if let Some(blocks) = cmd.notify_available()? { - self.set_blocks(conn, blocks); - } - } - Ok(()) + pub fn status_cmd_fd(&self) -> Option<RawFd> { + self.shared_state + .status_cmd + .as_ref() + .map(|cmd| cmd.output.as_raw_fd()) } fn bind_output(&mut self, conn: &mut Connection<Self>, global: &Global) { diff --git a/src/status_cmd.rs b/src/status_cmd.rs index f6c9c2c..6123a7c 100644 --- a/src/status_cmd.rs +++ b/src/status_cmd.rs @@ -1,11 +1,9 @@ use std::io::Write; -use std::process::{Child, ChildStdin, Command, Stdio}; +use std::os::fd::AsRawFd; +use std::process::{Child, ChildStdin, ChildStdout, Command, Stdio}; use anyhow::Result; -use tokio::io::AsyncReadExt; -use tokio::process::ChildStdout; - use crate::i3bar_protocol::{Block, Event, Protocol}; const INITIAL_BUF_CAPACITY: usize = 4096; @@ -17,6 +15,7 @@ pub struct StatusCmd { input: ChildStdin, protocol: Protocol, buf: Vec<u8>, + buf_used: usize, } impl StatusCmd { @@ -28,39 +27,48 @@ impl StatusCmd { .spawn()?; let output = child.stdout.take().unwrap(); let input = child.stdin.take().unwrap(); + nix::fcntl::fcntl( + output.as_raw_fd(), + nix::fcntl::F_SETFL(nix::fcntl::OFlag::O_NONBLOCK), + )?; Ok(Self { child, - output: ChildStdout::from_std(output)?, + output, input, protocol: Protocol::Unknown, - buf: Vec::with_capacity(INITIAL_BUF_CAPACITY), + buf: vec![0; INITIAL_BUF_CAPACITY], + buf_used: 0, }) } - pub fn notify_available(&mut self) -> Result<Option<Vec<Block>>> { - let rem = self.protocol.process_new_bytes(&self.buf)?; - if rem.is_empty() { - self.buf.clear(); - } else { - let used = self.buf.len() - rem.len(); - self.buf.drain(..used); + pub fn read(&mut self) -> Result<Option<Vec<Block>>> { + assert!(self.buf.len() >= INITIAL_BUF_CAPACITY); + if self.buf.len() == self.buf_used { + // Double the capacity + self.buf.resize(self.buf_used * 2, 0); } - Ok(self.protocol.get_blocks()) - } - pub async fn read(&mut self) -> Result<()> { - assert!(self.buf.capacity() >= INITIAL_BUF_CAPACITY); - if self.buf.capacity() == self.buf.len() { - // Double the capacity - self.buf.reserve(self.buf.len()); + match nix::unistd::read(self.output.as_raw_fd(), &mut self.buf[self.buf_used..]) { + Ok(0) => bail!("status command exited"), + Ok(n) => self.buf_used += n, + Err(nix::errno::Errno::EAGAIN) => return Ok(None), + Err(e) => bail!(e), } - let read_len = self.output.read_buf(&mut self.buf).await?; - if read_len == 0 { - bail!("status command exited"); + let rem = self + .protocol + .process_new_bytes(&self.buf[..self.buf_used])?; + + if rem.is_empty() { + self.buf_used = 0; + } else { + let rem_len = rem.len(); + self.buf + .copy_within((self.buf_used - rem_len)..self.buf_used, 0); + self.buf_used = rem_len; } - Ok(()) + Ok(self.protocol.get_blocks()) } pub fn send_click_event(&mut self, event: &Event) -> Result<()> { |
