LibInputBackend.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.IO;
  5. using System.Threading;
  6. using Avalonia.Input;
  7. using Avalonia.Input.Raw;
  8. using Avalonia.Threading;
  9. using static Avalonia.LinuxFramebuffer.Input.LibInput.LibInputNativeUnsafeMethods;
  10. namespace Avalonia.LinuxFramebuffer.Input.LibInput
  11. {
  12. public class LibInputBackend : IInputBackend
  13. {
  14. private IScreenInfoProvider _screen;
  15. private IInputRoot _inputRoot;
  16. private readonly Queue<Action> _inputThreadActions = new Queue<Action>();
  17. private TouchDevice _touch = new TouchDevice();
  18. private MouseDevice _mouse = new MouseDevice();
  19. private Point _mousePosition;
  20. private readonly Queue<RawInputEventArgs> _inputQueue = new Queue<RawInputEventArgs>();
  21. private Action<RawInputEventArgs> _onInput;
  22. private Dictionary<int, Point> _pointers = new Dictionary<int, Point>();
  23. public LibInputBackend()
  24. {
  25. var ctx = libinput_path_create_context();
  26. new Thread(()=>InputThread(ctx)).Start();
  27. }
  28. private unsafe void InputThread(IntPtr ctx)
  29. {
  30. var fd = libinput_get_fd(ctx);
  31. var timeval = stackalloc IntPtr[2];
  32. foreach (var f in Directory.GetFiles("/dev/input", "event*"))
  33. libinput_path_add_device(ctx, f);
  34. while (true)
  35. {
  36. IntPtr ev;
  37. libinput_dispatch(ctx);
  38. while ((ev = libinput_get_event(ctx)) != IntPtr.Zero)
  39. {
  40. var type = libinput_event_get_type(ev);
  41. if (type >= LibInputEventType.LIBINPUT_EVENT_TOUCH_DOWN &&
  42. type <= LibInputEventType.LIBINPUT_EVENT_TOUCH_CANCEL)
  43. HandleTouch(ev, type);
  44. if (type >= LibInputEventType.LIBINPUT_EVENT_POINTER_MOTION
  45. && type <= LibInputEventType.LIBINPUT_EVENT_POINTER_AXIS)
  46. HandlePointer(ev, type);
  47. libinput_event_destroy(ev);
  48. libinput_dispatch(ctx);
  49. }
  50. pollfd pfd = new pollfd {fd = fd, events = 1};
  51. NativeUnsafeMethods.poll(&pfd, new IntPtr(1), 10);
  52. }
  53. }
  54. private void ScheduleInput(RawInputEventArgs ev)
  55. {
  56. lock (_inputQueue)
  57. {
  58. _inputQueue.Enqueue(ev);
  59. if (_inputQueue.Count == 1)
  60. {
  61. Dispatcher.UIThread.Post(() =>
  62. {
  63. while (true)
  64. {
  65. Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
  66. RawInputEventArgs dequeuedEvent = null;
  67. lock(_inputQueue)
  68. if (_inputQueue.Count != 0)
  69. dequeuedEvent = _inputQueue.Dequeue();
  70. if (dequeuedEvent == null)
  71. return;
  72. _onInput?.Invoke(dequeuedEvent);
  73. }
  74. }, DispatcherPriority.Input);
  75. }
  76. }
  77. }
  78. private void HandleTouch(IntPtr ev, LibInputEventType type)
  79. {
  80. var tev = libinput_event_get_touch_event(ev);
  81. if(tev == IntPtr.Zero)
  82. return;
  83. if (type < LibInputEventType.LIBINPUT_EVENT_TOUCH_FRAME)
  84. {
  85. var info = _screen.ScaledSize;
  86. var slot = libinput_event_touch_get_slot(tev);
  87. Point pt;
  88. if (type == LibInputEventType.LIBINPUT_EVENT_TOUCH_DOWN
  89. || type == LibInputEventType.LIBINPUT_EVENT_TOUCH_MOTION)
  90. {
  91. var x = libinput_event_touch_get_x_transformed(tev, (int)info.Width);
  92. var y = libinput_event_touch_get_y_transformed(tev, (int)info.Height);
  93. pt = new Point(x, y);
  94. _pointers[slot] = pt;
  95. }
  96. else
  97. {
  98. _pointers.TryGetValue(slot, out pt);
  99. _pointers.Remove(slot);
  100. }
  101. var ts = libinput_event_touch_get_time_usec(tev) / 1000;
  102. if (_inputRoot == null)
  103. return;
  104. ScheduleInput(new RawTouchEventArgs(_touch, ts,
  105. _inputRoot,
  106. type == LibInputEventType.LIBINPUT_EVENT_TOUCH_DOWN ? RawPointerEventType.TouchBegin
  107. : type == LibInputEventType.LIBINPUT_EVENT_TOUCH_UP ? RawPointerEventType.TouchEnd
  108. : type == LibInputEventType.LIBINPUT_EVENT_TOUCH_MOTION ? RawPointerEventType.TouchUpdate
  109. : RawPointerEventType.TouchCancel,
  110. pt, InputModifiers.None, slot));
  111. }
  112. }
  113. private void HandlePointer(IntPtr ev, LibInputEventType type)
  114. {
  115. //TODO: support input modifiers
  116. var pev = libinput_event_get_pointer_event(ev);
  117. var info = _screen.ScaledSize;
  118. var ts = libinput_event_pointer_get_time_usec(pev) / 1000;
  119. if (type == LibInputEventType.LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE)
  120. {
  121. _mousePosition = new Point(libinput_event_pointer_get_absolute_x_transformed(pev, (int)info.Width),
  122. libinput_event_pointer_get_absolute_y_transformed(pev, (int)info.Height));
  123. ScheduleInput(new RawPointerEventArgs(_mouse, ts, _inputRoot, RawPointerEventType.Move, _mousePosition,
  124. InputModifiers.None));
  125. }
  126. else if (type == LibInputEventType.LIBINPUT_EVENT_POINTER_BUTTON)
  127. {
  128. var button = (EvKey)libinput_event_pointer_get_button(pev);
  129. var buttonState = libinput_event_pointer_get_button_state(pev);
  130. var evnt = button == EvKey.BTN_LEFT ?
  131. (buttonState == 1 ? RawPointerEventType.LeftButtonDown : RawPointerEventType.LeftButtonUp) :
  132. button == EvKey.BTN_MIDDLE ?
  133. (buttonState == 1 ? RawPointerEventType.MiddleButtonDown : RawPointerEventType.MiddleButtonUp) :
  134. button == EvKey.BTN_RIGHT ?
  135. (buttonState == 1 ?
  136. RawPointerEventType.RightButtonDown :
  137. RawPointerEventType.RightButtonUp) :
  138. (RawPointerEventType)(-1);
  139. if (evnt == (RawPointerEventType)(-1))
  140. return;
  141. ScheduleInput(
  142. new RawPointerEventArgs(_mouse, ts, _inputRoot, evnt, _mousePosition, InputModifiers.None));
  143. }
  144. }
  145. public void Initialize(IScreenInfoProvider screen, Action<RawInputEventArgs> onInput)
  146. {
  147. _screen = screen;
  148. _onInput = onInput;
  149. }
  150. public void SetInputRoot(IInputRoot root)
  151. {
  152. _inputRoot = root;
  153. }
  154. }
  155. }