OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/gamepad/gamepad_platform_data_fetcher_win.h" | 5 #include "content/browser/gamepad/gamepad_platform_data_fetcher_win.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 #include <string.h> | 8 #include <string.h> |
9 | 9 |
10 #include "base/strings/stringprintf.h" | 10 #include "base/strings/stringprintf.h" |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
49 case kDeviceSubTypeDrumKit: return L"DRUM_KIT"; | 49 case kDeviceSubTypeDrumKit: return L"DRUM_KIT"; |
50 case kDeviceSubTypeGuitarBass: return L"GUITAR_BASS"; | 50 case kDeviceSubTypeGuitarBass: return L"GUITAR_BASS"; |
51 case kDeviceSubTypeArcadePad: return L"ARCADE_PAD"; | 51 case kDeviceSubTypeArcadePad: return L"ARCADE_PAD"; |
52 default: return L"<UNKNOWN>"; | 52 default: return L"<UNKNOWN>"; |
53 } | 53 } |
54 } | 54 } |
55 | 55 |
56 } // namespace | 56 } // namespace |
57 | 57 |
58 GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin() | 58 GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin() |
59 : xinput_available_(false) { | 59 : xinput_dll_(base::FilePath(FILE_PATH_LITERAL("xinput1_3.dll"))), |
60 for (size_t i = 0; i < XUSER_MAX_COUNT; ++i) | 60 xinput_available_(GetXInputDllFunctions()) { |
61 xinput_connected_[i] = false; | 61 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
| 62 platform_pad_state_[i].status = DISCONNECTED; |
| 63 pad_state_[i].mapper = NULL; |
| 64 pad_state_[i].axis_mask = 0; |
| 65 pad_state_[i].button_mask = 0; |
| 66 } |
| 67 |
| 68 raw_input_fetcher_.reset(new RawInputDataFetcher()); |
| 69 raw_input_fetcher_->StartMonitor(); |
62 } | 70 } |
63 | 71 |
64 GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() { | 72 GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() { |
| 73 raw_input_fetcher_->StopMonitor(); |
65 } | 74 } |
66 | 75 |
67 void GamepadPlatformDataFetcherWin::OnAddedToProvider() { | 76 int GamepadPlatformDataFetcherWin::FirstAvailableGamepadId() const { |
68 xinput_dll_.Reset(base::LoadNativeLibrary( | 77 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
69 base::FilePath(FILE_PATH_LITERAL("xinput1_3.dll")), nullptr)); | 78 if (platform_pad_state_[i].status == DISCONNECTED) |
70 xinput_available_ = GetXInputDllFunctions(); | 79 return i; |
| 80 } |
| 81 return -1; |
| 82 } |
| 83 |
| 84 bool GamepadPlatformDataFetcherWin::HasXInputGamepad(int index) const { |
| 85 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
| 86 if (platform_pad_state_[i].status == XINPUT_CONNECTED && |
| 87 platform_pad_state_[i].xinput_index == index) |
| 88 return true; |
| 89 } |
| 90 return false; |
| 91 } |
| 92 |
| 93 bool GamepadPlatformDataFetcherWin::HasRawInputGamepad( |
| 94 const HANDLE handle) const { |
| 95 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
| 96 if (platform_pad_state_[i].status == RAWINPUT_CONNECTED && |
| 97 platform_pad_state_[i].raw_input_handle == handle) |
| 98 return true; |
| 99 } |
| 100 return false; |
71 } | 101 } |
72 | 102 |
73 void GamepadPlatformDataFetcherWin::EnumerateDevices() { | 103 void GamepadPlatformDataFetcherWin::EnumerateDevices() { |
74 TRACE_EVENT0("GAMEPAD", "EnumerateDevices"); | 104 TRACE_EVENT0("GAMEPAD", "EnumerateDevices"); |
75 | 105 |
76 if (xinput_available_) { | 106 // Mark all disconnected pads DISCONNECTED. |
77 for (size_t i = 0; i < XUSER_MAX_COUNT; ++i) { | 107 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
78 // Check to see if the xinput device is connected | 108 if (!pad_state_[i].data.connected) |
79 XINPUT_CAPABILITIES caps; | 109 platform_pad_state_[i].status = DISCONNECTED; |
80 DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps); | 110 } |
81 xinput_connected_[i] = (res == ERROR_SUCCESS); | 111 |
82 if (!xinput_connected_[i]) | 112 for (size_t i = 0; i < XUSER_MAX_COUNT; ++i) { |
| 113 if (HasXInputGamepad(i)) |
| 114 continue; |
| 115 int pad_index = FirstAvailableGamepadId(); |
| 116 if (pad_index == -1) |
| 117 return; // We can't add any more gamepads. |
| 118 WebGamepad& pad = pad_state_[pad_index].data; |
| 119 if (xinput_available_ && GetXInputPadConnectivity(i, &pad)) { |
| 120 platform_pad_state_[pad_index].status = XINPUT_CONNECTED; |
| 121 platform_pad_state_[pad_index].xinput_index = i; |
| 122 pad_state_[pad_index].mapper = NULL; |
| 123 pad_state_[pad_index].axis_mask = 0; |
| 124 pad_state_[pad_index].button_mask = 0; |
| 125 } |
| 126 } |
| 127 |
| 128 if (raw_input_fetcher_->Available()) { |
| 129 std::vector<RawGamepadInfo*> raw_inputs = |
| 130 raw_input_fetcher_->EnumerateDevices(); |
| 131 for (size_t i = 0; i < raw_inputs.size(); ++i) { |
| 132 RawGamepadInfo* gamepad = raw_inputs[i]; |
| 133 if (gamepad->buttons_length == 0 && gamepad->axes_length == 0) |
83 continue; | 134 continue; |
| 135 if (HasRawInputGamepad(gamepad->handle)) |
| 136 continue; |
| 137 int pad_index = FirstAvailableGamepadId(); |
| 138 if (pad_index == -1) |
| 139 return; |
| 140 WebGamepad& pad = pad_state_[pad_index].data; |
| 141 pad.connected = true; |
| 142 PadState& state = pad_state_[pad_index]; |
| 143 PlatformPadState& platform_state = platform_pad_state_[pad_index]; |
| 144 platform_state.status = RAWINPUT_CONNECTED; |
| 145 platform_state.raw_input_handle = gamepad->handle; |
84 | 146 |
85 PadState* state = provider()->GetPadState(GAMEPAD_SOURCE_WIN_XINPUT, i); | 147 std::string vendor = base::StringPrintf("%04x", gamepad->vendor_id); |
86 if (!state) | 148 std::string product = base::StringPrintf("%04x", gamepad->product_id); |
87 continue; // No slot available for this gamepad. | 149 state.mapper = GetGamepadStandardMappingFunction(vendor, product); |
| 150 state.axis_mask = 0; |
| 151 state.button_mask = 0; |
88 | 152 |
89 WebGamepad& pad = state->data; | 153 swprintf(pad.id, WebGamepad::idLengthCap, |
| 154 L"%ls (%lsVendor: %04x Product: %04x)", |
| 155 gamepad->id, state.mapper ? L"STANDARD GAMEPAD " : L"", |
| 156 gamepad->vendor_id, gamepad->product_id); |
90 | 157 |
91 if (state->active_state == GAMEPAD_NEWLY_ACTIVE) { | 158 if (state.mapper) |
92 // This is the first time we've seen this device, so do some one-time | |
93 // initialization | |
94 pad.connected = true; | |
95 swprintf(pad.id, | |
96 WebGamepad::idLengthCap, | |
97 L"Xbox 360 Controller (XInput STANDARD %ls)", | |
98 GamepadSubTypeName(caps.SubType)); | |
99 swprintf(pad.mapping, WebGamepad::mappingLengthCap, L"standard"); | 159 swprintf(pad.mapping, WebGamepad::mappingLengthCap, L"standard"); |
100 } | 160 else |
| 161 pad.mapping[0] = 0; |
101 } | 162 } |
102 } | 163 } |
103 } | 164 } |
104 | 165 |
105 void GamepadPlatformDataFetcherWin::GetGamepadData(bool devices_changed_hint) { | 166 void GamepadPlatformDataFetcherWin::GetGamepadData(WebGamepads* pads, |
| 167 bool devices_changed_hint) { |
106 TRACE_EVENT0("GAMEPAD", "GetGamepadData"); | 168 TRACE_EVENT0("GAMEPAD", "GetGamepadData"); |
107 | 169 |
108 if (!xinput_available_) | 170 if (!xinput_available_ && |
| 171 !raw_input_fetcher_->Available()) { |
| 172 pads->length = 0; |
109 return; | 173 return; |
| 174 } |
110 | 175 |
111 // A note on XInput devices: | 176 // A note on XInput devices: |
112 // If we got notification that system devices have been updated, then | 177 // If we got notification that system devices have been updated, then |
113 // run GetCapabilities to update the connected status and the device | 178 // run GetCapabilities to update the connected status and the device |
114 // identifier. It can be slow to do to both GetCapabilities and | 179 // identifier. It can be slow to do to both GetCapabilities and |
115 // GetState on unconnected devices, so we want to avoid a 2-5ms pause | 180 // GetState on unconnected devices, so we want to avoid a 2-5ms pause |
116 // here by only doing this when the devices are updated (despite | 181 // here by only doing this when the devices are updated (despite |
117 // documentation claiming it's OK to call it any time). | 182 // documentation claiming it's OK to call it any time). |
118 if (devices_changed_hint) | 183 if (devices_changed_hint) |
119 EnumerateDevices(); | 184 EnumerateDevices(); |
120 | 185 |
121 for (size_t i = 0; i < XUSER_MAX_COUNT; ++i) { | 186 pads->length = 0; |
122 if (xinput_connected_[i]) | 187 |
123 GetXInputPadData(i); | 188 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
| 189 // We rely on device_changed and GetCapabilities to tell us that |
| 190 // something's been connected, but we will mark as disconnected if |
| 191 // Get___PadState returns that we've lost the pad. |
| 192 if (!pad_state_[i].data.connected) |
| 193 continue; |
| 194 |
| 195 if (platform_pad_state_[i].status == XINPUT_CONNECTED) |
| 196 GetXInputPadData(i, &pad_state_[i].data); |
| 197 else if (platform_pad_state_[i].status == RAWINPUT_CONNECTED) |
| 198 GetRawInputPadData(i, &pad_state_[i].data); |
| 199 |
| 200 MapAndSanitizeGamepadData(&pad_state_[i], &pads->items[i]); |
| 201 |
| 202 if (pads->items[i].connected) |
| 203 pads->length++; |
124 } | 204 } |
125 } | 205 } |
126 | 206 |
127 void GamepadPlatformDataFetcherWin::GetXInputPadData(int i) { | 207 void GamepadPlatformDataFetcherWin::PauseHint(bool pause) { |
128 PadState* pad_state = provider()->GetPadState(GAMEPAD_SOURCE_WIN_XINPUT, i); | 208 if (pause) |
129 if (!pad_state) | 209 raw_input_fetcher_->StopMonitor(); |
130 return; | 210 else |
| 211 raw_input_fetcher_->StartMonitor(); |
| 212 } |
131 | 213 |
132 WebGamepad& pad = pad_state->data; | 214 bool GamepadPlatformDataFetcherWin::GetXInputPadConnectivity( |
| 215 int i, |
| 216 WebGamepad* pad) const { |
| 217 DCHECK(pad); |
| 218 TRACE_EVENT1("GAMEPAD", "GetXInputPadConnectivity", "id", i); |
| 219 XINPUT_CAPABILITIES caps; |
| 220 DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps); |
| 221 if (res == ERROR_DEVICE_NOT_CONNECTED) { |
| 222 pad->connected = false; |
| 223 return false; |
| 224 } else { |
| 225 pad->connected = true; |
| 226 swprintf(pad->id, |
| 227 WebGamepad::idLengthCap, |
| 228 L"Xbox 360 Controller (XInput STANDARD %ls)", |
| 229 GamepadSubTypeName(caps.SubType)); |
| 230 swprintf(pad->mapping, WebGamepad::mappingLengthCap, L"standard"); |
| 231 return true; |
| 232 } |
| 233 } |
133 | 234 |
| 235 void GamepadPlatformDataFetcherWin::GetXInputPadData( |
| 236 int i, |
| 237 WebGamepad* pad) { |
134 XINPUT_STATE state; | 238 XINPUT_STATE state; |
135 memset(&state, 0, sizeof(XINPUT_STATE)); | 239 memset(&state, 0, sizeof(XINPUT_STATE)); |
136 TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i); | 240 TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i); |
137 DWORD dwResult = xinput_get_state_(i, &state); | 241 DWORD dwResult = xinput_get_state_(platform_pad_state_[i].xinput_index, |
| 242 &state); |
138 TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i); | 243 TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i); |
139 | 244 |
140 if (dwResult == ERROR_SUCCESS) { | 245 if (dwResult == ERROR_SUCCESS) { |
141 pad.timestamp = state.dwPacketNumber; | 246 pad->timestamp = state.dwPacketNumber; |
142 pad.buttonsLength = 0; | 247 pad->buttonsLength = 0; |
143 WORD val = state.Gamepad.wButtons; | 248 WORD val = state.Gamepad.wButtons; |
144 #define ADD(b) pad.buttons[pad.buttonsLength].pressed = (val & (b)) != 0; \ | 249 #define ADD(b) pad->buttons[pad->buttonsLength].pressed = (val & (b)) != 0; \ |
145 pad.buttons[pad.buttonsLength++].value = ((val & (b)) ? 1.f : 0.f); | 250 pad->buttons[pad->buttonsLength++].value = ((val & (b)) ? 1.f : 0.f); |
146 ADD(XINPUT_GAMEPAD_A); | 251 ADD(XINPUT_GAMEPAD_A); |
147 ADD(XINPUT_GAMEPAD_B); | 252 ADD(XINPUT_GAMEPAD_B); |
148 ADD(XINPUT_GAMEPAD_X); | 253 ADD(XINPUT_GAMEPAD_X); |
149 ADD(XINPUT_GAMEPAD_Y); | 254 ADD(XINPUT_GAMEPAD_Y); |
150 ADD(XINPUT_GAMEPAD_LEFT_SHOULDER); | 255 ADD(XINPUT_GAMEPAD_LEFT_SHOULDER); |
151 ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER); | 256 ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER); |
152 | 257 |
153 pad.buttons[pad.buttonsLength].pressed = | 258 pad->buttons[pad->buttonsLength].pressed = |
154 state.Gamepad.bLeftTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD; | 259 state.Gamepad.bLeftTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD; |
155 pad.buttons[pad.buttonsLength++].value = | 260 pad->buttons[pad->buttonsLength++].value = |
156 state.Gamepad.bLeftTrigger / 255.f; | 261 state.Gamepad.bLeftTrigger / 255.f; |
157 | 262 |
158 pad.buttons[pad.buttonsLength].pressed = | 263 pad->buttons[pad->buttonsLength].pressed = |
159 state.Gamepad.bRightTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD; | 264 state.Gamepad.bRightTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD; |
160 pad.buttons[pad.buttonsLength++].value = | 265 pad->buttons[pad->buttonsLength++].value = |
161 state.Gamepad.bRightTrigger / 255.f; | 266 state.Gamepad.bRightTrigger / 255.f; |
162 | 267 |
163 ADD(XINPUT_GAMEPAD_BACK); | 268 ADD(XINPUT_GAMEPAD_BACK); |
164 ADD(XINPUT_GAMEPAD_START); | 269 ADD(XINPUT_GAMEPAD_START); |
165 ADD(XINPUT_GAMEPAD_LEFT_THUMB); | 270 ADD(XINPUT_GAMEPAD_LEFT_THUMB); |
166 ADD(XINPUT_GAMEPAD_RIGHT_THUMB); | 271 ADD(XINPUT_GAMEPAD_RIGHT_THUMB); |
167 ADD(XINPUT_GAMEPAD_DPAD_UP); | 272 ADD(XINPUT_GAMEPAD_DPAD_UP); |
168 ADD(XINPUT_GAMEPAD_DPAD_DOWN); | 273 ADD(XINPUT_GAMEPAD_DPAD_DOWN); |
169 ADD(XINPUT_GAMEPAD_DPAD_LEFT); | 274 ADD(XINPUT_GAMEPAD_DPAD_LEFT); |
170 ADD(XINPUT_GAMEPAD_DPAD_RIGHT); | 275 ADD(XINPUT_GAMEPAD_DPAD_RIGHT); |
171 #undef ADD | 276 #undef ADD |
172 pad.axesLength = 0; | 277 pad->axesLength = 0; |
173 | 278 |
174 float value = 0.0; | 279 float value = 0.0; |
175 #define ADD(a, factor) value = factor * NormalizeXInputAxis(a); \ | 280 #define ADD(a, factor) value = factor * NormalizeXInputAxis(a); \ |
176 pad.axes[pad.axesLength++] = value; | 281 pad->axes[pad->axesLength++] = value; |
177 | 282 |
178 // XInput are +up/+right, -down/-left, we want -up/-left. | 283 // XInput are +up/+right, -down/-left, we want -up/-left. |
179 ADD(state.Gamepad.sThumbLX, 1); | 284 ADD(state.Gamepad.sThumbLX, 1); |
180 ADD(state.Gamepad.sThumbLY, -1); | 285 ADD(state.Gamepad.sThumbLY, -1); |
181 ADD(state.Gamepad.sThumbRX, 1); | 286 ADD(state.Gamepad.sThumbRX, 1); |
182 ADD(state.Gamepad.sThumbRY, -1); | 287 ADD(state.Gamepad.sThumbRY, -1); |
183 #undef ADD | 288 #undef ADD |
| 289 } else { |
| 290 pad->connected = false; |
184 } | 291 } |
185 } | 292 } |
186 | 293 |
| 294 void GamepadPlatformDataFetcherWin::GetRawInputPadData( |
| 295 int index, |
| 296 WebGamepad* pad) { |
| 297 RawGamepadInfo* gamepad = raw_input_fetcher_->GetGamepadInfo( |
| 298 platform_pad_state_[index].raw_input_handle); |
| 299 if (!gamepad) { |
| 300 pad->connected = false; |
| 301 return; |
| 302 } |
| 303 |
| 304 pad->timestamp = gamepad->report_id; |
| 305 pad->buttonsLength = gamepad->buttons_length; |
| 306 pad->axesLength = gamepad->axes_length; |
| 307 |
| 308 for (unsigned int i = 0; i < pad->buttonsLength; i++) { |
| 309 pad->buttons[i].pressed = gamepad->buttons[i]; |
| 310 pad->buttons[i].value = gamepad->buttons[i] ? 1.0 : 0.0; |
| 311 } |
| 312 |
| 313 for (unsigned int i = 0; i < pad->axesLength; i++) |
| 314 pad->axes[i] = gamepad->axes[i].value; |
| 315 } |
| 316 |
187 bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() { | 317 bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() { |
188 xinput_get_capabilities_ = NULL; | 318 xinput_get_capabilities_ = NULL; |
189 xinput_get_state_ = NULL; | 319 xinput_get_state_ = NULL; |
190 xinput_enable_ = reinterpret_cast<XInputEnableFunc>( | 320 xinput_enable_ = reinterpret_cast<XInputEnableFunc>( |
191 xinput_dll_.GetFunctionPointer("XInputEnable")); | 321 xinput_dll_.GetFunctionPointer("XInputEnable")); |
192 if (!xinput_enable_) | 322 if (!xinput_enable_) |
193 return false; | 323 return false; |
194 xinput_get_capabilities_ = reinterpret_cast<XInputGetCapabilitiesFunc>( | 324 xinput_get_capabilities_ = reinterpret_cast<XInputGetCapabilitiesFunc>( |
195 xinput_dll_.GetFunctionPointer("XInputGetCapabilities")); | 325 xinput_dll_.GetFunctionPointer("XInputGetCapabilities")); |
196 if (!xinput_get_capabilities_) | 326 if (!xinput_get_capabilities_) |
197 return false; | 327 return false; |
198 xinput_get_state_ = reinterpret_cast<XInputGetStateFunc>( | 328 xinput_get_state_ = reinterpret_cast<XInputGetStateFunc>( |
199 xinput_dll_.GetFunctionPointer("XInputGetState")); | 329 xinput_dll_.GetFunctionPointer("XInputGetState")); |
200 if (!xinput_get_state_) | 330 if (!xinput_get_state_) |
201 return false; | 331 return false; |
202 xinput_enable_(true); | 332 xinput_enable_(true); |
203 return true; | 333 return true; |
204 } | 334 } |
205 | 335 |
206 } // namespace content | 336 } // namespace content |
OLD | NEW |