Skip to content

Commit 884455a

Browse files
authored
Merge pull request #94 from appergb/codex/windows-recording-fixes
fix: stabilize Windows recording startup
2 parents 9d92500 + f937f58 commit 884455a

11 files changed

Lines changed: 209 additions & 72 deletions

File tree

openless-all/app/package-lock.json

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

openless-all/app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "openless-app",
33
"private": true,
4-
"version": "1.2.5",
4+
"version": "1.2.6",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

openless-all/app/src-tauri/Cargo.lock

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

openless-all/app/src-tauri/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "openless"
3-
version = "1.2.5"
3+
version = "1.2.6"
44
description = "OpenLess — local voice input that types where your cursor is"
55
authors = ["OpenLess"]
66
edition = "2021"
@@ -54,10 +54,12 @@ objc2-app-kit = "0.2"
5454
[target.'cfg(target_os = "windows")'.dependencies]
5555
windows = { version = "0.58", features = [
5656
"Win32_Foundation",
57+
"Win32_UI_Shell",
5758
"Win32_UI_Input_KeyboardAndMouse",
5859
"Win32_UI_WindowsAndMessaging",
5960
"Win32_System_Threading",
6061
] }
62+
winreg = "0.52"
6163

6264
# 跨平台磨砂层(macOS NSVisualEffectView / Windows Mica)。
6365
[target.'cfg(any(target_os = "macos", target_os = "windows"))'.dependencies]

openless-all/app/src-tauri/src/commands.rs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -262,19 +262,46 @@ pub fn open_system_settings(pane: String) -> Result<(), String> {
262262
.map(|_| ())
263263
.map_err(|e| e.to_string())
264264
}
265-
#[cfg(not(target_os = "macos"))]
265+
#[cfg(target_os = "windows")]
266266
{
267+
use windows::core::PCWSTR;
268+
use windows::Win32::UI::Shell::ShellExecuteW;
269+
use windows::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL;
270+
271+
fn wide_null(value: &str) -> Vec<u16> {
272+
value.encode_utf16().chain(std::iter::once(0)).collect()
273+
}
274+
267275
let uri = match pane.as_str() {
268276
"microphone" => "ms-settings:privacy-microphone",
269277
"sound" => "ms-settings:sound",
270278
"accessibility" => "ms-settings:easeofaccess",
271279
_ => "ms-settings:",
272280
};
273-
std::process::Command::new("cmd")
274-
.args(["/C", "start", "", uri])
275-
.spawn()
276-
.map(|_| ())
277-
.map_err(|e| e.to_string())
281+
282+
let operation = wide_null("open");
283+
let target = wide_null(uri);
284+
let result = unsafe {
285+
ShellExecuteW(
286+
None,
287+
PCWSTR(operation.as_ptr()),
288+
PCWSTR(target.as_ptr()),
289+
PCWSTR::null(),
290+
PCWSTR::null(),
291+
SW_SHOWNORMAL,
292+
)
293+
};
294+
295+
if result.0 as isize <= 32 {
296+
Err(format!("ShellExecuteW failed: {}", result.0 as isize))
297+
} else {
298+
Ok(())
299+
}
300+
}
301+
#[cfg(all(not(target_os = "macos"), not(target_os = "windows")))]
302+
{
303+
let _ = pane;
304+
Err("open_system_settings is only supported on macOS and Windows".to_string())
278305
}
279306
}
280307

openless-all/app/src-tauri/src/coordinator.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -794,7 +794,10 @@ async fn end_session(inner: &Arc<Inner>) -> Result<(), String> {
794794
}
795795
};
796796
if !proceed_to_insert {
797-
log::info!("[coord] cancel detected before insert — discarding output (chars={})", polished.chars().count());
797+
log::info!(
798+
"[coord] cancel detected before insert — discarding output (chars={})",
799+
polished.chars().count()
800+
);
798801
return Ok(());
799802
}
800803

@@ -916,6 +919,15 @@ fn hotkey_injection_dry_run_enabled() -> bool {
916919
fn ensure_microphone_permission(inner: &Arc<Inner>) -> Result<(), String> {
917920
use crate::permissions::{self, PermissionStatus};
918921

922+
#[cfg(target_os = "windows")]
923+
{
924+
let _ = inner;
925+
if permissions::windows_microphone_access_explicitly_denied() {
926+
return Err("需要麦克风权限,当前状态: Denied".to_string());
927+
}
928+
return Ok(());
929+
}
930+
919931
let status = permissions::check_microphone();
920932
if matches!(
921933
status,
@@ -1144,6 +1156,35 @@ mod tests {
11441156
assert_eq!(state.phase, SessionPhase::Starting);
11451157
assert!(state.pending_stop);
11461158
}
1159+
1160+
#[tokio::test]
1161+
async fn repeated_pressed_edge_during_hold_session_does_not_restart() {
1162+
let coordinator = Coordinator::new();
1163+
coordinator
1164+
.inner
1165+
.prefs
1166+
.set(crate::types::UserPreferences {
1167+
hotkey: crate::types::HotkeyBinding {
1168+
trigger: HotkeyTrigger::RightControl,
1169+
mode: HotkeyMode::Hold,
1170+
},
1171+
..Default::default()
1172+
})
1173+
.unwrap();
1174+
coordinator.inner.state.lock().phase = SessionPhase::Listening;
1175+
coordinator
1176+
.inner
1177+
.hotkey_trigger_held
1178+
.store(true, Ordering::SeqCst);
1179+
1180+
handle_pressed_edge(&coordinator.inner).await;
1181+
1182+
assert_eq!(
1183+
coordinator.inner.state.lock().phase,
1184+
SessionPhase::Listening
1185+
);
1186+
assert!(coordinator.inner.hotkey_trigger_held.load(Ordering::SeqCst));
1187+
}
11471188
}
11481189

11491190
fn enabled_phrases(inner: &Arc<Inner>) -> Vec<String> {

openless-all/app/src-tauri/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ pub fn run() {
4242
// 否则两份 OpenLess(如 /Applications/ + dev build)会各自抓全局热键,
4343
// 导致按一次键、两个进程同时跑流水线、文本被插入两遍。见 issue #50。
4444
.plugin(tauri_plugin_single_instance::init(|app, _argv, _cwd| {
45-
log::info!("[single-instance] another instance launched, focusing existing main window");
45+
log::info!(
46+
"[single-instance] another instance launched, focusing existing main window"
47+
);
4648
show_main_window(app);
4749
}))
4850
.plugin(tauri_plugin_shell::init())

0 commit comments

Comments
 (0)