internal/gamepad: support Xbox

Updates #2084
This commit is contained in:
Hajime Hoshi 2022-07-01 21:44:46 +09:00
parent a6f6fdbfb7
commit ac5c48f575
2 changed files with 188 additions and 11 deletions

View File

@ -32,14 +32,6 @@ var (
procGameInputCreate = gameInput.NewProc("GameInputCreate")
)
type _GameInputEnumerationKind int32
const (
_GameInputNoEnumeration _GameInputEnumerationKind = 0
_GameInputAsyncEnumeration _GameInputEnumerationKind = 1
_GameInputBlockingEnumeration _GameInputEnumerationKind = 2
)
type _GameInputCallbackToken uint64
type _GameInputDeviceStatus int32
@ -58,6 +50,34 @@ const (
_GameInputDeviceAnyStatus _GameInputDeviceStatus = 0x00FFFFFF
)
type _GameInputEnumerationKind int32
const (
_GameInputNoEnumeration _GameInputEnumerationKind = 0
_GameInputAsyncEnumeration _GameInputEnumerationKind = 1
_GameInputBlockingEnumeration _GameInputEnumerationKind = 2
)
type _GameInputGamepadButtons int32
const (
_GameInputGamepadNone _GameInputGamepadButtons = 0x00000000
_GameInputGamepadMenu _GameInputGamepadButtons = 0x00000001
_GameInputGamepadView _GameInputGamepadButtons = 0x00000002
_GameInputGamepadA _GameInputGamepadButtons = 0x00000004
_GameInputGamepadB _GameInputGamepadButtons = 0x00000008
_GameInputGamepadX _GameInputGamepadButtons = 0x00000010
_GameInputGamepadY _GameInputGamepadButtons = 0x00000020
_GameInputGamepadDPadUp _GameInputGamepadButtons = 0x00000040
_GameInputGamepadDPadDown _GameInputGamepadButtons = 0x00000080
_GameInputGamepadDPadLeft _GameInputGamepadButtons = 0x00000100
_GameInputGamepadDPadRight _GameInputGamepadButtons = 0x00000200
_GameInputGamepadLeftShoulder _GameInputGamepadButtons = 0x00000400
_GameInputGamepadRightShoulder _GameInputGamepadButtons = 0x00000800
_GameInputGamepadLeftThumbstick _GameInputGamepadButtons = 0x00001000
_GameInputGamepadRightThumbstick _GameInputGamepadButtons = 0x00002000
)
type _GameInputKind int32
const (
@ -79,6 +99,16 @@ const (
_GameInputKindAny _GameInputKind = 0x0FFFFFFF
)
type _GameInputGamepadState struct {
buttons _GameInputGamepadButtons
leftTrigger float32
rightTrigger float32
leftThumbstickX float32
leftThumbstickY float32
rightThumbstickX float32
rightThumbstickY float32
}
func _GameInputCreate() (*_IGameInput, error) {
var gameInput *_IGameInput
r, _, _ := procGameInputCreate.Call(uintptr(unsafe.Pointer(&gameInput)))
@ -118,6 +148,18 @@ type _IGameInput_Vtbl struct {
SetFocusPolicy uintptr
}
func (i *_IGameInput) GetCurrentReading(inputKind _GameInputKind, device *_IGameInputDevice) (*_IGameInputReading, error) {
var reading *_IGameInputReading
r, _, _ := syscall.Syscall6(i.vtbl.GetCurrentReading, 4, uintptr(unsafe.Pointer(i)),
uintptr(inputKind), uintptr(unsafe.Pointer(device)), uintptr(unsafe.Pointer(&reading)),
0, 0)
runtime.KeepAlive(device)
if uint32(r) != uint32(windows.S_OK) {
return nil, fmt.Errorf("gamepad: IGameInput::GetCurrentReading failed: HRESULT(%d)", uint32(r))
}
return reading, nil
}
func (i *_IGameInput) RegisterDeviceCallback(device *_IGameInputDevice,
inputKind _GameInputKind,
statusFilter _GameInputDeviceStatus,
@ -165,3 +207,42 @@ type _IGameInputDevice_Vtbl struct {
AcquireExclusiveRawDeviceAccess uintptr
ReleaseExclusiveRawDeviceAccess uintptr
}
type _IGameInputReading struct {
vtbl *_IGameInputReading_Vtbl
}
type _IGameInputReading_Vtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
GetInputKind uintptr
GetSequenceNumber uintptr
GetTimestamp uintptr
GetDevice uintptr
GetRawReport uintptr
GetControllerAxisCount uintptr
GetControllerAxisState uintptr
GetControllerButtonCount uintptr
GetControllerButtonState uintptr
GetControllerSwitchCount uintptr
GetControllerSwitchState uintptr
GetKeyCount uintptr
GetKeyState uintptr
GetMouseState uintptr
GetTouchCount uintptr
GetTouchState uintptr
GetMotionState uintptr
GetArcadeStickState uintptr
GetFlightStickState uintptr
GetGamepadState uintptr
GetRacingWheelState uintptr
GetUiNavigationState uintptr
}
func (i *_IGameInputReading) GetGamepadState() (_GameInputGamepadState, bool) {
var state _GameInputGamepadState
r, _, _ := syscall.Syscall(i.vtbl.GetGamepadState, 2, uintptr(unsafe.Pointer(i)), uintptr(unsafe.Pointer(&state)), 0)
return state, int32(r) != 0
}

View File

@ -22,8 +22,50 @@ import (
"unsafe"
"golang.org/x/sys/windows"
"github.com/hajimehoshi/ebiten/v2/internal/gamepaddb"
)
func standardButtonToGamepadInputGamepadButton(b gamepaddb.StandardButton) (_GameInputGamepadButtons, bool) {
switch b {
case gamepaddb.StandardButtonRightBottom:
return _GameInputGamepadA, true
case gamepaddb.StandardButtonRightRight:
return _GameInputGamepadB, true
case gamepaddb.StandardButtonRightLeft:
return _GameInputGamepadX, true
case gamepaddb.StandardButtonRightTop:
return _GameInputGamepadY, true
case gamepaddb.StandardButtonFrontTopLeft:
return _GameInputGamepadLeftShoulder, true
case gamepaddb.StandardButtonFrontTopRight:
return _GameInputGamepadRightShoulder, true
case gamepaddb.StandardButtonFrontBottomLeft:
return 0, false // Use leftTrigger instead.
case gamepaddb.StandardButtonFrontBottomRight:
return 0, false // Use rightTrigger instead.
case gamepaddb.StandardButtonCenterLeft:
return _GameInputGamepadView, true
case gamepaddb.StandardButtonCenterRight:
return _GameInputGamepadMenu, true
case gamepaddb.StandardButtonLeftStick:
return _GameInputGamepadLeftThumbstick, true
case gamepaddb.StandardButtonRightStick:
return _GameInputGamepadRightThumbstick, true
case gamepaddb.StandardButtonLeftTop:
return _GameInputGamepadDPadUp, true
case gamepaddb.StandardButtonLeftBottom:
return _GameInputGamepadDPadDown, true
case gamepaddb.StandardButtonLeftLeft:
return _GameInputGamepadDPadLeft, true
case gamepaddb.StandardButtonLeftRight:
return _GameInputGamepadDPadRight, true
case gamepaddb.StandardButtonCenterCenter:
return 0, false
}
return 0, false
}
type nativeGamepadsXbox struct {
gameInput *_IGameInput
deviceCallbackPtr uintptr
@ -41,7 +83,7 @@ func (n *nativeGamepadsXbox) init(gamepads *gamepads) error {
if err := n.gameInput.RegisterDeviceCallback(
nil,
_GameInputKindAny,
_GameInputKindGamepad,
_GameInputDeviceConnected,
_GameInputBlockingEnumeration,
unsafe.Pointer(gamepads),
@ -80,9 +122,22 @@ func (n *nativeGamepadsXbox) deviceCallback(callbackToken _GameInputCallbackToke
type nativeGamepadXbox struct {
gameInputDevice *_IGameInputDevice
state _GameInputGamepadState
}
func (n *nativeGamepadXbox) update(gamepads *gamepads) error {
gameInput := gamepads.native.(*nativeGamepadsXbox).gameInput
r, err := gameInput.GetCurrentReading(_GameInputKindGamepad, n.gameInputDevice)
if err != nil {
return err
}
state, ok := r.GetGamepadState()
if !ok {
n.state = _GameInputGamepadState{}
return nil
}
n.state = state
return nil
}
@ -91,11 +146,11 @@ func (n *nativeGamepadXbox) hasOwnStandardLayoutMapping() bool {
}
func (n *nativeGamepadXbox) axisCount() int {
return 0
return int(gamepaddb.StandardAxisMax) + 1
}
func (n *nativeGamepadXbox) buttonCount() int {
return 0
return int(gamepaddb.StandardButtonMax) + 1
}
func (n *nativeGamepadXbox) hatCount() int {
@ -103,14 +158,55 @@ func (n *nativeGamepadXbox) hatCount() int {
}
func (n *nativeGamepadXbox) axisValue(axis int) float64 {
switch gamepaddb.StandardAxis(axis) {
case gamepaddb.StandardAxisLeftStickHorizontal:
return float64(n.state.leftThumbstickX)
case gamepaddb.StandardAxisLeftStickVertical:
return float64(n.state.leftThumbstickY)
case gamepaddb.StandardAxisRightStickHorizontal:
return float64(n.state.rightThumbstickX)
case gamepaddb.StandardAxisRightStickVertical:
return float64(n.state.rightThumbstickY)
}
return 0
}
func (n *nativeGamepadXbox) buttonValue(button int) float64 {
switch gamepaddb.StandardButton(button) {
case gamepaddb.StandardButtonFrontBottomLeft:
return float64(n.state.leftTrigger)
case gamepaddb.StandardButtonFrontBottomRight:
return float64(n.state.rightTrigger)
}
b, ok := standardButtonToGamepadInputGamepadButton(gamepaddb.StandardButton(button))
if !ok {
return 0
}
if n.state.buttons&b != 0 {
return 1
}
return 0
}
func (n *nativeGamepadXbox) isButtonPressed(button int) bool {
// Use XInput's trigger dead zone.
// See https://source.chromium.org/chromium/chromium/src/+/main:device/gamepad/public/cpp/gamepad.h;l=22-23;drc=6997f8a177359bb99598988ed5e900841984d242
// TODO: Integrate this value with the same one in the package gamepaddb.
const threshold = 30.0 / 255.0
switch gamepaddb.StandardButton(button) {
case gamepaddb.StandardButtonFrontBottomLeft:
return n.state.leftTrigger >= threshold
case gamepaddb.StandardButtonFrontBottomRight:
return n.state.rightTrigger >= threshold
}
b, ok := standardButtonToGamepadInputGamepadButton(gamepaddb.StandardButton(button))
if !ok {
return false
}
if n.state.buttons&b != 0 {
return true
}
return false
}