vendor dependencies, make some changes to how input is done
This commit is contained in:
+52
@@ -0,0 +1,52 @@
|
||||
// Copyright 2015 Hajime Hoshi
|
||||
//
|
||||
// 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.
|
||||
|
||||
package gamepaddb
|
||||
|
||||
type StandardButton int
|
||||
|
||||
// https://www.w3.org/TR/gamepad/#remapping
|
||||
const (
|
||||
StandardButtonRightBottom StandardButton = iota
|
||||
StandardButtonRightRight
|
||||
StandardButtonRightLeft
|
||||
StandardButtonRightTop
|
||||
StandardButtonFrontTopLeft
|
||||
StandardButtonFrontTopRight
|
||||
StandardButtonFrontBottomLeft
|
||||
StandardButtonFrontBottomRight
|
||||
StandardButtonCenterLeft
|
||||
StandardButtonCenterRight
|
||||
StandardButtonLeftStick
|
||||
StandardButtonRightStick
|
||||
StandardButtonLeftTop
|
||||
StandardButtonLeftBottom
|
||||
StandardButtonLeftLeft
|
||||
StandardButtonLeftRight
|
||||
StandardButtonCenterCenter
|
||||
|
||||
StandardButtonMax = StandardButtonCenterCenter
|
||||
)
|
||||
|
||||
type StandardAxis int
|
||||
|
||||
// https://www.w3.org/TR/gamepad/#remapping
|
||||
const (
|
||||
StandardAxisLeftStickHorizontal StandardAxis = iota
|
||||
StandardAxisLeftStickVertical
|
||||
StandardAxisRightStickHorizontal
|
||||
StandardAxisRightStickVertical
|
||||
|
||||
StandardAxisMax = StandardAxisRightStickVertical
|
||||
)
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
// Copyright 2024 The Ebitengine 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.
|
||||
|
||||
//go:build !js && !microsoftgdk && !nintendosdk && !playstation5
|
||||
|
||||
package gamepaddb
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
// gamecontrollerdb.txt is downloaded at https://github.com/mdqinc/SDL_GameControllerDB.
|
||||
|
||||
// To update the database file, run:
|
||||
//
|
||||
// curl --location --remote-name https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/master/gamecontrollerdb.txt
|
||||
|
||||
//go:embed gamecontrollerdb.txt
|
||||
var gamecontrollerdb_txt []byte
|
||||
|
||||
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(gamecontrollerdb_txt); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := Update(additionalGLFWGamepads); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
+2034
File diff suppressed because it is too large
Load Diff
+743
@@ -0,0 +1,743 @@
|
||||
// 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.
|
||||
|
||||
package gamepaddb
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type platform int
|
||||
|
||||
const (
|
||||
platformUnknown platform = iota
|
||||
platformWindows
|
||||
platformMacOS
|
||||
platformUnix
|
||||
platformAndroid
|
||||
platformIOS
|
||||
)
|
||||
|
||||
func currentPlatform() platform {
|
||||
if runtime.GOOS == "windows" {
|
||||
return platformWindows
|
||||
}
|
||||
|
||||
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" {
|
||||
return platformUnix
|
||||
}
|
||||
|
||||
if runtime.GOOS == "android" {
|
||||
return platformAndroid
|
||||
}
|
||||
|
||||
if runtime.GOOS == "ios" {
|
||||
return platformIOS
|
||||
}
|
||||
|
||||
if runtime.GOOS == "darwin" {
|
||||
return platformMacOS
|
||||
}
|
||||
|
||||
return platformUnknown
|
||||
}
|
||||
|
||||
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 float64
|
||||
AxisOffset float64
|
||||
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.0
|
||||
max := 1.0
|
||||
numstr := str[1:]
|
||||
|
||||
if str[0] == '+' {
|
||||
numstr = str[2:]
|
||||
// Only use the positive half, i.e. 0..1.
|
||||
min = 0
|
||||
} else if str[0] == '-' {
|
||||
numstr = str[2:]
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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:
|
||||
scale := 2 / (max - min)
|
||||
offset := -(max + min) / (max - min)
|
||||
if tilda {
|
||||
scale = -scale
|
||||
offset = -offset
|
||||
}
|
||||
|
||||
index, err := strconv.Atoi(numstr)
|
||||
if err != nil {
|
||||
return mapping{}, 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 mapping{}, err
|
||||
}
|
||||
return mapping{
|
||||
Type: mappingTypeButton,
|
||||
Index: index,
|
||||
}, nil
|
||||
|
||||
case str[0] == 'h':
|
||||
tokens := strings.Split(str[1:], ".")
|
||||
if len(tokens) < 2 {
|
||||
return mapping{}, fmt.Errorf("gamepaddb: unexpected hat: %s", str)
|
||||
}
|
||||
index, err := strconv.Atoi(tokens[0])
|
||||
if err != nil {
|
||||
return mapping{}, err
|
||||
}
|
||||
hat, err := strconv.Atoi(tokens[1])
|
||||
if err != nil {
|
||||
return mapping{}, err
|
||||
}
|
||||
return mapping{
|
||||
Type: mappingTypeHat,
|
||||
Index: index,
|
||||
HatState: hat,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return mapping{}, 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 {
|
||||
IsAxisReady(index int) bool
|
||||
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
|
||||
}
|
||||
_, ok := mappings[axis]
|
||||
return ok
|
||||
}
|
||||
|
||||
func StandardAxisValue(id string, axis StandardAxis, state GamepadState) float64 {
|
||||
mappingsM.RLock()
|
||||
defer mappingsM.RUnlock()
|
||||
|
||||
mappings := axisMappings(id)
|
||||
if mappings == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
mapping, ok := mappings[axis]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
|
||||
switch mapping.Type {
|
||||
case mappingTypeAxis:
|
||||
if !state.IsAxisReady(mapping.Index) {
|
||||
return 0
|
||||
}
|
||||
v := state.Axis(mapping.Index)*mapping.AxisScale + 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
|
||||
}
|
||||
_, ok := mappings[button]
|
||||
return ok
|
||||
}
|
||||
|
||||
func StandardButtonValue(id string, button StandardButton, state GamepadState) float64 {
|
||||
mappingsM.RLock()
|
||||
defer mappingsM.RUnlock()
|
||||
|
||||
return standardButtonValue(id, button, state)
|
||||
}
|
||||
|
||||
func standardButtonValue(id string, button StandardButton, state GamepadState) float64 {
|
||||
mappings := buttonMappings(id)
|
||||
if mappings == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
mapping, ok := mappings[button]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
|
||||
switch mapping.Type {
|
||||
case mappingTypeAxis:
|
||||
if !state.IsAxisReady(mapping.Index) {
|
||||
return 0
|
||||
}
|
||||
v := state.Axis(mapping.Index)*mapping.AxisScale + 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
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
func IsStandardButtonPressed(id string, button StandardButton, state GamepadState) bool {
|
||||
mappingsM.RLock()
|
||||
defer mappingsM.RUnlock()
|
||||
|
||||
mappings, ok := gamepadButtonMappings[id]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
mapping, ok := mappings[button]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
switch mapping.Type {
|
||||
case mappingTypeAxis:
|
||||
v := standardButtonValue(id, button, state)
|
||||
return v > ButtonPressedThreshold
|
||||
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
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
// Copyright 2022 The Ebitengine 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.
|
||||
|
||||
package gamepaddb
|
||||
|
||||
// See https://github.com/libsdl-org/SDL/blob/120c76c84bbce4c1bfed4e9eb74e10678bd83120/include/SDL_gamecontroller.h#L655-L680
|
||||
const (
|
||||
SDLControllerButtonA = 0
|
||||
SDLControllerButtonB = 1
|
||||
SDLControllerButtonX = 2
|
||||
SDLControllerButtonY = 3
|
||||
SDLControllerButtonBack = 4
|
||||
SDLControllerButtonGuide = 5
|
||||
SDLControllerButtonStart = 6
|
||||
SDLControllerButtonLeftStick = 7
|
||||
SDLControllerButtonRightStick = 8
|
||||
SDLControllerButtonLeftShoulder = 9
|
||||
SDLControllerButtonRightShoulder = 10
|
||||
SDLControllerButtonDpadUp = 11
|
||||
SDLControllerButtonDpadDown = 12
|
||||
SDLControllerButtonDpadLeft = 13
|
||||
SDLControllerButtonDpadRight = 14
|
||||
SDLControllerButtonMisc1 = 15
|
||||
SDLControllerButtonPaddle1 = 16
|
||||
SDLControllerButtonPaddle2 = 17
|
||||
SDLControllerButtonPaddle3 = 18
|
||||
SDLControllerButtonPaddle4 = 19
|
||||
SDLControllerButtonTouchpad = 20
|
||||
SDLControllerButtonMax = SDLControllerButtonTouchpad // This is different from the original SDL_CONTROLLER_BUTTON_MAX.
|
||||
)
|
||||
|
||||
// See https://github.com/libsdl-org/SDL/blob/120c76c84bbce4c1bfed4e9eb74e10678bd83120/include/SDL_gamecontroller.h#L550-L560
|
||||
const (
|
||||
SDLControllerAxisLeftX = 0
|
||||
SDLControllerAxisLeftY = 1
|
||||
SDLControllerAxisRightX = 2
|
||||
SDLControllerAxisRightY = 3
|
||||
SDLControllerAxisTriggerLeft = 4
|
||||
SDLControllerAxisTriggerRight = 5
|
||||
)
|
||||
Reference in New Issue
Block a user