diff --git a/app/internal/windows/windows.go b/app/internal/windows/windows.go index 4ddb62ed..5b4bcb5d 100644 --- a/app/internal/windows/windows.go +++ b/app/internal/windows/windows.go @@ -113,7 +113,15 @@ const ( HWND_TOPMOST = ^(uint32(1) - 1) // -1 - HTCLIENT = 1 + HTCLIENT = 1 + HTLEFT = 10 + HTRIGHT = 11 + HTTOP = 12 + HTTOPLEFT = 13 + HTTOPRIGHT = 14 + HTBOTTOM = 15 + HTBOTTOMLEFT = 16 + HTBOTTOMRIGHT = 17 IDC_APPSTARTING = 32650 // Standard arrow and small hourglass IDC_ARROW = 32512 // Standard arrow @@ -146,13 +154,16 @@ const ( SCS_SETSTR = GCS_COMPREADSTR | GCS_COMPSTR + SM_CXSIZEFRAME = 32 + SM_CYSIZEFRAME = 33 + SW_SHOWDEFAULT = 10 SW_SHOWMINIMIZED = 2 SW_SHOWMAXIMIZED = 3 SW_SHOWNORMAL = 1 SW_SHOW = 5 - SWP_FRAMECHANGED = 0x0020 + SWP_FRAMECHANGED = 0x0020 SWP_NOMOVE = 0x0002 SWP_NOOWNERZORDER = 0x0200 SWP_NOSIZE = 0x0001 @@ -231,6 +242,7 @@ const ( WM_MOUSEMOVE = 0x0200 WM_MOUSEWHEEL = 0x020A WM_MOUSEHWHEEL = 0x020E + WM_NCHITTEST = 0x0084 WM_PAINT = 0x000F WM_QUIT = 0x0012 WM_SETCURSOR = 0x0020 @@ -316,6 +328,7 @@ var ( _GetMessage = user32.NewProc("GetMessageW") _GetMessageTime = user32.NewProc("GetMessageTime") _GetMonitorInfo = user32.NewProc("GetMonitorInfoW") + _GetSystemMetrics = user32.NewProc("GetSystemMetrics") _GetWindowLong = user32.NewProc("GetWindowLongPtrW") _GetWindowLong32 = user32.NewProc("GetWindowLongW") _GetWindowPlacement = user32.NewProc("GetWindowPlacement") @@ -499,6 +512,11 @@ func GetMessageTime() time.Duration { return time.Duration(r) * time.Millisecond } +func GetSystemMetrics(nIndex int) int { + r, _, _ := _GetSystemMetrics.Call(uintptr(nIndex)) + return int(r) +} + // GetWindowDPI returns the effective DPI of the window. func GetWindowDPI(hwnd syscall.Handle) int { // Check for GetDpiForWindow, introduced in Windows 10. diff --git a/app/os_windows.go b/app/os_windows.go index dbb57e73..60854a39 100644 --- a/app/os_windows.go +++ b/app/os_windows.go @@ -54,8 +54,9 @@ type window struct { animating bool - deltas winDeltas - config Config + deltas winDeltas + borderSize image.Point + config Config } const _WM_WAKEUP = windows.WM_USER + iota @@ -210,6 +211,11 @@ func (w *window) update() { } } w.config.Size = size + + w.borderSize = image.Pt( + windows.GetSystemMetrics(windows.SM_CXSIZEFRAME), + windows.GetSystemMetrics(windows.SM_CYSIZEFRAME), + ) w.w.Event(ConfigEvent{Config: w.config}) } @@ -279,6 +285,15 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr w.w.Event(key.FocusEvent{Focus: true}) case windows.WM_KILLFOCUS: w.w.Event(key.FocusEvent{Focus: false}) + case windows.WM_NCHITTEST: + if w.config.Decorated || w.config.Mode != Windowed { + // Let the system handle it. + break + } + x, y := coordsFromlParam(lParam) + np := windows.Point{X: int32(x), Y: int32(y)} + windows.ScreenToClient(w.hwnd, &np) + return w.hitTest(int(np.X), int(np.Y)) case windows.WM_MOUSEMOVE: x, y := coordsFromlParam(lParam) p := f32.Point{X: float32(x), Y: float32(y)} @@ -411,6 +426,37 @@ func getModifiers() key.Modifiers { return kmods } +// hitTest returns the non-client area hit by the point, needed to +// process WM_NCHITTEST. +func (w *window) hitTest(x, y int) uintptr { + top := y <= w.borderSize.Y + bottom := y >= w.config.Size.Y-w.borderSize.Y + left := x <= w.borderSize.X + right := x >= w.config.Size.X-w.borderSize.X + switch { + default: + fallthrough + case !top && !bottom && !left && !right: + return windows.HTCLIENT + case top && left: + return windows.HTTOPLEFT + case top && right: + return windows.HTTOPRIGHT + case bottom && left: + return windows.HTBOTTOMLEFT + case bottom && right: + return windows.HTBOTTOMRIGHT + case top: + return windows.HTTOP + case bottom: + return windows.HTBOTTOM + case left: + return windows.HTLEFT + case right: + return windows.HTRIGHT + } +} + func (w *window) pointerButton(btn pointer.Buttons, press bool, lParam uintptr, kmods key.Modifiers) { var typ pointer.Type if press {