fix: write pane copies to native windows clipboard (#1082)

refs #1067

Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com>
This commit is contained in:
akbash 2026-07-06 17:13:36 +03:00 committed by GitHub
parent cdfb75da44
commit c17c419c10
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 64 additions and 5 deletions

View File

@ -49,10 +49,13 @@ portable-pty = { path = "vendor/portable-pty" }
windows-sys = { version = "0.61.2", features = [
"Wdk_System_Threading",
"Win32_Foundation",
"Win32_System_DataExchange",
"Win32_System_Diagnostics_Debug",
"Win32_System_Diagnostics_ToolHelp",
"Win32_System_Console",
"Win32_System_Kernel",
"Win32_System_Memory",
"Win32_System_Ole",
"Win32_System_Pipes",
"Win32_System_Threading",
"Win32_UI_Shell",

View File

@ -3,17 +3,19 @@ use std::{
ffi::c_void,
mem::{size_of, MaybeUninit},
path::PathBuf,
ptr::null_mut,
ptr::{copy_nonoverlapping, null_mut},
};
use windows_sys::{
Wdk::System::Threading::{NtQueryInformationProcess, ProcessBasicInformation},
Win32::{
Foundation::{
CloseHandle, LocalFree, HANDLE, INVALID_HANDLE_VALUE, NTSTATUS, STATUS_SUCCESS,
UNICODE_STRING,
CloseHandle, GlobalFree, LocalFree, HANDLE, INVALID_HANDLE_VALUE, NTSTATUS,
STATUS_SUCCESS, UNICODE_STRING,
},
System::{
Console::GetConsoleWindow,
DataExchange::{CloseClipboard, EmptyClipboard, OpenClipboard, SetClipboardData},
Diagnostics::{
Debug::ReadProcessMemory,
ToolHelp::{
@ -21,6 +23,8 @@ use windows_sys::{
TH32CS_SNAPPROCESS,
},
},
Memory::{GlobalAlloc, GlobalLock, GlobalUnlock, GMEM_MOVEABLE},
Ole::CF_UNICODETEXT,
Threading::{
GetExitCodeProcess, OpenProcess, TerminateProcess, PROCESS_BASIC_INFORMATION,
PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_VM_READ,
@ -420,8 +424,50 @@ pub fn process_exists(pid: u32) -> bool {
ok && exit_code == STILL_ACTIVE
}
pub fn write_clipboard(_bytes: &[u8]) -> bool {
false
pub fn write_clipboard(bytes: &[u8]) -> bool {
let Ok(text) = std::str::from_utf8(bytes) else {
return false;
};
if text.contains('\0') {
return false;
}
let mut utf16: Vec<u16> = text.encode_utf16().collect();
utf16.push(0);
let Some(byte_len) = utf16.len().checked_mul(size_of::<u16>()) else {
return false;
};
unsafe {
let owner = GetConsoleWindow();
if owner.is_null() || OpenClipboard(owner) == 0 {
return false;
}
let _clipboard = ClipboardGuard;
if EmptyClipboard() == 0 {
return false;
}
let memory = GlobalAlloc(GMEM_MOVEABLE, byte_len);
if memory.is_null() {
return false;
}
let locked = GlobalLock(memory);
if locked.is_null() {
GlobalFree(memory);
return false;
}
copy_nonoverlapping(utf16.as_ptr(), locked.cast::<u16>(), utf16.len());
GlobalUnlock(memory);
if SetClipboardData(CF_UNICODETEXT as u32, memory).is_null() {
GlobalFree(memory);
return false;
}
true
}
}
pub fn read_clipboard_text() -> Option<String> {
@ -467,6 +513,16 @@ fn wide_null(value: &str) -> Vec<u16> {
struct ProcessHandle(HANDLE);
struct ClipboardGuard;
impl Drop for ClipboardGuard {
fn drop(&mut self) {
unsafe {
CloseClipboard();
}
}
}
impl ProcessHandle {
fn open(pid: u32, access: u32) -> Option<Self> {
if pid == 0 {