diff --git a/src/app.rs b/src/app.rs index fac724c..d718f50 100644 --- a/src/app.rs +++ b/src/app.rs @@ -17,6 +17,8 @@ use wayland_protocols::wlr::unstable::layer_shell::v1::client::{ use crate::color::Color; use crate::buffer::Buffer; +use crate::cmd::Cmd; + use crate::modules::backlight::Backlight; use crate::modules::battery::UpowerBattery; use crate::modules::calendar::Calendar; @@ -25,12 +27,9 @@ use crate::modules::launcher::Launcher; use crate::modules::module::{Input, Module}; use crate::modules::sound::PulseAudio; -pub enum Cmd { - Exit, - Draw, - ForceDraw, - MouseInput { pos: (u32, u32), input: Input }, - KeyboardInput { input: Input }, +pub enum OutputMode { + Active, + All, } struct AppInner { @@ -41,12 +40,13 @@ struct AppInner { outputs: Vec<(u32, wl_output::WlOutput)>, shell: Option, dimensions: (u32, u32), - draw_tx: Sender, - all_outputs: bool, + draw_tx: Sender, + output_mode: OutputMode, + visible: bool, } impl AppInner { - fn new(tx: Sender, all_outputs: bool) -> AppInner{ + fn new(tx: Sender, output_mode: OutputMode) -> AppInner{ AppInner{ compositor: None, surfaces: Vec::new(), @@ -56,7 +56,8 @@ impl AppInner { shell: None, dimensions: (0, 0), draw_tx: tx, - all_outputs: all_outputs, + output_mode: output_mode, + visible: true, } } @@ -65,7 +66,7 @@ impl AppInner { shell: &zwlr_layer_shell_v1::ZwlrLayerShellV1, dimensions: (u32, u32), configured_surfaces: Arc>, - tx: Sender, + tx: Sender, output: Option<&wl_output::WlOutput> ) -> ( wl_surface::WlSurface, @@ -87,7 +88,7 @@ impl AppInner { zwlr_layer_surface_v1::Event::Configure { serial, .. } => { *(configured_surfaces.lock().unwrap()) += 1; layer.ack_configure(serial); - tx.send(false).unwrap(); + tx.send(Cmd::ForceDraw).unwrap(); } _ => unreachable!(), }, @@ -114,10 +115,6 @@ impl AppInner { None => return, }; - if !self.all_outputs && self.shell_surfaces.len() > 0 { - return; - } - for shell_surface in self.shell_surfaces.iter() { shell_surface.destroy(); } @@ -127,23 +124,34 @@ impl AppInner { self.configured_surfaces = Arc::new(Mutex::new(0)); - if self.all_outputs { - let mut surfaces = Vec::new(); - let mut shell_surfaces = Vec::new(); - for output in self.outputs.iter() { - let (surface, shell_surface) = AppInner::add_shell_surface(&compositor, &shell, self.dimensions, self.configured_surfaces.clone(), self.draw_tx.clone(), Some(&output.1)); - surfaces.push(surface); - shell_surfaces.push(shell_surface); - } - self.surfaces = surfaces; - self.shell_surfaces = shell_surfaces; - } else { - let (surface, shell_surface) = AppInner::add_shell_surface(&compositor, &shell, self.dimensions, self.configured_surfaces.clone(), self.draw_tx.clone(), None); - self.surfaces = vec![surface]; - self.shell_surfaces = vec![shell_surface]; - } + if self.visible { + match self.output_mode { + OutputMode::Active => { + if self.shell_surfaces.len() > 0 { + return; + } + let (surface, shell_surface) = AppInner::add_shell_surface(&compositor, &shell, self.dimensions, self.configured_surfaces.clone(), self.draw_tx.clone(), None); + self.surfaces = vec![surface]; + self.shell_surfaces = vec![shell_surface]; - self.draw_tx.send(false).unwrap(); + }, + OutputMode::All => { + let mut surfaces = Vec::new(); + let mut shell_surfaces = Vec::new(); + for output in self.outputs.iter() { + let (surface, shell_surface) = AppInner::add_shell_surface(&compositor, &shell, self.dimensions, self.configured_surfaces.clone(), self.draw_tx.clone(), Some(&output.1)); + surfaces.push(surface); + shell_surfaces.push(shell_surface); + } + self.surfaces = surfaces; + self.shell_surfaces = shell_surfaces; + } + } + self.draw_tx.send(Cmd::ForceDraw).unwrap(); + } else { + self.surfaces = Vec::new(); + self.shell_surfaces = Vec::new(); + } } fn add_output(&mut self, id: u32, output: wl_output::WlOutput) { @@ -249,6 +257,12 @@ impl App { Ok(()) } + pub fn toggle_visible(&mut self) { + let mut inner = self.inner.lock().unwrap(); + inner.visible = !inner.visible; + inner.outputs_changed(); + } + pub fn cmd_queue(&self) -> Arc>> { self.cmd_queue.clone() } @@ -291,8 +305,8 @@ impl App { None } - pub fn new(tx: Sender, all_outputs: bool) -> App { - let inner = Arc::new(Mutex::new(AppInner::new(tx.clone(), all_outputs))); + pub fn new(tx: Sender, output_mode: OutputMode) -> App { + let inner = Arc::new(Mutex::new(AppInner::new(tx.clone(), output_mode))); // // Set up modules @@ -304,7 +318,7 @@ impl App { Module::new(Box::new(Calendar::new()), (0, 368, 1232, 344)), ]; - if let Ok(m) = Launcher::new() { + if let Ok(m) = Launcher::new(tx.clone()) { modules.push(Module::new(Box::new(m), (0, 728, 1232, 32))); } diff --git a/src/main.rs b/src/main.rs index d936fda..fd84d83 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,8 @@ -use std::io::{Read, Write}; +use std::io::{Read, Write, BufRead, BufReader}; use std::os::unix::io::AsRawFd; use std::sync::mpsc::channel; use std::env; +use std::os::unix::net::{UnixStream, UnixListener}; use nix::poll::{poll, PollFd, PollFlags}; use os_pipe::pipe; @@ -11,31 +12,114 @@ mod color; mod draw; mod modules; mod app; +mod cmd; -use app::{App, Cmd}; +use app::{App, OutputMode}; +use cmd::Cmd; + +enum Mode { + Start, + StartOrKill, + ToggleVisible, +} fn main() { - let (mut rx_pipe, mut tx_pipe) = pipe().unwrap(); - let (tx_draw, rx_draw) = channel(); - - let all_outputs = match env::var("WLDASH_ALL_OUTPUTS") { - Ok(_) => true, - Err(_) => false, + let socket_path = match env::var("XDG_RUNTIME_DIR") { + Ok(dir) => dir + "/wldash", + Err(_) => "/tmp/wldash".to_string(), }; - let mut app = App::new(tx_draw, all_outputs); + let args: Vec = env::args().collect(); + let mode = match args.len() { + 1 => Mode::Start, + 2 => match args[1].as_str() { + "start" => Mode::Start, + "start-or-kill" => Mode::StartOrKill, + "toggle-visible" => Mode::ToggleVisible, + s => { + eprintln!("unsupported sub-command {}", s); + std::process::exit(1); + } + }, + v => { + eprintln!("expected 0 or 1 arguments, got {}", v); + std::process::exit(1); + } + }; + + match mode { + Mode::ToggleVisible => { + if let Ok(mut socket) = UnixStream::connect(socket_path.clone()) { + socket.write_all(b"toggle_visible\n").unwrap(); + return; + }; + eprintln!("wldash is not running"); + std::process::exit(1); + } + Mode::StartOrKill => { + if let Ok(mut socket) = UnixStream::connect(socket_path.clone()) { + socket.write_all(b"kill\n").unwrap(); + return; + }; + } + Mode::Start => { + if let Ok(_) = UnixStream::connect(socket_path.clone()) { + eprintln!("wldash is already running"); + std::process::exit(1); + }; + } + } + + let _ = std::fs::remove_file(socket_path.clone()); + + let listener = UnixListener::bind(socket_path.clone()).unwrap(); + + let (mut rx_pipe, mut tx_pipe) = pipe().unwrap(); + let ipc_pipe = tx_pipe.try_clone().unwrap(); + let (tx_draw, rx_draw) = channel(); + + let output_mode = match env::var("WLDASH_ALL_OUTPUTS") { + Ok(_) => OutputMode::All, + Err(_) => OutputMode::Active, + }; + + let mut app = App::new(tx_draw, output_mode); app.wipe(); let worker_queue = app.cmd_queue(); std::thread::spawn(move || loop { - if rx_draw.recv().unwrap() { - worker_queue.lock().unwrap().push_back(Cmd::Draw); - } else { - worker_queue.lock().unwrap().push_back(Cmd::ForceDraw); - } + let cmd = rx_draw.recv().unwrap(); + worker_queue.lock().unwrap().push_back(cmd); tx_pipe.write_all(&[0x1]).unwrap(); }); + let ipc_queue = app.cmd_queue(); + std::thread::spawn(move || loop { + for stream in listener.incoming() { + match stream { + Ok(stream) => { + let client_queue = ipc_queue.clone(); + let mut client_pipe = ipc_pipe.try_clone().unwrap(); + std::thread::spawn(move || { + let r = BufReader::new(stream); + for line in r.lines() { + match line { + Ok(v) => match v.as_str() { + "kill" => client_queue.lock().unwrap().push_back(Cmd::Exit), + "toggle_visible" => client_queue.lock().unwrap().push_back(Cmd::ToggleVisible), + v => eprintln!("unknown command: {}", v), + }, + Err(_) => return, + } + client_pipe.write_all(&[0x1]).unwrap(); + } + }); + } + Err(_) => break, + } + } + }); + let mut fds = [ PollFd::new(app.event_queue().get_connection_fd(), PollFlags::POLLIN), PollFd::new(rx_pipe.as_raw_fd(), PollFlags::POLLIN), @@ -70,8 +154,12 @@ fn main() { }); q.lock().unwrap().push_back(Cmd::Draw); } + Cmd::ToggleVisible => { + app.toggle_visible(); + } Cmd::Exit => { - std::process::exit(0); + let _ = std::fs::remove_file(socket_path); + return; } }, None => { diff --git a/src/modules/battery.rs b/src/modules/battery.rs index a0017da..2f5b0b1 100644 --- a/src/modules/battery.rs +++ b/src/modules/battery.rs @@ -2,6 +2,7 @@ use crate::buffer::Buffer; use crate::color::Color; use crate::draw::{draw_bar, draw_box, Font, ROBOTO_REGULAR}; use crate::modules::module::{Input, ModuleImpl}; +use crate::cmd::Cmd; use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex}; @@ -129,7 +130,7 @@ impl UpowerBattery { }) } - pub fn new(listener: Sender) -> Result { + pub fn new(listener: Sender) -> Result { let d = UpowerBattery::from_device("BAT0")?; let path = d.device_path.clone(); let inner = d.inner.clone(); @@ -175,7 +176,7 @@ impl UpowerBattery { inner.state = state; inner.capacity = capacity; inner.dirty = true; - listener.send(true).unwrap(); + listener.send(Cmd::Draw).unwrap(); } } }); diff --git a/src/modules/clock.rs b/src/modules/clock.rs index 224e33d..638d7c7 100644 --- a/src/modules/clock.rs +++ b/src/modules/clock.rs @@ -2,6 +2,7 @@ use crate::buffer::Buffer; use crate::color::Color; use crate::draw::{Font, ROBOTO_REGULAR}; use crate::modules::module::{Input, ModuleImpl}; +use crate::cmd::Cmd; use std::sync::mpsc::Sender; @@ -15,7 +16,7 @@ pub struct Clock { } impl Clock { - pub fn new(ch: Sender) -> Clock { + pub fn new(ch: Sender) -> Clock { std::thread::spawn(move || loop { let n = Local::now(); let target = (n + Duration::seconds(60)) @@ -27,7 +28,7 @@ impl Clock { let d = target - n; std::thread::sleep(d.to_std().unwrap()); - ch.send(true).unwrap(); + ch.send(Cmd::Draw).unwrap(); }); let mut date_cache = Font::new(&ROBOTO_REGULAR, 64.0); diff --git a/src/modules/launcher.rs b/src/modules/launcher.rs index d7bde7f..e266157 100644 --- a/src/modules/launcher.rs +++ b/src/modules/launcher.rs @@ -2,7 +2,9 @@ use crate::buffer::Buffer; use crate::color::Color; use crate::draw::{Font, ROBOTO_REGULAR}; use crate::modules::module::{Input, ModuleImpl}; +use crate::cmd::Cmd; +use std::sync::mpsc::Sender; use std::cell::RefCell; use std::cmp::Ordering; use std::io::Read; @@ -20,10 +22,11 @@ pub struct Launcher { offset: usize, font: RefCell, dirty: bool, + tx: Sender, } impl Launcher { - pub fn new() -> Result { + pub fn new(listener: Sender) -> Result { if !atty::is(Stream::Stdin) { let mut inbuf = String::new(); std::io::stdin().read_to_string(&mut inbuf)?; @@ -42,6 +45,7 @@ impl Launcher { offset: 0, font: RefCell::new(Font::new(&ROBOTO_REGULAR, 32.0)), dirty: true, + tx: listener, }) } else { Ok(Launcher { @@ -52,6 +56,7 @@ impl Launcher { offset: 0, font: RefCell::new(Font::new(&ROBOTO_REGULAR, 32.0)), dirty: true, + tx: listener, }) } } @@ -313,12 +318,12 @@ impl ModuleImpl for Launcher { }, Some('!') => { println!("{}", self.input.chars().skip(1).collect::()); - std::process::exit(0); + self.tx.send(Cmd::Exit).unwrap(); } _ => { if self.matches.len() > self.offset { println!("{}", self.matches[self.offset]); - std::process::exit(0); + self.tx.send(Cmd::Exit).unwrap(); } } }; diff --git a/src/modules/sound.rs b/src/modules/sound.rs index c45e9f3..3977def 100644 --- a/src/modules/sound.rs +++ b/src/modules/sound.rs @@ -2,6 +2,7 @@ use crate::buffer::Buffer; use crate::color::Color; use crate::draw::{draw_bar, draw_box, Font, ROBOTO_REGULAR}; use crate::modules::module::{Input, ModuleImpl}; +use crate::cmd::Cmd; use std::cell::RefCell; use std::cmp::{max, min}; @@ -404,7 +405,7 @@ pub struct PulseAudio { } impl PulseAudio { - pub fn new(listener: Sender) -> Result { + pub fn new(listener: Sender) -> Result { let (tx, rx) = channel(); let mut font = Font::new(&ROBOTO_REGULAR, 24.0); font.add_str_to_cache("volume"); @@ -419,7 +420,7 @@ impl PulseAudio { rx.recv().unwrap(); device.lock().unwrap().get_info().unwrap(); *(dirty.lock().unwrap()) = true; - listener.send(true).unwrap(); + listener.send(Cmd::Draw).unwrap(); }); Ok(pa) }