1
0

10-tablet-mouse-control.lua 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. -- SPDX-License-Identifier: MIT
  2. --
  3. -- This is an example libinput plugin
  4. --
  5. -- This plugin controls a mouse/pointer from a tablet device. This
  6. -- effectively hides stylus interactions and sends pointer events
  7. -- instead. In other words: mouse emulation for tablets, implemented
  8. -- by (remote) controlling a mouse device. This allows using a
  9. -- tablet stylus as a mouse replacement without tablet limitations
  10. -- from compositors or clients. Note that axis usually needed for
  11. -- drawing (like pressure, tilt or distance) are no longer emitted
  12. -- when this plugin is active and a mouse is connected. When no
  13. -- mouse is connected, this plugin doesn't change tablet events,
  14. -- thus the stylus works like a normal stylus.
  15. -- UNCOMMENT THIS LINE TO ACTIVATE THE PLUGIN
  16. -- libinput:register({1})
  17. -- globals
  18. pointer_device = nil
  19. tablet_device = nil
  20. maximum_x = nil
  21. maximum_y = nil
  22. function adjust_for_aspect_ratio(y)
  23. -- adjust y to match monitor 21:9 aspect ratio
  24. local adj_maximum_y = maximum_x * 1440 / 3440
  25. return math.floor(math.min(y * maximum_y / adj_maximum_y, maximum_y + 1))
  26. end
  27. function on_tablet_frame(device, frame, time_in_microseconds)
  28. -- emit tablet frame when there is no pointer device
  29. if not pointer_device then return nil end
  30. -- map tablet frame to pointer frame
  31. local events = {}
  32. for _, v in ipairs(frame) do
  33. if v.usage == evdev.ABS_MISC then
  34. -- save a few cycles on Wacom tablets by discarding a
  35. -- proximity in / out frame early, non-Wacom tablets should
  36. -- use BTN_TOOL_PEN/RUBBER/... instead
  37. return {}
  38. elseif v.usage == evdev.ABS_X then
  39. table.insert(events, { usage = evdev.ABS_X, value = v.value })
  40. elseif v.usage == evdev.ABS_Y then
  41. -- uncomment the next two lines and comment the original line
  42. -- for configuring aspect correction, see
  43. -- adjust_for_aspect_ratio() for details and configuration
  44. -- local adj_value = adjust_for_aspect_ratio(v.value)
  45. -- table.insert(events, { usage = evdev.ABS_Y, value = adj_value })
  46. table.insert(events, { usage = evdev.ABS_Y, value = v.value })
  47. elseif v.usage == evdev.BTN_TOUCH then
  48. table.insert(events, { usage = evdev.BTN_LEFT, value = v.value })
  49. elseif v.usage == evdev.BTN_STYLUS then
  50. table.insert(events, { usage = evdev.BTN_RIGHT, value = v.value })
  51. elseif v.usage == evdev.BTN_STYLUS2 then
  52. table.insert(events, { usage = evdev.BTN_MIDDLE, value = v.value })
  53. end
  54. end
  55. -- emit pointer frame, if any
  56. if #events > 0 then pointer_device:append_frame(events) end
  57. -- discard tablet frame
  58. return {}
  59. end
  60. function on_tablet_removed(device)
  61. libinput:log_info("Remove tablet device")
  62. tablet_device = nil
  63. end
  64. function on_pointer_removed(device)
  65. libinput:log_info("Remove pointer device")
  66. pointer_device = nil
  67. end
  68. function setup()
  69. if not pointer_device or not tablet_device then return end
  70. libinput:log_info("Controlling '" .. pointer_device:name() .. "' with '" .. tablet_device:name() .. "'")
  71. -- fetch absinfos from tablet
  72. local absinfo_x = {}
  73. local absinfo_y = {}
  74. for a, b in pairs(tablet_device:absinfos()) do
  75. if a == evdev.ABS_X then absinfo_x = b end
  76. if a == evdev.ABS_Y then absinfo_y = b end
  77. end
  78. -- copy max values for aspect ratio correction later on
  79. maximum_x = absinfo_x.maximum
  80. maximum_y = absinfo_y.maximum
  81. -- copy absinfos to pointer device
  82. pointer_device:set_absinfo(evdev.ABS_X, absinfo_x)
  83. pointer_device:set_absinfo(evdev.ABS_Y, absinfo_y)
  84. -- setup listeners
  85. pointer_device:connect("device-removed", on_pointer_removed)
  86. tablet_device:connect("device-removed", on_tablet_removed)
  87. tablet_device:connect("evdev-frame", on_tablet_frame)
  88. end
  89. function on_new_device(device)
  90. local udev = device:udev_properties()
  91. if udev["ID_INPUT_TABLET"] and not udev["ID_INPUT_TABLET_PAD"] then
  92. libinput:log_info("Found tablet device")
  93. tablet_device = device
  94. setup()
  95. end
  96. if udev["ID_INPUT_MOUSE"] then
  97. libinput:log_info("Found pointer device")
  98. pointer_device = device
  99. setup()
  100. end
  101. end
  102. -- setup listener
  103. libinput:connect("new-evdev-device", on_new_device)