//go:build windows package w32 import ( "fmt" "github.com/samber/lo" "log" "strconv" "syscall" "unsafe" ) const ( GCLP_HBRBACKGROUND int32 = -10 ) func ExtendFrameIntoClientArea(hwnd uintptr, extend bool) { // -1: Adds the default frame styling (aero shadow and e.g. rounded corners on Windows 11) // Also shows the caption buttons if transparent ant translucent but they don't work. // 0: Adds the default frame styling but no aero shadow, does not show the caption buttons. // 1: Adds the default frame styling (aero shadow and e.g. rounded corners on Windows 11) but no caption buttons // are shown if transparent ant translucent. var margins MARGINS if extend { margins = MARGINS{1, 1, 1, 1} // Only extend 1 pixel to have the default frame styling but no caption buttons } if err := dwmExtendFrameIntoClientArea(hwnd, &margins); err != nil { log.Fatal(fmt.Errorf("DwmExtendFrameIntoClientArea failed: %s", err)) } } func IsVisible(hwnd uintptr) bool { ret, _, _ := procIsWindowVisible.Call(hwnd) return ret != 0 } func IsWindowFullScreen(hwnd uintptr) bool { wRect := GetWindowRect(hwnd) m := MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY) var mi MONITORINFO mi.CbSize = uint32(unsafe.Sizeof(mi)) if !GetMonitorInfo(m, &mi) { return false } return wRect.Left == mi.RcMonitor.Left && wRect.Top == mi.RcMonitor.Top && wRect.Right == mi.RcMonitor.Right && wRect.Bottom == mi.RcMonitor.Bottom } func IsWindowMaximised(hwnd uintptr) bool { style := uint32(getWindowLong(hwnd, GWL_STYLE)) return style&WS_MAXIMIZE != 0 } func IsWindowMinimised(hwnd uintptr) bool { style := uint32(getWindowLong(hwnd, GWL_STYLE)) return style&WS_MINIMIZE != 0 } func RestoreWindow(hwnd uintptr) { showWindow(hwnd, SW_RESTORE) } func ShowWindowMaximised(hwnd uintptr) { showWindow(hwnd, SW_MAXIMIZE) } func ShowWindowMinimised(hwnd uintptr) { showWindow(hwnd, SW_MINIMIZE) } func SetBackgroundColour(hwnd uintptr, r, g, b uint8) { col := uint32(r) | uint32(g)<<8 | uint32(b)<<16 hbrush, _, _ := procCreateSolidBrush.Call(uintptr(col)) setClassLongPtr(hwnd, GCLP_HBRBACKGROUND, hbrush) } func IsWindowNormal(hwnd uintptr) bool { return !IsWindowMaximised(hwnd) && !IsWindowMinimised(hwnd) && !IsWindowFullScreen(hwnd) } func setClassLongPtr(hwnd uintptr, param int32, val uintptr) bool { proc := procSetClassLongPtr if strconv.IntSize == 32 { /* https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setclasslongptrw Note: To write code that is compatible with both 32-bit and 64-bit Windows, use SetClassLongPtr. When compiling for 32-bit Windows, SetClassLongPtr is defined as a call to the SetClassLong function => We have to do this dynamically when directly calling the DLL procedures */ proc = procSetClassLong } ret, _, _ := proc.Call( hwnd, uintptr(param), val, ) return ret != 0 } func getWindowLong(hwnd uintptr, index int) int32 { ret, _, _ := procGetWindowLong.Call( hwnd, uintptr(index)) return int32(ret) } func showWindow(hwnd uintptr, cmdshow int) bool { ret, _, _ := procShowWindow.Call( hwnd, uintptr(cmdshow)) return ret != 0 } func MustStringToUTF16Ptr(input string) *uint16 { return lo.Must(syscall.UTF16PtrFromString(input)) } func MustStringToUTF16uintptr(input string) uintptr { ret := lo.Must(syscall.UTF16PtrFromString(input)) return uintptr(unsafe.Pointer(ret)) } func MustStringToUTF16(input string) []uint16 { return lo.Must(syscall.UTF16FromString(input)) } func CenterWindow(hwnd HWND) { windowInfo := getWindowInfo(hwnd) frameless := windowInfo.IsPopup() info := GetMonitorInfoForWindow(hwnd) workRect := info.RcWork screenMiddleW := workRect.Left + (workRect.Right-workRect.Left)/2 screenMiddleH := workRect.Top + (workRect.Bottom-workRect.Top)/2 var winRect *RECT if !frameless { winRect = GetWindowRect(hwnd) } else { winRect = GetClientRect(hwnd) } winWidth := winRect.Right - winRect.Left winHeight := winRect.Bottom - winRect.Top windowX := screenMiddleW - (winWidth / 2) windowY := screenMiddleH - (winHeight / 2) SetWindowPos(hwnd, HWND_TOP, int(windowX), int(windowY), int(winWidth), int(winHeight), SWP_NOSIZE) } func getWindowInfo(hwnd HWND) *WINDOWINFO { var info WINDOWINFO info.CbSize = uint32(unsafe.Sizeof(info)) GetWindowInfo(hwnd, &info) return &info } func GetMonitorInfoForWindow(hwnd HWND) *MONITORINFO { currentMonitor := MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST) var info MONITORINFO info.CbSize = uint32(unsafe.Sizeof(info)) GetMonitorInfo(currentMonitor, &info) return &info }