_nixmouse.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. # -*- coding: utf-8 -*-
  2. import struct
  3. from subprocess import check_output
  4. import re
  5. from ._nixcommon import EV_KEY, EV_REL, EV_MSC, EV_SYN, EV_ABS, aggregate_devices, ensure_root
  6. from ._mouse_event import ButtonEvent, WheelEvent, MoveEvent, LEFT, RIGHT, MIDDLE, X, X2, UP, DOWN
  7. import ctypes
  8. import ctypes.util
  9. from ctypes import c_uint32, c_uint, c_int, byref
  10. display = None
  11. window = None
  12. x11 = None
  13. def build_display():
  14. global display, window, x11
  15. if display and window and x11: return
  16. x11 = ctypes.cdll.LoadLibrary(ctypes.util.find_library('X11'))
  17. # Required because we will have multiple threads calling x11,
  18. # such as the listener thread and then main using "move_to".
  19. x11.XInitThreads()
  20. display = x11.XOpenDisplay(None)
  21. # Known to cause segfault in Fedora 23 64bits, no known workarounds.
  22. # http://stackoverflow.com/questions/35137007/get-mouse-position-on-linux-pure-python
  23. window = x11.XDefaultRootWindow(display)
  24. def get_position():
  25. build_display()
  26. root_id, child_id = c_uint32(), c_uint32()
  27. root_x, root_y, win_x, win_y = c_int(), c_int(), c_int(), c_int()
  28. mask = c_uint()
  29. ret = x11.XQueryPointer(display, c_uint32(window), byref(root_id), byref(child_id),
  30. byref(root_x), byref(root_y),
  31. byref(win_x), byref(win_y), byref(mask))
  32. return root_x.value, root_y.value
  33. def move_to(x, y):
  34. build_display()
  35. x11.XWarpPointer(display, None, window, 0, 0, 0, 0, x, y)
  36. x11.XFlush(display)
  37. REL_X = 0x00
  38. REL_Y = 0x01
  39. REL_Z = 0x02
  40. REL_HWHEEL = 0x06
  41. REL_WHEEL = 0x08
  42. ABS_X = 0x00
  43. ABS_Y = 0x01
  44. BTN_MOUSE = 0x110
  45. BTN_LEFT = 0x110
  46. BTN_RIGHT = 0x111
  47. BTN_MIDDLE = 0x112
  48. BTN_SIDE = 0x113
  49. BTN_EXTRA = 0x114
  50. button_by_code = {
  51. BTN_LEFT: LEFT,
  52. BTN_RIGHT: RIGHT,
  53. BTN_MIDDLE: MIDDLE,
  54. BTN_SIDE: X,
  55. BTN_EXTRA: X2,
  56. }
  57. code_by_button = {button: code for code, button in button_by_code.items()}
  58. device = None
  59. def build_device():
  60. global device
  61. if device: return
  62. ensure_root()
  63. device = aggregate_devices('mouse')
  64. init = build_device
  65. def listen(queue):
  66. build_device()
  67. while True:
  68. time, type, code, value, device_id = device.read_event()
  69. if type == EV_SYN or type == EV_MSC:
  70. continue
  71. event_type = None
  72. arg = None
  73. if type == EV_KEY:
  74. event = ButtonEvent(DOWN if value else UP, button_by_code.get(code, '?'), time)
  75. elif type == EV_REL:
  76. value, = struct.unpack('i', struct.pack('I', value))
  77. if code == REL_WHEEL:
  78. event = WheelEvent(value, time)
  79. elif code in (REL_X, REL_Y):
  80. x, y = get_position()
  81. event = MoveEvent(x, y, time)
  82. if event is None:
  83. # Unknown event type.
  84. continue
  85. queue.put(event)
  86. def press(button=LEFT):
  87. build_device()
  88. device.write_event(EV_KEY, code_by_button[button], 0x01)
  89. def release(button=LEFT):
  90. build_device()
  91. device.write_event(EV_KEY, code_by_button[button], 0x00)
  92. def move_relative(x, y):
  93. build_device()
  94. # Note relative events are not in terms of pixels, but millimeters.
  95. if x < 0:
  96. x += 2**32
  97. if y < 0:
  98. y += 2**32
  99. device.write_event(EV_REL, REL_X, x)
  100. device.write_event(EV_REL, REL_Y, y)
  101. def wheel(delta=1):
  102. build_device()
  103. if delta < 0:
  104. delta += 2**32
  105. device.write_event(EV_REL, REL_WHEEL, delta)
  106. if __name__ == '__main__':
  107. #listen(print)
  108. move_to(100, 200)