16 #if defined(_WIN32) && !defined(CPPPARSER) 25 #ifndef XUSER_MAX_COUNT 26 #define XUSER_MAX_COUNT 4 29 #ifndef XINPUT_CAPS_FFB_SUPPORTED 30 #define XINPUT_CAPS_FFB_SUPPORTED 0x0001 32 #ifndef XINPUT_CAPS_NO_NAVIGATION 33 #define XINPUT_CAPS_NO_NAVIGATION 0x0010 36 #ifndef BATTERY_DEVTYPE_GAMEPAD 37 #define BATTERY_DEVTYPE_GAMEPAD 0x00 40 #ifndef XINPUT_DEVSUBTYPE_WHEEL 41 #define XINPUT_DEVSUBTYPE_WHEEL 0x02 43 #ifndef XINPUT_DEVSUBTYPE_ARCADE_STICK 44 #define XINPUT_DEVSUBTYPE_ARCADE_STICK 0x03 46 #ifndef XINPUT_DEVSUBTYPE_FLIGHT_STICK 47 #define XINPUT_DEVSUBTYPE_FLIGHT_STICK 0x04 49 #ifndef XINPUT_DEVSUBTYPE_DANCE_PAD 50 #define XINPUT_DEVSUBTYPE_DANCE_PAD 0x05 52 #ifndef XINPUT_DEVSUBTYPE_GUITAR 53 #define XINPUT_DEVSUBTYPE_GUITAR 0x06 55 #ifndef XINPUT_DEVSUBTYPE_DRUM_KIT 56 #define XINPUT_DEVSUBTYPE_DRUM_KIT 0x08 59 #ifndef BATTERY_TYPE_DISCONNECTED 60 #define BATTERY_TYPE_DISCONNECTED 0x00 63 #ifndef BATTERY_TYPE_WIRED 64 #define BATTERY_TYPE_WIRED 0x01 67 #ifndef BATTERY_LEVEL_FULL 68 #define BATTERY_LEVEL_FULL 0x03 71 typedef struct _XINPUT_BATTERY_INFORMATION {
74 } XINPUT_BATTERY_INFORMATION;
77 typedef struct _XINPUT_BUSINFO {
87 typedef struct _XINPUT_CAPABILITIES_EX {
91 XINPUT_GAMEPAD Gamepad;
92 XINPUT_VIBRATION Vibration;
100 } XINPUT_CAPABILITIES_EX;
102 typedef DWORD (*pXInputGetState)(DWORD, XINPUT_STATE *);
103 typedef DWORD (*pXInputSetState)(DWORD, XINPUT_VIBRATION *);
104 typedef DWORD (*pXInputGetCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES *);
105 typedef DWORD (*pXInputGetCapabilitiesEx)(DWORD, DWORD, DWORD, XINPUT_CAPABILITIES_EX *);
106 typedef DWORD (*pXInputGetBatteryInformation)(DWORD, BYTE, XINPUT_BATTERY_INFORMATION *);
107 typedef DWORD (*pXInputGetBaseBusInformation)(DWORD, XINPUT_BUSINFO *);
109 static pXInputGetState get_state =
nullptr;
110 static pXInputSetState set_state =
nullptr;
111 static pXInputGetCapabilities get_capabilities =
nullptr;
112 static pXInputGetCapabilitiesEx get_capabilities_ex =
nullptr;
113 static pXInputGetBatteryInformation get_battery_information =
nullptr;
114 static pXInputGetBaseBusInformation get_base_bus_information =
nullptr;
116 bool XInputDevice::_initialized =
false;
122 XInputDevice(DWORD user_index) :
127 nassertv(user_index >= 0 && user_index < XUSER_MAX_COUNT);
138 do_set_vibration(0, 0);
146 check_arrival(
const RID_DEVICE_INFO &info, DEVINST inst,
147 const std::string &name,
const std::string &manufacturer) {
154 nassertr_always(init_xinput(),
false);
157 XINPUT_CAPABILITIES_EX caps = {0};
159 if ((get_capabilities_ex && get_capabilities_ex(1, _index, 0, &caps) != ERROR_SUCCESS) &&
160 get_capabilities(_index, 0, (XINPUT_CAPABILITIES *)&caps) != ERROR_SUCCESS) {
164 if (get_state(_index, &state) != ERROR_SUCCESS) {
169 if ((caps.VendorID != 0 && caps.VendorID != info.hid.dwVendorId) ||
170 (caps.ProductID != 0 && caps.ProductID != info.hid.dwProductId)) {
178 _name =
"XInput Device #";
179 _name += format_string(_index + 1);
181 _manufacturer = manufacturer;
183 if (inst && caps.ProductID == 0 && caps.RevisionID != 0) {
187 ULONG buflen =
sizeof(buffer);
188 if (CM_Get_DevNode_Registry_Property(inst, CM_DRP_HARDWAREID, 0, buffer, &buflen, 0) == CR_SUCCESS) {
189 std::string ids(buffer, buflen);
191 sprintf(revstr,
"REV_%04x", caps.RevisionID);
192 if (ids.find(revstr) == std::string::npos) {
198 _is_connected =
true;
199 init_device(caps, state);
200 _vendor_id = info.hid.dwVendorId;
201 _product_id = info.hid.dwProductId;
213 nassertv_always(init_xinput());
216 bool connected =
false;
218 XINPUT_CAPABILITIES_EX caps = {0};
220 if (((get_capabilities_ex && get_capabilities_ex(1, _index, 0, &caps) == ERROR_SUCCESS) ||
221 get_capabilities(_index, 0, (XINPUT_CAPABILITIES *)&caps) == ERROR_SUCCESS) &&
222 get_state(_index, &state) == ERROR_SUCCESS) {
229 if (connected == _is_connected) {
233 _is_connected = connected;
236 _name =
"XInput Device #";
237 _name += format_string(_index + 1);
238 _vendor_id = caps.VendorID;
239 _product_id = caps.ProductID;
240 init_device(caps, state);
252 if (device_cat.is_debug()) {
253 device_cat.debug() <<
"Initializing XInput library.\n";
257 const char *dll_name =
"Xinput1_4.dll";
258 HMODULE module = LoadLibraryA(dll_name);
262 if (device_cat.is_debug()) {
264 <<
"Xinput1_4.dll not found, falling back to Xinput1_3.dll\n";
267 dll_name =
"Xinput1_3.dll";
268 module = LoadLibraryA(dll_name);
272 if (device_cat.is_debug()) {
274 <<
"Successfully loaded " << dll_name <<
"\n";
279 get_state = (pXInputGetState)GetProcAddress(module, MAKEINTRESOURCE(100));
280 if (get_state ==
nullptr) {
281 get_state = (pXInputGetState)GetProcAddress(module,
"XInputGetState");
282 if (get_state ==
nullptr) {
284 <<
"Failed to find function XInputGetState in " << dll_name <<
".\n";
289 set_state = (pXInputSetState)GetProcAddress(module,
"XInputSetState");
290 if (set_state ==
nullptr) {
292 <<
"Failed to find function XInputSetState in " << dll_name <<
".\n";
296 get_capabilities = (pXInputGetCapabilities)GetProcAddress(module,
"XInputGetCapabilities");
297 if (get_capabilities ==
nullptr) {
299 <<
"Failed to find function XInputGetCapabilities in " << dll_name <<
".\n";
303 get_battery_information = (pXInputGetBatteryInformation)GetProcAddress(module,
"XInputGetBatteryInformation");
304 get_base_bus_information = (pXInputGetBaseBusInformation)GetProcAddress(module, MAKEINTRESOURCE(104));
305 get_capabilities_ex = (pXInputGetCapabilitiesEx)GetProcAddress(module, MAKEINTRESOURCE(108));
310 <<
"Failed to load Xinput1_4.dll or Xinput1_3.dll.\n";
319 init_device(
const XINPUT_CAPABILITIES_EX &caps,
const XINPUT_STATE &state) {
320 nassertv(_initialized);
327 switch (caps.SubType) {
329 case XINPUT_DEVSUBTYPE_GAMEPAD:
330 _device_class = DeviceClass::gamepad;
331 _axes[0].axis = Axis::left_trigger;
332 _axes[1].axis = Axis::right_trigger;
333 _axes[2].axis = Axis::left_x;
334 _axes[3].axis = Axis::left_y;
335 _axes[4].axis = Axis::right_x;
336 _axes[5].axis = Axis::right_y;
339 case XINPUT_DEVSUBTYPE_WHEEL:
340 _device_class = DeviceClass::steering_wheel;
341 _axes[0].axis = Axis::brake;
342 _axes[1].axis = Axis::accelerator;
343 _axes[2].axis = Axis::wheel;
344 _axes[3].axis = Axis::none;
345 _axes[4].axis = Axis::none;
346 _axes[5].axis = Axis::none;
349 case XINPUT_DEVSUBTYPE_FLIGHT_STICK:
350 _device_class = DeviceClass::flight_stick;
351 _axes[0].axis = Axis::yaw;
352 _axes[1].axis = Axis::throttle;
353 _axes[2].axis = Axis::roll;
354 _axes[3].axis = Axis::pitch;
355 _axes[4].axis = Axis::none;
356 _axes[5].axis = Axis::none;
359 case XINPUT_DEVSUBTYPE_DANCE_PAD:
360 _device_class = DeviceClass::dance_pad;
361 _axes[0].axis = Axis::none;
362 _axes[1].axis = Axis::none;
363 _axes[2].axis = Axis::none;
364 _axes[3].axis = Axis::none;
365 _axes[4].axis = Axis::none;
366 _axes[5].axis = Axis::none;
370 _axes[0]._scale = 1.0 / 255.0;
371 _axes[1]._scale = 1.0 / 255.0;
372 _axes[2]._scale = 1.0 / 32767.5;
373 _axes[3]._scale = 1.0 / 32767.5;
374 _axes[4]._scale = 1.0 / 32767.5;
375 _axes[5]._scale = 1.0 / 32767.5;
377 _axes[2]._bias = 0.5 / 32767.5;
378 _axes[3]._bias = 0.5 / 32767.5;
379 _axes[4]._bias = 0.5 / 32767.5;
380 _axes[5]._bias = 0.5 / 32767.5;
382 if (caps.Flags & XINPUT_CAPS_NO_NAVIGATION) {
383 _buttons[0].handle = ButtonHandle::none();
384 _buttons[1].handle = ButtonHandle::none();
385 _buttons[2].handle = ButtonHandle::none();
386 _buttons[3].handle = ButtonHandle::none();
387 _buttons[4].handle = ButtonHandle::none();
388 _buttons[5].handle = ButtonHandle::none();
390 _buttons[0].handle = GamepadButton::dpad_up();
391 _buttons[1].handle = GamepadButton::dpad_down();
392 _buttons[2].handle = GamepadButton::dpad_left();
393 _buttons[3].handle = GamepadButton::dpad_right();
394 _buttons[4].handle = GamepadButton::start();
395 _buttons[5].handle = GamepadButton::back();
397 _buttons[6].handle = GamepadButton::lstick();
398 _buttons[7].handle = GamepadButton::rstick();
399 _buttons[8].handle = GamepadButton::lshoulder();
400 _buttons[9].handle = GamepadButton::rshoulder();
401 _buttons[10].handle = GamepadButton::guide();
402 _buttons[11].handle = GamepadButton::face_a();
403 _buttons[12].handle = GamepadButton::face_b();
404 _buttons[13].handle = GamepadButton::face_x();
405 _buttons[14].handle = GamepadButton::face_y();
407 if (caps.Vibration.wLeftMotorSpeed != 0 ||
408 caps.Vibration.wRightMotorSpeed != 0) {
409 enable_feature(Feature::vibration);
412 if (get_battery_information !=
nullptr) {
413 XINPUT_BATTERY_INFORMATION batt;
414 if (get_battery_information(_index, BATTERY_DEVTYPE_GAMEPAD, &batt) == ERROR_SUCCESS) {
415 if (batt.BatteryType != BATTERY_TYPE_DISCONNECTED &&
416 batt.BatteryType != BATTERY_TYPE_WIRED) {
418 enable_feature(Feature::battery);
419 _battery_data.level = batt.BatteryLevel;
420 _battery_data.max_level = BATTERY_LEVEL_FULL;
425 WORD buttons = state.Gamepad.wButtons;
427 for (
int i = 0; i < 16; ++i) {
429 _buttons[i]._state = (buttons & mask) ? S_down : S_up;
437 axis_changed(0, state.Gamepad.bLeftTrigger);
438 axis_changed(1, state.Gamepad.bRightTrigger);
439 axis_changed(2, state.Gamepad.sThumbLX);
440 axis_changed(3, state.Gamepad.sThumbLY);
441 axis_changed(4, state.Gamepad.sThumbRX);
442 axis_changed(5, state.Gamepad.sThumbRY);
444 _last_buttons = buttons;
445 _last_packet = state.dwPacketNumber;
454 do_set_vibration(
double strong,
double weak) {
455 nassertv_always(_is_connected);
457 XINPUT_VIBRATION vibration;
458 vibration.wLeftMotorSpeed = strong * 0xffff;
459 vibration.wRightMotorSpeed = weak * 0xffff;
460 set_state(_index, &vibration);
471 if (!_is_connected) {
477 if (get_state(_index, &state) != ERROR_SUCCESS) {
480 _is_connected =
false;
487 if (state.dwPacketNumber == _last_packet) {
493 WORD changed_buttons = _last_buttons ^ state.Gamepad.wButtons;
496 for (
int i = 0; i < 16; ++i) {
497 if (changed_buttons & mask) {
498 button_changed(i, (state.Gamepad.wButtons & mask) != 0);
507 axis_changed(0, state.Gamepad.bLeftTrigger);
508 axis_changed(1, state.Gamepad.bRightTrigger);
509 axis_changed(2, state.Gamepad.sThumbLX);
510 axis_changed(3, state.Gamepad.sThumbLY);
511 axis_changed(4, state.Gamepad.sThumbRX);
512 axis_changed(5, state.Gamepad.sThumbRY);
514 _last_buttons = state.Gamepad.wButtons;
515 _last_packet = state.dwPacketNumber;
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Similar to MutexHolder, but for a light mutex.