mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-26 18:52:44 +01:00
internal/uidriver/glfw: use an original implementation for macOS
Updates #1452
This commit is contained in:
parent
210036a5a5
commit
ff24f7718c
230
internal/gamepad/gamepad.go
Normal file
230
internal/gamepad/gamepad.go
Normal file
@ -0,0 +1,230 @@
|
||||
// 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 (
|
||||
"sync"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/driver"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/gamepaddb"
|
||||
)
|
||||
|
||||
const (
|
||||
hatCentered = 0
|
||||
hatUp = 1
|
||||
hatRight = 2
|
||||
hatDown = 4
|
||||
hatLeft = 8
|
||||
hatRightUp = hatRight | hatUp
|
||||
hatRightDown = hatRight | hatDown
|
||||
hatLeftUp = hatLeft | hatUp
|
||||
hatLeftDown = hatLeft | hatDown
|
||||
)
|
||||
|
||||
type gamepads struct {
|
||||
gamepads []*Gamepad
|
||||
m sync.Mutex
|
||||
|
||||
nativeGamepads
|
||||
}
|
||||
|
||||
var theGamepads gamepads
|
||||
|
||||
func init() {
|
||||
theGamepads.nativeGamepads.init()
|
||||
}
|
||||
|
||||
func AppendGamepadIDs(ids []driver.GamepadID) []driver.GamepadID {
|
||||
return theGamepads.appendGamepadIDs(ids)
|
||||
}
|
||||
|
||||
func Update() {
|
||||
theGamepads.update()
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func (g *gamepads) update() {
|
||||
g.m.Lock()
|
||||
defer g.m.Unlock()
|
||||
|
||||
for _, gp := range g.gamepads {
|
||||
if gp != nil {
|
||||
gp.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]
|
||||
}
|
||||
|
||||
func (g *gamepads) find(cond func(*Gamepad) bool) *Gamepad {
|
||||
g.m.Lock()
|
||||
defer g.m.Unlock()
|
||||
|
||||
for _, gp := range g.gamepads {
|
||||
if gp == nil {
|
||||
continue
|
||||
}
|
||||
if cond(gp) {
|
||||
return gp
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *gamepads) add(name, sdlID string) *Gamepad {
|
||||
g.m.Lock()
|
||||
defer g.m.Unlock()
|
||||
|
||||
for i, gp := range g.gamepads {
|
||||
if gp == nil {
|
||||
gp := &Gamepad{
|
||||
name: name,
|
||||
sdlID: sdlID,
|
||||
}
|
||||
g.gamepads[i] = gp
|
||||
return gp
|
||||
}
|
||||
}
|
||||
|
||||
gp := &Gamepad{
|
||||
name: name,
|
||||
sdlID: sdlID,
|
||||
}
|
||||
g.gamepads = append(g.gamepads, gp)
|
||||
return gp
|
||||
}
|
||||
|
||||
func (g *gamepads) remove(cond func(*Gamepad) bool) {
|
||||
g.m.Lock()
|
||||
defer g.m.Unlock()
|
||||
|
||||
for i, gp := range g.gamepads {
|
||||
if gp == nil {
|
||||
continue
|
||||
}
|
||||
if cond(gp) {
|
||||
g.gamepads[i] = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Gamepad struct {
|
||||
name string
|
||||
sdlID string
|
||||
m sync.Mutex
|
||||
|
||||
nativeGamepad
|
||||
}
|
||||
|
||||
func (g *Gamepad) update() {
|
||||
g.m.Lock()
|
||||
defer g.m.Unlock()
|
||||
|
||||
g.nativeGamepad.update()
|
||||
}
|
||||
|
||||
func (g *Gamepad) Name() string {
|
||||
// This is immutable and doesn't have to be protected by a mutex.
|
||||
return g.name
|
||||
}
|
||||
|
||||
func (g *Gamepad) SDLID() string {
|
||||
// This is immutable and doesn't have to be protected by a mutex.
|
||||
return g.sdlID
|
||||
}
|
||||
|
||||
func (g *Gamepad) AxisNum() int {
|
||||
g.m.Lock()
|
||||
defer g.m.Unlock()
|
||||
|
||||
return g.nativeGamepad.axisNum()
|
||||
}
|
||||
|
||||
func (g *Gamepad) ButtonNum() int {
|
||||
g.m.Lock()
|
||||
defer g.m.Unlock()
|
||||
|
||||
return g.nativeGamepad.buttonNum()
|
||||
}
|
||||
|
||||
func (g *Gamepad) HatNum() int {
|
||||
g.m.Lock()
|
||||
defer g.m.Unlock()
|
||||
|
||||
return g.nativeGamepad.hatNum()
|
||||
}
|
||||
|
||||
func (g *Gamepad) Axis(axis int) float64 {
|
||||
g.m.Lock()
|
||||
defer g.m.Unlock()
|
||||
|
||||
return g.nativeGamepad.axisValue(axis)
|
||||
}
|
||||
|
||||
func (g *Gamepad) Button(button int) bool {
|
||||
g.m.Lock()
|
||||
defer g.m.Unlock()
|
||||
|
||||
return g.nativeGamepad.isButtonPressed(button)
|
||||
}
|
||||
|
||||
func (g *Gamepad) Hat(hat int) int {
|
||||
g.m.Lock()
|
||||
defer g.m.Unlock()
|
||||
|
||||
return g.nativeGamepad.hatState(hat)
|
||||
}
|
||||
|
||||
func (g *Gamepad) IsStandardLayoutAvailable() bool {
|
||||
g.m.Lock()
|
||||
defer g.m.Unlock()
|
||||
|
||||
return gamepaddb.HasStandardLayoutMapping(g.sdlID)
|
||||
}
|
||||
|
||||
func (g *Gamepad) StandardAxisValue(axis driver.StandardGamepadAxis) float64 {
|
||||
return gamepaddb.AxisValue(g.sdlID, axis, g)
|
||||
}
|
||||
|
||||
func (g *Gamepad) StandardButtonValue(button driver.StandardGamepadButton) float64 {
|
||||
return gamepaddb.ButtonValue(g.sdlID, button, g)
|
||||
}
|
||||
|
||||
func (g *Gamepad) IsStandardButtonPressed(button driver.StandardGamepadButton) bool {
|
||||
return gamepaddb.IsButtonPressed(g.sdlID, button, g)
|
||||
}
|
396
internal/gamepad/gamepad_darwin.go
Normal file
396
internal/gamepad/gamepad_darwin.go
Normal file
@ -0,0 +1,396 @@
|
||||
// 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.
|
||||
|
||||
//go:build !ios
|
||||
// +build !ios
|
||||
|
||||
package gamepad
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// #cgo LDFLAGS: -framework CoreFoundation -framework IOKit
|
||||
//
|
||||
// #include <ForceFeedback/ForceFeedback.h>
|
||||
// #include <IOKit/hid/IOHIDLib.h>
|
||||
//
|
||||
// static CFStringRef cfStringRefIOHIDVendorIDKey() {
|
||||
// return CFSTR(kIOHIDVendorIDKey);
|
||||
// }
|
||||
//
|
||||
// static CFStringRef cfStringRefIOHIDProductIDKey() {
|
||||
// return CFSTR(kIOHIDProductIDKey);
|
||||
// }
|
||||
//
|
||||
// static CFStringRef cfStringRefIOHIDVersionNumberKey() {
|
||||
// return CFSTR(kIOHIDVersionNumberKey);
|
||||
// }
|
||||
//
|
||||
// static CFStringRef cfStringRefIOHIDProductKey() {
|
||||
// return CFSTR(kIOHIDProductKey);
|
||||
// }
|
||||
//
|
||||
// static CFStringRef cfStringRefIOHIDDeviceUsagePageKey() {
|
||||
// return CFSTR(kIOHIDDeviceUsagePageKey);
|
||||
// }
|
||||
//
|
||||
// static CFStringRef cfStringRefIOHIDDeviceUsageKey() {
|
||||
// return CFSTR(kIOHIDDeviceUsageKey);
|
||||
// }
|
||||
//
|
||||
// void ebitenGamepadMatchingCallback(void *ctx, IOReturn res, void *sender, IOHIDDeviceRef device);
|
||||
// void ebitenGamepadRemovalCallback(void *ctx, IOReturn res, void *sender, IOHIDDeviceRef device);
|
||||
import "C"
|
||||
|
||||
type nativeGamepads struct {
|
||||
hidManager C.IOHIDManagerRef
|
||||
}
|
||||
|
||||
type nativeGamepad struct {
|
||||
device C.IOHIDDeviceRef
|
||||
axes elements
|
||||
buttons elements
|
||||
hats elements
|
||||
|
||||
axisValues []float64
|
||||
buttonValues []bool
|
||||
hatValues []int
|
||||
}
|
||||
|
||||
type element struct {
|
||||
native C.IOHIDElementRef
|
||||
usage int
|
||||
index int
|
||||
minimum int
|
||||
maximum int
|
||||
}
|
||||
|
||||
type elements []element
|
||||
|
||||
func (e elements) Len() int {
|
||||
return len(e)
|
||||
}
|
||||
|
||||
func (e elements) Less(i, j int) bool {
|
||||
if e[i].usage != e[j].usage {
|
||||
return e[i].usage < e[j].usage
|
||||
}
|
||||
if e[i].index != e[j].index {
|
||||
return e[i].index < e[j].index
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e elements) Swap(i, j int) {
|
||||
e[i], e[j] = e[j], e[i]
|
||||
}
|
||||
|
||||
func (g *nativeGamepad) present() bool {
|
||||
return g.device != 0
|
||||
}
|
||||
|
||||
func (g *nativeGamepad) elementValue(e *element) int {
|
||||
if g.device == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var valueRef C.IOHIDValueRef
|
||||
if C.IOHIDDeviceGetValue(g.device, e.native, &valueRef) == C.kIOReturnSuccess {
|
||||
return int(C.IOHIDValueGetIntegerValue(valueRef))
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (g *nativeGamepad) update() {
|
||||
if cap(g.axisValues) < len(g.axes) {
|
||||
g.axisValues = make([]float64, len(g.axes))
|
||||
}
|
||||
g.axisValues = g.axisValues[:len(g.axes)]
|
||||
|
||||
if cap(g.buttonValues) < len(g.buttons) {
|
||||
g.buttonValues = make([]bool, len(g.buttons))
|
||||
}
|
||||
g.buttonValues = g.buttonValues[:len(g.buttons)]
|
||||
|
||||
if cap(g.hatValues) < len(g.hats) {
|
||||
g.hatValues = make([]int, len(g.hats))
|
||||
}
|
||||
g.hatValues = g.hatValues[:len(g.hats)]
|
||||
|
||||
for i, a := range g.axes {
|
||||
raw := g.elementValue(&a)
|
||||
if raw < a.minimum {
|
||||
a.minimum = raw
|
||||
}
|
||||
if raw > a.maximum {
|
||||
a.maximum = raw
|
||||
}
|
||||
var value float64
|
||||
if size := a.maximum - a.minimum; size != 0 {
|
||||
value = 2*float64(raw-a.minimum)/float64(size) - 1
|
||||
}
|
||||
g.axisValues[i] = value
|
||||
}
|
||||
|
||||
for i, b := range g.buttons {
|
||||
g.buttonValues[i] = g.elementValue(&b) > 0
|
||||
}
|
||||
|
||||
hatStates := []int{
|
||||
hatUp,
|
||||
hatRightUp,
|
||||
hatRight,
|
||||
hatRightDown,
|
||||
hatDown,
|
||||
hatLeftDown,
|
||||
hatLeft,
|
||||
hatLeftUp,
|
||||
}
|
||||
for i, h := range g.hats {
|
||||
if state := g.elementValue(&h); state < 0 || state >= len(hatStates) {
|
||||
g.hatValues[i] = hatCentered
|
||||
} else {
|
||||
g.hatValues[i] = hatStates[state]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *nativeGamepad) axisNum() int {
|
||||
return len(g.axisValues)
|
||||
}
|
||||
|
||||
func (g *nativeGamepad) buttonNum() int {
|
||||
return len(g.buttonValues)
|
||||
}
|
||||
|
||||
func (g *nativeGamepad) hatNum() int {
|
||||
return len(g.hatValues)
|
||||
}
|
||||
|
||||
func (g *nativeGamepad) axisValue(axis int) float64 {
|
||||
if axis < 0 || axis >= len(g.axisValues) {
|
||||
return 0
|
||||
}
|
||||
return g.axisValues[axis]
|
||||
}
|
||||
|
||||
func (g *nativeGamepad) isButtonPressed(button int) bool {
|
||||
if button < 0 || button >= len(g.buttonValues) {
|
||||
return false
|
||||
}
|
||||
return g.buttonValues[button]
|
||||
}
|
||||
|
||||
func (g *nativeGamepad) hatState(hat int) int {
|
||||
if hat < 0 || hat >= len(g.hatValues) {
|
||||
return hatCentered
|
||||
}
|
||||
return g.hatValues[hat]
|
||||
}
|
||||
|
||||
func (g *nativeGamepads) init() {
|
||||
var dicts []unsafe.Pointer
|
||||
|
||||
page := C.kHIDPage_GenericDesktop
|
||||
for _, usage := range []uint{
|
||||
C.kHIDUsage_GD_Joystick,
|
||||
C.kHIDUsage_GD_GamePad,
|
||||
C.kHIDUsage_GD_MultiAxisController,
|
||||
} {
|
||||
pageRef := C.CFNumberCreate(C.kCFAllocatorDefault, C.kCFNumberIntType, unsafe.Pointer(&page))
|
||||
if pageRef == 0 {
|
||||
panic("gamepad: CFNumberCreate returned nil")
|
||||
}
|
||||
defer C.CFRelease(C.CFTypeRef(pageRef))
|
||||
|
||||
usageRef := C.CFNumberCreate(C.kCFAllocatorDefault, C.kCFNumberIntType, unsafe.Pointer(&usage))
|
||||
if usageRef == 0 {
|
||||
panic("gamepad: CFNumberCreate returned nil")
|
||||
}
|
||||
defer C.CFRelease(C.CFTypeRef(usageRef))
|
||||
|
||||
keys := []unsafe.Pointer{
|
||||
unsafe.Pointer(C.cfStringRefIOHIDDeviceUsagePageKey()),
|
||||
unsafe.Pointer(C.cfStringRefIOHIDDeviceUsageKey()),
|
||||
}
|
||||
values := []unsafe.Pointer{
|
||||
unsafe.Pointer(pageRef),
|
||||
unsafe.Pointer(usageRef),
|
||||
}
|
||||
|
||||
dict := C.CFDictionaryCreate(C.kCFAllocatorDefault, &keys[0], &values[0], C.CFIndex(len(keys)), &C.kCFTypeDictionaryKeyCallBacks, &C.kCFTypeDictionaryValueCallBacks)
|
||||
if dict == 0 {
|
||||
panic("gamepad: CFDictionaryCreate returned nil")
|
||||
}
|
||||
defer C.CFRelease(C.CFTypeRef(unsafe.Pointer(dict)))
|
||||
|
||||
dicts = append(dicts, unsafe.Pointer(dict))
|
||||
}
|
||||
|
||||
matching := C.CFArrayCreate(C.kCFAllocatorDefault, &dicts[0], C.CFIndex(len(dicts)), &C.kCFTypeArrayCallBacks)
|
||||
if matching == 0 {
|
||||
panic("gamepad: CFArrayCreateMutable returned nil")
|
||||
}
|
||||
defer C.CFRelease(C.CFTypeRef(matching))
|
||||
|
||||
g.hidManager = C.IOHIDManagerCreate(C.kCFAllocatorDefault, C.kIOHIDOptionsTypeNone)
|
||||
if C.IOHIDManagerOpen(g.hidManager, C.kIOHIDOptionsTypeNone) != C.kIOReturnSuccess {
|
||||
panic("gamepad: IOHIDManagerOpen failed")
|
||||
}
|
||||
|
||||
C.IOHIDManagerSetDeviceMatchingMultiple(g.hidManager, matching)
|
||||
C.IOHIDManagerRegisterDeviceMatchingCallback(g.hidManager, C.IOHIDDeviceCallback(C.ebitenGamepadMatchingCallback), nil)
|
||||
C.IOHIDManagerRegisterDeviceRemovalCallback(g.hidManager, C.IOHIDDeviceCallback(C.ebitenGamepadRemovalCallback), nil)
|
||||
|
||||
C.IOHIDManagerScheduleWithRunLoop(g.hidManager, C.CFRunLoopGetMain(), C.kCFRunLoopDefaultMode)
|
||||
|
||||
// Execute the run loop once in order to register any initially-attached gamepads.
|
||||
C.CFRunLoopRunInMode(C.kCFRunLoopDefaultMode, 0, 0 /* false */)
|
||||
}
|
||||
|
||||
//export ebitenGamepadMatchingCallback
|
||||
func ebitenGamepadMatchingCallback(ctx unsafe.Pointer, res C.IOReturn, sender unsafe.Pointer, device C.IOHIDDeviceRef) {
|
||||
if theGamepads.find(func(g *Gamepad) bool {
|
||||
return g.device == device
|
||||
}) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
name := "Unknown"
|
||||
if prop := C.IOHIDDeviceGetProperty(device, C.cfStringRefIOHIDProductKey()); prop != 0 {
|
||||
var cstr [256]C.char
|
||||
C.CFStringGetCString(C.CFStringRef(prop), &cstr[0], C.CFIndex(len(cstr)), C.kCFStringEncodingUTF8)
|
||||
name = C.GoString(&cstr[0])
|
||||
}
|
||||
|
||||
var vendor uint32
|
||||
if prop := C.IOHIDDeviceGetProperty(device, C.cfStringRefIOHIDVendorIDKey()); prop != 0 {
|
||||
C.CFNumberGetValue(C.CFNumberRef(prop), C.kCFNumberSInt32Type, unsafe.Pointer(&vendor))
|
||||
}
|
||||
|
||||
var product uint32
|
||||
if prop := C.IOHIDDeviceGetProperty(device, C.cfStringRefIOHIDProductIDKey()); prop != 0 {
|
||||
C.CFNumberGetValue(C.CFNumberRef(prop), C.kCFNumberSInt32Type, unsafe.Pointer(&product))
|
||||
}
|
||||
|
||||
var version uint32
|
||||
if prop := C.IOHIDDeviceGetProperty(device, C.cfStringRefIOHIDVersionNumberKey()); prop != 0 {
|
||||
C.CFNumberGetValue(C.CFNumberRef(prop), C.kCFNumberSInt32Type, unsafe.Pointer(&version))
|
||||
}
|
||||
|
||||
var sdlID string
|
||||
if vendor != 0 && product != 0 {
|
||||
sdlID = fmt.Sprintf("03000000%02x%02x0000%02x%02x0000%02x%02x0000",
|
||||
byte(vendor), byte(vendor>>8),
|
||||
byte(product), byte(product>>8),
|
||||
byte(version), byte(version>>8))
|
||||
} else {
|
||||
bs := []byte(name)
|
||||
if len(bs) < 12 {
|
||||
bs = append(bs, make([]byte, 12-len(bs))...)
|
||||
}
|
||||
sdlID = fmt.Sprintf("05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
bs[0], bs[1], bs[2], bs[3], bs[4], bs[5], bs[6], bs[7], bs[8], bs[9], bs[10], bs[11])
|
||||
}
|
||||
|
||||
elements := C.IOHIDDeviceCopyMatchingElements(device, 0, C.kIOHIDOptionsTypeNone)
|
||||
defer C.CFRelease(C.CFTypeRef(elements))
|
||||
|
||||
g := theGamepads.add(name, sdlID)
|
||||
g.device = device
|
||||
|
||||
for i := C.CFIndex(0); i < C.CFArrayGetCount(elements); i++ {
|
||||
native := (C.IOHIDElementRef)(C.CFArrayGetValueAtIndex(elements, i))
|
||||
if C.CFGetTypeID(C.CFTypeRef(native)) != C.IOHIDElementGetTypeID() {
|
||||
continue
|
||||
}
|
||||
|
||||
typ := C.IOHIDElementGetType(native)
|
||||
if typ != C.kIOHIDElementTypeInput_Axis &&
|
||||
typ != C.kIOHIDElementTypeInput_Button &&
|
||||
typ != C.kIOHIDElementTypeInput_Misc {
|
||||
continue
|
||||
}
|
||||
|
||||
usage := C.IOHIDElementGetUsage(native)
|
||||
page := C.IOHIDElementGetUsagePage(native)
|
||||
|
||||
switch page {
|
||||
case C.kHIDPage_GenericDesktop:
|
||||
switch usage {
|
||||
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{
|
||||
native: native,
|
||||
usage: int(usage),
|
||||
index: len(g.axes),
|
||||
minimum: int(C.IOHIDElementGetLogicalMin(native)),
|
||||
maximum: int(C.IOHIDElementGetLogicalMax(native)),
|
||||
})
|
||||
case C.kHIDUsage_GD_Hatswitch:
|
||||
g.hats = append(g.hats, element{
|
||||
native: native,
|
||||
usage: int(usage),
|
||||
index: len(g.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{
|
||||
native: native,
|
||||
usage: int(usage),
|
||||
index: len(g.buttons),
|
||||
minimum: int(C.IOHIDElementGetLogicalMin(native)),
|
||||
maximum: int(C.IOHIDElementGetLogicalMax(native)),
|
||||
})
|
||||
}
|
||||
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{
|
||||
native: native,
|
||||
usage: int(usage),
|
||||
index: len(g.axes),
|
||||
minimum: int(C.IOHIDElementGetLogicalMin(native)),
|
||||
maximum: int(C.IOHIDElementGetLogicalMax(native)),
|
||||
})
|
||||
}
|
||||
case C.kHIDPage_Button, C.kHIDPage_Consumer:
|
||||
g.buttons = append(g.buttons, element{
|
||||
native: native,
|
||||
usage: int(usage),
|
||||
index: len(g.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
|
||||
})
|
||||
}
|
255
internal/uidriver/glfw/gamepad_glfw.go
Normal file
255
internal/uidriver/glfw/gamepad_glfw.go
Normal file
@ -0,0 +1,255 @@
|
||||
// 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.
|
||||
|
||||
//go:build !android && !js && !darwin
|
||||
// +build !android,!js,!darwin
|
||||
|
||||
package glfw
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/driver"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/gamepaddb"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/glfw"
|
||||
)
|
||||
|
||||
type nativeGamepads struct {
|
||||
}
|
||||
|
||||
func (i *Input) updateGamepads() {
|
||||
for id := glfw.Joystick(0); id < glfw.Joystick(len(i.gamepads)); id++ {
|
||||
i.gamepads[id].valid = false
|
||||
if !id.Present() {
|
||||
continue
|
||||
}
|
||||
|
||||
buttons := id.GetButtons()
|
||||
|
||||
// A gamepad can be detected even though there are not. Apparently, some special devices are
|
||||
// recognized as gamepads by GLFW. In this case, the number of the 'buttons' can exceeds the
|
||||
// maximum. Skip such devices as a tentative solution (#1173).
|
||||
if len(buttons) > driver.GamepadButtonNum {
|
||||
continue
|
||||
}
|
||||
|
||||
i.gamepads[id].valid = true
|
||||
|
||||
i.gamepads[id].buttonNum = len(buttons)
|
||||
for b := 0; b < len(i.gamepads[id].buttonPressed); b++ {
|
||||
if len(buttons) <= b {
|
||||
i.gamepads[id].buttonPressed[b] = false
|
||||
continue
|
||||
}
|
||||
i.gamepads[id].buttonPressed[b] = glfw.Action(buttons[b]) == glfw.Press
|
||||
}
|
||||
|
||||
axes32 := id.GetAxes()
|
||||
i.gamepads[id].axisNum = len(axes32)
|
||||
for a := 0; a < len(i.gamepads[id].axes); a++ {
|
||||
if len(axes32) <= a {
|
||||
i.gamepads[id].axes[a] = 0
|
||||
continue
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Input) AppendGamepadIDs(gamepadIDs []driver.GamepadID) []driver.GamepadID {
|
||||
if !i.ui.isRunning() {
|
||||
return nil
|
||||
}
|
||||
|
||||
i.ui.m.RLock()
|
||||
defer i.ui.m.RUnlock()
|
||||
for id, g := range i.gamepads {
|
||||
if g.valid {
|
||||
gamepadIDs = append(gamepadIDs, driver.GamepadID(id))
|
||||
}
|
||||
}
|
||||
return gamepadIDs
|
||||
}
|
||||
|
||||
func (i *Input) GamepadSDLID(id driver.GamepadID) string {
|
||||
if !i.ui.isRunning() {
|
||||
return ""
|
||||
}
|
||||
|
||||
i.ui.m.RLock()
|
||||
defer i.ui.m.RUnlock()
|
||||
if len(i.gamepads) <= int(id) {
|
||||
return ""
|
||||
}
|
||||
return i.gamepads[id].guid
|
||||
}
|
||||
|
||||
func (i *Input) GamepadName(id driver.GamepadID) string {
|
||||
if !i.ui.isRunning() {
|
||||
return ""
|
||||
}
|
||||
|
||||
i.ui.m.RLock()
|
||||
defer i.ui.m.RUnlock()
|
||||
if len(i.gamepads) <= int(id) {
|
||||
return ""
|
||||
}
|
||||
return i.gamepads[id].name
|
||||
}
|
||||
|
||||
func (i *Input) GamepadAxisNum(id driver.GamepadID) int {
|
||||
if !i.ui.isRunning() {
|
||||
return 0
|
||||
}
|
||||
|
||||
i.ui.m.RLock()
|
||||
defer i.ui.m.RUnlock()
|
||||
if len(i.gamepads) <= int(id) {
|
||||
return 0
|
||||
}
|
||||
return i.gamepads[id].axisNum
|
||||
}
|
||||
|
||||
func (i *Input) GamepadAxisValue(id driver.GamepadID, axis int) float64 {
|
||||
if !i.ui.isRunning() {
|
||||
return 0
|
||||
}
|
||||
|
||||
i.ui.m.RLock()
|
||||
defer i.ui.m.RUnlock()
|
||||
if len(i.gamepads) <= int(id) {
|
||||
return 0
|
||||
}
|
||||
return i.gamepads[id].axes[axis]
|
||||
}
|
||||
|
||||
func (i *Input) GamepadButtonNum(id driver.GamepadID) int {
|
||||
if !i.ui.isRunning() {
|
||||
return 0
|
||||
}
|
||||
|
||||
i.ui.m.RLock()
|
||||
defer i.ui.m.RUnlock()
|
||||
if len(i.gamepads) <= int(id) {
|
||||
return 0
|
||||
}
|
||||
return i.gamepads[id].buttonNum
|
||||
}
|
||||
|
||||
func (i *Input) IsGamepadButtonPressed(id driver.GamepadID, button driver.GamepadButton) bool {
|
||||
if !i.ui.isRunning() {
|
||||
return false
|
||||
}
|
||||
|
||||
i.ui.m.RLock()
|
||||
defer i.ui.m.RUnlock()
|
||||
if len(i.gamepads) <= int(id) {
|
||||
return false
|
||||
}
|
||||
return i.gamepads[id].buttonPressed[button]
|
||||
}
|
||||
|
||||
func (i *Input) IsStandardGamepadLayoutAvailable(id driver.GamepadID) bool {
|
||||
i.ui.m.Lock()
|
||||
defer i.ui.m.Unlock()
|
||||
|
||||
if len(i.gamepads) <= int(id) {
|
||||
return false
|
||||
}
|
||||
g := i.gamepads[int(id)]
|
||||
return gamepaddb.HasStandardLayoutMapping(g.guid)
|
||||
}
|
||||
|
||||
func (i *Input) StandardGamepadAxisValue(id driver.GamepadID, axis driver.StandardGamepadAxis) float64 {
|
||||
i.ui.m.Lock()
|
||||
defer i.ui.m.Unlock()
|
||||
|
||||
if len(i.gamepads) <= int(id) {
|
||||
return 0
|
||||
}
|
||||
g := i.gamepads[int(id)]
|
||||
return gamepaddb.AxisValue(g.guid, axis, gamepadState{&g})
|
||||
}
|
||||
|
||||
func (i *Input) StandardGamepadButtonValue(id driver.GamepadID, button driver.StandardGamepadButton) float64 {
|
||||
i.ui.m.Lock()
|
||||
defer i.ui.m.Unlock()
|
||||
|
||||
if len(i.gamepads) <= int(id) {
|
||||
return 0
|
||||
}
|
||||
g := i.gamepads[int(id)]
|
||||
return gamepaddb.ButtonValue(g.guid, button, gamepadState{&g})
|
||||
}
|
||||
|
||||
func (i *Input) IsStandardGamepadButtonPressed(id driver.GamepadID, button driver.StandardGamepadButton) bool {
|
||||
i.ui.m.Lock()
|
||||
defer i.ui.m.Unlock()
|
||||
|
||||
if len(i.gamepads) <= int(id) {
|
||||
return false
|
||||
}
|
||||
g := i.gamepads[int(id)]
|
||||
return gamepaddb.IsButtonPressed(g.guid, button, gamepadState{&g})
|
||||
}
|
||||
|
||||
func (i *Input) VibrateGamepad(id driver.GamepadID, duration time.Duration, strongMagnitude float64, weakMagnitude float64) {
|
||||
// TODO: Implement this (#1452)
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Confirm that all the hat state values are the same.
|
||||
if gamepaddb.HatUp != glfw.HatUp {
|
||||
panic("glfw: gamepaddb.HatUp must equal to glfw.HatUp but not")
|
||||
}
|
||||
if gamepaddb.HatRight != glfw.HatRight {
|
||||
panic("glfw: gamepaddb.HatRight must equal to glfw.HatRight but not")
|
||||
}
|
||||
if gamepaddb.HatDown != glfw.HatDown {
|
||||
panic("glfw: gamepaddb.HatDown must equal to glfw.HatDown but not")
|
||||
}
|
||||
if gamepaddb.HatLeft != glfw.HatLeft {
|
||||
panic("glfw: gamepaddb.HatLeft must equal to glfw.HatLeft but not")
|
||||
}
|
||||
}
|
||||
|
||||
type gamepadState struct {
|
||||
g *gamepad
|
||||
}
|
||||
|
||||
func (s gamepadState) Axis(index int) float64 {
|
||||
return s.g.axes[index]
|
||||
}
|
||||
|
||||
func (s gamepadState) Button(index int) bool {
|
||||
return s.g.buttonPressed[index]
|
||||
}
|
||||
|
||||
func (s gamepadState) Hat(index int) int {
|
||||
return s.g.hats[index]
|
||||
}
|
133
internal/uidriver/glfw/gamepad_native.go
Normal file
133
internal/uidriver/glfw/gamepad_native.go
Normal file
@ -0,0 +1,133 @@
|
||||
// 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.
|
||||
|
||||
//go:build darwin && !ios
|
||||
// +build darwin,!ios
|
||||
|
||||
package glfw
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/driver"
|
||||
gamepadpkg "github.com/hajimehoshi/ebiten/v2/internal/gamepad"
|
||||
)
|
||||
|
||||
type nativeGamepads struct{}
|
||||
|
||||
func (i *Input) updateGamepads() {
|
||||
gamepadpkg.Update()
|
||||
}
|
||||
|
||||
func (i *Input) AppendGamepadIDs(gamepadIDs []driver.GamepadID) []driver.GamepadID {
|
||||
return gamepadpkg.AppendGamepadIDs(gamepadIDs)
|
||||
}
|
||||
|
||||
func (i *Input) GamepadSDLID(id driver.GamepadID) string {
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
return ""
|
||||
}
|
||||
return g.SDLID()
|
||||
}
|
||||
|
||||
func (i *Input) GamepadName(id driver.GamepadID) string {
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
return ""
|
||||
}
|
||||
return g.Name()
|
||||
}
|
||||
|
||||
func (i *Input) GamepadAxisNum(id driver.GamepadID) int {
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
return 0
|
||||
}
|
||||
return g.AxisNum()
|
||||
}
|
||||
|
||||
func (i *Input) GamepadAxisValue(id driver.GamepadID, axis int) float64 {
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
return 0
|
||||
}
|
||||
return g.Axis(axis)
|
||||
}
|
||||
|
||||
func (i *Input) GamepadButtonNum(id driver.GamepadID) int {
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
// For backward compatibility, hats are treated as buttons in GLFW.
|
||||
return g.ButtonNum() + g.HatNum()*4
|
||||
}
|
||||
|
||||
func (i *Input) IsGamepadButtonPressed(id driver.GamepadID, button driver.GamepadButton) bool {
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
nbuttons := g.ButtonNum()
|
||||
if int(button) < nbuttons {
|
||||
return g.Button(int(button))
|
||||
}
|
||||
|
||||
// For backward compatibility, hats are treated as buttons in GLFW.
|
||||
if hat := (int(button) - nbuttons) / 4; hat < g.HatNum() {
|
||||
dir := (int(button) - nbuttons) % 4
|
||||
return g.Hat(hat)&(1<<dir) != 0
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (i *Input) IsStandardGamepadLayoutAvailable(id driver.GamepadID) bool {
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
return false
|
||||
}
|
||||
return g.IsStandardLayoutAvailable()
|
||||
}
|
||||
|
||||
func (i *Input) StandardGamepadAxisValue(id driver.GamepadID, axis driver.StandardGamepadAxis) float64 {
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
return 0
|
||||
}
|
||||
return g.StandardAxisValue(axis)
|
||||
}
|
||||
|
||||
func (i *Input) StandardGamepadButtonValue(id driver.GamepadID, button driver.StandardGamepadButton) float64 {
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
return 0
|
||||
}
|
||||
return g.StandardButtonValue(button)
|
||||
}
|
||||
|
||||
func (i *Input) IsStandardGamepadButtonPressed(id driver.GamepadID, button driver.StandardGamepadButton) bool {
|
||||
g := gamepadpkg.Get(id)
|
||||
if g == nil {
|
||||
return false
|
||||
}
|
||||
return g.IsStandardButtonPressed(button)
|
||||
}
|
||||
|
||||
func (i *Input) VibrateGamepad(id driver.GamepadID, duration time.Duration, strongMagnitude float64, weakMagnitude float64) {
|
||||
// TODO: Implement this (#1452)
|
||||
}
|
@ -20,11 +20,9 @@ package glfw
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/driver"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/gamepaddb"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/glfw"
|
||||
)
|
||||
|
||||
@ -52,6 +50,8 @@ type Input struct {
|
||||
touches map[driver.TouchID]pos // TODO: Implement this (#417)
|
||||
runeBuffer []rune
|
||||
ui *UserInterface
|
||||
|
||||
nativeGamepads
|
||||
}
|
||||
|
||||
type pos struct {
|
||||
@ -69,99 +69,6 @@ func (i *Input) CursorPosition() (x, y int) {
|
||||
return i.cursorX, i.cursorY
|
||||
}
|
||||
|
||||
func (i *Input) AppendGamepadIDs(gamepadIDs []driver.GamepadID) []driver.GamepadID {
|
||||
if !i.ui.isRunning() {
|
||||
return nil
|
||||
}
|
||||
|
||||
i.ui.m.RLock()
|
||||
defer i.ui.m.RUnlock()
|
||||
for id, g := range i.gamepads {
|
||||
if g.valid {
|
||||
gamepadIDs = append(gamepadIDs, driver.GamepadID(id))
|
||||
}
|
||||
}
|
||||
return gamepadIDs
|
||||
}
|
||||
|
||||
func (i *Input) GamepadSDLID(id driver.GamepadID) string {
|
||||
if !i.ui.isRunning() {
|
||||
return ""
|
||||
}
|
||||
|
||||
i.ui.m.RLock()
|
||||
defer i.ui.m.RUnlock()
|
||||
if len(i.gamepads) <= int(id) {
|
||||
return ""
|
||||
}
|
||||
return i.gamepads[id].guid
|
||||
}
|
||||
|
||||
func (i *Input) GamepadName(id driver.GamepadID) string {
|
||||
if !i.ui.isRunning() {
|
||||
return ""
|
||||
}
|
||||
|
||||
i.ui.m.RLock()
|
||||
defer i.ui.m.RUnlock()
|
||||
if len(i.gamepads) <= int(id) {
|
||||
return ""
|
||||
}
|
||||
return i.gamepads[id].name
|
||||
}
|
||||
|
||||
func (i *Input) GamepadAxisNum(id driver.GamepadID) int {
|
||||
if !i.ui.isRunning() {
|
||||
return 0
|
||||
}
|
||||
|
||||
i.ui.m.RLock()
|
||||
defer i.ui.m.RUnlock()
|
||||
if len(i.gamepads) <= int(id) {
|
||||
return 0
|
||||
}
|
||||
return i.gamepads[id].axisNum
|
||||
}
|
||||
|
||||
func (i *Input) GamepadAxisValue(id driver.GamepadID, axis int) float64 {
|
||||
if !i.ui.isRunning() {
|
||||
return 0
|
||||
}
|
||||
|
||||
i.ui.m.RLock()
|
||||
defer i.ui.m.RUnlock()
|
||||
if len(i.gamepads) <= int(id) {
|
||||
return 0
|
||||
}
|
||||
return i.gamepads[id].axes[axis]
|
||||
}
|
||||
|
||||
func (i *Input) GamepadButtonNum(id driver.GamepadID) int {
|
||||
if !i.ui.isRunning() {
|
||||
return 0
|
||||
}
|
||||
|
||||
i.ui.m.RLock()
|
||||
defer i.ui.m.RUnlock()
|
||||
if len(i.gamepads) <= int(id) {
|
||||
return 0
|
||||
}
|
||||
return i.gamepads[id].buttonNum
|
||||
}
|
||||
|
||||
func (i *Input) IsGamepadButtonPressed(id driver.GamepadID, button driver.GamepadButton) bool {
|
||||
if !i.ui.isRunning() {
|
||||
return false
|
||||
}
|
||||
|
||||
i.ui.m.RLock()
|
||||
defer i.ui.m.RUnlock()
|
||||
if len(i.gamepads) <= int(id) {
|
||||
return false
|
||||
}
|
||||
return i.gamepads[id].buttonPressed[button]
|
||||
}
|
||||
|
||||
func (i *Input) AppendTouchIDs(touchIDs []driver.TouchID) []driver.TouchID {
|
||||
if !i.ui.isRunning() {
|
||||
return nil
|
||||
@ -311,134 +218,5 @@ func (i *Input) update(window *glfw.Window, context driver.UIContext) {
|
||||
i.cursorX, i.cursorY = int(cx), int(cy)
|
||||
}
|
||||
|
||||
for id := glfw.Joystick(0); id < glfw.Joystick(len(i.gamepads)); id++ {
|
||||
i.gamepads[id].valid = false
|
||||
if !id.Present() {
|
||||
continue
|
||||
}
|
||||
|
||||
buttons := id.GetButtons()
|
||||
|
||||
// A gamepad can be detected even though there are not. Apparently, some special devices are
|
||||
// recognized as gamepads by GLFW. In this case, the number of the 'buttons' can exceeds the
|
||||
// maximum. Skip such devices as a tentative solution (#1173).
|
||||
if len(buttons) > driver.GamepadButtonNum {
|
||||
continue
|
||||
}
|
||||
|
||||
i.gamepads[id].valid = true
|
||||
|
||||
i.gamepads[id].buttonNum = len(buttons)
|
||||
for b := 0; b < len(i.gamepads[id].buttonPressed); b++ {
|
||||
if len(buttons) <= b {
|
||||
i.gamepads[id].buttonPressed[b] = false
|
||||
continue
|
||||
}
|
||||
i.gamepads[id].buttonPressed[b] = glfw.Action(buttons[b]) == glfw.Press
|
||||
}
|
||||
|
||||
axes32 := id.GetAxes()
|
||||
i.gamepads[id].axisNum = len(axes32)
|
||||
for a := 0; a < len(i.gamepads[id].axes); a++ {
|
||||
if len(axes32) <= a {
|
||||
i.gamepads[id].axes[a] = 0
|
||||
continue
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Input) IsStandardGamepadLayoutAvailable(id driver.GamepadID) bool {
|
||||
i.ui.m.Lock()
|
||||
defer i.ui.m.Unlock()
|
||||
|
||||
if len(i.gamepads) <= int(id) {
|
||||
return false
|
||||
}
|
||||
g := i.gamepads[int(id)]
|
||||
return gamepaddb.HasStandardLayoutMapping(g.guid)
|
||||
}
|
||||
|
||||
func (i *Input) StandardGamepadAxisValue(id driver.GamepadID, axis driver.StandardGamepadAxis) float64 {
|
||||
i.ui.m.Lock()
|
||||
defer i.ui.m.Unlock()
|
||||
|
||||
if len(i.gamepads) <= int(id) {
|
||||
return 0
|
||||
}
|
||||
g := i.gamepads[int(id)]
|
||||
return gamepaddb.AxisValue(g.guid, axis, gamepadState{&g})
|
||||
}
|
||||
|
||||
func (i *Input) StandardGamepadButtonValue(id driver.GamepadID, button driver.StandardGamepadButton) float64 {
|
||||
i.ui.m.Lock()
|
||||
defer i.ui.m.Unlock()
|
||||
|
||||
if len(i.gamepads) <= int(id) {
|
||||
return 0
|
||||
}
|
||||
g := i.gamepads[int(id)]
|
||||
return gamepaddb.ButtonValue(g.guid, button, gamepadState{&g})
|
||||
}
|
||||
|
||||
func (i *Input) IsStandardGamepadButtonPressed(id driver.GamepadID, button driver.StandardGamepadButton) bool {
|
||||
i.ui.m.Lock()
|
||||
defer i.ui.m.Unlock()
|
||||
|
||||
if len(i.gamepads) <= int(id) {
|
||||
return false
|
||||
}
|
||||
g := i.gamepads[int(id)]
|
||||
return gamepaddb.IsButtonPressed(g.guid, button, gamepadState{&g})
|
||||
}
|
||||
|
||||
func (i *Input) VibrateGamepad(id driver.GamepadID, duration time.Duration, strongMagnitude float64, weakMagnitude float64) {
|
||||
// TODO: Implement this (#1452)
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Confirm that all the hat state values are the same.
|
||||
if gamepaddb.HatUp != glfw.HatUp {
|
||||
panic("glfw: gamepaddb.HatUp must equal to glfw.HatUp but not")
|
||||
}
|
||||
if gamepaddb.HatRight != glfw.HatRight {
|
||||
panic("glfw: gamepaddb.HatRight must equal to glfw.HatRight but not")
|
||||
}
|
||||
if gamepaddb.HatDown != glfw.HatDown {
|
||||
panic("glfw: gamepaddb.HatDown must equal to glfw.HatDown but not")
|
||||
}
|
||||
if gamepaddb.HatLeft != glfw.HatLeft {
|
||||
panic("glfw: gamepaddb.HatLeft must equal to glfw.HatLeft but not")
|
||||
}
|
||||
}
|
||||
|
||||
type gamepadState struct {
|
||||
g *gamepad
|
||||
}
|
||||
|
||||
func (s gamepadState) Axis(index int) float64 {
|
||||
return s.g.axes[index]
|
||||
}
|
||||
|
||||
func (s gamepadState) Button(index int) bool {
|
||||
return s.g.buttonPressed[index]
|
||||
}
|
||||
|
||||
func (s gamepadState) Hat(index int) int {
|
||||
return s.g.hats[index]
|
||||
i.updateGamepads()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user