Skip to content

Commit aef1a9f

Browse files
committed
2.0.0-alpha.6
1 parent 61d99ed commit aef1a9f

249 files changed

Lines changed: 19393 additions & 3272 deletions

File tree

Some content is hidden

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

‎src/windhawk-core/Cargo.lock‎

Lines changed: 640 additions & 596 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src/windhawk-core/Cargo.toml‎

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ members = [
2020
]
2121

2222
[workspace.package]
23-
version = "2.0.0-alpha.5"
23+
version = "2.0.0-alpha.6"
2424
edition = "2024"
2525
rust-version = "1.96"
2626
publish = false
@@ -50,8 +50,8 @@ windhawk-core-client = { path = "core-client" }
5050
windhawk-core-host = { path = "core-host" }
5151
# The runtime-broker transport: a length-prefixed JSON channel over a duplex
5252
# named pipe, with the unprivileged end as the pipe server and the privileged one
53-
# as the pipe client. Outside the core layering, like the bridge and the referee:
54-
# no core crate at all, and nothing Windhawk-specific - it is generic over the
53+
# as the pipe client. Outside the core layering, like the bridge: no core crate
54+
# at all, and nothing Windhawk-specific - it is generic over the
5555
# request/response/push types so it is testable over an in-process pipe pair.
5656
windhawk-broker = { path = "broker" }
5757

@@ -84,7 +84,7 @@ thiserror = "2.0"
8484
# (matching js-yaml, the TS implementation we replace), where saphyr silently
8585
# keeps the last value. default-features off drops the `encoding` feature
8686
# (encoding_rs); the core feeds the parser &str it decoded itself.
87-
yaml-rust2 = { version = "0.11", default-features = false }
87+
yaml-rust2 = { version = "0.13", default-features = false }
8888
# Raw Win32 bindings for the adapter and ffi crates. Pinned to the version the
8989
# rest of the tree already resolves to, so the workspace edge adds no version of
9090
# its own; the Tauri UI and the embed-resource build chain still pull 0.59/0.60,
@@ -125,36 +125,41 @@ embed-resource = "3"
125125
# bridges the webview envelope protocol to core C-ABI invokes. windhawk-ui is
126126
# x64/ARM64 only, so it is excluded from the i686 build; the i686 production
127127
# core stays for the Electron extension. tauri-build is the build-script half
128-
# (codegen + ACL).
129-
tauri = { version = "2", features = [] }
130-
tauri-build = { version = "2", features = [] }
128+
# (codegen + ACL). tauri-runtime-wry is the webview runtime the app selects on
129+
# the builder, and the home of the wry extension traits shell.rs reaches the
130+
# WebView2 controller through. Tauri 3 is on its alpha line; it is what carries
131+
# tao 0.37 and wry 0.57, which the UI's window handling is written against.
132+
tauri = { version = "3.0.0-alpha.2", features = [] }
133+
tauri-build = { version = "3.0.0-alpha.1", features = [] }
134+
tauri-runtime-wry = { version = "3.0.0-alpha.2", features = [] }
131135
# The launcher contract + external-link shim: single-instance makes a bare
132136
# re-launch ensure-running-and-foreground (and `--quit` ask the running instance
133137
# to exit); opener routes the front-end's external links to the system browser.
134-
# Both are ui-crate-only, tracking tauri 2.
135-
tauri-plugin-single-instance = { version = "2", features = [] }
136-
tauri-plugin-opener = { version = "2", features = [] }
138+
# Both are ui-crate-only, tracking tauri 3.
139+
tauri-plugin-single-instance = { version = "3.0.0-alpha.1", features = [] }
140+
tauri-plugin-opener = { version = "3.0.0-alpha.1", features = [] }
137141
# The WebView2 COM bindings + safe event-handler wrappers. ui-crate-only; pinned
138142
# to the version already in the tauri transitive tree (no duplicate, no new
139143
# license/advisory). Used in shell.rs to disable a few browser shortcuts (print,
140144
# reload, hard reload, caret browsing) via the controller's
141145
# AcceleratorKeyPressed event, and to trim the context menu to back/forward plus
142146
# the input clipboard items via its ContextMenuRequested event, both reached
143147
# through Tauri's `with_webview`.
144-
webview2-com = "0.38"
148+
webview2-com = "0.39"
145149
# The windows-rs COM core (the `Interface` trait + `PWSTR`), needed by shell.rs to
146150
# QueryInterface the WebView2 controller's core object up to ICoreWebView2_11 (which
147151
# carries ContextMenuRequested) and to read each menu item's name. ui-crate-only; pinned
148-
# to "0.61", the exact version webview2-com already pulls (windows-core 0.61.2), so the
152+
# to "0.62", the exact version webview2-com, wry, and tao already pull, so the
149153
# `Interface` impls on the ICoreWebView2* types match and no new version/duplicate enters
150154
# the tree.
151-
windows-core = "0.61"
155+
windows-core = "0.62"
152156
# The ergonomic windows-rs bindings (RAII COM wrappers), used by the UI's native file
153157
# dialog (ui/src/file_dialog.rs: IFileOpenDialog/IFileSaveDialog for user-data
154-
# export/import). ui-crate-only; pinned to "0.61", the exact version webview2-com already
155-
# pulls (windows 0.61.3), so it adds no new crate/version to the tree. Features are
156-
# enabled on the consuming crate.
157-
windows = "0.61"
158+
# export/import). ui-crate-only; pinned to "0.62", the exact version webview2-com, wry,
159+
# and tao already pull, so it adds no new crate/version to the tree (tauri-plugin-opener
160+
# still pulls a 0.61 beside it, a duplicate cargo-deny warns on). Features are enabled
161+
# on the consuming crate.
162+
windows = "0.62"
158163
# The `log` facade, which the Tauri/wry window stack reports through. It reports a
159164
# window or webview that fails to build there and then DISCARDS it - the build
160165
# answers Ok regardless - so the UI installs a small capture of the last few

