Cogs.Core
WindowsGamepad.cpp
1#include "WindowsGamepad.h"
2
3#include "ViewContext.h"
4
5#include "Input/InputManager.h"
6#include "Input/GamepadMapping.h"
7
8#include "Services/Time.h"
9
10#define WIN32_LEAN_AND_MEAN
11#include <Windows.h>
12#include <xinput.h>
13
14#pragma comment(lib, "Xinput9_1_0.lib")
15
16void Cogs::Platform::GamepadHandler::initialize(Core::ViewContext* viewContext) {
17 view = viewContext;
18
19 mapping.fill(NoMapping);
20 checkedTime.fill(0);
21}
22
23void Cogs::Platform::GamepadHandler::update()
24{
25 Core::InputManager* inputManager = view->inputManager.get();
26 const double t = view->getContext()->time->getAnimationTime();
27
28 for (int i = 0; i < XUSER_MAX_COUNT; ++i) {
29 if (mapping[i] == Disconnected && (t - checkedTime[i]) < checkInterval) continue;
30
31 XINPUT_STATE xState = {};
32 auto result = XInputGetState(DWORD(i), &xState);
33
34 if (result != ERROR_SUCCESS) {
35 if (mapping[i] == Connected) {
36 auto & state = inputManager->getDevice((Core::InputDevices::EValues)(Core::InputDevices::Gamepad0 + i));
37
38 state.isConnected = false;
39 }
40
41 mapping[i] = Disconnected;
42 checkedTime[i] = t + i * 0.1; // Slightly shift to displace over multiple frames.
43
44 continue;
45 }
46
47 auto & state = inputManager->getDevice((Core::InputDevices::EValues)(Core::InputDevices::Gamepad0 + i));
48
49 if (mapping[i] == NoMapping) {
50 if (state.isConnected) {
51 // Someone has already grabbed this. Warning/Error.
52 continue;
53 }
54
55 // Connected new controller.
56
57 state.axisMask = 0x000000000000003F;
58 state.buttonMask = 0x000000000000FFFF;
59
60 state.isConnected = true;
61
62 mapping[i] = Connected;
63 }
64
65 for (size_t ii = 0; ii < 16; ++ii) {
66 state.setButton(ii, (xState.Gamepad.wButtons & (1 << ii)) != 0);
67 }
68
69 state.setAxis(Core::Gamepad::TriggerLeft, (float)xState.Gamepad.bLeftTrigger / 255.0f);
70 state.setAxis(Core::Gamepad::TriggerRight, (float)xState.Gamepad.bRightTrigger / 255.0f);
71
72 //NOTE: Difference between positive/negative max values of signed shorts. We could use clamped normalization
73 //here like the graphics APIs.
74 const float positiveRange = 32768 - 1;
75 const float negativeRange = 32768;
76
77 state.setAxis(Core::Gamepad::StickLeftX, (float)xState.Gamepad.sThumbLX / (xState.Gamepad.sThumbLX > 0 ? positiveRange : negativeRange));
78 state.setAxis(Core::Gamepad::StickLeftY, (float)xState.Gamepad.sThumbLY / (xState.Gamepad.sThumbLY > 0 ? positiveRange : negativeRange));
79 state.setAxis(Core::Gamepad::StickRightX, (float)xState.Gamepad.sThumbRX / (xState.Gamepad.sThumbRX > 0 ? positiveRange : negativeRange));
80 state.setAxis(Core::Gamepad::StickRightY, (float)xState.Gamepad.sThumbRY / (xState.Gamepad.sThumbRY > 0 ? positiveRange : negativeRange));
81
82 state.isValid = true;
83 }
84}