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);
256 if (device_cat.is_debug()) {
257 device_cat.debug() <<
"Initializing XInput library.\n";
261 const char *dll_name =
"Xinput1_4.dll";
262 HMODULE module = LoadLibraryA(dll_name);
266 if (device_cat.is_debug()) {
268 <<
"Xinput1_4.dll not found, falling back to Xinput1_3.dll\n";
271 dll_name =
"Xinput1_3.dll";
272 module = LoadLibraryA(dll_name);
276 if (device_cat.is_debug()) {
278 <<
"Successfully loaded " << dll_name <<
"\n";
283 get_state = (pXInputGetState)GetProcAddress(module, MAKEINTRESOURCE(100));
284 if (get_state ==
nullptr) {
285 get_state = (pXInputGetState)GetProcAddress(module,
"XInputGetState");
286 if (get_state ==
nullptr) {
288 <<
"Failed to find function XInputGetState in " << dll_name <<
".\n";
293 set_state = (pXInputSetState)GetProcAddress(module,
"XInputSetState");
294 if (set_state ==
nullptr) {
296 <<
"Failed to find function XInputSetState in " << dll_name <<
".\n";
300 get_capabilities = (pXInputGetCapabilities)GetProcAddress(module,
"XInputGetCapabilities");
301 if (get_capabilities ==
nullptr) {
303 <<
"Failed to find function XInputGetCapabilities in " << dll_name <<
".\n";
307 get_battery_information = (pXInputGetBatteryInformation)GetProcAddress(module,
"XInputGetBatteryInformation");
308 get_base_bus_information = (pXInputGetBaseBusInformation)GetProcAddress(module, MAKEINTRESOURCE(104));
309 get_capabilities_ex = (pXInputGetCapabilitiesEx)GetProcAddress(module, MAKEINTRESOURCE(108));
314 <<
"Failed to load Xinput1_4.dll or Xinput1_3.dll.\n";
323 init_device(
const XINPUT_CAPABILITIES_EX &caps,
const XINPUT_STATE &state) {
324 nassertv(_initialized);
331 switch (caps.SubType) {
333 case XINPUT_DEVSUBTYPE_GAMEPAD:
334 _device_class = DeviceClass::gamepad;
335 _axes[0].axis = Axis::left_trigger;
336 _axes[1].axis = Axis::right_trigger;
337 _axes[2].axis = Axis::left_x;
338 _axes[3].axis = Axis::left_y;
339 _axes[4].axis = Axis::right_x;
340 _axes[5].axis = Axis::right_y;
343 case XINPUT_DEVSUBTYPE_WHEEL:
344 _device_class = DeviceClass::steering_wheel;
345 _axes[0].axis = Axis::brake;
346 _axes[1].axis = Axis::accelerator;
347 _axes[2].axis = Axis::wheel;
348 _axes[3].axis = Axis::none;
349 _axes[4].axis = Axis::none;
350 _axes[5].axis = Axis::none;
353 case XINPUT_DEVSUBTYPE_FLIGHT_STICK:
354 _device_class = DeviceClass::flight_stick;
355 _axes[0].axis = Axis::yaw;
356 _axes[1].axis = Axis::throttle;
357 _axes[2].axis = Axis::roll;
358 _axes[3].axis = Axis::pitch;
359 _axes[4].axis = Axis::none;
360 _axes[5].axis = Axis::none;
363 case XINPUT_DEVSUBTYPE_DANCE_PAD:
364 _device_class = DeviceClass::dance_pad;
365 _axes[0].axis = Axis::none;
366 _axes[1].axis = Axis::none;
367 _axes[2].axis = Axis::none;
368 _axes[3].axis = Axis::none;
369 _axes[4].axis = Axis::none;
370 _axes[5].axis = Axis::none;
374 _axes[0]._scale = 1.0 / 255.0;
375 _axes[1]._scale = 1.0 / 255.0;
376 _axes[2]._scale = 1.0 / 32767.5;
377 _axes[3]._scale = 1.0 / 32767.5;
378 _axes[4]._scale = 1.0 / 32767.5;
379 _axes[5]._scale = 1.0 / 32767.5;
381 _axes[2]._bias = 0.5 / 32767.5;
382 _axes[3]._bias = 0.5 / 32767.5;
383 _axes[4]._bias = 0.5 / 32767.5;
384 _axes[5]._bias = 0.5 / 32767.5;
386 if (caps.Flags & XINPUT_CAPS_NO_NAVIGATION) {
387 _buttons[0].handle = ButtonHandle::none();
388 _buttons[1].handle = ButtonHandle::none();
389 _buttons[2].handle = ButtonHandle::none();
390 _buttons[3].handle = ButtonHandle::none();
391 _buttons[4].handle = ButtonHandle::none();
392 _buttons[5].handle = ButtonHandle::none();
394 _buttons[0].handle = GamepadButton::dpad_up();
395 _buttons[1].handle = GamepadButton::dpad_down();
396 _buttons[2].handle = GamepadButton::dpad_left();
397 _buttons[3].handle = GamepadButton::dpad_right();
398 _buttons[4].handle = GamepadButton::start();
399 _buttons[5].handle = GamepadButton::back();
401 _buttons[6].handle = GamepadButton::lstick();
402 _buttons[7].handle = GamepadButton::rstick();
403 _buttons[8].handle = GamepadButton::lshoulder();
404 _buttons[9].handle = GamepadButton::rshoulder();
405 _buttons[10].handle = GamepadButton::guide();
406 _buttons[11].handle = GamepadButton::face_a();
407 _buttons[12].handle = GamepadButton::face_b();
408 _buttons[13].handle = GamepadButton::face_x();
409 _buttons[14].handle = GamepadButton::face_y();
411 if (caps.Vibration.wLeftMotorSpeed != 0 ||
412 caps.Vibration.wRightMotorSpeed != 0) {
413 enable_feature(Feature::vibration);
416 if (get_battery_information !=
nullptr) {
417 XINPUT_BATTERY_INFORMATION batt;
418 if (get_battery_information(_index, BATTERY_DEVTYPE_GAMEPAD, &batt) == ERROR_SUCCESS) {
419 if (batt.BatteryType != BATTERY_TYPE_DISCONNECTED &&
420 batt.BatteryType != BATTERY_TYPE_WIRED) {
422 enable_feature(Feature::battery);
423 _battery_data.level = batt.BatteryLevel;
424 _battery_data.max_level = BATTERY_LEVEL_FULL;
429 WORD buttons = state.Gamepad.wButtons;
431 for (
int i = 0; i < 16; ++i) {
433 _buttons[i]._state = (buttons & mask) ? S_down : S_up;
441 axis_changed(0, state.Gamepad.bLeftTrigger);
442 axis_changed(1, state.Gamepad.bRightTrigger);
443 axis_changed(2, state.Gamepad.sThumbLX);
444 axis_changed(3, state.Gamepad.sThumbLY);
445 axis_changed(4, state.Gamepad.sThumbRX);
446 axis_changed(5, state.Gamepad.sThumbRY);
448 _last_buttons = buttons;
449 _last_packet = state.dwPacketNumber;
458 do_set_vibration(
double strong,
double weak) {
459 nassertv_always(_is_connected);
461 XINPUT_VIBRATION vibration;
462 vibration.wLeftMotorSpeed = strong * 0xffff;
463 vibration.wRightMotorSpeed = weak * 0xffff;
464 set_state(_index, &vibration);
475 if (!_is_connected) {
481 if (get_state(_index, &state) != ERROR_SUCCESS) {
484 _is_connected =
false;
491 if (state.dwPacketNumber == _last_packet) {
497 WORD changed_buttons = _last_buttons ^ state.Gamepad.wButtons;
500 for (
int i = 0; i < 16; ++i) {
501 if (changed_buttons & mask) {
502 button_changed(i, (state.Gamepad.wButtons & mask) != 0);
511 axis_changed(0, state.Gamepad.bLeftTrigger);
512 axis_changed(1, state.Gamepad.bRightTrigger);
513 axis_changed(2, state.Gamepad.sThumbLX);
514 axis_changed(3, state.Gamepad.sThumbLY);
515 axis_changed(4, state.Gamepad.sThumbRX);
516 axis_changed(5, state.Gamepad.sThumbRY);
518 _last_buttons = state.Gamepad.wButtons;
519 _last_packet = state.dwPacketNumber;
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Similar to MutexHolder, but for a light mutex.