mouse_client.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. #!/usr/bin/python3
  2. #
  3. # YAPTB Bluetooth keyboard emulation service
  4. # keyboard copy client.
  5. # Reads local key events and forwards them to the btk_server DBUS service
  6. #
  7. # Adapted from www.linuxuser.co.uk/tutorials/emulate-a-bluetooth-keyboard-with-the-raspberry-pi
  8. #
  9. #
  10. import os # used to all external commands
  11. import sys # used to exit the script
  12. import dbus
  13. import dbus.service
  14. import dbus.mainloop.glib
  15. import time
  16. import evdev # used to get input from the keyboard
  17. from evdev import *
  18. import pyudev
  19. import re
  20. import logging
  21. from logging import debug, info, warning, error
  22. import errno
  23. import os
  24. import sys
  25. import dbus
  26. import time
  27. import evdev
  28. from evdev import ecodes
  29. from select import select
  30. import logging
  31. from logging import debug, info, warning, error
  32. import pyudev
  33. import re
  34. import functools
  35. import errno
  36. import socket
  37. from configparser import ConfigParser
  38. logging.basicConfig(level=logging.DEBUG)
  39. class BluetoothDevice:
  40. by_index = {}
  41. by_addr = {}
  42. current = 0
  43. connecting_sockets = []
  44. mouse_delay = 20 / 1000
  45. mouse_speed = 1
  46. def __init__(self):
  47. self.bus = dbus.SystemBus()
  48. self.btkservice = self.bus.get_object(
  49. 'org.yaptb.btkbservice', '/org/yaptb/btkbservice')
  50. self.iface = self.bus.Interface(btkservice, 'org.yaptb.btkbservice')
  51. self.mouse_delay = 20 / 1000
  52. self.mouse_speed = 1
  53. def __str__(self):
  54. return "%s%d: %s %s" % ('*' if BluetoothDevice.current == self.index else ' ', self.index, self.addr, self.state)
  55. def send_input(self, ir):
  56. try:
  57. print("send")
  58. self.iface.send(0, bytes(ir))
  59. except OSError as err:
  60. error(err)
  61. @staticmethod
  62. def mouse_delay():
  63. return BluetoothDevice.mouse_delay
  64. @staticmethod
  65. def mouse_speed():
  66. return BluetoothDevice.mouse_speed
  67. @staticmethod
  68. def send_current(ir):
  69. BluetoothDevice.send_input(0, ir)
  70. @staticmethod
  71. def set_current():
  72. BluetoothDevice.send_input([0xA2, 2, 0, 0, 0, 0])
  73. class InputDevice():
  74. inputs = []
  75. @staticmethod
  76. def init():
  77. context = pyudev.Context()
  78. devs = context.list_devices(subsystem="input")
  79. InputDevice.monitor = pyudev.Monitor.from_netlink(context)
  80. InputDevice.monitor.filter_by(subsystem='input')
  81. InputDevice.monitor.start()
  82. for d in [*devs]:
  83. InputDevice.add_device(d)
  84. @staticmethod
  85. def add_device(dev):
  86. if dev.device_node == None or not re.match(".*/event\\d+", dev.device_node):
  87. return
  88. try:
  89. if "ID_INPUT_MOUSE" in dev.properties:
  90. print("detected mouse: " + dev.device_node)
  91. InputDevice.inputs.append(MouseInput(dev.device_node))
  92. except OSError:
  93. error("Failed to connect to %s", dev.device_node)
  94. @staticmethod
  95. def remove_device(dev):
  96. if dev.device_node == None or not re.match(".*/event\\d+", dev.device_node):
  97. return
  98. InputDevice.inputs = list(
  99. filter(lambda i: i.device_node != dev.device_node, InputDevice.inputs))
  100. info("Disconnected %s", dev)
  101. @staticmethod
  102. def set_leds_all(ledvalue):
  103. for dev in InputDevice.inputs:
  104. dev.set_leds(ledvalue)
  105. @staticmethod
  106. def grab(on):
  107. if on:
  108. debug("Grabbing all input devices")
  109. for dev in InputDevice.inputs:
  110. dev.device.grab()
  111. else:
  112. debug("Releasing all input devices")
  113. for dev in InputDevice.inputs:
  114. dev.device.ungrab()
  115. def __init__(self, device_node):
  116. self.device_node = device_node
  117. self.device = evdev.InputDevice(device_node)
  118. self.device.grab()
  119. info("Connected %s", self)
  120. def fileno(self):
  121. return self.device.fd
  122. def __str__(self):
  123. return "%s@%s (%s)" % (self.__class__.__name__, self.device_node, self.device.name)
  124. class MouseInput(InputDevice):
  125. def __init__(self, device_node):
  126. super().__init__(device_node)
  127. self.state = [0xA1, 2, 0, 0, 0, 0]
  128. self.x = 0
  129. self.y = 0
  130. self.z = 0
  131. self.change = False
  132. self.last = 0
  133. self.bus = dbus.SystemBus()
  134. self.btkservice = self.bus.get_object(
  135. 'org.yaptb.btkbservice', '/org/yaptb/btkbservice')
  136. self.iface = dbus.Interface(self.btkservice, 'org.yaptb.btkbservice')
  137. self.mouse_delay = 20 / 1000
  138. self.mouse_speed = 1
  139. def send_current(self, ir):
  140. try:
  141. print("send_mouse")
  142. self.iface.send_mouse(0, bytes(ir))
  143. except OSError as err:
  144. error(err)
  145. def change_state(self, event):
  146. if event.type == ecodes.EV_SYN:
  147. current = time.monotonic()
  148. diff = 20/1000
  149. if current - self.last < diff and not self.change:
  150. return
  151. self.last = current
  152. speed = 1
  153. self.state[3] = min(127, max(-127, int(self.x * speed))) & 255
  154. self.state[4] = min(127, max(-127, int(self.y * speed))) & 255
  155. self.state[5] = min(127, max(-127, self.z)) & 255
  156. self.x = 0
  157. self.y = 0
  158. self.z = 0
  159. self.change = False
  160. self.send_current(self.state)
  161. if event.type == ecodes.EV_KEY:
  162. debug("Key event %s %d", ecodes.BTN[event.code], event.value)
  163. self.change = True
  164. if event.code >= 272 and event.code <= 276 and event.value < 2:
  165. button_no = event.code - 272
  166. if event.value == 1:
  167. self.state[2] |= 1 << button_no
  168. else:
  169. self.state[2] &= ~(1 << button_no)
  170. if event.type == ecodes.EV_REL:
  171. if event.code == 0:
  172. self.x += event.value
  173. if event.code == 1:
  174. self.y += event.value
  175. if event.code == 8:
  176. self.z += event.value
  177. def get_info(self):
  178. print("hello")
  179. def set_leds(self, ledvalue):
  180. pass
  181. def event_loop():
  182. while True:
  183. for i in InputDevice.inputs:
  184. try:
  185. for event in i.device.read():
  186. i.change_state(event)
  187. except OSError as err:
  188. if err.errno == errno.ENODEV:
  189. InputDevice.remove_device(i)
  190. warning(err)
  191. if __name__ == "__main__":
  192. state = [0xA1, 2, 0, 0, 0, 0]
  193. x = 0
  194. y = 0
  195. z = 0
  196. change = False
  197. last = 0
  198. # context = pyudev.Context()
  199. # devs = context.list_devices(subsystem="input")
  200. # monitor = pyudev.Monitor.from_netlink(context)
  201. # monitor.filter_by(subsystem='input')
  202. # monitor.start()
  203. # for dev in [*devs]:
  204. # if dev.device_node == None or not re.match(".*/event\\d+", dev.device_node):
  205. # continue
  206. # try:
  207. # if "ID_INPUT_MOUSE" in dev.properties:
  208. # print("Mouse detected: " + dev.device_node)
  209. # except OSError:
  210. # error("Failed to connect to %s", dev.device_node)
  211. InputDevice.init()
  212. while True:
  213. desctiptors = [*InputDevice.inputs, InputDevice.monitor]
  214. r = select(desctiptors, [], [])
  215. for i in InputDevice.inputs:
  216. try:
  217. for event in i.device.read():
  218. print(event)
  219. i.change_state(event)
  220. except OSError as err:
  221. warning(err)