Skip to content

Commit d4ff462

Browse files
authored
Merge pull request #4064 from cmitu/joy2key-fix7
joy2key: match config by Product/Vendor IDs
2 parents 00ed1de + 6fcefbd commit d4ff462

File tree

1 file changed

+36
-11
lines changed

1 file changed

+36
-11
lines changed

scriptmodules/admin/joy2key/joy2key_sdl.py

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import uinput
3131

3232
from argparse import ArgumentParser
33-
from ctypes import create_string_buffer, byref
33+
from ctypes import create_string_buffer, byref, POINTER, c_int
3434
from configparser import ConfigParser
3535
from sdl2 import joystick, events, version, \
3636
SDL_WasInit, SDL_Init, SDL_QuitSubSystem, SDL_GetError, \
@@ -39,6 +39,8 @@
3939
SDL_JOYDEVICEADDED, SDL_JOYDEVICEREMOVED, SDL_QUIT, \
4040
SDL_JOYBUTTONDOWN, SDL_JOYBUTTONUP, SDL_JOYHATMOTION, SDL_JOYAXISMOTION, \
4141
SDL_GetTicks
42+
from sdl2.dll import _bind, nullfunc
43+
from sdl2.stdinc import Uint16
4244

4345
logging.basicConfig(level=logging.INFO, format=u"%(asctime)s %(levelname)-6s %(message)s")
4446
LOG = logging.getLogger(__name__)
@@ -118,15 +120,19 @@ class InputDev(object):
118120
Class representing a joystick device config.
119121
Maps the inputs of the device to event names
120122
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
122126
hats - a dictionary of { <HatNo>: list(<HatValue>, <Event>) }
123127
buttons - a dict of { <ButtonNo>: <Event> }
124128
axis - a dict of { <AxisNo>: list(<AxisDirection>, <Event>) }
125129
"""
126130

127-
def __init__(self, _name: str, _guid: str):
131+
def __init__(self, _name: str, _vid: int, _pid: int):
128132
self.name = _name
129-
self.guid = _guid
133+
self.guid = None
134+
self.vid = _vid
135+
self.pid = _pid
130136
self.axis = {}
131137
self.buttons = {}
132138
self.hats = {}
@@ -153,7 +159,7 @@ def get_axis_event(self, index: int, value: int) -> list:
153159
return None
154160

155161
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}')
157163

158164

159165
def generic_event_map(input: str, event_map: dict) -> str:
@@ -227,7 +233,7 @@ def get_all_ra_config(def_buttons: list) -> list:
227233
"""
228234
ra_config_list = []
229235
# add a generic mapping at index 0, to be used for un-configured joysticks
230-
generic_dev = InputDev("*", "*")
236+
generic_dev = InputDev("*", None, None)
231237
generic_dev.add_mappings(
232238
{}, # no axis
233239
{0: 'b', 1: 'a', 3: 'y', 4: 'x'}, # 4 buttons
@@ -236,7 +242,7 @@ def get_all_ra_config(def_buttons: list) -> list:
236242
ra_config_list.append(generic_dev)
237243
js_cfg_dir = CONFIG_DIR + '/all/retroarch-joypads/'
238244

239-
config = ConfigParser(delimiters="=", strict=False, interpolation=None)
245+
config = ConfigParser(delimiters="=", strict=False, interpolation=None, converters={'int': (lambda s: s.strip('"'))})
240246
for file in os.listdir(js_cfg_dir):
241247
# skip non '.cfg' files
242248
if not file.endswith('.cfg') or file.startswith('.'):
@@ -247,8 +253,12 @@ def get_all_ra_config(def_buttons: list) -> list:
247253
config.clear()
248254
# ConfigParser needs a section, make up a section to appease it
249255
config.read_string('[device]\n' + cfg_file.read())
256+
LOG.debug(f'Parsing config "{file}"')
250257
conf_vals = config['device']
251258
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)
252262

253263
# translate the RetroArch inputs from the configuration file
254264
axis, buttons, hats = {}, {}, {}
@@ -268,9 +278,10 @@ def get_all_ra_config(def_buttons: list) -> list:
268278
axis.setdefault(input_index, []).append((input_value, event_name))
269279
else:
270280
continue
271-
ra_dev_config = InputDev(dev_name, None)
281+
ra_dev_config = InputDev(dev_name, dev_vid, dev_pid)
272282
ra_dev_config.add_mappings(axis, buttons, hats)
273283
ra_config_list.append(ra_dev_config)
284+
LOG.debug(f'Added config for "{dev_name}" from "{file}"')
274285
except Exception as e:
275286
LOG.warning(f'Parsing error for {file}: {e}')
276287
continue
@@ -370,12 +381,16 @@ def handle_new_input(e: SDL_Event, axis_norm_value: int = 0) -> bool:
370381
stick = joystick.SDL_JoystickOpen(event.jdevice.which)
371382
name = joystick.SDL_JoystickName(stick).decode('utf-8')
372383
guid = create_string_buffer(33)
384+
vid = joystick.SDL_JoystickGetVendor(stick)
385+
pid = joystick.SDL_JoystickGetProduct(stick)
386+
373387
_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')
375389
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
377391
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)):
379394
# Add the matching joystick configuration to the watched list
380395
active_devices[joystick.SDL_JoystickInstanceID(stick)] = (key, stick)
381396
LOG.debug(f'Added configuration for known device {configs[key]}')
@@ -597,6 +612,16 @@ def signal_handler(signum, frame):
597612
if joystick.SDL_NumJoysticks() < 1:
598613
LOG.debug(f'No available joystick devices found on startup')
599614

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+
600625
event_loop(configs, joy_map)
601626

602627
SDL_QuitSubSystem(SDL_INIT_JOYSTICK)

0 commit comments

Comments
 (0)