Commit 5b7cddd2e177
tarball5 files changed+112-33
@@ -400,6 +400,7 @@ dependencies = [
400400
"monolith-layout",401401
"monolith-protocols",402402
"monolith-render-vk",403
+ "pound",403404
"serde_json",404405
"slotmap",405406
"smithay",@@ -535,6 +536,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
535536
checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"536537
537538
[[package]]539
+name = "pound"540
+version = "0.5.2"541
+source = "registry+https://github.com/rust-lang/crates.io-index"542
+checksum = "88a1548289dfa12bd53954303a19ef93cfc3d834f9211e3e182bf12aef41f003"543
+dependencies = [544
+ "pound-derive",545
+]546
+547
+[[package]]548
+name = "pound-derive"549
+version = "0.5.2"550
+source = "registry+https://github.com/rust-lang/crates.io-index"551
+checksum = "0b0f793b0c8b1db299b1a3bd960b8bd7b5aadc58b2291b647c8c0faa91b14af4"552
+dependencies = [553
+ "pound-derive-impl",554
+]555
+556
+[[package]]557
+name = "pound-derive-impl"558
+version = "0.5.2"559
+source = "registry+https://github.com/rust-lang/crates.io-index"560
+checksum = "8a6ddc2609d7787602494304b1b676676cddcc2f775db9aae178d4f47c6b7a45"561
+dependencies = [562
+ "proc-macro2",563
+ "quote",564
+ "venial",565
+]566
+567
+[[package]]538568
name = "presser"539569
version = "0.3.1"540570
source = "registry+https://github.com/rust-lang/crates.io-index"@@ -1088,6 +1118,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
10881118
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"10891119
10901120
[[package]]1121
+name = "venial"1122
+version = "0.6.1"1123
+source = "registry+https://github.com/rust-lang/crates.io-index"1124
+checksum = "9a42528baceab6c7784446df2a10f4185078c39bf73dc614f154353f1a6b1229"1125
+dependencies = [1126
+ "proc-macro2",1127
+ "quote",1128
+]1129
+1130
+[[package]]10911131
name = "version_check"10921132
version = "0.9.5"10931133
source = "registry+https://github.com/rust-lang/crates.io-index"@@ -18,6 +18,7 @@ monolith-ipc.workspace = true
1818
monolith-layout.workspace = true1919
monolith-protocols.workspace = true2020
monolith-render-vk.workspace = true21
+pound = "0.5"2122
serde_json.workspace = true2223
slotmap.workspace = true2324
smithay = { workspace = true, features = ["desktop"] }@@ -2,7 +2,7 @@
22
//! subcommands that talk to it.33
44
use crate::state::{State, identity};5
-use monolith_ipc::{Answer, Command, Envelope, Query, Reply, Request, VERSION};5
+use monolith_ipc::{Answer, Envelope, Query, Reply, Request, VERSION};66
use smithay::reexports::calloop::generic::Generic;77
use smithay::reexports::calloop::{EventLoop, Interest, LoopHandle, Mode, PostAction};88
use std::io::{BufRead, BufReader, ErrorKind, Read, Write};@@ -208,26 +208,12 @@ fn snapshot(state: &State) -> monolith_ipc::State {
208208
}209209
}210210
211
-/// Runs a client subcommand, if `args` names one.212
-pub fn client(args: &[String]) -> Option<ExitCode> {213
- let (sub, rest) = args.split_first()?;214
- let request = match sub.as_str() {215
- "msg" => match Command::parse(rest) {216
- Ok(cmd) => Request::Run(cmd),217
- Err(e) => return Some(fail(&e.to_string())),218
- },219
- "get" => match rest {220
- [q] if q == "state" => Request::Get(Query::State),221
- [q] if q == "version" => Request::Get(Query::Version),222
- _ => return Some(fail("get takes state or version")),223
- },224
- "subscribe" => Request::Subscribe {},225
- _ => return None,226
- };227
- Some(talk(request))211
+/// Sends `request` to the running compositor and prints the reply.212
+pub fn client(request: Request) -> ExitCode {213
+ talk(request)228214
}229215
230
-fn fail(msg: &str) -> ExitCode {216
+pub fn fail(msg: &str) -> ExitCode {231217
eprintln!("monolith: {msg}");232218
ExitCode::FAILURE233219
}@@ -6,23 +6,75 @@ mod launch;
66
mod nested;77
mod state;88
9
+use monolith_ipc::{Command, Query, Request};10
+use pound::{Parse, ValueEnum};911
use smithay::reexports::calloop::EventLoop;1012
use smithay::reexports::wayland_server::Display;1113
use state::State;14
+use std::path::PathBuf;1215
use std::process::ExitCode;1316
use tracing_subscriber::EnvFilter;1417
18
+/// a tiling Wayland compositor19
+#[derive(Parse)]20
+#[pound(name = "monolith")]21
+struct Cli {22
+ #[pound(subcommand)]23
+ cmd: Option<Cmd>,24
+}25
+26
+#[derive(Parse)]27
+enum Cmd {28
+ /// run the compositor, the default29
+ Run {30
+ /// read this config instead of ~/.config/monolith/config.toml31
+ #[pound(long)]32
+ config: Option<String>,33
+ /// a program to start inside, with its arguments34
+ #[pound(trailing)]35
+ program: Vec<String>,36
+ },37
+ /// send a command to the running compositor, e.g. `msg focus left`38
+ Msg {39
+ #[pound(trailing)]40
+ command: Vec<String>,41
+ },42
+ /// print the running compositor's state or version43
+ Get { what: What },44
+ /// print the state as a JSON line after every change45
+ Subscribe,46
+}47
+48
+#[derive(ValueEnum)]49
+enum What {50
+ State,51
+ Version,52
+}53
+1554
fn main() -> ExitCode {16
- let args: Vec<String> = std::env::args().skip(1).collect();17
- if let Some(code) = ipc::client(&args) {18
- return code;19
- }55
+ let (config, program) = match Cli::parse().cmd {56
+ None => (None, Vec::new()),57
+ Some(Cmd::Run { config, program }) => (config.map(PathBuf::from), program),58
+ Some(Cmd::Msg { command }) => {59
+ return match Command::parse(&command) {60
+ Ok(cmd) => ipc::client(Request::Run(cmd)),61
+ Err(e) => ipc::fail(&e.to_string()),62
+ };63
+ }64
+ Some(Cmd::Get { what }) => {65
+ return ipc::client(Request::Get(match what {66
+ What::State => Query::State,67
+ What::Version => Query::Version,68
+ }));69
+ }70
+ Some(Cmd::Subscribe) => return ipc::client(Request::Subscribe {}),71
+ };2072
tracing_subscriber::fmt()2173
.with_env_filter(2274
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),2375
)2476
.init();25
- match run(&args) {77
+ match run(config, &program) {2678
Ok(()) => ExitCode::SUCCESS,2779
Err(e) => {2880
tracing::error!("{e}");@@ -31,20 +83,19 @@ fn main() -> ExitCode {
3183
}3284
}3385
34
-/// Runs the compositor. Any arguments are a program to start inside it.35
-fn run(args: &[String]) -> Result<(), Box<dyn std::error::Error>> {86
+fn run(config: Option<PathBuf>, program: &[String]) -> Result<(), Box<dyn std::error::Error>> {3687
let mut event_loop: EventLoop<'static, State> = EventLoop::try_new()?;3788
let display: Display<State> = Display::new()?;38
- let mut state = State::new(&mut event_loop, display)?;89
+ let mut state = State::new(&mut event_loop, display, config)?;3990
nested::init(&mut event_loop, &mut state)?;4091
ipc::init(&event_loop, &mut state)?;4192
tracing::info!(socket = ?state.socket_name, device = state.renderer.device_name(), "monolith running nested");4293
43
- let startup = state.config.startup.clone();44
- for argv in startup45
- .iter()46
- .chain((!args.is_empty()).then_some(&args.to_vec()))47
- {94
+ let mut startup = state.config.startup.clone();95
+ if !program.is_empty() {96
+ startup.push(program.to_vec());97
+ }98
+ for argv in &startup {4899
if let Err(e) = state.spawn(argv) {49100
tracing::warn!("startup: {e}");50101
}@@ -71,9 +71,10 @@ impl State {
7171
pub fn new(7272
event_loop: &mut EventLoop<State>,7373
display: Display<State>,74
+ config_path: Option<PathBuf>,7475
) -> Result<Self, Box<dyn std::error::Error>> {7576
let dh = display.handle();76
- let config_path = Config::path();77
+ let config_path = config_path.or_else(Config::path);7778
let config = Config::load(config_path.as_deref()).unwrap_or_else(|e| {7879
tracing::error!("config: {e}, using the built-in default");7980
Config::parse(monolith_config::DEFAULT).expect("the default config parses")