‎src/windhawk-core/broker/Cargo.toml‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ edition.workspace = true
66
rust-version.workspace = true
77
publish.workspace = true
88

9-
# Outside the core layering, like the bridge and the referee: no core crate at
10-
# all. This is a transport and nothing else - it is generic over the request,
11-
# response, and push types and knows no Windhawk command, which is what makes it
12-
# testable over an in-process pipe pair with a fake handler. serde/serde_json
13-
# encode and decode the caller's frame types; windows-sys is the named pipe, the
14-
# overlapped I/O, the security descriptor, and the peer token/process queries.
9+
# Outside the core layering, like the bridge: no core crate at all. This is a
10+
# transport and nothing else - it is generic over the request, response, and
11+
# push types and knows no Windhawk command, which is what makes it testable over
12+
# an in-process pipe pair with a fake handler. serde/serde_json encode and
13+
# decode the caller's frame types; windows-sys is the named pipe, the overlapped
14+
# I/O, the security descriptor, and the peer token/process queries.
1515
[dependencies]
1616
serde = { workspace = true }
1717
serde_json = { workspace = true }

‎src/windhawk-core/cli/src/args.rs‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,13 @@ pub enum ModSettingsCommand {
395395
#[arg(value_name = "key=value", required = true)]
396396
pairs: Vec<String>,
397397
},
398+
/// Print the options a mod has written at runtime for its `$dynamicSelect`
399+
/// settings, keyed by setting path, in the order the mod wrote them.
400+
Options {
401+
/// Mod ID.
402+
#[arg(value_name = "id")]
403+
id: String,
404+
},
398405
}
399406

400407
#[derive(Subcommand, Debug)]
@@ -404,6 +411,9 @@ pub enum AppCommand {
404411
#[command(subcommand)]
405412
command: AppSettingsCommand,
406413
},
414+
/// Record the next keyboard shortcut pressed and print it in the form a
415+
/// mod's hotkey setting stores (e.g. ctrl+alt+84).
416+
CaptureHotkey,
407417
}
408418

409419
#[derive(Subcommand, Debug)]

‎src/windhawk-core/cli/src/bin/stub_compiler.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! A stub clang++ for the CLI's compile-bearing integration tests. It stands in
1+
//! A stub clang++ for testing the CLI's compile-bearing commands. It stands in
22
//! for the real compiler at `<CompilerPath>/bin/clang++.exe` so `mod install` /
33
//! `mod compile` / `mod update` can be driven without a toolchain, and doubles
44
//! as the `update run` installer payload.

