2019-08-26 17:53:11 +02:00
|
|
|
use std::env;
|
2019-08-28 18:06:07 +02:00
|
|
|
use std::io::{BufRead, BufReader, Read, Write};
|
|
|
|
|
use std::os::unix::io::AsRawFd;
|
|
|
|
|
use std::os::unix::net::{UnixListener, UnixStream};
|
|
|
|
|
use std::sync::mpsc::channel;
|
2019-07-30 00:42:05 +02:00
|
|
|
|
|
|
|
|
use nix::poll::{poll, PollFd, PollFlags};
|
2019-07-30 16:31:42 +02:00
|
|
|
use os_pipe::pipe;
|
2019-07-30 00:42:05 +02:00
|
|
|
|
2019-08-28 18:06:07 +02:00
|
|
|
mod app;
|
2019-07-31 10:26:12 +02:00
|
|
|
mod buffer;
|
2019-08-28 18:06:07 +02:00
|
|
|
mod cmd;
|
2019-07-31 10:26:12 +02:00
|
|
|
mod color;
|
|
|
|
|
mod draw;
|
2019-08-11 00:52:47 +02:00
|
|
|
mod modules;
|
2019-08-29 01:03:48 +02:00
|
|
|
mod desktop;
|
2019-07-30 00:42:05 +02:00
|
|
|
|
2019-08-27 15:07:39 +02:00
|
|
|
use app::{App, OutputMode};
|
|
|
|
|
use cmd::Cmd;
|
|
|
|
|
|
|
|
|
|
enum Mode {
|
|
|
|
|
Start,
|
|
|
|
|
StartOrKill,
|
|
|
|
|
ToggleVisible,
|
|
|
|
|
}
|
2019-07-30 00:42:05 +02:00
|
|
|
|
|
|
|
|
fn main() {
|
2019-08-27 15:07:39 +02:00
|
|
|
let socket_path = match env::var("XDG_RUNTIME_DIR") {
|
|
|
|
|
Ok(dir) => dir + "/wldash",
|
|
|
|
|
Err(_) => "/tmp/wldash".to_string(),
|
2019-08-26 17:53:11 +02:00
|
|
|
};
|
|
|
|
|
|
2019-08-27 15:07:39 +02:00
|
|
|
let args: Vec<String> = 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);
|
2019-08-05 23:45:59 +02:00
|
|
|
app.wipe();
|
2019-07-30 00:42:05 +02:00
|
|
|
|
2019-07-30 16:31:42 +02:00
|
|
|
let worker_queue = app.cmd_queue();
|
2019-08-28 18:06:07 +02:00
|
|
|
let _ = std::thread::Builder::new()
|
|
|
|
|
.name("cmd_proxy".to_string())
|
|
|
|
|
.spawn(move || loop {
|
|
|
|
|
let cmd = rx_draw.recv().unwrap();
|
|
|
|
|
worker_queue.lock().unwrap().push_back(cmd);
|
|
|
|
|
tx_pipe.write_all(&[0x1]).unwrap();
|
|
|
|
|
});
|
2019-07-30 16:31:42 +02:00
|
|
|
|
2019-08-27 15:07:39 +02:00
|
|
|
let ipc_queue = app.cmd_queue();
|
2019-08-28 18:06:07 +02:00
|
|
|
let _ = std::thread::Builder::new()
|
|
|
|
|
.name("ipc_server".to_string())
|
|
|
|
|
.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();
|
|
|
|
|
let _ = std::thread::Builder::new()
|
|
|
|
|
.name("ipc_client".to_string())
|
|
|
|
|
.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,
|
2019-08-27 15:07:39 +02:00
|
|
|
}
|
|
|
|
|
}
|
2019-08-28 18:06:07 +02:00
|
|
|
});
|
2019-08-27 15:07:39 +02:00
|
|
|
|
2019-07-30 16:31:42 +02:00
|
|
|
let mut fds = [
|
|
|
|
|
PollFd::new(app.event_queue().get_connection_fd(), PollFlags::POLLIN),
|
|
|
|
|
PollFd::new(rx_pipe.as_raw_fd(), PollFlags::POLLIN),
|
|
|
|
|
];
|
2019-07-30 00:42:05 +02:00
|
|
|
|
2019-08-05 23:45:59 +02:00
|
|
|
app.cmd_queue().lock().unwrap().push_back(Cmd::Draw);
|
|
|
|
|
|
2019-07-30 00:42:05 +02:00
|
|
|
let q = app.cmd_queue();
|
|
|
|
|
loop {
|
|
|
|
|
let cmd = q.lock().unwrap().pop_front();
|
|
|
|
|
match cmd {
|
|
|
|
|
Some(cmd) => match cmd {
|
|
|
|
|
Cmd::Draw => {
|
2019-07-31 00:42:03 +02:00
|
|
|
app.redraw(false).expect("Failed to draw");
|
2019-07-30 00:42:05 +02:00
|
|
|
app.flush_display();
|
2019-07-31 10:26:12 +02:00
|
|
|
}
|
2019-08-10 00:01:23 +02:00
|
|
|
Cmd::ForceDraw => {
|
|
|
|
|
app.redraw(true).expect("Failed to draw");
|
|
|
|
|
app.flush_display();
|
|
|
|
|
}
|
2019-08-02 15:59:10 +02:00
|
|
|
Cmd::MouseInput { pos, input } => {
|
2019-07-31 17:37:15 +02:00
|
|
|
if let Some(m) = app.get_module(pos) {
|
|
|
|
|
let bounds = m.get_bounds();
|
|
|
|
|
let input = input.offset((bounds.0, bounds.1));
|
|
|
|
|
m.input(input);
|
|
|
|
|
q.lock().unwrap().push_back(Cmd::Draw);
|
2019-07-31 10:26:12 +02:00
|
|
|
}
|
2019-07-30 00:42:05 +02:00
|
|
|
}
|
2019-08-02 15:59:10 +02:00
|
|
|
Cmd::KeyboardInput { input } => {
|
2019-08-12 01:47:50 +02:00
|
|
|
app.with_modules(|m| {
|
2019-08-02 15:59:10 +02:00
|
|
|
m.input(input.clone());
|
2019-08-12 01:47:50 +02:00
|
|
|
});
|
2019-08-02 15:59:10 +02:00
|
|
|
q.lock().unwrap().push_back(Cmd::Draw);
|
|
|
|
|
}
|
2019-08-27 15:07:39 +02:00
|
|
|
Cmd::ToggleVisible => {
|
|
|
|
|
app.toggle_visible();
|
|
|
|
|
}
|
2019-07-30 00:42:05 +02:00
|
|
|
Cmd::Exit => {
|
2019-08-27 15:07:39 +02:00
|
|
|
let _ = std::fs::remove_file(socket_path);
|
|
|
|
|
return;
|
2019-07-30 00:42:05 +02:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
None => {
|
|
|
|
|
app.flush_display();
|
|
|
|
|
|
|
|
|
|
poll(&mut fds, -1).unwrap();
|
|
|
|
|
|
|
|
|
|
if fds[0].revents().unwrap().contains(PollFlags::POLLIN) {
|
|
|
|
|
if let Some(guard) = app.event_queue().prepare_read() {
|
|
|
|
|
if let Err(e) = guard.read_events() {
|
|
|
|
|
if e.kind() != ::std::io::ErrorKind::WouldBlock {
|
|
|
|
|
eprintln!(
|
|
|
|
|
"Error while trying to read from the wayland socket: {:?}",
|
|
|
|
|
e
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
app.event_queue()
|
|
|
|
|
.dispatch_pending()
|
|
|
|
|
.expect("Failed to dispatch all messages.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if fds[1].revents().unwrap().contains(PollFlags::POLLIN) {
|
2019-07-30 16:31:42 +02:00
|
|
|
let mut v: Vec<u8> = vec![0x00];
|
|
|
|
|
rx_pipe.read_exact(&mut v).unwrap();
|
2019-07-30 00:42:05 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|