diff --git a/app/internal/window/GioView.java b/app/internal/window/GioView.java index 521e82bc..82bc36f3 100644 --- a/app/internal/window/GioView.java +++ b/app/internal/window/GioView.java @@ -106,6 +106,7 @@ public class GioView extends SurfaceView implements Choreographer.FrameCallback event.getToolType(i), event.getHistoricalX(i, j), event.getHistoricalY(i, j), + event.getButtonState(), time); } } @@ -123,6 +124,7 @@ public class GioView extends SurfaceView implements Choreographer.FrameCallback event.getToolType(i), event.getX(i), event.getY(i), + event.getButtonState(), event.getEventTime()); } return true; @@ -214,7 +216,7 @@ public class GioView extends SurfaceView implements Choreographer.FrameCallback static private native void onConfigurationChanged(long handle); static private native void onWindowInsets(long handle, int top, int right, int bottom, int left); static private native void onLowMemory(); - static private native void onTouchEvent(long handle, int action, int pointerID, int tool, float x, float y, long time); + static private native void onTouchEvent(long handle, int action, int pointerID, int tool, float x, float y, int buttons, long time); static private native void onKeyEvent(long handle, int code, int character, long time); static private native void onFrameCallback(long handle, long nanos); static private native boolean onBack(long handle); diff --git a/app/internal/window/gl_macos.m b/app/internal/window/gl_macos.m index abdb406b..fdadae2b 100644 --- a/app/internal/window/gl_macos.m +++ b/app/internal/window/gl_macos.m @@ -18,7 +18,7 @@ static void handleMouse(NSView *view, NSEvent *event, int typ, CGFloat dx, CGFlo dx *= 10; dy *= 10; } - gio_onMouse((__bridge CFTypeRef)view, typ, p.x, p.y, dx, dy, [event timestamp]); + gio_onMouse((__bridge CFTypeRef)view, typ, [NSEvent pressedMouseButtons], p.x, p.y, dx, dy, [event timestamp]); } static CVReturn displayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) { @@ -79,6 +79,18 @@ CVDisplayLinkRef displayLink; - (void)mouseUp:(NSEvent *)event { handleMouse(self, event, GIO_MOUSE_UP, 0, 0); } +- (void)middleMouseDown:(NSEvent *)event { + handleMouse(self, event, GIO_MOUSE_DOWN, 0, 0); +} +- (void)middletMouseUp:(NSEvent *)event { + handleMouse(self, event, GIO_MOUSE_UP, 0, 0); +} +- (void)rightMouseDown:(NSEvent *)event { + handleMouse(self, event, GIO_MOUSE_DOWN, 0, 0); +} +- (void)rightMouseUp:(NSEvent *)event { + handleMouse(self, event, GIO_MOUSE_UP, 0, 0); +} - (void)mouseMoved:(NSEvent *)event { handleMouse(self, event, GIO_MOUSE_MOVE, 0, 0); } diff --git a/app/internal/window/os_android.c b/app/internal/window/os_android.c index f8d25b6e..5150ab65 100644 --- a/app/internal/window/os_android.c +++ b/app/internal/window/os_android.c @@ -72,7 +72,7 @@ JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { }, { .name = "onTouchEvent", - .signature = "(JIIIFFJ)V", + .signature = "(JIIIFFIJ)V", .fnPtr = onTouchEvent }, { diff --git a/app/internal/window/os_android.go b/app/internal/window/os_android.go index b2062dae..5cce88d5 100644 --- a/app/internal/window/os_android.go +++ b/app/internal/window/os_android.go @@ -386,7 +386,7 @@ func onKeyEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, keyCode, r C.jint } //export onTouchEvent -func onTouchEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, action, pointerID, tool C.jint, x, y C.jfloat, t C.jlong) { +func onTouchEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, action, pointerID, tool C.jint, x, y C.jfloat, jbtns C.jint, t C.jlong) { w := views[handle] var typ pointer.Type switch action { @@ -402,6 +402,16 @@ func onTouchEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, action, pointer return } var src pointer.Source + var btns pointer.Buttons + if jbtns&C.AMOTION_EVENT_BUTTON_PRIMARY != 0 { + btns |= pointer.ButtonLeft + } + if jbtns&C.AMOTION_EVENT_BUTTON_SECONDARY != 0 { + btns |= pointer.ButtonRight + } + if jbtns&C.AMOTION_EVENT_BUTTON_TERTIARY != 0 { + btns |= pointer.ButtonMiddle + } switch tool { case C.AMOTION_EVENT_TOOL_TYPE_FINGER: src = pointer.Touch @@ -413,6 +423,7 @@ func onTouchEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, action, pointer w.callbacks.Event(pointer.Event{ Type: typ, Source: src, + Buttons: btns, PointerID: pointer.ID(pointerID), Time: time.Duration(t) * time.Millisecond, Position: f32.Point{X: float32(x), Y: float32(y)}, diff --git a/app/internal/window/os_js.go b/app/internal/window/os_js.go index a8900fc0..1644be4c 100644 --- a/app/internal/window/os_js.go +++ b/app/internal/window/os_js.go @@ -282,9 +282,21 @@ func (w *window) pointerEvent(typ pointer.Type, dx, dy float32, e js.Value) { Y: dy * scale, } t := time.Duration(e.Get("timeStamp").Int()) * time.Millisecond + jbtns := e.Get("buttons").Int() + var btns pointer.Buttons + if jbtns&1 != 0 { + btns |= pointer.ButtonLeft + } + if jbtns&2 != 0 { + btns |= pointer.ButtonRight + } + if jbtns&4 != 0 { + btns |= pointer.ButtonMiddle + } w.w.Event(pointer.Event{ Type: typ, Source: pointer.Mouse, + Buttons: btns, Position: pos, Scroll: scroll, Time: t, diff --git a/app/internal/window/os_macos.go b/app/internal/window/os_macos.go index ed62ab57..018bf642 100644 --- a/app/internal/window/os_macos.go +++ b/app/internal/window/os_macos.go @@ -151,7 +151,7 @@ func gio_onText(view C.CFTypeRef, cstr *C.char) { } //export gio_onMouse -func gio_onMouse(view C.CFTypeRef, cdir C.int, x, y, dx, dy C.CGFloat, ti C.double) { +func gio_onMouse(view C.CFTypeRef, cdir C.int, cbtns C.NSUInteger, x, y, dx, dy C.CGFloat, ti C.double) { var typ pointer.Type switch cdir { case C.GIO_MOUSE_MOVE: @@ -163,6 +163,16 @@ func gio_onMouse(view C.CFTypeRef, cdir C.int, x, y, dx, dy C.CGFloat, ti C.doub default: panic("invalid direction") } + var btns pointer.Buttons + if cbtns&(1<<0) != 0 { + btns |= pointer.ButtonLeft + } + if cbtns&(1<<1) != 0 { + btns |= pointer.ButtonRight + } + if cbtns&(1<<2) != 0 { + btns |= pointer.ButtonMiddle + } t := time.Duration(float64(ti)*float64(time.Second) + .5) viewDo(view, func(views viewMap, view C.CFTypeRef) { w := views[view] @@ -172,6 +182,7 @@ func gio_onMouse(view C.CFTypeRef, cdir C.int, x, y, dx, dy C.CGFloat, ti C.doub Type: typ, Source: pointer.Mouse, Time: t, + Buttons: btns, Position: f32.Point{X: x, Y: y}, Scroll: f32.Point{X: dx, Y: dy}, }) diff --git a/app/internal/window/os_wayland.go b/app/internal/window/os_wayland.go index ed362464..1ed9d43d 100644 --- a/app/internal/window/os_wayland.go +++ b/app/internal/window/os_wayland.go @@ -106,8 +106,9 @@ type window struct { steps image.Point dist f32.Point } - lastPos f32.Point - lastTouch f32.Point + pointerBtns pointer.Buttons + lastPos f32.Point + lastTouch f32.Point fling struct { yExtrapolation fling.Extrapolation @@ -514,18 +515,32 @@ func gio_onPointerMotion(data unsafe.Pointer, p *C.struct_wl_pointer, t C.uint32 } //export gio_onPointerButton -func gio_onPointerButton(data unsafe.Pointer, p *C.struct_wl_pointer, serial, t, button, state C.uint32_t) { +func gio_onPointerButton(data unsafe.Pointer, p *C.struct_wl_pointer, serial, t, wbtn, state C.uint32_t) { w := winMap[p] // From linux-event-codes.h. - const BTN_LEFT = 0x110 - if button != BTN_LEFT { + const ( + BTN_LEFT = 0x110 + BTN_RIGHT = 0x111 + BTN_MIDDLE = 0x112 + ) + var btn pointer.Buttons + switch wbtn { + case BTN_LEFT: + btn = pointer.ButtonLeft + case BTN_RIGHT: + btn = pointer.ButtonRight + case BTN_MIDDLE: + btn = pointer.ButtonMiddle + default: return } var typ pointer.Type switch state { case 0: + w.pointerBtns &^= btn typ = pointer.Release case 1: + w.pointerBtns |= btn typ = pointer.Press } w.flushScroll() @@ -533,6 +548,7 @@ func gio_onPointerButton(data unsafe.Pointer, p *C.struct_wl_pointer, serial, t, w.w.Event(pointer.Event{ Type: typ, Source: pointer.Mouse, + Buttons: w.pointerBtns, Position: w.lastPos, Time: time.Duration(t) * time.Millisecond, }) @@ -908,6 +924,7 @@ func (w *window) flushScroll() { w.w.Event(pointer.Event{ Type: pointer.Move, Source: pointer.Mouse, + Buttons: w.pointerBtns, Position: w.lastPos, Scroll: total, Time: w.scroll.time, @@ -929,6 +946,7 @@ func (w *window) onPointerMotion(x, y C.wl_fixed_t, t C.uint32_t) { w.w.Event(pointer.Event{ Type: pointer.Move, Position: w.lastPos, + Buttons: w.pointerBtns, Source: pointer.Mouse, Time: time.Duration(t) * time.Millisecond, }) diff --git a/app/internal/window/os_windows.go b/app/internal/window/os_windows.go index a12070b8..5bbc2863 100644 --- a/app/internal/window/os_windows.go +++ b/app/internal/window/os_windows.go @@ -56,13 +56,14 @@ type point struct { } type window struct { - hwnd syscall.Handle - hdc syscall.Handle - w Callbacks - width int - height int - stage system.Stage - dead bool + hwnd syscall.Handle + hdc syscall.Handle + w Callbacks + width int + height int + stage system.Stage + dead bool + pointerBtns pointer.Buttons mu sync.Mutex animating bool @@ -139,6 +140,8 @@ const ( _WM_KEYUP = 0x0101 _WM_LBUTTONDOWN = 0x0201 _WM_LBUTTONUP = 0x0202 + _WM_MBUTTONDOWN =0x0207 + _WM_MBUTTONUP =0x0208 _WM_MOUSEMOVE = 0x0200 _WM_MOUSEWHEEL = 0x020A _WM_PAINT = 0x000F @@ -148,6 +151,8 @@ const ( _WM_SHOWWINDOW = 0x0018 _WM_SIZE = 0x0005 _WM_SYSKEYDOWN = 0x0104 + _WM_RBUTTONDOWN =0x0204 + _WM_RBUTTONUP = 0x0205 _WM_TIMER = 0x0113 _WM_UNICHAR = 0x0109 _WM_USER = 0x0400 @@ -313,15 +318,17 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr w.w.Event(cmd) } case _WM_LBUTTONDOWN: - setCapture(w.hwnd) - x, y := coordsFromlParam(lParam) - p := f32.Point{X: float32(x), Y: float32(y)} - w.w.Event(pointer.Event{ - Type: pointer.Press, - Source: pointer.Mouse, - Position: p, - Time: getMessageTime(), - }) + w.pointerButton(pointer.ButtonLeft, true, lParam) + case _WM_LBUTTONUP: + w.pointerButton(pointer.ButtonLeft, false, lParam) + case _WM_RBUTTONDOWN: + w.pointerButton(pointer.ButtonRight, true, lParam) + case _WM_RBUTTONUP: + w.pointerButton(pointer.ButtonRight, false, lParam) + case _WM_MBUTTONDOWN: + w.pointerButton(pointer.ButtonMiddle, true, lParam) + case _WM_MBUTTONUP: + w.pointerButton(pointer.ButtonMiddle, false, lParam) case _WM_CANCELMODE: w.w.Event(pointer.Event{ Type: pointer.Cancel, @@ -330,16 +337,6 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr w.w.Event(key.FocusEvent{Focus: true}) case _WM_KILLFOCUS: w.w.Event(key.FocusEvent{Focus: false}) - case _WM_LBUTTONUP: - releaseCapture() - x, y := coordsFromlParam(lParam) - p := f32.Point{X: float32(x), Y: float32(y)} - w.w.Event(pointer.Event{ - Type: pointer.Release, - Source: pointer.Mouse, - Position: p, - Time: getMessageTime(), - }) case _WM_MOUSEMOVE: x, y := coordsFromlParam(lParam) p := f32.Point{X: float32(x), Y: float32(y)} @@ -367,6 +364,32 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr return defWindowProc(hwnd, msg, wParam, lParam) } +func (w *window) pointerButton(btn pointer.Buttons, press bool, lParam uintptr) { + var typ pointer.Type + if press { + typ = pointer.Press + if w.pointerBtns == 0 { + setCapture(w.hwnd) + } + w.pointerBtns |= btn + } else { + typ = pointer.Release + w.pointerBtns &^= btn + if w.pointerBtns == 0 { + releaseCapture() + } + } + x, y := coordsFromlParam(lParam) + p := f32.Point{X: float32(x), Y: float32(y)} + w.w.Event(pointer.Event{ + Type: typ, + Source: pointer.Mouse, + Position: p, + Buttons: w.pointerBtns, + Time: getMessageTime(), + }) +} + func coordsFromlParam(lParam uintptr) (int, int) { x := int(int16(lParam & 0xffff)) y := int(int16((lParam >> 16) & 0xffff)) diff --git a/gesture/gesture.go b/gesture/gesture.go index 9449825d..6d264567 100644 --- a/gesture/gesture.go +++ b/gesture/gesture.go @@ -128,6 +128,9 @@ func (c *Click) Events(q event.Queue) []ClickEvent { if c.state == StatePressed || !e.Hit { break } + if e.Source == pointer.Mouse && e.Buttons != pointer.ButtonLeft { + break + } c.state = StatePressed events = append(events, ClickEvent{Type: TypePress, Position: e.Position, Source: e.Source}) case pointer.Move: diff --git a/io/pointer/pointer.go b/io/pointer/pointer.go index 095a1f58..1dcde674 100644 --- a/io/pointer/pointer.go +++ b/io/pointer/pointer.go @@ -5,6 +5,7 @@ package pointer import ( "encoding/binary" "image" + "strings" "time" "gioui.org/f32" @@ -27,6 +28,8 @@ type Event struct { // Time is when the event was received. The // timestamp is relative to an undefined base. Time time.Duration + // Buttons are the set of pressed mouse buttons for this event. + Buttons Buttons // Hit is set when the event was within the registered // area for the handler. Hit can be false when a pointer // was pressed within the hit area, and then dragged @@ -86,6 +89,9 @@ type Priority uint8 // Source of an Event. type Source uint8 +// Buttons is a set of mouse buttons +type Buttons uint8 + // Must match app/internal/input.areaKind type areaKind uint8 @@ -116,6 +122,12 @@ const ( Grabbed ) +const ( + ButtonLeft Buttons = 1 << iota + ButtonRight + ButtonMiddle +) + const ( areaRect areaKind = iota areaEllipse @@ -199,4 +211,24 @@ func (s Source) String() string { } } +// Contain reports whether the set b contains +// all of the buttons. +func (b Buttons) Contain(buttons Buttons) bool { + return b&buttons == buttons +} + +func (b Buttons) String() string { + var strs []string + if b.Contain(ButtonLeft) { + strs = append(strs, "ButtonLeft") + } + if b.Contain(ButtonRight) { + strs = append(strs, "ButtonRight") + } + if b.Contain(ButtonMiddle) { + strs = append(strs, "ButtonMiddle") + } + return strings.Join(strs, "|") +} + func (Event) ImplementsEvent() {}