From 7cbd63accdce7aac05905b4c19f0abd041def6ff Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Sun, 5 Jan 2025 21:53:29 +0100 Subject: [PATCH] feat(win32 gui): add direct copy to windows clipboard support closes #100 --- src/renderer/vaxis/renderer.zig | 43 +++++++++++++++++++++++++-------- src/renderer/win32/renderer.zig | 1 + src/tui/editor.zig | 7 +++++- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/renderer/vaxis/renderer.zig b/src/renderer/vaxis/renderer.zig index 6318757..7af7735 100644 --- a/src/renderer/vaxis/renderer.zig +++ b/src/renderer/vaxis/renderer.zig @@ -350,18 +350,41 @@ pub fn request_system_clipboard(self: *Self) void { self.vx.requestSystemClipboard(self.tty.anyWriter()) catch |e| log.logger(log_name).err("request_system_clipboard", e); } -pub fn request_windows_clipboard(allocator: std.mem.Allocator) ![]u8 { +const win32 = struct { const windows = std.os.windows; - const win32 = struct { - pub extern "user32" fn OpenClipboard(hWndNewOwner: ?windows.HWND) callconv(windows.WINAPI) windows.BOOL; - pub extern "user32" fn CloseClipboard() callconv(windows.WINAPI) windows.BOOL; - pub extern "user32" fn SetClipboardData(uFormat: windows.UINT, hMem: windows.HANDLE) callconv(windows.WINAPI) ?windows.HANDLE; - pub extern "user32" fn GetClipboardData(uFormat: windows.UINT) callconv(windows.WINAPI) ?windows.HANDLE; - pub extern "kernel32" fn GlobalLock(hMem: windows.HANDLE) ?windows.LPVOID; - pub extern "kernel32" fn GlobalUnlock(hMem: windows.HANDLE) windows.BOOL; - const CF_TEXT = @as(c_int, 1); - }; + pub extern "user32" fn OpenClipboard(hWndNewOwner: ?windows.HWND) callconv(windows.WINAPI) windows.BOOL; + pub extern "user32" fn CloseClipboard() callconv(windows.WINAPI) windows.BOOL; + pub extern "user32" fn SetClipboardData(uFormat: windows.UINT, hMem: windows.HANDLE) callconv(windows.WINAPI) ?windows.HANDLE; + pub extern "user32" fn GetClipboardData(uFormat: windows.UINT) callconv(windows.WINAPI) ?windows.HANDLE; + pub extern "user32" fn EmptyClipboard() windows.BOOL; + pub extern "kernel32" fn GlobalAlloc(flags: c_int, size: usize) ?windows.HANDLE; + pub extern "kernel32" fn GlobalFree(hMem: windows.HANDLE) windows.BOOL; + pub extern "kernel32" fn GlobalLock(hMem: windows.HANDLE) ?windows.LPVOID; + pub extern "kernel32" fn GlobalUnlock(hMem: windows.HANDLE) windows.BOOL; + const CF_TEXT = @as(c_int, 1); + const GMEM_MOVEABLE = @as(c_int, 2); +}; +pub fn copy_to_windows_clipboard(text: []const u8) !void { + const mem = win32.GlobalAlloc(win32.GMEM_MOVEABLE, text.len + 1) orelse return error.GlobalAllocFalied; + const data: [*c]u8 = @ptrCast(win32.GlobalLock(mem) orelse return error.ClipboardDataLockFailed); + @memcpy(data[0..text.len], text); + data[text.len] = 0; + _ = win32.GlobalUnlock(mem); + + if (win32.OpenClipboard(null) == 0) { + _ = win32.GlobalFree(mem); + return error.OpenClipBoardFailed; + } + defer _ = win32.CloseClipboard(); + + _ = win32.EmptyClipboard(); + if (win32.SetClipboardData(win32.CF_TEXT, mem) == null) { + _ = win32.GlobalFree(mem); + } +} + +pub fn request_windows_clipboard(allocator: std.mem.Allocator) ![]u8 { if (win32.OpenClipboard(null) == 0) return error.OpenClipBoardFailed; defer _ = win32.CloseClipboard(); diff --git a/src/renderer/win32/renderer.zig b/src/renderer/win32/renderer.zig index c977662..42bf501 100644 --- a/src/renderer/win32/renderer.zig +++ b/src/renderer/win32/renderer.zig @@ -366,6 +366,7 @@ pub fn copy_to_system_clipboard(self: *Self, text: []const u8) void { std.log.warn("TODO: copy_to_system_clipboard", .{}); } +pub const copy_to_windows_clipboard = @import("tuirenderer").copy_to_windows_clipboard; pub const request_windows_clipboard = @import("tuirenderer").request_windows_clipboard; pub fn request_mouse_cursor_text(self: *Self, push_or_pop: bool) void { diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 9274e04..efdc5ce 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -2163,7 +2163,12 @@ pub const Editor = struct { if (self.clipboard) |old| self.allocator.free(old); self.clipboard = text; - tui.current().rdr.copy_to_system_clipboard(text); + if (builtin.os.tag == .windows) { + @import("renderer").copy_to_windows_clipboard(text) catch |e| + self.logger.print_err("clipboard", "failed to set clipboard: {any}", .{e}); + } else { + tui.current().rdr.copy_to_system_clipboard(text); + } } fn copy_selection(root: Buffer.Root, sel: Selection, text_allocator: Allocator, metrics: Buffer.Metrics) ![]u8 {