├── Bluetooth.py ├── README.md ├── gamepad ├── Server.py ├── gamepad.py ├── readmeplease.txt └── sdp_record.xml └── keymap.py /Bluetooth.py: -------------------------------------------------------------------------------- 1 | #**********************************************# 2 | ## This was taken from https://github.com/Mqrius/BluePloverPi/blob/master/PiTooth.py 3 | ## and then modified to be used for the amazon firetv. Its identified as a "joystick" 4 | ## rather then a "keyboard" like it was in the original file. 5 | #**********************************************# 6 | 7 | #!/usr/bin/python2.7 8 | #Code fixed from http://www.linuxuser.co.uk/tutorials/emulate-a-bluetooth-keyboard-with-the-raspberry-pi 9 | 10 | #!/usr/bin/python 11 | # 12 | # YAPTB Bluetooth keyboard emulator DBUS Service 13 | # 14 | # Adapted from 15 | # www.linuxuser.co.uk/tutorials/emulate-bluetooth-keyboard-with-the-raspberry-pi 16 | # 17 | # 18 | 19 | #from __future__ import absolute_import, print_function, unicode_literals 20 | from __future__ import absolute_import, print_function 21 | 22 | from optparse import OptionParser, make_option 23 | import os 24 | import sys 25 | import uuid 26 | import dbus 27 | import dbus.service 28 | import dbus.mainloop.glib 29 | import time 30 | import bluetooth 31 | from bluetooth import * 32 | 33 | 34 | import gtk 35 | from dbus.mainloop.glib import DBusGMainLoop 36 | 37 | 38 | # 39 | #define a bluez 5 profile object for our keyboard 40 | # 41 | class BTKbBluezProfile(dbus.service.Object): 42 | fd = -1 43 | 44 | @dbus.service.method("org.bluez.Profile1", 45 | in_signature="", out_signature="") 46 | def Release(self): 47 | print("Release") 48 | mainloop.quit() 49 | 50 | @dbus.service.method("org.bluez.Profile1", 51 | in_signature="", out_signature="") 52 | def Cancel(self): 53 | print("Cancel") 54 | 55 | @dbus.service.method("org.bluez.Profile1", in_signature="oha{sv}", out_signature="") 56 | def NewConnection(self, path, fd, properties): 57 | self.fd = fd.take() 58 | print("NewConnection(%s, %d)" % (path, self.fd)) 59 | for key in properties.keys(): 60 | if key == "Version" or key == "Features": 61 | print(" %s = 0x%04x" % (key, properties[key])) 62 | else: 63 | print(" %s = %s" % (key, properties[key])) 64 | 65 | 66 | 67 | @dbus.service.method("org.bluez.Profile1", in_signature="o", out_signature="") 68 | def RequestDisconnection(self, path): 69 | print("RequestDisconnection(%s)" % (path)) 70 | 71 | if (self.fd > 0): 72 | os.close(self.fd) 73 | self.fd = -1 74 | 75 | def __init__(self, bus, path): 76 | dbus.service.Object.__init__(self, bus, path) 77 | 78 | 79 | # 80 | #create a bluetooth device to emulate a HID keyboard, 81 | # advertize a SDP record using our bluez profile class 82 | # 83 | class BTKbDevice(): 84 | #change these constants 85 | MY_ADDRESS="B8:27:EB:52:59:8E" 86 | MY_DEV_NAME="DeskPi_BTKb" 87 | 88 | #define some constants 89 | P_CTRL = 17 #Service port - must match port configured in SDP record 90 | P_INTR =19 #Service port - must match port configured in SDP record#Interrrupt port 91 | PROFILE_DBUS_PATH="/bluez/yaptb/btkb_profile" #dbus path of the bluez profile we will create 92 | SDP_RECORD_PATH = sys.path[0] + "/sdp_record.xml" #file path of the sdp record to laod 93 | UUID="00001124-0000-1000-8000-00805f9b34fb" 94 | 95 | 96 | def __init__(self): 97 | 98 | print("Setting up BT device") 99 | 100 | self.init_bt_device() 101 | self.init_bluez_profile() 102 | 103 | 104 | #configure the bluetooth hardware device 105 | def init_bt_device(self): 106 | 107 | 108 | print("Configuring for name "+BTKbDevice.MY_DEV_NAME) 109 | 110 | #set the device class to a keybord and set the name 111 | os.system("hciconfig hcio class 0x002540") 112 | os.system("hciconfig hcio name " + BTKbDevice.MY_DEV_NAME) 113 | 114 | #make the device discoverable 115 | os.system("hciconfig hcio piscan") 116 | 117 | 118 | #set up a bluez profile to advertise device capabilities from a loaded service record 119 | def init_bluez_profile(self): 120 | 121 | print("Configuring Bluez Profile") 122 | 123 | #setup profile options 124 | service_record=self.read_sdp_service_record() 125 | 126 | opts = { 127 | "ServiceRecord":service_record, 128 | "Role":"server", 129 | "RequireAuthentication":False, 130 | "RequireAuthorization":True 131 | } 132 | 133 | #retrieve a proxy for the bluez profile interface 134 | bus = dbus.SystemBus() 135 | manager = dbus.Interface(bus.get_object("org.bluez","/org/bluez"), "org.bluez.ProfileManager1") 136 | 137 | profile = BTKbBluezProfile(bus, BTKbDevice.PROFILE_DBUS_PATH) 138 | 139 | manager.RegisterProfile(BTKbDevice.PROFILE_DBUS_PATH, BTKbDevice.UUID,opts) 140 | 141 | print("Profile registered ") 142 | 143 | 144 | #read and return an sdp record from a file 145 | def read_sdp_service_record(self): 146 | 147 | print("Reading service record") 148 | 149 | try: 150 | fh = open(BTKbDevice.SDP_RECORD_PATH, "r") 151 | except: 152 | sys.exit("Could not open the sdp record. Exiting...") 153 | 154 | return fh.read() 155 | 156 | 157 | 158 | #listen for incoming client connections 159 | #ideally this would be handled by the Bluez 5 profile 160 | #but that didn't seem to work 161 | def listen(self): 162 | 163 | print("Waiting for connections") 164 | self.scontrol=BluetoothSocket(L2CAP) 165 | self.sinterrupt=BluetoothSocket(L2CAP) 166 | 167 | #bind these sockets to a port - port zero to select next available 168 | self.scontrol.bind(("",self.P_CTRL)) 169 | self.sinterrupt.bind(("",self.P_INTR )) 170 | 171 | #Start listening on the server sockets 172 | self.scontrol.listen(1) # Limit of 1 connection 173 | self.sinterrupt.listen(1) 174 | 175 | self.ccontrol,cinfo = self.scontrol.accept() 176 | print ("Got a connection on the control channel from " + cinfo[0]) 177 | 178 | self.cinterrupt, cinfo = self.sinterrupt.accept() 179 | print ("Got a connection on the interrupt channel from " + cinfo[0]) 180 | 181 | 182 | #send a string to the bluetooth host machine 183 | def send_string(self,message): 184 | 185 | # print("Sending "+message) 186 | self.cinterrupt.send(message) 187 | 188 | 189 | 190 | #define a dbus service that emulates a bluetooth keyboard 191 | #this will enable different clients to connect to and use 192 | #the service 193 | class BTKbService(dbus.service.Object): 194 | 195 | def __init__(self): 196 | 197 | print("Setting up service") 198 | 199 | #set up as a dbus service 200 | bus_name=dbus.service.BusName("org.yaptb.btkbservice",bus=dbus.SystemBus()) 201 | dbus.service.Object.__init__(self,bus_name,"/org/yaptb/btkbservice") 202 | 203 | #create and setup our device 204 | self.device= BTKbDevice(); 205 | 206 | #start listening for connections 207 | self.device.listen(); 208 | 209 | 210 | @dbus.service.method('org.yaptb.btkbservice', in_signature='yay') 211 | def send_keys(self,modifier_byte,keys): 212 | 213 | cmd_str="" 214 | cmd_str+=chr(0xA1) 215 | cmd_str+=chr(0x01) 216 | cmd_str+=chr(modifier_byte) 217 | cmd_str+=chr(0x00) 218 | 219 | count=0 220 | for key_code in keys: 221 | if(count<6): 222 | cmd_str+=chr(key_code) 223 | count+=1 224 | 225 | self.device.send_string(cmd_str); 226 | 227 | 228 | #main routine 229 | if __name__ == "__main__": 230 | # we an only run as root 231 | if not os.geteuid() == 0: 232 | sys.exit("Only root can run this script") 233 | 234 | DBusGMainLoop(set_as_default=True) 235 | myservice = BTKbService(); 236 | gtk.main() 237 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Raspberry-Pi-Bluetooth-Remote 2 | A bluetooth remote for my amazon fire stick 3 | 4 | 5 | 1. Raspberry Pi 3 B+ so we have built in blue tooth. 6 | 7 | 8 | Roving Networks Bluetooth HID Profile: 9 | 10 | Structure of bluetooth message for joystick found: pg 7 11 | Key mappings: pg 8 12 | 13 | To setup the pi and configuring the bluetooth slave broadcast message 14 | https://www.gadgetdaily.xyz/create-a-cool-sliding-and-scrollable-mobile-menu/ 15 | 16 | 17 | HID values hopefully:(http://www.freebsddiary.org/APC/usb_hid_usages.php) 18 | 19 | help with connecting 20 | http://python-evdev.readthedocs.io/en/latest/tutorial.html 21 | 22 | 23 | 24 | http://www.bluez.org/bluez-5-api-introduction-and-porting-guide/ 25 | http://yetanotherpointlesstechblog.blogspot.com/2016/04/emulating-bluetooth-keyboard-with.html 26 | 27 | bluetoothctl 28 | 29 | 30 | Here the line for the Bluetooth Daemon Configuration (tested on Ubuntu) 31 | In the file /etc/systemd/system/dbus-org.bluez.service 32 | Replace the line ExecStart by: 33 | ExecStart=/usr/lib/bluetooth/bluetoothd -P input 34 | 35 | To advertise as something else: http://www.question-defense.com/tools/class-of-device-bluetooth-cod-list-in-binary-and-hex 36 | http://askubuntu.com/questions/439088/how-to-change-bluetooth-device-class 37 | http://netlab.cs.ucla.edu/wiki/files/class_of_device.pdf 38 | http://bluetooth-pentest.narod.ru/software/bluetooth_class_of_device-service_generator.html 39 | -------------------------------------------------------------------------------- /gamepad/Server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # 3 | # YAPTB Bluetooth keyboard emulator DBUS Service 4 | # 5 | # Adapted from 6 | # www.linuxuser.co.uk/tutorials/emulate-bluetooth-keyboard-with-the-raspberry-pi 7 | # 8 | # 9 | 10 | #from __future__ import absolute_import, print_function, unicode_literals 11 | from __future__ import absolute_import, print_function 12 | 13 | from optparse import OptionParser, make_option 14 | import os 15 | import sys 16 | import uuid 17 | import dbus 18 | import dbus.service 19 | import dbus.mainloop.glib 20 | import time 21 | import bluetooth 22 | from bluetooth import * 23 | 24 | 25 | import gtk 26 | from dbus.mainloop.glib import DBusGMainLoop 27 | 28 | 29 | # 30 | #define a bluez 5 profile object for our keyboard 31 | # 32 | 33 | class BTKbBluezProfile(dbus.service.Object): 34 | fd = -1 35 | 36 | @dbus.service.method("org.bluez.Profile1", 37 | in_signature="", out_signature="") 38 | def Release(self): 39 | print("Release") 40 | mainloop.quit() 41 | 42 | @dbus.service.method("org.bluez.Profile1", 43 | in_signature="", out_signature="") 44 | def Cancel(self): 45 | print("Cancel") 46 | 47 | @dbus.service.method("org.bluez.Profile1", in_signature="oha{sv}", out_signature="") 48 | def NewConnection(self, path, fd, properties): 49 | self.fd = fd.take() 50 | print("NewConnection(%s, %d)" % (path, self.fd)) 51 | for key in properties.keys(): 52 | if key == "Version" or key == "Features": 53 | print(" %s = 0x%04x" % (key, properties[key])) 54 | else: 55 | print(" %s = %s" % (key, properties[key])) 56 | 57 | 58 | 59 | @dbus.service.method("org.bluez.Profile1", in_signature="o", out_signature="") 60 | def RequestDisconnection(self, path): 61 | print("RequestDisconnection(%s)" % (path)) 62 | 63 | if (self.fd > 0): 64 | os.close(self.fd) 65 | self.fd = -1 66 | 67 | def __init__(self, bus, path): 68 | dbus.service.Object.__init__(self, bus, path) 69 | 70 | 71 | # 72 | #create a bluetooth device to emulate a HID keyboard, 73 | # advertize a SDP record using our bluez profile class 74 | # 75 | class BTKbDevice(): 76 | #change these constants 77 | MY_ADDRESS="B8:27:EB:52:59:8E" 78 | MY_DEV_NAME="JaredPi_Controller" 79 | 80 | #define some constants 81 | P_CTRL =17 #Service port - must match port configured in SDP record 82 | P_INTR =19 #Service port - must match port configured in SDP record#Interrrupt port 83 | PROFILE_DBUS_PATH="/bluez/yaptb/btkb_profile" #dbus path of the bluez profile we will create 84 | SDP_RECORD_PATH = sys.path[0] + "/sdp_record.xml" #file path of the sdp record to laod 85 | UUID="00001124-0000-1000-8000-00805f9b34fb" 86 | 87 | 88 | def __init__(self): 89 | 90 | print("Setting up BT device") 91 | 92 | self.init_bt_device() 93 | self.init_bluez_profile() 94 | 95 | 96 | #configure the bluetooth hardware device 97 | def init_bt_device(self): 98 | 99 | 100 | print("Configuring for name "+BTKbDevice.MY_DEV_NAME) 101 | 102 | #set the device class to a keybord and set the name 103 | os.system("hciconfig hcio class 0x504") 104 | os.system("hciconfig hcio name " + BTKbDevice.MY_DEV_NAME) 105 | 106 | #make the device discoverable 107 | os.system("hciconfig hcio piscan") 108 | 109 | 110 | #set up a bluez profile to advertise device capabilities from a loaded service record 111 | def init_bluez_profile(self): 112 | 113 | print("Configuring Bluez Profile") 114 | 115 | #setup profile options 116 | service_record=self.read_sdp_service_record() 117 | 118 | opts = { 119 | "ServiceRecord":service_record, 120 | "Role":"server", 121 | "RequireAuthentication":False, 122 | "RequireAuthorization":False 123 | } 124 | 125 | #retrieve a proxy for the bluez profile interface 126 | bus = dbus.SystemBus() 127 | manager = dbus.Interface(bus.get_object("org.bluez","/org/bluez"), "org.bluez.ProfileManager1") 128 | 129 | profile = BTKbBluezProfile(bus, BTKbDevice.PROFILE_DBUS_PATH) 130 | 131 | manager.RegisterProfile(BTKbDevice.PROFILE_DBUS_PATH, BTKbDevice.UUID,opts) 132 | 133 | print("Profile registered ") 134 | 135 | 136 | #read and return an sdp record from a file 137 | def read_sdp_service_record(self): 138 | 139 | print("Reading service record") 140 | 141 | try: 142 | fh = open(BTKbDevice.SDP_RECORD_PATH, "r") 143 | except: 144 | sys.exit("Could not open the sdp record. Exiting...") 145 | 146 | return fh.read() 147 | 148 | 149 | 150 | #listen for incoming client connections 151 | #ideally this would be handled by the Bluez 5 profile 152 | #but that didn't seem to work 153 | def listen(self): 154 | 155 | print("Waiting for connections") 156 | self.scontrol=BluetoothSocket(L2CAP) 157 | self.sinterrupt=BluetoothSocket(L2CAP) 158 | 159 | #bind these sockets to a port - port zero to select next available 160 | self.scontrol.bind((self.MY_ADDRESS,self.P_CTRL)) 161 | self.sinterrupt.bind((self.MY_ADDRESS,self.P_INTR )) 162 | 163 | #Start listening on the server sockets 164 | self.scontrol.listen(1) # Limit of 1 connection 165 | self.sinterrupt.listen(1) 166 | 167 | self.ccontrol,cinfo = self.scontrol.accept() 168 | print ("Got a connection on the control channel from " + cinfo[0]) 169 | 170 | self.cinterrupt, cinfo = self.sinterrupt.accept() 171 | print ("Got a connection on the interrupt channel from " + cinfo[0]) 172 | 173 | 174 | #send a string to the bluetooth host machine 175 | def send_string(self,message): 176 | 177 | print(type(message)) 178 | self.cinterrupt.send(message) 179 | 180 | 181 | 182 | #define a dbus service that emulates a bluetooth keyboard 183 | #this will enable different clients to connect to and use 184 | #the service 185 | class BTKbService(dbus.service.Object): 186 | 187 | def __init__(self): 188 | 189 | print("Setting up service") 190 | 191 | #set up as a dbus service 192 | bus_name=dbus.service.BusName("org.yaptb.btkbservice",bus=dbus.SystemBus()) 193 | dbus.service.Object.__init__(self,bus_name,"/org/yaptb/btkbservice") 194 | 195 | #create and setup our device 196 | self.device= BTKbDevice(); 197 | 198 | #start listening for connections 199 | self.device.listen(); 200 | 201 | 202 | @dbus.service.method('org.yaptb.btkbservice', in_signature='yay') 203 | def send_pad(self,keys): 204 | 205 | cmd_str="" 206 | 207 | for key_code in keys: 208 | cmd_str+=chr(key_code) 209 | 210 | 211 | self.device.send_string(cmd_str); 212 | 213 | 214 | #main routine 215 | if __name__ == "__main__": 216 | # we an only run as root 217 | if not os.geteuid() == 0: 218 | sys.exit("Only root can run this script") 219 | 220 | DBusGMainLoop(set_as_default=True) 221 | myservice = BTKbService(); 222 | gtk.main() 223 | 224 | -------------------------------------------------------------------------------- /gamepad/gamepad.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | import sys 5 | import dbus 6 | import dbus.service 7 | import dbus.mainloop.glib 8 | import time 9 | import evdev # used to get input from the keyboard 10 | from evdev import * 11 | #import keymap # used to map evdev input to hid keodes 12 | 13 | class Gamepad(): 14 | 15 | def __init__(self): 16 | 17 | 18 | D_Pad_Left = 0x93 19 | 20 | D_Pad_Right = 0x92 21 | 22 | D_Pad_Down = 0x91 23 | 24 | D_Pad_Up = 0x90 25 | 26 | self.state = [ 27 | 0xFD, #Raw Report mode. 28 | 0x06, #Size of 29 | 0x00, #Buttons being pressed 30 | 0x00, #X1 31 | 0x00, #send_pad Y1 32 | 0x00, #X2 33 | 0x00, #Y2 34 | ] 35 | 36 | 37 | print "setting up DBus Client" 38 | 39 | self.bus = dbus.SystemBus() 40 | self.btkservice = self.bus.get_object('org.yaptb.btkbservice', '/org/yaptb/btkbservice') 41 | self.iface = dbus.Interface(self.btkservice, 'org.yaptb.btkbservice') 42 | 43 | 44 | #forward keyboard events to the dbus service 45 | def send_input(self): 46 | 47 | bin_str="" 48 | element=self.state[2] 49 | for bit in element: 50 | bin_str += str(bit) 51 | 52 | 53 | 54 | self.iface.send_pad(self.state) 55 | 56 | 57 | 58 | if __name__ == "__main__": 59 | 60 | print "Setting up keyboard" 61 | 62 | #kb = Keyboard() 63 | 64 | print "starting event loop" 65 | #kb.event_loop() 66 | 67 | game = Gamepad() 68 | 69 | for i in range(0,100,1): 70 | time.sleep(4) 71 | self.state[2] = 0x91 72 | game.send_input() 73 | self.state[2] = 0x00 74 | game.send_input() 75 | 76 | 77 | -------------------------------------------------------------------------------- /gamepad/readmeplease.txt: -------------------------------------------------------------------------------- 1 | 2 | ####################################################################################################################################################################################################################################################################################################################################################### 3 | This is the actual gamepad code.My documentation of setting up the bluetooth was questionable at best 4 | so ill try to give the gist of what 5 | I hope to accomplish intially and add things of interest as I go. I plan on modifying the code 6 | from the example that inspired this, these modifications will primarily come in the form of me 7 | replacing the keyboard class with a gamepad class.This abstraction will be responsable for sending 8 | messages in the joystick/gamepad forat to my amazon fire tv via gesture controlled commands(Skywriter). 9 | I will also makes some changes to the advertising of the pi so it appears as a joystick instead of a 10 | keyboard. The stack structure for a joystick bluetooth message is also different from its 11 | keyboard counterpart. 12 | -------------------------------------------------------------------------------- /gamepad/sdp_record.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /keymap.py: -------------------------------------------------------------------------------- 1 | keytable = { 2 | "KEY_RESERVED" : 0, 3 | "KEY_ESC" : 41, 4 | "KEY_1" : 30, 5 | "KEY_2" : 31, 6 | "KEY_3" : 32, 7 | "KEY_4" : 33, 8 | "KEY_5" : 34, 9 | "KEY_6" : 35, 10 | "KEY_7" : 36, 11 | "KEY_8" : 37, 12 | "KEY_9" : 38, 13 | "KEY_0" : 39, 14 | "KEY_MINUS" : 45, 15 | "KEY_EQUAL" : 46, 16 | "KEY_BACKSPACE" : 42, 17 | "KEY_TAB" : 43, 18 | "KEY_Q" : 20, 19 | "KEY_W" : 26, 20 | "KEY_E" : 8, 21 | "KEY_R" : 21, 22 | "KEY_T" : 23, 23 | "KEY_Y" : 28, 24 | "KEY_U" : 24, 25 | "KEY_I" : 12, 26 | "KEY_O" : 18, 27 | "KEY_P" : 19, 28 | "KEY_LEFTBRACE" : 47, 29 | "KEY_RIGHTBRACE" : 48, 30 | "KEY_ENTER" : 40, 31 | "KEY_LEFTCTRL" : 224, 32 | "KEY_A" : 4, 33 | "KEY_S" : 22, 34 | "KEY_D" : 7, 35 | "KEY_F" : 9, 36 | "KEY_G" : 10, 37 | "KEY_H" : 11, 38 | "KEY_J" : 13, 39 | "KEY_K" : 14, 40 | "KEY_L" : 15, 41 | "KEY_SEMICOLON" : 51, 42 | "KEY_APOSTROPHE" : 52, 43 | "KEY_GRAVE" : 53, 44 | "KEY_LEFTSHIFT" : 225, 45 | "KEY_BACKSLASH" : 50, 46 | "KEY_Z" : 29, 47 | "KEY_X" : 27, 48 | "KEY_C" : 6, 49 | "KEY_V" : 25, 50 | "KEY_B" : 5, 51 | "KEY_N" : 17, 52 | "KEY_M" : 16, 53 | "KEY_COMMA" : 54, 54 | "KEY_DOT" : 55, 55 | "KEY_SLASH" : 56, 56 | "KEY_RIGHTSHIFT" : 229, 57 | "KEY_KPASTERISK" : 85, 58 | "KEY_LEFTALT" : 226, 59 | "KEY_SPACE" : 44, 60 | "KEY_CAPSLOCK" : 57, 61 | "KEY_F1" : 58, 62 | "KEY_F2" : 59, 63 | "KEY_F3" : 60, 64 | "KEY_F4" : 61, 65 | "KEY_F5" : 62, 66 | "KEY_F6" : 63, 67 | "KEY_F7" : 64, 68 | "KEY_F8" : 65, 69 | "KEY_F9" : 66, 70 | "KEY_F10" : 67, 71 | "KEY_NUMLOCK" : 83, 72 | "KEY_SCROLLLOCK" : 71, 73 | "KEY_KP7" : 95, 74 | "KEY_KP8" : 96, 75 | "KEY_KP9" : 97, 76 | "KEY_KPMINUS" : 86, 77 | "KEY_KP4" : 92, 78 | "KEY_KP5" : 93, 79 | "KEY_KP6" : 94, 80 | "KEY_KPPLUS" : 87, 81 | "KEY_KP1" : 89, 82 | "KEY_KP2" : 90, 83 | "KEY_KP3" : 91, 84 | "KEY_KP0" : 98, 85 | "KEY_KPDOT" : 99, 86 | "KEY_ZENKAKUHANKAKU" : 148, 87 | "KEY_102ND" : 100, 88 | "KEY_F11" : 68, 89 | "KEY_F12" : 69, 90 | "KEY_RO" : 135, 91 | "KEY_KATAKANA" : 146, 92 | "KEY_HIRAGANA" : 147, 93 | "KEY_HENKAN" : 138, 94 | "KEY_KATAKANAHIRAGANA" : 136, 95 | "KEY_MUHENKAN" : 139, 96 | "KEY_KPJPCOMMA" : 140, 97 | "KEY_KPENTER" : 88, 98 | "KEY_RIGHTCTRL" : 228, 99 | "KEY_KPSLASH" : 84, 100 | "KEY_SYSRQ" : 70, 101 | "KEY_RIGHTALT" : 230, 102 | "KEY_HOME" : 74, 103 | "KEY_UP" : 82, 104 | "KEY_PAGEUP" : 75, 105 | "KEY_LEFT" : 80, 106 | "KEY_RIGHT" : 79, 107 | "KEY_END" : 77, 108 | "KEY_DOWN" : 81, 109 | "KEY_PAGEDOWN" : 78, 110 | "KEY_INSERT" : 73, 111 | "KEY_DELETE" : 76, 112 | "KEY_MUTE" : 239, 113 | "KEY_VOLUMEDOWN" : 238, 114 | "KEY_VOLUMEUP" : 237, 115 | "KEY_POWER" : 102, 116 | "KEY_KPEQUAL" : 103, 117 | "KEY_PAUSE" : 72, 118 | "KEY_KPCOMMA" : 133, 119 | "KEY_HANGEUL" : 144, 120 | "KEY_HANJA" : 145, 121 | "KEY_YEN" : 137, 122 | "KEY_LEFTMETA" : 227, 123 | "KEY_RIGHTMETA" : 231, 124 | "KEY_COMPOSE" : 101, 125 | "KEY_STOP" : 243, 126 | "KEY_AGAIN" : 121, 127 | "KEY_PROPS" : 118, 128 | "KEY_UNDO" : 122, 129 | "KEY_FRONT" : 119, 130 | "KEY_COPY" : 124, 131 | "KEY_OPEN" : 116, 132 | "KEY_PASTE" : 125, 133 | "KEY_FIND" : 244, 134 | "KEY_CUT" : 123, 135 | "KEY_HELP" : 117, 136 | "KEY_CALC" : 251, 137 | "KEY_SLEEP" : 248, 138 | "KEY_WWW" : 240, 139 | "KEY_COFFEE" : 249, 140 | "KEY_BACK" : 241, 141 | "KEY_FORWARD" : 242, 142 | "KEY_EJECTCD" : 236, 143 | "KEY_NEXTSONG" : 235, 144 | "KEY_PLAYPAUSE" : 232, 145 | "KEY_PREVIOUSSONG" : 234, 146 | "KEY_STOPCD" : 233, 147 | "KEY_REFRESH" : 250, 148 | "KEY_EDIT" : 247, 149 | "KEY_SCROLLUP" : 245, 150 | "KEY_SCROLLDOWN" : 246, 151 | "KEY_F13" : 104, 152 | "KEY_F14" : 105, 153 | "KEY_F15" : 106, 154 | "KEY_F16" : 107, 155 | "KEY_F17" : 108, 156 | "KEY_F18" : 109, 157 | "KEY_F19" : 110, 158 | "KEY_F20" : 111, 159 | "KEY_F21" : 112, 160 | "KEY_F22" : 113, 161 | "KEY_F23" : 114, 162 | "KEY_F24" : 115 163 | } 164 | 165 | # Map modifier keys to array element in the bit array 166 | modkeys = { 167 | "KEY_RIGHTMETA" : 0, 168 | "KEY_RIGHTALT" : 1, 169 | "KEY_RIGHTSHIFT" : 2, 170 | "KEY_RIGHTCTRL" : 3, 171 | "KEY_LEFTMETA" : 4, 172 | "KEY_LEFTALT": 5, 173 | "KEY_LEFTSHIFT": 6, 174 | "KEY_LEFTCTRL": 7 175 | } 176 | 177 | def convert(evdev_keycode): 178 | return keytable[evdev_keycode] 179 | 180 | def modkey(evdev_keycode): 181 | if evdev_keycode in modkeys: 182 | return modkeys[evdev_keycode] 183 | else: 184 | return -1 # Return an invalid array element 185 | --------------------------------------------------------------------------------