mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 03:08:54 +01:00
47558d20c5
Before this fix, the button and axis IDs are from the OS. These didn't match with the SDL game controller databaes unfortunately. This fix changes the assignments of the buttons and the axes to match with the database. Closes #2312
761 lines
19 KiB
Go
761 lines
19 KiB
Go
// Copyright 2021 The Ebiten Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// gamecontrollerdb.txt is downloaded at https://github.com/gabomdq/SDL_GameControllerDB.
|
|
|
|
// To update the database file, run:
|
|
//
|
|
// curl --location --remote-name https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt
|
|
|
|
//go:generate go run github.com/hajimehoshi/file2byteslice/cmd/file2byteslice@v1.0.0 -package gamepaddb -input=./gamecontrollerdb.txt -output=./gamecontrollerdb.txt.go -var=gamecontrollerdbTxt
|
|
|
|
package gamepaddb
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
type platform int
|
|
|
|
const (
|
|
platformUnknown platform = iota
|
|
platformWindows
|
|
platformMacOS
|
|
platformUnix
|
|
platformAndroid
|
|
platformIOS
|
|
)
|
|
|
|
var currentPlatform platform
|
|
|
|
func init() {
|
|
if runtime.GOOS == "windows" {
|
|
currentPlatform = platformWindows
|
|
return
|
|
}
|
|
|
|
if runtime.GOOS == "aix" ||
|
|
runtime.GOOS == "dragonfly" ||
|
|
runtime.GOOS == "freebsd" ||
|
|
runtime.GOOS == "hurd" ||
|
|
runtime.GOOS == "illumos" ||
|
|
runtime.GOOS == "linux" ||
|
|
runtime.GOOS == "netbsd" ||
|
|
runtime.GOOS == "openbsd" ||
|
|
runtime.GOOS == "solaris" {
|
|
currentPlatform = platformUnix
|
|
return
|
|
}
|
|
|
|
if runtime.GOOS == "android" {
|
|
currentPlatform = platformAndroid
|
|
return
|
|
}
|
|
|
|
if isIOS {
|
|
currentPlatform = platformIOS
|
|
return
|
|
}
|
|
|
|
if runtime.GOOS == "darwin" {
|
|
currentPlatform = platformMacOS
|
|
return
|
|
}
|
|
}
|
|
|
|
var additionalGLFWGamepads = []byte(`
|
|
78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,
|
|
78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,
|
|
78696e70757403000000000000000000,XInput Arcade Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,
|
|
78696e70757404000000000000000000,XInput Flight Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,
|
|
78696e70757405000000000000000000,XInput Dance Pad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,
|
|
78696e70757406000000000000000000,XInput Guitar (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,
|
|
78696e70757408000000000000000000,XInput Drum Kit (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,
|
|
`)
|
|
|
|
func init() {
|
|
if err := Update(gamecontrollerdbTxt); err != nil {
|
|
panic(err)
|
|
}
|
|
if err := Update(additionalGLFWGamepads); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
type mappingType int
|
|
|
|
const (
|
|
mappingTypeButton mappingType = iota
|
|
mappingTypeAxis
|
|
mappingTypeHat
|
|
)
|
|
|
|
const (
|
|
HatUp = 1
|
|
HatRight = 2
|
|
HatDown = 4
|
|
HatLeft = 8
|
|
)
|
|
|
|
type mapping struct {
|
|
Type mappingType
|
|
Index int
|
|
AxisScale int
|
|
AxisOffset int
|
|
HatState int
|
|
}
|
|
|
|
var (
|
|
gamepadNames = map[string]string{}
|
|
gamepadButtonMappings = map[string]map[StandardButton]*mapping{}
|
|
gamepadAxisMappings = map[string]map[StandardAxis]*mapping{}
|
|
mappingsM sync.RWMutex
|
|
)
|
|
|
|
func parseLine(line string, platform platform) (id string, name string, buttons map[StandardButton]*mapping, axes map[StandardAxis]*mapping, err error) {
|
|
line = strings.TrimSpace(line)
|
|
if len(line) == 0 {
|
|
return "", "", nil, nil, nil
|
|
}
|
|
if line[0] == '#' {
|
|
return "", "", nil, nil, nil
|
|
}
|
|
tokens := strings.Split(line, ",")
|
|
if len(tokens) < 2 {
|
|
return "", "", nil, nil, fmt.Errorf("gamepaddb: syntax error")
|
|
}
|
|
|
|
for _, token := range tokens[2:] {
|
|
if len(token) == 0 {
|
|
continue
|
|
}
|
|
tks := strings.Split(token, ":")
|
|
if len(tks) < 2 {
|
|
return "", "", nil, nil, fmt.Errorf("gamepaddb: syntax error")
|
|
}
|
|
|
|
// Note that the platform part is listed in the definition of SDL_GetPlatform.
|
|
if tks[0] == "platform" {
|
|
switch tks[1] {
|
|
case "Windows":
|
|
if platform != platformWindows {
|
|
return "", "", nil, nil, nil
|
|
}
|
|
case "Mac OS X":
|
|
if platform != platformMacOS {
|
|
return "", "", nil, nil, nil
|
|
}
|
|
case "Linux":
|
|
if platform != platformUnix {
|
|
return "", "", nil, nil, nil
|
|
}
|
|
case "Android":
|
|
if platform != platformAndroid {
|
|
return "", "", nil, nil, nil
|
|
}
|
|
case "iOS":
|
|
if platform != platformIOS {
|
|
return "", "", nil, nil, nil
|
|
}
|
|
case "":
|
|
// Allow any platforms
|
|
default:
|
|
return "", "", nil, nil, fmt.Errorf("gamepaddb: unexpected platform: %s", tks[1])
|
|
}
|
|
continue
|
|
}
|
|
|
|
gb, err := parseMappingElement(tks[1])
|
|
if err != nil {
|
|
return "", "", nil, nil, err
|
|
}
|
|
|
|
if b, ok := toStandardGamepadButton(tks[0]); ok {
|
|
if buttons == nil {
|
|
buttons = map[StandardButton]*mapping{}
|
|
}
|
|
buttons[b] = gb
|
|
continue
|
|
}
|
|
|
|
if a, ok := toStandardGamepadAxis(tks[0]); ok {
|
|
if axes == nil {
|
|
axes = map[StandardAxis]*mapping{}
|
|
}
|
|
axes[a] = gb
|
|
continue
|
|
}
|
|
|
|
// The buttons like "misc1" are ignored so far.
|
|
// There is no corresponding button in the Web standard gamepad layout.
|
|
}
|
|
|
|
return tokens[0], tokens[1], buttons, axes, nil
|
|
}
|
|
|
|
func parseMappingElement(str string) (*mapping, error) {
|
|
switch {
|
|
case str[0] == 'a' || strings.HasPrefix(str, "+a") || strings.HasPrefix(str, "-a"):
|
|
var tilda bool
|
|
if str[len(str)-1] == '~' {
|
|
str = str[:len(str)-1]
|
|
tilda = true
|
|
}
|
|
|
|
min := -1
|
|
max := 1
|
|
numstr := str[1:]
|
|
|
|
if str[0] == '+' {
|
|
numstr = str[2:]
|
|
min = 0
|
|
} else if str[0] == '-' {
|
|
numstr = str[2:]
|
|
max = 0
|
|
}
|
|
|
|
scale := 2 / (max - min)
|
|
offset := -(max + min)
|
|
if tilda {
|
|
scale = -scale
|
|
offset = -offset
|
|
}
|
|
|
|
index, err := strconv.Atoi(numstr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &mapping{
|
|
Type: mappingTypeAxis,
|
|
Index: index,
|
|
AxisScale: scale,
|
|
AxisOffset: offset,
|
|
}, nil
|
|
|
|
case str[0] == 'b':
|
|
index, err := strconv.Atoi(str[1:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &mapping{
|
|
Type: mappingTypeButton,
|
|
Index: index,
|
|
}, nil
|
|
|
|
case str[0] == 'h':
|
|
tokens := strings.Split(str[1:], ".")
|
|
if len(tokens) < 2 {
|
|
return nil, fmt.Errorf("gamepaddb: unexpected hat: %s", str)
|
|
}
|
|
index, err := strconv.Atoi(tokens[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
hat, err := strconv.Atoi(tokens[1])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &mapping{
|
|
Type: mappingTypeHat,
|
|
Index: index,
|
|
HatState: hat,
|
|
}, nil
|
|
}
|
|
|
|
return nil, fmt.Errorf("gamepaddb: unepxected mapping: %s", str)
|
|
}
|
|
|
|
func toStandardGamepadButton(str string) (StandardButton, bool) {
|
|
switch str {
|
|
case "a":
|
|
return StandardButtonRightBottom, true
|
|
case "b":
|
|
return StandardButtonRightRight, true
|
|
case "x":
|
|
return StandardButtonRightLeft, true
|
|
case "y":
|
|
return StandardButtonRightTop, true
|
|
case "back":
|
|
return StandardButtonCenterLeft, true
|
|
case "start":
|
|
return StandardButtonCenterRight, true
|
|
case "guide":
|
|
return StandardButtonCenterCenter, true
|
|
case "leftshoulder":
|
|
return StandardButtonFrontTopLeft, true
|
|
case "rightshoulder":
|
|
return StandardButtonFrontTopRight, true
|
|
case "leftstick":
|
|
return StandardButtonLeftStick, true
|
|
case "rightstick":
|
|
return StandardButtonRightStick, true
|
|
case "dpup":
|
|
return StandardButtonLeftTop, true
|
|
case "dpright":
|
|
return StandardButtonLeftRight, true
|
|
case "dpdown":
|
|
return StandardButtonLeftBottom, true
|
|
case "dpleft":
|
|
return StandardButtonLeftLeft, true
|
|
case "lefttrigger":
|
|
return StandardButtonFrontBottomLeft, true
|
|
case "righttrigger":
|
|
return StandardButtonFrontBottomRight, true
|
|
default:
|
|
return 0, false
|
|
}
|
|
}
|
|
|
|
func toStandardGamepadAxis(str string) (StandardAxis, bool) {
|
|
switch str {
|
|
case "leftx":
|
|
return StandardAxisLeftStickHorizontal, true
|
|
case "lefty":
|
|
return StandardAxisLeftStickVertical, true
|
|
case "rightx":
|
|
return StandardAxisRightStickHorizontal, true
|
|
case "righty":
|
|
return StandardAxisRightStickVertical, true
|
|
default:
|
|
return 0, false
|
|
}
|
|
}
|
|
|
|
func buttonMappings(id string) map[StandardButton]*mapping {
|
|
if m, ok := gamepadButtonMappings[id]; ok {
|
|
return m
|
|
}
|
|
if currentPlatform == platformAndroid {
|
|
if addAndroidDefaultMappings(id) {
|
|
return gamepadButtonMappings[id]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func axisMappings(id string) map[StandardAxis]*mapping {
|
|
if m, ok := gamepadAxisMappings[id]; ok {
|
|
return m
|
|
}
|
|
if currentPlatform == platformAndroid {
|
|
if addAndroidDefaultMappings(id) {
|
|
return gamepadAxisMappings[id]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func HasStandardLayoutMapping(id string) bool {
|
|
mappingsM.RLock()
|
|
defer mappingsM.RUnlock()
|
|
|
|
return buttonMappings(id) != nil || axisMappings(id) != nil
|
|
}
|
|
|
|
type GamepadState interface {
|
|
Axis(index int) float64
|
|
Button(index int) bool
|
|
Hat(index int) int
|
|
}
|
|
|
|
func Name(id string) string {
|
|
mappingsM.RLock()
|
|
defer mappingsM.RUnlock()
|
|
|
|
return gamepadNames[id]
|
|
}
|
|
|
|
func HasStandardAxis(id string, axis StandardAxis) bool {
|
|
mappingsM.RLock()
|
|
defer mappingsM.RUnlock()
|
|
|
|
mappings := axisMappings(id)
|
|
if mappings == nil {
|
|
return false
|
|
}
|
|
|
|
mapping := mappings[axis]
|
|
if mapping == nil {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func AxisValue(id string, axis StandardAxis, state GamepadState) float64 {
|
|
mappingsM.RLock()
|
|
defer mappingsM.RUnlock()
|
|
|
|
mappings := axisMappings(id)
|
|
if mappings == nil {
|
|
return 0
|
|
}
|
|
|
|
mapping := mappings[axis]
|
|
if mapping == nil {
|
|
return 0
|
|
}
|
|
|
|
switch mapping.Type {
|
|
case mappingTypeAxis:
|
|
v := state.Axis(mapping.Index)*float64(mapping.AxisScale) + float64(mapping.AxisOffset)
|
|
if v > 1 {
|
|
return 1
|
|
} else if v < -1 {
|
|
return -1
|
|
}
|
|
return v
|
|
case mappingTypeButton:
|
|
if state.Button(mapping.Index) {
|
|
return 1
|
|
} else {
|
|
return -1
|
|
}
|
|
case mappingTypeHat:
|
|
if state.Hat(mapping.Index)&mapping.HatState != 0 {
|
|
return 1
|
|
} else {
|
|
return -1
|
|
}
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func HasStandardButton(id string, button StandardButton) bool {
|
|
mappingsM.RLock()
|
|
defer mappingsM.RUnlock()
|
|
|
|
mappings := buttonMappings(id)
|
|
if mappings == nil {
|
|
return false
|
|
}
|
|
|
|
mapping := mappings[button]
|
|
if mapping == nil {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func ButtonValue(id string, button StandardButton, state GamepadState) float64 {
|
|
mappingsM.RLock()
|
|
defer mappingsM.RUnlock()
|
|
|
|
return buttonValue(id, button, state)
|
|
}
|
|
|
|
func buttonValue(id string, button StandardButton, state GamepadState) float64 {
|
|
mappings := buttonMappings(id)
|
|
if mappings == nil {
|
|
return 0
|
|
}
|
|
|
|
mapping := mappings[button]
|
|
if mapping == nil {
|
|
return 0
|
|
}
|
|
|
|
switch mapping.Type {
|
|
case mappingTypeAxis:
|
|
v := state.Axis(mapping.Index)*float64(mapping.AxisScale) + float64(mapping.AxisOffset)
|
|
if v > 1 {
|
|
v = 1
|
|
} else if v < -1 {
|
|
v = -1
|
|
}
|
|
// Adjust [-1, 1] to [0, 1]
|
|
return (v + 1) / 2
|
|
case mappingTypeButton:
|
|
if state.Button(mapping.Index) {
|
|
return 1
|
|
}
|
|
return 0
|
|
case mappingTypeHat:
|
|
if state.Hat(mapping.Index)&mapping.HatState != 0 {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func IsButtonPressed(id string, button StandardButton, state GamepadState) bool {
|
|
// Use XInput's trigger dead zone.
|
|
// See https://source.chromium.org/chromium/chromium/src/+/main:device/gamepad/public/cpp/gamepad.h;l=22-23;drc=6997f8a177359bb99598988ed5e900841984d242
|
|
const threshold = 30.0 / 255.0
|
|
|
|
mappingsM.RLock()
|
|
defer mappingsM.RUnlock()
|
|
|
|
mappings, ok := gamepadButtonMappings[id]
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
mapping := mappings[button]
|
|
if mapping == nil {
|
|
return false
|
|
}
|
|
|
|
switch mapping.Type {
|
|
case mappingTypeAxis:
|
|
v := buttonValue(id, button, state)
|
|
return v > threshold
|
|
case mappingTypeButton:
|
|
return state.Button(mapping.Index)
|
|
case mappingTypeHat:
|
|
return state.Hat(mapping.Index)&mapping.HatState != 0
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// Update adds new gamepad mappings.
|
|
// The string must be in the format of SDL_GameControllerDB.
|
|
//
|
|
// Update works atomically. If an error happens, nothing is updated.
|
|
func Update(mappingData []byte) error {
|
|
mappingsM.Lock()
|
|
defer mappingsM.Unlock()
|
|
|
|
buf := bytes.NewBuffer(mappingData)
|
|
s := bufio.NewScanner(buf)
|
|
|
|
type parsedLine struct {
|
|
id string
|
|
name string
|
|
buttons map[StandardButton]*mapping
|
|
axes map[StandardAxis]*mapping
|
|
}
|
|
var lines []parsedLine
|
|
|
|
for s.Scan() {
|
|
line := s.Text()
|
|
id, name, buttons, axes, err := parseLine(line, currentPlatform)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if id != "" {
|
|
lines = append(lines, parsedLine{
|
|
id: id,
|
|
name: name,
|
|
buttons: buttons,
|
|
axes: axes,
|
|
})
|
|
}
|
|
}
|
|
|
|
if err := s.Err(); err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, l := range lines {
|
|
gamepadNames[l.id] = l.name
|
|
gamepadButtonMappings[l.id] = l.buttons
|
|
gamepadAxisMappings[l.id] = l.axes
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func addAndroidDefaultMappings(id string) bool {
|
|
// See https://github.com/libsdl-org/SDL/blob/120c76c84bbce4c1bfed4e9eb74e10678bd83120/src/joystick/SDL_gamecontroller.c#L468-L568
|
|
|
|
const faceButtonMask = ((1 << SDLControllerButtonA) |
|
|
(1 << SDLControllerButtonB) |
|
|
(1 << SDLControllerButtonX) |
|
|
(1 << SDLControllerButtonY))
|
|
|
|
idBytes, err := hex.DecodeString(id)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
buttonMask := uint16(idBytes[12]) | (uint16(idBytes[13]) << 8)
|
|
axisMask := uint16(idBytes[14]) | (uint16(idBytes[15]) << 8)
|
|
if buttonMask == 0 && axisMask == 0 {
|
|
return false
|
|
}
|
|
if buttonMask&faceButtonMask == 0 {
|
|
return false
|
|
}
|
|
|
|
gamepadButtonMappings[id] = map[StandardButton]*mapping{}
|
|
gamepadAxisMappings[id] = map[StandardAxis]*mapping{}
|
|
|
|
// For mappings, see mobile/ebitenmobileview/input_android.go.
|
|
|
|
if buttonMask&(1<<SDLControllerButtonA) != 0 {
|
|
gamepadButtonMappings[id][StandardButtonRightBottom] = &mapping{
|
|
Type: mappingTypeButton,
|
|
Index: SDLControllerButtonA,
|
|
}
|
|
}
|
|
if buttonMask&(1<<SDLControllerButtonB) != 0 {
|
|
gamepadButtonMappings[id][StandardButtonRightRight] = &mapping{
|
|
Type: mappingTypeButton,
|
|
Index: SDLControllerButtonB,
|
|
}
|
|
} else {
|
|
// Use the back button as "B" for easy UI navigation with TV remotes.
|
|
gamepadButtonMappings[id][StandardButtonRightRight] = &mapping{
|
|
Type: mappingTypeButton,
|
|
Index: SDLControllerButtonBack,
|
|
}
|
|
buttonMask &^= uint16(1) << SDLControllerButtonBack
|
|
}
|
|
if buttonMask&(1<<SDLControllerButtonX) != 0 {
|
|
gamepadButtonMappings[id][StandardButtonRightLeft] = &mapping{
|
|
Type: mappingTypeButton,
|
|
Index: SDLControllerButtonX,
|
|
}
|
|
}
|
|
if buttonMask&(1<<SDLControllerButtonY) != 0 {
|
|
gamepadButtonMappings[id][StandardButtonRightTop] = &mapping{
|
|
Type: mappingTypeButton,
|
|
Index: SDLControllerButtonY,
|
|
}
|
|
}
|
|
if buttonMask&(1<<SDLControllerButtonBack) != 0 {
|
|
gamepadButtonMappings[id][StandardButtonCenterLeft] = &mapping{
|
|
Type: mappingTypeButton,
|
|
Index: SDLControllerButtonBack,
|
|
}
|
|
}
|
|
if buttonMask&(1<<SDLControllerButtonGuide) != 0 {
|
|
// TODO: If SDKVersion >= 30, add this code:
|
|
//
|
|
// gamepadButtonMappings[id][StandardButtonCenterCenter] = &mapping{
|
|
// Type: mappingTypeButton,
|
|
// Index: SDLControllerButtonGuide,
|
|
// }
|
|
}
|
|
if buttonMask&(1<<SDLControllerButtonStart) != 0 {
|
|
gamepadButtonMappings[id][StandardButtonCenterRight] = &mapping{
|
|
Type: mappingTypeButton,
|
|
Index: SDLControllerButtonStart,
|
|
}
|
|
}
|
|
if buttonMask&(1<<SDLControllerButtonLeftStick) != 0 {
|
|
gamepadButtonMappings[id][StandardButtonLeftStick] = &mapping{
|
|
Type: mappingTypeButton,
|
|
Index: SDLControllerButtonLeftStick,
|
|
}
|
|
}
|
|
if buttonMask&(1<<SDLControllerButtonRightStick) != 0 {
|
|
gamepadButtonMappings[id][StandardButtonRightStick] = &mapping{
|
|
Type: mappingTypeButton,
|
|
Index: SDLControllerButtonRightStick,
|
|
}
|
|
}
|
|
if buttonMask&(1<<SDLControllerButtonLeftShoulder) != 0 {
|
|
gamepadButtonMappings[id][StandardButtonFrontTopLeft] = &mapping{
|
|
Type: mappingTypeButton,
|
|
Index: SDLControllerButtonLeftShoulder,
|
|
}
|
|
}
|
|
if buttonMask&(1<<SDLControllerButtonRightShoulder) != 0 {
|
|
gamepadButtonMappings[id][StandardButtonFrontTopRight] = &mapping{
|
|
Type: mappingTypeButton,
|
|
Index: SDLControllerButtonRightShoulder,
|
|
}
|
|
}
|
|
|
|
if buttonMask&(1<<SDLControllerButtonDpadUp) != 0 {
|
|
gamepadButtonMappings[id][StandardButtonLeftTop] = &mapping{
|
|
Type: mappingTypeButton,
|
|
Index: SDLControllerButtonDpadUp,
|
|
}
|
|
}
|
|
if buttonMask&(1<<SDLControllerButtonDpadDown) != 0 {
|
|
gamepadButtonMappings[id][StandardButtonLeftBottom] = &mapping{
|
|
Type: mappingTypeButton,
|
|
Index: SDLControllerButtonDpadDown,
|
|
}
|
|
}
|
|
if buttonMask&(1<<SDLControllerButtonDpadLeft) != 0 {
|
|
gamepadButtonMappings[id][StandardButtonLeftLeft] = &mapping{
|
|
Type: mappingTypeButton,
|
|
Index: SDLControllerButtonDpadLeft,
|
|
}
|
|
}
|
|
if buttonMask&(1<<SDLControllerButtonDpadRight) != 0 {
|
|
gamepadButtonMappings[id][StandardButtonLeftRight] = &mapping{
|
|
Type: mappingTypeButton,
|
|
Index: SDLControllerButtonDpadRight,
|
|
}
|
|
}
|
|
|
|
if axisMask&(1<<SDLControllerAxisLeftX) != 0 {
|
|
gamepadAxisMappings[id][StandardAxisLeftStickHorizontal] = &mapping{
|
|
Type: mappingTypeAxis,
|
|
Index: SDLControllerAxisLeftX,
|
|
AxisScale: 1,
|
|
AxisOffset: 0,
|
|
}
|
|
}
|
|
if axisMask&(1<<SDLControllerAxisLeftY) != 0 {
|
|
gamepadAxisMappings[id][StandardAxisLeftStickVertical] = &mapping{
|
|
Type: mappingTypeAxis,
|
|
Index: SDLControllerAxisLeftY,
|
|
AxisScale: 1,
|
|
AxisOffset: 0,
|
|
}
|
|
}
|
|
if axisMask&(1<<SDLControllerAxisRightX) != 0 {
|
|
gamepadAxisMappings[id][StandardAxisRightStickHorizontal] = &mapping{
|
|
Type: mappingTypeAxis,
|
|
Index: SDLControllerAxisRightX,
|
|
AxisScale: 1,
|
|
AxisOffset: 0,
|
|
}
|
|
}
|
|
if axisMask&(1<<SDLControllerAxisRightY) != 0 {
|
|
gamepadAxisMappings[id][StandardAxisRightStickVertical] = &mapping{
|
|
Type: mappingTypeAxis,
|
|
Index: SDLControllerAxisRightY,
|
|
AxisScale: 1,
|
|
AxisOffset: 0,
|
|
}
|
|
}
|
|
if axisMask&(1<<SDLControllerAxisTriggerLeft) != 0 {
|
|
gamepadButtonMappings[id][StandardButtonFrontBottomLeft] = &mapping{
|
|
Type: mappingTypeAxis,
|
|
Index: SDLControllerAxisTriggerLeft,
|
|
AxisScale: 1,
|
|
AxisOffset: 0,
|
|
}
|
|
}
|
|
if axisMask&(1<<SDLControllerAxisTriggerRight) != 0 {
|
|
gamepadButtonMappings[id][StandardButtonFrontBottomRight] = &mapping{
|
|
Type: mappingTypeAxis,
|
|
Index: SDLControllerAxisTriggerRight,
|
|
AxisScale: 1,
|
|
AxisOffset: 0,
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|