dinput/tests: Add some windows.gaming.input device tests.
[wine.git] / dlls / dinput / joystick_hid.c
blob80991e8c150ada0ea093493aca924e81ef5edba0
1 /* DirectInput HID Joystick device
3 * Copyright 2021 RĂ©mi Bernon for CodeWeavers
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #include <assert.h>
21 #include <stdarg.h>
22 #include <string.h>
23 #include <math.h>
25 #include "ntstatus.h"
26 #define WIN32_NO_STATUS
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winternl.h"
30 #include "winuser.h"
31 #include "winerror.h"
32 #include "winreg.h"
34 #include "ddk/hidsdi.h"
35 #include "setupapi.h"
36 #include "devguid.h"
37 #include "dinput.h"
38 #include "setupapi.h"
40 #include "dinput_private.h"
41 #include "device_private.h"
43 #include "initguid.h"
44 #include "devpkey.h"
46 #include "wine/debug.h"
47 #include "wine/hid.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
51 DEFINE_GUID( GUID_DEVINTERFACE_WINEXINPUT,0x6c53d5fd,0x6480,0x440f,0xb6,0x18,0x47,0x67,0x50,0xc5,0xe1,0xa6 );
52 DEFINE_GUID( hid_joystick_guid, 0x9e573edb, 0x7734, 0x11d2, 0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7 );
53 DEFINE_DEVPROPKEY( DEVPROPKEY_HID_HANDLE, 0xbc62e415, 0xf4fe, 0x405c, 0x8e, 0xda, 0x63, 0x6f, 0xb5, 0x9f, 0x08, 0x98, 2 );
55 struct pid_control_report
57 BYTE id;
58 UINT collection;
59 UINT control_coll;
62 struct pid_effect_update
64 BYTE id;
65 UINT collection;
66 UINT type_coll;
67 UINT axes_coll;
68 UINT axis_count;
69 UINT direction_coll;
70 UINT direction_count;
71 struct hid_value_caps *axis_caps[6];
72 struct hid_value_caps *direction_caps[6];
73 struct hid_value_caps *duration_caps;
74 struct hid_value_caps *gain_caps;
75 struct hid_value_caps *sample_period_caps;
76 struct hid_value_caps *start_delay_caps;
77 struct hid_value_caps *trigger_button_caps;
78 struct hid_value_caps *trigger_repeat_interval_caps;
81 struct pid_set_periodic
83 BYTE id;
84 UINT collection;
85 struct hid_value_caps *magnitude_caps;
86 struct hid_value_caps *period_caps;
87 struct hid_value_caps *phase_caps;
88 struct hid_value_caps *offset_caps;
91 struct pid_set_envelope
93 BYTE id;
94 UINT collection;
95 struct hid_value_caps *attack_level_caps;
96 struct hid_value_caps *attack_time_caps;
97 struct hid_value_caps *fade_level_caps;
98 struct hid_value_caps *fade_time_caps;
101 struct pid_set_condition
103 BYTE id;
104 UINT collection;
105 struct hid_value_caps *center_point_offset_caps;
106 struct hid_value_caps *positive_coefficient_caps;
107 struct hid_value_caps *negative_coefficient_caps;
108 struct hid_value_caps *positive_saturation_caps;
109 struct hid_value_caps *negative_saturation_caps;
110 struct hid_value_caps *dead_band_caps;
113 struct pid_set_constant_force
115 BYTE id;
116 UINT collection;
117 struct hid_value_caps *magnitude_caps;
120 struct pid_set_ramp_force
122 BYTE id;
123 UINT collection;
124 struct hid_value_caps *start_caps;
125 struct hid_value_caps *end_caps;
128 struct pid_device_gain
130 BYTE id;
131 UINT collection;
132 struct hid_value_caps *device_gain_caps;
135 struct pid_device_pool
137 BYTE id;
138 UINT collection;
139 struct hid_value_caps *device_managed_caps;
142 struct pid_block_free
144 BYTE id;
145 UINT collection;
148 struct pid_block_load
150 BYTE id;
151 UINT collection;
152 UINT status_coll;
155 struct pid_new_effect
157 BYTE id;
158 UINT collection;
159 UINT type_coll;
162 struct pid_effect_state
164 BYTE id;
165 UINT collection;
166 struct hid_value_caps *safety_switch_caps;
167 struct hid_value_caps *actuator_power_caps;
168 struct hid_value_caps *actuator_override_switch_caps;
171 struct hid_joystick
173 struct dinput_device base;
174 LONG internal_ref;
176 HANDLE device;
177 OVERLAPPED read_ovl;
178 PHIDP_PREPARSED_DATA preparsed;
180 WCHAR device_path[MAX_PATH];
181 HIDD_ATTRIBUTES attrs;
182 HIDP_CAPS caps;
184 char *input_report_buf;
185 char *output_report_buf;
186 char *feature_report_buf;
187 USAGE_AND_PAGE *usages_buf;
188 UINT usages_count;
190 BYTE effect_inuse[255];
191 struct list effect_list;
192 struct pid_control_report pid_device_control;
193 struct pid_control_report pid_effect_control;
194 struct pid_effect_update pid_effect_update;
195 struct pid_set_periodic pid_set_periodic;
196 struct pid_set_envelope pid_set_envelope;
197 struct pid_set_condition pid_set_condition;
198 struct pid_set_constant_force pid_set_constant_force;
199 struct pid_set_ramp_force pid_set_ramp_force;
200 struct pid_device_gain pid_device_gain;
201 struct pid_device_pool pid_device_pool;
202 struct pid_block_free pid_block_free;
203 struct pid_block_load pid_block_load;
204 struct pid_new_effect pid_new_effect;
205 struct pid_effect_state pid_effect_state;
208 static inline struct hid_joystick *impl_from_IDirectInputDevice8W( IDirectInputDevice8W *iface )
210 return CONTAINING_RECORD( CONTAINING_RECORD( iface, struct dinput_device, IDirectInputDevice8W_iface ),
211 struct hid_joystick, base );
214 struct hid_joystick_effect
216 IDirectInputEffect IDirectInputEffect_iface;
217 LONG ref;
218 USAGE type;
219 ULONG index;
221 struct list entry;
222 struct hid_joystick *joystick;
224 DWORD axes[6];
225 LONG directions[6];
226 DICONSTANTFORCE constant_force;
227 DIRAMPFORCE ramp_force;
228 DICONDITION condition[6];
229 DIENVELOPE envelope;
230 DIPERIODIC periodic;
231 DIEFFECT params;
232 DWORD modified;
233 DWORD flags;
234 DWORD status;
236 char *effect_control_buf;
237 char *effect_update_buf;
238 char *type_specific_buf;
239 char *set_envelope_buf;
242 static inline struct hid_joystick_effect *impl_from_IDirectInputEffect( IDirectInputEffect *iface )
244 return CONTAINING_RECORD( iface, struct hid_joystick_effect, IDirectInputEffect_iface );
247 static inline BOOL is_exclusively_acquired( struct hid_joystick *joystick )
249 return joystick->base.status == STATUS_ACQUIRED && (joystick->base.dwCoopLevel & DISCL_EXCLUSIVE);
252 static const GUID *object_usage_to_guid( USAGE usage_page, USAGE usage )
254 switch (usage_page)
256 case HID_USAGE_PAGE_BUTTON: return &GUID_Button;
257 case HID_USAGE_PAGE_SIMULATION:
258 switch (usage)
260 case HID_USAGE_SIMULATION_STEERING: return &GUID_XAxis;
261 case HID_USAGE_SIMULATION_ACCELERATOR: return &GUID_YAxis;
262 case HID_USAGE_SIMULATION_BRAKE: return &GUID_RzAxis;
263 case HID_USAGE_SIMULATION_RUDDER: return &GUID_RzAxis;
264 case HID_USAGE_SIMULATION_THROTTLE: return &GUID_Slider;
266 break;
267 case HID_USAGE_PAGE_GENERIC:
268 switch (usage)
270 case HID_USAGE_GENERIC_X: return &GUID_XAxis;
271 case HID_USAGE_GENERIC_Y: return &GUID_YAxis;
272 case HID_USAGE_GENERIC_Z: return &GUID_ZAxis;
273 case HID_USAGE_GENERIC_WHEEL: return &GUID_ZAxis;
274 case HID_USAGE_GENERIC_RX: return &GUID_RxAxis;
275 case HID_USAGE_GENERIC_RY: return &GUID_RyAxis;
276 case HID_USAGE_GENERIC_RZ: return &GUID_RzAxis;
277 case HID_USAGE_GENERIC_SLIDER: return &GUID_Slider;
278 case HID_USAGE_GENERIC_HATSWITCH: return &GUID_POV;
280 break;
283 return &GUID_Unknown;
286 static inline USAGE effect_guid_to_usage( const GUID *guid )
288 if (IsEqualGUID( guid, &GUID_CustomForce )) return PID_USAGE_ET_CUSTOM_FORCE_DATA;
289 if (IsEqualGUID( guid, &GUID_ConstantForce )) return PID_USAGE_ET_CONSTANT_FORCE;
290 if (IsEqualGUID( guid, &GUID_RampForce )) return PID_USAGE_ET_RAMP;
291 if (IsEqualGUID( guid, &GUID_Square )) return PID_USAGE_ET_SQUARE;
292 if (IsEqualGUID( guid, &GUID_Sine )) return PID_USAGE_ET_SINE;
293 if (IsEqualGUID( guid, &GUID_Triangle )) return PID_USAGE_ET_TRIANGLE;
294 if (IsEqualGUID( guid, &GUID_SawtoothUp )) return PID_USAGE_ET_SAWTOOTH_UP;
295 if (IsEqualGUID( guid, &GUID_SawtoothDown )) return PID_USAGE_ET_SAWTOOTH_DOWN;
296 if (IsEqualGUID( guid, &GUID_Spring )) return PID_USAGE_ET_SPRING;
297 if (IsEqualGUID( guid, &GUID_Damper )) return PID_USAGE_ET_DAMPER;
298 if (IsEqualGUID( guid, &GUID_Inertia )) return PID_USAGE_ET_INERTIA;
299 if (IsEqualGUID( guid, &GUID_Friction )) return PID_USAGE_ET_FRICTION;
300 return 0;
303 static inline const GUID *effect_usage_to_guid( USAGE usage )
305 switch (usage)
307 case PID_USAGE_ET_CUSTOM_FORCE_DATA: return &GUID_CustomForce;
308 case PID_USAGE_ET_CONSTANT_FORCE: return &GUID_ConstantForce;
309 case PID_USAGE_ET_RAMP: return &GUID_RampForce;
310 case PID_USAGE_ET_SQUARE: return &GUID_Square;
311 case PID_USAGE_ET_SINE: return &GUID_Sine;
312 case PID_USAGE_ET_TRIANGLE: return &GUID_Triangle;
313 case PID_USAGE_ET_SAWTOOTH_UP: return &GUID_SawtoothUp;
314 case PID_USAGE_ET_SAWTOOTH_DOWN: return &GUID_SawtoothDown;
315 case PID_USAGE_ET_SPRING: return &GUID_Spring;
316 case PID_USAGE_ET_DAMPER: return &GUID_Damper;
317 case PID_USAGE_ET_INERTIA: return &GUID_Inertia;
318 case PID_USAGE_ET_FRICTION: return &GUID_Friction;
320 return &GUID_Unknown;
323 static const WCHAR *effect_guid_to_string( const GUID *guid )
325 if (IsEqualGUID( guid, &GUID_CustomForce )) return L"GUID_CustomForce";
326 if (IsEqualGUID( guid, &GUID_ConstantForce )) return L"GUID_ConstantForce";
327 if (IsEqualGUID( guid, &GUID_RampForce )) return L"GUID_RampForce";
328 if (IsEqualGUID( guid, &GUID_Square )) return L"GUID_Square";
329 if (IsEqualGUID( guid, &GUID_Sine )) return L"GUID_Sine";
330 if (IsEqualGUID( guid, &GUID_Triangle )) return L"GUID_Triangle";
331 if (IsEqualGUID( guid, &GUID_SawtoothUp )) return L"GUID_SawtoothUp";
332 if (IsEqualGUID( guid, &GUID_SawtoothDown )) return L"GUID_SawtoothDown";
333 if (IsEqualGUID( guid, &GUID_Spring )) return L"GUID_Spring";
334 if (IsEqualGUID( guid, &GUID_Damper )) return L"GUID_Damper";
335 if (IsEqualGUID( guid, &GUID_Inertia )) return L"GUID_Inertia";
336 if (IsEqualGUID( guid, &GUID_Friction )) return L"GUID_Friction";
337 return L"GUID_Unknown";
340 static const WCHAR *object_usage_to_string( DIDEVICEOBJECTINSTANCEW *instance )
342 switch (MAKELONG(instance->wUsage, instance->wUsagePage))
344 case MAKELONG(HID_USAGE_DIGITIZER_TIP_PRESSURE, HID_USAGE_PAGE_DIGITIZER): return L"Tip Pressure";
345 case MAKELONG(HID_USAGE_CONSUMER_VOLUME, HID_USAGE_PAGE_CONSUMER): return L"Volume";
347 case MAKELONG(HID_USAGE_GENERIC_HATSWITCH, HID_USAGE_PAGE_GENERIC): return L"Hat Switch";
348 case MAKELONG(HID_USAGE_GENERIC_JOYSTICK, HID_USAGE_PAGE_GENERIC): return L"Joystick";
349 case MAKELONG(HID_USAGE_GENERIC_RX, HID_USAGE_PAGE_GENERIC): return L"X Rotation";
350 case MAKELONG(HID_USAGE_GENERIC_RY, HID_USAGE_PAGE_GENERIC): return L"Y Rotation";
351 case MAKELONG(HID_USAGE_GENERIC_RZ, HID_USAGE_PAGE_GENERIC): return L"Z Rotation";
352 case MAKELONG(HID_USAGE_GENERIC_WHEEL, HID_USAGE_PAGE_GENERIC): return L"Wheel";
353 case MAKELONG(HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC): return L"X Axis";
354 case MAKELONG(HID_USAGE_GENERIC_Y, HID_USAGE_PAGE_GENERIC): return L"Y Axis";
355 case MAKELONG(HID_USAGE_GENERIC_Z, HID_USAGE_PAGE_GENERIC): return L"Z Axis";
357 case MAKELONG(PID_USAGE_ATTACK_LEVEL, HID_USAGE_PAGE_PID): return L"Attack Level";
358 case MAKELONG(PID_USAGE_ATTACK_TIME, HID_USAGE_PAGE_PID): return L"Attack Time";
359 case MAKELONG(PID_USAGE_AXES_ENABLE, HID_USAGE_PAGE_PID): return L"Axes Enable";
361 case MAKELONG(PID_USAGE_DC_DEVICE_CONTINUE, HID_USAGE_PAGE_PID): return L"DC Device Continue";
362 case MAKELONG(PID_USAGE_DC_DEVICE_PAUSE, HID_USAGE_PAGE_PID): return L"DC Device Pause";
363 case MAKELONG(PID_USAGE_DC_DEVICE_RESET, HID_USAGE_PAGE_PID): return L"DC Device Reset";
364 case MAKELONG(PID_USAGE_DC_DISABLE_ACTUATORS, HID_USAGE_PAGE_PID): return L"DC Disable Actuators";
365 case MAKELONG(PID_USAGE_DC_ENABLE_ACTUATORS, HID_USAGE_PAGE_PID): return L"DC Enable Actuators";
366 case MAKELONG(PID_USAGE_DC_STOP_ALL_EFFECTS, HID_USAGE_PAGE_PID): return L"DC Stop All Effects";
368 case MAKELONG(PID_USAGE_DEVICE_GAIN, HID_USAGE_PAGE_PID): return L"Device Gain";
369 case MAKELONG(PID_USAGE_DEVICE_GAIN_REPORT, HID_USAGE_PAGE_PID): return L"Device Gain Report";
370 case MAKELONG(PID_USAGE_CP_OFFSET, HID_USAGE_PAGE_PID): return L"CP Offset";
371 case MAKELONG(PID_USAGE_DEAD_BAND, HID_USAGE_PAGE_PID): return L"Dead Band";
372 case MAKELONG(PID_USAGE_DEVICE_CONTROL, HID_USAGE_PAGE_PID): return L"PID Device Control";
373 case MAKELONG(PID_USAGE_DEVICE_CONTROL_REPORT, HID_USAGE_PAGE_PID): return L"PID Device Control Report";
374 case MAKELONG(PID_USAGE_DIRECTION, HID_USAGE_PAGE_PID): return L"Direction";
375 case MAKELONG(PID_USAGE_DIRECTION_ENABLE, HID_USAGE_PAGE_PID): return L"Direction Enable";
376 case MAKELONG(PID_USAGE_DURATION, HID_USAGE_PAGE_PID): return L"Duration";
377 case MAKELONG(PID_USAGE_EFFECT_BLOCK_INDEX, HID_USAGE_PAGE_PID): return L"Effect Block Index";
378 case MAKELONG(PID_USAGE_EFFECT_OPERATION, HID_USAGE_PAGE_PID): return L"Effect Operation";
379 case MAKELONG(PID_USAGE_EFFECT_OPERATION_REPORT, HID_USAGE_PAGE_PID): return L"Effect Operation Report";
380 case MAKELONG(PID_USAGE_EFFECT_TYPE, HID_USAGE_PAGE_PID): return L"Effect Type";
382 case MAKELONG(PID_USAGE_ET_CONSTANT_FORCE, HID_USAGE_PAGE_PID): return L"ET Constant Force";
383 case MAKELONG(PID_USAGE_ET_CUSTOM_FORCE_DATA, HID_USAGE_PAGE_PID): return L"ET Custom Force Data";
384 case MAKELONG(PID_USAGE_ET_DAMPER, HID_USAGE_PAGE_PID): return L"ET Damper";
385 case MAKELONG(PID_USAGE_ET_FRICTION, HID_USAGE_PAGE_PID): return L"ET Friction";
386 case MAKELONG(PID_USAGE_ET_INERTIA, HID_USAGE_PAGE_PID): return L"ET Inertia";
387 case MAKELONG(PID_USAGE_ET_RAMP, HID_USAGE_PAGE_PID): return L"ET Ramp";
388 case MAKELONG(PID_USAGE_ET_SAWTOOTH_DOWN, HID_USAGE_PAGE_PID): return L"ET Sawtooth Down";
389 case MAKELONG(PID_USAGE_ET_SAWTOOTH_UP, HID_USAGE_PAGE_PID): return L"ET Sawtooth Up";
390 case MAKELONG(PID_USAGE_ET_SINE, HID_USAGE_PAGE_PID): return L"ET Sine";
391 case MAKELONG(PID_USAGE_ET_SPRING, HID_USAGE_PAGE_PID): return L"ET Spring";
392 case MAKELONG(PID_USAGE_ET_SQUARE, HID_USAGE_PAGE_PID): return L"ET Square";
393 case MAKELONG(PID_USAGE_ET_TRIANGLE, HID_USAGE_PAGE_PID): return L"ET Triangle";
395 case MAKELONG(PID_USAGE_NEGATIVE_COEFFICIENT, HID_USAGE_PAGE_PID): return L"Negative Coefficient";
396 case MAKELONG(PID_USAGE_NEGATIVE_SATURATION, HID_USAGE_PAGE_PID): return L"Negative Saturation";
397 case MAKELONG(PID_USAGE_POSITIVE_COEFFICIENT, HID_USAGE_PAGE_PID): return L"Positive Coefficient";
398 case MAKELONG(PID_USAGE_POSITIVE_SATURATION, HID_USAGE_PAGE_PID): return L"Positive Saturation";
399 case MAKELONG(PID_USAGE_SET_CONDITION_REPORT, HID_USAGE_PAGE_PID): return L"Set Condition Report";
400 case MAKELONG(PID_USAGE_TYPE_SPECIFIC_BLOCK_OFFSET, HID_USAGE_PAGE_PID): return L"Type Specific Block Offset";
402 case MAKELONG(PID_USAGE_FADE_LEVEL, HID_USAGE_PAGE_PID): return L"Fade Level";
403 case MAKELONG(PID_USAGE_FADE_TIME, HID_USAGE_PAGE_PID): return L"Fade Time";
404 case MAKELONG(PID_USAGE_LOOP_COUNT, HID_USAGE_PAGE_PID): return L"Loop Count";
405 case MAKELONG(PID_USAGE_MAGNITUDE, HID_USAGE_PAGE_PID): return L"Magnitude";
406 case MAKELONG(PID_USAGE_OP_EFFECT_START, HID_USAGE_PAGE_PID): return L"Op Effect Start";
407 case MAKELONG(PID_USAGE_OP_EFFECT_START_SOLO, HID_USAGE_PAGE_PID): return L"Op Effect Start Solo";
408 case MAKELONG(PID_USAGE_OP_EFFECT_STOP, HID_USAGE_PAGE_PID): return L"Op Effect Stop";
409 case MAKELONG(PID_USAGE_SET_EFFECT_REPORT, HID_USAGE_PAGE_PID): return L"Set Effect Report";
410 case MAKELONG(PID_USAGE_SET_ENVELOPE_REPORT, HID_USAGE_PAGE_PID): return L"Set Envelope Report";
411 case MAKELONG(PID_USAGE_SET_PERIODIC_REPORT, HID_USAGE_PAGE_PID): return L"Set Periodic Report";
412 case MAKELONG(PID_USAGE_START_DELAY, HID_USAGE_PAGE_PID): return L"Start Delay";
413 case MAKELONG(PID_USAGE_STATE_REPORT, HID_USAGE_PAGE_PID): return L"PID State Report";
414 case MAKELONG(PID_USAGE_TRIGGER_BUTTON, HID_USAGE_PAGE_PID): return L"Trigger Button";
416 case MAKELONG(HID_USAGE_SIMULATION_RUDDER, HID_USAGE_PAGE_SIMULATION): return L"Rudder";
417 default: return NULL;
421 static HRESULT find_next_effect_id( struct hid_joystick *impl, ULONG *index, USAGE type )
423 struct pid_device_pool *device_pool = &impl->pid_device_pool;
424 struct pid_new_effect *new_effect = &impl->pid_new_effect;
425 struct pid_block_load *block_load = &impl->pid_block_load;
426 ULONG i, count, report_len = impl->caps.FeatureReportByteLength;
427 NTSTATUS status;
428 USAGE usage;
430 if (!device_pool->device_managed_caps)
432 for (i = 0; i < ARRAY_SIZE(impl->effect_inuse); ++i)
433 if (!impl->effect_inuse[i]) break;
434 if (i == ARRAY_SIZE(impl->effect_inuse)) return DIERR_DEVICEFULL;
435 impl->effect_inuse[i] = TRUE;
436 *index = i + 1;
438 else
440 status = HidP_InitializeReportForID( HidP_Feature, new_effect->id, impl->preparsed,
441 impl->feature_report_buf, report_len );
442 if (status != HIDP_STATUS_SUCCESS) return status;
444 count = 1;
445 status = HidP_SetUsages( HidP_Feature, HID_USAGE_PAGE_PID, new_effect->type_coll,
446 &type, &count, impl->preparsed, impl->feature_report_buf, report_len );
447 if (status != HIDP_STATUS_SUCCESS) return status;
449 if (!HidD_SetFeature( impl->device, impl->feature_report_buf, report_len )) return DIERR_INPUTLOST;
451 status = HidP_InitializeReportForID( HidP_Feature, block_load->id, impl->preparsed,
452 impl->feature_report_buf, report_len );
453 if (status != HIDP_STATUS_SUCCESS) return status;
455 if (!HidD_GetFeature( impl->device, impl->feature_report_buf, report_len )) return DIERR_INPUTLOST;
457 count = 1;
458 status = HidP_GetUsages( HidP_Feature, HID_USAGE_PAGE_PID, block_load->status_coll,
459 &usage, &count, impl->preparsed, impl->feature_report_buf, report_len );
460 if (status != HIDP_STATUS_SUCCESS) return status;
462 if (count != 1 || usage == PID_USAGE_BLOCK_LOAD_ERROR) return DIERR_INPUTLOST;
463 if (usage == PID_USAGE_BLOCK_LOAD_FULL) return DIERR_DEVICEFULL;
465 status = HidP_GetUsageValue( HidP_Feature, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX,
466 index, impl->preparsed, impl->feature_report_buf, report_len );
467 if (status != HIDP_STATUS_SUCCESS) return status;
470 return DI_OK;
473 typedef BOOL (*enum_object_callback)( struct hid_joystick *impl, struct hid_value_caps *caps,
474 DIDEVICEOBJECTINSTANCEW *instance, void *data );
476 static BOOL enum_object( struct hid_joystick *impl, const DIPROPHEADER *filter, DWORD flags,
477 enum_object_callback callback, struct hid_value_caps *caps,
478 DIDEVICEOBJECTINSTANCEW *instance, void *data )
480 if (flags != DIDFT_ALL && !(flags & DIDFT_GETTYPE( instance->dwType ))) return DIENUM_CONTINUE;
482 switch (filter->dwHow)
484 case DIPH_DEVICE:
485 return callback( impl, caps, instance, data );
486 case DIPH_BYOFFSET:
487 if (filter->dwObj != instance->dwOfs) return DIENUM_CONTINUE;
488 return callback( impl, caps, instance, data );
489 case DIPH_BYID:
490 if ((filter->dwObj & 0x00ffffff) != (instance->dwType & 0x00ffffff)) return DIENUM_CONTINUE;
491 return callback( impl, caps, instance, data );
492 case DIPH_BYUSAGE:
493 if (HIWORD( filter->dwObj ) != instance->wUsagePage) return DIENUM_CONTINUE;
494 if (LOWORD( filter->dwObj ) != instance->wUsage) return DIENUM_CONTINUE;
495 return callback( impl, caps, instance, data );
496 default:
497 FIXME( "unimplemented filter dwHow %#lx dwObj %#lx\n", filter->dwHow, filter->dwObj );
498 break;
501 return DIENUM_CONTINUE;
504 static void check_pid_effect_axis_caps( struct hid_joystick *impl, DIDEVICEOBJECTINSTANCEW *instance )
506 struct pid_effect_update *effect_update = &impl->pid_effect_update;
507 ULONG i;
509 for (i = 0; i < effect_update->axis_count; ++i)
511 if (effect_update->axis_caps[i]->usage_page != instance->wUsagePage) continue;
512 if (effect_update->axis_caps[i]->usage_min > instance->wUsage) continue;
513 if (effect_update->axis_caps[i]->usage_max >= instance->wUsage) break;
516 if (i == effect_update->axis_count) return;
517 instance->dwType |= DIDFT_FFACTUATOR;
518 instance->dwFlags |= DIDOI_FFACTUATOR;
521 static void set_axis_type( DIDEVICEOBJECTINSTANCEW *instance, BOOL *seen, DWORD i, DWORD *count )
523 if (!seen[i]) instance->dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( i );
524 else instance->dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 6 + (*count)++ );
525 seen[i] = TRUE;
528 static BOOL enum_objects( struct hid_joystick *impl, const DIPROPHEADER *filter, DWORD flags,
529 enum_object_callback callback, void *data )
531 DWORD collection = 0, object = 0, axis = 0, button = 0, pov = 0, value_ofs = 0, button_ofs = 0, j, count, len;
532 struct hid_preparsed_data *preparsed = (struct hid_preparsed_data *)impl->preparsed;
533 DIDEVICEOBJECTINSTANCEW instance = {.dwSize = sizeof(DIDEVICEOBJECTINSTANCEW)};
534 struct hid_value_caps *caps, *caps_end, *nary, *nary_end, *effect_caps;
535 struct hid_collection_node *node, *node_end;
536 WORD version = impl->base.dinput->dwVersion;
537 BOOL ret, seen_axis[6] = {0};
538 const WCHAR *tmp;
540 button_ofs += impl->caps.NumberInputValueCaps * sizeof(LONG);
541 if (version >= 0x800)
543 button_ofs += impl->caps.NumberOutputValueCaps * sizeof(LONG);
544 button_ofs += impl->caps.NumberFeatureValueCaps * sizeof(LONG);
547 for (caps = HID_INPUT_VALUE_CAPS( preparsed ), caps_end = caps + preparsed->input_caps_count;
548 caps != caps_end; ++caps)
550 if (!caps->usage_page) continue;
551 if (caps->flags & HID_VALUE_CAPS_IS_BUTTON) continue;
553 if (caps->usage_page >= HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN)
554 value_ofs += (caps->usage_max - caps->usage_min + 1) * sizeof(LONG);
555 else for (j = caps->usage_min; j <= caps->usage_max; ++j)
557 instance.dwOfs = value_ofs;
558 switch (MAKELONG(j, caps->usage_page))
560 case MAKELONG(HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC):
561 case MAKELONG(HID_USAGE_GENERIC_Y, HID_USAGE_PAGE_GENERIC):
562 case MAKELONG(HID_USAGE_GENERIC_Z, HID_USAGE_PAGE_GENERIC):
563 case MAKELONG(HID_USAGE_GENERIC_RX, HID_USAGE_PAGE_GENERIC):
564 case MAKELONG(HID_USAGE_GENERIC_RY, HID_USAGE_PAGE_GENERIC):
565 case MAKELONG(HID_USAGE_GENERIC_RZ, HID_USAGE_PAGE_GENERIC):
566 set_axis_type( &instance, seen_axis, j - HID_USAGE_GENERIC_X, &axis );
567 instance.dwFlags = DIDOI_ASPECTPOSITION;
568 break;
569 case MAKELONG(HID_USAGE_SIMULATION_STEERING, HID_USAGE_PAGE_SIMULATION):
570 set_axis_type( &instance, seen_axis, 0, &axis );
571 instance.dwFlags = DIDOI_ASPECTPOSITION;
572 break;
573 case MAKELONG(HID_USAGE_SIMULATION_ACCELERATOR, HID_USAGE_PAGE_SIMULATION):
574 set_axis_type( &instance, seen_axis, 1, &axis );
575 instance.dwFlags = DIDOI_ASPECTPOSITION;
576 break;
577 case MAKELONG(HID_USAGE_GENERIC_WHEEL, HID_USAGE_PAGE_GENERIC):
578 case MAKELONG(HID_USAGE_SIMULATION_THROTTLE, HID_USAGE_PAGE_SIMULATION):
579 set_axis_type( &instance, seen_axis, 2, &axis );
580 instance.dwFlags = DIDOI_ASPECTPOSITION;
581 break;
582 case MAKELONG(HID_USAGE_SIMULATION_RUDDER, HID_USAGE_PAGE_SIMULATION):
583 case MAKELONG(HID_USAGE_SIMULATION_BRAKE, HID_USAGE_PAGE_SIMULATION):
584 set_axis_type( &instance, seen_axis, 5, &axis );
585 instance.dwFlags = DIDOI_ASPECTPOSITION;
586 break;
587 case MAKELONG(HID_USAGE_GENERIC_HATSWITCH, HID_USAGE_PAGE_GENERIC):
588 instance.dwType = DIDFT_POV | DIDFT_MAKEINSTANCE( pov++ );
589 instance.dwFlags = 0;
590 break;
591 case MAKELONG(HID_USAGE_GENERIC_SLIDER, HID_USAGE_PAGE_GENERIC):
592 case MAKELONG(HID_USAGE_GENERIC_DIAL, HID_USAGE_PAGE_GENERIC):
593 instance.dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 6 + axis++ );
594 instance.dwFlags = DIDOI_ASPECTPOSITION;
595 break;
596 default:
597 instance.dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 6 + axis++ );
598 instance.dwFlags = 0;
599 break;
601 instance.wUsagePage = caps->usage_page;
602 instance.wUsage = j;
603 instance.guidType = *object_usage_to_guid( instance.wUsagePage, instance.wUsage );
604 instance.wReportId = caps->report_id;
605 instance.wCollectionNumber = caps->link_collection;
606 instance.dwDimension = caps->units;
607 instance.wExponent = caps->units_exp;
608 if ((tmp = object_usage_to_string( &instance ))) lstrcpynW( instance.tszName, tmp, MAX_PATH );
609 else swprintf( instance.tszName, MAX_PATH, L"Unknown %u", DIDFT_GETINSTANCE( instance.dwType ) );
610 check_pid_effect_axis_caps( impl, &instance );
611 ret = enum_object( impl, filter, flags, callback, caps, &instance, data );
612 if (ret != DIENUM_CONTINUE) return ret;
613 value_ofs += sizeof(LONG);
614 object++;
618 effect_caps = impl->pid_effect_update.trigger_button_caps;
620 for (caps = HID_INPUT_VALUE_CAPS( preparsed ), caps_end = caps + preparsed->input_caps_count;
621 caps != caps_end; ++caps)
623 if (!caps->usage_page) continue;
624 if (!(caps->flags & HID_VALUE_CAPS_IS_BUTTON)) continue;
626 if (caps->usage_page >= HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN)
627 button_ofs += caps->usage_max - caps->usage_min + 1;
628 else for (j = caps->usage_min; j <= caps->usage_max; ++j)
630 instance.dwOfs = button_ofs;
631 instance.dwType = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( button++ );
632 instance.dwFlags = 0;
633 if (effect_caps && effect_caps->logical_min <= j && effect_caps->logical_max >= j)
635 instance.dwType |= DIDFT_FFEFFECTTRIGGER;
636 instance.dwFlags |= DIDOI_FFEFFECTTRIGGER;
638 instance.wUsagePage = caps->usage_page;
639 instance.wUsage = j;
640 instance.guidType = *object_usage_to_guid( instance.wUsagePage, instance.wUsage );
641 instance.wReportId = caps->report_id;
642 instance.wCollectionNumber = caps->link_collection;
643 instance.dwDimension = caps->units;
644 instance.wExponent = caps->units_exp;
645 swprintf( instance.tszName, MAX_PATH, L"Button %u", DIDFT_GETINSTANCE( instance.dwType ) );
646 ret = enum_object( impl, filter, flags, callback, caps, &instance, data );
647 if (ret != DIENUM_CONTINUE) return ret;
648 button_ofs++;
649 object++;
653 count = preparsed->output_caps_count + preparsed->feature_caps_count;
654 for (caps = HID_OUTPUT_VALUE_CAPS( preparsed ), caps_end = caps + count;
655 caps != caps_end; ++caps)
657 if (!caps->usage_page) continue;
659 if (caps->usage_page >= HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN)
661 if (caps->flags & HID_VALUE_CAPS_IS_BUTTON) button_ofs += caps->usage_max - caps->usage_min + 1;
662 else value_ofs += (caps->usage_max - caps->usage_min + 1) * sizeof(LONG);
664 else if (caps->flags & HID_VALUE_CAPS_ARRAY_HAS_MORE)
666 for (nary_end = caps - 1; caps != caps_end; caps++)
667 if (!(caps->flags & HID_VALUE_CAPS_ARRAY_HAS_MORE)) break;
669 for (nary = caps; nary != nary_end; nary--)
671 if (version < 0x800) instance.dwOfs = 0;
672 else instance.dwOfs = button_ofs;
674 instance.dwType = DIDFT_NODATA | DIDFT_MAKEINSTANCE( object++ ) | DIDFT_OUTPUT;
675 instance.dwFlags = 0x80008000;
676 instance.wUsagePage = nary->usage_page;
677 instance.wUsage = nary->usage_min;
678 instance.guidType = GUID_Unknown;
679 instance.wReportId = nary->report_id;
680 instance.wCollectionNumber = nary->link_collection;
681 instance.dwDimension = caps->units;
682 instance.wExponent = caps->units_exp;
683 if ((tmp = object_usage_to_string( &instance ))) lstrcpynW( instance.tszName, tmp, MAX_PATH );
684 else swprintf( instance.tszName, MAX_PATH, L"Unknown %u", DIDFT_GETINSTANCE( instance.dwType ) );
685 ret = enum_object( impl, filter, flags, callback, nary, &instance, data );
686 if (ret != DIENUM_CONTINUE) return ret;
687 button_ofs++;
690 else for (j = caps->usage_min; j <= caps->usage_max; ++j)
692 if (version < 0x800) instance.dwOfs = 0;
693 else if (caps->flags & HID_VALUE_CAPS_IS_BUTTON) instance.dwOfs = button_ofs;
694 else instance.dwOfs = value_ofs;
696 instance.dwType = DIDFT_NODATA | DIDFT_MAKEINSTANCE( object++ ) | DIDFT_OUTPUT;
697 instance.dwFlags = 0x80008000;
698 instance.wUsagePage = caps->usage_page;
699 instance.wUsage = j;
700 instance.guidType = GUID_Unknown;
701 instance.wReportId = caps->report_id;
702 instance.wCollectionNumber = caps->link_collection;
703 instance.dwDimension = caps->units;
704 instance.wExponent = caps->units_exp;
705 if ((tmp = object_usage_to_string( &instance ))) lstrcpynW( instance.tszName, tmp, MAX_PATH );
706 else swprintf( instance.tszName, MAX_PATH, L"Unknown %u", DIDFT_GETINSTANCE( instance.dwType ) );
707 ret = enum_object( impl, filter, flags, callback, caps, &instance, data );
708 if (ret != DIENUM_CONTINUE) return ret;
710 if (caps->flags & HID_VALUE_CAPS_IS_BUTTON) button_ofs++;
711 else value_ofs += sizeof(LONG);
715 for (node = HID_COLLECTION_NODES( preparsed ), node_end = node + preparsed->number_link_collection_nodes;
716 node != node_end; ++node)
718 if (!node->usage_page) continue;
719 if (node->usage_page < HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN)
721 instance.dwOfs = 0;
722 instance.dwType = DIDFT_COLLECTION | DIDFT_MAKEINSTANCE( collection++ ) | DIDFT_NODATA;
723 instance.dwFlags = 0;
724 instance.wUsagePage = node->usage_page;
725 instance.wUsage = node->usage;
726 instance.guidType = *object_usage_to_guid( instance.wUsagePage, instance.wUsage );
727 instance.wReportId = 0;
728 instance.wCollectionNumber = node->parent;
729 instance.dwDimension = 0;
730 instance.wExponent = 0;
731 len = swprintf( instance.tszName, MAX_PATH, L"Collection %u - ", DIDFT_GETINSTANCE( instance.dwType ) );
732 if ((tmp = object_usage_to_string( &instance ))) lstrcpynW( instance.tszName + len, tmp, MAX_PATH - len );
733 else swprintf( instance.tszName + len, MAX_PATH - len, L"Unknown %u", DIDFT_GETINSTANCE( instance.dwType ) );
734 ret = enum_object( impl, filter, flags, callback, NULL, &instance, data );
735 if (ret != DIENUM_CONTINUE) return ret;
739 return DIENUM_CONTINUE;
742 static void set_report_value( struct hid_joystick *impl, char *report_buf,
743 struct hid_value_caps *caps, LONG value )
745 ULONG report_len = impl->caps.OutputReportByteLength;
746 PHIDP_PREPARSED_DATA preparsed = impl->preparsed;
747 LONG log_min, log_max, phy_min, phy_max;
748 NTSTATUS status;
750 if (!caps) return;
752 log_min = caps->logical_min;
753 log_max = caps->logical_max;
754 phy_min = caps->physical_min;
755 phy_max = caps->physical_max;
757 if (phy_max || phy_min)
759 if (value > phy_max || value < phy_min) value = -1;
760 else value = log_min + (value - phy_min) * (log_max - log_min) / (phy_max - phy_min);
763 status = HidP_SetUsageValue( HidP_Output, caps->usage_page, caps->link_collection,
764 caps->usage_min, value, preparsed, report_buf, report_len );
765 if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_SetUsageValue %04x:%04x returned %#lx\n",
766 caps->usage_page, caps->usage_min, status );
769 static void hid_joystick_addref( IDirectInputDevice8W *iface )
771 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
772 ULONG ref = InterlockedIncrement( &impl->internal_ref );
773 TRACE( "iface %p, internal ref %lu.\n", iface, ref );
776 static void hid_joystick_release( IDirectInputDevice8W *iface )
778 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
779 ULONG ref = InterlockedDecrement( &impl->internal_ref );
780 TRACE( "iface %p, internal ref %lu.\n", iface, ref );
782 if (!ref)
784 free( impl->usages_buf );
785 free( impl->feature_report_buf );
786 free( impl->output_report_buf );
787 free( impl->input_report_buf );
788 HidD_FreePreparsedData( impl->preparsed );
789 CloseHandle( impl->base.read_event );
790 CloseHandle( impl->device );
791 dinput_device_destroy( iface );
795 static HRESULT hid_joystick_get_property( IDirectInputDevice8W *iface, DWORD property,
796 DIPROPHEADER *header, const DIDEVICEOBJECTINSTANCEW *instance )
798 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
800 switch (property)
802 case (DWORD_PTR)DIPROP_PRODUCTNAME:
804 DIPROPSTRING *value = (DIPROPSTRING *)header;
805 lstrcpynW( value->wsz, impl->base.instance.tszProductName, MAX_PATH );
806 return DI_OK;
808 case (DWORD_PTR)DIPROP_INSTANCENAME:
810 DIPROPSTRING *value = (DIPROPSTRING *)header;
811 lstrcpynW( value->wsz, impl->base.instance.tszInstanceName, MAX_PATH );
812 return DI_OK;
814 case (DWORD_PTR)DIPROP_VIDPID:
816 DIPROPDWORD *value = (DIPROPDWORD *)header;
817 if (!impl->attrs.VendorID || !impl->attrs.ProductID) return DIERR_UNSUPPORTED;
818 value->dwData = MAKELONG( impl->attrs.VendorID, impl->attrs.ProductID );
819 return DI_OK;
821 case (DWORD_PTR)DIPROP_JOYSTICKID:
823 DIPROPDWORD *value = (DIPROPDWORD *)header;
824 value->dwData = impl->base.instance.guidInstance.Data3;
825 return DI_OK;
827 case (DWORD_PTR)DIPROP_GUIDANDPATH:
829 DIPROPGUIDANDPATH *value = (DIPROPGUIDANDPATH *)header;
830 value->guidClass = GUID_DEVCLASS_HIDCLASS;
831 lstrcpynW( value->wszPath, impl->device_path, MAX_PATH );
832 return DI_OK;
834 case (DWORD_PTR)DIPROP_FFLOAD:
836 DIPROPDWORD *value = (DIPROPDWORD *)header;
837 if (!(impl->base.caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_UNSUPPORTED;
838 if (!is_exclusively_acquired( impl )) return DIERR_NOTEXCLUSIVEACQUIRED;
839 value->dwData = 0;
840 return DI_OK;
844 return DIERR_UNSUPPORTED;
847 static HRESULT hid_joystick_send_device_gain( IDirectInputDevice8W *iface, LONG device_gain )
849 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
850 struct pid_device_gain *report = &impl->pid_device_gain;
851 ULONG report_len = impl->caps.OutputReportByteLength;
852 char *report_buf = impl->output_report_buf;
853 NTSTATUS status;
855 TRACE( "iface %p.\n", iface );
857 if (!report->id || !report->device_gain_caps) return DI_OK;
859 status = HidP_InitializeReportForID( HidP_Output, report->id, impl->preparsed, report_buf, report_len );
860 if (status != HIDP_STATUS_SUCCESS) return status;
862 set_report_value( impl, report_buf, report->device_gain_caps, device_gain );
864 if (!WriteFile( impl->device, report_buf, report_len, NULL, NULL )) return DIERR_INPUTLOST;
865 return DI_OK;
868 static HRESULT hid_joystick_acquire( IDirectInputDevice8W *iface )
870 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
871 ULONG report_len = impl->caps.InputReportByteLength;
872 BOOL ret;
874 if (impl->device == INVALID_HANDLE_VALUE)
876 impl->device = CreateFileW( impl->device_path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
877 NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, 0 );
878 if (impl->device == INVALID_HANDLE_VALUE) return DIERR_UNPLUGGED;
881 memset( &impl->read_ovl, 0, sizeof(impl->read_ovl) );
882 impl->read_ovl.hEvent = impl->base.read_event;
883 ret = ReadFile( impl->device, impl->input_report_buf, report_len, NULL, &impl->read_ovl );
884 if (!ret && GetLastError() != ERROR_IO_PENDING)
886 CloseHandle( impl->device );
887 impl->device = INVALID_HANDLE_VALUE;
888 return DIERR_UNPLUGGED;
891 IDirectInputDevice8_SendForceFeedbackCommand( iface, DISFFC_RESET );
892 return DI_OK;
895 static HRESULT hid_joystick_send_force_feedback_command( IDirectInputDevice8W *iface, DWORD command, BOOL unacquire );
897 static HRESULT hid_joystick_unacquire( IDirectInputDevice8W *iface )
899 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
900 BOOL ret;
902 if (impl->device == INVALID_HANDLE_VALUE) return DI_NOEFFECT;
904 ret = CancelIoEx( impl->device, &impl->read_ovl );
905 if (!ret) WARN( "CancelIoEx failed, last error %lu\n", GetLastError() );
906 else WaitForSingleObject( impl->base.read_event, INFINITE );
908 if (!(impl->base.caps.dwFlags & DIDC_FORCEFEEDBACK)) return DI_OK;
909 if (!is_exclusively_acquired( impl )) return DI_OK;
910 hid_joystick_send_force_feedback_command( iface, DISFFC_RESET, TRUE );
911 return DI_OK;
914 static HRESULT hid_joystick_create_effect( IDirectInputDevice8W *iface, IDirectInputEffect **out );
916 static HRESULT hid_joystick_get_effect_info( IDirectInputDevice8W *iface, DIEFFECTINFOW *info, const GUID *guid )
918 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
919 struct pid_effect_update *effect_update = &impl->pid_effect_update;
920 struct pid_set_condition *set_condition = &impl->pid_set_condition;
921 struct pid_set_periodic *set_periodic = &impl->pid_set_periodic;
922 struct pid_set_envelope *set_envelope = &impl->pid_set_envelope;
923 PHIDP_PREPARSED_DATA preparsed = impl->preparsed;
924 HIDP_BUTTON_CAPS button;
925 ULONG type, collection;
926 NTSTATUS status;
927 USAGE usage = 0;
928 USHORT count;
930 switch ((usage = effect_guid_to_usage( guid )))
932 case PID_USAGE_ET_SQUARE:
933 case PID_USAGE_ET_SINE:
934 case PID_USAGE_ET_TRIANGLE:
935 case PID_USAGE_ET_SAWTOOTH_UP:
936 case PID_USAGE_ET_SAWTOOTH_DOWN:
937 type = DIEFT_PERIODIC;
938 break;
939 case PID_USAGE_ET_SPRING:
940 case PID_USAGE_ET_DAMPER:
941 case PID_USAGE_ET_INERTIA:
942 case PID_USAGE_ET_FRICTION:
943 type = DIEFT_CONDITION;
944 break;
945 case PID_USAGE_ET_CONSTANT_FORCE:
946 type = DIEFT_CONSTANTFORCE;
947 break;
948 case PID_USAGE_ET_RAMP:
949 type = DIEFT_RAMPFORCE;
950 break;
951 case PID_USAGE_ET_CUSTOM_FORCE_DATA:
952 type = DIEFT_CUSTOMFORCE;
953 break;
954 default:
955 return DIERR_DEVICENOTREG;
958 if (!(collection = effect_update->collection)) return DIERR_DEVICENOTREG;
960 info->dwDynamicParams = DIEP_TYPESPECIFICPARAMS;
961 if (effect_update->axis_count) info->dwDynamicParams |= DIEP_AXES;
962 if (effect_update->duration_caps) info->dwDynamicParams |= DIEP_DURATION;
963 if (effect_update->gain_caps) info->dwDynamicParams |= DIEP_GAIN;
964 if (effect_update->sample_period_caps) info->dwDynamicParams |= DIEP_SAMPLEPERIOD;
965 if (effect_update->start_delay_caps)
967 type |= DIEFT_STARTDELAY;
968 info->dwDynamicParams |= DIEP_STARTDELAY;
970 if (effect_update->direction_coll) info->dwDynamicParams |= DIEP_DIRECTION;
971 if (effect_update->axes_coll) info->dwDynamicParams |= DIEP_AXES;
973 if (!(collection = effect_update->type_coll)) return DIERR_DEVICENOTREG;
974 else
976 count = 1;
977 status = HidP_GetSpecificButtonCaps( HidP_Output, HID_USAGE_PAGE_PID, collection,
978 usage, &button, &count, preparsed );
979 if (status != HIDP_STATUS_SUCCESS)
981 WARN( "HidP_GetSpecificButtonCaps %#x returned %#lx\n", usage, status );
982 return DIERR_DEVICENOTREG;
984 else if (!count)
986 WARN( "effect usage %#x not found\n", usage );
987 return DIERR_DEVICENOTREG;
991 if ((DIEFT_GETTYPE(type) == DIEFT_PERIODIC) && (collection = set_periodic->collection))
993 if (set_periodic->magnitude_caps) info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS;
994 if (set_periodic->offset_caps) info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS;
995 if (set_periodic->period_caps) info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS;
996 if (set_periodic->phase_caps) info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS;
999 if ((DIEFT_GETTYPE(type) == DIEFT_PERIODIC ||
1000 DIEFT_GETTYPE(type) == DIEFT_RAMPFORCE ||
1001 DIEFT_GETTYPE(type) == DIEFT_CONSTANTFORCE) &&
1002 (collection = set_envelope->collection))
1004 info->dwDynamicParams |= DIEP_ENVELOPE;
1005 if (set_envelope->attack_level_caps) type |= DIEFT_FFATTACK;
1006 if (set_envelope->attack_time_caps) type |= DIEFT_FFATTACK;
1007 if (set_envelope->fade_level_caps) type |= DIEFT_FFFADE;
1008 if (set_envelope->fade_time_caps) type |= DIEFT_FFFADE;
1009 if (effect_update->trigger_button_caps) info->dwDynamicParams |= DIEP_TRIGGERBUTTON;
1010 if (effect_update->trigger_repeat_interval_caps) info->dwDynamicParams |= DIEP_TRIGGERREPEATINTERVAL;
1013 if (DIEFT_GETTYPE(type) == DIEFT_CONDITION && (collection = set_condition->collection))
1015 if (set_condition->center_point_offset_caps)
1016 info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS;
1017 if (set_condition->positive_coefficient_caps || set_condition->negative_coefficient_caps)
1018 info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS;
1019 if (set_condition->positive_saturation_caps || set_condition->negative_saturation_caps)
1021 info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS;
1022 type |= DIEFT_SATURATION;
1024 if (set_condition->dead_band_caps)
1026 info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS;
1027 type |= DIEFT_DEADBAND;
1031 info->guid = *guid;
1032 info->dwEffType = type;
1033 info->dwStaticParams = info->dwDynamicParams;
1034 lstrcpynW( info->tszName, effect_guid_to_string( guid ), MAX_PATH );
1036 return DI_OK;
1039 static BOOL CALLBACK unload_effect_object( IDirectInputEffect *effect, void *context )
1041 IDirectInputEffect_Unload( effect );
1042 return DIENUM_CONTINUE;
1045 static HRESULT hid_joystick_send_force_feedback_command( IDirectInputDevice8W *iface, DWORD command, BOOL unacquire )
1047 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
1048 struct pid_control_report *report = &impl->pid_device_control;
1049 ULONG report_len = impl->caps.OutputReportByteLength;
1050 char *report_buf = impl->output_report_buf;
1051 NTSTATUS status;
1052 USAGE usage;
1053 ULONG count;
1055 TRACE( "iface %p, command %#lx.\n", iface, command );
1057 switch (command)
1059 case DISFFC_RESET: usage = PID_USAGE_DC_DEVICE_RESET; break;
1060 case DISFFC_STOPALL: usage = PID_USAGE_DC_STOP_ALL_EFFECTS; break;
1061 case DISFFC_PAUSE: usage = PID_USAGE_DC_DEVICE_PAUSE; break;
1062 case DISFFC_CONTINUE: usage = PID_USAGE_DC_DEVICE_CONTINUE; break;
1063 case DISFFC_SETACTUATORSON: usage = PID_USAGE_DC_ENABLE_ACTUATORS; break;
1064 case DISFFC_SETACTUATORSOFF: usage = PID_USAGE_DC_DISABLE_ACTUATORS; break;
1067 if (command == DISFFC_RESET)
1069 IDirectInputDevice8_EnumCreatedEffectObjects( iface, unload_effect_object, NULL, 0 );
1070 impl->base.force_feedback_state = DIGFFS_STOPPED | DIGFFS_EMPTY;
1073 count = 1;
1074 status = HidP_InitializeReportForID( HidP_Output, report->id, impl->preparsed, report_buf, report_len );
1075 if (status != HIDP_STATUS_SUCCESS) return status;
1077 status = HidP_SetUsages( HidP_Output, HID_USAGE_PAGE_PID, report->control_coll, &usage,
1078 &count, impl->preparsed, report_buf, report_len );
1079 if (status != HIDP_STATUS_SUCCESS) return status;
1081 if (!WriteFile( impl->device, report_buf, report_len, NULL, NULL )) return DIERR_INPUTLOST;
1082 if (!unacquire && command == DISFFC_RESET) hid_joystick_send_device_gain( iface, impl->base.device_gain );
1084 return DI_OK;
1087 static HRESULT hid_joystick_enum_created_effect_objects( IDirectInputDevice8W *iface,
1088 LPDIENUMCREATEDEFFECTOBJECTSCALLBACK callback,
1089 void *context, DWORD flags )
1091 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
1092 struct hid_joystick_effect *effect, *next;
1094 TRACE( "iface %p, callback %p, context %p, flags %#lx.\n", iface, callback, context, flags );
1096 LIST_FOR_EACH_ENTRY_SAFE(effect, next, &impl->effect_list, struct hid_joystick_effect, entry)
1097 if (callback( &effect->IDirectInputEffect_iface, context ) != DIENUM_CONTINUE) break;
1099 return DI_OK;
1102 struct parse_device_state_params
1104 BYTE old_state[DEVICE_STATE_MAX_SIZE];
1105 BYTE buttons[128];
1106 DWORD time;
1107 DWORD seq;
1110 static BOOL check_device_state_button( struct hid_joystick *impl, struct hid_value_caps *caps,
1111 DIDEVICEOBJECTINSTANCEW *instance, void *data )
1113 IDirectInputDevice8W *iface = &impl->base.IDirectInputDevice8W_iface;
1114 struct parse_device_state_params *params = data;
1115 BYTE old_value, value;
1117 if (instance->wReportId != impl->base.device_state_report_id) return DIENUM_CONTINUE;
1119 value = params->buttons[instance->wUsage - 1];
1120 old_value = params->old_state[instance->dwOfs];
1121 impl->base.device_state[instance->dwOfs] = value;
1122 if (old_value != value)
1123 queue_event( iface, instance->dwType, value, params->time, params->seq );
1125 return DIENUM_CONTINUE;
1128 static LONG sign_extend( ULONG value, struct object_properties *properties )
1130 UINT sign = 1 << (properties->bit_size - 1);
1131 if (sign <= 1 || properties->logical_min >= 0) return value;
1132 return value - ((value & sign) << 1);
1135 static LONG scale_value( ULONG value, struct object_properties *properties )
1137 LONG tmp = sign_extend( value, properties ), log_min, log_max, phy_min, phy_max;
1138 log_min = properties->logical_min;
1139 log_max = properties->logical_max;
1140 phy_min = properties->range_min;
1141 phy_max = properties->range_max;
1143 if (log_min > tmp || log_max < tmp) return -1; /* invalid / null value */
1144 return phy_min + MulDiv( tmp - log_min, phy_max - phy_min, log_max - log_min );
1147 static LONG scale_axis_value( ULONG value, struct object_properties *properties )
1149 LONG tmp = sign_extend( value, properties ), log_ctr, log_min, log_max, phy_ctr, phy_min, phy_max;
1150 ULONG bit_max = (1 << properties->bit_size) - 1;
1152 log_min = properties->logical_min;
1153 log_max = properties->logical_max;
1154 phy_min = properties->range_min;
1155 phy_max = properties->range_max;
1156 /* xinput HID gamepad have bogus logical value range, let's use the bit range instead */
1157 if (log_min == 0 && log_max == -1) log_max = bit_max;
1159 if (phy_min == 0) phy_ctr = phy_max >> 1;
1160 else phy_ctr = round( (phy_min + phy_max) / 2.0 );
1161 if (log_min == 0) log_ctr = log_max >> 1;
1162 else log_ctr = round( (log_min + log_max) / 2.0 );
1164 tmp -= log_ctr;
1165 if (tmp <= 0)
1167 log_max = MulDiv( log_min - log_ctr, properties->deadzone, 10000 );
1168 log_min = MulDiv( log_min - log_ctr, properties->saturation, 10000 );
1169 phy_max = phy_ctr;
1171 else
1173 log_min = MulDiv( log_max - log_ctr, properties->deadzone, 10000 );
1174 log_max = MulDiv( log_max - log_ctr, properties->saturation, 10000 );
1175 phy_min = phy_ctr;
1178 if (tmp <= log_min) return phy_min;
1179 if (tmp >= log_max) return phy_max;
1180 return phy_min + MulDiv( tmp - log_min, phy_max - phy_min, log_max - log_min );
1183 static BOOL read_device_state_value( struct hid_joystick *impl, struct hid_value_caps *caps,
1184 DIDEVICEOBJECTINSTANCEW *instance, void *data )
1186 struct object_properties *properties = impl->base.object_properties + instance->dwOfs / sizeof(LONG);
1187 IDirectInputDevice8W *iface = &impl->base.IDirectInputDevice8W_iface;
1188 ULONG logical_value, report_len = impl->caps.InputReportByteLength;
1189 struct parse_device_state_params *params = data;
1190 char *report_buf = impl->input_report_buf;
1191 LONG old_value, value;
1192 NTSTATUS status;
1194 if (instance->wReportId != impl->base.device_state_report_id) return DIENUM_CONTINUE;
1196 status = HidP_GetUsageValue( HidP_Input, instance->wUsagePage, 0, instance->wUsage,
1197 &logical_value, impl->preparsed, report_buf, report_len );
1198 if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_GetUsageValue %04x:%04x returned %#lx\n",
1199 instance->wUsagePage, instance->wUsage, status );
1200 if (instance->dwType & DIDFT_AXIS) value = scale_axis_value( logical_value, properties );
1201 else value = scale_value( logical_value, properties );
1203 old_value = *(LONG *)(params->old_state + instance->dwOfs);
1204 *(LONG *)(impl->base.device_state + instance->dwOfs) = value;
1205 if (old_value != value)
1206 queue_event( iface, instance->dwType, value, params->time, params->seq );
1208 return DIENUM_CONTINUE;
1211 static HRESULT hid_joystick_read( IDirectInputDevice8W *iface )
1213 static const DIPROPHEADER filter =
1215 .dwSize = sizeof(filter),
1216 .dwHeaderSize = sizeof(filter),
1217 .dwHow = DIPH_DEVICE,
1219 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
1220 ULONG i, index, count, report_len = impl->caps.InputReportByteLength;
1221 DIDATAFORMAT *format = impl->base.device_format;
1222 char *report_buf = impl->input_report_buf;
1223 struct parse_device_state_params params;
1224 struct hid_joystick_effect *effect;
1225 UINT device_state, effect_state;
1226 USAGE_AND_PAGE *usages;
1227 NTSTATUS status;
1228 HRESULT hr;
1229 BOOL ret;
1231 ret = GetOverlappedResult( impl->device, &impl->read_ovl, &count, FALSE );
1233 EnterCriticalSection( &impl->base.crit );
1234 while (ret)
1236 if (TRACE_ON(dinput))
1238 TRACE( "iface %p, size %lu, report:\n", iface, count );
1239 for (i = 0; i < count;)
1241 char buffer[256], *buf = buffer;
1242 buf += sprintf(buf, "%08lx ", i);
1243 do { buf += sprintf(buf, " %02x", (BYTE)report_buf[i] ); }
1244 while (++i % 16 && i < count);
1245 TRACE("%s\n", buffer);
1249 count = impl->usages_count;
1250 memset( impl->usages_buf, 0, count * sizeof(*impl->usages_buf) );
1251 status = HidP_GetUsagesEx( HidP_Input, 0, impl->usages_buf, &count,
1252 impl->preparsed, report_buf, report_len );
1253 if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_GetUsagesEx returned %#lx\n", status );
1255 if (report_buf[0] == impl->base.device_state_report_id)
1257 params.time = GetCurrentTime();
1258 params.seq = impl->base.dinput->evsequence++;
1259 memcpy( params.old_state, impl->base.device_state, format->dwDataSize );
1260 memset( params.buttons, 0, sizeof(params.buttons) );
1261 memset( impl->base.device_state, 0, format->dwDataSize );
1263 while (count--)
1265 usages = impl->usages_buf + count;
1266 if (usages->UsagePage != HID_USAGE_PAGE_BUTTON)
1267 FIXME( "unimplemented usage page %x.\n", usages->UsagePage );
1268 else if (usages->Usage >= 128)
1269 FIXME( "ignoring extraneous button %d.\n", usages->Usage );
1270 else
1271 params.buttons[usages->Usage - 1] = 0x80;
1274 enum_objects( impl, &filter, DIDFT_AXIS | DIDFT_POV, read_device_state_value, &params );
1275 enum_objects( impl, &filter, DIDFT_BUTTON, check_device_state_button, &params );
1276 if (impl->base.hEvent && memcmp( &params.old_state, impl->base.device_state, format->dwDataSize ))
1277 SetEvent( impl->base.hEvent );
1279 else if (report_buf[0] == impl->pid_effect_state.id && is_exclusively_acquired( impl ))
1281 status = HidP_GetUsageValue( HidP_Input, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX,
1282 &index, impl->preparsed, report_buf, report_len );
1283 if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_GetUsageValue EFFECT_BLOCK_INDEX returned %#lx\n", status );
1285 effect_state = 0;
1286 device_state = impl->base.force_feedback_state & DIGFFS_EMPTY;
1287 while (count--)
1289 USAGE_AND_PAGE *button = impl->usages_buf + count;
1290 if (button->UsagePage != HID_USAGE_PAGE_PID)
1291 FIXME( "unimplemented usage page %#04x.\n", button->UsagePage );
1292 else switch (button->Usage)
1294 case PID_USAGE_DEVICE_PAUSED: device_state |= DIGFFS_PAUSED; break;
1295 case PID_USAGE_ACTUATORS_ENABLED: device_state |= DIGFFS_ACTUATORSON; break;
1296 case PID_USAGE_SAFETY_SWITCH: device_state |= DIGFFS_SAFETYSWITCHON; break;
1297 case PID_USAGE_ACTUATOR_OVERRIDE_SWITCH: device_state |= DIGFFS_USERFFSWITCHON; break;
1298 case PID_USAGE_ACTUATOR_POWER: device_state |= DIGFFS_POWERON; break;
1299 case PID_USAGE_EFFECT_PLAYING: effect_state = DIEGES_PLAYING; break;
1300 default: FIXME( "unimplemented usage %#04x\n", button->Usage ); break;
1303 if (!(device_state & DIGFFS_ACTUATORSON)) device_state |= DIGFFS_ACTUATORSOFF;
1304 if (!(device_state & DIGFFS_SAFETYSWITCHON) && impl->pid_effect_state.safety_switch_caps)
1305 device_state |= DIGFFS_SAFETYSWITCHOFF;
1306 if (!(device_state & DIGFFS_USERFFSWITCHON) && impl->pid_effect_state.actuator_override_switch_caps)
1307 device_state |= DIGFFS_USERFFSWITCHOFF;
1308 if (!(device_state & DIGFFS_POWERON) && impl->pid_effect_state.actuator_power_caps)
1309 device_state |= DIGFFS_POWEROFF;
1311 TRACE( "effect %lu state %#x, device state %#x\n", index, effect_state, device_state );
1313 LIST_FOR_EACH_ENTRY( effect, &impl->effect_list, struct hid_joystick_effect, entry )
1314 if (effect->index == index) effect->status = effect_state;
1315 impl->base.force_feedback_state = device_state;
1318 memset( &impl->read_ovl, 0, sizeof(impl->read_ovl) );
1319 impl->read_ovl.hEvent = impl->base.read_event;
1320 ret = ReadFile( impl->device, report_buf, report_len, &count, &impl->read_ovl );
1323 if (GetLastError() == ERROR_IO_PENDING || GetLastError() == ERROR_OPERATION_ABORTED) hr = DI_OK;
1324 else
1326 WARN( "GetOverlappedResult/ReadFile failed, error %lu\n", GetLastError() );
1327 CloseHandle(impl->device);
1328 impl->device = INVALID_HANDLE_VALUE;
1329 hr = DIERR_INPUTLOST;
1331 LeaveCriticalSection( &impl->base.crit );
1333 return hr;
1336 struct enum_objects_params
1338 LPDIENUMDEVICEOBJECTSCALLBACKW callback;
1339 void *context;
1342 static BOOL enum_objects_callback( struct hid_joystick *impl, struct hid_value_caps *caps,
1343 DIDEVICEOBJECTINSTANCEW *instance, void *data )
1345 struct enum_objects_params *params = data;
1346 if (instance->wUsagePage == HID_USAGE_PAGE_PID && !(instance->dwType & DIDFT_NODATA))
1347 return DIENUM_CONTINUE;
1348 return params->callback( instance, params->context );
1351 static HRESULT hid_joystick_enum_objects( IDirectInputDevice8W *iface, const DIPROPHEADER *filter,
1352 DWORD flags, LPDIENUMDEVICEOBJECTSCALLBACKW callback, void *context )
1354 struct enum_objects_params params = {.callback = callback, .context = context};
1355 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
1356 return enum_objects( impl, filter, flags, enum_objects_callback, &params );
1359 static const struct dinput_device_vtbl hid_joystick_vtbl =
1361 hid_joystick_release,
1362 NULL,
1363 hid_joystick_read,
1364 hid_joystick_acquire,
1365 hid_joystick_unacquire,
1366 hid_joystick_enum_objects,
1367 hid_joystick_get_property,
1368 hid_joystick_get_effect_info,
1369 hid_joystick_create_effect,
1370 hid_joystick_send_force_feedback_command,
1371 hid_joystick_send_device_gain,
1372 hid_joystick_enum_created_effect_objects,
1375 static DWORD device_type_for_version( DWORD type, DWORD version )
1377 if (version >= 0x0800) return type;
1379 switch (GET_DIDEVICE_TYPE( type ))
1381 case DI8DEVTYPE_JOYSTICK:
1382 if (GET_DIDEVICE_SUBTYPE( type ) == DI8DEVTYPEJOYSTICK_LIMITED)
1383 return DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_UNKNOWN << 8) | DIDEVTYPE_HID;
1384 return DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8) | DIDEVTYPE_HID;
1386 case DI8DEVTYPE_GAMEPAD:
1387 return DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_GAMEPAD << 8) | DIDEVTYPE_HID;
1389 case DI8DEVTYPE_DRIVING:
1390 return DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_WHEEL << 8) | DIDEVTYPE_HID;
1392 case DI8DEVTYPE_FLIGHT:
1393 return DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_FLIGHTSTICK << 8) | DIDEVTYPE_HID;
1395 case DI8DEVTYPE_SUPPLEMENTAL:
1396 if (GET_DIDEVICE_SUBTYPE( type ) == DI8DEVTYPESUPPLEMENTAL_HEADTRACKER)
1397 return DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_HEADTRACKER << 8) | DIDEVTYPE_HID;
1398 if (GET_DIDEVICE_SUBTYPE( type ) == DI8DEVTYPESUPPLEMENTAL_RUDDERPEDALS)
1399 return DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_RUDDER << 8) | DIDEVTYPE_HID;
1400 return DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_UNKNOWN << 8) | DIDEVTYPE_HID;
1402 case DI8DEVTYPE_1STPERSON:
1403 return DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_UNKNOWN << 8) | DIDEVTYPE_HID;
1405 default:
1406 return DIDEVTYPE_DEVICE | DIDEVTYPE_HID;
1410 static BOOL hid_joystick_device_try_open( UINT32 handle, const WCHAR *path, HANDLE *device,
1411 PHIDP_PREPARSED_DATA *preparsed, HIDD_ATTRIBUTES *attrs,
1412 HIDP_CAPS *caps, DIDEVICEINSTANCEW *instance, DWORD version )
1414 BOOL has_accelerator, has_brake, has_clutch;
1415 PHIDP_PREPARSED_DATA preparsed_data = NULL;
1416 DWORD type = 0, button_count = 0;
1417 HIDP_BUTTON_CAPS buttons[10];
1418 HIDP_VALUE_CAPS value;
1419 HANDLE device_file;
1420 NTSTATUS status;
1421 USHORT count;
1423 device_file = CreateFileW( path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
1424 NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, 0 );
1425 if (device_file == INVALID_HANDLE_VALUE) return FALSE;
1427 if (!HidD_GetPreparsedData( device_file, &preparsed_data )) goto failed;
1428 if (!HidD_GetAttributes( device_file, attrs )) goto failed;
1429 if (HidP_GetCaps( preparsed_data, caps ) != HIDP_STATUS_SUCCESS) goto failed;
1431 if (caps->UsagePage == HID_USAGE_PAGE_GAME) FIXME( "game usage page not implemented!\n" );
1432 if (caps->UsagePage == HID_USAGE_PAGE_SIMULATION) FIXME( "simulation usage page not implemented!\n" );
1433 if (caps->UsagePage != HID_USAGE_PAGE_GENERIC) goto failed;
1434 if (caps->Usage != HID_USAGE_GENERIC_GAMEPAD && caps->Usage != HID_USAGE_GENERIC_JOYSTICK) goto failed;
1436 if (!HidD_GetProductString( device_file, instance->tszInstanceName, MAX_PATH * sizeof(WCHAR) )) goto failed;
1437 if (!HidD_GetProductString( device_file, instance->tszProductName, MAX_PATH * sizeof(WCHAR) )) goto failed;
1439 instance->guidInstance = hid_joystick_guid;
1440 instance->guidInstance.Data1 ^= handle;
1441 instance->guidProduct = dinput_pidvid_guid;
1442 instance->guidProduct.Data1 = MAKELONG( attrs->VendorID, attrs->ProductID );
1443 instance->guidFFDriver = GUID_NULL;
1444 instance->wUsagePage = caps->UsagePage;
1445 instance->wUsage = caps->Usage;
1447 count = ARRAY_SIZE(buttons);
1448 status = HidP_GetSpecificButtonCaps( HidP_Output, HID_USAGE_PAGE_PID, 0,
1449 PID_USAGE_DC_DEVICE_RESET, buttons, &count, preparsed_data );
1450 if (status == HIDP_STATUS_SUCCESS && count > 0)
1451 instance->guidFFDriver = IID_IDirectInputPIDDriver;
1453 count = ARRAY_SIZE(buttons);
1454 status = HidP_GetSpecificButtonCaps( HidP_Input, HID_USAGE_PAGE_BUTTON, 0, 0, buttons, &count, preparsed_data );
1455 if (status != HIDP_STATUS_SUCCESS) count = button_count = 0;
1456 while (count--)
1458 if (!buttons[count].IsRange) button_count += 1;
1459 else button_count += buttons[count].Range.UsageMax - buttons[count].Range.UsageMin + 1;
1462 switch (caps->Usage)
1464 case HID_USAGE_GENERIC_GAMEPAD:
1465 type = DI8DEVTYPE_GAMEPAD | DIDEVTYPE_HID;
1466 if (button_count < 6) type |= DI8DEVTYPEGAMEPAD_LIMITED << 8;
1467 else type |= DI8DEVTYPEGAMEPAD_STANDARD << 8;
1468 break;
1469 case HID_USAGE_GENERIC_JOYSTICK:
1470 type = DI8DEVTYPE_JOYSTICK | DIDEVTYPE_HID;
1471 if (button_count < 5) type |= DI8DEVTYPEJOYSTICK_LIMITED << 8;
1472 else type |= DI8DEVTYPEJOYSTICK_STANDARD << 8;
1474 count = 1;
1475 status = HidP_GetSpecificValueCaps( HidP_Input, HID_USAGE_PAGE_GENERIC, 0,
1476 HID_USAGE_GENERIC_Z, &value, &count, preparsed_data );
1477 if (status != HIDP_STATUS_SUCCESS || !count)
1478 type = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_LIMITED << 8) | DIDEVTYPE_HID;
1480 count = 1;
1481 status = HidP_GetSpecificValueCaps( HidP_Input, HID_USAGE_PAGE_GENERIC, 0,
1482 HID_USAGE_GENERIC_HATSWITCH, &value, &count, preparsed_data );
1483 if (status != HIDP_STATUS_SUCCESS || !count)
1484 type = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_LIMITED << 8) | DIDEVTYPE_HID;
1486 break;
1489 count = 1;
1490 status = HidP_GetSpecificValueCaps( HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_X,
1491 &value, &count, preparsed_data );
1492 if (status != HIDP_STATUS_SUCCESS || !count)
1493 type = DI8DEVTYPE_SUPPLEMENTAL | (DI8DEVTYPESUPPLEMENTAL_UNKNOWN << 8) | DIDEVTYPE_HID;
1495 count = 1;
1496 status = HidP_GetSpecificValueCaps( HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Y,
1497 &value, &count, preparsed_data );
1498 if (status != HIDP_STATUS_SUCCESS || !count)
1499 type = DI8DEVTYPE_SUPPLEMENTAL | (DI8DEVTYPESUPPLEMENTAL_UNKNOWN << 8) | DIDEVTYPE_HID;
1501 count = 1;
1502 status = HidP_GetSpecificValueCaps( HidP_Input, HID_USAGE_PAGE_SIMULATION, 0, HID_USAGE_SIMULATION_STEERING,
1503 &value, &count, preparsed_data );
1504 if (status == HIDP_STATUS_SUCCESS && count)
1506 type = DI8DEVTYPE_DRIVING | DIDEVTYPE_HID;
1508 count = 1;
1509 status = HidP_GetSpecificValueCaps( HidP_Input, HID_USAGE_PAGE_SIMULATION, 0, HID_USAGE_SIMULATION_ACCELERATOR,
1510 &value, &count, preparsed_data );
1511 has_accelerator = (status == HIDP_STATUS_SUCCESS && count);
1513 count = 1;
1514 status = HidP_GetSpecificValueCaps( HidP_Input, HID_USAGE_PAGE_SIMULATION, 0, HID_USAGE_SIMULATION_BRAKE,
1515 &value, &count, preparsed_data );
1516 has_brake = (status == HIDP_STATUS_SUCCESS && count);
1518 count = 1;
1519 status = HidP_GetSpecificValueCaps( HidP_Input, HID_USAGE_PAGE_SIMULATION, 0, HID_USAGE_SIMULATION_CLUTCH,
1520 &value, &count, preparsed_data );
1521 has_clutch = (status == HIDP_STATUS_SUCCESS && count);
1523 if (has_accelerator && has_brake && has_clutch)
1524 type |= (DI8DEVTYPEDRIVING_THREEPEDALS << 8);
1525 else if (has_accelerator && has_brake)
1526 type |= (DI8DEVTYPEDRIVING_DUALPEDALS << 8);
1527 else
1528 type |= (DI8DEVTYPEDRIVING_LIMITED << 8);
1531 instance->dwDevType = device_type_for_version( type, version );
1533 *device = device_file;
1534 *preparsed = preparsed_data;
1535 return TRUE;
1537 failed:
1538 CloseHandle( device_file );
1539 HidD_FreePreparsedData( preparsed_data );
1540 return FALSE;
1543 static HRESULT hid_joystick_device_open( int index, DIDEVICEINSTANCEW *filter, WCHAR *device_path,
1544 HANDLE *device, PHIDP_PREPARSED_DATA *preparsed,
1545 HIDD_ATTRIBUTES *attrs, HIDP_CAPS *caps, DWORD version )
1547 char buffer[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + MAX_PATH * sizeof(WCHAR)];
1548 SP_DEVICE_INTERFACE_DETAIL_DATA_W *detail = (void *)buffer;
1549 SP_DEVICE_INTERFACE_DATA iface = {.cbSize = sizeof(iface)};
1550 SP_DEVINFO_DATA devinfo = {.cbSize = sizeof(devinfo)};
1551 DIDEVICEINSTANCEW instance = *filter;
1552 WCHAR device_id[MAX_PATH], *tmp;
1553 HDEVINFO set, xi_set;
1554 UINT32 i = 0, handle;
1555 BOOL override;
1556 DWORD type;
1557 GUID hid;
1559 TRACE( "index %d, product %s, instance %s\n", index, debugstr_guid( &filter->guidProduct ),
1560 debugstr_guid( &filter->guidInstance ) );
1562 HidD_GetHidGuid( &hid );
1564 set = SetupDiGetClassDevsW( &hid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT );
1565 if (set == INVALID_HANDLE_VALUE) return DIERR_DEVICENOTREG;
1566 xi_set = SetupDiGetClassDevsW( &GUID_DEVINTERFACE_WINEXINPUT, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT );
1568 *device = NULL;
1569 *preparsed = NULL;
1570 while (SetupDiEnumDeviceInterfaces( set, NULL, &hid, i++, &iface ))
1572 detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
1573 if (!SetupDiGetDeviceInterfaceDetailW( set, &iface, detail, sizeof(buffer), NULL, &devinfo ))
1574 continue;
1575 if (!SetupDiGetDevicePropertyW( set, &devinfo, &DEVPROPKEY_HID_HANDLE, &type,
1576 (BYTE *)&handle, sizeof(handle), NULL, 0 ) ||
1577 type != DEVPROP_TYPE_UINT32)
1578 continue;
1579 if (!hid_joystick_device_try_open( handle, detail->DevicePath, device, preparsed,
1580 attrs, caps, &instance, version ))
1581 continue;
1583 if (device_instance_is_disabled( &instance, &override ))
1584 goto next;
1586 if (override && SetupDiGetDeviceInstanceIdW( set, &devinfo, device_id, MAX_PATH, NULL ) &&
1587 (tmp = wcsstr( device_id, L"&IG_" )))
1589 memcpy( tmp, L"&XI_", sizeof(L"&XI_") - sizeof(WCHAR) );
1590 if (!SetupDiOpenDeviceInfoW( xi_set, device_id, NULL, 0, &devinfo ))
1591 goto next;
1592 if (!SetupDiEnumDeviceInterfaces( xi_set, &devinfo, &GUID_DEVINTERFACE_WINEXINPUT, 0, &iface ))
1593 goto next;
1594 if (!SetupDiGetDeviceInterfaceDetailW( xi_set, &iface, detail, sizeof(buffer), NULL, &devinfo ))
1595 goto next;
1597 CloseHandle( *device );
1598 HidD_FreePreparsedData( *preparsed );
1599 if (!hid_joystick_device_try_open( handle, detail->DevicePath, device, preparsed,
1600 attrs, caps, &instance, version ))
1601 continue;
1604 /* enumerate device by GUID */
1605 if (index < 0 && IsEqualGUID( &filter->guidProduct, &instance.guidProduct )) break;
1606 if (index < 0 && IsEqualGUID( &filter->guidInstance, &instance.guidInstance )) break;
1608 /* enumerate all devices */
1609 if (index >= 0 && !index--) break;
1611 next:
1612 CloseHandle( *device );
1613 HidD_FreePreparsedData( *preparsed );
1614 *device = NULL;
1615 *preparsed = NULL;
1618 if (xi_set != INVALID_HANDLE_VALUE) SetupDiDestroyDeviceInfoList( xi_set );
1619 SetupDiDestroyDeviceInfoList( set );
1620 if (!*device || !*preparsed) return DIERR_DEVICENOTREG;
1622 lstrcpynW( device_path, detail->DevicePath, MAX_PATH );
1623 *filter = instance;
1624 return DI_OK;
1627 HRESULT hid_joystick_enum_device( DWORD type, DWORD flags, DIDEVICEINSTANCEW *instance, DWORD version, int index )
1629 HIDD_ATTRIBUTES attrs = {.Size = sizeof(attrs)};
1630 PHIDP_PREPARSED_DATA preparsed;
1631 WCHAR device_path[MAX_PATH];
1632 HIDP_CAPS caps;
1633 HANDLE device;
1634 HRESULT hr;
1636 TRACE( "type %#lx, flags %#lx, instance %p, version %#lx, index %d\n", type, flags, instance, version, index );
1638 hr = hid_joystick_device_open( index, instance, device_path, &device, &preparsed,
1639 &attrs, &caps, version );
1640 if (hr != DI_OK) return hr;
1642 HidD_FreePreparsedData( preparsed );
1643 CloseHandle( device );
1645 TRACE( "found device %s, usage %04x:%04x, product %s, instance %s, name %s\n", debugstr_w(device_path),
1646 instance->wUsagePage, instance->wUsage, debugstr_guid( &instance->guidProduct ),
1647 debugstr_guid( &instance->guidInstance ), debugstr_w(instance->tszInstanceName) );
1649 return DI_OK;
1652 static BOOL init_object_properties( struct hid_joystick *impl, struct hid_value_caps *caps,
1653 DIDEVICEOBJECTINSTANCEW *instance, void *data )
1655 struct object_properties *properties = impl->base.object_properties + instance->dwOfs / sizeof(LONG);
1656 LONG tmp;
1658 properties->bit_size = caps->bit_size;
1659 properties->physical_min = caps->physical_min;
1660 properties->physical_max = caps->physical_max;
1661 properties->logical_min = caps->logical_min;
1662 properties->logical_max = caps->logical_max;
1664 if (instance->dwType & DIDFT_AXIS) properties->range_max = 65535;
1665 else
1667 properties->range_max = 36000;
1668 tmp = caps->logical_max - caps->logical_min;
1669 if (tmp > 0) properties->range_max -= 36000 / (tmp + 1);
1672 properties->saturation = 10000;
1673 return DIENUM_CONTINUE;
1676 static BOOL init_pid_reports( struct hid_joystick *impl, struct hid_value_caps *caps,
1677 DIDEVICEOBJECTINSTANCEW *instance, void *data )
1679 struct pid_set_constant_force *set_constant_force = &impl->pid_set_constant_force;
1680 struct pid_set_ramp_force *set_ramp_force = &impl->pid_set_ramp_force;
1681 struct pid_control_report *device_control = &impl->pid_device_control;
1682 struct pid_control_report *effect_control = &impl->pid_effect_control;
1683 struct pid_effect_update *effect_update = &impl->pid_effect_update;
1684 struct pid_set_condition *set_condition = &impl->pid_set_condition;
1685 struct pid_set_periodic *set_periodic = &impl->pid_set_periodic;
1686 struct pid_set_envelope *set_envelope = &impl->pid_set_envelope;
1687 struct pid_device_gain *device_gain = &impl->pid_device_gain;
1688 struct pid_device_pool *device_pool = &impl->pid_device_pool;
1689 struct pid_block_free *block_free = &impl->pid_block_free;
1690 struct pid_block_load *block_load = &impl->pid_block_load;
1691 struct pid_new_effect *new_effect = &impl->pid_new_effect;
1692 struct pid_effect_state *effect_state = &impl->pid_effect_state;
1694 #define SET_COLLECTION( rep ) \
1695 do \
1697 if (rep->collection) FIXME( "duplicate " #rep " report!\n" ); \
1698 else rep->collection = DIDFT_GETINSTANCE( instance->dwType ); \
1699 } while (0)
1701 #define SET_SUB_COLLECTION( rep, sub ) \
1702 do { \
1703 if (instance->wCollectionNumber != rep->collection) \
1704 FIXME( "unexpected " #rep "." #sub " parent!\n" ); \
1705 else if (rep->sub) \
1706 FIXME( "duplicate " #rep "." #sub " collection!\n" ); \
1707 else \
1708 rep->sub = DIDFT_GETINSTANCE( instance->dwType ); \
1709 } while (0)
1711 if (instance->wUsagePage == HID_USAGE_PAGE_PID)
1713 switch (instance->wUsage)
1715 case PID_USAGE_DEVICE_CONTROL_REPORT: SET_COLLECTION( device_control ); break;
1716 case PID_USAGE_EFFECT_OPERATION_REPORT: SET_COLLECTION( effect_control ); break;
1717 case PID_USAGE_SET_EFFECT_REPORT: SET_COLLECTION( effect_update ); break;
1718 case PID_USAGE_SET_PERIODIC_REPORT: SET_COLLECTION( set_periodic ); break;
1719 case PID_USAGE_SET_ENVELOPE_REPORT: SET_COLLECTION( set_envelope ); break;
1720 case PID_USAGE_SET_CONDITION_REPORT: SET_COLLECTION( set_condition ); break;
1721 case PID_USAGE_SET_CONSTANT_FORCE_REPORT: SET_COLLECTION( set_constant_force ); break;
1722 case PID_USAGE_SET_RAMP_FORCE_REPORT: SET_COLLECTION( set_ramp_force ); break;
1723 case PID_USAGE_DEVICE_GAIN_REPORT: SET_COLLECTION( device_gain ); break;
1724 case PID_USAGE_POOL_REPORT: SET_COLLECTION( device_pool ); break;
1725 case PID_USAGE_BLOCK_FREE_REPORT: SET_COLLECTION( block_free ); break;
1726 case PID_USAGE_BLOCK_LOAD_REPORT: SET_COLLECTION( block_load ); break;
1727 case PID_USAGE_CREATE_NEW_EFFECT_REPORT: SET_COLLECTION( new_effect ); break;
1728 case PID_USAGE_STATE_REPORT: SET_COLLECTION( effect_state ); break;
1730 case PID_USAGE_DEVICE_CONTROL: SET_SUB_COLLECTION( device_control, control_coll ); break;
1731 case PID_USAGE_EFFECT_OPERATION: SET_SUB_COLLECTION( effect_control, control_coll ); break;
1732 case PID_USAGE_EFFECT_TYPE:
1733 if (instance->wCollectionNumber == effect_update->collection)
1734 SET_SUB_COLLECTION( effect_update, type_coll );
1735 else if (instance->wCollectionNumber == new_effect->collection)
1736 SET_SUB_COLLECTION( new_effect, type_coll );
1737 break;
1738 case PID_USAGE_AXES_ENABLE: SET_SUB_COLLECTION( effect_update, axes_coll ); break;
1739 case PID_USAGE_DIRECTION: SET_SUB_COLLECTION( effect_update, direction_coll ); break;
1740 case PID_USAGE_BLOCK_LOAD_STATUS: SET_SUB_COLLECTION( block_load, status_coll ); break;
1744 #undef SET_SUB_COLLECTION
1745 #undef SET_COLLECTION
1747 return DIENUM_CONTINUE;
1750 static BOOL init_pid_caps( struct hid_joystick *impl, struct hid_value_caps *caps,
1751 DIDEVICEOBJECTINSTANCEW *instance, void *data )
1753 struct pid_set_constant_force *set_constant_force = &impl->pid_set_constant_force;
1754 struct pid_set_ramp_force *set_ramp_force = &impl->pid_set_ramp_force;
1755 struct pid_control_report *device_control = &impl->pid_device_control;
1756 struct pid_control_report *effect_control = &impl->pid_effect_control;
1757 struct pid_effect_update *effect_update = &impl->pid_effect_update;
1758 struct pid_set_condition *set_condition = &impl->pid_set_condition;
1759 struct pid_set_periodic *set_periodic = &impl->pid_set_periodic;
1760 struct pid_set_envelope *set_envelope = &impl->pid_set_envelope;
1761 struct pid_device_gain *device_gain = &impl->pid_device_gain;
1762 struct pid_device_pool *device_pool = &impl->pid_device_pool;
1763 struct pid_block_free *block_free = &impl->pid_block_free;
1764 struct pid_block_load *block_load = &impl->pid_block_load;
1765 struct pid_new_effect *new_effect = &impl->pid_new_effect;
1766 struct pid_effect_state *effect_state = &impl->pid_effect_state;
1768 #define SET_REPORT_ID( rep ) \
1769 do \
1771 if (!rep->id) \
1772 rep->id = instance->wReportId; \
1773 else if (rep->id != instance->wReportId) \
1774 FIXME( "multiple " #rep " report ids!\n" ); \
1775 } while (0)
1777 if (!instance->wCollectionNumber)
1778 return DIENUM_CONTINUE;
1780 if (instance->wCollectionNumber == effect_state->collection)
1782 SET_REPORT_ID( effect_state );
1783 if (instance->wUsage == PID_USAGE_SAFETY_SWITCH)
1784 effect_state->safety_switch_caps = caps;
1785 if (instance->wUsage == PID_USAGE_ACTUATOR_POWER)
1786 effect_state->actuator_power_caps = caps;
1787 if (instance->wUsage == PID_USAGE_ACTUATOR_OVERRIDE_SWITCH)
1788 effect_state->actuator_override_switch_caps = caps;
1791 if (!(instance->dwType & DIDFT_OUTPUT)) return DIENUM_CONTINUE;
1793 if (instance->wCollectionNumber == device_control->control_coll)
1794 SET_REPORT_ID( device_control );
1795 if (instance->wCollectionNumber == effect_control->control_coll)
1796 SET_REPORT_ID( effect_control );
1797 if (instance->wCollectionNumber == effect_update->type_coll)
1798 SET_REPORT_ID( effect_update );
1799 if (instance->wCollectionNumber == effect_update->collection)
1801 SET_REPORT_ID( effect_update );
1802 if (instance->wUsage == PID_USAGE_DURATION)
1803 effect_update->duration_caps = caps;
1804 if (instance->wUsage == PID_USAGE_GAIN)
1806 caps->physical_min = 0;
1807 caps->physical_max = 10000;
1808 effect_update->gain_caps = caps;
1810 if (instance->wUsage == PID_USAGE_SAMPLE_PERIOD)
1811 effect_update->sample_period_caps = caps;
1812 if (instance->wUsage == PID_USAGE_START_DELAY)
1813 effect_update->start_delay_caps = caps;
1814 if (instance->wUsage == PID_USAGE_TRIGGER_BUTTON)
1815 effect_update->trigger_button_caps = caps;
1816 if (instance->wUsage == PID_USAGE_TRIGGER_REPEAT_INTERVAL)
1817 effect_update->trigger_repeat_interval_caps = caps;
1819 if (instance->wCollectionNumber == effect_update->axes_coll)
1821 SET_REPORT_ID( effect_update );
1822 caps->physical_min = 0;
1823 caps->physical_max = 36000;
1824 if (effect_update->axis_count >= 6) FIXME( "more than 6 PID axes detected\n" );
1825 else effect_update->axis_caps[effect_update->axis_count] = caps;
1826 effect_update->axis_count++;
1828 if (instance->wCollectionNumber == effect_update->direction_coll)
1830 SET_REPORT_ID( effect_update );
1831 caps->physical_min = 0;
1832 caps->physical_max = 35900;
1833 if (effect_update->direction_count >= 6) FIXME( "more than 6 PID directions detected\n" );
1834 else effect_update->direction_caps[effect_update->direction_count] = caps;
1835 effect_update->direction_count++;
1837 if (instance->wCollectionNumber == set_periodic->collection)
1839 SET_REPORT_ID( set_periodic );
1840 if (instance->wUsage == PID_USAGE_MAGNITUDE)
1842 caps->physical_min = 0;
1843 caps->physical_max = 10000;
1844 set_periodic->magnitude_caps = caps;
1846 if (instance->wUsage == PID_USAGE_PERIOD)
1847 set_periodic->period_caps = caps;
1848 if (instance->wUsage == PID_USAGE_PHASE)
1850 caps->physical_min = 0;
1851 caps->physical_max = 35900;
1852 set_periodic->phase_caps = caps;
1854 if (instance->wUsage == PID_USAGE_OFFSET)
1856 caps->physical_min = -10000;
1857 caps->physical_max = 10000;
1858 set_periodic->offset_caps = caps;
1861 if (instance->wCollectionNumber == set_envelope->collection)
1863 SET_REPORT_ID( set_envelope );
1864 if (instance->wUsage == PID_USAGE_ATTACK_LEVEL)
1866 caps->physical_min = 0;
1867 caps->physical_max = 10000;
1868 set_envelope->attack_level_caps = caps;
1870 if (instance->wUsage == PID_USAGE_ATTACK_TIME)
1871 set_envelope->attack_time_caps = caps;
1872 if (instance->wUsage == PID_USAGE_FADE_LEVEL)
1874 caps->physical_min = 0;
1875 caps->physical_max = 10000;
1876 set_envelope->fade_level_caps = caps;
1878 if (instance->wUsage == PID_USAGE_FADE_TIME)
1879 set_envelope->fade_time_caps = caps;
1881 if (instance->wCollectionNumber == set_condition->collection)
1883 SET_REPORT_ID( set_condition );
1884 if (instance->wUsage == PID_USAGE_CP_OFFSET)
1886 caps->physical_min = -10000;
1887 caps->physical_max = 10000;
1888 set_condition->center_point_offset_caps = caps;
1890 if (instance->wUsage == PID_USAGE_POSITIVE_COEFFICIENT)
1892 caps->physical_min = -10000;
1893 caps->physical_max = 10000;
1894 set_condition->positive_coefficient_caps = caps;
1896 if (instance->wUsage == PID_USAGE_NEGATIVE_COEFFICIENT)
1898 caps->physical_min = -10000;
1899 caps->physical_max = 10000;
1900 set_condition->negative_coefficient_caps = caps;
1902 if (instance->wUsage == PID_USAGE_POSITIVE_SATURATION)
1904 caps->physical_min = 0;
1905 caps->physical_max = 10000;
1906 set_condition->positive_saturation_caps = caps;
1908 if (instance->wUsage == PID_USAGE_NEGATIVE_SATURATION)
1910 caps->physical_min = 0;
1911 caps->physical_max = 10000;
1912 set_condition->negative_saturation_caps = caps;
1914 if (instance->wUsage == PID_USAGE_DEAD_BAND)
1916 caps->physical_min = 0;
1917 caps->physical_max = 10000;
1918 set_condition->dead_band_caps = caps;
1921 if (instance->wCollectionNumber == set_constant_force->collection)
1923 SET_REPORT_ID( set_constant_force );
1924 if (instance->wUsage == PID_USAGE_MAGNITUDE)
1926 caps->physical_min = -10000;
1927 caps->physical_max = 10000;
1928 set_constant_force->magnitude_caps = caps;
1931 if (instance->wCollectionNumber == set_ramp_force->collection)
1933 SET_REPORT_ID( set_ramp_force );
1934 if (instance->wUsage == PID_USAGE_RAMP_START)
1936 caps->physical_min = -10000;
1937 caps->physical_max = 10000;
1938 set_ramp_force->start_caps = caps;
1940 if (instance->wUsage == PID_USAGE_RAMP_END)
1942 caps->physical_min = -10000;
1943 caps->physical_max = 10000;
1944 set_ramp_force->end_caps = caps;
1947 if (instance->wCollectionNumber == device_gain->collection)
1949 SET_REPORT_ID( device_gain );
1950 if (instance->wUsage == PID_USAGE_DEVICE_GAIN)
1952 caps->physical_min = 0;
1953 caps->physical_max = 10000;
1954 device_gain->device_gain_caps = caps;
1957 if (instance->wCollectionNumber == device_pool->collection)
1959 SET_REPORT_ID( device_pool );
1960 if (instance->wUsage == PID_USAGE_DEVICE_MANAGED_POOL)
1961 device_pool->device_managed_caps = caps;
1963 if (instance->wCollectionNumber == block_free->collection)
1964 SET_REPORT_ID( block_free );
1965 if (instance->wCollectionNumber == block_load->collection)
1966 SET_REPORT_ID( block_load );
1967 if (instance->wCollectionNumber == block_load->status_coll)
1968 SET_REPORT_ID( block_load );
1969 if (instance->wCollectionNumber == new_effect->collection)
1970 SET_REPORT_ID( new_effect );
1971 if (instance->wCollectionNumber == new_effect->type_coll)
1972 SET_REPORT_ID( new_effect );
1974 #undef SET_REPORT_ID
1976 return DIENUM_CONTINUE;
1979 HRESULT hid_joystick_create_device( struct dinput *dinput, const GUID *guid, IDirectInputDevice8W **out )
1981 static const DIPROPHEADER filter =
1983 .dwSize = sizeof(filter),
1984 .dwHeaderSize = sizeof(filter),
1985 .dwHow = DIPH_DEVICE,
1987 DIDEVICEINSTANCEW instance =
1989 .dwSize = sizeof(instance),
1990 .guidProduct = *guid,
1991 .guidInstance = *guid
1993 DIPROPRANGE range =
1995 .diph =
1997 .dwSize = sizeof(range),
1998 .dwHeaderSize = sizeof(DIPROPHEADER),
1999 .dwHow = DIPH_DEVICE,
2002 HIDD_ATTRIBUTES attrs = {.Size = sizeof(attrs)};
2003 struct object_properties *object_properties;
2004 struct hid_preparsed_data *preparsed;
2005 struct hid_joystick *impl = NULL;
2006 USAGE_AND_PAGE *usages;
2007 char *buffer;
2008 HRESULT hr;
2009 DWORD size;
2011 TRACE( "dinput %p, guid %s, out %p\n", dinput, debugstr_guid( guid ), out );
2013 *out = NULL;
2014 instance.guidProduct.Data1 = dinput_pidvid_guid.Data1;
2015 instance.guidInstance.Data1 = hid_joystick_guid.Data1;
2016 if (IsEqualGUID( &dinput_pidvid_guid, &instance.guidProduct ))
2017 instance.guidProduct = *guid;
2018 else if (IsEqualGUID( &hid_joystick_guid, &instance.guidInstance ))
2019 instance.guidInstance = *guid;
2020 else
2021 return DIERR_DEVICENOTREG;
2023 hr = dinput_device_alloc( sizeof(struct hid_joystick), &hid_joystick_vtbl, guid, dinput, (void **)&impl );
2024 if (FAILED(hr)) return hr;
2025 impl->base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": hid_joystick.base.crit");
2026 impl->base.dwCoopLevel = DISCL_NONEXCLUSIVE | DISCL_BACKGROUND;
2027 impl->base.read_event = CreateEventW( NULL, TRUE, FALSE, NULL );
2028 impl->internal_ref = 1;
2030 hr = hid_joystick_device_open( -1, &instance, impl->device_path, &impl->device, &impl->preparsed,
2031 &attrs, &impl->caps, dinput->dwVersion );
2032 if (hr != DI_OK) goto failed;
2034 impl->base.instance = instance;
2035 impl->base.caps.dwDevType = instance.dwDevType;
2036 impl->attrs = attrs;
2037 list_init( &impl->effect_list );
2039 preparsed = (struct hid_preparsed_data *)impl->preparsed;
2040 size = preparsed->input_caps_count * sizeof(struct object_properties);
2041 if (!(object_properties = calloc( 1, size ))) goto failed;
2042 impl->base.object_properties = object_properties;
2043 enum_objects( impl, &filter, DIDFT_AXIS | DIDFT_POV, init_object_properties, NULL );
2045 size = impl->caps.InputReportByteLength;
2046 if (!(buffer = malloc( size ))) goto failed;
2047 impl->input_report_buf = buffer;
2048 size = impl->caps.OutputReportByteLength;
2049 if (!(buffer = malloc( size ))) goto failed;
2050 impl->output_report_buf = buffer;
2051 size = impl->caps.FeatureReportByteLength;
2052 if (!(buffer = malloc( size ))) goto failed;
2053 impl->feature_report_buf = buffer;
2054 impl->usages_count = HidP_MaxUsageListLength( HidP_Input, 0, impl->preparsed );
2055 size = impl->usages_count * sizeof(USAGE_AND_PAGE);
2056 if (!(usages = malloc( size ))) goto failed;
2057 impl->usages_buf = usages;
2059 enum_objects( impl, &filter, DIDFT_COLLECTION, init_pid_reports, NULL );
2060 enum_objects( impl, &filter, DIDFT_NODATA | DIDFT_BUTTON | DIDFT_AXIS, init_pid_caps, NULL );
2062 TRACE( "device control id %u, coll %u, control coll %u\n", impl->pid_device_control.id,
2063 impl->pid_device_control.collection, impl->pid_device_control.control_coll );
2064 TRACE( "effect control id %u, coll %u\n", impl->pid_effect_control.id, impl->pid_effect_control.collection );
2065 TRACE( "effect update id %u, coll %u, type_coll %u\n", impl->pid_effect_update.id,
2066 impl->pid_effect_update.collection, impl->pid_effect_update.type_coll );
2067 TRACE( "set periodic id %u, coll %u\n", impl->pid_set_periodic.id, impl->pid_set_periodic.collection );
2068 TRACE( "set envelope id %u, coll %u\n", impl->pid_set_envelope.id, impl->pid_set_envelope.collection );
2069 TRACE( "set condition id %u, coll %u\n", impl->pid_set_condition.id, impl->pid_set_condition.collection );
2070 TRACE( "set constant force id %u, coll %u\n", impl->pid_set_constant_force.id,
2071 impl->pid_set_constant_force.collection );
2072 TRACE( "set ramp force id %u, coll %u\n", impl->pid_set_ramp_force.id, impl->pid_set_ramp_force.collection );
2073 TRACE( "device gain id %u, coll %u\n", impl->pid_device_gain.id, impl->pid_device_gain.collection );
2074 TRACE( "device pool id %u, coll %u\n", impl->pid_device_pool.id, impl->pid_device_pool.collection );
2075 TRACE( "block free id %u, coll %u\n", impl->pid_block_free.id, impl->pid_block_free.collection );
2076 TRACE( "block load id %u, coll %u, status_coll %u\n", impl->pid_block_load.id,
2077 impl->pid_block_load.collection, impl->pid_block_load.status_coll );
2078 TRACE( "create new effect id %u, coll %u, type_coll %u\n", impl->pid_new_effect.id,
2079 impl->pid_new_effect.collection, impl->pid_new_effect.type_coll );
2080 TRACE( "effect state id %u, coll %u\n", impl->pid_effect_state.id, impl->pid_effect_state.collection );
2082 if (impl->pid_device_control.id)
2084 impl->base.caps.dwFlags |= DIDC_FORCEFEEDBACK;
2085 if (impl->pid_effect_update.start_delay_caps)
2086 impl->base.caps.dwFlags |= DIDC_STARTDELAY;
2087 if (impl->pid_set_envelope.attack_level_caps ||
2088 impl->pid_set_envelope.attack_time_caps)
2089 impl->base.caps.dwFlags |= DIDC_FFATTACK;
2090 if (impl->pid_set_envelope.fade_level_caps ||
2091 impl->pid_set_envelope.fade_time_caps)
2092 impl->base.caps.dwFlags |= DIDC_FFFADE;
2093 if (impl->pid_set_condition.positive_saturation_caps ||
2094 impl->pid_set_condition.negative_saturation_caps)
2095 impl->base.caps.dwFlags |= DIDC_SATURATION;
2096 if (impl->pid_set_condition.dead_band_caps)
2097 impl->base.caps.dwFlags |= DIDC_DEADBAND;
2098 impl->base.caps.dwFFSamplePeriod = 1000000;
2099 impl->base.caps.dwFFMinTimeResolution = 1000000;
2100 impl->base.caps.dwHardwareRevision = 1;
2101 impl->base.caps.dwFFDriverVersion = 1;
2104 if (FAILED(hr = dinput_device_init( &impl->base.IDirectInputDevice8W_iface ))) goto failed;
2106 *out = &impl->base.IDirectInputDevice8W_iface;
2107 return DI_OK;
2109 failed:
2110 IDirectInputDevice_Release( &impl->base.IDirectInputDevice8W_iface );
2111 return hr;
2114 static HRESULT WINAPI hid_joystick_effect_QueryInterface( IDirectInputEffect *iface, REFIID iid, void **out )
2116 TRACE( "iface %p, iid %s, out %p\n", iface, debugstr_guid( iid ), out );
2118 if (IsEqualGUID( iid, &IID_IUnknown ) ||
2119 IsEqualGUID( iid, &IID_IDirectInputEffect ))
2121 IDirectInputEffect_AddRef( iface );
2122 *out = iface;
2123 return S_OK;
2126 FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) );
2127 *out = NULL;
2128 return E_NOINTERFACE;
2131 static ULONG WINAPI hid_joystick_effect_AddRef( IDirectInputEffect *iface )
2133 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
2134 ULONG ref = InterlockedIncrement( &impl->ref );
2135 TRACE( "iface %p, ref %lu.\n", iface, ref );
2136 return ref;
2139 static ULONG WINAPI hid_joystick_effect_Release( IDirectInputEffect *iface )
2141 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
2142 ULONG ref = InterlockedDecrement( &impl->ref );
2143 TRACE( "iface %p, ref %lu.\n", iface, ref );
2144 if (!ref)
2146 IDirectInputEffect_Unload( iface );
2147 EnterCriticalSection( &impl->joystick->base.crit );
2148 list_remove( &impl->entry );
2149 LeaveCriticalSection( &impl->joystick->base.crit );
2150 hid_joystick_release( &impl->joystick->base.IDirectInputDevice8W_iface );
2151 free( impl->set_envelope_buf );
2152 free( impl->type_specific_buf );
2153 free( impl->effect_update_buf );
2154 free( impl->effect_control_buf );
2155 free( impl );
2157 return ref;
2160 static HRESULT WINAPI hid_joystick_effect_Initialize( IDirectInputEffect *iface, HINSTANCE inst,
2161 DWORD version, REFGUID guid )
2163 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
2164 struct hid_joystick *joystick = impl->joystick;
2165 ULONG count, report_len = joystick->caps.OutputReportByteLength;
2166 NTSTATUS status;
2167 USAGE type;
2169 TRACE( "iface %p, inst %p, version %#lx, guid %s\n", iface, inst, version, debugstr_guid( guid ) );
2171 if (!inst) return DIERR_INVALIDPARAM;
2172 if (!guid) return E_POINTER;
2173 if (!(type = effect_guid_to_usage( guid ))) return DIERR_DEVICENOTREG;
2175 status = HidP_InitializeReportForID( HidP_Output, joystick->pid_effect_update.id,
2176 joystick->preparsed, impl->effect_update_buf, report_len );
2177 if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG;
2179 impl->type_specific_buf[0] = 0;
2180 impl->set_envelope_buf[0] = 0;
2182 switch (type)
2184 case PID_USAGE_ET_SQUARE:
2185 case PID_USAGE_ET_SINE:
2186 case PID_USAGE_ET_TRIANGLE:
2187 case PID_USAGE_ET_SAWTOOTH_UP:
2188 case PID_USAGE_ET_SAWTOOTH_DOWN:
2189 status = HidP_InitializeReportForID( HidP_Output, joystick->pid_set_periodic.id,
2190 joystick->preparsed, impl->type_specific_buf, report_len );
2191 if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG;
2192 impl->params.lpvTypeSpecificParams = &impl->periodic;
2193 break;
2194 case PID_USAGE_ET_SPRING:
2195 case PID_USAGE_ET_DAMPER:
2196 case PID_USAGE_ET_INERTIA:
2197 case PID_USAGE_ET_FRICTION:
2198 status = HidP_InitializeReportForID( HidP_Output, joystick->pid_set_condition.id, joystick->preparsed,
2199 impl->type_specific_buf, report_len );
2200 if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG;
2201 impl->params.lpvTypeSpecificParams = &impl->condition;
2202 break;
2203 case PID_USAGE_ET_CONSTANT_FORCE:
2204 status = HidP_InitializeReportForID( HidP_Output, joystick->pid_set_constant_force.id, joystick->preparsed,
2205 impl->type_specific_buf, report_len );
2206 if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG;
2207 impl->params.lpvTypeSpecificParams = &impl->constant_force;
2208 break;
2209 case PID_USAGE_ET_RAMP:
2210 status = HidP_InitializeReportForID( HidP_Output, joystick->pid_set_ramp_force.id, joystick->preparsed,
2211 impl->type_specific_buf, report_len );
2212 if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG;
2213 impl->params.lpvTypeSpecificParams = &impl->ramp_force;
2214 break;
2215 case PID_USAGE_ET_CUSTOM_FORCE_DATA:
2216 FIXME( "effect type %#x not implemented!\n", type );
2217 break;
2220 switch (type)
2222 case PID_USAGE_ET_SQUARE:
2223 case PID_USAGE_ET_SINE:
2224 case PID_USAGE_ET_TRIANGLE:
2225 case PID_USAGE_ET_SAWTOOTH_UP:
2226 case PID_USAGE_ET_SAWTOOTH_DOWN:
2227 case PID_USAGE_ET_CONSTANT_FORCE:
2228 case PID_USAGE_ET_RAMP:
2229 status = HidP_InitializeReportForID( HidP_Output, joystick->pid_set_envelope.id, joystick->preparsed,
2230 impl->set_envelope_buf, report_len );
2231 if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG;
2232 break;
2235 count = 1;
2236 status = HidP_SetUsages( HidP_Output, HID_USAGE_PAGE_PID, joystick->pid_effect_update.type_coll,
2237 &type, &count, joystick->preparsed, impl->effect_update_buf, report_len );
2238 if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG;
2240 impl->type = type;
2241 return DI_OK;
2244 static HRESULT WINAPI hid_joystick_effect_GetEffectGuid( IDirectInputEffect *iface, GUID *guid )
2246 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
2248 TRACE( "iface %p, guid %p.\n", iface, guid );
2250 if (!guid) return E_POINTER;
2251 *guid = *effect_usage_to_guid( impl->type );
2253 return DI_OK;
2256 static BOOL get_parameters_object_id( struct hid_joystick *impl, struct hid_value_caps *caps,
2257 DIDEVICEOBJECTINSTANCEW *instance, void *data )
2259 *(DWORD *)data = instance->dwType;
2260 return DIENUM_STOP;
2263 static BOOL get_parameters_object_ofs( struct hid_joystick *impl, struct hid_value_caps *caps,
2264 DIDEVICEOBJECTINSTANCEW *instance, void *data )
2266 DIDATAFORMAT *device_format = impl->base.device_format, *user_format = impl->base.user_format;
2267 DIOBJECTDATAFORMAT *device_obj, *user_obj;
2269 if (!user_format) return DIENUM_CONTINUE;
2271 user_obj = user_format->rgodf + device_format->dwNumObjs;
2272 device_obj = device_format->rgodf + device_format->dwNumObjs;
2273 while (user_obj-- > user_format->rgodf && device_obj-- > device_format->rgodf)
2275 if (!user_obj->dwType) continue;
2276 if (device_obj->dwType == instance->dwType) break;
2278 if (user_obj < user_format->rgodf) return DIENUM_CONTINUE;
2280 *(DWORD *)data = user_obj->dwOfs;
2281 return DIENUM_STOP;
2284 static void convert_directions_to_spherical( const DIEFFECT *in, DIEFFECT *out )
2286 DWORD i, j, direction_flags = DIEFF_CARTESIAN | DIEFF_POLAR | DIEFF_SPHERICAL;
2287 double tmp;
2289 switch (in->dwFlags & direction_flags)
2291 case DIEFF_CARTESIAN:
2292 for (i = 1; i < in->cAxes; ++i)
2294 tmp = in->rglDirection[0];
2295 for (j = 1; j < i; ++j) tmp = sqrt( tmp * tmp + in->rglDirection[j] * in->rglDirection[j] );
2296 tmp = atan2( in->rglDirection[i], tmp );
2297 out->rglDirection[i - 1] = tmp * 18000 / M_PI;
2299 if (in->cAxes) out->rglDirection[in->cAxes - 1] = 0;
2300 out->cAxes = in->cAxes;
2301 break;
2302 case DIEFF_POLAR:
2303 out->rglDirection[0] = (in->rglDirection[0] % 36000) - 9000;
2304 if (out->rglDirection[0] < 0) out->rglDirection[0] += 36000;
2305 for (i = 1; i < in->cAxes; ++i) out->rglDirection[i] = 0;
2306 out->cAxes = in->cAxes;
2307 break;
2308 case DIEFF_SPHERICAL:
2309 if (!in->cAxes) i = 0;
2310 else for (i = 0; i < in->cAxes - 1; ++i)
2312 out->rglDirection[i] = in->rglDirection[i] % 36000;
2313 if (out->rglDirection[i] < 0) out->rglDirection[i] += 36000;
2315 out->rglDirection[i] = 0;
2316 out->cAxes = in->cAxes;
2317 break;
2321 static void convert_directions_from_spherical( const DIEFFECT *in, DIEFFECT *out )
2323 DWORD i, j, direction_flags = DIEFF_CARTESIAN | DIEFF_POLAR | DIEFF_SPHERICAL;
2324 LONG tmp;
2326 switch (out->dwFlags & direction_flags)
2328 case DIEFF_CARTESIAN:
2329 out->rglDirection[0] = 10000;
2330 for (i = 1; i <= in->cAxes; ++i)
2332 tmp = cos( in->rglDirection[i - 1] * M_PI / 18000 ) * 10000;
2333 for (j = 0; j < i; ++j)
2334 out->rglDirection[j] = round( out->rglDirection[j] * tmp / 10000.0 );
2335 out->rglDirection[i] = sin( in->rglDirection[i - 1] * M_PI / 18000 ) * 10000;
2337 out->cAxes = in->cAxes;
2338 break;
2339 case DIEFF_POLAR:
2340 out->rglDirection[0] = (in->rglDirection[0] + 9000) % 36000;
2341 if (out->rglDirection[0] < 0) out->rglDirection[0] += 36000;
2342 out->rglDirection[1] = 0;
2343 out->cAxes = 2;
2344 break;
2345 case DIEFF_SPHERICAL:
2346 for (i = 0; i < in->cAxes; ++i)
2348 out->rglDirection[i] = in->rglDirection[i] % 36000;
2349 if (out->rglDirection[i] < 0) out->rglDirection[i] += 36000;
2351 out->cAxes = in->cAxes;
2352 break;
2356 static void convert_directions( const DIEFFECT *in, DIEFFECT *out )
2358 DWORD direction_flags = DIEFF_CARTESIAN | DIEFF_POLAR | DIEFF_SPHERICAL;
2359 LONG directions[6] = {0};
2360 DIEFFECT spherical = {.rglDirection = directions};
2362 switch (in->dwFlags & direction_flags)
2364 case DIEFF_CARTESIAN:
2365 switch (out->dwFlags & direction_flags)
2367 case DIEFF_CARTESIAN:
2368 memcpy( out->rglDirection, in->rglDirection, in->cAxes * sizeof(LONG) );
2369 out->cAxes = in->cAxes;
2370 break;
2371 case DIEFF_POLAR:
2372 convert_directions_to_spherical( in, &spherical );
2373 convert_directions_from_spherical( &spherical, out );
2374 break;
2375 case DIEFF_SPHERICAL:
2376 convert_directions_to_spherical( in, out );
2377 break;
2379 break;
2381 case DIEFF_POLAR:
2382 switch (out->dwFlags & direction_flags)
2384 case DIEFF_POLAR:
2385 memcpy( out->rglDirection, in->rglDirection, in->cAxes * sizeof(LONG) );
2386 out->cAxes = in->cAxes;
2387 break;
2388 case DIEFF_CARTESIAN:
2389 convert_directions_to_spherical( in, &spherical );
2390 convert_directions_from_spherical( &spherical, out );
2391 break;
2392 case DIEFF_SPHERICAL:
2393 convert_directions_to_spherical( in, out );
2394 break;
2396 break;
2398 case DIEFF_SPHERICAL:
2399 switch (out->dwFlags & direction_flags)
2401 case DIEFF_POLAR:
2402 case DIEFF_CARTESIAN:
2403 convert_directions_from_spherical( in, out );
2404 break;
2405 case DIEFF_SPHERICAL:
2406 convert_directions_to_spherical( in, out );
2407 break;
2409 break;
2413 static HRESULT WINAPI hid_joystick_effect_GetParameters( IDirectInputEffect *iface, DIEFFECT *params, DWORD flags )
2415 DIPROPHEADER filter =
2417 .dwSize = sizeof(DIPROPHEADER),
2418 .dwHeaderSize = sizeof(DIPROPHEADER),
2419 .dwHow = DIPH_BYUSAGE,
2421 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
2422 ULONG i, count, capacity, object_flags, direction_flags;
2423 BOOL ret;
2425 TRACE( "iface %p, params %p, flags %#lx.\n", iface, params, flags );
2427 if (!params) return DI_OK;
2428 if (params->dwSize != sizeof(DIEFFECT_DX6) && params->dwSize != sizeof(DIEFFECT_DX5)) return DIERR_INVALIDPARAM;
2429 capacity = params->cAxes;
2430 object_flags = params->dwFlags & (DIEFF_OBJECTIDS | DIEFF_OBJECTOFFSETS);
2431 direction_flags = params->dwFlags & (DIEFF_CARTESIAN | DIEFF_POLAR | DIEFF_SPHERICAL);
2433 if (flags & DIEP_AXES)
2435 if (!object_flags) return DIERR_INVALIDPARAM;
2436 params->cAxes = impl->params.cAxes;
2437 if (capacity < impl->params.cAxes) return DIERR_MOREDATA;
2439 for (i = 0; i < impl->params.cAxes; ++i)
2441 if (!params->rgdwAxes) return DIERR_INVALIDPARAM;
2442 filter.dwObj = impl->params.rgdwAxes[i];
2443 if (object_flags & DIEFF_OBJECTIDS)
2444 ret = enum_objects( impl->joystick, &filter, DIDFT_AXIS, get_parameters_object_id,
2445 &params->rgdwAxes[i] );
2446 else
2447 ret = enum_objects( impl->joystick, &filter, DIDFT_AXIS, get_parameters_object_ofs,
2448 &params->rgdwAxes[i] );
2449 if (ret != DIENUM_STOP) params->rgdwAxes[i] = 0;
2453 if (flags & DIEP_DIRECTION)
2455 if (!direction_flags) return DIERR_INVALIDPARAM;
2457 count = params->cAxes = impl->params.cAxes;
2458 if (!count) params->dwFlags &= ~(DIEFF_CARTESIAN | DIEFF_POLAR | DIEFF_SPHERICAL);
2459 if ((direction_flags & DIEFF_POLAR) && count != 2) return DIERR_INVALIDPARAM;
2460 if (capacity < params->cAxes) return DIERR_MOREDATA;
2462 if (!count) params->rglDirection = NULL;
2463 else if (!params->rglDirection) return DIERR_INVALIDPARAM;
2464 else convert_directions( &impl->params, params );
2467 if (flags & DIEP_TYPESPECIFICPARAMS)
2469 capacity = params->cbTypeSpecificParams;
2470 params->cbTypeSpecificParams = impl->params.cbTypeSpecificParams;
2471 if (capacity < impl->params.cbTypeSpecificParams) return DIERR_MOREDATA;
2472 if (impl->params.lpvTypeSpecificParams)
2474 if (!params->lpvTypeSpecificParams) return E_POINTER;
2475 memcpy( params->lpvTypeSpecificParams, impl->params.lpvTypeSpecificParams,
2476 impl->params.cbTypeSpecificParams );
2480 if (flags & DIEP_ENVELOPE)
2482 if (!params->lpEnvelope) return E_POINTER;
2483 if (params->lpEnvelope->dwSize != sizeof(DIENVELOPE)) return DIERR_INVALIDPARAM;
2484 if (!impl->params.lpEnvelope) params->lpEnvelope = NULL;
2485 else memcpy( params->lpEnvelope, impl->params.lpEnvelope, sizeof(DIENVELOPE) );
2488 if (flags & DIEP_DURATION) params->dwDuration = impl->params.dwDuration;
2489 if (flags & DIEP_GAIN) params->dwGain = impl->params.dwGain;
2490 if (flags & DIEP_SAMPLEPERIOD) params->dwSamplePeriod = impl->params.dwSamplePeriod;
2491 if (flags & DIEP_STARTDELAY)
2493 if (params->dwSize != sizeof(DIEFFECT_DX6)) return DIERR_INVALIDPARAM;
2494 params->dwStartDelay = impl->params.dwStartDelay;
2496 if (flags & DIEP_TRIGGERREPEATINTERVAL) params->dwTriggerRepeatInterval = impl->params.dwTriggerRepeatInterval;
2498 if (flags & DIEP_TRIGGERBUTTON)
2500 if (!object_flags) return DIERR_INVALIDPARAM;
2502 filter.dwObj = impl->params.dwTriggerButton;
2503 if (object_flags & DIEFF_OBJECTIDS)
2504 ret = enum_objects( impl->joystick, &filter, DIDFT_BUTTON, get_parameters_object_id,
2505 &params->dwTriggerButton );
2506 else
2507 ret = enum_objects( impl->joystick, &filter, DIDFT_BUTTON, get_parameters_object_ofs,
2508 &params->dwTriggerButton );
2509 if (ret != DIENUM_STOP) params->dwTriggerButton = -1;
2512 return DI_OK;
2515 static BOOL set_parameters_object( struct hid_joystick *impl, struct hid_value_caps *caps,
2516 DIDEVICEOBJECTINSTANCEW *instance, void *data )
2518 DWORD usages = MAKELONG( instance->wUsage, instance->wUsagePage );
2519 *(DWORD *)data = usages;
2520 return DIENUM_STOP;
2523 static HRESULT WINAPI hid_joystick_effect_SetParameters( IDirectInputEffect *iface,
2524 const DIEFFECT *params, DWORD flags )
2526 DIPROPHEADER filter =
2528 .dwSize = sizeof(DIPROPHEADER),
2529 .dwHeaderSize = sizeof(DIPROPHEADER),
2530 .dwHow = DIPH_BYUSAGE,
2532 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
2533 ULONG i, count, old_value, object_flags, direction_flags;
2534 HRESULT hr;
2535 BOOL ret;
2537 TRACE( "iface %p, params %p, flags %#lx.\n", iface, params, flags );
2539 if (!params) return E_POINTER;
2540 if (params->dwSize != sizeof(DIEFFECT_DX6) && params->dwSize != sizeof(DIEFFECT_DX5)) return DIERR_INVALIDPARAM;
2541 object_flags = params->dwFlags & (DIEFF_OBJECTIDS | DIEFF_OBJECTOFFSETS);
2542 direction_flags = params->dwFlags & (DIEFF_CARTESIAN | DIEFF_POLAR | DIEFF_SPHERICAL);
2544 if (object_flags & DIEFF_OBJECTIDS) filter.dwHow = DIPH_BYID;
2545 else filter.dwHow = DIPH_BYOFFSET;
2547 if (flags & DIEP_AXES)
2549 if (!object_flags) return DIERR_INVALIDPARAM;
2550 if (!params->rgdwAxes) return DIERR_INVALIDPARAM;
2551 if (impl->params.cAxes) return DIERR_ALREADYINITIALIZED;
2552 count = impl->joystick->pid_effect_update.axis_count;
2553 if (params->cAxes > count) return DIERR_INVALIDPARAM;
2555 impl->params.cAxes = params->cAxes;
2556 for (i = 0; i < params->cAxes; ++i)
2558 filter.dwObj = params->rgdwAxes[i];
2559 ret = enum_objects( impl->joystick, &filter, DIDFT_AXIS, set_parameters_object,
2560 &impl->params.rgdwAxes[i] );
2561 if (ret != DIENUM_STOP) impl->params.rgdwAxes[i] = 0;
2564 impl->modified |= DIEP_AXES;
2567 if (flags & DIEP_DIRECTION)
2569 if (!direction_flags) return DIERR_INVALIDPARAM;
2570 if (!params->rglDirection) return DIERR_INVALIDPARAM;
2572 count = impl->params.cAxes;
2573 if (params->cAxes < count) return DIERR_INVALIDPARAM;
2574 if ((direction_flags & DIEFF_POLAR) && count != 2) return DIERR_INVALIDPARAM;
2575 if ((direction_flags & DIEFF_CARTESIAN) && params->cAxes != count) return DIERR_INVALIDPARAM;
2577 impl->params.dwFlags &= ~(DIEFF_CARTESIAN | DIEFF_POLAR | DIEFF_SPHERICAL);
2578 impl->params.dwFlags |= direction_flags;
2579 if (memcmp( impl->params.rglDirection, params->rglDirection, count * sizeof(LONG) ))
2580 impl->modified |= DIEP_DIRECTION;
2581 memcpy( impl->params.rglDirection, params->rglDirection, count * sizeof(LONG) );
2584 if (flags & DIEP_TYPESPECIFICPARAMS)
2586 if (!params->lpvTypeSpecificParams) return E_POINTER;
2587 switch (impl->type)
2589 case PID_USAGE_ET_SQUARE:
2590 case PID_USAGE_ET_SINE:
2591 case PID_USAGE_ET_TRIANGLE:
2592 case PID_USAGE_ET_SAWTOOTH_UP:
2593 case PID_USAGE_ET_SAWTOOTH_DOWN:
2594 if (params->cbTypeSpecificParams != sizeof(DIPERIODIC))
2595 return DIERR_INVALIDPARAM;
2596 break;
2597 case PID_USAGE_ET_SPRING:
2598 case PID_USAGE_ET_DAMPER:
2599 case PID_USAGE_ET_INERTIA:
2600 case PID_USAGE_ET_FRICTION:
2601 if (params->cbTypeSpecificParams != sizeof(DICONDITION) && impl->params.cAxes &&
2602 params->cbTypeSpecificParams != impl->params.cAxes * sizeof(DICONDITION))
2603 return DIERR_INVALIDPARAM;
2604 break;
2605 case PID_USAGE_ET_CONSTANT_FORCE:
2606 if (params->cbTypeSpecificParams != sizeof(DICONSTANTFORCE))
2607 return DIERR_INVALIDPARAM;
2608 break;
2609 case PID_USAGE_ET_RAMP:
2610 if (params->cbTypeSpecificParams != sizeof(DIRAMPFORCE))
2611 return DIERR_INVALIDPARAM;
2612 break;
2613 case PID_USAGE_ET_CUSTOM_FORCE_DATA:
2614 FIXME( "custom force data not implemented!\n" );
2615 return DIERR_UNSUPPORTED;
2618 if (memcmp( impl->params.lpvTypeSpecificParams, params->lpvTypeSpecificParams,
2619 params->cbTypeSpecificParams ))
2620 impl->modified |= DIEP_TYPESPECIFICPARAMS;
2621 memcpy( impl->params.lpvTypeSpecificParams, params->lpvTypeSpecificParams,
2622 params->cbTypeSpecificParams );
2623 impl->params.cbTypeSpecificParams = params->cbTypeSpecificParams;
2626 if ((flags & DIEP_ENVELOPE) && params->lpEnvelope)
2628 if (params->lpEnvelope->dwSize != sizeof(DIENVELOPE)) return DIERR_INVALIDPARAM;
2629 impl->params.lpEnvelope = &impl->envelope;
2630 if (memcmp( impl->params.lpEnvelope, params->lpEnvelope, sizeof(DIENVELOPE) ))
2631 impl->modified |= DIEP_ENVELOPE;
2632 memcpy( impl->params.lpEnvelope, params->lpEnvelope, sizeof(DIENVELOPE) );
2635 if (flags & DIEP_DURATION)
2637 impl->modified |= DIEP_DURATION;
2638 impl->params.dwDuration = params->dwDuration;
2640 if (flags & DIEP_GAIN)
2642 if (impl->params.dwGain != params->dwGain) impl->modified |= DIEP_GAIN;
2643 impl->params.dwGain = params->dwGain;
2645 if (flags & DIEP_SAMPLEPERIOD)
2647 if (impl->params.dwSamplePeriod != params->dwSamplePeriod) impl->modified |= DIEP_SAMPLEPERIOD;
2648 impl->params.dwSamplePeriod = params->dwSamplePeriod;
2650 if (flags & DIEP_STARTDELAY)
2652 if (params->dwSize != sizeof(DIEFFECT_DX6)) return DIERR_INVALIDPARAM;
2653 if (impl->params.dwStartDelay != params->dwStartDelay) impl->modified |= DIEP_STARTDELAY;
2654 impl->params.dwStartDelay = params->dwStartDelay;
2656 if (flags & DIEP_TRIGGERREPEATINTERVAL)
2658 if (impl->params.dwTriggerRepeatInterval != params->dwTriggerRepeatInterval)
2659 impl->modified |= DIEP_TRIGGERREPEATINTERVAL;
2660 impl->params.dwTriggerRepeatInterval = params->dwTriggerRepeatInterval;
2663 if (flags & DIEP_TRIGGERBUTTON)
2665 if (!object_flags) return DIERR_INVALIDPARAM;
2667 filter.dwObj = params->dwTriggerButton;
2668 old_value = impl->params.dwTriggerButton;
2669 ret = enum_objects( impl->joystick, &filter, DIDFT_BUTTON, set_parameters_object,
2670 &impl->params.dwTriggerButton );
2671 if (ret != DIENUM_STOP) impl->params.dwTriggerButton = -1;
2672 if (impl->params.dwTriggerButton != old_value) impl->modified |= DIEP_TRIGGERBUTTON;
2675 impl->flags |= flags;
2677 if (flags & DIEP_NODOWNLOAD) return DI_DOWNLOADSKIPPED;
2678 if (flags & DIEP_START) hr = IDirectInputEffect_Start( iface, 1, 0 );
2679 else hr = IDirectInputEffect_Download( iface );
2680 if (hr == DIERR_NOTEXCLUSIVEACQUIRED) return DI_DOWNLOADSKIPPED;
2681 if (FAILED(hr)) return hr;
2682 return DI_OK;
2685 static HRESULT WINAPI hid_joystick_effect_Start( IDirectInputEffect *iface, DWORD iterations, DWORD flags )
2687 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
2688 struct pid_control_report *effect_control = &impl->joystick->pid_effect_control;
2689 ULONG count, report_len = impl->joystick->caps.OutputReportByteLength;
2690 PHIDP_PREPARSED_DATA preparsed = impl->joystick->preparsed;
2691 HANDLE device = impl->joystick->device;
2692 NTSTATUS status;
2693 USAGE control;
2694 HRESULT hr;
2696 TRACE( "iface %p, iterations %lu, flags %#lx.\n", iface, iterations, flags );
2698 if ((flags & ~(DIES_NODOWNLOAD|DIES_SOLO))) return DIERR_INVALIDPARAM;
2699 if (flags & DIES_SOLO) control = PID_USAGE_OP_EFFECT_START_SOLO;
2700 else control = PID_USAGE_OP_EFFECT_START;
2702 EnterCriticalSection( &impl->joystick->base.crit );
2703 if (!is_exclusively_acquired( impl->joystick ))
2704 hr = DIERR_NOTEXCLUSIVEACQUIRED;
2705 else if ((flags & DIES_NODOWNLOAD) && !impl->index)
2706 hr = DIERR_NOTDOWNLOADED;
2707 else if ((flags & DIES_NODOWNLOAD) || SUCCEEDED(hr = IDirectInputEffect_Download( iface )))
2709 count = 1;
2710 status = HidP_InitializeReportForID( HidP_Output, effect_control->id, preparsed,
2711 impl->effect_control_buf, report_len );
2713 if (status != HIDP_STATUS_SUCCESS) hr = status;
2714 else status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX,
2715 impl->index, preparsed, impl->effect_control_buf, report_len );
2717 if (status != HIDP_STATUS_SUCCESS) hr = status;
2718 else status = HidP_SetUsages( HidP_Output, HID_USAGE_PAGE_PID, effect_control->control_coll,
2719 &control, &count, preparsed, impl->effect_control_buf, report_len );
2721 if (status != HIDP_STATUS_SUCCESS) hr = status;
2722 else status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_LOOP_COUNT,
2723 iterations, preparsed, impl->effect_control_buf, report_len );
2725 if (status != HIDP_STATUS_SUCCESS) hr = status;
2726 else if (WriteFile( device, impl->effect_control_buf, report_len, NULL, NULL )) hr = DI_OK;
2727 else hr = DIERR_INPUTLOST;
2729 if (SUCCEEDED(hr)) impl->status |= DIEGES_PLAYING;
2730 else impl->status &= ~DIEGES_PLAYING;
2732 LeaveCriticalSection( &impl->joystick->base.crit );
2734 return hr;
2737 static HRESULT WINAPI hid_joystick_effect_Stop( IDirectInputEffect *iface )
2739 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
2740 struct pid_control_report *effect_control = &impl->joystick->pid_effect_control;
2741 ULONG count, report_len = impl->joystick->caps.OutputReportByteLength;
2742 PHIDP_PREPARSED_DATA preparsed = impl->joystick->preparsed;
2743 HANDLE device = impl->joystick->device;
2744 NTSTATUS status;
2745 USAGE control;
2746 HRESULT hr;
2748 TRACE( "iface %p.\n", iface );
2750 EnterCriticalSection( &impl->joystick->base.crit );
2751 if (!is_exclusively_acquired( impl->joystick ))
2752 hr = DIERR_NOTEXCLUSIVEACQUIRED;
2753 else if (!impl->index)
2754 hr = DIERR_NOTDOWNLOADED;
2755 else
2757 count = 1;
2758 control = PID_USAGE_OP_EFFECT_STOP;
2759 status = HidP_InitializeReportForID( HidP_Output, effect_control->id, preparsed,
2760 impl->effect_control_buf, report_len );
2762 if (status != HIDP_STATUS_SUCCESS) hr = status;
2763 else status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX,
2764 impl->index, preparsed, impl->effect_control_buf, report_len );
2766 if (status != HIDP_STATUS_SUCCESS) hr = status;
2767 else status = HidP_SetUsages( HidP_Output, HID_USAGE_PAGE_PID, effect_control->control_coll,
2768 &control, &count, preparsed, impl->effect_control_buf, report_len );
2770 if (status != HIDP_STATUS_SUCCESS) hr = status;
2771 else status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_LOOP_COUNT,
2772 0, preparsed, impl->effect_control_buf, report_len );
2774 if (status != HIDP_STATUS_SUCCESS) hr = status;
2775 else if (WriteFile( device, impl->effect_control_buf, report_len, NULL, NULL )) hr = DI_OK;
2776 else hr = DIERR_INPUTLOST;
2778 impl->status &= ~DIEGES_PLAYING;
2780 LeaveCriticalSection( &impl->joystick->base.crit );
2782 return hr;
2785 static HRESULT WINAPI hid_joystick_effect_GetEffectStatus( IDirectInputEffect *iface, DWORD *status )
2787 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
2788 HRESULT hr = DI_OK;
2790 TRACE( "iface %p, status %p.\n", iface, status );
2792 if (!status) return E_POINTER;
2793 *status = 0;
2795 EnterCriticalSection( &impl->joystick->base.crit );
2796 if (!is_exclusively_acquired( impl->joystick ))
2797 hr = DIERR_NOTEXCLUSIVEACQUIRED;
2798 else if (!impl->index)
2799 hr = DIERR_NOTDOWNLOADED;
2800 else
2801 *status = impl->status;
2802 LeaveCriticalSection( &impl->joystick->base.crit );
2804 return hr;
2807 static void set_parameter_value( struct hid_joystick_effect *impl, char *report_buf,
2808 struct hid_value_caps *caps, LONG value )
2810 return set_report_value( impl->joystick, report_buf, caps, value );
2813 static void set_parameter_value_angle( struct hid_joystick_effect *impl, char *report_buf,
2814 struct hid_value_caps *caps, LONG value )
2816 LONG exp;
2817 if (!caps) return;
2818 exp = caps->units_exp;
2819 if (caps->units != 0x14) WARN( "unknown angle unit caps %#lx\n", caps->units );
2820 else if (exp < -2) while (exp++ < -2) value *= 10;
2821 else if (exp > -2) while (exp-- > -2) value /= 10;
2822 set_parameter_value( impl, report_buf, caps, value );
2825 static void set_parameter_value_us( struct hid_joystick_effect *impl, char *report_buf,
2826 struct hid_value_caps *caps, LONG value )
2828 LONG exp;
2829 if (!caps) return;
2830 exp = caps->units_exp;
2831 if (value == INFINITE) value = caps->physical_min - 1;
2832 else if (caps->units != 0x1003) WARN( "unknown time unit caps %#lx\n", caps->units );
2833 else if (exp < -6) while (exp++ < -6) value *= 10;
2834 else if (exp > -6) while (exp-- > -6) value /= 10;
2835 set_parameter_value( impl, report_buf, caps, value );
2838 static BOOL is_axis_usage_enabled( struct hid_joystick_effect *impl, USAGE usage )
2840 DWORD i = impl->params.cAxes;
2841 while (i--) if (LOWORD(impl->params.rgdwAxes[i]) == usage) return TRUE;
2842 return FALSE;
2845 static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface )
2847 static const DWORD complete_mask = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS;
2848 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
2849 struct pid_set_constant_force *set_constant_force = &impl->joystick->pid_set_constant_force;
2850 struct pid_set_ramp_force *set_ramp_force = &impl->joystick->pid_set_ramp_force;
2851 struct pid_effect_update *effect_update = &impl->joystick->pid_effect_update;
2852 struct pid_set_condition *set_condition = &impl->joystick->pid_set_condition;
2853 struct pid_set_periodic *set_periodic = &impl->joystick->pid_set_periodic;
2854 struct pid_set_envelope *set_envelope = &impl->joystick->pid_set_envelope;
2855 ULONG report_len = impl->joystick->caps.OutputReportByteLength;
2856 HANDLE device = impl->joystick->device;
2857 struct hid_value_caps *caps;
2858 LONG directions[4] = {0};
2859 DWORD i, tmp, count;
2860 DIEFFECT spherical;
2861 NTSTATUS status;
2862 USAGE usage;
2863 HRESULT hr;
2865 TRACE( "iface %p\n", iface );
2867 EnterCriticalSection( &impl->joystick->base.crit );
2868 if (impl->modified) hr = DI_OK;
2869 else hr = DI_NOEFFECT;
2871 if (!is_exclusively_acquired( impl->joystick ))
2872 hr = DIERR_NOTEXCLUSIVEACQUIRED;
2873 else if ((impl->flags & complete_mask) != complete_mask)
2874 hr = DIERR_INCOMPLETEEFFECT;
2875 else if (!impl->index && SUCCEEDED(hr = find_next_effect_id( impl->joystick, &impl->index, impl->type )))
2877 if (!impl->type_specific_buf[0]) status = HIDP_STATUS_SUCCESS;
2878 else status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX,
2879 impl->index, impl->joystick->preparsed, impl->type_specific_buf, report_len );
2880 if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_SetUsageValue returned %#lx\n", status );
2882 if (!impl->set_envelope_buf[0]) status = HIDP_STATUS_SUCCESS;
2883 else status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX,
2884 impl->index, impl->joystick->preparsed, impl->set_envelope_buf, report_len );
2885 if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_SetUsageValue returned %#lx\n", status );
2887 status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX,
2888 impl->index, impl->joystick->preparsed, impl->effect_update_buf, report_len );
2889 if (status != HIDP_STATUS_SUCCESS) hr = status;
2890 else hr = DI_OK;
2893 if (hr == DI_OK)
2895 switch (impl->type)
2897 case PID_USAGE_ET_SQUARE:
2898 case PID_USAGE_ET_SINE:
2899 case PID_USAGE_ET_TRIANGLE:
2900 case PID_USAGE_ET_SAWTOOTH_UP:
2901 case PID_USAGE_ET_SAWTOOTH_DOWN:
2902 if (!(impl->modified & DIEP_TYPESPECIFICPARAMS)) break;
2904 set_parameter_value( impl, impl->type_specific_buf, set_periodic->magnitude_caps,
2905 impl->periodic.dwMagnitude );
2906 set_parameter_value_us( impl, impl->type_specific_buf, set_periodic->period_caps,
2907 impl->periodic.dwPeriod );
2908 set_parameter_value( impl, impl->type_specific_buf, set_periodic->phase_caps,
2909 impl->periodic.dwPhase );
2910 set_parameter_value( impl, impl->type_specific_buf, set_periodic->offset_caps,
2911 impl->periodic.lOffset );
2913 if (!WriteFile( device, impl->type_specific_buf, report_len, NULL, NULL )) hr = DIERR_INPUTLOST;
2914 else impl->modified &= ~DIEP_TYPESPECIFICPARAMS;
2915 break;
2916 case PID_USAGE_ET_SPRING:
2917 case PID_USAGE_ET_DAMPER:
2918 case PID_USAGE_ET_INERTIA:
2919 case PID_USAGE_ET_FRICTION:
2920 if (!(impl->modified & DIEP_TYPESPECIFICPARAMS)) break;
2922 for (i = 0; i < impl->params.cbTypeSpecificParams / sizeof(DICONDITION); ++i)
2924 status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_PARAMETER_BLOCK_OFFSET,
2925 i, impl->joystick->preparsed, impl->type_specific_buf, report_len );
2926 if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_SetUsageValue %04x:%04x returned %#lx\n",
2927 HID_USAGE_PAGE_PID, PID_USAGE_PARAMETER_BLOCK_OFFSET, status );
2928 set_parameter_value( impl, impl->type_specific_buf, set_condition->center_point_offset_caps,
2929 impl->condition[i].lOffset );
2930 set_parameter_value( impl, impl->type_specific_buf, set_condition->positive_coefficient_caps,
2931 impl->condition[i].lPositiveCoefficient );
2932 set_parameter_value( impl, impl->type_specific_buf, set_condition->negative_coefficient_caps,
2933 impl->condition[i].lNegativeCoefficient );
2934 set_parameter_value( impl, impl->type_specific_buf, set_condition->positive_saturation_caps,
2935 impl->condition[i].dwPositiveSaturation );
2936 set_parameter_value( impl, impl->type_specific_buf, set_condition->negative_saturation_caps,
2937 impl->condition[i].dwNegativeSaturation );
2938 set_parameter_value( impl, impl->type_specific_buf, set_condition->dead_band_caps,
2939 impl->condition[i].lDeadBand );
2941 if (!WriteFile( device, impl->type_specific_buf, report_len, NULL, NULL )) hr = DIERR_INPUTLOST;
2942 else impl->modified &= ~DIEP_TYPESPECIFICPARAMS;
2944 break;
2945 case PID_USAGE_ET_CONSTANT_FORCE:
2946 if (!(impl->modified & DIEP_TYPESPECIFICPARAMS)) break;
2948 set_parameter_value( impl, impl->type_specific_buf, set_constant_force->magnitude_caps,
2949 impl->constant_force.lMagnitude );
2951 if (!WriteFile( device, impl->type_specific_buf, report_len, NULL, NULL )) hr = DIERR_INPUTLOST;
2952 else impl->modified &= ~DIEP_TYPESPECIFICPARAMS;
2953 break;
2954 case PID_USAGE_ET_RAMP:
2955 if (!(impl->modified & DIEP_TYPESPECIFICPARAMS)) break;
2957 set_parameter_value( impl, impl->type_specific_buf, set_ramp_force->start_caps,
2958 impl->ramp_force.lStart );
2959 set_parameter_value( impl, impl->type_specific_buf, set_ramp_force->end_caps,
2960 impl->ramp_force.lEnd );
2962 if (!WriteFile( device, impl->type_specific_buf, report_len, NULL, NULL )) hr = DIERR_INPUTLOST;
2963 else impl->modified &= ~DIEP_TYPESPECIFICPARAMS;
2964 break;
2968 if (hr == DI_OK)
2970 switch (impl->type)
2972 case PID_USAGE_ET_SQUARE:
2973 case PID_USAGE_ET_SINE:
2974 case PID_USAGE_ET_TRIANGLE:
2975 case PID_USAGE_ET_SAWTOOTH_UP:
2976 case PID_USAGE_ET_SAWTOOTH_DOWN:
2977 case PID_USAGE_ET_CONSTANT_FORCE:
2978 case PID_USAGE_ET_RAMP:
2979 if (!(impl->modified & DIEP_ENVELOPE)) break;
2981 set_parameter_value( impl, impl->set_envelope_buf, set_envelope->attack_level_caps,
2982 impl->envelope.dwAttackLevel );
2983 set_parameter_value_us( impl, impl->set_envelope_buf, set_envelope->attack_time_caps,
2984 impl->envelope.dwAttackTime );
2985 set_parameter_value( impl, impl->set_envelope_buf, set_envelope->fade_level_caps,
2986 impl->envelope.dwFadeLevel );
2987 set_parameter_value_us( impl, impl->set_envelope_buf, set_envelope->fade_time_caps,
2988 impl->envelope.dwFadeTime );
2990 if (!WriteFile( device, impl->set_envelope_buf, report_len, NULL, NULL )) hr = DIERR_INPUTLOST;
2991 else impl->modified &= ~DIEP_ENVELOPE;
2992 break;
2996 if (hr == DI_OK && impl->modified)
2998 set_parameter_value_us( impl, impl->effect_update_buf, effect_update->duration_caps,
2999 impl->params.dwDuration );
3000 set_parameter_value( impl, impl->effect_update_buf, effect_update->gain_caps,
3001 impl->params.dwGain );
3002 set_parameter_value_us( impl, impl->effect_update_buf, effect_update->sample_period_caps,
3003 impl->params.dwSamplePeriod );
3004 set_parameter_value_us( impl, impl->effect_update_buf, effect_update->start_delay_caps,
3005 impl->params.dwStartDelay );
3006 set_parameter_value_us( impl, impl->effect_update_buf, effect_update->trigger_repeat_interval_caps,
3007 impl->params.dwTriggerRepeatInterval );
3009 count = 1;
3010 usage = PID_USAGE_DIRECTION_ENABLE;
3011 status = HidP_SetUsages( HidP_Output, HID_USAGE_PAGE_PID, 0, &usage, &count,
3012 impl->joystick->preparsed, impl->effect_update_buf, report_len );
3013 if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_SetUsages returned %#lx\n", status );
3015 spherical.rglDirection = directions;
3016 convert_directions_to_spherical( &impl->params, &spherical );
3018 /* FIXME: as far as the test cases go, directions are only written if
3019 * either X or Y axes are enabled, maybe need more tests though */
3020 if (!is_axis_usage_enabled( impl, HID_USAGE_GENERIC_X ) &&
3021 !is_axis_usage_enabled( impl, HID_USAGE_GENERIC_Y ))
3022 WARN( "neither X or Y axes are selected, skipping direction\n" );
3023 else for (i = 0; i < min( effect_update->direction_count, spherical.cAxes ); ++i)
3025 tmp = directions[i] + (i == 0 ? 9000 : 0);
3026 caps = effect_update->direction_caps[effect_update->direction_count - i - 1];
3027 set_parameter_value_angle( impl, impl->effect_update_buf, caps, tmp % 36000 );
3030 status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_TRIGGER_BUTTON,
3031 impl->params.dwTriggerButton, impl->joystick->preparsed,
3032 impl->effect_update_buf, report_len );
3033 if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_SetUsageValue returned %#lx\n", status );
3035 if (!WriteFile( device, impl->effect_update_buf, report_len, NULL, NULL )) hr = DIERR_INPUTLOST;
3036 else impl->modified = 0;
3038 if (SUCCEEDED(hr)) impl->joystick->base.force_feedback_state &= ~DIGFFS_EMPTY;
3040 LeaveCriticalSection( &impl->joystick->base.crit );
3042 return hr;
3045 static void check_empty_force_feedback_state( struct hid_joystick *joystick )
3047 struct hid_joystick_effect *effect;
3048 LIST_FOR_EACH_ENTRY( effect, &joystick->effect_list, struct hid_joystick_effect, entry )
3049 if (effect->index) return;
3050 joystick->base.force_feedback_state |= DIGFFS_EMPTY;
3053 static HRESULT WINAPI hid_joystick_effect_Unload( IDirectInputEffect *iface )
3055 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
3056 struct hid_joystick *joystick = impl->joystick;
3057 struct pid_device_pool *device_pool = &joystick->pid_device_pool;
3058 struct pid_block_free *block_free = &joystick->pid_block_free;
3059 ULONG report_len = joystick->caps.OutputReportByteLength;
3060 HRESULT hr = DI_OK;
3061 NTSTATUS status;
3063 TRACE( "iface %p\n", iface );
3065 EnterCriticalSection( &joystick->base.crit );
3066 if (!impl->index)
3067 hr = DI_NOEFFECT;
3068 else if (SUCCEEDED(hr = IDirectInputEffect_Stop( iface )))
3070 if (!device_pool->device_managed_caps)
3071 joystick->effect_inuse[impl->index - 1] = FALSE;
3072 else if (block_free->id)
3074 status = HidP_InitializeReportForID( HidP_Output, block_free->id, joystick->preparsed,
3075 joystick->output_report_buf, report_len );
3077 if (status != HIDP_STATUS_SUCCESS) hr = status;
3078 else status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX,
3079 impl->index, joystick->preparsed, joystick->output_report_buf, report_len );
3081 if (status != HIDP_STATUS_SUCCESS) hr = status;
3082 else if (WriteFile( joystick->device, joystick->output_report_buf, report_len, NULL, NULL )) hr = DI_OK;
3083 else hr = DIERR_INPUTLOST;
3086 impl->modified = impl->flags;
3087 impl->index = 0;
3088 check_empty_force_feedback_state( joystick );
3090 LeaveCriticalSection( &joystick->base.crit );
3092 return hr;
3095 static HRESULT WINAPI hid_joystick_effect_Escape( IDirectInputEffect *iface, DIEFFESCAPE *escape )
3097 FIXME( "iface %p, escape %p stub!\n", iface, escape );
3098 return DIERR_UNSUPPORTED;
3101 static IDirectInputEffectVtbl hid_joystick_effect_vtbl =
3103 /*** IUnknown methods ***/
3104 hid_joystick_effect_QueryInterface,
3105 hid_joystick_effect_AddRef,
3106 hid_joystick_effect_Release,
3107 /*** IDirectInputEffect methods ***/
3108 hid_joystick_effect_Initialize,
3109 hid_joystick_effect_GetEffectGuid,
3110 hid_joystick_effect_GetParameters,
3111 hid_joystick_effect_SetParameters,
3112 hid_joystick_effect_Start,
3113 hid_joystick_effect_Stop,
3114 hid_joystick_effect_GetEffectStatus,
3115 hid_joystick_effect_Download,
3116 hid_joystick_effect_Unload,
3117 hid_joystick_effect_Escape,
3120 static HRESULT hid_joystick_create_effect( IDirectInputDevice8W *iface, IDirectInputEffect **out )
3122 struct hid_joystick *joystick = impl_from_IDirectInputDevice8W( iface );
3123 struct hid_joystick_effect *impl;
3124 ULONG report_len;
3126 if (!(impl = calloc( 1, sizeof(*impl) ))) return DIERR_OUTOFMEMORY;
3127 impl->IDirectInputEffect_iface.lpVtbl = &hid_joystick_effect_vtbl;
3128 impl->ref = 1;
3129 impl->joystick = joystick;
3130 hid_joystick_addref( &joystick->base.IDirectInputDevice8W_iface );
3132 EnterCriticalSection( &joystick->base.crit );
3133 list_add_tail( &joystick->effect_list, &impl->entry );
3134 LeaveCriticalSection( &joystick->base.crit );
3136 report_len = joystick->caps.OutputReportByteLength;
3137 if (!(impl->effect_control_buf = malloc( report_len ))) goto failed;
3138 if (!(impl->effect_update_buf = malloc( report_len ))) goto failed;
3139 if (!(impl->type_specific_buf = malloc( report_len ))) goto failed;
3140 if (!(impl->set_envelope_buf = malloc( report_len ))) goto failed;
3142 impl->envelope.dwSize = sizeof(DIENVELOPE);
3143 impl->params.dwSize = sizeof(DIEFFECT);
3144 impl->params.rgdwAxes = impl->axes;
3145 impl->params.rglDirection = impl->directions;
3146 impl->params.dwTriggerButton = -1;
3147 impl->status = 0;
3149 *out = &impl->IDirectInputEffect_iface;
3150 return DI_OK;
3152 failed:
3153 IDirectInputEffect_Release( &impl->IDirectInputEffect_iface );
3154 return DIERR_OUTOFMEMORY;