Skip to content

Commit 51de98b

Browse files
authored
feat: restructure crates; improve folder structure of wm crate (#908)
* Previously, the `wm` crate contained essentially everything - it's used as a library *and* the entrypoint for both the main executable + cli. This isn't super intuitive, so it's being split out into the following: ``` wm (main app) wm-cli (cli app) wm-common (common types/utilities) wm-ipc-client (small websocket client) wm-platform (abstractions over Windows apis) wm-watcher (watcher app) ``` * Change CLI to be a distinct binary from the `wm` crate. * This means the CLI can be compiled without `uiAccess`, fixing the issue where CLI commands often errors with `Permission denied` (fixes #861, #902).
1 parent 47593a0 commit 51de98b

File tree

141 files changed

+2300
-2129
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

141 files changed

+2300
-2129
lines changed

Cargo.lock

+70-14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
[workspace]
22
resolver = "2"
33
members = ["packages/*"]
4-
default-members = ["packages/wm", "packages/watcher"]
4+
default-members = ["packages/wm", "packages/wm-cli", "packages/wm-watcher"]
55

66
[workspace.dependencies]
77
anyhow = { version = "1", features = ["backtrace"] }
8+
clap = { version = "4", features = ["derive"] }
9+
futures-util = "0.3"
10+
home = "0.5"
11+
serde = { version = "1", features = ["derive"] }
812
serde_json = { version = "1", features = ["raw_value"] }
9-
tokio = { version = "1", features = ["full"] }
1013
tauri-winres = "0.1"
14+
tokio = { version = "1", features = ["full"] }
15+
tokio-tungstenite = "0.21"
1116
tracing = "0.1"
1217
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
18+
uuid = { version = "1", features = ["v4", "serde"] }

packages/wm-cli/Cargo.toml

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[package]
2+
name = "wm-cli"
3+
version = "0.0.0"
4+
edition = "2021"
5+
6+
[lib]
7+
path = "src/lib.rs"
8+
9+
[[bin]]
10+
name = "glazewm-cli"
11+
path = "src/main.rs"
12+
13+
[build-dependencies]
14+
tauri-winres = { workspace = true }
15+
16+
[dependencies]
17+
anyhow = { workspace = true }
18+
futures-util = { workspace = true }
19+
serde_json = { workspace = true }
20+
tokio = { workspace = true }
21+
tokio-tungstenite = { workspace = true }
22+
uuid = { workspace = true }
23+
wm-common = { path = "../wm-common" }
24+
wm-ipc-client = { path = "../wm-ipc-client" }

packages/wm-cli/build.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use tauri_winres::VersionInfo;
2+
3+
fn main() {
4+
println!("cargo:rerun-if-env-changed=VERSION_NUMBER");
5+
let mut res = tauri_winres::WindowsResource::new();
6+
7+
res.set_icon("../../resources/assets/icon.ico");
8+
9+
// Set language to English (US).
10+
res.set_language(0x0409);
11+
12+
res.set("OriginalFilename", "glazewm.exe");
13+
res.set("ProductName", "GlazeWM CLI");
14+
res.set("FileDescription", "GlazeWM CLI");
15+
16+
let version_parts = env!("VERSION_NUMBER")
17+
.split('.')
18+
.take(3)
19+
.map(|part| part.parse().unwrap_or(0))
20+
.collect::<Vec<u16>>();
21+
22+
let [major, minor, patch] =
23+
<[u16; 3]>::try_from(version_parts).unwrap_or([0, 0, 0]);
24+
25+
let version_str = format!("{}.{}.{}.0", major, minor, patch);
26+
res.set("FileVersion", &version_str);
27+
res.set("ProductVersion", &version_str);
28+
29+
let version_u64 = ((major as u64) << 48)
30+
| ((minor as u64) << 32)
31+
| ((patch as u64) << 16);
32+
33+
res.set_version_info(VersionInfo::FILEVERSION, version_u64);
34+
res.set_version_info(VersionInfo::PRODUCTVERSION, version_u64);
35+
36+
res.compile().unwrap();
37+
}

packages/wm-cli/src/lib.rs

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use anyhow::Context;
2+
use wm_common::ClientResponseData;
3+
use wm_ipc_client::IpcClient;
4+
5+
pub async fn start(args: Vec<String>) -> anyhow::Result<()> {
6+
let mut client = IpcClient::connect().await?;
7+
8+
let message = args[1..].join(" ");
9+
client
10+
.send(&message)
11+
.await
12+
.context("Failed to send command to IPC server.")?;
13+
14+
let client_response = client
15+
.client_response(&message)
16+
.await
17+
.context("Failed to receive response from IPC server.")?;
18+
19+
match client_response.data {
20+
// For event subscriptions, omit the initial response message and
21+
// continuously output subsequent event messages.
22+
Some(ClientResponseData::EventSubscribe(data)) => loop {
23+
let event_subscription = client
24+
.event_subscription(&data.subscription_id)
25+
.await
26+
.context("Failed to receive response from IPC server.")?;
27+
28+
println!("{}", serde_json::to_string(&event_subscription)?);
29+
},
30+
// For all other messages, output and exit when the first response
31+
// message is received.
32+
_ => {
33+
println!("{}", serde_json::to_string(&client_response)?);
34+
}
35+
}
36+
37+
Ok(())
38+
}

packages/wm-cli/src/main.rs

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use std::{env, process::Command};
2+
3+
use anyhow::Context;
4+
use wm_cli::start;
5+
use wm_common::AppCommand;
6+
7+
#[tokio::main]
8+
async fn main() -> anyhow::Result<()> {
9+
let args = std::env::args().collect::<Vec<_>>();
10+
let app_command = AppCommand::parse_with_default(&args);
11+
12+
match app_command {
13+
AppCommand::Start { .. } => {
14+
let exe_dir = env::current_exe()?
15+
.parent()
16+
.context("Failed to resolve path to the current executable.")?
17+
.to_owned();
18+
19+
// Main executable is either in the current directory (when running
20+
// debug/release builds) or in the parent directory when packaged.
21+
let main_path =
22+
[exe_dir.join("glazewm.exe"), exe_dir.join("../glazewm.exe")]
23+
.into_iter()
24+
.find(|path| path.exists())
25+
.and_then(|path| path.to_str().map(|s| s.to_string()))
26+
.context("Failed to resolve path to the main executable.")?;
27+
28+
// UIAccess applications can't be started directly, so we need to use
29+
// CMD to start it. The start command is used to avoid a long-running
30+
// CMD process in the background.
31+
Command::new("cmd")
32+
.args(
33+
["/C", "start", "", &main_path]
34+
.into_iter()
35+
.chain(args.iter().skip(1).map(|s| s.as_str())),
36+
)
37+
.spawn()
38+
.context("Failed to start main executable.")?;
39+
40+
Ok(())
41+
}
42+
_ => start(args).await,
43+
}
44+
}

packages/wm-common/Cargo.toml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "wm-common"
3+
version = "0.0.0"
4+
edition = "2021"
5+
6+
[lib]
7+
path = "src/lib.rs"
8+
9+
[dependencies]
10+
anyhow = { workspace = true }
11+
clap = { workspace = true }
12+
regex = "1"
13+
serde = { workspace = true }
14+
tracing = { workspace = true }
15+
uuid = { workspace = true }
File renamed without changes.

0 commit comments

Comments
 (0)