Skip to content

Commit 16ad21e

Browse files
Merge pull request batocera-linux#2552 from glebm/fix-sdl2
Fix SDL2 GameController mappings
2 parents 9f76c85 + cb5a790 commit 16ad21e

4 files changed

Lines changed: 87 additions & 100 deletions

File tree

package/batocera/core/batocera-configgen/configgen/configgen/controllersConfig.py

Lines changed: 72 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@
44

55
esInputs = batoceraFiles.esInputs
66

7+
"""Default mapping of Batocera keys to SDL_GAMECONTROLLERCONFIG keys."""
8+
_DEFAULT_SDL_MAPPING = {
9+
'b': 'a', 'a': 'b',
10+
'x': 'y', 'y': 'x',
11+
'l2': 'lefttrigger', 'r2': 'righttrigger',
12+
'l3': 'leftstick', 'r3': 'rightstick',
13+
'pageup': 'leftshoulder', 'pagedown': 'rightshoulder',
14+
'start': 'start', 'select': 'back',
15+
'up': 'dpup', 'down': 'dpdown', 'left': 'dpleft', 'right': 'dpright',
16+
'joystick1up': 'lefty', 'joystick1left': 'leftx',
17+
'joystick2up': 'righty', 'joystick2left': 'rightx', 'hotkey': 'guide'
18+
}
719

