mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-02-04 23:14:28 +01:00
internal/gamepad: make the APIs concurrent-safe again
This commit is contained in:
parent
2e9ce94ed0
commit
1d59023649
@ -40,6 +40,7 @@ const (
|
|||||||
type gamepads struct {
|
type gamepads struct {
|
||||||
inited bool
|
inited bool
|
||||||
gamepads []*Gamepad
|
gamepads []*Gamepad
|
||||||
|
m sync.Mutex
|
||||||
|
|
||||||
nativeGamepads
|
nativeGamepads
|
||||||
}
|
}
|
||||||
@ -50,22 +51,22 @@ func init() {
|
|||||||
theGamepads.nativeGamepads.gamepads = &theGamepads
|
theGamepads.nativeGamepads.gamepads = &theGamepads
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendGamepadIDs must be called on the main thread.
|
|
||||||
func AppendGamepadIDs(ids []driver.GamepadID) []driver.GamepadID {
|
func AppendGamepadIDs(ids []driver.GamepadID) []driver.GamepadID {
|
||||||
return theGamepads.appendGamepadIDs(ids)
|
return theGamepads.appendGamepadIDs(ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update must be called on the main thread.
|
|
||||||
func Update() {
|
func Update() {
|
||||||
theGamepads.update()
|
theGamepads.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get must be called on the main thread.
|
|
||||||
func Get(id driver.GamepadID) *Gamepad {
|
func Get(id driver.GamepadID) *Gamepad {
|
||||||
return theGamepads.get(id)
|
return theGamepads.get(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
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))
|
||||||
@ -75,6 +76,9 @@ func (g *gamepads) appendGamepadIDs(ids []driver.GamepadID) []driver.GamepadID {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *gamepads) update() {
|
func (g *gamepads) update() {
|
||||||
|
g.m.Lock()
|
||||||
|
defer g.m.Unlock()
|
||||||
|
|
||||||
if !g.inited {
|
if !g.inited {
|
||||||
g.nativeGamepads.init()
|
g.nativeGamepads.init()
|
||||||
g.inited = true
|
g.inited = true
|
||||||
@ -89,14 +93,15 @@ func (g *gamepads) update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *gamepads) get(id driver.GamepadID) *Gamepad {
|
func (g *gamepads) get(id driver.GamepadID) *Gamepad {
|
||||||
|
g.m.Lock()
|
||||||
|
defer g.m.Unlock()
|
||||||
|
|
||||||
if id < 0 || int(id) >= len(g.gamepads) {
|
if id < 0 || int(id) >= len(g.gamepads) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return g.gamepads[id]
|
return g.gamepads[id]
|
||||||
}
|
}
|
||||||
|
|
||||||
// find can be invoked from callbacks on the OS's main thread.
|
|
||||||
// As a callback can be called synchronously from update, using a mutex is not a good idea.
|
|
||||||
func (g *gamepads) find(cond func(*Gamepad) bool) *Gamepad {
|
func (g *gamepads) find(cond func(*Gamepad) bool) *Gamepad {
|
||||||
for _, gp := range g.gamepads {
|
for _, gp := range g.gamepads {
|
||||||
if gp == nil {
|
if gp == nil {
|
||||||
@ -109,8 +114,6 @@ func (g *gamepads) find(cond func(*Gamepad) bool) *Gamepad {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// add can be invoked from callbacks on the OS's main thread.
|
|
||||||
// As a callback can be called synchronously from update, using a mutex is not a good idea.
|
|
||||||
func (g *gamepads) add(name, sdlID string) *Gamepad {
|
func (g *gamepads) add(name, sdlID string) *Gamepad {
|
||||||
for i, gp := range g.gamepads {
|
for i, gp := range g.gamepads {
|
||||||
if gp == nil {
|
if gp == nil {
|
||||||
@ -131,8 +134,6 @@ func (g *gamepads) add(name, sdlID string) *Gamepad {
|
|||||||
return gp
|
return gp
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove can be invoked from callbacks on the OS's main thread.
|
|
||||||
// As a callback can be called synchronously from update, using a mutex is not a good idea.
|
|
||||||
func (g *gamepads) remove(cond func(*Gamepad) bool) {
|
func (g *gamepads) remove(cond func(*Gamepad) bool) {
|
||||||
for i, gp := range g.gamepads {
|
for i, gp := range g.gamepads {
|
||||||
if gp == nil {
|
if gp == nil {
|
||||||
|
@ -20,6 +20,7 @@ package gamepad
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@ -61,6 +62,9 @@ type nativeGamepads struct {
|
|||||||
gamepads *gamepads
|
gamepads *gamepads
|
||||||
|
|
||||||
hidManager C.IOHIDManagerRef
|
hidManager C.IOHIDManagerRef
|
||||||
|
devicesToAdd []C.IOHIDDeviceRef
|
||||||
|
devicesToRemove []C.IOHIDDeviceRef
|
||||||
|
devicesM sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
type nativeGamepad struct {
|
type nativeGamepad struct {
|
||||||
@ -285,7 +289,36 @@ func (g *nativeGamepads) init() {
|
|||||||
|
|
||||||
//export ebitenGamepadMatchingCallback
|
//export ebitenGamepadMatchingCallback
|
||||||
func ebitenGamepadMatchingCallback(ctx unsafe.Pointer, res C.IOReturn, sender unsafe.Pointer, device C.IOHIDDeviceRef) {
|
func ebitenGamepadMatchingCallback(ctx unsafe.Pointer, res C.IOReturn, sender unsafe.Pointer, device C.IOHIDDeviceRef) {
|
||||||
if theGamepads.find(func(g *Gamepad) bool {
|
theGamepads.devicesM.Lock()
|
||||||
|
defer theGamepads.devicesM.Unlock()
|
||||||
|
theGamepads.devicesToAdd = append(theGamepads.devicesToAdd, device)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export ebitenGamepadRemovalCallback
|
||||||
|
func ebitenGamepadRemovalCallback(ctx unsafe.Pointer, res C.IOReturn, sender unsafe.Pointer, device C.IOHIDDeviceRef) {
|
||||||
|
theGamepads.devicesM.Lock()
|
||||||
|
defer theGamepads.devicesM.Unlock()
|
||||||
|
theGamepads.devicesToRemove = append(theGamepads.devicesToRemove, device)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *nativeGamepads) update() {
|
||||||
|
theGamepads.devicesM.Lock()
|
||||||
|
defer theGamepads.devicesM.Unlock()
|
||||||
|
|
||||||
|
for _, device := range g.devicesToAdd {
|
||||||
|
g.addDevice(device)
|
||||||
|
}
|
||||||
|
for _, device := range g.devicesToRemove {
|
||||||
|
g.gamepads.remove(func(g *Gamepad) bool {
|
||||||
|
return g.device == device
|
||||||
|
})
|
||||||
|
}
|
||||||
|
g.devicesToAdd = g.devicesToAdd[:0]
|
||||||
|
g.devicesToRemove = g.devicesToRemove[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *nativeGamepads) addDevice(device C.IOHIDDeviceRef) {
|
||||||
|
if g.gamepads.find(func(g *Gamepad) bool {
|
||||||
return g.device == device
|
return g.device == device
|
||||||
}) != nil {
|
}) != nil {
|
||||||
return
|
return
|
||||||
@ -331,8 +364,8 @@ func ebitenGamepadMatchingCallback(ctx unsafe.Pointer, res C.IOReturn, sender un
|
|||||||
elements := C.IOHIDDeviceCopyMatchingElements(device, 0, C.kIOHIDOptionsTypeNone)
|
elements := C.IOHIDDeviceCopyMatchingElements(device, 0, C.kIOHIDOptionsTypeNone)
|
||||||
defer C.CFRelease(C.CFTypeRef(elements))
|
defer C.CFRelease(C.CFTypeRef(elements))
|
||||||
|
|
||||||
g := theGamepads.add(name, sdlID)
|
gp := g.gamepads.add(name, sdlID)
|
||||||
g.device = device
|
gp.device = device
|
||||||
|
|
||||||
for i := C.CFIndex(0); i < C.CFArrayGetCount(elements); i++ {
|
for i := C.CFIndex(0); i < C.CFArrayGetCount(elements); i++ {
|
||||||
native := (C.IOHIDElementRef)(C.CFArrayGetValueAtIndex(elements, i))
|
native := (C.IOHIDElementRef)(C.CFArrayGetValueAtIndex(elements, i))
|
||||||
@ -356,27 +389,27 @@ func ebitenGamepadMatchingCallback(ctx unsafe.Pointer, res C.IOReturn, sender un
|
|||||||
case C.kHIDUsage_GD_X, C.kHIDUsage_GD_Y, C.kHIDUsage_GD_Z,
|
case C.kHIDUsage_GD_X, C.kHIDUsage_GD_Y, C.kHIDUsage_GD_Z,
|
||||||
C.kHIDUsage_GD_Rx, C.kHIDUsage_GD_Ry, C.kHIDUsage_GD_Rz,
|
C.kHIDUsage_GD_Rx, C.kHIDUsage_GD_Ry, C.kHIDUsage_GD_Rz,
|
||||||
C.kHIDUsage_GD_Slider, C.kHIDUsage_GD_Dial, C.kHIDUsage_GD_Wheel:
|
C.kHIDUsage_GD_Slider, C.kHIDUsage_GD_Dial, C.kHIDUsage_GD_Wheel:
|
||||||
g.axes = append(g.axes, element{
|
gp.axes = append(gp.axes, element{
|
||||||
native: native,
|
native: native,
|
||||||
usage: int(usage),
|
usage: int(usage),
|
||||||
index: len(g.axes),
|
index: len(gp.axes),
|
||||||
minimum: int(C.IOHIDElementGetLogicalMin(native)),
|
minimum: int(C.IOHIDElementGetLogicalMin(native)),
|
||||||
maximum: int(C.IOHIDElementGetLogicalMax(native)),
|
maximum: int(C.IOHIDElementGetLogicalMax(native)),
|
||||||
})
|
})
|
||||||
case C.kHIDUsage_GD_Hatswitch:
|
case C.kHIDUsage_GD_Hatswitch:
|
||||||
g.hats = append(g.hats, element{
|
gp.hats = append(gp.hats, element{
|
||||||
native: native,
|
native: native,
|
||||||
usage: int(usage),
|
usage: int(usage),
|
||||||
index: len(g.hats),
|
index: len(gp.hats),
|
||||||
minimum: int(C.IOHIDElementGetLogicalMin(native)),
|
minimum: int(C.IOHIDElementGetLogicalMin(native)),
|
||||||
maximum: int(C.IOHIDElementGetLogicalMax(native)),
|
maximum: int(C.IOHIDElementGetLogicalMax(native)),
|
||||||
})
|
})
|
||||||
case C.kHIDUsage_GD_DPadUp, C.kHIDUsage_GD_DPadRight, C.kHIDUsage_GD_DPadDown, C.kHIDUsage_GD_DPadLeft,
|
case C.kHIDUsage_GD_DPadUp, C.kHIDUsage_GD_DPadRight, C.kHIDUsage_GD_DPadDown, C.kHIDUsage_GD_DPadLeft,
|
||||||
C.kHIDUsage_GD_SystemMainMenu, C.kHIDUsage_GD_Select, C.kHIDUsage_GD_Start:
|
C.kHIDUsage_GD_SystemMainMenu, C.kHIDUsage_GD_Select, C.kHIDUsage_GD_Start:
|
||||||
g.buttons = append(g.buttons, element{
|
gp.buttons = append(gp.buttons, element{
|
||||||
native: native,
|
native: native,
|
||||||
usage: int(usage),
|
usage: int(usage),
|
||||||
index: len(g.buttons),
|
index: len(gp.buttons),
|
||||||
minimum: int(C.IOHIDElementGetLogicalMin(native)),
|
minimum: int(C.IOHIDElementGetLogicalMin(native)),
|
||||||
maximum: int(C.IOHIDElementGetLogicalMax(native)),
|
maximum: int(C.IOHIDElementGetLogicalMax(native)),
|
||||||
})
|
})
|
||||||
@ -384,36 +417,26 @@ func ebitenGamepadMatchingCallback(ctx unsafe.Pointer, res C.IOReturn, sender un
|
|||||||
case C.kHIDPage_Simulation:
|
case C.kHIDPage_Simulation:
|
||||||
switch usage {
|
switch usage {
|
||||||
case C.kHIDUsage_Sim_Accelerator, C.kHIDUsage_Sim_Brake, C.kHIDUsage_Sim_Throttle, C.kHIDUsage_Sim_Rudder, C.kHIDUsage_Sim_Steering:
|
case C.kHIDUsage_Sim_Accelerator, C.kHIDUsage_Sim_Brake, C.kHIDUsage_Sim_Throttle, C.kHIDUsage_Sim_Rudder, C.kHIDUsage_Sim_Steering:
|
||||||
g.axes = append(g.axes, element{
|
gp.axes = append(gp.axes, element{
|
||||||
native: native,
|
native: native,
|
||||||
usage: int(usage),
|
usage: int(usage),
|
||||||
index: len(g.axes),
|
index: len(gp.axes),
|
||||||
minimum: int(C.IOHIDElementGetLogicalMin(native)),
|
minimum: int(C.IOHIDElementGetLogicalMin(native)),
|
||||||
maximum: int(C.IOHIDElementGetLogicalMax(native)),
|
maximum: int(C.IOHIDElementGetLogicalMax(native)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
case C.kHIDPage_Button, C.kHIDPage_Consumer:
|
case C.kHIDPage_Button, C.kHIDPage_Consumer:
|
||||||
g.buttons = append(g.buttons, element{
|
gp.buttons = append(gp.buttons, element{
|
||||||
native: native,
|
native: native,
|
||||||
usage: int(usage),
|
usage: int(usage),
|
||||||
index: len(g.buttons),
|
index: len(gp.buttons),
|
||||||
minimum: int(C.IOHIDElementGetLogicalMin(native)),
|
minimum: int(C.IOHIDElementGetLogicalMin(native)),
|
||||||
maximum: int(C.IOHIDElementGetLogicalMax(native)),
|
maximum: int(C.IOHIDElementGetLogicalMax(native)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Stable(g.axes)
|
sort.Stable(gp.axes)
|
||||||
sort.Stable(g.buttons)
|
sort.Stable(gp.buttons)
|
||||||
sort.Stable(g.hats)
|
sort.Stable(gp.hats)
|
||||||
}
|
|
||||||
|
|
||||||
//export ebitenGamepadRemovalCallback
|
|
||||||
func ebitenGamepadRemovalCallback(ctx unsafe.Pointer, res C.IOReturn, sender unsafe.Pointer, device C.IOHIDDeviceRef) {
|
|
||||||
theGamepads.remove(func(g *Gamepad) bool {
|
|
||||||
return g.device == device
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *nativeGamepads) update() {
|
|
||||||
}
|
}
|
||||||
|
@ -32,23 +32,11 @@ func (i *Input) updateGamepads() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) AppendGamepadIDs(gamepadIDs []driver.GamepadID) []driver.GamepadID {
|
func (i *Input) AppendGamepadIDs(gamepadIDs []driver.GamepadID) []driver.GamepadID {
|
||||||
var gs []driver.GamepadID
|
return gamepadpkg.AppendGamepadIDs(gamepadIDs)
|
||||||
i.ui.t.Call(func() {
|
|
||||||
gs = gamepadpkg.AppendGamepadIDs(gamepadIDs)
|
|
||||||
})
|
|
||||||
return gs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Input) gamepad(id driver.GamepadID) *gamepadpkg.Gamepad {
|
|
||||||
var g *gamepadpkg.Gamepad
|
|
||||||
i.ui.t.Call(func() {
|
|
||||||
g = gamepadpkg.Get(id)
|
|
||||||
})
|
|
||||||
return g
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) GamepadSDLID(id driver.GamepadID) string {
|
func (i *Input) GamepadSDLID(id driver.GamepadID) string {
|
||||||
g := i.gamepad(id)
|
g := gamepadpkg.Get(id)
|
||||||
if g == nil {
|
if g == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@ -56,7 +44,7 @@ func (i *Input) GamepadSDLID(id driver.GamepadID) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) GamepadName(id driver.GamepadID) string {
|
func (i *Input) GamepadName(id driver.GamepadID) string {
|
||||||
g := i.gamepad(id)
|
g := gamepadpkg.Get(id)
|
||||||
if g == nil {
|
if g == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@ -64,7 +52,7 @@ func (i *Input) GamepadName(id driver.GamepadID) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) GamepadAxisNum(id driver.GamepadID) int {
|
func (i *Input) GamepadAxisNum(id driver.GamepadID) int {
|
||||||
g := i.gamepad(id)
|
g := gamepadpkg.Get(id)
|
||||||
if g == nil {
|
if g == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -72,7 +60,7 @@ func (i *Input) GamepadAxisNum(id driver.GamepadID) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) GamepadAxisValue(id driver.GamepadID, axis int) float64 {
|
func (i *Input) GamepadAxisValue(id driver.GamepadID, axis int) float64 {
|
||||||
g := i.gamepad(id)
|
g := gamepadpkg.Get(id)
|
||||||
if g == nil {
|
if g == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -80,7 +68,7 @@ func (i *Input) GamepadAxisValue(id driver.GamepadID, axis int) float64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) GamepadButtonNum(id driver.GamepadID) int {
|
func (i *Input) GamepadButtonNum(id driver.GamepadID) int {
|
||||||
g := i.gamepad(id)
|
g := gamepadpkg.Get(id)
|
||||||
if g == nil {
|
if g == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -90,7 +78,7 @@ func (i *Input) GamepadButtonNum(id driver.GamepadID) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) IsGamepadButtonPressed(id driver.GamepadID, button driver.GamepadButton) bool {
|
func (i *Input) IsGamepadButtonPressed(id driver.GamepadID, button driver.GamepadButton) bool {
|
||||||
g := i.gamepad(id)
|
g := gamepadpkg.Get(id)
|
||||||
if g == nil {
|
if g == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -110,7 +98,7 @@ func (i *Input) IsGamepadButtonPressed(id driver.GamepadID, button driver.Gamepa
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) IsStandardGamepadLayoutAvailable(id driver.GamepadID) bool {
|
func (i *Input) IsStandardGamepadLayoutAvailable(id driver.GamepadID) bool {
|
||||||
g := i.gamepad(id)
|
g := gamepadpkg.Get(id)
|
||||||
if g == nil {
|
if g == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -118,7 +106,7 @@ func (i *Input) IsStandardGamepadLayoutAvailable(id driver.GamepadID) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) StandardGamepadAxisValue(id driver.GamepadID, axis driver.StandardGamepadAxis) float64 {
|
func (i *Input) StandardGamepadAxisValue(id driver.GamepadID, axis driver.StandardGamepadAxis) float64 {
|
||||||
g := i.gamepad(id)
|
g := gamepadpkg.Get(id)
|
||||||
if g == nil {
|
if g == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -126,7 +114,7 @@ func (i *Input) StandardGamepadAxisValue(id driver.GamepadID, axis driver.Standa
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) StandardGamepadButtonValue(id driver.GamepadID, button driver.StandardGamepadButton) float64 {
|
func (i *Input) StandardGamepadButtonValue(id driver.GamepadID, button driver.StandardGamepadButton) float64 {
|
||||||
g := i.gamepad(id)
|
g := gamepadpkg.Get(id)
|
||||||
if g == nil {
|
if g == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -134,7 +122,7 @@ func (i *Input) StandardGamepadButtonValue(id driver.GamepadID, button driver.St
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Input) IsStandardGamepadButtonPressed(id driver.GamepadID, button driver.StandardGamepadButton) bool {
|
func (i *Input) IsStandardGamepadButtonPressed(id driver.GamepadID, button driver.StandardGamepadButton) bool {
|
||||||
g := i.gamepad(id)
|
g := gamepadpkg.Get(id)
|
||||||
if g == nil {
|
if g == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -142,7 +130,7 @@ 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) {
|
||||||
g := i.gamepad(id)
|
g := gamepadpkg.Get(id)
|
||||||
if g == nil {
|
if g == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user