diff --git a/app/internal/windows/windows.go b/app/internal/windows/windows.go index a8afa38e..b23392f3 100644 --- a/app/internal/windows/windows.go +++ b/app/internal/windows/windows.go @@ -108,6 +108,74 @@ type MonitorInfo struct { Flags uint32 } +type POINTER_INPUT_TYPE int32 + +const ( + PT_POINTER POINTER_INPUT_TYPE = 1 + PT_TOUCH POINTER_INPUT_TYPE = 2 + PT_PEN POINTER_INPUT_TYPE = 3 + PT_MOUSE POINTER_INPUT_TYPE = 4 + PT_TOUCHPAD POINTER_INPUT_TYPE = 5 +) + +type POINTER_INFO_POINTER_FLAGS int32 + +const ( + POINTER_FLAG_NEW POINTER_INFO_POINTER_FLAGS = 0x00000001 + POINTER_FLAG_INRANGE POINTER_INFO_POINTER_FLAGS = 0x00000002 + POINTER_FLAG_INCONTACT POINTER_INFO_POINTER_FLAGS = 0x00000004 + POINTER_FLAG_FIRSTBUTTON POINTER_INFO_POINTER_FLAGS = 0x00000010 + POINTER_FLAG_SECONDBUTTON POINTER_INFO_POINTER_FLAGS = 0x00000020 + POINTER_FLAG_THIRDBUTTON POINTER_INFO_POINTER_FLAGS = 0x00000040 + POINTER_FLAG_FOURTHBUTTON POINTER_INFO_POINTER_FLAGS = 0x00000080 + POINTER_FLAG_FIFTHBUTTON POINTER_INFO_POINTER_FLAGS = 0x00000100 + POINTER_FLAG_PRIMARY POINTER_INFO_POINTER_FLAGS = 0x00002000 + POINTER_FLAG_CONFIDENCE POINTER_INFO_POINTER_FLAGS = 0x00004000 + POINTER_FLAG_CANCELED POINTER_INFO_POINTER_FLAGS = 0x00008000 + POINTER_FLAG_DOWN POINTER_INFO_POINTER_FLAGS = 0x00010000 + POINTER_FLAG_UPDATE POINTER_INFO_POINTER_FLAGS = 0x00020000 + POINTER_FLAG_UP POINTER_INFO_POINTER_FLAGS = 0x00040000 + POINTER_FLAG_WHEEL POINTER_INFO_POINTER_FLAGS = 0x00080000 + POINTER_FLAG_HWHEEL POINTER_INFO_POINTER_FLAGS = 0x00100000 + POINTER_FLAG_CAPTURECHANGED POINTER_INFO_POINTER_FLAGS = 0x00200000 + POINTER_FLAG_HASTRANSFORM POINTER_INFO_POINTER_FLAGS = 0x00400000 +) + +type POINTER_BUTTON_CHANGE_TYPE int32 + +const ( + POINTER_CHANGE_NONE POINTER_BUTTON_CHANGE_TYPE = 0 + POINTER_CHANGE_FIRSTBUTTON_DOWN POINTER_BUTTON_CHANGE_TYPE = 1 + POINTER_CHANGE_FIRSTBUTTON_UP POINTER_BUTTON_CHANGE_TYPE = 2 + POINTER_CHANGE_SECONDBUTTON_DOWN POINTER_BUTTON_CHANGE_TYPE = 3 + POINTER_CHANGE_SECONDBUTTON_UP POINTER_BUTTON_CHANGE_TYPE = 4 + POINTER_CHANGE_THIRDBUTTON_DOWN POINTER_BUTTON_CHANGE_TYPE = 5 + POINTER_CHANGE_THIRDBUTTON_UP POINTER_BUTTON_CHANGE_TYPE = 6 + POINTER_CHANGE_FOURTHBUTTON_DOWN POINTER_BUTTON_CHANGE_TYPE = 7 + POINTER_CHANGE_FOURTHBUTTON_UP POINTER_BUTTON_CHANGE_TYPE = 8 + POINTER_CHANGE_FIFTHBUTTON_DOWN POINTER_BUTTON_CHANGE_TYPE = 9 + POINTER_CHANGE_FIFTHBUTTON_UP POINTER_BUTTON_CHANGE_TYPE = 10 +) + +type PointerInfo struct { + PointerType POINTER_INPUT_TYPE + PointerId uint32 + FrameId uint32 + PointerFlags POINTER_INFO_POINTER_FLAGS + SourceDevice syscall.Handle + HwndTarget syscall.Handle + PtPixelLocation Point + PtHimetricLocation Point + PtPixelLocationRaw Point + PtHimetricLocationRaw Point + DwTime uint32 + HistoryCount uint32 + InputData int32 + DwKeyStates uint32 + PerformanceCount uint64 + ButtonChangeType POINTER_BUTTON_CHANGE_TYPE +} + const ( TRUE = 1 @@ -244,45 +312,51 @@ const ( UNICODE_NOCHAR = 65535 - WM_CANCELMODE = 0x001F - WM_CHAR = 0x0102 - WM_CLOSE = 0x0010 - WM_CREATE = 0x0001 - WM_DPICHANGED = 0x02E0 - WM_DESTROY = 0x0002 - WM_ERASEBKGND = 0x0014 - WM_GETMINMAXINFO = 0x0024 - WM_IME_COMPOSITION = 0x010F - WM_IME_ENDCOMPOSITION = 0x010E - WM_IME_STARTCOMPOSITION = 0x010D - WM_KEYDOWN = 0x0100 - WM_KEYUP = 0x0101 - WM_KILLFOCUS = 0x0008 - WM_LBUTTONDOWN = 0x0201 - WM_LBUTTONUP = 0x0202 - WM_MBUTTONDOWN = 0x0207 - WM_MBUTTONUP = 0x0208 - WM_MOUSEMOVE = 0x0200 - WM_MOUSEWHEEL = 0x020A - WM_MOUSEHWHEEL = 0x020E - WM_NCACTIVATE = 0x0086 - WM_NCHITTEST = 0x0084 - WM_NCCALCSIZE = 0x0083 - WM_PAINT = 0x000F - WM_QUIT = 0x0012 - WM_SETCURSOR = 0x0020 - WM_SETFOCUS = 0x0007 - WM_SHOWWINDOW = 0x0018 - WM_SIZE = 0x0005 - WM_STYLECHANGED = 0x007D - WM_SYSKEYDOWN = 0x0104 - WM_SYSKEYUP = 0x0105 - WM_RBUTTONDOWN = 0x0204 - WM_RBUTTONUP = 0x0205 - WM_TIMER = 0x0113 - WM_UNICHAR = 0x0109 - WM_USER = 0x0400 - WM_WINDOWPOSCHANGED = 0x0047 + WM_CANCELMODE = 0x001F + WM_CHAR = 0x0102 + WM_CLOSE = 0x0010 + WM_CREATE = 0x0001 + WM_DPICHANGED = 0x02E0 + WM_DESTROY = 0x0002 + WM_ERASEBKGND = 0x0014 + WM_GETMINMAXINFO = 0x0024 + WM_IME_COMPOSITION = 0x010F + WM_IME_ENDCOMPOSITION = 0x010E + WM_IME_STARTCOMPOSITION = 0x010D + WM_KEYDOWN = 0x0100 + WM_KEYUP = 0x0101 + WM_KILLFOCUS = 0x0008 + WM_LBUTTONDOWN = 0x0201 + WM_LBUTTONUP = 0x0202 + WM_MBUTTONDOWN = 0x0207 + WM_MBUTTONUP = 0x0208 + WM_MOUSEMOVE = 0x0200 + WM_MOUSEWHEEL = 0x020A + WM_MOUSEHWHEEL = 0x020E + WM_NCACTIVATE = 0x0086 + WM_NCHITTEST = 0x0084 + WM_NCCALCSIZE = 0x0083 + WM_PAINT = 0x000F + WM_POINTERCAPTURECHANGED = 0x024C + WM_POINTERDOWN = 0x0246 + WM_POINTERUP = 0x0247 + WM_POINTERUPDATE = 0x0245 + WM_POINTERWHEEL = 0x024E + WM_POINTERHWHEEL = 0x024F + WM_QUIT = 0x0012 + WM_RBUTTONDOWN = 0x0204 + WM_RBUTTONUP = 0x0205 + WM_SETCURSOR = 0x0020 + WM_SETFOCUS = 0x0007 + WM_SHOWWINDOW = 0x0018 + WM_SIZE = 0x0005 + WM_STYLECHANGED = 0x007D + WM_SYSKEYDOWN = 0x0104 + WM_SYSKEYUP = 0x0105 + WM_TIMER = 0x0113 + WM_UNICHAR = 0x0109 + WM_USER = 0x0400 + WM_WINDOWPOSCHANGED = 0x0047 WS_CLIPCHILDREN = 0x02000000 WS_CLIPSIBLINGS = 0x04000000 @@ -346,6 +420,7 @@ var ( _DestroyWindow = user32.NewProc("DestroyWindow") _DispatchMessage = user32.NewProc("DispatchMessageW") _EmptyClipboard = user32.NewProc("EmptyClipboard") + _EnableMouseInPointer = user32.NewProc("EnableMouseInPointer") _GetWindowRect = user32.NewProc("GetWindowRect") _GetClientRect = user32.NewProc("GetClientRect") _GetClipboardData = user32.NewProc("GetClipboardData") @@ -355,6 +430,7 @@ var ( _GetMessage = user32.NewProc("GetMessageW") _GetMessageTime = user32.NewProc("GetMessageTime") _GetMonitorInfo = user32.NewProc("GetMonitorInfoW") + _GetPointerInfo = user32.NewProc("GetPointerInfo") _GetSystemMetrics = user32.NewProc("GetSystemMetrics") _GetWindowLong = user32.NewProc("GetWindowLongPtrW") _GetWindowLong32 = user32.NewProc("GetWindowLongW") @@ -372,6 +448,7 @@ var ( _PostQuitMessage = user32.NewProc("PostQuitMessage") _ReleaseCapture = user32.NewProc("ReleaseCapture") _RegisterClassExW = user32.NewProc("RegisterClassExW") + _RegisterTouchWindow = user32.NewProc("RegisterTouchWindow") _ReleaseDC = user32.NewProc("ReleaseDC") _ScreenToClient = user32.NewProc("ScreenToClient") _ShowWindow = user32.NewProc("ShowWindow") @@ -445,6 +522,31 @@ func CreateWindowEx(dwExStyle uint32, lpClassName uint16, lpWindowName string, d return syscall.Handle(hwnd), nil } +func GetPointerInfo(pointerId uint32) (PointerInfo, error) { + var info PointerInfo + r1, _, err := _GetPointerInfo.Call(uintptr(pointerId), uintptr(unsafe.Pointer(&info))) + if r1 == 0 { + return PointerInfo{}, fmt.Errorf("GetPointerInfo failed: %v", err) + } + return info, nil +} + +func RegisterTouchWindow(hwnd syscall.Handle, flags uint32) error { + r1, _, err := _RegisterTouchWindow.Call(uintptr(hwnd), uintptr(flags)) + if r1 == 0 { + return fmt.Errorf("RegisterTouchWindow failed: %v", err) + } + return nil +} + +func EnableMouseInPointer(enable uint) error { + r1, _, err := _EnableMouseInPointer.Call(uintptr(enable)) + if r1 == 0 { + return fmt.Errorf("EnableMouseInPointer failed: %v", err) + } + return nil +} + func DefWindowProc(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) uintptr { r, _, _ := _DefWindowProc.Call(uintptr(hwnd), uintptr(msg), wparam, lparam) return r diff --git a/app/os_windows.go b/app/os_windows.go index 239c7443..f601d910 100644 --- a/app/os_windows.go +++ b/app/os_windows.go @@ -36,10 +36,9 @@ type Win32ViewEvent struct { } type window struct { - hwnd syscall.Handle - hdc syscall.Handle - w *callbacks - pointerBtns pointer.Buttons + hwnd syscall.Handle + hdc syscall.Handle + w *callbacks // cursorIn tracks whether the cursor was inside the window according // to the most recent WM_SETCURSOR. @@ -175,6 +174,12 @@ func (w *window) init() error { if err != nil { return err } + if err := windows.RegisterTouchWindow(hwnd, 0); err != nil { + return err + } + if err := windows.EnableMouseInPointer(1); err != nil { + return err + } w.hdc, err = windows.GetDC(hwnd) if err != nil { windows.DestroyWindow(hwnd) @@ -265,18 +270,32 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr return 0 } } - case windows.WM_LBUTTONDOWN: - w.pointerButton(pointer.ButtonPrimary, true, lParam, getModifiers()) - case windows.WM_LBUTTONUP: - w.pointerButton(pointer.ButtonPrimary, false, lParam, getModifiers()) - case windows.WM_RBUTTONDOWN: - w.pointerButton(pointer.ButtonSecondary, true, lParam, getModifiers()) - case windows.WM_RBUTTONUP: - w.pointerButton(pointer.ButtonSecondary, false, lParam, getModifiers()) - case windows.WM_MBUTTONDOWN: - w.pointerButton(pointer.ButtonTertiary, true, lParam, getModifiers()) - case windows.WM_MBUTTONUP: - w.pointerButton(pointer.ButtonTertiary, false, lParam, getModifiers()) + case windows.WM_POINTERDOWN, windows.WM_POINTERUP, windows.WM_POINTERUPDATE, windows.WM_POINTERCAPTURECHANGED: + pid := getPointerIDwParam(wParam) + pi, err := windows.GetPointerInfo(uint32(pid)) + if err != nil { + panic(err) + } + switch msg { + case windows.WM_POINTERDOWN: + windows.SetCapture(w.hwnd) + case windows.WM_POINTERUP: + windows.ReleaseCapture() + } + + kind := pointer.Move + switch pi.ButtonChangeType { + case windows.POINTER_CHANGE_FIRSTBUTTON_DOWN, windows.POINTER_CHANGE_SECONDBUTTON_DOWN, windows.POINTER_CHANGE_THIRDBUTTON_DOWN, windows.POINTER_CHANGE_FOURTHBUTTON_DOWN, windows.POINTER_CHANGE_FIFTHBUTTON_DOWN: + kind = pointer.Press + case windows.POINTER_CHANGE_FIRSTBUTTON_UP, windows.POINTER_CHANGE_SECONDBUTTON_UP, windows.POINTER_CHANGE_THIRDBUTTON_UP, windows.POINTER_CHANGE_FOURTHBUTTON_UP, windows.POINTER_CHANGE_FIFTHBUTTON_UP: + kind = pointer.Release + } + + if (pi.PointerFlags&windows.POINTER_FLAG_CANCELED != 0) || (msg == windows.WM_POINTERCAPTURECHANGED) { + kind = pointer.Cancel + } + + w.pointerUpdate(pi, pid, kind, lParam) case windows.WM_CANCELMODE: w.ProcessEvent(pointer.Event{ Kind: pointer.Cancel, @@ -296,20 +315,9 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr 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)} - w.ProcessEvent(pointer.Event{ - Kind: pointer.Move, - Source: pointer.Mouse, - Position: p, - Buttons: w.pointerBtns, - Time: windows.GetMessageTime(), - Modifiers: getModifiers(), - }) - case windows.WM_MOUSEWHEEL: + case windows.WM_POINTERWHEEL: w.scrollEvent(wParam, lParam, false, getModifiers()) - case windows.WM_MOUSEHWHEEL: + case windows.WM_POINTERHWHEEL: w.scrollEvent(wParam, lParam, true, getModifiers()) case windows.WM_DESTROY: w.ProcessEvent(Win32ViewEvent{}) @@ -499,34 +507,28 @@ func (w *window) hitTest(x, y int) uintptr { return windows.HTCLIENT } -func (w *window) pointerButton(btn pointer.Buttons, press bool, lParam uintptr, kmods key.Modifiers) { +func (w *window) pointerUpdate(pi windows.PointerInfo, pid pointer.ID, kind pointer.Kind, lParam uintptr) { if !w.config.Focused { windows.SetFocus(w.hwnd) } - var kind pointer.Kind - if press { - kind = pointer.Press - if w.pointerBtns == 0 { - windows.SetCapture(w.hwnd) - } - w.pointerBtns |= btn - } else { - kind = pointer.Release - w.pointerBtns &^= btn - if w.pointerBtns == 0 { - windows.ReleaseCapture() - } + src := pointer.Touch + if pi.PointerType == windows.PT_MOUSE { + src = pointer.Mouse } + x, y := coordsFromlParam(lParam) - p := f32.Point{X: float32(x), Y: float32(y)} + np := windows.Point{X: int32(x), Y: int32(y)} + windows.ScreenToClient(w.hwnd, &np) + p := f32.Point{X: float32(np.X), Y: float32(np.Y)} w.ProcessEvent(pointer.Event{ Kind: kind, - Source: pointer.Mouse, + Source: src, Position: p, - Buttons: w.pointerBtns, + PointerID: pid, + Buttons: getPointerButtons(pi), Time: windows.GetMessageTime(), - Modifiers: kmods, + Modifiers: getModifiers(), }) } @@ -537,6 +539,12 @@ func coordsFromlParam(lParam uintptr) (int, int) { } func (w *window) scrollEvent(wParam, lParam uintptr, horizontal bool, kmods key.Modifiers) { + pid := getPointerIDwParam(wParam) + pi, err := windows.GetPointerInfo(uint32(pid)) + if err != nil { + panic(err) + } + x, y := coordsFromlParam(lParam) // The WM_MOUSEWHEEL coordinates are in screen coordinates, in contrast // to other mouse events. @@ -559,7 +567,7 @@ func (w *window) scrollEvent(wParam, lParam uintptr, horizontal bool, kmods key. Kind: pointer.Scroll, Source: pointer.Mouse, Position: p, - Buttons: w.pointerBtns, + Buttons: getPointerButtons(pi), Scroll: sp, Modifiers: kmods, Time: windows.GetMessageTime(), @@ -1005,3 +1013,45 @@ func (Win32ViewEvent) ImplementsEvent() {} func (w Win32ViewEvent) Valid() bool { return w != (Win32ViewEvent{}) } + +// LOWORD (minwindef.h) +func loWord(val uint32) uint16 { + return uint16(val & 0xFFFF) +} + +// GET_POINTERID_WPARAM (winuser.h) +func getPointerIDwParam(wParam uintptr) pointer.ID { + return pointer.ID(loWord(uint32(wParam))) +} + +func getPointerButtons(pi windows.PointerInfo) pointer.Buttons { + var btns pointer.Buttons + + if pi.PointerFlags&windows.POINTER_FLAG_FIRSTBUTTON != 0 { + btns |= pointer.ButtonPrimary + } else { + btns &^= pointer.ButtonPrimary + } + if pi.PointerFlags&windows.POINTER_FLAG_SECONDBUTTON != 0 { + btns |= pointer.ButtonSecondary + } else { + btns &^= pointer.ButtonSecondary + } + if pi.PointerFlags&windows.POINTER_FLAG_THIRDBUTTON != 0 { + btns |= pointer.ButtonTertiary + } else { + btns &^= pointer.ButtonTertiary + } + if pi.PointerFlags&windows.POINTER_FLAG_FOURTHBUTTON != 0 { + btns |= pointer.ButtonQuaternary + } else { + btns &^= pointer.ButtonQuaternary + } + if pi.PointerFlags&windows.POINTER_FLAG_FIFTHBUTTON != 0 { + btns |= pointer.ButtonQuinary + } else { + btns &^= pointer.ButtonQuinary + } + + return btns +}