123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- #!/usr/bin/python3
- #
- # Thanhle Bluetooth keyboard emulation service
- # keyboard copy client.
- # Reads local key events and forwards them to the btk_server DBUS service
- #
- import os # used to all external commands
- import sys # used to exit the script
- import dbus
- import dbus.service
- import dbus.mainloop.glib
- import time
- import evdev # used to get input from the keyboard
- from evdev import ecodes, InputDevice, list_devices
- import keymap # used to map evdev input to hid keodes
- import select
- import logging
- from logging import error
- logging.basicConfig(level=logging.DEBUG)
- DBUS_OBJ_BTK_NAME = 'org.thanhle.btkbservice'
- DBUS_OBJ_BTK_PATH = '/org/thanhle/btkbservice'
- # Define a client to listen to local key events
- class Keyboard():
- def __init__(self):
- # the structure for a bt keyboard input report (size is 10 bytes)
- self.state = [
- 0xA1, # this is an input report
- 0x01, # Usage report = Keyboard
- # Bit array for Modifier keys
- [0, # Right GUI - Windows Key
- 0, # Right ALT
- 0, # Right Shift
- 0, # Right Control
- 0, # Left GUI
- 0, # Left ALT
- 0, # Left Shift
- 0], # Left Control
- 0x00, # Vendor reserved
- 0x00, # rest is space for 6 keys
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x00]
- print("setting up DBus Client")
- self.bus = dbus.SystemBus()
- self.btkservice = self.bus.get_object(DBUS_OBJ_BTK_NAME, DBUS_OBJ_BTK_PATH)
- self.iface = dbus.Interface(self.btkservice, DBUS_OBJ_BTK_NAME)
- print("waiting for keyboard")
- self.wait_keyboards()
- def wait_keyboards(self, device_dir='/dev/input'):
- have_dev = False
- # keep trying to key a keyboard
- while have_dev == False:
- devices = [InputDevice(path) for path in list_devices(device_dir)]
- self.keyboards = [dev for dev in devices if "Keyboard" in dev.name]
- if self.keyboards:
- for keyboard in self.keyboards:
- print("find keyboard: ", keyboard.name)
- break
- print("Keyboard not found, waiting 3 seconds and retrying")
- time.sleep(3)
- def change_state(self, event):
- evdev_code = ecodes.KEY[event.code]
- modkey_element = keymap.modkey(evdev_code)
- if modkey_element > 0:
- if self.state[2][modkey_element] == 0:
- self.state[2][modkey_element] = 1
- else:
- self.state[2][modkey_element] = 0
- else:
- # Get the keycode of the key
- hex_key = keymap.convert(ecodes.KEY[event.code])
- if hex_key == -1:
- return
- # Loop through elements 4 to 9 of the inport report structure
- for i in range(4, 10):
- if self.state[i] == hex_key and event.value == 0:
- # Code 0 so we need to depress it
- self.state[i] = 0x00
- elif self.state[i] == 0x00 and event.value == 1:
- # if the current space if empty and the key is being pressed
- self.state[i] = hex_key
- break
- # poll for keyboard events
- def event_loop(self):
- fd_to_device = {dev.fd: dev for dev in self.keyboards}
- while True:
- read_list, _, _ = select.select(fd_to_device, [], [])
- for fd in read_list:
- for event in fd_to_device[fd].read():
- # only bother if we hit a key and its an up or down event
- if event.type == ecodes.EV_KEY and event.value < 2:
- self.change_state(event)
- print("sending key: %s, status: %s, key board code: %d, bluetooth code: %d" % (
- ecodes.KEY[event.code],
- ("UP" if event.value == 0 else "DOWN"),
- event.code,
- self.state[4]
- ))
- self.send_input()
- # forward keyboard events to the dbus service
- def send_input(self):
- modifier_bin_str = ""
- element = self.state[2]
- for bit in element:
- modifier_bin_str += str(bit)
- try:
- self.iface.send_keys(int(modifier_bin_str, 2), self.state[4:10])
- except dbus.exceptions.DBusException as err:
- error(err)
- if __name__ == "__main__":
- print("Setting up keyboard")
- kb = Keyboard()
- print("starting event loop")
- kb.event_loop()
|