Advertisement
Guest User

/opt/retropie/supplementary/runcommand/joy2key.py

a guest
Jun 29th, 2017
86
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.21 KB | None | 0 0
  1. #!/usr/bin/python
  2.  
  3. # This file is part of The RetroPie Project
  4. #
  5. # The RetroPie Project is the legal property of its developers, whose names are
  6. # too numerous to list here. Please refer to the COPYRIGHT.md file distributed with this source.
  7. #
  8. # See the LICENSE.md file at the top-level directory of this distribution and
  9. # at https://raw.githubusercontent.com/RetroPie/RetroPie-Setup/master/LICENSE.md
  10. #
  11.  
  12. import os, sys, struct, time, fcntl, termios, signal
  13. import curses, errno, re
  14. from pyudev import Context
  15.  
  16.  
  17. # struct js_event {
  18. # __u32 time; /* event timestamp in milliseconds */
  19. # __s16 value; /* value */
  20. # __u8 type; /* event type */
  21. # __u8 number; /* axis/button number */
  22. # };
  23.  
  24. JS_MIN = -32768
  25. JS_MAX = 32768
  26. JS_REP = 0.20
  27.  
  28. JS_THRESH = 0.75
  29.  
  30. JS_EVENT_BUTTON = 0x01
  31. JS_EVENT_AXIS = 0x02
  32. JS_EVENT_INIT = 0x80
  33.  
  34. CONFIG_DIR = '/opt/retropie/configs/'
  35. RETROARCH_CFG = CONFIG_DIR + 'all/retroarch.cfg'
  36.  
  37. def ini_get(key, cfg_file):
  38. pattern = r'[ |\t]*' + key + r'[ |\t]*=[ |\t]*'
  39. value_m = r'"*([^"\|\r]*)"*'
  40. value = ''
  41. with open(cfg_file, 'r') as ini_file:
  42. for line in ini_file:
  43. if re.match(pattern, line):
  44. value = re.sub(pattern + value_m + '.*\n', r'\1', line)
  45. break
  46. return value
  47.  
  48. def get_btn_num(btn, cfg):
  49. num = ini_get('input_' + btn + '_btn', cfg)
  50. if num: return num
  51. num = ini_get('input_player1_' + btn + '_btn', cfg)
  52. if num: return num
  53. return ''
  54.  
  55. def get_button_codes(dev_path):
  56. js_cfg_dir = CONFIG_DIR + 'all/retroarch-joypads/'
  57. js_cfg = ''
  58. dev_name = ''
  59. js_cfg = ''
  60. dev_name = ''
  61.  
  62. # getting joystick name
  63. for device in Context().list_devices(DEVNAME=dev_path):
  64. dev_name_file = device.get('DEVPATH')
  65. dev_name_file = '/sys' + os.path.dirname(dev_name_file) + '/name'
  66. for line in open(dev_name_file, 'r'):
  67. dev_name = line.rstrip('\n')
  68. break
  69. if not dev_name:
  70. return default_button_codes
  71.  
  72. # getting retroarch config file for joystick
  73. for f in os.listdir(js_cfg_dir):
  74. if f.endswith('.cfg'):
  75. if ini_get('input_device', js_cfg_dir + f) == dev_name:
  76. js_cfg = js_cfg_dir + f
  77. break
  78. if not js_cfg:
  79. js_cfg = RETROARCH_CFG
  80.  
  81. # getting configs for buttons A, B, X and Y
  82. btn_num = {}
  83. biggest_num = 0
  84. i = 0
  85. for btn in 'a', 'b', 'x', 'y':
  86. i += 1
  87. if i > len(default_button_codes):
  88. break
  89. btn_num[btn] = get_btn_num(btn, js_cfg)
  90. try:
  91. btn_num[btn] = int(btn_num[btn])
  92. except ValueError:
  93. return default_button_codes
  94. if btn_num[btn] > biggest_num:
  95. biggest_num = btn_num[btn]
  96.  
  97. # building the button codes list
  98. btn_codes = [''] * (biggest_num + 1)
  99. i = 0
  100. for btn in 'a', 'b', 'x', 'y':
  101. btn_codes[btn_num[btn]] = default_button_codes[i]
  102. i += 1
  103. if i >= len(default_button_codes): break
  104.  
  105. # if button A is <enter> and menu_swap_ok_cancel_buttons is true, swap buttons A and B functions
  106. if btn_codes[btn_num['a']] == '\n' and ini_get('menu_swap_ok_cancel_buttons', RETROARCH_CFG) == 'true':
  107. btn_codes[btn_num['a']] = btn_codes[btn_num['b']]
  108. btn_codes[btn_num['b']] = '\n'
  109.  
  110. return btn_codes
  111.  
  112. def signal_handler(signum, frame):
  113. close_fds(js_fds)
  114. sys.exit(0)
  115.  
  116. def get_hex_chars(key_str):
  117.  
  118. def get_hex_chars(key_str):
  119. if (key_str.startswith("0x")):
  120. return key_str[2:].decode('hex')
  121. else:
  122. return curses.tigetstr(key_str)
  123.  
  124. def get_devices():
  125. devs = []
  126. if sys.argv[1] == '/dev/input/jsX':
  127. for dev in os.listdir('/dev/input'):
  128. if dev.startswith('js'):
  129. devs.append('/dev/input/' + dev)
  130. else:
  131. devs.append(sys.argv[1])
  132.  
  133. return devs
  134.  
  135. def open_devices():
  136. devs = get_devices()
  137.  
  138. fds = []
  139. for dev in devs:
  140. try:
  141. fds.append(os.open(dev, os.O_RDONLY | os.O_NONBLOCK ))
  142. js_button_codes[fds[-1]] = get_button_codes(dev)
  143. except:
  144. pass
  145.  
  146. return devs, fds
  147.  
  148. def close_fds(fds):
  149. for fd in fds:
  150. os.close(fd)
  151.  
  152. def read_event(fd):
  153. while True:
  154. try:
  155. event = os.read(fd, event_size)
  156. except OSError, e:
  157. if e.errno == errno.EWOULDBLOCK:
  158. return None
  159. return False
  160.  
  161. else:
  162. return event
  163.  
  164. def process_event(event):
  165.  
  166. (js_time, js_value, js_type, js_number) = struct.unpack(event_format, event)
  167.  
  168. # ignore init events
  169. if js_type & JS_EVENT_INIT:
  170. return False
  171.  
  172. hex_chars = ""
  173.  
  174. if js_type == JS_EVENT_BUTTON:
  175.  
  176. if js_type == JS_EVENT_BUTTON:
  177. if js_number < len(button_codes) and js_value == 1:
  178. hex_chars = button_codes[js_number]
  179.  
  180. if js_type == JS_EVENT_AXIS and js_number <= 7:
  181. if js_number % 2 == 0:
  182. if js_value <= JS_MIN * JS_THRESH:
  183. hex_chars = axis_codes[0]
  184. if js_value >= JS_MAX * JS_THRESH:
  185. hex_chars = axis_codes[1]
  186. if js_number % 2 == 1:
  187. if js_value <= JS_MIN * JS_THRESH:
  188. hex_chars = axis_codes[2]
  189. if js_value >= JS_MAX * JS_THRESH:
  190. hex_chars = axis_codes[3]
  191.  
  192. if hex_chars:
  193. for c in hex_chars:
  194. fcntl.ioctl(tty_fd, termios.TIOCSTI, c)
  195. return True
  196.  
  197. return False
  198.  
  199. signal.signal(signal.SIGINT, signal_handler)
  200.  
  201. js_button_codes = {}
  202. button_codes = []
  203. default_button_codes = []
  204. axis_codes = []
  205.  
  206. curses.setupterm()
  207.  
  208. i = 0
  209. for arg in sys.argv[2:]:
  210. chars = get_hex_chars(arg)
  211. if i < 4:
  212. axis_codes.append(chars)
  213. else:
  214. default_button_codes.append(chars)
  215. i += 1
  216.  
  217. event_format = 'IhBB'
  218. event_size = struct.calcsize(event_format)
  219.  
  220. try:
  221. tty_fd = open('/dev/tty', 'w')
  222. except:
  223. print 'Unable to open /dev/tty'
  224. sys.exit(1)
  225.  
  226. js_fds = []
  227. rescan_time = time.time()
  228. while True:
  229. if not js_fds:
  230. js_devs, js_fds = open_devices()
  231. if js_fds:
  232. i = 0
  233. if js_fds:
  234. i = 0
  235. current = time.time()
  236. js_last = [None] * len(js_fds)
  237. for js in js_fds:
  238. js_last[i] = current
  239. i += 1
  240. else:
  241. time.sleep(1)
  242. else:
  243. i = 0
  244. for fd in js_fds:
  245. event = read_event(fd)
  246. if event:
  247. if time.time() - js_last[i] > JS_REP:
  248. if fd in js_button_codes:
  249. button_codes = js_button_codes[fd]
  250. else:
  251. button_codes = default_button_codes
  252. if process_event(event):
  253. js_last[i] = time.time()
  254. elif event == False:
  255. close_fds(js_fds)
  256. js_fds = []
  257. break
  258. i += 1
  259.  
  260. if time.time() - rescan_time > 2:
  261. rescan_time = time.time()
  262. if cmp(js_devs, get_devices()):
  263. close_fds(js_fds)
  264. js_fds = []
  265.  
  266. time.sleep(0.01)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement