From b6ffab3ae3d44c9d9ba6c17cea802f649d5aba87 Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Wed, 17 Nov 2021 17:05:24 -0800 Subject: [PATCH] basic clipboard monitoring working in Windows --- build.zig | 1 + src/main-windows.zig | 658 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 653 insertions(+), 6 deletions(-) diff --git a/build.zig b/build.zig index 57f8c9c..958591c 100644 --- a/build.zig +++ b/build.zig @@ -45,6 +45,7 @@ pub fn build(b: *std.build.Builder) void { if (target.getOs().tag == .windows) { // woah...we don't actually need libc! exe.linkSystemLibrary("user32"); + exe.linkSystemLibrary("kernel32"); } exe.install(); diff --git a/src/main-windows.zig b/src/main-windows.zig index d8e1b19..b0a7dba 100644 --- a/src/main-windows.zig +++ b/src/main-windows.zig @@ -192,6 +192,10 @@ extern "USER32" fn RegisterClassA( lpWndClass: ?*const WNDCLASSA, ) callconv(w.WINAPI) u16; +extern "USER32" fn DestroyWindow( + hWnd: ?w.HWND, +) callconv(w.WINAPI) w.BOOL; + const WS_OVERLAPPEDWINDOW = WINDOW_STYLE.TILEDWINDOW; const CW_USEDEFAULT = @as(i32, -2147483648); const BOOL = i32; @@ -270,6 +274,359 @@ extern "USER32" fn DispatchMessageA( lpMsg: ?*const MSG, ) callconv(w.WINAPI) LRESULT; +const WM_NULL = @as(u32, 0); +const WM_CREATE = @as(u32, 1); +const WM_DESTROY = @as(u32, 2); +const WM_MOVE = @as(u32, 3); +const WM_SIZE = @as(u32, 5); +const WM_ACTIVATE = @as(u32, 6); +const WA_INACTIVE = @as(u32, 0); +const WA_ACTIVE = @as(u32, 1); +const WA_CLICKACTIVE = @as(u32, 2); +const WM_SETFOCUS = @as(u32, 7); +const WM_KILLFOCUS = @as(u32, 8); +const WM_ENABLE = @as(u32, 10); +const WM_SETREDRAW = @as(u32, 11); +const WM_SETTEXT = @as(u32, 12); +const WM_GETTEXT = @as(u32, 13); +const WM_GETTEXTLENGTH = @as(u32, 14); +const WM_PAINT = @as(u32, 15); +const WM_CLOSE = @as(u32, 16); +const WM_QUERYENDSESSION = @as(u32, 17); +const WM_QUERYOPEN = @as(u32, 19); +const WM_ENDSESSION = @as(u32, 22); +const WM_QUIT = @as(u32, 18); +const WM_ERASEBKGND = @as(u32, 20); +const WM_SYSCOLORCHANGE = @as(u32, 21); +const WM_SHOWWINDOW = @as(u32, 24); +const WM_WININICHANGE = @as(u32, 26); +const WM_SETTINGCHANGE = @as(u32, 26); +const WM_DEVMODECHANGE = @as(u32, 27); +const WM_ACTIVATEAPP = @as(u32, 28); +const WM_FONTCHANGE = @as(u32, 29); +const WM_TIMECHANGE = @as(u32, 30); +const WM_CANCELMODE = @as(u32, 31); +const WM_SETCURSOR = @as(u32, 32); +const WM_MOUSEACTIVATE = @as(u32, 33); +const WM_CHILDACTIVATE = @as(u32, 34); +const WM_QUEUESYNC = @as(u32, 35); +const WM_GETMINMAXINFO = @as(u32, 36); +const WM_PAINTICON = @as(u32, 38); +const WM_ICONERASEBKGND = @as(u32, 39); +const WM_NEXTDLGCTL = @as(u32, 40); +const WM_SPOOLERSTATUS = @as(u32, 42); +const WM_DRAWITEM = @as(u32, 43); +const WM_MEASUREITEM = @as(u32, 44); +const WM_DELETEITEM = @as(u32, 45); +const WM_VKEYTOITEM = @as(u32, 46); +const WM_CHARTOITEM = @as(u32, 47); +const WM_SETFONT = @as(u32, 48); +const WM_GETFONT = @as(u32, 49); +const WM_SETHOTKEY = @as(u32, 50); +const WM_GETHOTKEY = @as(u32, 51); +const WM_QUERYDRAGICON = @as(u32, 55); +const WM_COMPAREITEM = @as(u32, 57); +const WM_GETOBJECT = @as(u32, 61); +const WM_COMPACTING = @as(u32, 65); +const WM_COMMNOTIFY = @as(u32, 68); +const WM_WINDOWPOSCHANGING = @as(u32, 70); +const WM_WINDOWPOSCHANGED = @as(u32, 71); +const WM_POWER = @as(u32, 72); + +const WM_COMMAND = @as(u32, 273); +const WM_INITMENU = @as(u32, 278); +const WM_INITMENUPOPUP = @as(u32, 279); + +const WM_DRAWCLIPBOARD = @as(u32, 776); +const WM_SIZECLIPBOARD = @as(u32, 779); +const WM_CHANGECBCHAIN = @as(u32, 781); + +const RECT = extern struct { + left: i32, + top: i32, + right: i32, + bottom: i32, +}; + +extern "USER32" fn GetClientRect( + hWnd: ?w.HWND, + lpRect: ?*RECT, +) callconv(w.WINAPI) BOOL; + +extern "USER32" fn SendMessageA( + hWnd: ?w.HWND, + Msg: u32, + wParam: w.WPARAM, + lParam: w.LPARAM, +) callconv(w.WINAPI) LRESULT; + +extern "USER32" fn DefWindowProcA( + hWnd: ?w.HWND, + Msg: u32, + wParam: w.WPARAM, + lParam: w.LPARAM, +) callconv(w.WINAPI) LRESULT; + +// GDI +const HDC = *opaque {}; + +pub const PAINTSTRUCT = extern struct { + hdc: ?HDC, + fErase: BOOL, + rcPaint: RECT, + fRestore: BOOL, + fIncUpdate: BOOL, + rgbReserved: [32]u8, +}; + +extern "USER32" fn BeginPaint( + hWnd: ?w.HWND, + lpPaint: ?*PAINTSTRUCT, +) callconv(w.WINAPI) ?HDC; + +extern "USER32" fn EndPaint( + hWnd: ?w.HWND, + lpPaint: ?*const PAINTSTRUCT, +) callconv(w.WINAPI) BOOL; + +const DRAW_TEXT_FORMAT = enum(u32) { + BOTTOM = 8, + CALCRECT = 1024, + CENTER = 1, + EDITCONTROL = 8192, + END_ELLIPSIS = 32768, + EXPANDTABS = 64, + EXTERNALLEADING = 512, + HIDEPREFIX = 1048576, + INTERNAL = 4096, + LEFT = 0, + MODIFYSTRING = 65536, + NOCLIP = 256, + NOFULLWIDTHCHARBREAK = 524288, + NOPREFIX = 2048, + PATH_ELLIPSIS = 16384, + PREFIXONLY = 2097152, + RIGHT = 2, + RTLREADING = 131072, + SINGLELINE = 32, + TABSTOP = 128, + // TOP = 0, this enum value conflicts with LEFT + VCENTER = 4, + WORDBREAK = 16, + WORD_ELLIPSIS = 262144, + _, + pub fn initFlags(o: struct { + BOTTOM: u1 = 0, + CALCRECT: u1 = 0, + CENTER: u1 = 0, + EDITCONTROL: u1 = 0, + END_ELLIPSIS: u1 = 0, + EXPANDTABS: u1 = 0, + EXTERNALLEADING: u1 = 0, + HIDEPREFIX: u1 = 0, + INTERNAL: u1 = 0, + LEFT: u1 = 0, + MODIFYSTRING: u1 = 0, + NOCLIP: u1 = 0, + NOFULLWIDTHCHARBREAK: u1 = 0, + NOPREFIX: u1 = 0, + PATH_ELLIPSIS: u1 = 0, + PREFIXONLY: u1 = 0, + RIGHT: u1 = 0, + RTLREADING: u1 = 0, + SINGLELINE: u1 = 0, + TABSTOP: u1 = 0, + VCENTER: u1 = 0, + WORDBREAK: u1 = 0, + WORD_ELLIPSIS: u1 = 0, + }) DRAW_TEXT_FORMAT { + return @intToEnum(DRAW_TEXT_FORMAT, (if (o.BOTTOM == 1) @enumToInt(DRAW_TEXT_FORMAT.BOTTOM) else 0) | (if (o.CALCRECT == 1) @enumToInt(DRAW_TEXT_FORMAT.CALCRECT) else 0) | (if (o.CENTER == 1) @enumToInt(DRAW_TEXT_FORMAT.CENTER) else 0) | (if (o.EDITCONTROL == 1) @enumToInt(DRAW_TEXT_FORMAT.EDITCONTROL) else 0) | (if (o.END_ELLIPSIS == 1) @enumToInt(DRAW_TEXT_FORMAT.END_ELLIPSIS) else 0) | (if (o.EXPANDTABS == 1) @enumToInt(DRAW_TEXT_FORMAT.EXPANDTABS) else 0) | (if (o.EXTERNALLEADING == 1) @enumToInt(DRAW_TEXT_FORMAT.EXTERNALLEADING) else 0) | (if (o.HIDEPREFIX == 1) @enumToInt(DRAW_TEXT_FORMAT.HIDEPREFIX) else 0) | (if (o.INTERNAL == 1) @enumToInt(DRAW_TEXT_FORMAT.INTERNAL) else 0) | (if (o.LEFT == 1) @enumToInt(DRAW_TEXT_FORMAT.LEFT) else 0) | (if (o.MODIFYSTRING == 1) @enumToInt(DRAW_TEXT_FORMAT.MODIFYSTRING) else 0) | (if (o.NOCLIP == 1) @enumToInt(DRAW_TEXT_FORMAT.NOCLIP) else 0) | (if (o.NOFULLWIDTHCHARBREAK == 1) @enumToInt(DRAW_TEXT_FORMAT.NOFULLWIDTHCHARBREAK) else 0) | (if (o.NOPREFIX == 1) @enumToInt(DRAW_TEXT_FORMAT.NOPREFIX) else 0) | (if (o.PATH_ELLIPSIS == 1) @enumToInt(DRAW_TEXT_FORMAT.PATH_ELLIPSIS) else 0) | (if (o.PREFIXONLY == 1) @enumToInt(DRAW_TEXT_FORMAT.PREFIXONLY) else 0) | (if (o.RIGHT == 1) @enumToInt(DRAW_TEXT_FORMAT.RIGHT) else 0) | (if (o.RTLREADING == 1) @enumToInt(DRAW_TEXT_FORMAT.RTLREADING) else 0) | (if (o.SINGLELINE == 1) @enumToInt(DRAW_TEXT_FORMAT.SINGLELINE) else 0) | (if (o.TABSTOP == 1) @enumToInt(DRAW_TEXT_FORMAT.TABSTOP) else 0) | (if (o.VCENTER == 1) @enumToInt(DRAW_TEXT_FORMAT.VCENTER) else 0) | (if (o.WORDBREAK == 1) @enumToInt(DRAW_TEXT_FORMAT.WORDBREAK) else 0) | (if (o.WORD_ELLIPSIS == 1) @enumToInt(DRAW_TEXT_FORMAT.WORD_ELLIPSIS) else 0)); + } +}; + +const DT_BOTTOM = DRAW_TEXT_FORMAT.BOTTOM; +const DT_CALCRECT = DRAW_TEXT_FORMAT.CALCRECT; +const DT_CENTER = DRAW_TEXT_FORMAT.CENTER; +const DT_EDITCONTROL = DRAW_TEXT_FORMAT.EDITCONTROL; +const DT_END_ELLIPSIS = DRAW_TEXT_FORMAT.END_ELLIPSIS; +const DT_EXPANDTABS = DRAW_TEXT_FORMAT.EXPANDTABS; +const DT_EXTERNALLEADING = DRAW_TEXT_FORMAT.EXTERNALLEADING; +const DT_HIDEPREFIX = DRAW_TEXT_FORMAT.HIDEPREFIX; +const DT_INTERNAL = DRAW_TEXT_FORMAT.INTERNAL; +const DT_LEFT = DRAW_TEXT_FORMAT.LEFT; +const DT_MODIFYSTRING = DRAW_TEXT_FORMAT.MODIFYSTRING; +const DT_NOCLIP = DRAW_TEXT_FORMAT.NOCLIP; +const DT_NOFULLWIDTHCHARBREAK = DRAW_TEXT_FORMAT.NOFULLWIDTHCHARBREAK; +const DT_NOPREFIX = DRAW_TEXT_FORMAT.NOPREFIX; +const DT_PATH_ELLIPSIS = DRAW_TEXT_FORMAT.PATH_ELLIPSIS; +const DT_PREFIXONLY = DRAW_TEXT_FORMAT.PREFIXONLY; +const DT_RIGHT = DRAW_TEXT_FORMAT.RIGHT; +const DT_RTLREADING = DRAW_TEXT_FORMAT.RTLREADING; +const DT_SINGLELINE = DRAW_TEXT_FORMAT.SINGLELINE; +const DT_TABSTOP = DRAW_TEXT_FORMAT.TABSTOP; +const DT_TOP = DRAW_TEXT_FORMAT.LEFT; +const DT_VCENTER = DRAW_TEXT_FORMAT.VCENTER; +const DT_WORDBREAK = DRAW_TEXT_FORMAT.WORDBREAK; +const DT_WORD_ELLIPSIS = DRAW_TEXT_FORMAT.WORD_ELLIPSIS; + +extern "USER32" fn DrawTextA( + hdc: ?HDC, + lpchText: [*:0]const u8, + cchText: i32, + lprc: ?*RECT, + format: DRAW_TEXT_FORMAT, +) callconv(w.WINAPI) i32; + +extern "USER32" fn InvalidateRect( + hWnd: ?w.HWND, + lpRect: ?*const RECT, + bErase: w.BOOL, +) callconv(w.WINAPI) BOOL; + +extern "USER32" fn UpdateWindow( + hWnd: ?w.HWND, +) callconv(w.WINAPI) BOOL; + +// Data exchange +extern "USER32" fn GetClipboardData( + uFormat: u32, +) callconv(w.WINAPI) ?w.HANDLE; + +extern "USER32" fn GetClipboardOwner() callconv(w.WINAPI) ?w.HWND; + +extern "USER32" fn SetClipboardViewer( + hWndNewViewer: ?w.HWND, +) callconv(w.WINAPI) ?w.HWND; + +extern "USER32" fn OpenClipboard( + hWndNewOwner: ?w.HWND, +) callconv(w.WINAPI) BOOL; + +extern "USER32" fn CloseClipboard() callconv(w.WINAPI) BOOL; + +extern "USER32" fn ChangeClipboardChain( + hWndRemove: ?w.HWND, + hWndNewNext: ?w.HWND, +) callconv(w.WINAPI) BOOL; + +extern "USER32" fn GetPriorityClipboardFormat( + paFormatPriorityList: [*]u32, + cFormats: i32, +) callconv(w.WINAPI) i32; + +// System +extern "USER32" fn PostQuitMessage( + nExitCode: i32, +) callconv(w.WINAPI) void; + +const CLIPBOARD_FORMATS = enum(u32) { + TEXT = 1, + BITMAP = 2, + METAFILEPICT = 3, + SYLK = 4, + DIF = 5, + TIFF = 6, + OEMTEXT = 7, + DIB = 8, + PALETTE = 9, + PENDATA = 10, + RIFF = 11, + WAVE = 12, + UNICODETEXT = 13, + ENHMETAFILE = 14, + HDROP = 15, + LOCALE = 16, + DIBV5 = 17, + MAX = 18, + OWNERDISPLAY = 128, + DSPTEXT = 129, + DSPBITMAP = 130, + DSPMETAFILEPICT = 131, + DSPENHMETAFILE = 142, + PRIVATEFIRST = 512, + PRIVATELAST = 767, + GDIOBJFIRST = 768, + GDIOBJLAST = 1023, +}; +const CF_TEXT = CLIPBOARD_FORMATS.TEXT; +const CF_BITMAP = CLIPBOARD_FORMATS.BITMAP; +const CF_METAFILEPICT = CLIPBOARD_FORMATS.METAFILEPICT; +const CF_SYLK = CLIPBOARD_FORMATS.SYLK; +const CF_DIF = CLIPBOARD_FORMATS.DIF; +const CF_TIFF = CLIPBOARD_FORMATS.TIFF; +const CF_OEMTEXT = CLIPBOARD_FORMATS.OEMTEXT; +const CF_DIB = CLIPBOARD_FORMATS.DIB; +const CF_PALETTE = CLIPBOARD_FORMATS.PALETTE; +const CF_PENDATA = CLIPBOARD_FORMATS.PENDATA; +const CF_RIFF = CLIPBOARD_FORMATS.RIFF; +const CF_WAVE = CLIPBOARD_FORMATS.WAVE; +const CF_UNICODETEXT = CLIPBOARD_FORMATS.UNICODETEXT; +const CF_ENHMETAFILE = CLIPBOARD_FORMATS.ENHMETAFILE; +const CF_HDROP = CLIPBOARD_FORMATS.HDROP; +const CF_LOCALE = CLIPBOARD_FORMATS.LOCALE; +const CF_DIBV5 = CLIPBOARD_FORMATS.DIBV5; +const CF_MAX = CLIPBOARD_FORMATS.MAX; +const CF_OWNERDISPLAY = CLIPBOARD_FORMATS.OWNERDISPLAY; +const CF_DSPTEXT = CLIPBOARD_FORMATS.DSPTEXT; +const CF_DSPBITMAP = CLIPBOARD_FORMATS.DSPBITMAP; +const CF_DSPMETAFILEPICT = CLIPBOARD_FORMATS.DSPMETAFILEPICT; +const CF_DSPENHMETAFILE = CLIPBOARD_FORMATS.DSPENHMETAFILE; +const CF_PRIVATEFIRST = CLIPBOARD_FORMATS.PRIVATEFIRST; +const CF_PRIVATELAST = CLIPBOARD_FORMATS.PRIVATELAST; +const CF_GDIOBJFIRST = CLIPBOARD_FORMATS.GDIOBJFIRST; +const CF_GDIOBJLAST = CLIPBOARD_FORMATS.GDIOBJLAST; + +// memory +extern "KERNEL32" fn GlobalUnlock( + hMem: isize, +) callconv(w.WINAPI) BOOL; + +extern "KERNEL32" fn GlobalLock( + hMem: isize, +) callconv(w.WINAPI) ?*c_void; + +const GLOBAL_ALLOC_FLAGS = enum(u32) { + HND = 66, + MEM_FIXED = 0, + MEM_MOVEABLE = 2, + MEM_ZEROINIT = 64, + // PTR = 64, this enum value conflicts with MEM_ZEROINIT + _, + pub fn initFlags(o: struct { + HND: u1 = 0, + MEM_FIXED: u1 = 0, + MEM_MOVEABLE: u1 = 0, + MEM_ZEROINIT: u1 = 0, + }) GLOBAL_ALLOC_FLAGS { + return @intToEnum(GLOBAL_ALLOC_FLAGS, (if (o.HND == 1) @enumToInt(GLOBAL_ALLOC_FLAGS.HND) else 0) | (if (o.MEM_FIXED == 1) @enumToInt(GLOBAL_ALLOC_FLAGS.MEM_FIXED) else 0) | (if (o.MEM_MOVEABLE == 1) @enumToInt(GLOBAL_ALLOC_FLAGS.MEM_MOVEABLE) else 0) | (if (o.MEM_ZEROINIT == 1) @enumToInt(GLOBAL_ALLOC_FLAGS.MEM_ZEROINIT) else 0)); + } +}; +const GHND = GLOBAL_ALLOC_FLAGS.HND; +const GMEM_FIXED = GLOBAL_ALLOC_FLAGS.MEM_FIXED; +const GMEM_MOVEABLE = GLOBAL_ALLOC_FLAGS.MEM_MOVEABLE; +const GMEM_ZEROINIT = GLOBAL_ALLOC_FLAGS.MEM_ZEROINIT; +const GPTR = GLOBAL_ALLOC_FLAGS.MEM_ZEROINIT; + +extern "KERNEL32" fn GlobalAlloc( + uFlags: GLOBAL_ALLOC_FLAGS, + dwBytes: usize, +) callconv(w.WINAPI) isize; + +extern "KERNEL32" fn GlobalFree( + hMem: isize, +) callconv(w.WINAPI) isize; + +// resource.h +// #define IDD_MFPLAYBACK_DIALOG 102 +// #define IDM_EXIT 105 +// #define IDC_MFPLAYBACK 109 +// #define IDD_OPENURL 129 +// #define IDC_EDIT_URL 1000 +// #define ID_FILE_OPENFILE 32771 +// #define ID_FILE_OPENURL 32772 +// #define IDC_STATIC -1 +const IDM_EXIT = @as(u32, 105); + pub export fn wWinMain(hInstance: w.HINSTANCE, hPrevInstance: ?w.HINSTANCE, lpCmdLine: w.PWSTR, nCmdShow: w.INT) w.INT { _ = hPrevInstance; _ = lpCmdLine; @@ -338,14 +695,303 @@ pub export fn wWinMain(hInstance: w.HINSTANCE, hPrevInstance: ?w.HINSTANCE, lpCm return 0; } +// Globals +var uFormat: w.UINT = @bitReverse(c_uint, 0); +var fAuto: w.BOOL = w.TRUE; +var hwndNextViewer: ?w.HWND = null; + fn MainWndProc(hwnd: w.HWND, uMsg: u32, wParam: w.WPARAM, lParam: w.LPARAM) callconv(w.WINAPI) w.LRESULT { //APIENTRY { - _ = hwnd; - _ = uMsg; // uMsg is actually a pointer to the message - _ = wParam; - _ = lParam; + // static HWND hwndNextViewer; + // + // HDC hdc; + // HDC hdcMem; + // PAINTSTRUCT ps; + // LPPAINTSTRUCT lpps; + // RECT rc; + // LPRECT lprc; + // HGLOBAL hglb; + // LPSTR lpstr; + // HBITMAP hbm; + // HENHMETAFILE hemf; + // HWND hwndOwner; - _ = MessageBoxA(null, "all your codebase are belong to us", "title", 0); - std.time.sleep(2 * std.time.ns_per_s); + switch (uMsg) { + WM_PAINT => { + var ps: PAINTSTRUCT = .{ + .hdc = null, + .fErase = 0, + .rcPaint = .{ + .left = 0, + .top = 0, + .right = 0, + .bottom = 0, + }, + .fRestore = 0, + .fIncUpdate = 0, + .rgbReserved = [_]u8{0} ** 32, + }; + var rc: RECT = .{ + .left = 0, + .top = 0, + .right = 0, + .bottom = 0, + }; + var hdc = BeginPaint(hwnd, &ps); + // Branch depending on the clipboard format. + switch (uFormat) { + @enumToInt(CF_OWNERDISPLAY) => { + // hwndOwner = GetClipboardOwner(); + // hglb = GlobalAlloc(GMEM_MOVEABLE, + // sizeof(PAINTSTRUCT)); + // lpps = GlobalLock(hglb); + // memcpy(lpps, &ps, sizeof(PAINTSTRUCT)); + // GlobalUnlock(hglb); + // + // SendMessage(hwndOwner, WM_PAINTCLIPBOARD, + // (WPARAM) hwnd, (LPARAM) hglb); + // + // GlobalFree(hglb); + }, + + @enumToInt(CF_BITMAP) => { + // hdcMem = CreateCompatibleDC(hdc); + // if (hdcMem != NULL) + // { + // if (OpenClipboard(hwnd)) + // { + // hbm = (HBITMAP) + // GetClipboardData(uFormat); + // SelectObject(hdcMem, hbm); + // GetClientRect(hwnd, &rc); + // + // BitBlt(hdc, 0, 0, rc.right, rc.bottom, + // hdcMem, 0, 0, SRCCOPY); + // CloseClipboard(); + // } + // DeleteDC(hdcMem); + // } + }, + @enumToInt(CF_TEXT) => { + if (OpenClipboard(hwnd) > 0) { + var hglb = GetClipboardData(uFormat).?; + const hnd = @intCast(isize, @ptrToInt(hglb)); + var lpstr = GlobalLock(hnd).?; + + _ = GetClientRect(hwnd, &rc); + _ = DrawTextA(hdc, @ptrCast([*:0]const u8, lpstr), -1, &rc, DT_LEFT); + + _ = GlobalUnlock(hnd); + _ = CloseClipboard(); + } + }, + + @enumToInt(CF_ENHMETAFILE) => { + // if (OpenClipboard(hwnd)) + // { + // hemf = GetClipboardData(uFormat); + // GetClientRect(hwnd, &rc); + // PlayEnhMetaFile(hdc, hemf, &rc); + // CloseClipboard(); + // } + }, + 0 => { + _ = GetClientRect(hwnd, &rc); + _ = DrawTextA( + hdc, + "The clipboard is empty.", + -1, + &rc, + @intToEnum(DRAW_TEXT_FORMAT, @enumToInt(DT_CENTER) | @enumToInt(DT_SINGLELINE) | + @enumToInt(DT_VCENTER)), + ); + }, + + else => { + _ = GetClientRect(hwnd, &rc); + _ = DrawTextA( + hdc, + "Unable to display format.", + -1, + &rc, + @intToEnum(DRAW_TEXT_FORMAT, @enumToInt(DT_CENTER) | @enumToInt(DT_SINGLELINE) | + @enumToInt(DT_VCENTER)), + ); + }, + } + _ = EndPaint(hwnd, &ps); + }, + WM_SIZE => { + if (uFormat == @enumToInt(CF_OWNERDISPLAY)) { + var hwndOwner = GetClipboardOwner().?; + var hglb = GlobalAlloc(GMEM_MOVEABLE, @sizeOf(RECT)); + defer _ = GlobalFree(hglb); + var lprc = GlobalLock(hglb); + _ = GetClientRect(hwnd, @ptrCast(?*RECT, @alignCast(@alignOf(?*RECT), lprc))); + _ = GlobalUnlock(hglb); + + // hwnd and hglb are just casts + _ = SendMessageA(hwndOwner, WM_SIZECLIPBOARD, @ptrToInt(hwnd), hglb); + } + }, + WM_CREATE => { + + // Add the window to the clipboard viewer chain. + + hwndNextViewer = SetClipboardViewer(hwnd); + }, + + WM_CHANGECBCHAIN => { + + // If the next window is closing, repair the chain. + + // if ((HWND) wParam == hwndNextViewer) + if (wParam == @ptrToInt(hwndNextViewer.?)) { + hwndNextViewer = @intToPtr(w.HWND, @intCast(usize, lParam)); // just a cast + + // Otherwise, pass the message to the next link. + + } else if (hwndNextViewer) |hnv| { + _ = SendMessageA(hnv, uMsg, wParam, lParam); + } + }, + + WM_DESTROY => { + _ = ChangeClipboardChain(hwnd, hwndNextViewer); + PostQuitMessage(0); + }, + + WM_DRAWCLIPBOARD => { // clipboard contents changed. + + // Update the window by using Auto clipboard format. + + SetAutoView(hwnd); + + // Pass the message to the next window in clipboard + // viewer chain. + + _ = SendMessageA(hwndNextViewer, uMsg, wParam, lParam); + }, + + WM_INITMENUPOPUP => { + // if (!HIWORD(lParam)) + // if (lParam > 0xffffffff) + // InitMenu(hwnd, wParam); // (HMENU) wParam); + }, + + WM_COMMAND => { + // switch (LOWORD(wParam)) { + switch (wParam & 0xffffffff) { + IDM_EXIT => _ = DestroyWindow(hwnd), + // IDM_AUTO => SetAutoView(hwnd), + else => { + fAuto = w.FALSE; + uFormat = @intCast(w.UINT, wParam & 0xffffffff); // LOWORD(wParam); + _ = InvalidateRect(hwnd, null, w.TRUE); + }, + } + }, + else => return DefWindowProcA(hwnd, uMsg, wParam, lParam), + } return 0; } +var auPriorityList: [4]u32 = .{ + @enumToInt(CF_OWNERDISPLAY), + @enumToInt(CF_TEXT), + @enumToInt(CF_ENHMETAFILE), + @enumToInt(CF_BITMAP), +}; + +fn SetAutoView(hwnd: w.HWND) callconv(w.WINAPI) void { + var runtime_zero: usize = 0; + var auPriorityListSlice = auPriorityList[runtime_zero..auPriorityList.len]; + uFormat = @intCast(c_uint, GetPriorityClipboardFormat(auPriorityListSlice.ptr, 4)); + fAuto = w.TRUE; + + _ = InvalidateRect(hwnd, null, w.TRUE); + _ = UpdateWindow(hwnd); +} + +// fn InitMenu(hwnd: w.HWND, hmenu: w.HMENU) callconv(w.WINAPI) void { +// // UINT uFormat; +// // char szFormatName[80]; +// // LPCSTR lpFormatName; +// // UINT fuFlags; +// // UINT idMenuItem; +// +// // If a menu is not the display menu, no initialization is necessary. +// +// if (GetMenuItemID(hmenu, 0) != IDM_AUTO) +// return; +// +// // Delete all menu items except the first. +// +// while (GetMenuItemCount(hmenu) > 1) +// DeleteMenu(hmenu, 1, MF_BYPOSITION); +// +// // Check or uncheck the Auto menu item. +// +// if (fAuto) { +// fuFlags = MF_BYCOMMAND | MF_CHECKED; +// } else fuFlags = MF_BYCOMMAND | MF_UNCHECKED; +// +// CheckMenuItem(hmenu, IDM_AUTO, fuFlags); +// +// // If there are no clipboard formats, return. +// +// if (CountClipboardFormats() == 0) +// return; +// +// // Open the clipboard. +// +// if (!OpenClipboard(hwnd)) +// return; +// +// // Add a separator and then a menu item for each format. +// +// AppendMenu(hmenu, MF_SEPARATOR, 0, null); +// var uFormat = EnumClipboardFormats(0); +// +// while (uFormat) { +// // Call an application-defined function to get the name +// // of the clipboard format. +// +// var lpFormatName = GetPredefinedClipboardFormatName(uFormat); +// +// // For registered formats, get the registered name. +// +// if (lpFormatName == null) { +// +// // Note that, if the format name is larger than the +// // buffer, it is truncated. +// if (GetClipboardFormatName(uFormat, szFormatName, @sizeOf(szFormatName))) { +// lpFormatName = szFormatName; +// } else lpFormatName = "(unknown)"; +// } +// +// // Add a menu item for the format. For displayable +// // formats, use the format ID for the menu ID. +// +// if (IsDisplayableFormat(uFormat)) { +// fuFlags = MF_STRING; +// idMenuItem = uFormat; +// } else { +// fuFlags = MF_STRING | MF_GRAYED; +// idMenuItem = 0; +// } +// AppendMenu(hmenu, fuFlags, idMenuItem, lpFormatName); +// +// uFormat = EnumClipboardFormats(uFormat); +// } +// CloseClipboard(); +// } +// +// fn IsDisplayableFormat(ufmt: w.UINT) callconv(w.WINAPI) w.BOOL { +// switch (ufmt) { +// CF_OWNERDISPLAY => return true, +// CF_TEXT => return true, +// CF_ENHMETAFILE => return true, +// CF_BITMAP => return true, +// else => return false, +// } +// }