30
30
import uinput
31
31
32
32
from argparse import ArgumentParser
33
- from ctypes import create_string_buffer , byref
33
+ from ctypes import create_string_buffer , byref , POINTER , c_int
34
34
from configparser import ConfigParser
35
35
from sdl2 import joystick , events , version , \
36
36
SDL_WasInit , SDL_Init , SDL_QuitSubSystem , SDL_GetError , \
39
39
SDL_JOYDEVICEADDED , SDL_JOYDEVICEREMOVED , SDL_QUIT , \
40
40
SDL_JOYBUTTONDOWN , SDL_JOYBUTTONUP , SDL_JOYHATMOTION , SDL_JOYAXISMOTION , \
41
41
SDL_GetTicks
42
+ from sdl2 .dll import _bind , nullfunc
43
+ from sdl2 .stdinc import Uint16
42
44
43
45
logging .basicConfig (level = logging .INFO , format = u"%(asctime)s %(levelname)-6s %(message)s" )
44
46
LOG = logging .getLogger (__name__ )
@@ -118,15 +120,19 @@ class InputDev(object):
118
120
Class representing a joystick device config.
119
121
Maps the inputs of the device to event names
120
122
name: the device's name
121
- guid: the GUID, as retuned by SDL
123
+ guid: the GUID, as returned by SDL
124
+ pid: the ProductID of the device
125
+ vid: the VendorID of the device
122
126
hats - a dictionary of { <HatNo>: list(<HatValue>, <Event>) }
123
127
buttons - a dict of { <ButtonNo>: <Event> }
124
128
axis - a dict of { <AxisNo>: list(<AxisDirection>, <Event>) }
125
129
"""
126
130
127
- def __init__ (self , _name : str , _guid : str ):
131
+ def __init__ (self , _name : str , _vid : int , _pid : int ):
128
132
self .name = _name
129
- self .guid = _guid
133
+ self .guid = None
134
+ self .vid = _vid
135
+ self .pid = _pid
130
136
self .axis = {}
131
137
self .buttons = {}
132
138
self .hats = {}
@@ -153,7 +159,7 @@ def get_axis_event(self, index: int, value: int) -> list:
153
159
return None
154
160
155
161
def __str__ (self ) -> str :
156
- return str (f'{ self .name } , hats: { self .hats } , buttons: { self .buttons } , axis: { self .axis } ' )
162
+ return str (f'{ self .name } (P: { self . pid } , V: { self . vid } ) , hats: { self .hats } , buttons: { self .buttons } , axis: { self .axis } ' )
157
163
158
164
159
165
def generic_event_map (input : str , event_map : dict ) -> str :
@@ -227,7 +233,7 @@ def get_all_ra_config(def_buttons: list) -> list:
227
233
"""
228
234
ra_config_list = []
229
235
# add a generic mapping at index 0, to be used for un-configured joysticks
230
- generic_dev = InputDev ("*" , "*" )
236
+ generic_dev = InputDev ("*" , None , None )
231
237
generic_dev .add_mappings (
232
238
{}, # no axis
233
239
{0 : 'b' , 1 : 'a' , 3 : 'y' , 4 : 'x' }, # 4 buttons
@@ -236,7 +242,7 @@ def get_all_ra_config(def_buttons: list) -> list:
236
242
ra_config_list .append (generic_dev )
237
243
js_cfg_dir = CONFIG_DIR + '/all/retroarch-joypads/'
238
244
239
- config = ConfigParser (delimiters = "=" , strict = False , interpolation = None )
245
+ config = ConfigParser (delimiters = "=" , strict = False , interpolation = None , converters = { 'int' : ( lambda s : s . strip ( '"' ))} )
240
246
for file in os .listdir (js_cfg_dir ):
241
247
# skip non '.cfg' files
242
248
if not file .endswith ('.cfg' ) or file .startswith ('.' ):
@@ -247,8 +253,12 @@ def get_all_ra_config(def_buttons: list) -> list:
247
253
config .clear ()
248
254
# ConfigParser needs a section, make up a section to appease it
249
255
config .read_string ('[device]\n ' + cfg_file .read ())
256
+ LOG .debug (f'Parsing config "{ file } "' )
250
257
conf_vals = config ['device' ]
251
258
dev_name = conf_vals ['input_device' ].strip ('"' )
259
+ # fallback to None if there are no PID/VID in the configuration
260
+ dev_vid = conf_vals .getint ('input_vendor_id' , None )
261
+ dev_pid = conf_vals .getint ('input_product_id' , None )
252
262
253
263
# translate the RetroArch inputs from the configuration file
254
264
axis , buttons , hats = {}, {}, {}
@@ -268,9 +278,10 @@ def get_all_ra_config(def_buttons: list) -> list:
268
278
axis .setdefault (input_index , []).append ((input_value , event_name ))
269
279
else :
270
280
continue
271
- ra_dev_config = InputDev (dev_name , None )
281
+ ra_dev_config = InputDev (dev_name , dev_vid , dev_pid )
272
282
ra_dev_config .add_mappings (axis , buttons , hats )
273
283
ra_config_list .append (ra_dev_config )
284
+ LOG .debug (f'Added config for "{ dev_name } " from "{ file } "' )
274
285
except Exception as e :
275
286
LOG .warning (f'Parsing error for { file } : { e } ' )
276
287
continue
@@ -370,12 +381,16 @@ def handle_new_input(e: SDL_Event, axis_norm_value: int = 0) -> bool:
370
381
stick = joystick .SDL_JoystickOpen (event .jdevice .which )
371
382
name = joystick .SDL_JoystickName (stick ).decode ('utf-8' )
372
383
guid = create_string_buffer (33 )
384
+ vid = joystick .SDL_JoystickGetVendor (stick )
385
+ pid = joystick .SDL_JoystickGetProduct (stick )
386
+
373
387
_SDL_JoystickGetGUIDString (joystick .SDL_JoystickGetGUID (stick ), guid , 33 )
374
- LOG .debug (f'Joystick #{ joystick .SDL_JoystickInstanceID (stick )} { name } added' )
388
+ LOG .debug (f'Joystick #{ joystick .SDL_JoystickInstanceID (stick )} { name } (P: { pid } , V: { vid } ) added' )
375
389
conf_found = False
376
- # try to find a configuration for the joystick
390
+ # try to find a configuration for the joystick, based on name, GUID and Vendor/Product IDs
377
391
for key , dev_conf in enumerate (configs ):
378
- if dev_conf .name == str (name ) or dev_conf .guid == guid .value .decode ():
392
+ if dev_conf .name == str (name ) or dev_conf .guid == guid .value .decode () or \
393
+ (dev_conf .pid == str (pid ) and dev_conf .vid == str (vid )):
379
394
# Add the matching joystick configuration to the watched list
380
395
active_devices [joystick .SDL_JoystickInstanceID (stick )] = (key , stick )
381
396
LOG .debug (f'Added configuration for known device { configs [key ]} ' )
@@ -597,6 +612,16 @@ def signal_handler(signum, frame):
597
612
if joystick .SDL_NumJoysticks () < 1 :
598
613
LOG .debug (f'No available joystick devices found on startup' )
599
614
615
+ # 'SDL_JoystickGetVendor' and 'SDL_JoystickGetProduct' are not in PySDL2 before 0.9.6
616
+ # so add a local implementation for them when they're not found
617
+ if 'SDL_JoystickGetVendor' not in dir (joystick ):
618
+ LOG .debug (f'Function "SDL_JoystickGetVendor" not found in PySDL2 { wrapper_version } , adding a local definition for it' )
619
+ joystick .SDL_JoystickGetVendor = _bind ("SDL_JoystickGetVendor" , [POINTER (joystick .SDL_Joystick )], Uint16 , nullfunc )
620
+
621
+ if 'SDL_JoystickGetProduct' not in dir (joystick ):
622
+ LOG .debug (f'Function "SDL_JoystickGetProduct" not found in PySDL2 { wrapper_version } , adding a local definition for it' )
623
+ joystick .SDL_JoystickGetProduct = _bind ("SDL_JoystickGetProduct" , [POINTER (joystick .SDL_Joystick )], Uint16 , nullfunc )
624
+
600
625
event_loop (configs , joy_map )
601
626
602
627
SDL_QuitSubSystem (SDL_INIT_JOYSTICK )
0 commit comments