mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +01:00
Add internal/gamepaddb (#1805)
This is basically a revert of 93a156a718
.
This implements parsing the SDL gamepad mappings by Ebiten instead
of GLFW, so that Ebiten can handle parsing errors completely.
Closes #1802
This commit is contained in:
parent
28963a66ee
commit
f23dadb8ae
5
input.go
5
input.go
@ -16,6 +16,7 @@ package ebiten
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/driver"
|
"github.com/hajimehoshi/ebiten/v2/internal/driver"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/internal/gamepaddb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AppendInputChars appends "printable" runes, read from the keyboard at the time update is called, to runes,
|
// AppendInputChars appends "printable" runes, read from the keyboard at the time update is called, to runes,
|
||||||
@ -251,11 +252,11 @@ func IsStandardGamepadLayoutAvailable(id GamepadID) bool {
|
|||||||
//
|
//
|
||||||
// On platforms where gamepad mappings are not managed by Ebiten, this always returns false and nil.
|
// On platforms where gamepad mappings are not managed by Ebiten, this always returns false and nil.
|
||||||
//
|
//
|
||||||
// UpdateStandardGamepadLayoutMappings must be called on the main thread before ebiten.Run, and is concurrent-safe after ebiten.Run.
|
// UpdateStandardGamepadLayoutMappings is concurrent-safe.
|
||||||
//
|
//
|
||||||
// Updated mappings take effect immediately even for already connected gamepads.
|
// Updated mappings take effect immediately even for already connected gamepads.
|
||||||
func UpdateStandardGamepadLayoutMappings(mappings string) (bool, error) {
|
func UpdateStandardGamepadLayoutMappings(mappings string) (bool, error) {
|
||||||
return uiDriver().Input().UpdateStandardGamepadLayoutMappings(mappings)
|
return gamepaddb.Update([]byte(mappings))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TouchID represents a touch's identifier.
|
// TouchID represents a touch's identifier.
|
||||||
|
@ -34,7 +34,6 @@ type Input interface {
|
|||||||
IsStandardGamepadButtonPressed(id GamepadID, button StandardGamepadButton) bool
|
IsStandardGamepadButtonPressed(id GamepadID, button StandardGamepadButton) bool
|
||||||
IsStandardGamepadLayoutAvailable(id GamepadID) bool
|
IsStandardGamepadLayoutAvailable(id GamepadID) bool
|
||||||
StandardGamepadAxisValue(id GamepadID, button StandardGamepadAxis) float64
|
StandardGamepadAxisValue(id GamepadID, button StandardGamepadAxis) float64
|
||||||
UpdateStandardGamepadLayoutMappings(mapping string) (bool, error)
|
|
||||||
TouchPosition(id TouchID) (x, y int)
|
TouchPosition(id TouchID) (x, y int)
|
||||||
Wheel() (xoff, yoff float64)
|
Wheel() (xoff, yoff float64)
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
454
internal/gamepaddb/gamepaddb.go
Normal file
454
internal/gamepaddb/gamepaddb.go
Normal file
@ -0,0 +1,454 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
//go:generate curl --location --remote-name https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt
|
||||||
|
//go:generate file2byteslice -package gamepaddb -input=./gamecontrollerdb.txt -output=./gamecontrollerdb.txt.go -var=gamecontrollerdbTxt
|
||||||
|
|
||||||
|
package gamepaddb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/internal/driver"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 (
|
||||||
|
gamepadButtonMappings = map[string]map[driver.StandardGamepadButton]*mapping{}
|
||||||
|
gamepadAxisMappings = map[string]map[driver.StandardGamepadAxis]*mapping{}
|
||||||
|
mappingsM sync.RWMutex
|
||||||
|
)
|
||||||
|
|
||||||
|
func processLine(line string, platform platform) error {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if len(line) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if line[0] == '#' {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
tokens := strings.Split(line, ",")
|
||||||
|
id := tokens[0]
|
||||||
|
for _, token := range tokens[2:] {
|
||||||
|
if len(token) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tks := strings.Split(token, ":")
|
||||||
|
if tks[0] == "platform" {
|
||||||
|
switch tks[1] {
|
||||||
|
case "Windows":
|
||||||
|
if platform != platformWindows {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case "Mac OS X":
|
||||||
|
if platform != platformMacOS {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case "Linux":
|
||||||
|
if platform != platformUnix {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case "Android":
|
||||||
|
if platform != platformAndroid {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case "iOS":
|
||||||
|
if platform != platformIOS {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("gamepaddb: unexpected platform: %s", tks[1])
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Found a token without a colon e.g., 'sat' or 'm_nin' on a Saturn controller. Ignore this.
|
||||||
|
if len(tks) == 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
gb, err := parseMappingElement(tks[1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if b, ok := toStandardGamepadButton(tks[0]); ok {
|
||||||
|
m, ok := gamepadButtonMappings[id]
|
||||||
|
if !ok {
|
||||||
|
m = map[driver.StandardGamepadButton]*mapping{}
|
||||||
|
gamepadButtonMappings[id] = m
|
||||||
|
}
|
||||||
|
m[b] = gb
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if a, ok := toStandardGamepadAxis(tks[0]); ok {
|
||||||
|
m, ok := gamepadAxisMappings[id]
|
||||||
|
if !ok {
|
||||||
|
m = map[driver.StandardGamepadAxis]*mapping{}
|
||||||
|
gamepadAxisMappings[id] = m
|
||||||
|
}
|
||||||
|
m[a] = gb
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// The buttons like "misc1" are ignored so far.
|
||||||
|
// There is no corresponding button in the Web standard gamepad layout.
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseMappingElement(str string) (*mapping, error) {
|
||||||
|
switch {
|
||||||
|
case str[0] == 'a' || strings.HasPrefix(str, "+a") || strings.HasPrefix(str, "-a") || str[0] == '~':
|
||||||
|
var tilda bool
|
||||||
|
if str[0] == '~' {
|
||||||
|
str = str[1:]
|
||||||
|
tilda = true
|
||||||
|
}
|
||||||
|
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) (driver.StandardGamepadButton, bool) {
|
||||||
|
switch str {
|
||||||
|
case "a":
|
||||||
|
return driver.StandardGamepadButtonRightBottom, true
|
||||||
|
case "b":
|
||||||
|
return driver.StandardGamepadButtonRightRight, true
|
||||||
|
case "x":
|
||||||
|
return driver.StandardGamepadButtonRightLeft, true
|
||||||
|
case "y":
|
||||||
|
return driver.StandardGamepadButtonRightTop, true
|
||||||
|
case "back":
|
||||||
|
return driver.StandardGamepadButtonCenterLeft, true
|
||||||
|
case "start":
|
||||||
|
return driver.StandardGamepadButtonCenterRight, true
|
||||||
|
case "guide":
|
||||||
|
return driver.StandardGamepadButtonCenterCenter, true
|
||||||
|
case "leftshoulder":
|
||||||
|
return driver.StandardGamepadButtonFrontTopLeft, true
|
||||||
|
case "rightshoulder":
|
||||||
|
return driver.StandardGamepadButtonFrontTopRight, true
|
||||||
|
case "leftstick":
|
||||||
|
return driver.StandardGamepadButtonLeftStick, true
|
||||||
|
case "rightstick":
|
||||||
|
return driver.StandardGamepadButtonRightStick, true
|
||||||
|
case "dpup":
|
||||||
|
return driver.StandardGamepadButtonLeftTop, true
|
||||||
|
case "dpright":
|
||||||
|
return driver.StandardGamepadButtonLeftRight, true
|
||||||
|
case "dpdown":
|
||||||
|
return driver.StandardGamepadButtonLeftBottom, true
|
||||||
|
case "dpleft":
|
||||||
|
return driver.StandardGamepadButtonLeftLeft, true
|
||||||
|
case "lefttrigger":
|
||||||
|
return driver.StandardGamepadButtonFrontBottomLeft, true
|
||||||
|
case "righttrigger":
|
||||||
|
return driver.StandardGamepadButtonFrontBottomRight, true
|
||||||
|
default:
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toStandardGamepadAxis(str string) (driver.StandardGamepadAxis, bool) {
|
||||||
|
switch str {
|
||||||
|
case "leftx":
|
||||||
|
return driver.StandardGamepadAxisLeftStickHorizontal, true
|
||||||
|
case "lefty":
|
||||||
|
return driver.StandardGamepadAxisLeftStickVertical, true
|
||||||
|
case "rightx":
|
||||||
|
return driver.StandardGamepadAxisRightStickHorizontal, true
|
||||||
|
case "righty":
|
||||||
|
return driver.StandardGamepadAxisRightStickVertical, true
|
||||||
|
default:
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func HasStandardLayoutMapping(id string) bool {
|
||||||
|
mappingsM.RLock()
|
||||||
|
defer mappingsM.RUnlock()
|
||||||
|
|
||||||
|
if _, ok := gamepadButtonMappings[id]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, ok := gamepadAxisMappings[id]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type GamepadState interface {
|
||||||
|
Axis(index int) float64
|
||||||
|
Button(index int) bool
|
||||||
|
Hat(index int) int
|
||||||
|
}
|
||||||
|
|
||||||
|
func AxisValue(id string, axis driver.StandardGamepadAxis, state GamepadState) float64 {
|
||||||
|
mappingsM.RLock()
|
||||||
|
defer mappingsM.RUnlock()
|
||||||
|
|
||||||
|
mappings, ok := gamepadAxisMappings[id]
|
||||||
|
if !ok {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
switch m := mappings[axis]; m.Type {
|
||||||
|
case mappingTypeAxis:
|
||||||
|
v := state.Axis(m.Index)*float64(m.AxisScale) + float64(m.AxisOffset)
|
||||||
|
if v > 1 {
|
||||||
|
return 1
|
||||||
|
} else if v < -1 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
case mappingTypeButton:
|
||||||
|
if state.Button(m.Index) {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
case mappingTypeHat:
|
||||||
|
if state.Hat(m.Index)&m.HatState != 0 {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsButtonPressed(id string, button driver.StandardGamepadButton, state GamepadState) bool {
|
||||||
|
mappingsM.RLock()
|
||||||
|
defer mappingsM.RUnlock()
|
||||||
|
|
||||||
|
mappings, ok := gamepadButtonMappings[id]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch m := mappings[button]; m.Type {
|
||||||
|
case mappingTypeAxis:
|
||||||
|
v := state.Axis(m.Index)*float64(m.AxisScale) + float64(m.AxisOffset)
|
||||||
|
if m.AxisOffset < 0 || m.AxisOffset == 0 && m.AxisScale > 0 {
|
||||||
|
return v >= 0
|
||||||
|
} else {
|
||||||
|
return v <= 0
|
||||||
|
}
|
||||||
|
case mappingTypeButton:
|
||||||
|
return state.Button(m.Index)
|
||||||
|
case mappingTypeHat:
|
||||||
|
return state.Hat(m.Index)&m.HatState != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update adds new gamepad mappings.
|
||||||
|
// The string must be in the format of SDL_GameControllerDB.
|
||||||
|
func Update(mapping []byte) (bool, error) {
|
||||||
|
if currentPlatform == platformUnknown {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement this (#1557)
|
||||||
|
if currentPlatform == platformAndroid || currentPlatform == platformIOS {
|
||||||
|
// Note: NOT returning an error, as mappings also do not matter right now.
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mappingsM.Lock()
|
||||||
|
defer mappingsM.Unlock()
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(mapping)
|
||||||
|
r := bufio.NewReader(buf)
|
||||||
|
for {
|
||||||
|
line, err := r.ReadString('\n')
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if err := processLine(line, currentPlatform); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
20
internal/gamepaddb/gamepaddb_ios.go
Normal file
20
internal/gamepaddb/gamepaddb_ios.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
//go:build darwin && ios
|
||||||
|
// +build darwin,ios
|
||||||
|
|
||||||
|
package gamepaddb
|
||||||
|
|
||||||
|
const isIOS = true
|
20
internal/gamepaddb/gamepaddb_notios.go
Normal file
20
internal/gamepaddb/gamepaddb_notios.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
//go:build !darwin || !ios
|
||||||
|
// +build !darwin !ios
|
||||||
|
|
||||||
|
package gamepaddb
|
||||||
|
|
||||||
|
const isIOS = false
|
@ -24,8 +24,6 @@ import (
|
|||||||
type (
|
type (
|
||||||
Action int
|
Action int
|
||||||
ErrorCode int
|
ErrorCode int
|
||||||
GamepadAxis int
|
|
||||||
GamepadButton int
|
|
||||||
Hint int
|
Hint int
|
||||||
InputMode int
|
InputMode int
|
||||||
Joystick int
|
Joystick int
|
||||||
@ -169,30 +167,3 @@ const (
|
|||||||
HatLeftUp = HatLeft | HatUp
|
HatLeftUp = HatLeft | HatUp
|
||||||
HatLeftDown = HatLeft | HatDown
|
HatLeftDown = HatLeft | HatDown
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
AxisLeftX = GamepadAxis(0)
|
|
||||||
AxisLeftY = GamepadAxis(1)
|
|
||||||
AxisRightX = GamepadAxis(2)
|
|
||||||
AxisRightY = GamepadAxis(3)
|
|
||||||
AxisLeftTrigger = GamepadAxis(4)
|
|
||||||
AxisRightTrigger = GamepadAxis(5)
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
ButtonA = GamepadButton(0)
|
|
||||||
ButtonB = GamepadButton(1)
|
|
||||||
ButtonX = GamepadButton(2)
|
|
||||||
ButtonY = GamepadButton(3)
|
|
||||||
ButtonLeftBumper = GamepadButton(4)
|
|
||||||
ButtonRightBumper = GamepadButton(5)
|
|
||||||
ButtonBack = GamepadButton(6)
|
|
||||||
ButtonStart = GamepadButton(7)
|
|
||||||
ButtonGuide = GamepadButton(8)
|
|
||||||
ButtonLeftThumb = GamepadButton(9)
|
|
||||||
ButtonRightThumb = GamepadButton(10)
|
|
||||||
ButtonDpadUp = GamepadButton(11)
|
|
||||||
ButtonDpadRight = GamepadButton(12)
|
|
||||||
ButtonDpadDown = GamepadButton(13)
|
|
||||||
ButtonDpadLeft = GamepadButton(14)
|
|
||||||
)
|
|
||||||
|
@ -289,19 +289,6 @@ func (j Joystick) GetHats() []JoystickHatState {
|
|||||||
return hats
|
return hats
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j Joystick) GetGamepadState() *GamepadState {
|
|
||||||
s := glfw.Joystick(j).GetGamepadState()
|
|
||||||
if s == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
state := &GamepadState{}
|
|
||||||
for i, b := range s.Buttons {
|
|
||||||
state.Buttons[i] = Action(b)
|
|
||||||
}
|
|
||||||
copy(state.Axes[:], s.Axes[:])
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetMonitors() []*Monitor {
|
func GetMonitors() []*Monitor {
|
||||||
ms := []*Monitor{}
|
ms := []*Monitor{}
|
||||||
for _, m := range glfw.GetMonitors() {
|
for _, m := range glfw.GetMonitors() {
|
||||||
@ -360,10 +347,6 @@ func Terminate() {
|
|||||||
glfw.Terminate()
|
glfw.Terminate()
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateGamepadMappings(mapping string) bool {
|
|
||||||
return glfw.UpdateGamepadMappings(mapping)
|
|
||||||
}
|
|
||||||
|
|
||||||
func WaitEvents() {
|
func WaitEvents() {
|
||||||
glfw.WaitEvents()
|
glfw.WaitEvents()
|
||||||
}
|
}
|
||||||
|
@ -435,25 +435,6 @@ func (j Joystick) GetHats() []JoystickHatState {
|
|||||||
return hats
|
return hats
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j Joystick) GetGamepadState() *GamepadState {
|
|
||||||
var s struct {
|
|
||||||
Buttons [15]uint8
|
|
||||||
Axes [6]float32
|
|
||||||
}
|
|
||||||
r := glfwDLL.call("glfwGetGamepadState", uintptr(j), uintptr(unsafe.Pointer(&s)))
|
|
||||||
panicError()
|
|
||||||
if r != True {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
state := &GamepadState{}
|
|
||||||
for i, b := range s.Buttons {
|
|
||||||
state.Buttons[i] = Action(b)
|
|
||||||
}
|
|
||||||
copy(state.Axes[:], s.Axes[:])
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetMonitors() []*Monitor {
|
func GetMonitors() []*Monitor {
|
||||||
var l int32
|
var l int32
|
||||||
ptr := glfwDLL.call("glfwGetMonitors", uintptr(unsafe.Pointer(&l)))
|
ptr := glfwDLL.call("glfwGetMonitors", uintptr(unsafe.Pointer(&l)))
|
||||||
@ -551,14 +532,6 @@ func Terminate() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateGamepadMappings(mapping string) bool {
|
|
||||||
m := append([]byte(mapping), 0)
|
|
||||||
defer runtime.KeepAlive(m)
|
|
||||||
r := glfwDLL.call("glfwUpdateGamepadMappings", uintptr(unsafe.Pointer(&m[0])))
|
|
||||||
panicError()
|
|
||||||
return byte(r) == True
|
|
||||||
}
|
|
||||||
|
|
||||||
func WaitEvents() {
|
func WaitEvents() {
|
||||||
glfwDLL.call("glfwWaitEvents")
|
glfwDLL.call("glfwWaitEvents")
|
||||||
panicError()
|
panicError()
|
||||||
|
@ -33,8 +33,3 @@ type VidMode struct {
|
|||||||
BlueBits int
|
BlueBits int
|
||||||
RefreshRate int
|
RefreshRate int
|
||||||
}
|
}
|
||||||
|
|
||||||
type GamepadState struct {
|
|
||||||
Buttons [15]Action
|
|
||||||
Axes [6]float32
|
|
||||||
}
|
|
||||||
|
@ -23,12 +23,12 @@
|
|||||||
package glfw
|
package glfw
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"math"
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/driver"
|
"github.com/hajimehoshi/ebiten/v2/internal/driver"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/internal/gamepaddb"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/glfw"
|
"github.com/hajimehoshi/ebiten/v2/internal/glfw"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -40,7 +40,8 @@ type gamepad struct {
|
|||||||
axes [16]float64
|
axes [16]float64
|
||||||
buttonNum int
|
buttonNum int
|
||||||
buttonPressed [256]bool
|
buttonPressed [256]bool
|
||||||
state *glfw.GamepadState
|
hatsNum int
|
||||||
|
hats [16]int
|
||||||
}
|
}
|
||||||
|
|
||||||
type Input struct {
|
type Input struct {
|
||||||
@ -319,12 +320,6 @@ func (i *Input) update(window *glfw.Window, context driver.UIContext) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
i.gamepads[id].state = id.GetGamepadState()
|
|
||||||
|
|
||||||
// Note that GLFW's gamepad GUID follows SDL's GUID.
|
|
||||||
i.gamepads[id].guid = id.GetGUID()
|
|
||||||
i.gamepads[id].name = id.GetName()
|
|
||||||
|
|
||||||
buttons := id.GetButtons()
|
buttons := id.GetButtons()
|
||||||
|
|
||||||
// A gamepad can be detected even though there are not. Apparently, some special devices are
|
// A gamepad can be detected even though there are not. Apparently, some special devices are
|
||||||
@ -354,6 +349,20 @@ func (i *Input) update(window *glfw.Window, context driver.UIContext) {
|
|||||||
}
|
}
|
||||||
i.gamepads[id].axes[a] = float64(axes32[a])
|
i.gamepads[id].axes[a] = float64(axes32[a])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hats := id.GetHats()
|
||||||
|
i.gamepads[id].hatsNum = len(hats)
|
||||||
|
for h := 0; h < len(i.gamepads[id].hats); h++ {
|
||||||
|
if len(hats) <= h {
|
||||||
|
i.gamepads[id].hats[h] = 0
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
i.gamepads[id].hats[h] = int(hats[h])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that GLFW's gamepad GUID follows SDL's GUID.
|
||||||
|
i.gamepads[id].guid = id.GetGUID()
|
||||||
|
i.gamepads[id].name = id.GetName()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,7 +374,7 @@ func (i *Input) IsStandardGamepadLayoutAvailable(id driver.GamepadID) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
g := i.gamepads[int(id)]
|
g := i.gamepads[int(id)]
|
||||||
return g.state != nil
|
return gamepaddb.HasStandardLayoutMapping(g.guid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) StandardGamepadAxisValue(id driver.GamepadID, axis driver.StandardGamepadAxis) float64 {
|
func (i *Input) StandardGamepadAxisValue(id driver.GamepadID, axis driver.StandardGamepadAxis) float64 {
|
||||||
@ -376,10 +385,7 @@ func (i *Input) StandardGamepadAxisValue(id driver.GamepadID, axis driver.Standa
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
g := i.gamepads[int(id)]
|
g := i.gamepads[int(id)]
|
||||||
if g.state == nil {
|
return gamepaddb.AxisValue(g.guid, axis, gamepadState{&g})
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return float64(g.state.Axes[standardAxisToGLFWAxis(axis)])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) IsStandardGamepadButtonPressed(id driver.GamepadID, button driver.StandardGamepadButton) bool {
|
func (i *Input) IsStandardGamepadButtonPressed(id driver.GamepadID, button driver.StandardGamepadButton) bool {
|
||||||
@ -390,89 +396,37 @@ func (i *Input) IsStandardGamepadButtonPressed(id driver.GamepadID, button drive
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
g := i.gamepads[int(id)]
|
g := i.gamepads[int(id)]
|
||||||
if g.state == nil {
|
return gamepaddb.IsButtonPressed(g.guid, button, gamepadState{&g})
|
||||||
return false
|
|
||||||
}
|
|
||||||
switch button {
|
|
||||||
case driver.StandardGamepadButtonFrontBottomLeft:
|
|
||||||
return g.state.Axes[glfw.AxisLeftTrigger] > 0
|
|
||||||
case driver.StandardGamepadButtonFrontBottomRight:
|
|
||||||
return g.state.Axes[glfw.AxisRightTrigger] > 0
|
|
||||||
}
|
|
||||||
return g.state.Buttons[standardButtonToGLFWButton(button)] == glfw.Press
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func standardAxisToGLFWAxis(axis driver.StandardGamepadAxis) glfw.GamepadAxis {
|
func init() {
|
||||||
switch axis {
|
// Confirm that all the hat state values are the same.
|
||||||
case driver.StandardGamepadAxisLeftStickHorizontal:
|
if gamepaddb.HatUp != glfw.HatUp {
|
||||||
return glfw.AxisLeftX
|
panic("glfw: gamepaddb.HatUp must equal to glfw.HatUp but not")
|
||||||
case driver.StandardGamepadAxisLeftStickVertical:
|
}
|
||||||
return glfw.AxisLeftY
|
if gamepaddb.HatRight != glfw.HatRight {
|
||||||
case driver.StandardGamepadAxisRightStickHorizontal:
|
panic("glfw: gamepaddb.HatRight must equal to glfw.HatRight but not")
|
||||||
return glfw.AxisRightX
|
}
|
||||||
case driver.StandardGamepadAxisRightStickVertical:
|
if gamepaddb.HatDown != glfw.HatDown {
|
||||||
return glfw.AxisRightY
|
panic("glfw: gamepaddb.HatDown must equal to glfw.HatDown but not")
|
||||||
default:
|
}
|
||||||
panic(fmt.Sprintf("glfw: invalid or inconvertible StandardGamepadAxis: %d", axis))
|
if gamepaddb.HatLeft != glfw.HatLeft {
|
||||||
|
panic("glfw: gamepaddb.HatLeft must equal to glfw.HatLeft but not")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func standardButtonToGLFWButton(button driver.StandardGamepadButton) glfw.GamepadButton {
|
type gamepadState struct {
|
||||||
switch button {
|
g *gamepad
|
||||||
case driver.StandardGamepadButtonRightBottom:
|
|
||||||
return glfw.ButtonA
|
|
||||||
case driver.StandardGamepadButtonRightRight:
|
|
||||||
return glfw.ButtonB
|
|
||||||
case driver.StandardGamepadButtonRightLeft:
|
|
||||||
return glfw.ButtonX
|
|
||||||
case driver.StandardGamepadButtonRightTop:
|
|
||||||
return glfw.ButtonY
|
|
||||||
case driver.StandardGamepadButtonFrontTopLeft:
|
|
||||||
return glfw.ButtonLeftBumper
|
|
||||||
case driver.StandardGamepadButtonFrontTopRight:
|
|
||||||
return glfw.ButtonRightBumper
|
|
||||||
case driver.StandardGamepadButtonCenterLeft:
|
|
||||||
return glfw.ButtonBack
|
|
||||||
case driver.StandardGamepadButtonCenterRight:
|
|
||||||
return glfw.ButtonStart
|
|
||||||
case driver.StandardGamepadButtonLeftStick:
|
|
||||||
return glfw.ButtonLeftThumb
|
|
||||||
case driver.StandardGamepadButtonRightStick:
|
|
||||||
return glfw.ButtonRightThumb
|
|
||||||
case driver.StandardGamepadButtonLeftTop:
|
|
||||||
return glfw.ButtonDpadUp
|
|
||||||
case driver.StandardGamepadButtonLeftBottom:
|
|
||||||
return glfw.ButtonDpadDown
|
|
||||||
case driver.StandardGamepadButtonLeftLeft:
|
|
||||||
return glfw.ButtonDpadLeft
|
|
||||||
case driver.StandardGamepadButtonLeftRight:
|
|
||||||
return glfw.ButtonDpadRight
|
|
||||||
case driver.StandardGamepadButtonCenterCenter:
|
|
||||||
return glfw.ButtonGuide
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("glfw: invalid or inconvertible StandardGamepadButton: %d", button))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateStandardGamepadLayoutMappings can be used to provide new gamepad mappings to Ebiten.
|
func (s gamepadState) Axis(index int) float64 {
|
||||||
// The string must be in the format of SDL_GameControllerDB.
|
return s.g.axes[index]
|
||||||
func (i *Input) UpdateStandardGamepadLayoutMappings(mapping string) (bool, error) {
|
|
||||||
var err error
|
|
||||||
if i.ui.isRunning() {
|
|
||||||
err = i.ui.t.Call(func() error {
|
|
||||||
return i.updateStandardGamepadLayoutMappings(mapping)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
err = i.updateStandardGamepadLayoutMappings(mapping)
|
|
||||||
}
|
|
||||||
return err == nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) updateStandardGamepadLayoutMappings(mapping string) error {
|
func (s gamepadState) Button(index int) bool {
|
||||||
if !glfw.UpdateGamepadMappings(mapping) {
|
return s.g.buttonPressed[index]
|
||||||
// Would be nice if we could actually get the error string.
|
}
|
||||||
// However, go-gl currently does not provide it in any way.
|
|
||||||
return fmt.Errorf("glfw: could not parse or update gamepad mappings")
|
func (s gamepadState) Hat(index int) int {
|
||||||
}
|
return s.g.hats[index]
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
@ -172,10 +172,6 @@ func initialize() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !glfw.UpdateGamepadMappings(string(gamecontrollerdbTxt)) {
|
|
||||||
return fmt.Errorf("glfw: UpdateGamepadMappings failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
glfw.WindowHint(glfw.Visible, glfw.False)
|
glfw.WindowHint(glfw.Visible, glfw.False)
|
||||||
glfw.WindowHint(glfw.ClientAPI, glfw.NoAPI)
|
glfw.WindowHint(glfw.ClientAPI, glfw.NoAPI)
|
||||||
|
|
||||||
|
@ -496,9 +496,3 @@ func (i *Input) IsStandardGamepadButtonPressed(id driver.GamepadID, button drive
|
|||||||
}
|
}
|
||||||
return g.standardButtonPressed[button]
|
return g.standardButtonPressed[button]
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateStandardGamepadLayoutMappings is not supported for JS - the browser maintains the mappings.
|
|
||||||
func (i *Input) UpdateStandardGamepadLayoutMappings(mapping string) (bool, error) {
|
|
||||||
// All OK - browser owns this though.
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
@ -147,13 +147,6 @@ func (i *Input) StandardGamepadAxisValue(id driver.GamepadID, axis driver.Standa
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateStandardGamepadLayoutMappings is not supported on mobile - this still needs to be implemented.
|
|
||||||
func (i *Input) UpdateStandardGamepadLayoutMappings(mapping string) (bool, error) {
|
|
||||||
// TODO: Implement this (#1557)
|
|
||||||
// Note: NOT returning an error, as mappings also do not matter right now (all functions above return nothing is pressed anyway).
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Input) AppendTouchIDs(touchIDs []driver.TouchID) []driver.TouchID {
|
func (i *Input) AppendTouchIDs(touchIDs []driver.TouchID) []driver.TouchID {
|
||||||
i.ui.m.RLock()
|
i.ui.m.RLock()
|
||||||
defer i.ui.m.RUnlock()
|
defer i.ui.m.RUnlock()
|
||||||
|
Loading…
Reference in New Issue
Block a user