‎src/windhawk-core/cli/src/bin/stub_schtasks.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
//! A stub `schtasks.exe` for the CLI's registry-mode integration tests,
2-
//! the analogue of `stub_compiler`. The core toggles a Windows scheduled task
1+
//! A stub `schtasks.exe` for testing the CLI's registry-mode commands, the
2+
//! analogue of `stub_compiler`. The core toggles a Windows scheduled task
33
//! via `schtasks.exe /change /tn <task> /enable|/disable` when `app settings
44
//! set disableRunUIScheduledTask` is applied in registry mode; the
55
//! `WINDHAWK_DEBUG_SCHTASKS_PATH` override points the core at this stub so the

‎src/windhawk-core/cli/src/commands/app.rs‎

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33
//! single setting through the preview restart gate and the post-write tray
44
//! poke. The dotted-key schema, value parsing, and patch builder are the pure
55
//! `validate::app_settings` helpers; this module owns the I/O (read, preview,
6-
//! apply, poke) and the render.
6+
//! apply, poke) and the render. `app capture-hotkey` records one keyboard
7+
//! chord through the core (the C ABI async path, the held modifiers streamed
8+
//! to stderr) and prints it in the form a mod's hotkey setting stores.
79
810
use std::io::{self, Write};
911

1012
use serde_json::{Value, json};
1113
use windhawk_core_protocol::{
12-
AppSettings, AppSettingsIntents, AppSettingsPatch, AppSettingsPatchParams, NotifyTrayParams,
13-
TrayAction,
14+
AppSettings, AppSettingsIntents, AppSettingsPatch, AppSettingsPatchParams,
15+
CaptureHotkeyProgress, CaptureHotkeyResult, HotkeyCaptureCanceled, HotkeyModifiers,
16+
NotifyTrayParams, OperationEvent, TrayAction,
1417
};
1518

1619
use crate::Environment;
@@ -34,6 +37,83 @@ pub fn dispatch(
3437
confirm_app_restart,
3538
} => set(env, &key, &value, confirm_app_restart),
3639
},
40+
AppCommand::CaptureHotkey => capture_hotkey(env),
41+
}
42+
}
43+
44+
// ---------------------------------------------------------------------------
45+
// app capture-hotkey
46+
// ---------------------------------------------------------------------------
47+
48+
/// Record one chord. The hook the core installs takes every key while it runs,
49+
/// the console's Ctrl+C included, so the capture ends by the chord, a click on
50+
/// another window, or its own timeout; a Ctrl+C is a chord like any other.
51+
fn capture_hotkey(env: &Environment) -> Result<Box<dyn CommandResult>, CliError> {
52+
env.logger.info("Press a shortcut...");
53+
let result: CaptureHotkeyResult =
54+
env.core
55+
.invoke_async_as("captureHotkey", &json!({}), |event| match event {
56+
OperationEvent::Progress { payload } => {
57+
// A payload this build does not decode is a newer core's;
58+
// nothing to show for it.
59+
if let Ok(progress) =
60+
serde_json::from_value::<CaptureHotkeyProgress>(payload.clone())
61+
{
62+
env.logger
63+
.info(&format!("Held: {}", held_label(progress.modifiers)));
64+
}
65+
}
66+
// The terminal events never reach this callback (the invoke consumes
67+
// them); the catch-all keeps the match exhaustive.
68+
OperationEvent::Installing
69+
| OperationEvent::Completed { .. }
70+
| OperationEvent::Failed { .. } => {}
71+
})?;
72+
73+
match (result.hotkey, result.canceled) {
74+
(Some(hotkey), _) => Ok(Box::new(CaptureHotkeyCliResult { hotkey })),
75+
(None, canceled) => Err(CliError::cancelled(match canceled {
76+
Some(HotkeyCaptureCanceled::FocusLost) => {
77+
"The capture ended: another window came to the front."
78+
}
79+
Some(HotkeyCaptureCanceled::Timeout) => {
80+
"The capture ended: no shortcut was pressed within the time allowed."
81+
}
82+
Some(HotkeyCaptureCanceled::Canceled) | None => "The capture was canceled.",
83+
})),
84+
}
85+
}
86+
87+
/// The held modifiers as a person reads them (`Ctrl+Alt`), or `none`.
88+
fn held_label(modifiers: HotkeyModifiers) -> String {
89+
let names: Vec<&str> = [
90+
(modifiers.ctrl, "Ctrl"),
91+
(modifiers.alt, "Alt"),
92+
(modifiers.shift, "Shift"),
93+
(modifiers.win, "Win"),
94+
]
95+
.into_iter()
96+
.filter(|(held, _)| *held)
97+
.map(|(_, name)| name)
98+
.collect();
99+
if names.is_empty() {
100+
"none".to_owned()
101+
} else {
102+
names.join("+")
103+
}
104+
}
105+
106+
struct CaptureHotkeyCliResult {
107+
hotkey: String,
108+
}
109+
110+
impl CommandResult for CaptureHotkeyCliResult {
111+
fn json_data(&self) -> Value {
112+
json!({ "hotkey": self.hotkey })
113+
}
114+
115+
fn write_text(&self, out: &mut dyn Write) -> io::Result<()> {
116+
writeln!(out, "{}", self.hotkey)
37117
}
38118
}
39119

