mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +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 {
|
||||
inited bool
|
||||
gamepads []*Gamepad
|
||||
m sync.Mutex
|
||||
|
||||
nativeGamepads
|
||||
}
|
||||
@ -50,22 +51,22 @@ func init() {
|
||||
theGamepads.nativeGamepads.gamepads = &theGamepads
|
||||
}
|
||||
|
||||
// AppendGamepadIDs must be called on the main thread.
|
||||
func AppendGamepadIDs(ids []driver.GamepadID) []driver.GamepadID {
|
||||
return theGamepads.appendGamepadIDs(ids)
|
||||
}
|
||||
|
||||
// Update must be called on the main thread.
|
||||
func Update() {
|
||||
theGamepads.update()
|
||||
}
|
||||
|
||||
// Get must be called on the main thread.
|
||||
func Get(id driver.GamepadID) *Gamepad {
|
||||
return theGamepads.get(id)
|
||||
}
|
||||
|
||||
func (g *gamepads) appendGamepadIDs(ids []driver.GamepadID) []driver.GamepadID {
|
||||
g.m.Lock()
|
||||
defer g.m.Unlock()
|
||||
|
||||
for i, gp := range g.gamepads {
|
||||
if gp != nil && gp.present() {
|
||||
ids = append(ids, driver.GamepadID(i))
|
||||
@ -75,6 +76,9 @@ func (g *gamepads) appendGamepadIDs(ids []driver.GamepadID) []driver.GamepadID {
|
||||
}
|
||||
|
||||
func (g *gamepads) update() {
|
||||
g.m.Lock()
|
||||
defer g.m.Unlock()
|
||||
|
||||
if !g.inited {
|
||||
g.nativeGamepads.init()
|
||||
g.inited = true
|
||||
@ -89,14 +93,15 @@ func (g *gamepads) update() {
|
||||
}
|
||||
|
||||
func (g *gamepads) get(id driver.GamepadID) *Gamepad {
|
||||
g.m.Lock()
|
||||
defer g.m.Unlock()
|
||||
|
||||
if id < 0 || int(id) >= len(g.gamepads) {
|
||||
return nil
|
||||
}
|
||||
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 {
|
||||
for _, gp := range g.gamepads {
|
||||
if gp == nil {
|
||||
@ -109,8 +114,6 @@ func (g *gamepads) find(cond func(*Gamepad) bool) *Gamepad {
|
||||
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 {
|
||||
for i, gp := range g.gamepads {
|
||||
if gp == nil {
|
||||
@ -131,8 +134,6 @@ func (g *gamepads) add(name, sdlID string) *Gamepad {
|
||||
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) {
|
||||
for i, gp := range g.gamepads {
|
||||
if gp == nil {
|
||||
|
@ -20,6 +20,7 @@ package gamepad
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
@ -60,7 +61,10 @@ import "C"
|
||||
type nativeGamepads struct {
|
||||
gamepads *gamepads
|
||||
|
||||
hidManager C.IOHIDManagerRef
|
||||
hidManager C.IOHIDManagerRef
|
||||
devicesToAdd []C.IOHIDDeviceRef
|
||||
devicesToRemove []C.IOHIDDeviceRef
|
||||
devicesM sync.Mutex
|
||||
}
|
||||
|
||||
type nativeGamepad struct {
|
||||
@ -285,7 +289,36 @@ func (g *nativeGamepads) init() {
|
||||
|
||||
//export ebitenGamepadMatchingCallback
|
||||
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
|
||||
}) != nil {
|
||||
return
|
||||
@ -331,8 +364,8 @@ func ebitenGamepadMatchingCallback(ctx unsafe.Pointer, res C.IOReturn, sender un
|
||||
elements := C.IOHIDDeviceCopyMatchingElements(device, 0, C.kIOHIDOptionsTypeNone)
|
||||
defer C.CFRelease(C.CFTypeRef(elements))
|
||||
|
||||
g := theGamepads.add(name, sdlID)
|
||||
g.device = device
|
||||
gp := g.gamepads.add(name, sdlID)
|
||||
gp.device = device
|
||||
|
||||
for i := C.CFIndex(0); i < C.CFArrayGetCount(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,
|
||||
C.kHIDUsage_GD_Rx, C.kHIDUsage_GD_Ry, C.kHIDUsage_GD_Rz,
|
||||
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,
|
||||
usage: int(usage),
|
||||
index: len(g.axes),
|
||||
index: len(gp.axes),
|
||||
minimum: int(C.IOHIDElementGetLogicalMin(native)),
|
||||
maximum: int(C.IOHIDElementGetLogicalMax(native)),
|
||||
})
|
||||
case C.kHIDUsage_GD_Hatswitch:
|
||||
g.hats = append(g.hats, element{
|
||||
gp.hats = append(gp.hats, element{
|
||||
native: native,
|
||||
usage: int(usage),
|
||||
index: len(g.hats),
|
||||
index: len(gp.hats),
|
||||
minimum: int(C.IOHIDElementGetLogicalMin(native)),
|
||||
maximum: int(C.IOHIDElementGetLogicalMax(native)),
|
||||
})
|
||||
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:
|
||||
g.buttons = append(g.buttons, element{
|
||||
gp.buttons = append(gp.buttons, element{
|
||||
native: native,
|
||||
usage: int(usage),
|
||||
index: len(g.buttons),
|
||||
index: len(gp.buttons),
|
||||
minimum: int(C.IOHIDElementGetLogicalMin(native)),
|
||||
maximum: int(C.IOHIDElementGetLogicalMax(native)),
|
||||
})
|
||||
@ -384,36 +417,26 @@ func ebitenGamepadMatchingCallback(ctx unsafe.Pointer, res C.IOReturn, sender un
|
||||
case C.kHIDPage_Simulation:
|
||||
switch usage {
|
||||
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,
|
||||
usage: int(usage),
|
||||
index: len(g.axes),
|
||||
index: len(gp.axes),
|
||||
minimum: int(C.IOHIDElementGetLogicalMin(native)),
|
||||
maximum: int(C.IOHIDElementGetLogicalMax(native)),
|
||||
})
|
||||
}
|
||||
case C.kHIDPage_Button, C.kHIDPage_Consumer:
|
||||
g.buttons = append(g.buttons, element{
|
||||
gp.buttons = append(gp.buttons, element{
|
||||
native: native,
|
||||
usage: int(usage),
|
||||
index: len(g.buttons),
|
||||
index: len(gp.buttons),
|
||||
minimum: int(C.IOHIDElementGetLogicalMin(native)),
|
||||
maximum: int(C.IOHIDElementGetLogicalMax(native)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Stable(g.axes)
|
||||
sort.Stable(g.buttons)
|
||||
sort.Stable(g.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() {
|
||||
sort.Stable(gp.axes)
|
||||
sort.Stable(gp.buttons)
|
||||
sort.Stable(gp.hats)
|
||||
}
|
||||
|
@ -32,23 +32,11 @@ func (i *Input) updateGamepads() {
|
||||
}
|
||||
|
||||
func (i *Input) AppendGamepadIDs(gamepadIDs []driver.GamepadID) []driver.GamepadID {
|
||||
var gs []driver.GamepadID
|
||||
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
|
||||
return gamepadpkg.AppendGamepadIDs(gamepadIDs)
|
||||
}
|
||||
|
||||
func (i *Input) GamepadSDLID(id driver.GamepadID) string {
|
||||
g := i.gamepad(id)
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
return ""
|
||||
}
|
||||
@ -56,7 +44,7 @@ func (i *Input) GamepadSDLID(id driver.GamepadID) string {
|
||||
}
|
||||
|
||||
func (i *Input) GamepadName(id driver.GamepadID) string {
|
||||
g := i.gamepad(id)
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
return ""
|
||||
}
|
||||
@ -64,7 +52,7 @@ func (i *Input) GamepadName(id driver.GamepadID) string {
|
||||
}
|
||||
|
||||
func (i *Input) GamepadAxisNum(id driver.GamepadID) int {
|
||||
g := i.gamepad(id)
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
return 0
|
||||
}
|
||||
@ -72,7 +60,7 @@ func (i *Input) GamepadAxisNum(id driver.GamepadID) int {
|
||||
}
|
||||
|
||||
func (i *Input) GamepadAxisValue(id driver.GamepadID, axis int) float64 {
|
||||
g := i.gamepad(id)
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
return 0
|
||||
}
|
||||
@ -80,7 +68,7 @@ func (i *Input) GamepadAxisValue(id driver.GamepadID, axis int) float64 {
|
||||
}
|
||||
|
||||
func (i *Input) GamepadButtonNum(id driver.GamepadID) int {
|
||||
g := i.gamepad(id)
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
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 {
|
||||
g := i.gamepad(id)
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
return false
|
||||
}
|
||||
@ -110,7 +98,7 @@ func (i *Input) IsGamepadButtonPressed(id driver.GamepadID, button driver.Gamepa
|
||||
}
|
||||
|
||||
func (i *Input) IsStandardGamepadLayoutAvailable(id driver.GamepadID) bool {
|
||||
g := i.gamepad(id)
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
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 {
|
||||
g := i.gamepad(id)
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
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 {
|
||||
g := i.gamepad(id)
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
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 {
|
||||
g := i.gamepad(id)
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
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) {
|
||||
g := i.gamepad(id)
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
return
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user