Merge pull request #121 from Cooper-X-Oak/codex/windows-current-caret-insertion
fix(windows): 粘贴前校验原目标窗口已恢复
This commit is contained in:
3 files changed
+55
-12
No files matched your search
@@ -5,6 +5,8 @@ param(
|
||||
[string]$Phrase = "OpenLess Windows real regression",
|
||||
[int]$TimeoutSeconds = 120,
|
||||
[int]$VirtualKey = 0xA3,
|
||||
[int]$ManualSpeechSeconds = 8,
|
||||
[switch]$ManualSpeech,
|
||||
[switch]$DebugHotkeyEvents
|
||||
)
|
||||
|
||||
@@ -356,7 +358,7 @@ try {
|
||||
|
||||
$inputTarget = $null
|
||||
try {
|
||||
if (-not (Wait-LogPattern $logPath "WH_KEYBOARD_LL installed" 20)) {
|
||||
if (-not (Wait-LogPattern $logPath "hotkey listener installed|Windows low-level keyboard hook" 20)) {
|
||||
throw "Windows low-level keyboard hook was not installed."
|
||||
}
|
||||
|
||||
@@ -370,7 +372,12 @@ try {
|
||||
throw "OpenLess recording session did not start."
|
||||
}
|
||||
|
||||
Speak-TestPhrase $Phrase
|
||||
if ($ManualSpeech) {
|
||||
Write-Host "[action] Please speak into the real microphone for $ManualSpeechSeconds seconds."
|
||||
Start-Sleep -Seconds $ManualSpeechSeconds
|
||||
} else {
|
||||
Speak-TestPhrase $Phrase
|
||||
}
|
||||
Start-Sleep -Milliseconds 800
|
||||
Release-Hotkey
|
||||
|
||||
@@ -388,8 +395,8 @@ try {
|
||||
if ([string]::IsNullOrWhiteSpace($latest.rawTranscript) -or [string]::IsNullOrWhiteSpace($latest.finalText)) {
|
||||
throw "Latest history item is missing rawTranscript or finalText."
|
||||
}
|
||||
if (@("copiedFallback", "pasteSent") -notcontains $latest.insertStatus) {
|
||||
throw "Expected Windows insertStatus copiedFallback or pasteSent, got '$($latest.insertStatus)'."
|
||||
if ($latest.insertStatus -ne "pasteSent") {
|
||||
throw "Expected Windows insertStatus pasteSent after guarded foreground restore, got '$($latest.insertStatus)'."
|
||||
}
|
||||
|
||||
Focus-Window $inputTarget.Process | Out-Null
|
||||
@@ -403,6 +410,9 @@ try {
|
||||
if ([string]::IsNullOrWhiteSpace($targetText)) {
|
||||
throw "$Target clipboard readback is empty after Ctrl+A/C."
|
||||
}
|
||||
if (-not $targetText.Contains($latest.finalText)) {
|
||||
throw "$Target readback does not contain latest finalText; insertion was not proven at the target caret."
|
||||
}
|
||||
|
||||
Write-Host "[ok] History updated. raw='$($latest.rawTranscript)'"
|
||||
Write-Host "[ok] Final text length=$($latest.finalText.Length), insertStatus=$($latest.insertStatus)"
|
||||
|
||||
@@ -1276,9 +1276,16 @@ async fn end_session(inner: &Arc<Inner>) -> Result<(), String> {
|
||||
}
|
||||
|
||||
let focus_target = inner.state.lock().focus_target;
|
||||
restore_focus_target_if_possible(focus_target);
|
||||
let focus_ready_for_paste = restore_focus_target_if_possible(focus_target);
|
||||
let restore_clipboard = inner.prefs.get().restore_clipboard_after_paste;
|
||||
let status = inner.inserter.insert(&polished, restore_clipboard);
|
||||
let status = if focus_ready_for_paste {
|
||||
inner.inserter.insert(&polished, restore_clipboard)
|
||||
} else {
|
||||
log::warn!(
|
||||
"[coord] original insertion target is not foreground; copied output without paste"
|
||||
);
|
||||
inner.inserter.copy_fallback(&polished)
|
||||
};
|
||||
let inserted_chars = polished.chars().count() as u32;
|
||||
|
||||
// 累计每条 enabled 词条在最终文本中的命中次数。
|
||||
@@ -2338,7 +2345,7 @@ fn capture_frontmost_app() -> Option<String> {
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn restore_focus_target_if_possible(target: Option<usize>) {
|
||||
fn restore_focus_target_if_possible(target: Option<usize>) -> bool {
|
||||
use std::ffi::c_void;
|
||||
use std::time::Duration;
|
||||
use windows::Win32::Foundation::HWND;
|
||||
@@ -2346,18 +2353,22 @@ fn restore_focus_target_if_possible(target: Option<usize>) {
|
||||
GetForegroundWindow, IsIconic, IsWindow, SetForegroundWindow, ShowWindow, SW_RESTORE,
|
||||
};
|
||||
|
||||
let Some(raw_target) = target else { return };
|
||||
let Some(raw_target) = target else {
|
||||
log::warn!("[coord] no original Windows insertion target captured");
|
||||
return false;
|
||||
};
|
||||
let hwnd = HWND(raw_target as *mut c_void);
|
||||
if hwnd.0.is_null() {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if !unsafe { IsWindow(hwnd).as_bool() } {
|
||||
return;
|
||||
log::warn!("[coord] original Windows insertion target is no longer a valid window");
|
||||
return false;
|
||||
}
|
||||
|
||||
let foreground = unsafe { GetForegroundWindow() };
|
||||
if foreground == hwnd {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if unsafe { IsIconic(hwnd).as_bool() } {
|
||||
@@ -2365,10 +2376,19 @@ fn restore_focus_target_if_possible(target: Option<usize>) {
|
||||
}
|
||||
let _ = unsafe { SetForegroundWindow(hwnd) };
|
||||
std::thread::sleep(Duration::from_millis(60));
|
||||
|
||||
let foreground = unsafe { GetForegroundWindow() };
|
||||
if foreground != hwnd {
|
||||
log::warn!("[coord] failed to restore original Windows insertion target before paste");
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn restore_focus_target_if_possible(_target: Option<usize>) {}
|
||||
fn restore_focus_target_if_possible(_target: Option<usize>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn show_capsule_window_no_activate() -> bool {
|
||||
|
||||
@@ -50,6 +50,19 @@ impl TextInserter {
|
||||
}
|
||||
insertion_success_status()
|
||||
}
|
||||
|
||||
/// Copy text without attempting a synthetic paste. Used when the platform cannot
|
||||
/// prove the original input target is active enough to safely receive Ctrl/Cmd+V.
|
||||
pub fn copy_fallback(&self, text: &str) -> InsertStatus {
|
||||
if text.is_empty() {
|
||||
return InsertStatus::CopiedFallback;
|
||||
}
|
||||
if copy_to_clipboard(text) {
|
||||
InsertStatus::CopiedFallback
|
||||
} else {
|
||||
InsertStatus::Failed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TextInserter {
|
||||
|
||||
Reference in new issue
Block a user