@@ -353,6 +433,26 @@ mod render_tests {
353433
assert_eq!(render_text(&list), "<empty list>\n");
354434
}
355435

436+
#[test]
437+
fn capture_hotkey_renders_the_stored_form_and_labels_the_held_modifiers() {
438+
let result = CaptureHotkeyCliResult {
439+
hotkey: "ctrl+alt+84".to_owned(),
440+
};
441+
assert_eq!(render_text(&result), "ctrl+alt+84\n");
442+
assert_eq!(result.json_data(), json!({ "hotkey": "ctrl+alt+84" }));
443+
444+
assert_eq!(
445+
held_label(HotkeyModifiers {
446+
ctrl: true,
447+
alt: false,
448+
shift: true,
449+
win: true,
450+
}),
451+
"Ctrl+Shift+Win"
452+
);
453+
assert_eq!(held_label(HotkeyModifiers::default()), "none");
454+
}
455+
356456
#[test]
357457
fn app_settings_set_renders_restart_and_silent() {
358458
let restart = AppSettingsSetResult {

‎src/windhawk-core/cli/src/commands/data.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pub fn export(env: &Environment, args: DataExportArgs) -> Result<Box<dyn Command
4343
let result: ExportUserDataResult = env.core.invoke_as("exportUserData", &params)?;
4444

4545
// Best-effort export: per-mod warnings go to stderr (they also ride in the
46-
// `--json` summary), matching `data export` in the plan.
46+
// `--json` summary).
4747
for warning in &result.summary.warnings {
4848
env.logger
4949
.warn(&format!("{}: {}", warning.mod_id, warning.message));
@@ -310,7 +310,7 @@ fn build_selection(flags: &SelectionFlags) -> Result<UserDataSelection, CliError
310310
}
311311

312312
/// Reject a `--skip-*` / `--with-*` id that is not in the export scope (a usage
313-
/// error, exit 2, per CLI_SPEC). Only runs when there are per-mod overrides; a
313+
/// error, exit 2). Only runs when there are per-mod overrides; a
314314
/// keyword scope reads the installed set to resolve which ids it selects, while
315315
/// an explicit `--mods` list is the scope itself.
316316
#[track_caller]

‎src/windhawk-core/cli/src/commands/mods/mod.rs‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! The `mod` group: `mod list`, `mod show`, `mod config get`/`set`, `mod
2-
//! settings get`/`set`, `mod enable`/`disable`, `mod remove` (sync), plus the
3-
//! compile-bearing async commands `mod install`, `mod update`, and `mod
4-
//! compile`.
2+
//! settings get`/`set`/`options`, `mod enable`/`disable`, `mod remove` (sync),
3+
//! plus the compile-bearing async commands `mod install`, `mod update`, and
4+
//! `mod compile`.
55
66
mod config;
77
mod install;
@@ -40,6 +40,7 @@ pub fn dispatch(
4040
ModCommand::Settings { command } => match command {
4141
ModSettingsCommand::Get { id, key } => settings::settings_get(env, &id, key.as_deref()),
4242
ModSettingsCommand::Set { id, pairs } => settings::settings_set(env, &id, &pairs),
43+
ModSettingsCommand::Options { id } => settings::settings_options(env, &id),
4344
},
4445
ModCommand::Install(args) => install::install(env, args),
4546
ModCommand::Update(args) => install::update(env, args),

0 commit comments

Comments
 (0)