kb_client.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. #!/usr/bin/python3
  2. #
  3. # Thanhle Bluetooth keyboard emulation service
  4. # keyboard copy client.
  5. # Reads local key events and forwards them to the btk_server DBUS service
  6. #
  7. import os # used to all external commands
  8. import sys # used to exit the script
  9. import dbus
  10. import dbus.service
  11. import dbus.mainloop.glib
  12. import time
  13. import evdev # used to get input from the keyboard
  14. from evdev import ecodes, InputDevice, list_devices
  15. import keymap # used to map evdev input to hid keodes
  16. import select
  17. import logging
  18. from logging import error
  19. logging.basicConfig(level=logging.DEBUG)
  20. DBUS_OBJ_BTK_NAME = 'org.thanhle.btkbservice'
  21. DBUS_OBJ_BTK_PATH = '/org/thanhle/btkbservice'
  22. # Define a client to listen to local key events
  23. class Keyboard():
  24. def __init__(self):
  25. # the structure for a bt keyboard input report (size is 10 bytes)
  26. self.state = [
  27. 0xA1, # this is an input report
  28. 0x01, # Usage report = Keyboard
  29. # Bit array for Modifier keys
  30. [0, # Right GUI - Windows Key
  31. 0, # Right ALT
  32. 0, # Right Shift
  33. 0, # Right Control
  34. 0, # Left GUI
  35. 0, # Left ALT
  36. 0, # Left Shift
  37. 0], # Left Control
  38. 0x00, # Vendor reserved
  39. 0x00, # rest is space for 6 keys
  40. 0x00,
  41. 0x00,
  42. 0x00,
  43. 0x00,
  44. 0x00]
  45. print("setting up DBus Client")
  46. self.bus = dbus.SystemBus()
  47. self.btkservice = self.bus.get_object(DBUS_OBJ_BTK_NAME, DBUS_OBJ_BTK_PATH)
  48. self.iface = dbus.Interface(self.btkservice, DBUS_OBJ_BTK_NAME)
  49. print("waiting for keyboard")
  50. self.wait_keyboards()
  51. def wait_keyboards(self, device_dir='/dev/input'):
  52. have_dev = False
  53. # keep trying to key a keyboard
  54. while have_dev == False:
  55. devices = [InputDevice(path) for path in list_devices(device_dir)]
  56. self.keyboards = [dev for dev in devices if "Keyboard" in dev.name]
  57. if self.keyboards:
  58. for keyboard in self.keyboards:
  59. print("find keyboard: ", keyboard.name)
  60. break
  61. print("Keyboard not found, waiting 3 seconds and retrying")
  62. time.sleep(3)
  63. def change_state(self, event):
  64. evdev_code = ecodes.KEY[event.code]
  65. modkey_element = keymap.modkey(evdev_code)
  66. if modkey_element > 0:
  67. if self.state[2][modkey_element] == 0:
  68. self.state[2][modkey_element] = 1
  69. else:
  70. self.state[2][modkey_element] = 0
  71. else:
  72. # Get the keycode of the key
  73. hex_key = keymap.convert(ecodes.KEY[event.code])
  74. if hex_key == -1:
  75. return
  76. # Loop through elements 4 to 9 of the inport report structure
  77. for i in range(4, 10):
  78. if self.state[i] == hex_key and event.value == 0:
  79. # Code 0 so we need to depress it
  80. self.state[i] = 0x00
  81. elif self.state[i] == 0x00 and event.value == 1:
  82. # if the current space if empty and the key is being pressed
  83. self.state[i] = hex_key
  84. break
  85. # poll for keyboard events
  86. def event_loop(self):
  87. fd_to_device = {dev.fd: dev for dev in self.keyboards}
  88. while True:
  89. read_list, _, _ = select.select(fd_to_device, [], [])
  90. for fd in read_list:
  91. for event in fd_to_device[fd].read():
  92. # only bother if we hit a key and its an up or down event
  93. if event.type == ecodes.EV_KEY and event.value < 2:
  94. self.change_state(event)
  95. print("sending key: %s, status: %s, key board code: %d, bluetooth code: %d" % (
  96. ecodes.KEY[event.code],
  97. ("UP" if event.value == 0 else "DOWN"),
  98. event.code,
  99. self.state[4]
  100. ))
  101. self.send_input()
  102. # forward keyboard events to the dbus service
  103. def send_input(self):
  104. modifier_bin_str = ""
  105. element = self.state[2]
  106. for bit in element:
  107. modifier_bin_str += str(bit)
  108. try:
  109. self.iface.send_keys(int(modifier_bin_str, 2), self.state[4:10])
  110. except dbus.exceptions.DBusException as err:
  111. error(err)
  112. if __name__ == "__main__":
  113. print("Setting up keyboard")
  114. kb = Keyboard()
  115. print("starting event loop")
  116. kb.event_loop()