2021-09-11 15:46:05 +02:00
// 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.
2021-09-24 21:03:17 +02:00
// To update the database file, run:
//
// curl --location --remote-name https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt
2021-09-11 15:46:05 +02:00
package gamepaddb
import (
"bufio"
"bytes"
2022-09-17 14:01:56 +02:00
_ "embed"
2022-09-06 18:05:12 +02:00
"encoding/hex"
2021-09-11 15:46:05 +02:00
"fmt"
"runtime"
"strconv"
"strings"
"sync"
)
2022-09-17 14:01:56 +02:00
//go:embed gamecontrollerdb.txt
var gamecontrollerdb_txt [ ] byte
2021-09-11 15:46:05 +02:00
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
}
2022-09-14 17:27:09 +02:00
if runtime . GOOS == "ios" {
2021-09-11 15:46:05 +02:00
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 ( ) {
2022-09-17 14:01:56 +02:00
if err := Update ( gamecontrollerdb_txt ) ; err != nil {
2021-09-11 15:46:05 +02:00
panic ( err )
}
2022-07-29 05:13:01 +02:00
if err := Update ( additionalGLFWGamepads ) ; err != nil {
2021-09-11 15:46:05 +02:00
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 (
2022-01-25 13:13:59 +01:00
gamepadNames = map [ string ] string { }
2022-02-05 15:11:09 +01:00
gamepadButtonMappings = map [ string ] map [ StandardButton ] * mapping { }
gamepadAxisMappings = map [ string ] map [ StandardAxis ] * mapping { }
2021-09-11 15:46:05 +02:00
mappingsM sync . RWMutex
)
2022-04-02 13:19:02 +02:00
func parseLine ( line string , platform platform ) ( id string , name string , buttons map [ StandardButton ] * mapping , axes map [ StandardAxis ] * mapping , err error ) {
2021-09-11 15:46:05 +02:00
line = strings . TrimSpace ( line )
if len ( line ) == 0 {
2022-04-02 13:19:02 +02:00
return "" , "" , nil , nil , nil
2021-09-11 15:46:05 +02:00
}
if line [ 0 ] == '#' {
2022-04-02 13:19:02 +02:00
return "" , "" , nil , nil , nil
2021-09-11 15:46:05 +02:00
}
tokens := strings . Split ( line , "," )
2022-07-29 05:13:01 +02:00
if len ( tokens ) < 2 {
return "" , "" , nil , nil , fmt . Errorf ( "gamepaddb: syntax error" )
}
2021-09-11 15:46:05 +02:00
for _ , token := range tokens [ 2 : ] {
if len ( token ) == 0 {
continue
}
tks := strings . Split ( token , ":" )
2022-07-29 05:13:01 +02:00
if len ( tks ) < 2 {
return "" , "" , nil , nil , fmt . Errorf ( "gamepaddb: syntax error" )
}
2022-02-05 19:37:59 +01:00
// Note that the platform part is listed in the definition of SDL_GetPlatform.
2021-09-11 15:46:05 +02:00
if tks [ 0 ] == "platform" {
switch tks [ 1 ] {
case "Windows" :
if platform != platformWindows {
2022-04-02 13:19:02 +02:00
return "" , "" , nil , nil , nil
2021-09-11 15:46:05 +02:00
}
case "Mac OS X" :
if platform != platformMacOS {
2022-04-02 13:19:02 +02:00
return "" , "" , nil , nil , nil
2021-09-11 15:46:05 +02:00
}
case "Linux" :
if platform != platformUnix {
2022-04-02 13:19:02 +02:00
return "" , "" , nil , nil , nil
2021-09-11 15:46:05 +02:00
}
case "Android" :
if platform != platformAndroid {
2022-04-02 13:19:02 +02:00
return "" , "" , nil , nil , nil
2021-09-11 15:46:05 +02:00
}
case "iOS" :
if platform != platformIOS {
2022-04-02 13:19:02 +02:00
return "" , "" , nil , nil , nil
2021-09-11 15:46:05 +02:00
}
2022-02-05 19:37:59 +01:00
case "" :
// Allow any platforms
2021-09-11 15:46:05 +02:00
default :
2022-04-02 13:19:02 +02:00
return "" , "" , nil , nil , fmt . Errorf ( "gamepaddb: unexpected platform: %s" , tks [ 1 ] )
2021-09-11 15:46:05 +02:00
}
continue
}
gb , err := parseMappingElement ( tks [ 1 ] )
if err != nil {
2022-04-02 13:19:02 +02:00
return "" , "" , nil , nil , err
2021-09-11 15:46:05 +02:00
}
if b , ok := toStandardGamepadButton ( tks [ 0 ] ) ; ok {
2022-04-02 13:19:02 +02:00
if buttons == nil {
buttons = map [ StandardButton ] * mapping { }
2021-09-11 15:46:05 +02:00
}
2022-04-02 13:19:02 +02:00
buttons [ b ] = gb
2021-09-11 15:46:05 +02:00
continue
}
if a , ok := toStandardGamepadAxis ( tks [ 0 ] ) ; ok {
2022-04-02 13:19:02 +02:00
if axes == nil {
axes = map [ StandardAxis ] * mapping { }
2021-09-11 15:46:05 +02:00
}
2022-04-02 13:19:02 +02:00
axes [ a ] = gb
2021-09-11 15:46:05 +02:00
continue
}
// The buttons like "misc1" are ignored so far.
// There is no corresponding button in the Web standard gamepad layout.
}
2022-04-02 13:19:02 +02:00
return tokens [ 0 ] , tokens [ 1 ] , buttons , axes , nil
2021-09-11 15:46:05 +02:00
}
func parseMappingElement ( str string ) ( * mapping , error ) {
switch {
2021-09-24 20:56:06 +02:00
case str [ 0 ] == 'a' || strings . HasPrefix ( str , "+a" ) || strings . HasPrefix ( str , "-a" ) :
2021-09-11 15:46:05 +02:00
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 : ]
2022-09-19 17:34:46 +02:00
// Only use the positive half, i.e. 0..1.
2021-09-11 15:46:05 +02:00
min = 0
} else if str [ 0 ] == '-' {
numstr = str [ 2 : ]
2022-09-19 17:34:46 +02:00
// Only use the negative half, i.e. -1..0,
// but invert the sense so 0 does not "press" buttons.
//
// In other words, this is the same as '+' but with the input axis
// value reversed.
//
// See SDL's source:
// https://github.com/libsdl-org/SDL/blob/f398d8a42422c049d77c744658f1cd2bb011ed4a/src/joystick/SDL_gamecontroller.c#L960
min , max = 0 , min
2021-09-11 15:46:05 +02:00
}
2022-09-19 17:34:46 +02:00
// Map min..max to -1..+1.
//
// See SDL's source:
// https://github.com/libsdl-org/SDL/blob/f398d8a42422c049d77c744658f1cd2bb011ed4a/src/joystick/SDL_gamecontroller.c#L276
// then simplify assuming output range -1..+1.
//
// Yields:
2021-09-11 15:46:05 +02:00
scale := 2 / ( max - min )
2022-09-19 17:34:46 +02:00
offset := - ( max + min ) / ( max - min )
2021-09-11 15:46:05 +02:00
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 )
}
2022-02-05 15:11:09 +01:00
func toStandardGamepadButton ( str string ) ( StandardButton , bool ) {
2021-09-11 15:46:05 +02:00
switch str {
case "a" :
2022-02-05 15:11:09 +01:00
return StandardButtonRightBottom , true
2021-09-11 15:46:05 +02:00
case "b" :
2022-02-05 15:11:09 +01:00
return StandardButtonRightRight , true
2021-09-11 15:46:05 +02:00
case "x" :
2022-02-05 15:11:09 +01:00
return StandardButtonRightLeft , true
2021-09-11 15:46:05 +02:00
case "y" :
2022-02-05 15:11:09 +01:00
return StandardButtonRightTop , true
2021-09-11 15:46:05 +02:00
case "back" :
2022-02-05 15:11:09 +01:00
return StandardButtonCenterLeft , true
2021-09-11 15:46:05 +02:00
case "start" :
2022-02-05 15:11:09 +01:00
return StandardButtonCenterRight , true
2021-09-11 15:46:05 +02:00
case "guide" :
2022-02-05 15:11:09 +01:00
return StandardButtonCenterCenter , true
2021-09-11 15:46:05 +02:00
case "leftshoulder" :
2022-02-05 15:11:09 +01:00
return StandardButtonFrontTopLeft , true
2021-09-11 15:46:05 +02:00
case "rightshoulder" :
2022-02-05 15:11:09 +01:00
return StandardButtonFrontTopRight , true
2021-09-11 15:46:05 +02:00
case "leftstick" :
2022-02-05 15:11:09 +01:00
return StandardButtonLeftStick , true
2021-09-11 15:46:05 +02:00
case "rightstick" :
2022-02-05 15:11:09 +01:00
return StandardButtonRightStick , true
2021-09-11 15:46:05 +02:00
case "dpup" :
2022-02-05 15:11:09 +01:00
return StandardButtonLeftTop , true
2021-09-11 15:46:05 +02:00
case "dpright" :
2022-02-05 15:11:09 +01:00
return StandardButtonLeftRight , true
2021-09-11 15:46:05 +02:00
case "dpdown" :
2022-02-05 15:11:09 +01:00
return StandardButtonLeftBottom , true
2021-09-11 15:46:05 +02:00
case "dpleft" :
2022-02-05 15:11:09 +01:00
return StandardButtonLeftLeft , true
2021-09-11 15:46:05 +02:00
case "lefttrigger" :
2022-02-05 15:11:09 +01:00
return StandardButtonFrontBottomLeft , true
2021-09-11 15:46:05 +02:00
case "righttrigger" :
2022-02-05 15:11:09 +01:00
return StandardButtonFrontBottomRight , true
2021-09-11 15:46:05 +02:00
default :
return 0 , false
}
}
2022-02-05 15:11:09 +01:00
func toStandardGamepadAxis ( str string ) ( StandardAxis , bool ) {
2021-09-11 15:46:05 +02:00
switch str {
case "leftx" :
2022-02-05 15:11:09 +01:00
return StandardAxisLeftStickHorizontal , true
2021-09-11 15:46:05 +02:00
case "lefty" :
2022-02-05 15:11:09 +01:00
return StandardAxisLeftStickVertical , true
2021-09-11 15:46:05 +02:00
case "rightx" :
2022-02-05 15:11:09 +01:00
return StandardAxisRightStickHorizontal , true
2021-09-11 15:46:05 +02:00
case "righty" :
2022-02-05 15:11:09 +01:00
return StandardAxisRightStickVertical , true
2021-09-11 15:46:05 +02:00
default :
return 0 , false
}
}
2022-02-05 15:11:09 +01:00
func buttonMappings ( id string ) map [ StandardButton ] * mapping {
2022-09-08 05:29:02 +02:00
if m , ok := gamepadButtonMappings [ id ] ; ok {
return m
}
2022-01-04 15:14:15 +01:00
if currentPlatform == platformAndroid {
2022-09-06 18:05:12 +02:00
if addAndroidDefaultMappings ( id ) {
return gamepadButtonMappings [ id ]
2022-01-04 15:14:15 +01:00
}
}
return nil
}
2022-02-05 15:11:09 +01:00
func axisMappings ( id string ) map [ StandardAxis ] * mapping {
2022-09-08 05:29:02 +02:00
if m , ok := gamepadAxisMappings [ id ] ; ok {
return m
}
2022-01-04 15:14:15 +01:00
if currentPlatform == platformAndroid {
2022-09-06 18:05:12 +02:00
if addAndroidDefaultMappings ( id ) {
return gamepadAxisMappings [ id ]
2022-01-04 15:14:15 +01:00
}
}
return nil
}
2021-09-11 15:46:05 +02:00
func HasStandardLayoutMapping ( id string ) bool {
mappingsM . RLock ( )
defer mappingsM . RUnlock ( )
2022-01-04 15:14:15 +01:00
return buttonMappings ( id ) != nil || axisMappings ( id ) != nil
2021-09-11 15:46:05 +02:00
}
type GamepadState interface {
Axis ( index int ) float64
Button ( index int ) bool
Hat ( index int ) int
}
2022-01-25 13:13:59 +01:00
func Name ( id string ) string {
mappingsM . RLock ( )
defer mappingsM . RUnlock ( )
return gamepadNames [ id ]
}
2022-08-11 05:35:20 +02:00
func HasStandardAxis ( id string , axis StandardAxis ) bool {
mappingsM . RLock ( )
defer mappingsM . RUnlock ( )
mappings := axisMappings ( id )
if mappings == nil {
return false
}
2023-12-24 15:37:31 +01:00
return mappings [ axis ] != nil
2022-08-11 05:35:20 +02:00
}
2022-02-05 15:11:09 +01:00
func AxisValue ( id string , axis StandardAxis , state GamepadState ) float64 {
2021-09-11 15:46:05 +02:00
mappingsM . RLock ( )
defer mappingsM . RUnlock ( )
2022-01-04 15:14:15 +01:00
mappings := axisMappings ( id )
if mappings == nil {
2021-09-11 15:46:05 +02:00
return 0
}
2021-09-24 17:58:03 +02:00
mapping := mappings [ axis ]
if mapping == nil {
return 0
}
switch mapping . Type {
2021-09-11 15:46:05 +02:00
case mappingTypeAxis :
2021-09-24 17:58:03 +02:00
v := state . Axis ( mapping . Index ) * float64 ( mapping . AxisScale ) + float64 ( mapping . AxisOffset )
2021-09-11 15:46:05 +02:00
if v > 1 {
return 1
} else if v < - 1 {
return - 1
}
return v
case mappingTypeButton :
2021-09-24 17:58:03 +02:00
if state . Button ( mapping . Index ) {
2021-09-11 15:46:05 +02:00
return 1
} else {
return - 1
}
case mappingTypeHat :
2021-09-24 17:58:03 +02:00
if state . Hat ( mapping . Index ) & mapping . HatState != 0 {
2021-09-11 15:46:05 +02:00
return 1
} else {
return - 1
}
}
return 0
}
2022-08-11 05:35:20 +02:00
func HasStandardButton ( id string , button StandardButton ) bool {
mappingsM . RLock ( )
defer mappingsM . RUnlock ( )
mappings := buttonMappings ( id )
if mappings == nil {
return false
}
2023-12-24 15:37:31 +01:00
return mappings [ button ] != nil
2022-08-11 05:35:20 +02:00
}
2022-02-05 15:11:09 +01:00
func ButtonValue ( id string , button StandardButton , state GamepadState ) float64 {
2021-09-12 13:13:23 +02:00
mappingsM . RLock ( )
defer mappingsM . RUnlock ( )
2021-09-23 08:25:27 +02:00
return buttonValue ( id , button , state )
}
2022-02-05 15:11:09 +01:00
func buttonValue ( id string , button StandardButton , state GamepadState ) float64 {
2022-01-04 15:14:15 +01:00
mappings := buttonMappings ( id )
if mappings == nil {
2021-09-12 13:13:23 +02:00
return 0
}
2021-09-24 17:58:03 +02:00
mapping := mappings [ button ]
if mapping == nil {
return 0
}
switch mapping . Type {
2021-09-12 13:13:23 +02:00
case mappingTypeAxis :
2021-09-24 17:58:03 +02:00
v := state . Axis ( mapping . Index ) * float64 ( mapping . AxisScale ) + float64 ( mapping . AxisOffset )
2021-09-12 13:13:23 +02:00
if v > 1 {
2021-09-23 08:18:16 +02:00
v = 1
2021-09-12 13:13:23 +02:00
} else if v < - 1 {
2021-09-23 08:18:16 +02:00
v = - 1
2021-09-12 13:13:23 +02:00
}
// Adjust [-1, 1] to [0, 1]
return ( v + 1 ) / 2
case mappingTypeButton :
2021-09-24 17:58:03 +02:00
if state . Button ( mapping . Index ) {
2021-09-12 13:13:23 +02:00
return 1
}
return 0
case mappingTypeHat :
2021-09-24 17:58:03 +02:00
if state . Hat ( mapping . Index ) & mapping . HatState != 0 {
2021-09-12 13:13:23 +02:00
return 1
}
return 0
}
return 0
}
2023-03-03 15:29:04 +01:00
// ButtonPressedThreshold represents the value up to which a button counts as not yet pressed.
// This has been set to match 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
// Note: should be used with >, not >=, comparisons.
const ButtonPressedThreshold = 30.0 / 255.0
2021-09-23 08:25:27 +02:00
2023-03-03 15:29:04 +01:00
func IsButtonPressed ( id string , button StandardButton , state GamepadState ) bool {
2021-09-11 15:46:05 +02:00
mappingsM . RLock ( )
defer mappingsM . RUnlock ( )
mappings , ok := gamepadButtonMappings [ id ]
if ! ok {
return false
}
2021-09-24 17:58:03 +02:00
mapping := mappings [ button ]
if mapping == nil {
return false
}
switch mapping . Type {
2021-09-11 15:46:05 +02:00
case mappingTypeAxis :
2021-09-23 08:25:27 +02:00
v := buttonValue ( id , button , state )
2023-03-03 15:29:04 +01:00
return v > ButtonPressedThreshold
2021-09-11 15:46:05 +02:00
case mappingTypeButton :
2021-09-24 17:58:03 +02:00
return state . Button ( mapping . Index )
2021-09-11 15:46:05 +02:00
case mappingTypeHat :
2021-09-24 17:58:03 +02:00
return state . Hat ( mapping . Index ) & mapping . HatState != 0
2021-09-11 15:46:05 +02:00
}
return false
}
// Update adds new gamepad mappings.
// The string must be in the format of SDL_GameControllerDB.
2022-04-02 13:19:02 +02:00
//
// Update works atomically. If an error happens, nothing is updated.
2022-07-29 05:13:01 +02:00
func Update ( mappingData [ ] byte ) error {
2021-09-11 15:46:05 +02:00
mappingsM . Lock ( )
defer mappingsM . Unlock ( )
2022-04-02 13:19:02 +02:00
buf := bytes . NewBuffer ( mappingData )
2022-07-29 05:17:55 +02:00
s := bufio . NewScanner ( buf )
2022-04-02 13:19:02 +02:00
type parsedLine struct {
id string
name string
buttons map [ StandardButton ] * mapping
axes map [ StandardAxis ] * mapping
}
var lines [ ] parsedLine
2022-07-29 05:17:55 +02:00
for s . Scan ( ) {
line := s . Text ( )
id , name , buttons , axes , err := parseLine ( line , currentPlatform )
if err != nil {
2022-07-29 05:13:01 +02:00
return err
2021-09-11 15:46:05 +02:00
}
2022-04-02 13:19:02 +02:00
if id != "" {
lines = append ( lines , parsedLine {
id : id ,
name : name ,
buttons : buttons ,
axes : axes ,
} )
2021-09-11 15:46:05 +02:00
}
2022-07-29 05:17:55 +02:00
}
if err := s . Err ( ) ; err != nil {
return err
2021-09-11 15:46:05 +02:00
}
2022-04-02 13:19:02 +02:00
for _ , l := range lines {
gamepadNames [ l . id ] = l . name
gamepadButtonMappings [ l . id ] = l . buttons
gamepadAxisMappings [ l . id ] = l . axes
}
2022-07-29 05:13:01 +02:00
return nil
2021-09-11 15:46:05 +02:00
}
2022-01-04 15:14:15 +01:00
func addAndroidDefaultMappings ( id string ) bool {
// See https://github.com/libsdl-org/SDL/blob/120c76c84bbce4c1bfed4e9eb74e10678bd83120/src/joystick/SDL_gamecontroller.c#L468-L568
2022-09-07 08:36:53 +02:00
2022-01-04 15:14:15 +01:00
const faceButtonMask = ( ( 1 << SDLControllerButtonA ) |
( 1 << SDLControllerButtonB ) |
( 1 << SDLControllerButtonX ) |
( 1 << SDLControllerButtonY ) )
2022-09-06 18:05:12 +02:00
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 )
2022-01-04 15:14:15 +01:00
if buttonMask == 0 && axisMask == 0 {
return false
}
if buttonMask & faceButtonMask == 0 {
return false
}
2022-02-05 15:11:09 +01:00
gamepadButtonMappings [ id ] = map [ StandardButton ] * mapping { }
2022-04-19 16:35:58 +02:00
gamepadAxisMappings [ id ] = map [ StandardAxis ] * mapping { }
2022-09-07 08:54:11 +02:00
// For mappings, see mobile/ebitenmobileview/input_android.go.
2022-01-04 15:14:15 +01:00
if buttonMask & ( 1 << SDLControllerButtonA ) != 0 {
2022-02-05 15:11:09 +01:00
gamepadButtonMappings [ id ] [ StandardButtonRightBottom ] = & mapping {
2022-01-04 15:14:15 +01:00
Type : mappingTypeButton ,
2022-09-08 05:29:02 +02:00
Index : SDLControllerButtonA ,
2022-01-04 15:14:15 +01:00
}
}
if buttonMask & ( 1 << SDLControllerButtonB ) != 0 {
2022-02-05 15:11:09 +01:00
gamepadButtonMappings [ id ] [ StandardButtonRightRight ] = & mapping {
2022-01-04 15:14:15 +01:00
Type : mappingTypeButton ,
2022-09-08 05:29:02 +02:00
Index : SDLControllerButtonB ,
2022-01-04 15:14:15 +01:00
}
} else {
// Use the back button as "B" for easy UI navigation with TV remotes.
2022-02-05 15:11:09 +01:00
gamepadButtonMappings [ id ] [ StandardButtonRightRight ] = & mapping {
2022-01-04 15:14:15 +01:00
Type : mappingTypeButton ,
2022-09-08 05:29:02 +02:00
Index : SDLControllerButtonBack ,
2022-01-04 15:14:15 +01:00
}
2022-02-02 20:35:55 +01:00
buttonMask &^= uint16 ( 1 ) << SDLControllerButtonBack
2022-01-04 15:14:15 +01:00
}
if buttonMask & ( 1 << SDLControllerButtonX ) != 0 {
2022-02-05 15:11:09 +01:00
gamepadButtonMappings [ id ] [ StandardButtonRightLeft ] = & mapping {
2022-01-04 15:14:15 +01:00
Type : mappingTypeButton ,
2022-09-08 05:29:02 +02:00
Index : SDLControllerButtonX ,
2022-01-04 15:14:15 +01:00
}
}
if buttonMask & ( 1 << SDLControllerButtonY ) != 0 {
2022-02-05 15:11:09 +01:00
gamepadButtonMappings [ id ] [ StandardButtonRightTop ] = & mapping {
2022-01-04 15:14:15 +01:00
Type : mappingTypeButton ,
2022-09-08 05:29:02 +02:00
Index : SDLControllerButtonY ,
2022-01-04 15:14:15 +01:00
}
}
if buttonMask & ( 1 << SDLControllerButtonBack ) != 0 {
2022-02-05 15:11:09 +01:00
gamepadButtonMappings [ id ] [ StandardButtonCenterLeft ] = & mapping {
2022-01-04 15:14:15 +01:00
Type : mappingTypeButton ,
2022-09-08 05:29:02 +02:00
Index : SDLControllerButtonBack ,
2022-01-04 15:14:15 +01:00
}
}
if buttonMask & ( 1 << SDLControllerButtonGuide ) != 0 {
// TODO: If SDKVersion >= 30, add this code:
//
2022-02-05 15:11:09 +01:00
// gamepadButtonMappings[id][StandardButtonCenterCenter] = &mapping{
2022-01-04 15:14:15 +01:00
// Type: mappingTypeButton,
// Index: SDLControllerButtonGuide,
// }
}
if buttonMask & ( 1 << SDLControllerButtonStart ) != 0 {
2022-02-05 15:11:09 +01:00
gamepadButtonMappings [ id ] [ StandardButtonCenterRight ] = & mapping {
2022-01-04 15:14:15 +01:00
Type : mappingTypeButton ,
2022-09-08 05:29:02 +02:00
Index : SDLControllerButtonStart ,
2022-01-04 15:14:15 +01:00
}
}
if buttonMask & ( 1 << SDLControllerButtonLeftStick ) != 0 {
2022-02-05 15:11:09 +01:00
gamepadButtonMappings [ id ] [ StandardButtonLeftStick ] = & mapping {
2022-01-04 15:14:15 +01:00
Type : mappingTypeButton ,
2022-09-08 05:29:02 +02:00
Index : SDLControllerButtonLeftStick ,
2022-01-04 15:14:15 +01:00
}
}
if buttonMask & ( 1 << SDLControllerButtonRightStick ) != 0 {
2022-02-05 15:11:09 +01:00
gamepadButtonMappings [ id ] [ StandardButtonRightStick ] = & mapping {
2022-01-04 15:14:15 +01:00
Type : mappingTypeButton ,
2022-09-08 05:29:02 +02:00
Index : SDLControllerButtonRightStick ,
2022-01-04 15:14:15 +01:00
}
}
if buttonMask & ( 1 << SDLControllerButtonLeftShoulder ) != 0 {
2022-02-05 15:11:09 +01:00
gamepadButtonMappings [ id ] [ StandardButtonFrontTopLeft ] = & mapping {
2022-01-04 15:14:15 +01:00
Type : mappingTypeButton ,
2022-09-08 05:29:02 +02:00
Index : SDLControllerButtonLeftShoulder ,
2022-01-04 15:14:15 +01:00
}
}
if buttonMask & ( 1 << SDLControllerButtonRightShoulder ) != 0 {
2022-02-05 15:11:09 +01:00
gamepadButtonMappings [ id ] [ StandardButtonFrontTopRight ] = & mapping {
2022-01-04 15:14:15 +01:00
Type : mappingTypeButton ,
2022-09-08 05:29:02 +02:00
Index : SDLControllerButtonRightShoulder ,
2022-01-04 15:14:15 +01:00
}
}
2022-09-07 08:54:11 +02:00
2022-01-04 15:14:15 +01:00
if buttonMask & ( 1 << SDLControllerButtonDpadUp ) != 0 {
2022-02-05 15:11:09 +01:00
gamepadButtonMappings [ id ] [ StandardButtonLeftTop ] = & mapping {
2022-09-08 05:29:02 +02:00
Type : mappingTypeButton ,
Index : SDLControllerButtonDpadUp ,
2022-01-04 15:14:15 +01:00
}
}
if buttonMask & ( 1 << SDLControllerButtonDpadDown ) != 0 {
2022-02-05 15:11:09 +01:00
gamepadButtonMappings [ id ] [ StandardButtonLeftBottom ] = & mapping {
2022-09-08 05:29:02 +02:00
Type : mappingTypeButton ,
Index : SDLControllerButtonDpadDown ,
2022-01-04 15:14:15 +01:00
}
}
if buttonMask & ( 1 << SDLControllerButtonDpadLeft ) != 0 {
2022-02-05 15:11:09 +01:00
gamepadButtonMappings [ id ] [ StandardButtonLeftLeft ] = & mapping {
2022-09-08 05:29:02 +02:00
Type : mappingTypeButton ,
Index : SDLControllerButtonDpadLeft ,
2022-01-04 15:14:15 +01:00
}
}
if buttonMask & ( 1 << SDLControllerButtonDpadRight ) != 0 {
2022-02-05 15:11:09 +01:00
gamepadButtonMappings [ id ] [ StandardButtonLeftRight ] = & mapping {
2022-09-08 05:29:02 +02:00
Type : mappingTypeButton ,
Index : SDLControllerButtonDpadRight ,
2022-01-04 15:14:15 +01:00
}
}
if axisMask & ( 1 << SDLControllerAxisLeftX ) != 0 {
2022-02-05 15:11:09 +01:00
gamepadAxisMappings [ id ] [ StandardAxisLeftStickHorizontal ] = & mapping {
2022-01-04 15:14:15 +01:00
Type : mappingTypeAxis ,
2022-09-08 05:29:02 +02:00
Index : SDLControllerAxisLeftX ,
2022-01-04 15:14:15 +01:00
AxisScale : 1 ,
AxisOffset : 0 ,
}
}
if axisMask & ( 1 << SDLControllerAxisLeftY ) != 0 {
2022-02-05 15:11:09 +01:00
gamepadAxisMappings [ id ] [ StandardAxisLeftStickVertical ] = & mapping {
2022-01-04 15:14:15 +01:00
Type : mappingTypeAxis ,
2022-09-08 05:29:02 +02:00
Index : SDLControllerAxisLeftY ,
2022-01-04 15:14:15 +01:00
AxisScale : 1 ,
AxisOffset : 0 ,
}
}
if axisMask & ( 1 << SDLControllerAxisRightX ) != 0 {
2022-02-05 15:11:09 +01:00
gamepadAxisMappings [ id ] [ StandardAxisRightStickHorizontal ] = & mapping {
2022-01-04 15:14:15 +01:00
Type : mappingTypeAxis ,
2022-09-08 05:29:02 +02:00
Index : SDLControllerAxisRightX ,
2022-01-04 15:14:15 +01:00
AxisScale : 1 ,
AxisOffset : 0 ,
}
}
if axisMask & ( 1 << SDLControllerAxisRightY ) != 0 {
2022-02-05 15:11:09 +01:00
gamepadAxisMappings [ id ] [ StandardAxisRightStickVertical ] = & mapping {
2022-01-04 15:14:15 +01:00
Type : mappingTypeAxis ,
2022-09-08 05:29:02 +02:00
Index : SDLControllerAxisRightY ,
2022-01-04 15:14:15 +01:00
AxisScale : 1 ,
AxisOffset : 0 ,
}
}
if axisMask & ( 1 << SDLControllerAxisTriggerLeft ) != 0 {
2022-02-05 15:11:09 +01:00
gamepadButtonMappings [ id ] [ StandardButtonFrontBottomLeft ] = & mapping {
2022-01-04 15:14:15 +01:00
Type : mappingTypeAxis ,
2022-09-08 05:29:02 +02:00
Index : SDLControllerAxisTriggerLeft ,
AxisScale : 1 ,
AxisOffset : 0 ,
2022-01-04 15:14:15 +01:00
}
}
if axisMask & ( 1 << SDLControllerAxisTriggerRight ) != 0 {
2022-02-05 15:11:09 +01:00
gamepadButtonMappings [ id ] [ StandardButtonFrontBottomRight ] = & mapping {
2022-01-04 15:14:15 +01:00
Type : mappingTypeAxis ,
2022-09-08 05:29:02 +02:00
Index : SDLControllerAxisTriggerRight ,
AxisScale : 1 ,
AxisOffset : 0 ,
2022-01-04 15:14:15 +01:00
}
}
return true
}