From ab36ea3cebff25047a21cca83bf48ccc2c9fbe96 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Sat, 1 Oct 2022 11:48:55 +0200 Subject: [PATCH] feat: Add support at Pointer Wheel --- .../Input/LibInput/LibInputBackend.Pointer.cs | 110 ++++++++++++++++++ .../Input/LibInput/LibInputBackend.cs | 59 ++-------- .../LibInput/LibInputNativeUnsafeMethods.cs | 53 ++++++++- 3 files changed, 166 insertions(+), 56 deletions(-) create mode 100644 src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.Pointer.cs diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.Pointer.cs b/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.Pointer.cs new file mode 100644 index 0000000000..ef039a38bc --- /dev/null +++ b/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.Pointer.cs @@ -0,0 +1,110 @@ +using System; +using Avalonia.Input; +using Avalonia.Input.Raw; +using Avalonia.Logging; +using static Avalonia.LinuxFramebuffer.Input.LibInput.LibInputNativeUnsafeMethods; + +namespace Avalonia.LinuxFramebuffer.Input.LibInput; + +public partial class LibInputBackend +{ + private MouseDevice _mouse = new MouseDevice(); + private Point _mousePosition; + private const string Pointer = LibInput + "/" + nameof(Pointer); + + private void HandlePointer(IntPtr ev, LibInputEventType type) + { + var modifiers = RawInputModifiers.None; //TODO: support input modifiers + var pev = libinput_event_get_pointer_event(ev); + var info = _screen.ScaledSize; + var ts = libinput_event_pointer_get_time_usec(pev) / 1000; + switch (type) + { + case LibInputEventType.LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: + _mousePosition = new Point(libinput_event_pointer_get_absolute_x_transformed(pev, (int)info.Width), + libinput_event_pointer_get_absolute_y_transformed(pev, (int)info.Height)); + ScheduleInput(new RawPointerEventArgs(_mouse, ts, _inputRoot, RawPointerEventType.Move, _mousePosition, + modifiers)); + break; + case LibInputEventType.LIBINPUT_EVENT_POINTER_BUTTON: + { + var button = (EvKey)libinput_event_pointer_get_button(pev); + var buttonState = libinput_event_pointer_get_button_state(pev); + + RawPointerEventArgs evnt = button switch + { + EvKey.BTN_LEFT when buttonState == 1 + => new(_mouse, ts, _inputRoot, RawPointerEventType.LeftButtonDown, _mousePosition, modifiers), + EvKey.BTN_LEFT when buttonState == 0 + => new(_mouse, ts, _inputRoot, RawPointerEventType.LeftButtonUp, _mousePosition, modifiers), + EvKey.BTN_RIGHT when buttonState == 1 + => new(_mouse, ts, _inputRoot, RawPointerEventType.RightButtonUp, _mousePosition, modifiers), + EvKey.BTN_RIGHT when buttonState == 2 + => new(_mouse, ts, _inputRoot, RawPointerEventType.RightButtonDown, _mousePosition, modifiers), + EvKey.BTN_MIDDLE when buttonState == 1 + => new(_mouse, ts, _inputRoot, RawPointerEventType.MiddleButtonDown, _mousePosition, modifiers), + EvKey.BTN_MIDDLE when buttonState == 2 + => new(_mouse, ts, _inputRoot, RawPointerEventType.MiddleButtonUp, _mousePosition, modifiers), + _ => default, + }; + if (evnt is not null) + { + ScheduleInput(evnt); + } + else + { + Logger.TryGet(LogEventLevel.Warning, Pointer) + ?.Log(this, $"The button {button} is not associated"); + } + } + break; + // Backward compatibility with low-res wheel + case LibInputEventType.LIBINPUT_EVENT_POINTER_AXIS: + { + var sourceAxis = libinput_event_pointer_get_axis_source(pev); + switch (sourceAxis) + { + case LibInputPointerAxisSource.LIBINPUT_POINTER_AXIS_SOURCE_WHEEL: + { + var value = libinput_event_pointer_get_axis_value_discrete(pev, + LibInputPointerAxis.LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL); + ScheduleInput(new RawMouseWheelEventArgs(_mouse + , ts + , _inputRoot + , _mousePosition + , new Vector(0, -value) + , modifiers)); + } + break; + case LibInputPointerAxisSource.LIBINPUT_POINTER_AXIS_SOURCE_FINGER: + case LibInputPointerAxisSource.LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS: + case LibInputPointerAxisSource.LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT: + default: + Logger.TryGet(LogEventLevel.Debug, Pointer) + ?.Log(this, $"The pointer axis {sourceAxis} is not managed."); + break; + } + } + break; + // Hi-Res wheel + case LibInputEventType.LIBINPUT_EVENT_POINTER_SCROLL_WHEEL: + { + var value = new Vector(0, + -libinput_event_pointer_get_scroll_value_v120(pev, + LibInputPointerAxis.LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL) / 120); + ScheduleInput(new RawMouseWheelEventArgs(_mouse + , ts + , _inputRoot + , _mousePosition + , value + , modifiers)); + } + break; + default: + Logger.TryGet(LogEventLevel.Warning, Pointer) + ?.Log(this, $"The pointer event {type} is not mapped."); + break; + } + + } +} diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs b/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs index 702ae3f8e5..77e8202fac 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs @@ -4,18 +4,16 @@ using System.IO; using System.Threading; using Avalonia.Input; using Avalonia.Input.Raw; -using static Avalonia.LinuxFramebuffer.Input.LibInput.LibInputNativeUnsafeMethods; +using static Avalonia.LinuxFramebuffer.Input.LibInput.LibInputNativeUnsafeMethods; namespace Avalonia.LinuxFramebuffer.Input.LibInput { - public class LibInputBackend : IInputBackend + public partial class LibInputBackend : IInputBackend { private IScreenInfoProvider _screen; private IInputRoot _inputRoot; private readonly Queue _inputThreadActions = new Queue(); private TouchDevice _touch = new TouchDevice(); - private MouseDevice _mouse = new MouseDevice(); - private Point _mousePosition; - + private const string LibInput = nameof(Avalonia.LinuxFramebuffer) + "/" + nameof(Avalonia.LinuxFramebuffer.Input) + "/" + nameof(LibInput); private readonly RawEventGroupingThreadingHelper _inputQueue; private Action _onInput; private Dictionary _pointers = new Dictionary(); @@ -24,13 +22,13 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput { var ctx = libinput_path_create_context(); _inputQueue = new(e => _onInput?.Invoke(e)); - new Thread(()=>InputThread(ctx)).Start(); + new Thread(() => InputThread(ctx)).Start(); } private unsafe void InputThread(IntPtr ctx) { var fd = libinput_get_fd(ctx); - + var timeval = stackalloc IntPtr[2]; @@ -38,12 +36,11 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput libinput_path_add_device(ctx, f); while (true) { - IntPtr ev; libinput_dispatch(ctx); while ((ev = libinput_get_event(ctx)) != IntPtr.Zero) { - + var type = libinput_event_get_type(ev); if (type >= LibInputEventType.LIBINPUT_EVENT_TOUCH_DOWN && type <= LibInputEventType.LIBINPUT_EVENT_TOUCH_CANCEL) @@ -52,12 +49,12 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput if (type >= LibInputEventType.LIBINPUT_EVENT_POINTER_MOTION && type <= LibInputEventType.LIBINPUT_EVENT_POINTER_AXIS) HandlePointer(ev, type); - + libinput_event_destroy(ev); libinput_dispatch(ctx); } - pollfd pfd = new pollfd {fd = fd, events = 1}; + pollfd pfd = new pollfd { fd = fd, events = 1 }; NativeUnsafeMethods.poll(&pfd, new IntPtr(1), 10); } } @@ -67,7 +64,7 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput private void HandleTouch(IntPtr ev, LibInputEventType type) { var tev = libinput_event_get_touch_event(ev); - if(tev == IntPtr.Zero) + if (tev == IntPtr.Zero) return; if (type < LibInputEventType.LIBINPUT_EVENT_TOUCH_FRAME) { @@ -102,44 +99,6 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput } } - private void HandlePointer(IntPtr ev, LibInputEventType type) - { - //TODO: support input modifiers - var pev = libinput_event_get_pointer_event(ev); - var info = _screen.ScaledSize; - var ts = libinput_event_pointer_get_time_usec(pev) / 1000; - if (type == LibInputEventType.LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE) - { - _mousePosition = new Point(libinput_event_pointer_get_absolute_x_transformed(pev, (int)info.Width), - libinput_event_pointer_get_absolute_y_transformed(pev, (int)info.Height)); - ScheduleInput(new RawPointerEventArgs(_mouse, ts, _inputRoot, RawPointerEventType.Move, _mousePosition, - RawInputModifiers.None)); - } - else if (type == LibInputEventType.LIBINPUT_EVENT_POINTER_BUTTON) - { - var button = (EvKey)libinput_event_pointer_get_button(pev); - var buttonState = libinput_event_pointer_get_button_state(pev); - - - var evnt = button == EvKey.BTN_LEFT ? - (buttonState == 1 ? RawPointerEventType.LeftButtonDown : RawPointerEventType.LeftButtonUp) : - button == EvKey.BTN_MIDDLE ? - (buttonState == 1 ? RawPointerEventType.MiddleButtonDown : RawPointerEventType.MiddleButtonUp) : - button == EvKey.BTN_RIGHT ? - (buttonState == 1 ? - RawPointerEventType.RightButtonDown : - RawPointerEventType.RightButtonUp) : - (RawPointerEventType)(-1); - if (evnt == (RawPointerEventType)(-1)) - return; - - - ScheduleInput( - new RawPointerEventArgs(_mouse, ts, _inputRoot, evnt, _mousePosition, RawInputModifiers.None)); - } - - } - public void Initialize(IScreenInfoProvider screen, Action onInput) { _screen = screen; diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputNativeUnsafeMethods.cs b/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputNativeUnsafeMethods.cs index 0492090461..df6defb653 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputNativeUnsafeMethods.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputNativeUnsafeMethods.cs @@ -77,6 +77,9 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE, LIBINPUT_EVENT_POINTER_BUTTON, LIBINPUT_EVENT_POINTER_AXIS, + LIBINPUT_EVENT_POINTER_SCROLL_WHEEL, + LIBINPUT_EVENT_POINTER_SCROLL_FINGER, + LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS, LIBINPUT_EVENT_TOUCH_DOWN = 500, LIBINPUT_EVENT_TOUCH_UP, LIBINPUT_EVENT_TOUCH_MOTION, @@ -97,8 +100,38 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput LIBINPUT_EVENT_GESTURE_PINCH_END, LIBINPUT_EVENT_SWITCH_TOGGLE = 900, } - - + + public enum LibInputPointerAxisSource + { + /** + * The event is caused by the rotation of a wheel. + **/ + LIBINPUT_POINTER_AXIS_SOURCE_WHEEL = 1, + /** + * The event is caused by the movement of one or more fingers on a device. + **/ + LIBINPUT_POINTER_AXIS_SOURCE_FINGER, + /** + * The event is caused by the motion of some device. + **/ + LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS, + /** + * The event is caused by the tilting of a mouse wheel rather than + * its rotation. This method is commonly used on mice without + * separate horizontal scroll wheels. + * @deprecated This axis source is deprecated as of libinput 1.16. + * It was never used by any device before libinput 1.16. All wheel + * tilt devices use @ref LIBINPUT_POINTER_AXIS_SOURCE_WHEEL instead. + **/ + LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT, + }; + + public enum LibInputPointerAxis + { + LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL = 0, + LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL = 1, + }; + [DllImport(LibInput)] public extern static void libinput_event_destroy(IntPtr ev); @@ -119,21 +152,29 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput [DllImport(LibInput)] public extern static IntPtr libinput_event_get_pointer_event(IntPtr ev); - - + [DllImport(LibInput)] public extern static ulong libinput_event_pointer_get_time_usec(IntPtr ev); [DllImport(LibInput)] public extern static double libinput_event_pointer_get_absolute_x_transformed(IntPtr ev, int width); - + [DllImport(LibInput)] public extern static double libinput_event_pointer_get_absolute_y_transformed(IntPtr ev, int height); - + [DllImport(LibInput)] public extern static int libinput_event_pointer_get_button(IntPtr ev); [DllImport(LibInput)] public extern static int libinput_event_pointer_get_button_state(IntPtr ev); + + [DllImport(LibInput)] + public extern static LibInputPointerAxisSource libinput_event_pointer_get_axis_source(IntPtr ev); + + [DllImport((LibInput))] + public extern static double libinput_event_pointer_get_axis_value_discrete(IntPtr ev, LibInputPointerAxis axis); + + [DllImport(LibInput)] + public extern static double libinput_event_pointer_get_scroll_value_v120(IntPtr ev, LibInputPointerAxis axis); } }