// Copyright 2022 The Ebitengine 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 !nintendosdk package gamepad import ( "fmt" "runtime" "syscall" "unsafe" "golang.org/x/sys/windows" ) type handleError windows.Handle func (h handleError) Error() string { return fmt.Sprintf("HANDLE(%d)", h) } var ( gameInput = windows.NewLazySystemDLL("GameInput.dll") procGameInputCreate = gameInput.NewProc("GameInputCreate") ) 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 _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 ( _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 ) type _GameInputGamepadState struct { buttons _GameInputGamepadButtons leftTrigger float32 rightTrigger float32 leftThumbstickX float32 leftThumbstickY float32 rightThumbstickX float32 rightThumbstickY float32 } type _GameInputRumbleParams struct { lowFrequency float32 highFrequency float32 leftTrigger float32 rightTrigger float32 } func _GameInputCreate() (*_IGameInput, error) { var gameInput *_IGameInput r, _, _ := procGameInputCreate.Call(uintptr(unsafe.Pointer(&gameInput))) if uint32(r) != uint32(windows.S_OK) { return nil, fmt.Errorf("gamepad: GameInputCreate failed: %w", handleError(windows.Handle(uint32(r)))) } return gameInput, nil } type _IGameInput struct { vtbl *_IGameInput_Vtbl } 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) 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: %w", handleError(windows.Handle(uint32(r)))) } return reading, nil } 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: %w", handleError(windows.Handle(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 } func (i *_IGameInputDevice) SetRumbleState(params *_GameInputRumbleParams, timestamp uint64) { _, _, _ = syscall.Syscall(i.vtbl.SetRumbleState, 3, uintptr(unsafe.Pointer(i)), uintptr(unsafe.Pointer(params)), uintptr(timestamp)) runtime.KeepAlive(params) } 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 } func (i *_IGameInputReading) Release() uint32 { r, _, _ := syscall.Syscall(i.vtbl.Release, 1, uintptr(unsafe.Pointer(i)), 0, 0) return uint32(r) }