mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-13 04:22:05 +01:00
internal/gamepad: move the implementation of gamepad for GOOS=js
Updates #1957
This commit is contained in:
parent
82298edfae
commit
5edfd1b743
@ -12,13 +12,14 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
//go:build darwin && !ios
|
//go:build (darwin && !ios) || js
|
||||||
// +build darwin,!ios
|
// +build darwin,!ios js
|
||||||
|
|
||||||
package gamepad
|
package gamepad
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"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/gamepaddb"
|
||||||
@ -47,6 +48,7 @@ var theGamepads gamepads
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
theGamepads.nativeGamepads.init()
|
theGamepads.nativeGamepads.init()
|
||||||
|
theGamepads.nativeGamepads.gamepads = &theGamepads
|
||||||
}
|
}
|
||||||
|
|
||||||
func AppendGamepadIDs(ids []driver.GamepadID) []driver.GamepadID {
|
func AppendGamepadIDs(ids []driver.GamepadID) []driver.GamepadID {
|
||||||
@ -62,9 +64,6 @@ func Get(id driver.GamepadID) *Gamepad {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *gamepads) appendGamepadIDs(ids []driver.GamepadID) []driver.GamepadID {
|
func (g *gamepads) appendGamepadIDs(ids []driver.GamepadID) []driver.GamepadID {
|
||||||
g.m.Lock()
|
|
||||||
defer g.m.Unlock()
|
|
||||||
|
|
||||||
for i, gp := range g.gamepads {
|
for i, gp := range g.gamepads {
|
||||||
if gp != nil && gp.present() {
|
if gp != nil && gp.present() {
|
||||||
ids = append(ids, driver.GamepadID(i))
|
ids = append(ids, driver.GamepadID(i))
|
||||||
@ -77,6 +76,7 @@ func (g *gamepads) update() {
|
|||||||
g.m.Lock()
|
g.m.Lock()
|
||||||
defer g.m.Unlock()
|
defer g.m.Unlock()
|
||||||
|
|
||||||
|
g.nativeGamepads.update()
|
||||||
for _, gp := range g.gamepads {
|
for _, gp := range g.gamepads {
|
||||||
if gp != nil {
|
if gp != nil {
|
||||||
gp.update()
|
gp.update()
|
||||||
@ -87,7 +87,10 @@ func (g *gamepads) update() {
|
|||||||
func (g *gamepads) get(id driver.GamepadID) *Gamepad {
|
func (g *gamepads) get(id driver.GamepadID) *Gamepad {
|
||||||
g.m.Lock()
|
g.m.Lock()
|
||||||
defer g.m.Unlock()
|
defer g.m.Unlock()
|
||||||
|
return g.getImpl(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gamepads) getImpl(id driver.GamepadID) *Gamepad {
|
||||||
if id < 0 || int(id) >= len(g.gamepads) {
|
if id < 0 || int(id) >= len(g.gamepads) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -97,7 +100,10 @@ func (g *gamepads) get(id driver.GamepadID) *Gamepad {
|
|||||||
func (g *gamepads) find(cond func(*Gamepad) bool) *Gamepad {
|
func (g *gamepads) find(cond func(*Gamepad) bool) *Gamepad {
|
||||||
g.m.Lock()
|
g.m.Lock()
|
||||||
defer g.m.Unlock()
|
defer g.m.Unlock()
|
||||||
|
return g.findImpl(cond)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gamepads) findImpl(cond func(*Gamepad) bool) *Gamepad {
|
||||||
for _, gp := range g.gamepads {
|
for _, gp := range g.gamepads {
|
||||||
if gp == nil {
|
if gp == nil {
|
||||||
continue
|
continue
|
||||||
@ -112,7 +118,10 @@ func (g *gamepads) find(cond func(*Gamepad) bool) *Gamepad {
|
|||||||
func (g *gamepads) add(name, sdlID string) *Gamepad {
|
func (g *gamepads) add(name, sdlID string) *Gamepad {
|
||||||
g.m.Lock()
|
g.m.Lock()
|
||||||
defer g.m.Unlock()
|
defer g.m.Unlock()
|
||||||
|
return g.addImpl(name, sdlID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gamepads) addImpl(name, sdlID string) *Gamepad {
|
||||||
for i, gp := range g.gamepads {
|
for i, gp := range g.gamepads {
|
||||||
if gp == nil {
|
if gp == nil {
|
||||||
gp := &Gamepad{
|
gp := &Gamepad{
|
||||||
@ -135,7 +144,10 @@ func (g *gamepads) add(name, sdlID string) *Gamepad {
|
|||||||
func (g *gamepads) remove(cond func(*Gamepad) bool) {
|
func (g *gamepads) remove(cond func(*Gamepad) bool) {
|
||||||
g.m.Lock()
|
g.m.Lock()
|
||||||
defer g.m.Unlock()
|
defer g.m.Unlock()
|
||||||
|
g.removeImpl(cond)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gamepads) removeImpl(cond func(*Gamepad) bool) {
|
||||||
for i, gp := range g.gamepads {
|
for i, gp := range g.gamepads {
|
||||||
if gp == nil {
|
if gp == nil {
|
||||||
continue
|
continue
|
||||||
@ -220,17 +232,42 @@ func (g *Gamepad) IsStandardLayoutAvailable() bool {
|
|||||||
g.m.Lock()
|
g.m.Lock()
|
||||||
defer g.m.Unlock()
|
defer g.m.Unlock()
|
||||||
|
|
||||||
return gamepaddb.HasStandardLayoutMapping(g.sdlID)
|
if gamepaddb.HasStandardLayoutMapping(g.sdlID) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return g.hasOwnStandardLayoutMapping()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Gamepad) StandardAxisValue(axis driver.StandardGamepadAxis) float64 {
|
func (g *Gamepad) StandardAxisValue(axis driver.StandardGamepadAxis) float64 {
|
||||||
return gamepaddb.AxisValue(g.sdlID, axis, g)
|
if gamepaddb.HasStandardLayoutMapping(g.sdlID) {
|
||||||
|
return gamepaddb.AxisValue(g.sdlID, axis, g)
|
||||||
|
}
|
||||||
|
if g.hasOwnStandardLayoutMapping() {
|
||||||
|
return g.nativeGamepad.axisValue(int(axis))
|
||||||
|
}
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Gamepad) StandardButtonValue(button driver.StandardGamepadButton) float64 {
|
func (g *Gamepad) StandardButtonValue(button driver.StandardGamepadButton) float64 {
|
||||||
return gamepaddb.ButtonValue(g.sdlID, button, g)
|
if gamepaddb.HasStandardLayoutMapping(g.sdlID) {
|
||||||
|
return gamepaddb.ButtonValue(g.sdlID, button, g)
|
||||||
|
}
|
||||||
|
if g.hasOwnStandardLayoutMapping() {
|
||||||
|
return g.nativeGamepad.buttonValue(int(button))
|
||||||
|
}
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Gamepad) IsStandardButtonPressed(button driver.StandardGamepadButton) bool {
|
func (g *Gamepad) IsStandardButtonPressed(button driver.StandardGamepadButton) bool {
|
||||||
return gamepaddb.IsButtonPressed(g.sdlID, button, g)
|
if gamepaddb.HasStandardLayoutMapping(g.sdlID) {
|
||||||
|
return gamepaddb.IsButtonPressed(g.sdlID, button, g)
|
||||||
|
}
|
||||||
|
if g.hasOwnStandardLayoutMapping() {
|
||||||
|
return g.nativeGamepad.isButtonPressed(int(button))
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Gamepad) Vibrate(duration time.Duration, strongMagnitude float64, weakMagnitude float64) {
|
||||||
|
g.nativeGamepad.vibrate(duration, strongMagnitude, weakMagnitude)
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ package gamepad
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -57,6 +58,8 @@ import (
|
|||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
type nativeGamepads struct {
|
type nativeGamepads struct {
|
||||||
|
gamepads *gamepads
|
||||||
|
|
||||||
hidManager C.IOHIDManagerRef
|
hidManager C.IOHIDManagerRef
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,6 +173,10 @@ func (g *nativeGamepad) update() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *nativeGamepad) hasOwnStandardLayoutMapping() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (g *nativeGamepad) axisNum() int {
|
func (g *nativeGamepad) axisNum() int {
|
||||||
return len(g.axisValues)
|
return len(g.axisValues)
|
||||||
}
|
}
|
||||||
@ -189,6 +196,10 @@ func (g *nativeGamepad) axisValue(axis int) float64 {
|
|||||||
return g.axisValues[axis]
|
return g.axisValues[axis]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *nativeGamepad) buttonValue(button int) float64 {
|
||||||
|
panic("gamepad: buttonValue is not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
func (g *nativeGamepad) isButtonPressed(button int) bool {
|
func (g *nativeGamepad) isButtonPressed(button int) bool {
|
||||||
if button < 0 || button >= len(g.buttonValues) {
|
if button < 0 || button >= len(g.buttonValues) {
|
||||||
return false
|
return false
|
||||||
@ -203,6 +214,10 @@ func (g *nativeGamepad) hatState(hat int) int {
|
|||||||
return g.hatValues[hat]
|
return g.hatValues[hat]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *nativeGamepad) vibrate(duration time.Duration, strongMagnitude float64, weakMagnitude float64) {
|
||||||
|
// TODO: Implement this (#1452)
|
||||||
|
}
|
||||||
|
|
||||||
func (g *nativeGamepads) init() {
|
func (g *nativeGamepads) init() {
|
||||||
var dicts []C.CFDictionaryRef
|
var dicts []C.CFDictionaryRef
|
||||||
|
|
||||||
@ -399,3 +414,6 @@ func ebitenGamepadRemovalCallback(ctx unsafe.Pointer, res C.IOReturn, sender uns
|
|||||||
return g.device == device
|
return g.device == device
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *nativeGamepads) update() {
|
||||||
|
}
|
||||||
|
183
internal/gamepad/gamepad_js.go
Normal file
183
internal/gamepad/gamepad_js.go
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
// Copyright 2022 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 gamepad
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"syscall/js"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
object = js.Global().Get("Object")
|
||||||
|
go2cpp = js.Global().Get("go2cpp")
|
||||||
|
)
|
||||||
|
|
||||||
|
type nativeGamepads struct {
|
||||||
|
gamepads *gamepads
|
||||||
|
|
||||||
|
indices map[int]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *nativeGamepads) init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *nativeGamepads) update() {
|
||||||
|
// TODO: Use the gamepad events instead of navigator.getGamepads after go2cpp is removed.
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
for k := range g.indices {
|
||||||
|
delete(g.indices, k)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
nav := js.Global().Get("navigator")
|
||||||
|
if !nav.Truthy() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
gps := nav.Call("getGamepads")
|
||||||
|
if !gps.Truthy() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := gps.Length()
|
||||||
|
for idx := 0; idx < l; idx++ {
|
||||||
|
gp := gps.Index(idx)
|
||||||
|
if !gp.Truthy() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
index := gp.Get("index").Int()
|
||||||
|
|
||||||
|
if g.indices == nil {
|
||||||
|
g.indices = map[int]struct{}{}
|
||||||
|
}
|
||||||
|
g.indices[index] = struct{}{}
|
||||||
|
|
||||||
|
// The gamepad is not registered yet, register this.
|
||||||
|
gamepad := g.gamepads.findImpl(func(gamepad *Gamepad) bool {
|
||||||
|
return index == gamepad.index
|
||||||
|
})
|
||||||
|
if gamepad == nil {
|
||||||
|
name := gp.Get("id").String()
|
||||||
|
|
||||||
|
// This emulates the implementation of EMSCRIPTEN_JoystickGetDeviceGUID.
|
||||||
|
// https://github.com/libsdl-org/SDL/blob/0e9560aea22818884921e5e5064953257bfe7fa7/src/joystick/emscripten/SDL_sysjoystick.c#L385
|
||||||
|
var sdlID [16]byte
|
||||||
|
copy(sdlID[:], []byte(name))
|
||||||
|
|
||||||
|
gamepad = g.gamepads.addImpl(name, hex.EncodeToString(sdlID[:]))
|
||||||
|
gamepad.index = index
|
||||||
|
gamepad.mapping = gp.Get("mapping").String()
|
||||||
|
}
|
||||||
|
gamepad.value = gp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove an unused gamepads.
|
||||||
|
g.gamepads.removeImpl(func(gamepad *Gamepad) bool {
|
||||||
|
_, ok := g.indices[gamepad.index]
|
||||||
|
return !ok
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type nativeGamepad struct {
|
||||||
|
value js.Value
|
||||||
|
index int
|
||||||
|
mapping string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *nativeGamepad) present() bool {
|
||||||
|
return g.value.Truthy()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *nativeGamepad) hasOwnStandardLayoutMapping() bool {
|
||||||
|
// With go2cpp, the controller must have the standard
|
||||||
|
if go2cpp.Truthy() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return g.mapping == "standard"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *nativeGamepad) update() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *nativeGamepad) axisNum() int {
|
||||||
|
return g.value.Get("axes").Length()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *nativeGamepad) buttonNum() int {
|
||||||
|
return g.value.Get("buttons").Length()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *nativeGamepad) hatNum() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *nativeGamepad) axisValue(axis int) float64 {
|
||||||
|
axes := g.value.Get("axes")
|
||||||
|
if axis < 0 || axis >= axes.Length() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return axes.Index(axis).Float()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *nativeGamepad) buttonValue(button int) float64 {
|
||||||
|
buttons := g.value.Get("buttons")
|
||||||
|
if button < 0 || button >= buttons.Length() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return buttons.Index(button).Get("value").Float()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *nativeGamepad) isButtonPressed(button int) bool {
|
||||||
|
buttons := g.value.Get("buttons")
|
||||||
|
if button < 0 || button >= buttons.Length() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return buttons.Index(button).Get("pressed").Bool()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *nativeGamepad) hatState(hat int) int {
|
||||||
|
return hatCentered
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *nativeGamepad) vibrate(duration time.Duration, strongMagnitude float64, weakMagnitude float64) {
|
||||||
|
// vibrationActuator is avaialble on Chrome.
|
||||||
|
if va := g.value.Get("vibrationActuator"); va.Truthy() {
|
||||||
|
if !va.Get("playEffect").Truthy() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
prop := object.New()
|
||||||
|
prop.Set("startDelay", 0)
|
||||||
|
prop.Set("duration", float64(duration/time.Millisecond))
|
||||||
|
prop.Set("strongMagnitude", strongMagnitude)
|
||||||
|
prop.Set("weakMagnitude", weakMagnitude)
|
||||||
|
va.Call("playEffect", "dual-rumble", prop)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// hapticActuators is available on Firefox.
|
||||||
|
if ha := g.value.Get("hapticActuators"); ha.Truthy() {
|
||||||
|
// TODO: Is this order correct?
|
||||||
|
if ha.Length() > 0 {
|
||||||
|
ha.Index(0).Call("pulse", strongMagnitude, float64(duration/time.Millisecond))
|
||||||
|
}
|
||||||
|
if ha.Length() > 1 {
|
||||||
|
ha.Index(1).Call("pulse", weakMagnitude, float64(duration/time.Millisecond))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
@ -129,5 +129,9 @@ func (i *Input) IsStandardGamepadButtonPressed(id driver.GamepadID, button drive
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) VibrateGamepad(id driver.GamepadID, duration time.Duration, strongMagnitude float64, weakMagnitude float64) {
|
func (i *Input) VibrateGamepad(id driver.GamepadID, duration time.Duration, strongMagnitude float64, weakMagnitude float64) {
|
||||||
// TODO: Implement this (#1452)
|
g := gamepadpkg.Get(id)
|
||||||
|
if g == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.Vibrate(duration, strongMagnitude, weakMagnitude)
|
||||||
}
|
}
|
||||||
|
@ -15,16 +15,14 @@
|
|||||||
package js
|
package js
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
|
||||||
"syscall/js"
|
"syscall/js"
|
||||||
"time"
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/driver"
|
"github.com/hajimehoshi/ebiten/v2/internal/driver"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/internal/gamepad"
|
||||||
)
|
)
|
||||||
|
|
||||||
var object = js.Global().Get("Object")
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
stringKeydown = js.ValueOf("keydown")
|
stringKeydown = js.ValueOf("keydown")
|
||||||
stringKeypress = js.ValueOf("keypress")
|
stringKeypress = js.ValueOf("keypress")
|
||||||
@ -62,30 +60,6 @@ type pos struct {
|
|||||||
Y int
|
Y int
|
||||||
}
|
}
|
||||||
|
|
||||||
type gamepad struct {
|
|
||||||
value js.Value
|
|
||||||
|
|
||||||
name string
|
|
||||||
mapping string
|
|
||||||
axisNum int
|
|
||||||
axes [16]float64
|
|
||||||
buttonNum int
|
|
||||||
buttonPressed [256]bool
|
|
||||||
buttonValues [256]float64
|
|
||||||
|
|
||||||
standardButtonPressed [driver.StandardGamepadButtonMax + 1]bool
|
|
||||||
standardButtonValues [driver.StandardGamepadButtonMax + 1]float64
|
|
||||||
standardAxisValues [driver.StandardGamepadAxisMax + 1]float64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *gamepad) hasStandardLayoutMapping() bool {
|
|
||||||
// With go2cpp, the controller must have the standard
|
|
||||||
if go2cpp.Truthy() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return g.mapping == "standard"
|
|
||||||
}
|
|
||||||
|
|
||||||
type Input struct {
|
type Input struct {
|
||||||
keyPressed map[int]bool
|
keyPressed map[int]bool
|
||||||
keyPressedEdge map[int]bool
|
keyPressedEdge map[int]bool
|
||||||
@ -96,7 +70,6 @@ type Input struct {
|
|||||||
origCursorY int
|
origCursorY int
|
||||||
wheelX float64
|
wheelX float64
|
||||||
wheelY float64
|
wheelY float64
|
||||||
gamepads map[driver.GamepadID]gamepad
|
|
||||||
touches map[driver.TouchID]pos
|
touches map[driver.TouchID]pos
|
||||||
runeBuffer []rune
|
runeBuffer []rune
|
||||||
ui *UserInterface
|
ui *UserInterface
|
||||||
@ -111,71 +84,58 @@ func (i *Input) CursorPosition() (x, y int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) GamepadSDLID(id driver.GamepadID) string {
|
func (i *Input) GamepadSDLID(id driver.GamepadID) string {
|
||||||
// This emulates the implementation of EMSCRIPTEN_JoystickGetDeviceGUID.
|
g := gamepad.Get(id)
|
||||||
// https://github.com/libsdl-org/SDL/blob/0e9560aea22818884921e5e5064953257bfe7fa7/src/joystick/emscripten/SDL_sysjoystick.c#L385
|
if g == nil {
|
||||||
g, ok := i.gamepads[id]
|
|
||||||
if !ok {
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
var sdlid [16]byte
|
return g.SDLID()
|
||||||
copy(sdlid[:], []byte(g.name))
|
|
||||||
return hex.EncodeToString(sdlid[:])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GamepadName returns a string containing some information about the controller.
|
// GamepadName returns a string containing some information about the controller.
|
||||||
// A PS2 controller returned "810-3-USB Gamepad" on Firefox
|
// A PS2 controller returned "810-3-USB Gamepad" on Firefox
|
||||||
// A Xbox 360 controller returned "xinput" on Firefox and "Xbox 360 Controller (XInput STANDARD GAMEPAD)" on Chrome
|
// A Xbox 360 controller returned "xinput" on Firefox and "Xbox 360 Controller (XInput STANDARD GAMEPAD)" on Chrome
|
||||||
func (i *Input) GamepadName(id driver.GamepadID) string {
|
func (i *Input) GamepadName(id driver.GamepadID) string {
|
||||||
g, ok := i.gamepads[id]
|
g := gamepad.Get(id)
|
||||||
if !ok {
|
if g == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return g.name
|
return g.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) AppendGamepadIDs(gamepadIDs []driver.GamepadID) []driver.GamepadID {
|
func (i *Input) AppendGamepadIDs(gamepadIDs []driver.GamepadID) []driver.GamepadID {
|
||||||
for id := range i.gamepads {
|
return gamepad.AppendGamepadIDs(gamepadIDs)
|
||||||
gamepadIDs = append(gamepadIDs, id)
|
|
||||||
}
|
|
||||||
return gamepadIDs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) GamepadAxisNum(id driver.GamepadID) int {
|
func (i *Input) GamepadAxisNum(id driver.GamepadID) int {
|
||||||
g, ok := i.gamepads[id]
|
g := gamepad.Get(id)
|
||||||
if !ok {
|
if g == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return g.axisNum
|
return g.AxisNum()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) GamepadAxisValue(id driver.GamepadID, axis int) float64 {
|
func (i *Input) GamepadAxisValue(id driver.GamepadID, axis int) float64 {
|
||||||
g, ok := i.gamepads[id]
|
g := gamepad.Get(id)
|
||||||
if !ok {
|
if g == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
if g.axisNum <= axis {
|
return g.Axis(axis)
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return g.axes[axis]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) GamepadButtonNum(id driver.GamepadID) int {
|
func (i *Input) GamepadButtonNum(id driver.GamepadID) int {
|
||||||
g, ok := i.gamepads[id]
|
g := gamepad.Get(id)
|
||||||
if !ok {
|
if g == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return g.buttonNum
|
return g.ButtonNum()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) IsGamepadButtonPressed(id driver.GamepadID, button driver.GamepadButton) bool {
|
func (i *Input) IsGamepadButtonPressed(id driver.GamepadID, button driver.GamepadButton) bool {
|
||||||
g, ok := i.gamepads[id]
|
g := gamepad.Get(id)
|
||||||
if !ok {
|
if g == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if g.buttonNum <= int(button) {
|
return g.Button(int(button))
|
||||||
return false
|
|
||||||
}
|
|
||||||
return g.buttonPressed[button]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) AppendTouchIDs(touchIDs []driver.TouchID) []driver.TouchID {
|
func (i *Input) AppendTouchIDs(touchIDs []driver.TouchID) []driver.TouchID {
|
||||||
@ -293,63 +253,7 @@ func (i *Input) mouseUp(code int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) updateGamepads() {
|
func (i *Input) updateGamepads() {
|
||||||
nav := js.Global().Get("navigator")
|
gamepad.Update()
|
||||||
if !nav.Truthy() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
gamepads := nav.Call("getGamepads")
|
|
||||||
if !gamepads.Truthy() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for k := range i.gamepads {
|
|
||||||
delete(i.gamepads, k)
|
|
||||||
}
|
|
||||||
|
|
||||||
l := gamepads.Length()
|
|
||||||
for idx := 0; idx < l; idx++ {
|
|
||||||
gp := gamepads.Index(idx)
|
|
||||||
if !gp.Truthy() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
id := driver.GamepadID(gp.Get("index").Int())
|
|
||||||
g := gamepad{
|
|
||||||
value: gp,
|
|
||||||
}
|
|
||||||
g.name = gp.Get("id").String()
|
|
||||||
g.mapping = gp.Get("mapping").String()
|
|
||||||
|
|
||||||
axes := gp.Get("axes")
|
|
||||||
axesNum := axes.Length()
|
|
||||||
g.axisNum = axesNum
|
|
||||||
for a := 0; a < axesNum; a++ {
|
|
||||||
g.axes[a] = axes.Index(a).Float()
|
|
||||||
}
|
|
||||||
|
|
||||||
buttons := gp.Get("buttons")
|
|
||||||
buttonsNum := buttons.Length()
|
|
||||||
g.buttonNum = buttonsNum
|
|
||||||
for b := 0; b < buttonsNum; b++ {
|
|
||||||
btn := buttons.Index(b)
|
|
||||||
g.buttonPressed[b] = btn.Get("pressed").Bool()
|
|
||||||
g.buttonValues[b] = btn.Get("value").Float()
|
|
||||||
}
|
|
||||||
|
|
||||||
if g.mapping == "standard" {
|
|
||||||
// When the gamepad's mapping is "standard", the button and axis IDs are already mapped as the standard layout.
|
|
||||||
// See https://www.w3.org/TR/gamepad/#remapping.
|
|
||||||
copy(g.standardButtonPressed[:], g.buttonPressed[:])
|
|
||||||
copy(g.standardButtonValues[:], g.buttonValues[:])
|
|
||||||
copy(g.standardAxisValues[:], g.axes[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
if i.gamepads == nil {
|
|
||||||
i.gamepads = map[driver.GamepadID]gamepad{}
|
|
||||||
}
|
|
||||||
i.gamepads[id] = g
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) updateFromEvent(e js.Value) {
|
func (i *Input) updateFromEvent(e js.Value) {
|
||||||
@ -474,76 +378,41 @@ func (i *Input) updateForGo2Cpp() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) IsStandardGamepadLayoutAvailable(id driver.GamepadID) bool {
|
func (i *Input) IsStandardGamepadLayoutAvailable(id driver.GamepadID) bool {
|
||||||
g, ok := i.gamepads[id]
|
g := gamepad.Get(id)
|
||||||
if !ok {
|
if g == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return g.hasStandardLayoutMapping()
|
return g.IsStandardLayoutAvailable()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) StandardGamepadAxisValue(id driver.GamepadID, axis driver.StandardGamepadAxis) float64 {
|
func (i *Input) StandardGamepadAxisValue(id driver.GamepadID, axis driver.StandardGamepadAxis) float64 {
|
||||||
g, ok := i.gamepads[id]
|
g := gamepad.Get(id)
|
||||||
if !ok {
|
if g == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
if !g.hasStandardLayoutMapping() {
|
return g.StandardAxisValue(axis)
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return g.standardAxisValues[axis]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) StandardGamepadButtonValue(id driver.GamepadID, button driver.StandardGamepadButton) float64 {
|
func (i *Input) StandardGamepadButtonValue(id driver.GamepadID, button driver.StandardGamepadButton) float64 {
|
||||||
g, ok := i.gamepads[id]
|
g := gamepad.Get(id)
|
||||||
if !ok {
|
if g == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
if !g.hasStandardLayoutMapping() {
|
return g.StandardButtonValue(button)
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return g.standardButtonValues[button]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) IsStandardGamepadButtonPressed(id driver.GamepadID, button driver.StandardGamepadButton) bool {
|
func (i *Input) IsStandardGamepadButtonPressed(id driver.GamepadID, button driver.StandardGamepadButton) bool {
|
||||||
g, ok := i.gamepads[id]
|
g := gamepad.Get(id)
|
||||||
if !ok {
|
if g == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if !g.hasStandardLayoutMapping() {
|
return g.IsStandardButtonPressed(button)
|
||||||
return false
|
|
||||||
}
|
|
||||||
return g.standardButtonPressed[button]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) VibrateGamepad(id driver.GamepadID, duration time.Duration, strongMagnitude float64, weakMagnitude float64) {
|
func (i *Input) VibrateGamepad(id driver.GamepadID, duration time.Duration, strongMagnitude float64, weakMagnitude float64) {
|
||||||
g, ok := i.gamepads[id]
|
g := gamepad.Get(id)
|
||||||
if !ok {
|
if g == nil {
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// vibrationActuator is avaialble on Chrome.
|
|
||||||
if va := g.value.Get("vibrationActuator"); va.Truthy() {
|
|
||||||
if !va.Get("playEffect").Truthy() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
prop := object.New()
|
|
||||||
prop.Set("startDelay", 0)
|
|
||||||
prop.Set("duration", float64(duration/time.Millisecond))
|
|
||||||
prop.Set("strongMagnitude", strongMagnitude)
|
|
||||||
prop.Set("weakMagnitude", weakMagnitude)
|
|
||||||
va.Call("playEffect", "dual-rumble", prop)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// hapticActuators is available on Firefox.
|
|
||||||
if ha := g.value.Get("hapticActuators"); ha.Truthy() {
|
|
||||||
// TODO: Is this order correct?
|
|
||||||
if ha.Length() > 0 {
|
|
||||||
ha.Index(0).Call("pulse", strongMagnitude, float64(duration/time.Millisecond))
|
|
||||||
}
|
|
||||||
if ha.Length() > 1 {
|
|
||||||
ha.Index(1).Call("pulse", weakMagnitude, float64(duration/time.Millisecond))
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
g.Vibrate(duration, strongMagnitude, weakMagnitude)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user