diff --git a/internal/gamepad/api_xbox_windows.go b/internal/gamepad/api_xbox_windows.go index 477535971..e6c382057 100644 --- a/internal/gamepad/api_xbox_windows.go +++ b/internal/gamepad/api_xbox_windows.go @@ -19,6 +19,8 @@ package gamepad import ( "fmt" + "runtime" + "syscall" "unsafe" "golang.org/x/sys/windows" @@ -30,6 +32,53 @@ var ( procGameInputCreate = gameInput.NewProc("GameInputCreate") ) +type _GameInputEnumerationKind int32 + +const ( + _GameInputNoEnumeration _GameInputEnumerationKind = 0 + _GameInputAsyncEnumeration _GameInputEnumerationKind = 1 + _GameInputBlockingEnumeration _GameInputEnumerationKind = 2 +) + +type _GameInputCallbackToken uint64 + +type _GameInputDeviceStatus int32 + +const ( + _GameInputDeviceNoStatus _GameInputDeviceStatus = 0x00000000 + _GameInputDeviceConnected _GameInputDeviceStatus = 0x00000001 + _GameInputDeviceInputEnabled _GameInputDeviceStatus = 0x00000002 + _GameInputDeviceOutputEnabled _GameInputDeviceStatus = 0x00000004 + _GameInputDeviceRawIoEnabled _GameInputDeviceStatus = 0x00000008 + _GameInputDeviceAudioCapture _GameInputDeviceStatus = 0x00000010 + _GameInputDeviceAudioRender _GameInputDeviceStatus = 0x00000020 + _GameInputDeviceSynchronized _GameInputDeviceStatus = 0x00000040 + _GameInputDeviceWireless _GameInputDeviceStatus = 0x00000080 + _GameInputDeviceUserIdle _GameInputDeviceStatus = 0x00100000 + _GameInputDeviceAnyStatus _GameInputDeviceStatus = 0x00FFFFFF +) + +type _GameInputKind int32 + +const ( + _GameInputKindUnknown _GameInputKind = 0x00000000 + _GameInputKindRawDeviceReport _GameInputKind = 0x00000001 + _GameInputKindControllerAxis _GameInputKind = 0x00000002 + _GameInputKindControllerButton _GameInputKind = 0x00000004 + _GameInputKindControllerSwitch _GameInputKind = 0x00000008 + _GameInputKindController _GameInputKind = 0x0000000E + _GameInputKindKeyboard _GameInputKind = 0x00000010 + _GameInputKindMouse _GameInputKind = 0x00000020 + _GameInputKindTouch _GameInputKind = 0x00000100 + _GameInputKindMotion _GameInputKind = 0x00001000 + _GameInputKindArcadeStick _GameInputKind = 0x00010000 + _GameInputKindFlightStick _GameInputKind = 0x00020000 + _GameInputKindGamepad _GameInputKind = 0x00040000 + _GameInputKindRacingWheel _GameInputKind = 0x00080000 + _GameInputKindUiNavigation _GameInputKind = 0x01000000 + _GameInputKindAny _GameInputKind = 0x0FFFFFFF +) + func _GameInputCreate() (*_IGameInput, error) { var gameInput *_IGameInput r, _, _ := procGameInputCreate.Call(uintptr(unsafe.Pointer(&gameInput))) @@ -47,4 +96,72 @@ type _IGameInput_Vtbl struct { QueryInterface uintptr AddRef uintptr Release uintptr + + GetCurrentTimestamp uintptr + GetCurrentReading uintptr + GetNextReading uintptr + GetPreviousReading uintptr + GetTemporalReading uintptr + RegisterReadingCallback uintptr + RegisterDeviceCallback uintptr + RegisterGuideButtonCallback uintptr + RegisterKeyboardLayoutCallback uintptr + StopCallback uintptr + UnregisterCallback uintptr + CreateDispatcher uintptr + CreateAggregateDevice uintptr + FindDeviceFromId uintptr + FindDeviceFromObject uintptr + FindDeviceFromPlatformHandle uintptr + FindDeviceFromPlatformString uintptr + EnableOemDeviceSupport uintptr + SetFocusPolicy uintptr +} + +func (i *_IGameInput) RegisterDeviceCallback(device *_IGameInputDevice, + inputKind _GameInputKind, + statusFilter _GameInputDeviceStatus, + enumerationKind _GameInputEnumerationKind, + context unsafe.Pointer, + callbackFunc uintptr, + callbackToken *_GameInputCallbackToken) error { + r, _, _ := syscall.Syscall9(i.vtbl.RegisterDeviceCallback, 8, uintptr(unsafe.Pointer(i)), + uintptr(unsafe.Pointer(device)), uintptr(inputKind), uintptr(statusFilter), + uintptr(enumerationKind), uintptr(context), callbackFunc, + uintptr(unsafe.Pointer(callbackToken)), 0) + runtime.KeepAlive(device) + runtime.KeepAlive(callbackToken) + if uint32(r) != uint32(windows.S_OK) { + return fmt.Errorf("gamepad: IGameInput::RegisterDeviceCallback failed: HRESULT(%d)", uint32(r)) + } + return nil +} + +type _IGameInputDevice struct { + vtbl *_IGameInputDevice_Vtbl +} + +type _IGameInputDevice_Vtbl struct { + QueryInterface uintptr + AddRef uintptr + Release uintptr + + GetDeviceInfo uintptr + GetDeviceStatus uintptr + GetBatteryState uintptr + CreateForceFeedbackEffect uintptr + IsForceFeedbackMotorPoweredOn uintptr + SetForceFeedbackMotorGain uintptr + SetHapticMotorState uintptr + SetRumbleState uintptr + SetInputSynchronizationState uintptr + SendInputSynchronizationHint uintptr + PowerOff uintptr + CreateRawDeviceReport uintptr + GetRawDeviceFeature uintptr + SetRawDeviceFeature uintptr + SendRawDeviceOutput uintptr + ExecuteRawDeviceIoControl uintptr + AcquireExclusiveRawDeviceAccess uintptr + ReleaseExclusiveRawDeviceAccess uintptr } diff --git a/internal/gamepad/gamepad_xbox_windows.go b/internal/gamepad/gamepad_xbox_windows.go index 0e1bf249b..cc71aa4c4 100644 --- a/internal/gamepad/gamepad_xbox_windows.go +++ b/internal/gamepad/gamepad_xbox_windows.go @@ -17,8 +17,27 @@ package gamepad +import ( + "unsafe" + + "golang.org/x/sys/windows" +) + type nativeGamepadsXbox struct { - gameInput *_IGameInput + gameInput *_IGameInput + deviceCallbackPtr uintptr + token _GameInputCallbackToken +} + +func xboxDeviceCallback( + callbackToken _GameInputCallbackToken, + context unsafe.Pointer, + device *_IGameInputDevice, + timestamp uint64, + currentStatus _GameInputDeviceStatus, + previousStatus _GameInputDeviceStatus) uintptr { + // TODO: Implement this. + return 0 } func (n *nativeGamepadsXbox) init(gamepads *gamepads) error { @@ -26,7 +45,21 @@ func (n *nativeGamepadsXbox) init(gamepads *gamepads) error { if err != nil { return err } + n.gameInput = g + n.deviceCallbackPtr = windows.NewCallbackCDecl(xboxDeviceCallback) + + if err := n.gameInput.RegisterDeviceCallback( + nil, + _GameInputKindGamepad, + _GameInputDeviceConnected, + _GameInputBlockingEnumeration, + nil, + n.deviceCallbackPtr, + &n.token, + ); err != nil { + return err + } return nil }