820
class Input:
921
def __init__(self, name, type, id, value, code):
@@ -32,50 +44,7 @@ def __init__(self, configName, type, guid, player, index="-1", realName="", inpu
3244
self.inputs = inputs
3345

3446
def generateSDLGameDBLine(self):
35-
# Making a dirty assumption here : if a dpad is an axis, then it shouldn't have any analog joystick
36-
nameMapping = {
37-
'a' : { 'button' : 'b' },
38-
'b' : { 'button' : 'a' },
39-
'x' : { 'button' : 'y' },
40-
'y' : { 'button' : 'x' },
41-
'start' : { 'button' : 'start' },
42-
'select' : { 'button' : 'back' },
43-
'pageup' : { 'button' : 'leftshoulder' },
44-
'pagedown' : { 'button' : 'rightshoulder' },
45-
'l2' : { 'button' : 'lefttrigger', 'axis' : 'lefttrigger' },
46-
'r2' : { 'button' : 'righttrigger', 'axis' : 'righttrigger' },
47-
'l3' : { 'button' : 'leftstick' },
48-
'r3' : { 'button' : 'rightstick' },
49-
'up' : { 'button' : 'dpup', 'hat' : 'dpup', 'axis' : 'lefty' },
50-
'down' : { 'button' : 'dpdown', 'hat' : 'dpdown' },
51-
'left' : { 'button' : 'dpleft', 'hat' : 'dpleft', 'axis' : 'leftx' },
52-
'right' : { 'button' : 'dpright', 'hat' : 'dpright' },
53-
'joystick1up' : { 'axis' : 'lefty' },
54-
'joystick1left' : { 'axis' : 'leftx' },
55-
'joystick2up' : { 'axis' : 'righty' },
56-
'joystick2left' : { 'axis' : 'rightx' },
57-
'hotkey' : { 'button' : 'guide' }
58-
}
59-
typePrefix = {
60-
'axis' : 'a',
61-
'button' : 'b',
62-
'hat' : 'h0.' # Force dpad 0 until ES handles others
63-
}
64-
65-
if not self.inputs:
66-
return None
67-
# TODO: python3 - force to use unicode
68-
strOut = u"{},{},platform:Linux,".format(self.guid, self.configName)
69-
70-
for idx, input in self.inputs.iteritems():
71-
if input.name in nameMapping and input.type in typePrefix and input.type in nameMapping[input.name] :
72-
if input.type == 'hat':
73-
# TODO: python3 - force to use unicode
74-
strOut += u"{}:{}{},".format(nameMapping[input.name][input.type], typePrefix[input.type], input.value)
75-
else:
76-
# TODO: python3 - force to use unicode
77-
strOut += u"{}:{}{},".format(nameMapping[input.name][input.type], typePrefix[input.type], input.id)
78-
return strOut
47+
return _generateSdlGameControllerConfig(self)
7948

8049

8150
# Load all controllers from the es_input.cfg
@@ -143,11 +112,65 @@ def findBestControllerConfig(controllers, x, pxguid, pxindex, pxname, pxdev, pxn
143112
controller.inputs, pxdev, pxnbbuttons, pxnbhats, pxnbaxes)
144113
return None
145114

146-
def generateSDLGameDBAllControllers(controllers, outputFile = "/tmp/gamecontrollerdb.txt"):
147-
finalData = []
115+
116+
def _generateSdlGameControllerConfig(controller, sdlMapping=_DEFAULT_SDL_MAPPING):
117+
"""Returns an SDL_GAMECONTROLLERCONFIG-formatted string for the given configuration."""
118+
config = []
119+
config.append(controller.guid)
120+
config.append(controller.configName)
121+
for k in controller.inputs:
122+
input = controller.inputs[k]
123+
keyname = sdlMapping.get(input.name, None)
124+
if input.name is not None:
125+
sdlConf = _keyToSdlGameControllerConfig(
126+
keyname, input.type, input.id, input.value)
127+
if sdlConf is not None:
128+
config.append(sdlConf)
129+
config.append('')
130+
return ','.join(config)
131+
132+
133+
def _keyToSdlGameControllerConfig(keyname, type, id, value=None):
134+
"""
135+
Converts a key mapping to the SDL_GAMECONTROLLER format.
136+
137+
Arguments:
138+
keyname: (str) One of the SDL_GAMECONTROLLERCONFIG keys.
139+
type: (str) 'button', 'hat', or 'axis'
140+
id: (int) Numeric key id.
141+
value: (int) Hat value. Only used if type == 'hat'.
142+
Returns:
143+
(str) SDL_GAMECONTROLLERCONFIG-formatted key mapping string.
144+
Examples:
145+
keyToSdlGameControllerConfig('button', 'leftshoulder', 6)
146+
'leftshoulder:b6'
147+
148+
keyToSdlGameControllerConfig('hat', 'dpleft', 0, 8)
149+
'dpleft:h0.9'
150+
151+
keyToSdlGameControllerConfig('axis', 'lefty', 1)
152+
'lefty:a1'
153+
"""
154+
if type == 'button':
155+
return '{}:b{}'.format(keyname, id)
156+
elif type == 'hat':
157+
return '{}:h{}.{}'.format(keyname, id, value)
158+
elif type == 'axis':
159+
return '{}:a{}{}'.format(keyname, id, '~' if int(value) > 0 else '')
160+
elif type == 'key':
161+
return None
162+
else:
163+
raise ValueError, 'unknown key type: {!r}'.format(type)
164+
165+
166+
def generateSdlGameControllerConfig(controllers):
167+
configs = []
148168
for idx, controller in controllers.iteritems():
149-
finalData.append(controller.generateSDLGameDBLine())
150-
sdlData = "\n".join(finalData).encode("utf-8")
169+
configs.append(controller.generateSDLGameDBLine())
170+
return "\n".join(configs)
171+
172+
173+
def writeSDLGameDBAllControllers(controllers, outputFile = "/tmp/gamecontrollerdb.txt"):
151174
with open(outputFile, "w") as text_file:
152-
text_file.write(sdlData)
175+
text_file.write(generateSdlGameControllerConfig(controllers).encode("utf-8"))
153176
return outputFile

package/batocera/core/batocera-configgen/configgen/configgen/generators/cemu/cemuControllers.py

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -49,41 +49,8 @@ def generateControllerConfig(system, playersControllers, rom):
4949

5050
#We are exporting SDL_GAMECONTROLLERCONFIG in cemuGenerator, so we can assume all controllers are now working with xInput
5151
nplayer = 0
52-
sdlstring = ''
53-
double_pads = dict()
54-
55-
sdlMapping = {
56-
'b': 'a', 'a': 'b',
57-
'x': 'y', 'y': 'x',
58-
'l2': 'lefttrigger', 'r2': 'righttrigger',
59-
'l3': 'leftstick', 'r3': 'rightstick',
60-
'pageup': 'leftshoulder', 'pagedown': 'rightshoulder',
61-
'start': 'start', 'select': 'back',
62-
'up': 'dpup', 'down': 'dpdown', 'left': 'dpleft', 'right': 'dpright',
63-
'joystick1up': 'lefty', 'joystick1left': 'leftx',
64-
'joystick2up': 'righty', 'joystick2left': 'rightx', 'hotkey': 'guide'
65-
}
66-
67-
68-
6952

7053
for playercontroller, pad in sorted(playersControllers.items()):
71-
#if nplayer == 0: #For Future Hotkeys
72-
73-
if pad.configName not in double_pads:
74-
double_pads[pad.configName] = 1
75-
sdlstring=sdlstring + pad.guid + ',' + pad.configName
76-
for x in pad.inputs:
77-
input = pad.inputs[x]
78-
keyname = None
79-
if input.name in sdlMapping:
80-
keyname = sdlMapping[input.name]
81-
if keyname is not None:
82-
sdlstring=sdlstring + write_key(keyname, input.type, input.id, input.value, pad.nbaxes, False, None)
83-
sdlstring=sdlstring + ',platform:Linux,\n'
84-
85-
86-
8754
cemuSettings = ConfigParser.ConfigParser()
8855
cemuSettings.optionxform = str
8956

@@ -167,16 +134,3 @@ def generateControllerConfig(system, playersControllers, rom):
167134
cemuSettings.write(configfile)
168135
nplayer+=1
169136

170-
return sdlstring
171-
172-
def write_key(keyname, input_type, input_id, input_value, input_global_id, reverse, hotkey_id):
173-
#Sample Output
174-
#a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3
175-
output = "," + keyname + ":"
176-
if input_type == "button":
177-
output = output + "b" + str(input_id)
178-
elif input_type == "hat":
179-
output = output + "h" + str(input_id) + "." + str(input_value)
180-
elif input_type == "axis":
181-
output = output + "a" + str(input_id)
182-
return output

package/batocera/core/batocera-configgen/configgen/configgen/generators/cemu/cemuGenerator.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import batoceraFiles
88
from xml.dom import minidom
99
import codecs
10+
import controllersConfig
1011
import cemuControllers
1112
from shutil import copyfile
1213

@@ -28,11 +29,20 @@ def generate(self, system, rom, playersControllers, gameResolution):
2829
CemuGenerator.CemuConfig(batoceraFiles.CONF + "/cemu/settings.xml")
2930
# copy the file from where cemu reads it
3031
copyfile(batoceraFiles.CONF + "/cemu/settings.xml", "/usr/cemu/settings.xml")
31-
32-
sdlstring = cemuControllers.generateControllerConfig(system, playersControllers, rom)
33-
32+
33+
cemuControllers.generateControllerConfig(system, playersControllers, rom)
34+
3435
commandArray = ["wine64", "/usr/cemu/Cemu.exe", "-g", "z:" + rom, "-m", "z:" + batoceraFiles.SAVES + "/cemu", "-f"]
35-
return Command.Command(array=commandArray, env={"WINEPREFIX":batoceraFiles.SAVES + "/cemu", "vblank_mode":"0", "mesa_glthread":"true", "SDL_GAMECONTROLLERCONFIG":sdlstring, "WINEDLLOVERRIDES":"mscoree=;mshtml=;dbghelp.dll=n,b", "__GL_THREADED_OPTIMIZATIONS":"1" })
36+
return Command.Command(
37+
array=commandArray,
38+
env={
39+
"WINEPREFIX": batoceraFiles.SAVES + "/cemu",
40+
"vblank_mode": "0",
41+
"mesa_glthread": "true",
42+
"SDL_GAMECONTROLLERCONFIG": controllersConfig.generateSdlGameControllerConfig(playersControllers),
43+
"WINEDLLOVERRIDES": "mscoree=;mshtml=;dbghelp.dll=n,b",
44+
"__GL_THREADED_OPTIMIZATIONS": "1"
45+
})
3646

3747
@staticmethod
3848
def CemuConfig(configFile):

package/batocera/core/batocera-configgen/configgen/configgen/generators/moonlight/moonlightGenerator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def getResolutionMode(self, config):
1818
def generate(self, system, rom, playersControllers, gameResolution):
1919
moonlightConfig.generateMoonlightConfig()
2020
outputFile = batoceraFiles.moonlightCustom + '/gamecontrollerdb.txt'
21-
configFile = controllersConfig.generateSDLGameDBAllControllers(playersControllers, outputFile)
21+
configFile = controllersConfig.writeSDLGameDBAllControllers(playersControllers, outputFile)
2222
gameName,confFile = self.getRealGameNameAndConfigFile(rom)
2323
commandArray = [batoceraFiles.batoceraBins[system.config['emulator']], 'stream','-config', confFile]
2424
commandArray.append('-app')

0 commit comments

Comments
 (0)