From fc1a60b159c85d73657e40ce5c755b3561afe379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro?= Date: Tue, 24 Sep 2019 01:04:57 +0100 Subject: [PATCH 1/2] Added support for extra mouse buttons. --- native/Avalonia.Native/inc/avalonia-native.h | 14 ++++- native/Avalonia.Native/src/OSX/window.mm | 41 +++++++++++++-- src/Avalonia.Input/IKeyboardDevice.cs | 2 + src/Avalonia.Input/MouseDevice.cs | 16 ++++++ src/Avalonia.Input/PointerPoint.cs | 22 +++++++- src/Avalonia.Input/Raw/RawPointerEventArgs.cs | 4 ++ src/Avalonia.X11/X11Window.cs | 36 +++++++++---- src/Windows/Avalonia.Win32/WindowImpl.cs | 52 +++++++++++++------ 8 files changed, 152 insertions(+), 35 deletions(-) diff --git a/native/Avalonia.Native/inc/avalonia-native.h b/native/Avalonia.Native/inc/avalonia-native.h index 06d6bdf311..677fa2f4c5 100644 --- a/native/Avalonia.Native/inc/avalonia-native.h +++ b/native/Avalonia.Native/inc/avalonia-native.h @@ -92,9 +92,17 @@ enum AvnRawMouseEventType RightButtonUp, MiddleButtonDown, MiddleButtonUp, + XButton1Down, + XButton1Up, + XButton2Down, + XButton2Up, Move, Wheel, - NonClientLeftButtonDown + NonClientLeftButtonDown, + TouchBegin, + TouchUpdate, + TouchEnd, + TouchCancel }; enum AvnRawKeyEventType @@ -112,7 +120,9 @@ enum AvnInputModifiers Windows = 8, LeftMouseButton = 16, RightMouseButton = 32, - MiddleMouseButton = 64 + MiddleMouseButton = 64, + XButton1MouseButton = 128, + XButton2MouseButton = 256 }; enum AvnWindowState diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 021f6e5603..4c0a551c72 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -716,7 +716,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent AvnFramebuffer _swRenderedFrameBuffer; bool _queuedDisplayFromThread; NSTrackingArea* _area; - bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isMouseOver; + bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed, _isMouseOver; NSEvent* _lastMouseDownEvent; bool _lastKeyHandled; } @@ -941,9 +941,23 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent - (void)otherMouseDown:(NSEvent *)event { - _isMiddlePressed = true; _lastMouseDownEvent = event; - [self mouseEvent:event withType:MiddleButtonDown]; + + switch(event.buttonNumber) + { + case 3: + _isMiddlePressed = true; + [self mouseEvent:event withType:MiddleButtonDown]; + break; + case 4: + _isXButton1Pressed = true; + [self mouseEvent:event withType:XButton1Down]; + break; + case 5: + _isXButton2Pressed = true; + [self mouseEvent:event withType:XButton2Down]; + break; + } } - (void)rightMouseDown:(NSEvent *)event @@ -961,8 +975,21 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent - (void)otherMouseUp:(NSEvent *)event { - _isMiddlePressed = false; - [self mouseEvent:event withType:MiddleButtonUp]; + switch(event.buttonNumber) + { + case 3: + _isMiddlePressed = false; + [self mouseEvent:event withType:MiddleButtonUp]; + break; + case 4: + _isXButton1Pressed = false; + [self mouseEvent:event withType:XButton1Up]; + break; + case 5: + _isXButton2Pressed = false; + [self mouseEvent:event withType:XButton2Up]; + break; + } } - (void)rightMouseUp:(NSEvent *)event @@ -1061,6 +1088,10 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent rv |= MiddleMouseButton; if (_isRightPressed) rv |= RightMouseButton; + if (_isXButton1Pressed) + rv |= XButton1MouseButton; + if (_isXButton2Pressed) + rv |= XButton2MouseButton; return (AvnInputModifiers)rv; } diff --git a/src/Avalonia.Input/IKeyboardDevice.cs b/src/Avalonia.Input/IKeyboardDevice.cs index 1a82f7d671..144979523f 100644 --- a/src/Avalonia.Input/IKeyboardDevice.cs +++ b/src/Avalonia.Input/IKeyboardDevice.cs @@ -48,6 +48,8 @@ namespace Avalonia.Input LeftMouseButton = 16, RightMouseButton = 32, MiddleMouseButton = 64, + XButton1MouseButton = 128, + XButton2MouseButton = 256, KeyboardMask = Alt | Control | Shift | Meta } diff --git a/src/Avalonia.Input/MouseDevice.cs b/src/Avalonia.Input/MouseDevice.cs index 4dcf0eee53..c74498171e 100644 --- a/src/Avalonia.Input/MouseDevice.cs +++ b/src/Avalonia.Input/MouseDevice.cs @@ -120,6 +120,10 @@ namespace Avalonia.Input rv++; if (props.IsRightButtonPressed) rv++; + if (props.IsXButton1Pressed) + rv++; + if (props.IsXButton2Pressed) + rv++; return rv; } @@ -142,6 +146,8 @@ namespace Avalonia.Input case RawPointerEventType.LeftButtonDown: case RawPointerEventType.RightButtonDown: case RawPointerEventType.MiddleButtonDown: + case RawPointerEventType.XButton1Down: + case RawPointerEventType.XButton2Down: if (ButtonCount(props) > 1) e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers); else @@ -151,6 +157,8 @@ namespace Avalonia.Input case RawPointerEventType.LeftButtonUp: case RawPointerEventType.RightButtonUp: case RawPointerEventType.MiddleButtonUp: + case RawPointerEventType.XButton1Up: + case RawPointerEventType.XButton2Up: if (ButtonCount(props) != 0) e.Handled = MouseMove(mouse, e.Timestamp, e.Root, e.Position, props, keyModifiers); else @@ -186,12 +194,20 @@ namespace Avalonia.Input kind = PointerUpdateKind.MiddleButtonPressed; if (args.Type == RawPointerEventType.RightButtonDown) kind = PointerUpdateKind.RightButtonPressed; + if (args.Type == RawPointerEventType.XButton1Down) + kind = PointerUpdateKind.XButton1Pressed; + if (args.Type == RawPointerEventType.XButton2Down) + kind = PointerUpdateKind.XButton2Pressed; if (args.Type == RawPointerEventType.LeftButtonUp) kind = PointerUpdateKind.LeftButtonReleased; if (args.Type == RawPointerEventType.MiddleButtonUp) kind = PointerUpdateKind.MiddleButtonReleased; if (args.Type == RawPointerEventType.RightButtonUp) kind = PointerUpdateKind.RightButtonReleased; + if (args.Type == RawPointerEventType.XButton1Up) + kind = PointerUpdateKind.XButton1Released; + if (args.Type == RawPointerEventType.XButton2Up) + kind = PointerUpdateKind.XButton2Released; return new PointerPointProperties(args.InputModifiers, kind); } diff --git a/src/Avalonia.Input/PointerPoint.cs b/src/Avalonia.Input/PointerPoint.cs index 1068a0d4d4..96132fdc90 100644 --- a/src/Avalonia.Input/PointerPoint.cs +++ b/src/Avalonia.Input/PointerPoint.cs @@ -21,18 +21,24 @@ namespace Avalonia.Input public bool IsLeftButtonPressed { get; } public bool IsMiddleButtonPressed { get; } public bool IsRightButtonPressed { get; } + public bool IsXButton1Pressed { get; } + public bool IsXButton2Pressed { get; } + public PointerUpdateKind PointerUpdateKind { get; } + private PointerPointProperties() - { - + { } public PointerPointProperties(RawInputModifiers modifiers, PointerUpdateKind kind) { PointerUpdateKind = kind; + IsLeftButtonPressed = modifiers.HasFlagCustom(RawInputModifiers.LeftMouseButton); IsMiddleButtonPressed = modifiers.HasFlagCustom(RawInputModifiers.MiddleMouseButton); IsRightButtonPressed = modifiers.HasFlagCustom(RawInputModifiers.RightMouseButton); + IsXButton1Pressed = modifiers.HasFlagCustom(RawInputModifiers.XButton1MouseButton); + IsXButton2Pressed = modifiers.HasFlagCustom(RawInputModifiers.XButton2MouseButton); // The underlying input source might be reporting the previous state, // so make sure that we reflect the current state @@ -49,6 +55,14 @@ namespace Avalonia.Input IsRightButtonPressed = true; if (kind == PointerUpdateKind.RightButtonReleased) IsRightButtonPressed = false; + if (kind == PointerUpdateKind.XButton1Pressed) + IsXButton1Pressed = true; + if (kind == PointerUpdateKind.XButton1Released) + IsXButton1Pressed = false; + if (kind == PointerUpdateKind.XButton2Pressed) + IsXButton2Pressed = true; + if (kind == PointerUpdateKind.XButton2Released) + IsXButton2Pressed = false; } public static PointerPointProperties None { get; } = new PointerPointProperties(); @@ -59,9 +73,13 @@ namespace Avalonia.Input LeftButtonPressed, MiddleButtonPressed, RightButtonPressed, + XButton1Pressed, + XButton2Pressed, LeftButtonReleased, MiddleButtonReleased, RightButtonReleased, + XButton1Released, + XButton2Released, Other } diff --git a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs b/src/Avalonia.Input/Raw/RawPointerEventArgs.cs index 56854c7d29..4b2c95943d 100644 --- a/src/Avalonia.Input/Raw/RawPointerEventArgs.cs +++ b/src/Avalonia.Input/Raw/RawPointerEventArgs.cs @@ -14,6 +14,10 @@ namespace Avalonia.Input.Raw RightButtonUp, MiddleButtonDown, MiddleButtonUp, + XButton1Down, + XButton1Up, + XButton2Down, + XButton2Up, Move, Wheel, NonClientLeftButtonDown, diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 32460fed86..6ff5b96f12 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -351,10 +351,17 @@ namespace Avalonia.X11 { if (ActivateTransientChildIfNeeded()) return; - if (ev.ButtonEvent.button < 4) - MouseEvent(ev.ButtonEvent.button == 1 ? RawPointerEventType.LeftButtonDown - : ev.ButtonEvent.button == 2 ? RawPointerEventType.MiddleButtonDown - : RawPointerEventType.RightButtonDown, ref ev, ev.ButtonEvent.state); + if (ev.ButtonEvent.button < 4 || ev.ButtonEvent.button == 8 || ev.ButtonEvent.button == 9) + MouseEvent( + ev.ButtonEvent.button switch + { + 1 => RawPointerEventType.LeftButtonDown, + 2 => RawPointerEventType.MiddleButtonDown, + 3 => RawPointerEventType.RightButtonDown, + 8 => RawPointerEventType.XButton1Down, + 9 => RawPointerEventType.XButton2Down + }, + ref ev, ev.ButtonEvent.state); else { var delta = ev.ButtonEvent.button == 4 @@ -372,10 +379,17 @@ namespace Avalonia.X11 } else if (ev.type == XEventName.ButtonRelease) { - if (ev.ButtonEvent.button < 4) - MouseEvent(ev.ButtonEvent.button == 1 ? RawPointerEventType.LeftButtonUp - : ev.ButtonEvent.button == 2 ? RawPointerEventType.MiddleButtonUp - : RawPointerEventType.RightButtonUp, ref ev, ev.ButtonEvent.state); + if (ev.ButtonEvent.button < 4 || ev.ButtonEvent.button == 8 || ev.ButtonEvent.button == 9) + MouseEvent( + ev.ButtonEvent.button switch + { + 1 => RawPointerEventType.LeftButtonUp, + 2 => RawPointerEventType.MiddleButtonUp, + 3 => RawPointerEventType.RightButtonUp, + 8 => RawPointerEventType.XButton1Up, + 9 => RawPointerEventType.XButton2Up + }, + ref ev, ev.ButtonEvent.state); } else if (ev.type == XEventName.ConfigureNotify) { @@ -586,8 +600,12 @@ namespace Avalonia.X11 rv |= RawInputModifiers.LeftMouseButton; if (state.HasFlag(XModifierMask.Button2Mask)) rv |= RawInputModifiers.RightMouseButton; - if (state.HasFlag(XModifierMask.Button2Mask)) + if (state.HasFlag(XModifierMask.Button3Mask)) rv |= RawInputModifiers.MiddleMouseButton; + if (state.HasFlag(XModifierMask.Button4Mask)) + rv |= RawInputModifiers.XButton1MouseButton; + if (state.HasFlag(XModifierMask.Button5Mask)) + rv |= RawInputModifiers.XButton2MouseButton; if (state.HasFlag(XModifierMask.ShiftMask)) rv |= RawInputModifiers.Shift; if (state.HasFlag(XModifierMask.ControlMask)) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 9d35342fb9..0a39a9e0f6 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -540,34 +540,42 @@ namespace Avalonia.Win32 case UnmanagedMethods.WindowsMessage.WM_LBUTTONDOWN: case UnmanagedMethods.WindowsMessage.WM_RBUTTONDOWN: case UnmanagedMethods.WindowsMessage.WM_MBUTTONDOWN: - if(ShouldIgnoreTouchEmulatedMessage()) + case UnmanagedMethods.WindowsMessage.WM_XBUTTONDOWN: + if (ShouldIgnoreTouchEmulatedMessage()) break; e = new RawPointerEventArgs( _mouseDevice, timestamp, _owner, - msg == (int)UnmanagedMethods.WindowsMessage.WM_LBUTTONDOWN - ? RawPointerEventType.LeftButtonDown - : msg == (int)UnmanagedMethods.WindowsMessage.WM_RBUTTONDOWN - ? RawPointerEventType.RightButtonDown - : RawPointerEventType.MiddleButtonDown, + (UnmanagedMethods.WindowsMessage)msg switch + { + UnmanagedMethods.WindowsMessage.WM_LBUTTONDOWN => RawPointerEventType.LeftButtonDown, + UnmanagedMethods.WindowsMessage.WM_RBUTTONDOWN => RawPointerEventType.RightButtonDown, + UnmanagedMethods.WindowsMessage.WM_MBUTTONDOWN => RawPointerEventType.MiddleButtonDown, + UnmanagedMethods.WindowsMessage.WM_XBUTTONDOWN => + HighWord(ToInt32(wParam)) == 1 ? RawPointerEventType.XButton1Down : RawPointerEventType.XButton2Down + }, DipFromLParam(lParam), GetMouseModifiers(wParam)); break; case UnmanagedMethods.WindowsMessage.WM_LBUTTONUP: case UnmanagedMethods.WindowsMessage.WM_RBUTTONUP: case UnmanagedMethods.WindowsMessage.WM_MBUTTONUP: - if(ShouldIgnoreTouchEmulatedMessage()) + case UnmanagedMethods.WindowsMessage.WM_XBUTTONUP: + if (ShouldIgnoreTouchEmulatedMessage()) break; e = new RawPointerEventArgs( _mouseDevice, timestamp, _owner, - msg == (int)UnmanagedMethods.WindowsMessage.WM_LBUTTONUP - ? RawPointerEventType.LeftButtonUp - : msg == (int)UnmanagedMethods.WindowsMessage.WM_RBUTTONUP - ? RawPointerEventType.RightButtonUp - : RawPointerEventType.MiddleButtonUp, + (UnmanagedMethods.WindowsMessage)msg switch + { + UnmanagedMethods.WindowsMessage.WM_LBUTTONUP => RawPointerEventType.LeftButtonUp, + UnmanagedMethods.WindowsMessage.WM_RBUTTONUP => RawPointerEventType.RightButtonUp, + UnmanagedMethods.WindowsMessage.WM_MBUTTONUP => RawPointerEventType.MiddleButtonUp, + UnmanagedMethods.WindowsMessage.WM_XBUTTONUP => + HighWord(ToInt32(wParam)) == 1 ? RawPointerEventType.XButton1Up : RawPointerEventType.XButton2Up, + }, DipFromLParam(lParam), GetMouseModifiers(wParam)); break; @@ -627,15 +635,19 @@ namespace Avalonia.Win32 case UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN: case UnmanagedMethods.WindowsMessage.WM_NCRBUTTONDOWN: case UnmanagedMethods.WindowsMessage.WM_NCMBUTTONDOWN: + case UnmanagedMethods.WindowsMessage.WM_NCXBUTTONDOWN: e = new RawPointerEventArgs( _mouseDevice, timestamp, _owner, - msg == (int)UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN - ? RawPointerEventType.NonClientLeftButtonDown - : msg == (int)UnmanagedMethods.WindowsMessage.WM_NCRBUTTONDOWN - ? RawPointerEventType.RightButtonDown - : RawPointerEventType.MiddleButtonDown, + (UnmanagedMethods.WindowsMessage)msg switch + { + UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN => RawPointerEventType.NonClientLeftButtonDown, + UnmanagedMethods.WindowsMessage.WM_NCRBUTTONDOWN => RawPointerEventType.RightButtonDown, + UnmanagedMethods.WindowsMessage.WM_NCMBUTTONDOWN => RawPointerEventType.MiddleButtonDown, + UnmanagedMethods.WindowsMessage.WM_NCXBUTTONDOWN => + HighWord(ToInt32(wParam)) == 1 ? RawPointerEventType.XButton1Down : RawPointerEventType.XButton2Down, + }, PointToClient(PointFromLParam(lParam)), GetMouseModifiers(wParam)); break; case WindowsMessage.WM_TOUCH: @@ -778,6 +790,10 @@ namespace Avalonia.Win32 modifiers |= RawInputModifiers.RightMouseButton; if (keys.HasFlagCustom(UnmanagedMethods.ModifierKeys.MK_MBUTTON)) modifiers |= RawInputModifiers.MiddleMouseButton; + if (keys.HasFlag(UnmanagedMethods.ModifierKeys.MK_XBUTTON1)) + modifiers |= RawInputModifiers.XButton1MouseButton; + if (keys.HasFlag(UnmanagedMethods.ModifierKeys.MK_XBUTTON2)) + modifiers |= RawInputModifiers.XButton2MouseButton; return modifiers; } @@ -1056,5 +1072,7 @@ namespace Avalonia.Win32 } } IntPtr EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Handle => Handle.Handle; + + private static int HighWord(int param) => param >> 16; } } From 3f70b5e494fc7e6b7553490f59f315032075f064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro?= Date: Tue, 24 Sep 2019 16:26:46 +0100 Subject: [PATCH 2/2] Implemented extra mouse buttons in XI2Manager. --- src/Avalonia.X11/XI2Manager.cs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.X11/XI2Manager.cs b/src/Avalonia.X11/XI2Manager.cs index e37ed39bee..ac14efe133 100644 --- a/src/Avalonia.X11/XI2Manager.cs +++ b/src/Avalonia.X11/XI2Manager.cs @@ -240,11 +240,15 @@ namespace Avalonia.X11 if (ev.Type == XiEventType.XI_ButtonPress || ev.Type == XiEventType.XI_ButtonRelease) { var down = ev.Type == XiEventType.XI_ButtonPress; - var type = - ev.Button == 1 ? (down ? RawPointerEventType.LeftButtonDown : RawPointerEventType.LeftButtonUp) - : ev.Button == 2 ? (down ? RawPointerEventType.MiddleButtonDown : RawPointerEventType.MiddleButtonUp) - : ev.Button == 3 ? (down ? RawPointerEventType.RightButtonDown : RawPointerEventType.RightButtonUp) - : (RawPointerEventType?)null; + var type = ev.Button switch + { + 1 => down ? RawPointerEventType.LeftButtonDown : RawPointerEventType.LeftButtonUp, + 2 => down ? RawPointerEventType.MiddleButtonDown : RawPointerEventType.MiddleButtonUp, + 3 => down ? RawPointerEventType.RightButtonDown : RawPointerEventType.RightButtonUp, + 8 => down ? RawPointerEventType.XButton1Down : RawPointerEventType.XButton1Up, + 9 => down ? RawPointerEventType.XButton2Down : RawPointerEventType.XButton2Up, + _ => (RawPointerEventType?)null + }; if (type.HasValue) client.ScheduleInput(new RawPointerEventArgs(client.MouseDevice, ev.Timestamp, client.InputRoot, type.Value, ev.Position, ev.Modifiers)); @@ -283,12 +287,14 @@ namespace Avalonia.X11 var buttons = ev->buttons.Mask; if (XIMaskIsSet(buttons, 1)) Modifiers |= RawInputModifiers.LeftMouseButton; - if (XIMaskIsSet(buttons, 2)) Modifiers |= RawInputModifiers.MiddleMouseButton; - if (XIMaskIsSet(buttons, 3)) Modifiers |= RawInputModifiers.RightMouseButton; + if (XIMaskIsSet(buttons, 8)) + Modifiers |= RawInputModifiers.XButton1MouseButton; + if (XIMaskIsSet(buttons, 9)) + Modifiers |= RawInputModifiers.XButton2MouseButton; } Valuators = new Dictionary();