dinput: Clear DIA_APPNOMAP BuildActionMap flag with specific device semantic.
[wine.git] / dlls / dinput / joystick_hid.c
blob3320ac92a1ccc5839f5968d344786ef7284d33db
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"
33 #include "winioctl.h"
35 #include "ddk/hidclass.h"
36 #include "ddk/hidsdi.h"
37 #include "setupapi.h"
38 #include "devguid.h"
39 #include "dinput.h"
40 #include "setupapi.h"
42 #include "dinput_private.h"
43 #include "device_private.h"
45 #include "initguid.h"
47 #include "wine/debug.h"
48 #include "wine/hid.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
52 DEFINE_GUID( GUID_DEVINTERFACE_WINEXINPUT,0x6c53d5fd,0x6480,0x440f,0xb6,0x18,0x47,0x67,0x50,0xc5,0xe1,0xa6 );
53 DEFINE_GUID( hid_joystick_guid, 0x9e573edb, 0x7734, 0x11d2, 0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7 );
54 DEFINE_GUID( device_path_guid, 0x00000000, 0x0000, 0x0000, 0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf8 );
56 struct pid_control_report
58 BYTE id;
59 UINT collection;
60 UINT control_coll;
63 struct pid_effect_update
65 BYTE id;
66 UINT collection;
67 UINT type_coll;
68 UINT axes_coll;
69 UINT axis_count;
70 UINT direction_coll;
71 UINT direction_count;
72 struct hid_value_caps *axis_caps[6];
73 struct hid_value_caps *direction_caps[6];
74 struct hid_value_caps *duration_caps;
75 struct hid_value_caps *gain_caps;
76 struct hid_value_caps *sample_period_caps;
77 struct hid_value_caps *start_delay_caps;
78 struct hid_value_caps *trigger_button_caps;
79 struct hid_value_caps *trigger_repeat_interval_caps;
82 struct pid_set_periodic
84 BYTE id;
85 UINT collection;
86 struct hid_value_caps *magnitude_caps;
87 struct hid_value_caps *period_caps;
88 struct hid_value_caps *phase_caps;
89 struct hid_value_caps *offset_caps;
92 struct pid_set_envelope
94 BYTE id;
95 UINT collection;
96 struct hid_value_caps *attack_level_caps;
97 struct hid_value_caps *attack_time_caps;
98 struct hid_value_caps *fade_level_caps;
99 struct hid_value_caps *fade_time_caps;
102 struct pid_set_condition
104 BYTE id;
105 UINT collection;
106 struct hid_value_caps *center_point_offset_caps;
107 struct hid_value_caps *positive_coefficient_caps;
108 struct hid_value_caps *negative_coefficient_caps;
109 struct hid_value_caps *positive_saturation_caps;
110 struct hid_value_caps *negative_saturation_caps;
111 struct hid_value_caps *dead_band_caps;
114 struct pid_set_constant_force
116 BYTE id;
117 UINT collection;
118 struct hid_value_caps *magnitude_caps;
121 struct pid_set_ramp_force
123 BYTE id;
124 UINT collection;
125 struct hid_value_caps *start_caps;
126 struct hid_value_caps *end_caps;
129 struct pid_device_gain
131 BYTE id;
132 UINT collection;
133 struct hid_value_caps *device_gain_caps;
136 struct pid_device_pool
138 BYTE id;
139 UINT collection;
140 struct hid_value_caps *device_managed_caps;
143 struct pid_block_free
145 BYTE id;
146 UINT collection;
149 struct pid_block_load
151 BYTE id;
152 UINT collection;
153 UINT status_coll;
156 struct pid_new_effect
158 BYTE id;
159 UINT collection;
160 UINT type_coll;
163 struct pid_effect_state
165 BYTE id;
166 UINT collection;
167 struct hid_value_caps *safety_switch_caps;
168 struct hid_value_caps *actuator_power_caps;
169 struct hid_value_caps *actuator_override_switch_caps;
172 struct hid_joystick
174 struct dinput_device base;
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_DIAL: return &GUID_Slider;
279 case HID_USAGE_GENERIC_HATSWITCH: return &GUID_POV;
281 break;
284 return &GUID_Unknown;
287 static inline USAGE effect_guid_to_usage( const GUID *guid )
289 if (IsEqualGUID( guid, &GUID_CustomForce )) return PID_USAGE_ET_CUSTOM_FORCE_DATA;
290 if (IsEqualGUID( guid, &GUID_ConstantForce )) return PID_USAGE_ET_CONSTANT_FORCE;
291 if (IsEqualGUID( guid, &GUID_RampForce )) return PID_USAGE_ET_RAMP;
292 if (IsEqualGUID( guid, &GUID_Square )) return PID_USAGE_ET_SQUARE;
293 if (IsEqualGUID( guid, &GUID_Sine )) return PID_USAGE_ET_SINE;
294 if (IsEqualGUID( guid, &GUID_Triangle )) return PID_USAGE_ET_TRIANGLE;
295 if (IsEqualGUID( guid, &GUID_SawtoothUp )) return PID_USAGE_ET_SAWTOOTH_UP;
296 if (IsEqualGUID( guid, &GUID_SawtoothDown )) return PID_USAGE_ET_SAWTOOTH_DOWN;
297 if (IsEqualGUID( guid, &GUID_Spring )) return PID_USAGE_ET_SPRING;
298 if (IsEqualGUID( guid, &GUID_Damper )) return PID_USAGE_ET_DAMPER;
299 if (IsEqualGUID( guid, &GUID_Inertia )) return PID_USAGE_ET_INERTIA;
300 if (IsEqualGUID( guid, &GUID_Friction )) return PID_USAGE_ET_FRICTION;
301 return 0;
304 static inline const GUID *effect_usage_to_guid( USAGE usage )
306 switch (usage)
308 case PID_USAGE_ET_CUSTOM_FORCE_DATA: return &GUID_CustomForce;
309 case PID_USAGE_ET_CONSTANT_FORCE: return &GUID_ConstantForce;
310 case PID_USAGE_ET_RAMP: return &GUID_RampForce;
311 case PID_USAGE_ET_SQUARE: return &GUID_Square;
312 case PID_USAGE_ET_SINE: return &GUID_Sine;
313 case PID_USAGE_ET_TRIANGLE: return &GUID_Triangle;
314 case PID_USAGE_ET_SAWTOOTH_UP: return &GUID_SawtoothUp;
315 case PID_USAGE_ET_SAWTOOTH_DOWN: return &GUID_SawtoothDown;
316 case PID_USAGE_ET_SPRING: return &GUID_Spring;
317 case PID_USAGE_ET_DAMPER: return &GUID_Damper;
318 case PID_USAGE_ET_INERTIA: return &GUID_Inertia;
319 case PID_USAGE_ET_FRICTION: return &GUID_Friction;
321 return &GUID_Unknown;
324 static const WCHAR *effect_guid_to_string( const GUID *guid )
326 if (IsEqualGUID( guid, &GUID_CustomForce )) return L"GUID_CustomForce";
327 if (IsEqualGUID( guid, &GUID_ConstantForce )) return L"GUID_ConstantForce";
328 if (IsEqualGUID( guid, &GUID_RampForce )) return L"GUID_RampForce";
329 if (IsEqualGUID( guid, &GUID_Square )) return L"GUID_Square";
330 if (IsEqualGUID( guid, &GUID_Sine )) return L"GUID_Sine";
331 if (IsEqualGUID( guid, &GUID_Triangle )) return L"GUID_Triangle";
332 if (IsEqualGUID( guid, &GUID_SawtoothUp )) return L"GUID_SawtoothUp";
333 if (IsEqualGUID( guid, &GUID_SawtoothDown )) return L"GUID_SawtoothDown";
334 if (IsEqualGUID( guid, &GUID_Spring )) return L"GUID_Spring";
335 if (IsEqualGUID( guid, &GUID_Damper )) return L"GUID_Damper";
336 if (IsEqualGUID( guid, &GUID_Inertia )) return L"GUID_Inertia";
337 if (IsEqualGUID( guid, &GUID_Friction )) return L"GUID_Friction";
338 return L"GUID_Unknown";
341 static const WCHAR *object_usage_to_string( DIDEVICEOBJECTINSTANCEW *instance )
343 switch (MAKELONG(instance->wUsage, instance->wUsagePage))
345 case MAKELONG(HID_USAGE_DIGITIZER_TIP_PRESSURE, HID_USAGE_PAGE_DIGITIZER): return L"Tip Pressure";
346 case MAKELONG(HID_USAGE_CONSUMER_VOLUME, HID_USAGE_PAGE_CONSUMER): return L"Volume";
348 case MAKELONG(HID_USAGE_GENERIC_HATSWITCH, HID_USAGE_PAGE_GENERIC): return L"Hat Switch";
349 case MAKELONG(HID_USAGE_GENERIC_JOYSTICK, HID_USAGE_PAGE_GENERIC): return L"Joystick";
350 case MAKELONG(HID_USAGE_GENERIC_RX, HID_USAGE_PAGE_GENERIC): return L"X Rotation";
351 case MAKELONG(HID_USAGE_GENERIC_RY, HID_USAGE_PAGE_GENERIC): return L"Y Rotation";
352 case MAKELONG(HID_USAGE_GENERIC_RZ, HID_USAGE_PAGE_GENERIC): return L"Z Rotation";
353 case MAKELONG(HID_USAGE_GENERIC_WHEEL, HID_USAGE_PAGE_GENERIC): return L"Wheel";
354 case MAKELONG(HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC): return L"X Axis";
355 case MAKELONG(HID_USAGE_GENERIC_Y, HID_USAGE_PAGE_GENERIC): return L"Y Axis";
356 case MAKELONG(HID_USAGE_GENERIC_Z, HID_USAGE_PAGE_GENERIC): return L"Z Axis";
357 case MAKELONG(HID_USAGE_GENERIC_SLIDER, HID_USAGE_PAGE_GENERIC): return L"Slider";
358 case MAKELONG(HID_USAGE_GENERIC_DIAL, HID_USAGE_PAGE_GENERIC): return L"Dial";
360 case MAKELONG(PID_USAGE_ATTACK_LEVEL, HID_USAGE_PAGE_PID): return L"Attack Level";
361 case MAKELONG(PID_USAGE_ATTACK_TIME, HID_USAGE_PAGE_PID): return L"Attack Time";
362 case MAKELONG(PID_USAGE_AXES_ENABLE, HID_USAGE_PAGE_PID): return L"Axes Enable";
364 case MAKELONG(PID_USAGE_DC_DEVICE_CONTINUE, HID_USAGE_PAGE_PID): return L"DC Device Continue";
365 case MAKELONG(PID_USAGE_DC_DEVICE_PAUSE, HID_USAGE_PAGE_PID): return L"DC Device Pause";
366 case MAKELONG(PID_USAGE_DC_DEVICE_RESET, HID_USAGE_PAGE_PID): return L"DC Device Reset";
367 case MAKELONG(PID_USAGE_DC_DISABLE_ACTUATORS, HID_USAGE_PAGE_PID): return L"DC Disable Actuators";
368 case MAKELONG(PID_USAGE_DC_ENABLE_ACTUATORS, HID_USAGE_PAGE_PID): return L"DC Enable Actuators";
369 case MAKELONG(PID_USAGE_DC_STOP_ALL_EFFECTS, HID_USAGE_PAGE_PID): return L"DC Stop All Effects";
371 case MAKELONG(PID_USAGE_DEVICE_GAIN, HID_USAGE_PAGE_PID): return L"Device Gain";
372 case MAKELONG(PID_USAGE_DEVICE_GAIN_REPORT, HID_USAGE_PAGE_PID): return L"Device Gain Report";
373 case MAKELONG(PID_USAGE_CP_OFFSET, HID_USAGE_PAGE_PID): return L"CP Offset";
374 case MAKELONG(PID_USAGE_DEAD_BAND, HID_USAGE_PAGE_PID): return L"Dead Band";
375 case MAKELONG(PID_USAGE_DEVICE_CONTROL, HID_USAGE_PAGE_PID): return L"PID Device Control";
376 case MAKELONG(PID_USAGE_DEVICE_CONTROL_REPORT, HID_USAGE_PAGE_PID): return L"PID Device Control Report";
377 case MAKELONG(PID_USAGE_DIRECTION, HID_USAGE_PAGE_PID): return L"Direction";
378 case MAKELONG(PID_USAGE_DIRECTION_ENABLE, HID_USAGE_PAGE_PID): return L"Direction Enable";
379 case MAKELONG(PID_USAGE_DURATION, HID_USAGE_PAGE_PID): return L"Duration";
380 case MAKELONG(PID_USAGE_EFFECT_BLOCK_INDEX, HID_USAGE_PAGE_PID): return L"Effect Block Index";
381 case MAKELONG(PID_USAGE_EFFECT_OPERATION, HID_USAGE_PAGE_PID): return L"Effect Operation";
382 case MAKELONG(PID_USAGE_EFFECT_OPERATION_REPORT, HID_USAGE_PAGE_PID): return L"Effect Operation Report";
383 case MAKELONG(PID_USAGE_EFFECT_TYPE, HID_USAGE_PAGE_PID): return L"Effect Type";
385 case MAKELONG(PID_USAGE_ET_CONSTANT_FORCE, HID_USAGE_PAGE_PID): return L"ET Constant Force";
386 case MAKELONG(PID_USAGE_ET_CUSTOM_FORCE_DATA, HID_USAGE_PAGE_PID): return L"ET Custom Force Data";
387 case MAKELONG(PID_USAGE_ET_DAMPER, HID_USAGE_PAGE_PID): return L"ET Damper";
388 case MAKELONG(PID_USAGE_ET_FRICTION, HID_USAGE_PAGE_PID): return L"ET Friction";
389 case MAKELONG(PID_USAGE_ET_INERTIA, HID_USAGE_PAGE_PID): return L"ET Inertia";
390 case MAKELONG(PID_USAGE_ET_RAMP, HID_USAGE_PAGE_PID): return L"ET Ramp";
391 case MAKELONG(PID_USAGE_ET_SAWTOOTH_DOWN, HID_USAGE_PAGE_PID): return L"ET Sawtooth Down";
392 case MAKELONG(PID_USAGE_ET_SAWTOOTH_UP, HID_USAGE_PAGE_PID): return L"ET Sawtooth Up";
393 case MAKELONG(PID_USAGE_ET_SINE, HID_USAGE_PAGE_PID): return L"ET Sine";
394 case MAKELONG(PID_USAGE_ET_SPRING, HID_USAGE_PAGE_PID): return L"ET Spring";
395 case MAKELONG(PID_USAGE_ET_SQUARE, HID_USAGE_PAGE_PID): return L"ET Square";
396 case MAKELONG(PID_USAGE_ET_TRIANGLE, HID_USAGE_PAGE_PID): return L"ET Triangle";
398 case MAKELONG(PID_USAGE_NEGATIVE_COEFFICIENT, HID_USAGE_PAGE_PID): return L"Negative Coefficient";
399 case MAKELONG(PID_USAGE_NEGATIVE_SATURATION, HID_USAGE_PAGE_PID): return L"Negative Saturation";
400 case MAKELONG(PID_USAGE_POSITIVE_COEFFICIENT, HID_USAGE_PAGE_PID): return L"Positive Coefficient";
401 case MAKELONG(PID_USAGE_POSITIVE_SATURATION, HID_USAGE_PAGE_PID): return L"Positive Saturation";
402 case MAKELONG(PID_USAGE_SET_CONDITION_REPORT, HID_USAGE_PAGE_PID): return L"Set Condition Report";
403 case MAKELONG(PID_USAGE_TYPE_SPECIFIC_BLOCK_OFFSET, HID_USAGE_PAGE_PID): return L"Type Specific Block Offset";
405 case MAKELONG(PID_USAGE_FADE_LEVEL, HID_USAGE_PAGE_PID): return L"Fade Level";
406 case MAKELONG(PID_USAGE_FADE_TIME, HID_USAGE_PAGE_PID): return L"Fade Time";
407 case MAKELONG(PID_USAGE_LOOP_COUNT, HID_USAGE_PAGE_PID): return L"Loop Count";
408 case MAKELONG(PID_USAGE_MAGNITUDE, HID_USAGE_PAGE_PID): return L"Magnitude";
409 case MAKELONG(PID_USAGE_OP_EFFECT_START, HID_USAGE_PAGE_PID): return L"Op Effect Start";
410 case MAKELONG(PID_USAGE_OP_EFFECT_START_SOLO, HID_USAGE_PAGE_PID): return L"Op Effect Start Solo";
411 case MAKELONG(PID_USAGE_OP_EFFECT_STOP, HID_USAGE_PAGE_PID): return L"Op Effect Stop";
412 case MAKELONG(PID_USAGE_SET_EFFECT_REPORT, HID_USAGE_PAGE_PID): return L"Set Effect Report";
413 case MAKELONG(PID_USAGE_SET_ENVELOPE_REPORT, HID_USAGE_PAGE_PID): return L"Set Envelope Report";
414 case MAKELONG(PID_USAGE_SET_PERIODIC_REPORT, HID_USAGE_PAGE_PID): return L"Set Periodic Report";
415 case MAKELONG(PID_USAGE_START_DELAY, HID_USAGE_PAGE_PID): return L"Start Delay";
416 case MAKELONG(PID_USAGE_STATE_REPORT, HID_USAGE_PAGE_PID): return L"PID State Report";
417 case MAKELONG(PID_USAGE_TRIGGER_BUTTON, HID_USAGE_PAGE_PID): return L"Trigger Button";
419 case MAKELONG(HID_USAGE_SIMULATION_RUDDER, HID_USAGE_PAGE_SIMULATION): return L"Rudder";
420 case MAKELONG(HID_USAGE_SIMULATION_THROTTLE, HID_USAGE_PAGE_SIMULATION): return L"Throttle";
421 case MAKELONG(HID_USAGE_SIMULATION_ACCELERATOR, HID_USAGE_PAGE_SIMULATION): return L"Accelerator";
422 case MAKELONG(HID_USAGE_SIMULATION_BRAKE, HID_USAGE_PAGE_SIMULATION): return L"Brake";
423 case MAKELONG(HID_USAGE_SIMULATION_CLUTCH, HID_USAGE_PAGE_SIMULATION): return L"Clutch";
424 case MAKELONG(HID_USAGE_SIMULATION_STEERING, HID_USAGE_PAGE_SIMULATION): return L"Steering";
425 default: return NULL;
429 static HRESULT find_next_effect_id( struct hid_joystick *impl, ULONG *index, USAGE type )
431 struct pid_device_pool *device_pool = &impl->pid_device_pool;
432 struct pid_new_effect *new_effect = &impl->pid_new_effect;
433 struct pid_block_load *block_load = &impl->pid_block_load;
434 ULONG i, count, report_len = impl->caps.FeatureReportByteLength;
435 NTSTATUS status;
436 USAGE usage;
438 if (!device_pool->device_managed_caps)
440 for (i = 0; i < ARRAY_SIZE(impl->effect_inuse); ++i)
441 if (!impl->effect_inuse[i]) break;
442 if (i == ARRAY_SIZE(impl->effect_inuse)) return DIERR_DEVICEFULL;
443 impl->effect_inuse[i] = TRUE;
444 *index = i + 1;
446 else
448 status = HidP_InitializeReportForID( HidP_Feature, new_effect->id, impl->preparsed,
449 impl->feature_report_buf, report_len );
450 if (status != HIDP_STATUS_SUCCESS) return status;
452 count = 1;
453 status = HidP_SetUsages( HidP_Feature, HID_USAGE_PAGE_PID, new_effect->type_coll,
454 &type, &count, impl->preparsed, impl->feature_report_buf, report_len );
455 if (status != HIDP_STATUS_SUCCESS) return status;
457 if (!HidD_SetFeature( impl->device, impl->feature_report_buf, report_len )) return DIERR_INPUTLOST;
459 status = HidP_InitializeReportForID( HidP_Feature, block_load->id, impl->preparsed,
460 impl->feature_report_buf, report_len );
461 if (status != HIDP_STATUS_SUCCESS) return status;
463 if (!HidD_GetFeature( impl->device, impl->feature_report_buf, report_len )) return DIERR_INPUTLOST;
465 count = 1;
466 status = HidP_GetUsages( HidP_Feature, HID_USAGE_PAGE_PID, block_load->status_coll,
467 &usage, &count, impl->preparsed, impl->feature_report_buf, report_len );
468 if (status != HIDP_STATUS_SUCCESS) return status;
470 if (count != 1 || usage == PID_USAGE_BLOCK_LOAD_ERROR) return DIERR_INPUTLOST;
471 if (usage == PID_USAGE_BLOCK_LOAD_FULL) return DIERR_DEVICEFULL;
473 status = HidP_GetUsageValue( HidP_Feature, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX,
474 index, impl->preparsed, impl->feature_report_buf, report_len );
475 if (status != HIDP_STATUS_SUCCESS) return status;
478 return DI_OK;
481 static BOOL enum_object( struct hid_joystick *impl, const DIPROPHEADER *filter, DWORD flags,
482 enum_object_callback callback, UINT index, struct hid_value_caps *caps,
483 DIDEVICEOBJECTINSTANCEW *instance, void *data )
485 if (flags != DIDFT_ALL && !(flags & DIDFT_GETTYPE( instance->dwType ))) return DIENUM_CONTINUE;
487 switch (filter->dwHow)
489 case DIPH_DEVICE:
490 return callback( &impl->base, index, caps, instance, data );
491 case DIPH_BYOFFSET:
492 if (filter->dwObj != instance->dwOfs) return DIENUM_CONTINUE;
493 return callback( &impl->base, index, caps, instance, data );
494 case DIPH_BYID:
495 if ((filter->dwObj & 0x00ffffff) != (instance->dwType & 0x00ffffff)) return DIENUM_CONTINUE;
496 return callback( &impl->base, index, caps, instance, data );
497 case DIPH_BYUSAGE:
498 if (HIWORD( filter->dwObj ) != instance->wUsagePage) return DIENUM_CONTINUE;
499 if (LOWORD( filter->dwObj ) != instance->wUsage) return DIENUM_CONTINUE;
500 return callback( &impl->base, index, caps, instance, data );
501 default:
502 FIXME( "unimplemented filter dwHow %#lx dwObj %#lx\n", filter->dwHow, filter->dwObj );
503 break;
506 return DIENUM_CONTINUE;
509 static void check_pid_effect_axis_caps( struct hid_joystick *impl, DIDEVICEOBJECTINSTANCEW *instance )
511 struct pid_effect_update *effect_update = &impl->pid_effect_update;
512 ULONG i;
514 for (i = 0; i < effect_update->axis_count; ++i)
516 if (effect_update->axis_caps[i]->usage_page != instance->wUsagePage) continue;
517 if (effect_update->axis_caps[i]->usage_min > instance->wUsage) continue;
518 if (effect_update->axis_caps[i]->usage_max >= instance->wUsage) break;
521 if (i == effect_update->axis_count) return;
522 instance->dwType |= DIDFT_FFACTUATOR;
523 instance->dwFlags |= DIDOI_FFACTUATOR;
526 static void set_axis_type( DIDEVICEOBJECTINSTANCEW *instance, BOOL *seen, DWORD i, DWORD *count )
528 if (!seen[i]) instance->dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( i );
529 else instance->dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 6 + (*count)++ );
530 seen[i] = TRUE;
533 static BOOL enum_objects( struct hid_joystick *impl, const DIPROPHEADER *filter, DWORD flags,
534 enum_object_callback callback, void *data )
536 DWORD collection = 0, object = 0, axis = 0, button = 0, pov = 0, value_ofs = 0, button_ofs = 0, j, count, len;
537 struct hid_preparsed_data *preparsed = (struct hid_preparsed_data *)impl->preparsed;
538 DIDEVICEOBJECTINSTANCEW instance = {.dwSize = sizeof(DIDEVICEOBJECTINSTANCEW)};
539 struct hid_value_caps *caps, *caps_end, *nary, *nary_end, *effect_caps;
540 struct hid_collection_node *node, *node_end;
541 WORD version = impl->base.dinput->dwVersion;
542 BOOL ret, seen_axis[6] = {0};
543 const WCHAR *tmp;
545 button_ofs += impl->caps.NumberInputValueCaps * sizeof(LONG);
546 if (version >= 0x800)
548 button_ofs += impl->caps.NumberOutputValueCaps * sizeof(LONG);
549 button_ofs += impl->caps.NumberFeatureValueCaps * sizeof(LONG);
552 for (caps = HID_INPUT_VALUE_CAPS( preparsed ), caps_end = caps + preparsed->input_caps_count;
553 caps != caps_end; ++caps)
555 if (!caps->usage_page) continue;
556 if (caps->flags & HID_VALUE_CAPS_IS_BUTTON) continue;
558 if (caps->usage_page >= HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN)
559 value_ofs += (caps->usage_max - caps->usage_min + 1) * sizeof(LONG);
560 else for (j = caps->usage_min; j <= caps->usage_max; ++j)
562 instance.dwOfs = value_ofs;
563 switch (MAKELONG(j, caps->usage_page))
565 case MAKELONG(HID_USAGE_GENERIC_X, HID_USAGE_PAGE_GENERIC):
566 case MAKELONG(HID_USAGE_GENERIC_Y, HID_USAGE_PAGE_GENERIC):
567 case MAKELONG(HID_USAGE_GENERIC_Z, HID_USAGE_PAGE_GENERIC):
568 case MAKELONG(HID_USAGE_GENERIC_RX, HID_USAGE_PAGE_GENERIC):
569 case MAKELONG(HID_USAGE_GENERIC_RY, HID_USAGE_PAGE_GENERIC):
570 case MAKELONG(HID_USAGE_GENERIC_RZ, HID_USAGE_PAGE_GENERIC):
571 set_axis_type( &instance, seen_axis, j - HID_USAGE_GENERIC_X, &axis );
572 instance.dwFlags = DIDOI_ASPECTPOSITION;
573 break;
574 case MAKELONG(HID_USAGE_SIMULATION_STEERING, HID_USAGE_PAGE_SIMULATION):
575 set_axis_type( &instance, seen_axis, 0, &axis );
576 instance.dwFlags = DIDOI_ASPECTPOSITION;
577 break;
578 case MAKELONG(HID_USAGE_SIMULATION_ACCELERATOR, HID_USAGE_PAGE_SIMULATION):
579 set_axis_type( &instance, seen_axis, 1, &axis );
580 instance.dwFlags = DIDOI_ASPECTPOSITION;
581 break;
582 case MAKELONG(HID_USAGE_GENERIC_WHEEL, HID_USAGE_PAGE_GENERIC):
583 case MAKELONG(HID_USAGE_SIMULATION_THROTTLE, HID_USAGE_PAGE_SIMULATION):
584 set_axis_type( &instance, seen_axis, 2, &axis );
585 instance.dwFlags = DIDOI_ASPECTPOSITION;
586 break;
587 case MAKELONG(HID_USAGE_SIMULATION_RUDDER, HID_USAGE_PAGE_SIMULATION):
588 case MAKELONG(HID_USAGE_SIMULATION_BRAKE, HID_USAGE_PAGE_SIMULATION):
589 set_axis_type( &instance, seen_axis, 5, &axis );
590 instance.dwFlags = DIDOI_ASPECTPOSITION;
591 break;
592 case MAKELONG(HID_USAGE_GENERIC_HATSWITCH, HID_USAGE_PAGE_GENERIC):
593 instance.dwType = DIDFT_POV | DIDFT_MAKEINSTANCE( pov++ );
594 instance.dwFlags = 0;
595 break;
596 case MAKELONG(HID_USAGE_GENERIC_SLIDER, HID_USAGE_PAGE_GENERIC):
597 case MAKELONG(HID_USAGE_GENERIC_DIAL, HID_USAGE_PAGE_GENERIC):
598 instance.dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 6 + axis++ );
599 instance.dwFlags = DIDOI_ASPECTPOSITION;
600 break;
601 default:
602 instance.dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 6 + axis++ );
603 instance.dwFlags = 0;
604 break;
606 instance.wUsagePage = caps->usage_page;
607 instance.wUsage = j;
608 instance.guidType = *object_usage_to_guid( instance.wUsagePage, instance.wUsage );
609 instance.wReportId = caps->report_id;
610 instance.wCollectionNumber = caps->link_collection;
611 instance.dwDimension = caps->units;
612 instance.wExponent = caps->units_exp;
613 if ((tmp = object_usage_to_string( &instance ))) lstrcpynW( instance.tszName, tmp, MAX_PATH );
614 else swprintf( instance.tszName, MAX_PATH, L"Unknown %u", DIDFT_GETINSTANCE( instance.dwType ) );
615 check_pid_effect_axis_caps( impl, &instance );
616 ret = enum_object( impl, filter, flags, callback, object, caps, &instance, data );
617 if (ret != DIENUM_CONTINUE) return ret;
618 value_ofs += sizeof(LONG);
619 object++;
623 effect_caps = impl->pid_effect_update.trigger_button_caps;
625 for (caps = HID_INPUT_VALUE_CAPS( preparsed ), caps_end = caps + preparsed->input_caps_count;
626 caps != caps_end; ++caps)
628 if (!caps->usage_page) continue;
629 if (!(caps->flags & HID_VALUE_CAPS_IS_BUTTON)) continue;
631 if (caps->usage_page >= HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN)
632 button_ofs += caps->usage_max - caps->usage_min + 1;
633 else for (j = caps->usage_min; j <= caps->usage_max; ++j)
635 instance.dwOfs = button_ofs;
636 instance.dwType = DIDFT_PSHBUTTON | DIDFT_MAKEINSTANCE( button++ );
637 instance.dwFlags = 0;
638 if (effect_caps && effect_caps->logical_min <= j && effect_caps->logical_max >= j)
640 instance.dwType |= DIDFT_FFEFFECTTRIGGER;
641 instance.dwFlags |= DIDOI_FFEFFECTTRIGGER;
643 instance.wUsagePage = caps->usage_page;
644 instance.wUsage = j;
645 instance.guidType = *object_usage_to_guid( instance.wUsagePage, instance.wUsage );
646 instance.wReportId = caps->report_id;
647 instance.wCollectionNumber = caps->link_collection;
648 instance.dwDimension = caps->units;
649 instance.wExponent = caps->units_exp;
650 swprintf( instance.tszName, MAX_PATH, L"Button %u", DIDFT_GETINSTANCE( instance.dwType ) );
651 ret = enum_object( impl, filter, flags, callback, object, caps, &instance, data );
652 if (ret != DIENUM_CONTINUE) return ret;
653 button_ofs++;
654 object++;
658 count = preparsed->output_caps_count + preparsed->feature_caps_count;
659 for (caps = HID_OUTPUT_VALUE_CAPS( preparsed ), caps_end = caps + count;
660 caps != caps_end; ++caps)
662 if (!caps->usage_page) continue;
664 if (caps->usage_page >= HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN)
666 if (caps->flags & HID_VALUE_CAPS_IS_BUTTON) button_ofs += caps->usage_max - caps->usage_min + 1;
667 else value_ofs += (caps->usage_max - caps->usage_min + 1) * sizeof(LONG);
669 else if (caps->flags & HID_VALUE_CAPS_ARRAY_HAS_MORE)
671 for (nary_end = caps - 1; caps != caps_end; caps++)
672 if (!(caps->flags & HID_VALUE_CAPS_ARRAY_HAS_MORE)) break;
674 for (nary = caps; nary != nary_end; nary--)
676 if (version < 0x800) instance.dwOfs = 0;
677 else instance.dwOfs = button_ofs;
679 instance.dwType = DIDFT_NODATA | DIDFT_MAKEINSTANCE( object++ ) | DIDFT_OUTPUT;
680 instance.dwFlags = 0x80008000;
681 instance.wUsagePage = nary->usage_page;
682 instance.wUsage = nary->usage_min;
683 instance.guidType = GUID_Unknown;
684 instance.wReportId = nary->report_id;
685 instance.wCollectionNumber = nary->link_collection;
686 instance.dwDimension = caps->units;
687 instance.wExponent = caps->units_exp;
688 if ((tmp = object_usage_to_string( &instance ))) lstrcpynW( instance.tszName, tmp, MAX_PATH );
689 else swprintf( instance.tszName, MAX_PATH, L"Unknown %u", DIDFT_GETINSTANCE( instance.dwType ) );
690 ret = enum_object( impl, filter, flags, callback, -1, nary, &instance, data );
691 if (ret != DIENUM_CONTINUE) return ret;
692 button_ofs++;
695 else for (j = caps->usage_min; j <= caps->usage_max; ++j)
697 if (version < 0x800) instance.dwOfs = 0;
698 else if (caps->flags & HID_VALUE_CAPS_IS_BUTTON) instance.dwOfs = button_ofs;
699 else instance.dwOfs = value_ofs;
701 instance.dwType = DIDFT_NODATA | DIDFT_MAKEINSTANCE( object++ ) | DIDFT_OUTPUT;
702 instance.dwFlags = 0x80008000;
703 instance.wUsagePage = caps->usage_page;
704 instance.wUsage = j;
705 instance.guidType = GUID_Unknown;
706 instance.wReportId = caps->report_id;
707 instance.wCollectionNumber = caps->link_collection;
708 instance.dwDimension = caps->units;
709 instance.wExponent = caps->units_exp;
710 if ((tmp = object_usage_to_string( &instance ))) lstrcpynW( instance.tszName, tmp, MAX_PATH );
711 else swprintf( instance.tszName, MAX_PATH, L"Unknown %u", DIDFT_GETINSTANCE( instance.dwType ) );
712 ret = enum_object( impl, filter, flags, callback, -1, caps, &instance, data );
713 if (ret != DIENUM_CONTINUE) return ret;
715 if (caps->flags & HID_VALUE_CAPS_IS_BUTTON) button_ofs++;
716 else value_ofs += sizeof(LONG);
720 for (node = HID_COLLECTION_NODES( preparsed ), node_end = node + preparsed->number_link_collection_nodes;
721 node != node_end; ++node)
723 if (!node->usage_page) continue;
724 if (node->usage_page < HID_USAGE_PAGE_VENDOR_DEFINED_BEGIN)
726 instance.dwOfs = 0;
727 instance.dwType = DIDFT_COLLECTION | DIDFT_MAKEINSTANCE( collection++ ) | DIDFT_NODATA;
728 instance.dwFlags = 0;
729 instance.wUsagePage = node->usage_page;
730 instance.wUsage = node->usage;
731 instance.guidType = GUID_Unknown;
732 instance.wReportId = 0;
733 instance.wCollectionNumber = node->parent;
734 instance.dwDimension = 0;
735 instance.wExponent = 0;
736 len = swprintf( instance.tszName, MAX_PATH, L"Collection %u - ", DIDFT_GETINSTANCE( instance.dwType ) );
737 if ((tmp = object_usage_to_string( &instance ))) lstrcpynW( instance.tszName + len, tmp, MAX_PATH - len );
738 else swprintf( instance.tszName + len, MAX_PATH - len, L"Unknown %u", DIDFT_GETINSTANCE( instance.dwType ) );
739 ret = enum_object( impl, filter, flags, callback, -1, NULL, &instance, data );
740 if (ret != DIENUM_CONTINUE) return ret;
744 return DIENUM_CONTINUE;
747 static void set_report_value( struct hid_joystick *impl, char *report_buf,
748 struct hid_value_caps *caps, LONG value )
750 ULONG report_len = impl->caps.OutputReportByteLength;
751 PHIDP_PREPARSED_DATA preparsed = impl->preparsed;
752 LONG log_min, log_max, phy_min, phy_max;
753 NTSTATUS status;
755 if (!caps) return;
757 log_min = caps->logical_min;
758 log_max = caps->logical_max;
759 phy_min = caps->physical_min;
760 phy_max = caps->physical_max;
762 if (phy_max || phy_min)
764 if (value > phy_max || value < phy_min) value = -1;
765 else value = log_min + (value - phy_min) * (log_max - log_min) / (phy_max - phy_min);
768 status = HidP_SetUsageValue( HidP_Output, caps->usage_page, caps->link_collection,
769 caps->usage_min, value, preparsed, report_buf, report_len );
770 if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_SetUsageValue %04x:%04x returned %#lx\n",
771 caps->usage_page, caps->usage_min, status );
774 static void hid_joystick_destroy( IDirectInputDevice8W *iface )
776 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
777 TRACE( "iface %p.\n", iface );
779 free( impl->usages_buf );
780 free( impl->feature_report_buf );
781 free( impl->output_report_buf );
782 free( impl->input_report_buf );
783 HidD_FreePreparsedData( impl->preparsed );
784 CloseHandle( impl->base.read_event );
785 CloseHandle( impl->device );
788 static HRESULT hid_joystick_get_property( IDirectInputDevice8W *iface, DWORD property,
789 DIPROPHEADER *header, const DIDEVICEOBJECTINSTANCEW *instance )
791 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
793 switch (property)
795 case (DWORD_PTR)DIPROP_PRODUCTNAME:
797 DIPROPSTRING *value = (DIPROPSTRING *)header;
798 lstrcpynW( value->wsz, impl->base.instance.tszProductName, MAX_PATH );
799 return DI_OK;
801 case (DWORD_PTR)DIPROP_INSTANCENAME:
803 DIPROPSTRING *value = (DIPROPSTRING *)header;
804 lstrcpynW( value->wsz, impl->base.instance.tszInstanceName, MAX_PATH );
805 return DI_OK;
807 case (DWORD_PTR)DIPROP_VIDPID:
809 DIPROPDWORD *value = (DIPROPDWORD *)header;
810 if (!impl->attrs.VendorID || !impl->attrs.ProductID) return DIERR_UNSUPPORTED;
811 value->dwData = MAKELONG( impl->attrs.VendorID, impl->attrs.ProductID );
812 return DI_OK;
814 case (DWORD_PTR)DIPROP_JOYSTICKID:
816 DIPROPDWORD *value = (DIPROPDWORD *)header;
817 value->dwData = impl->base.instance.guidInstance.Data3;
818 return DI_OK;
820 case (DWORD_PTR)DIPROP_GUIDANDPATH:
822 DIPROPGUIDANDPATH *value = (DIPROPGUIDANDPATH *)header;
823 value->guidClass = GUID_DEVCLASS_HIDCLASS;
824 lstrcpynW( value->wszPath, impl->device_path, MAX_PATH );
825 return DI_OK;
827 case (DWORD_PTR)DIPROP_FFLOAD:
829 DIPROPDWORD *value = (DIPROPDWORD *)header;
830 if (!(impl->base.caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_UNSUPPORTED;
831 if (!is_exclusively_acquired( impl )) return DIERR_NOTEXCLUSIVEACQUIRED;
832 value->dwData = 0;
833 return DI_OK;
837 return DIERR_UNSUPPORTED;
840 static HRESULT hid_joystick_send_device_gain( IDirectInputDevice8W *iface, LONG device_gain )
842 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
843 struct pid_device_gain *report = &impl->pid_device_gain;
844 ULONG report_len = impl->caps.OutputReportByteLength;
845 char *report_buf = impl->output_report_buf;
846 NTSTATUS status;
848 TRACE( "iface %p.\n", iface );
850 if (!report->id || !report->device_gain_caps) return DI_OK;
852 status = HidP_InitializeReportForID( HidP_Output, report->id, impl->preparsed, report_buf, report_len );
853 if (status != HIDP_STATUS_SUCCESS) return status;
855 set_report_value( impl, report_buf, report->device_gain_caps, device_gain );
857 if (!WriteFile( impl->device, report_buf, report_len, NULL, NULL )) return DIERR_INPUTLOST;
858 return DI_OK;
861 static HRESULT hid_joystick_acquire( IDirectInputDevice8W *iface )
863 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
864 ULONG report_len = impl->caps.InputReportByteLength;
865 BOOL ret;
867 if (impl->device == INVALID_HANDLE_VALUE)
869 impl->device = CreateFileW( impl->device_path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
870 NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, 0 );
871 if (impl->device == INVALID_HANDLE_VALUE) return DIERR_UNPLUGGED;
874 memset( &impl->read_ovl, 0, sizeof(impl->read_ovl) );
875 impl->read_ovl.hEvent = impl->base.read_event;
876 ret = ReadFile( impl->device, impl->input_report_buf, report_len, NULL, &impl->read_ovl );
877 if (!ret && GetLastError() != ERROR_IO_PENDING)
879 CloseHandle( impl->device );
880 impl->device = INVALID_HANDLE_VALUE;
881 return DIERR_UNPLUGGED;
884 IDirectInputDevice8_SendForceFeedbackCommand( iface, DISFFC_RESET );
885 return DI_OK;
888 static HRESULT hid_joystick_send_force_feedback_command( IDirectInputDevice8W *iface, DWORD command, BOOL unacquire );
890 static HRESULT hid_joystick_unacquire( IDirectInputDevice8W *iface )
892 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
893 BOOL ret;
895 if (impl->device == INVALID_HANDLE_VALUE) return DI_NOEFFECT;
897 ret = CancelIoEx( impl->device, &impl->read_ovl );
898 if (!ret) WARN( "CancelIoEx failed, last error %lu\n", GetLastError() );
899 else WaitForSingleObject( impl->base.read_event, INFINITE );
901 if (!(impl->base.caps.dwFlags & DIDC_FORCEFEEDBACK)) return DI_OK;
902 if (!is_exclusively_acquired( impl )) return DI_OK;
903 hid_joystick_send_force_feedback_command( iface, DISFFC_RESET, TRUE );
904 return DI_OK;
907 static HRESULT hid_joystick_create_effect( IDirectInputDevice8W *iface, IDirectInputEffect **out );
909 static HRESULT hid_joystick_get_effect_info( IDirectInputDevice8W *iface, DIEFFECTINFOW *info, const GUID *guid )
911 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
912 struct pid_effect_update *effect_update = &impl->pid_effect_update;
913 struct pid_set_condition *set_condition = &impl->pid_set_condition;
914 struct pid_set_periodic *set_periodic = &impl->pid_set_periodic;
915 struct pid_set_envelope *set_envelope = &impl->pid_set_envelope;
916 PHIDP_PREPARSED_DATA preparsed = impl->preparsed;
917 HIDP_BUTTON_CAPS button;
918 ULONG type, collection;
919 NTSTATUS status;
920 USAGE usage = 0;
921 USHORT count;
923 switch ((usage = effect_guid_to_usage( guid )))
925 case PID_USAGE_ET_SQUARE:
926 case PID_USAGE_ET_SINE:
927 case PID_USAGE_ET_TRIANGLE:
928 case PID_USAGE_ET_SAWTOOTH_UP:
929 case PID_USAGE_ET_SAWTOOTH_DOWN:
930 type = DIEFT_PERIODIC;
931 break;
932 case PID_USAGE_ET_SPRING:
933 case PID_USAGE_ET_DAMPER:
934 case PID_USAGE_ET_INERTIA:
935 case PID_USAGE_ET_FRICTION:
936 type = DIEFT_CONDITION;
937 break;
938 case PID_USAGE_ET_CONSTANT_FORCE:
939 type = DIEFT_CONSTANTFORCE;
940 break;
941 case PID_USAGE_ET_RAMP:
942 type = DIEFT_RAMPFORCE;
943 break;
944 case PID_USAGE_ET_CUSTOM_FORCE_DATA:
945 type = DIEFT_CUSTOMFORCE;
946 break;
947 default:
948 return DIERR_DEVICENOTREG;
951 if (!(collection = effect_update->collection)) return DIERR_DEVICENOTREG;
953 info->dwDynamicParams = DIEP_TYPESPECIFICPARAMS;
954 if (effect_update->axis_count) info->dwDynamicParams |= DIEP_AXES;
955 if (effect_update->duration_caps) info->dwDynamicParams |= DIEP_DURATION;
956 if (effect_update->gain_caps) info->dwDynamicParams |= DIEP_GAIN;
957 if (effect_update->sample_period_caps) info->dwDynamicParams |= DIEP_SAMPLEPERIOD;
958 if (effect_update->start_delay_caps)
960 type |= DIEFT_STARTDELAY;
961 info->dwDynamicParams |= DIEP_STARTDELAY;
963 if (effect_update->direction_coll) info->dwDynamicParams |= DIEP_DIRECTION;
964 if (effect_update->axes_coll) info->dwDynamicParams |= DIEP_AXES;
966 if (!(collection = effect_update->type_coll)) return DIERR_DEVICENOTREG;
967 else
969 count = 1;
970 status = HidP_GetSpecificButtonCaps( HidP_Output, HID_USAGE_PAGE_PID, collection,
971 usage, &button, &count, preparsed );
972 if (status != HIDP_STATUS_SUCCESS)
974 WARN( "HidP_GetSpecificButtonCaps %#x returned %#lx\n", usage, status );
975 return DIERR_DEVICENOTREG;
977 else if (!count)
979 WARN( "effect usage %#x not found\n", usage );
980 return DIERR_DEVICENOTREG;
984 if ((DIEFT_GETTYPE(type) == DIEFT_PERIODIC) && (collection = set_periodic->collection))
986 if (set_periodic->magnitude_caps) info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS;
987 if (set_periodic->offset_caps) info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS;
988 if (set_periodic->period_caps) info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS;
989 if (set_periodic->phase_caps) info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS;
992 if ((DIEFT_GETTYPE(type) == DIEFT_PERIODIC ||
993 DIEFT_GETTYPE(type) == DIEFT_RAMPFORCE ||
994 DIEFT_GETTYPE(type) == DIEFT_CONSTANTFORCE) &&
995 (collection = set_envelope->collection))
997 info->dwDynamicParams |= DIEP_ENVELOPE;
998 if (set_envelope->attack_level_caps) type |= DIEFT_FFATTACK;
999 if (set_envelope->attack_time_caps) type |= DIEFT_FFATTACK;
1000 if (set_envelope->fade_level_caps) type |= DIEFT_FFFADE;
1001 if (set_envelope->fade_time_caps) type |= DIEFT_FFFADE;
1002 if (effect_update->trigger_button_caps) info->dwDynamicParams |= DIEP_TRIGGERBUTTON;
1003 if (effect_update->trigger_repeat_interval_caps) info->dwDynamicParams |= DIEP_TRIGGERREPEATINTERVAL;
1006 if (DIEFT_GETTYPE(type) == DIEFT_CONDITION && (collection = set_condition->collection))
1008 if (set_condition->center_point_offset_caps)
1009 info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS;
1010 if (set_condition->positive_coefficient_caps || set_condition->negative_coefficient_caps)
1011 info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS;
1012 if (set_condition->positive_saturation_caps || set_condition->negative_saturation_caps)
1014 info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS;
1015 type |= DIEFT_SATURATION;
1017 if (set_condition->dead_band_caps)
1019 info->dwDynamicParams |= DIEP_TYPESPECIFICPARAMS;
1020 type |= DIEFT_DEADBAND;
1024 info->guid = *guid;
1025 info->dwEffType = type;
1026 info->dwStaticParams = info->dwDynamicParams;
1027 lstrcpynW( info->tszName, effect_guid_to_string( guid ), MAX_PATH );
1029 return DI_OK;
1032 static BOOL CALLBACK unload_effect_object( IDirectInputEffect *effect, void *context )
1034 IDirectInputEffect_Unload( effect );
1035 return DIENUM_CONTINUE;
1038 static HRESULT hid_joystick_send_force_feedback_command( IDirectInputDevice8W *iface, DWORD command, BOOL unacquire )
1040 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
1041 struct pid_control_report *report = &impl->pid_device_control;
1042 ULONG report_len = impl->caps.OutputReportByteLength;
1043 char *report_buf = impl->output_report_buf;
1044 NTSTATUS status;
1045 USAGE usage;
1046 ULONG count;
1048 TRACE( "iface %p, command %#lx.\n", iface, command );
1050 switch (command)
1052 case DISFFC_RESET: usage = PID_USAGE_DC_DEVICE_RESET; break;
1053 case DISFFC_STOPALL: usage = PID_USAGE_DC_STOP_ALL_EFFECTS; break;
1054 case DISFFC_PAUSE: usage = PID_USAGE_DC_DEVICE_PAUSE; break;
1055 case DISFFC_CONTINUE: usage = PID_USAGE_DC_DEVICE_CONTINUE; break;
1056 case DISFFC_SETACTUATORSON: usage = PID_USAGE_DC_ENABLE_ACTUATORS; break;
1057 case DISFFC_SETACTUATORSOFF: usage = PID_USAGE_DC_DISABLE_ACTUATORS; break;
1060 if (command == DISFFC_RESET)
1062 IDirectInputDevice8_EnumCreatedEffectObjects( iface, unload_effect_object, NULL, 0 );
1063 impl->base.force_feedback_state = DIGFFS_STOPPED | DIGFFS_EMPTY;
1066 count = 1;
1067 status = HidP_InitializeReportForID( HidP_Output, report->id, impl->preparsed, report_buf, report_len );
1068 if (status != HIDP_STATUS_SUCCESS) return status;
1070 status = HidP_SetUsages( HidP_Output, HID_USAGE_PAGE_PID, report->control_coll, &usage,
1071 &count, impl->preparsed, report_buf, report_len );
1072 if (status != HIDP_STATUS_SUCCESS) return status;
1074 if (!WriteFile( impl->device, report_buf, report_len, NULL, NULL )) return DIERR_INPUTLOST;
1075 if (!unacquire && command == DISFFC_RESET) hid_joystick_send_device_gain( iface, impl->base.device_gain );
1077 return DI_OK;
1080 static HRESULT hid_joystick_enum_created_effect_objects( IDirectInputDevice8W *iface,
1081 LPDIENUMCREATEDEFFECTOBJECTSCALLBACK callback,
1082 void *context, DWORD flags )
1084 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
1085 struct hid_joystick_effect *effect, *next;
1087 TRACE( "iface %p, callback %p, context %p, flags %#lx.\n", iface, callback, context, flags );
1089 LIST_FOR_EACH_ENTRY_SAFE(effect, next, &impl->effect_list, struct hid_joystick_effect, entry)
1090 if (callback( &effect->IDirectInputEffect_iface, context ) != DIENUM_CONTINUE) break;
1092 return DI_OK;
1095 struct parse_device_state_params
1097 BYTE old_state[DEVICE_STATE_MAX_SIZE];
1098 BYTE buttons[128];
1099 DWORD time;
1100 DWORD seq;
1103 static BOOL check_device_state_button( struct dinput_device *device, UINT index, struct hid_value_caps *caps,
1104 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
1106 IDirectInputDevice8W *iface = &device->IDirectInputDevice8W_iface;
1107 struct parse_device_state_params *params = data;
1108 BYTE old_value, value;
1110 if (instance->wReportId != device->device_state_report_id) return DIENUM_CONTINUE;
1112 value = params->buttons[instance->wUsage - 1];
1113 old_value = params->old_state[instance->dwOfs];
1114 device->device_state[instance->dwOfs] = value;
1115 if (old_value != value) queue_event( iface, index, value, params->time, params->seq );
1117 return DIENUM_CONTINUE;
1120 static LONG sign_extend( ULONG value, struct object_properties *properties )
1122 UINT sign = 1 << (properties->bit_size - 1);
1123 if (sign <= 1 || properties->logical_min >= 0) return value;
1124 return value - ((value & sign) << 1);
1127 static LONG scale_value( ULONG value, struct object_properties *properties )
1129 LONG tmp = sign_extend( value, properties ), log_min, log_max, phy_min, phy_max;
1130 log_min = properties->logical_min;
1131 log_max = properties->logical_max;
1132 phy_min = properties->range_min;
1133 phy_max = properties->range_max;
1135 if (log_min > tmp || log_max < tmp) return -1; /* invalid / null value */
1136 return phy_min + MulDiv( tmp - log_min, phy_max - phy_min, log_max - log_min );
1139 static LONG scale_axis_value( ULONG value, struct object_properties *properties )
1141 LONG tmp = sign_extend( value, properties ), log_ctr, log_min, log_max, phy_ctr, phy_min, phy_max;
1142 ULONG bit_max = (1 << properties->bit_size) - 1;
1144 log_min = properties->logical_min;
1145 log_max = properties->logical_max;
1146 phy_min = properties->range_min;
1147 phy_max = properties->range_max;
1148 /* xinput HID gamepad have bogus logical value range, let's use the bit range instead */
1149 if (log_min == 0 && log_max == -1) log_max = bit_max;
1151 if (phy_min == 0) phy_ctr = phy_max >> 1;
1152 else phy_ctr = round( (phy_min + phy_max) / 2.0 );
1153 if (log_min == 0) log_ctr = log_max >> 1;
1154 else log_ctr = round( (log_min + log_max) / 2.0 );
1156 tmp -= log_ctr;
1157 if (tmp <= 0)
1159 log_max = MulDiv( log_min - log_ctr, properties->deadzone, 10000 );
1160 log_min = MulDiv( log_min - log_ctr, properties->saturation, 10000 );
1161 phy_max = phy_ctr;
1163 else
1165 log_min = MulDiv( log_max - log_ctr, properties->deadzone, 10000 );
1166 log_max = MulDiv( log_max - log_ctr, properties->saturation, 10000 );
1167 phy_min = phy_ctr;
1170 if (tmp <= log_min) return phy_min;
1171 if (tmp >= log_max) return phy_max;
1172 return phy_min + MulDiv( tmp - log_min, phy_max - phy_min, log_max - log_min );
1175 static BOOL read_device_state_value( struct dinput_device *device, UINT index, struct hid_value_caps *caps,
1176 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
1178 struct hid_joystick *impl = CONTAINING_RECORD( device, struct hid_joystick, base );
1179 IDirectInputDevice8W *iface = &impl->base.IDirectInputDevice8W_iface;
1180 ULONG logical_value, report_len = impl->caps.InputReportByteLength;
1181 struct parse_device_state_params *params = data;
1182 char *report_buf = impl->input_report_buf;
1183 struct object_properties *properties;
1184 LONG old_value, value;
1185 NTSTATUS status;
1187 if (index == -1) return DIENUM_STOP;
1188 properties = device->object_properties + index;
1190 if (instance->wReportId != impl->base.device_state_report_id) return DIENUM_CONTINUE;
1192 status = HidP_GetUsageValue( HidP_Input, instance->wUsagePage, 0, instance->wUsage,
1193 &logical_value, impl->preparsed, report_buf, report_len );
1194 if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_GetUsageValue %04x:%04x returned %#lx\n",
1195 instance->wUsagePage, instance->wUsage, status );
1196 if (instance->dwType & DIDFT_AXIS) value = scale_axis_value( logical_value, properties );
1197 else value = scale_value( logical_value, properties );
1199 old_value = *(LONG *)(params->old_state + instance->dwOfs);
1200 *(LONG *)(impl->base.device_state + instance->dwOfs) = value;
1201 if (old_value != value) queue_event( iface, index, value, params->time, params->seq );
1203 return DIENUM_CONTINUE;
1206 static HRESULT hid_joystick_read( IDirectInputDevice8W *iface )
1208 static const DIPROPHEADER filter =
1210 .dwSize = sizeof(filter),
1211 .dwHeaderSize = sizeof(filter),
1212 .dwHow = DIPH_DEVICE,
1214 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
1215 ULONG i, index, count, report_len = impl->caps.InputReportByteLength;
1216 DIDATAFORMAT *format = &impl->base.device_format;
1217 char *report_buf = impl->input_report_buf;
1218 struct parse_device_state_params params;
1219 struct hid_joystick_effect *effect;
1220 UINT device_state, effect_state;
1221 USAGE_AND_PAGE *usages;
1222 NTSTATUS status;
1223 HRESULT hr;
1224 BOOL ret;
1226 ret = GetOverlappedResult( impl->device, &impl->read_ovl, &count, FALSE );
1228 EnterCriticalSection( &impl->base.crit );
1229 while (ret)
1231 if (TRACE_ON(dinput))
1233 TRACE( "iface %p, size %lu, report:\n", iface, count );
1234 for (i = 0; i < count;)
1236 char buffer[256], *buf = buffer;
1237 buf += sprintf(buf, "%08lx ", i);
1238 do { buf += sprintf(buf, " %02x", (BYTE)report_buf[i] ); }
1239 while (++i % 16 && i < count);
1240 TRACE("%s\n", buffer);
1244 count = impl->usages_count;
1245 memset( impl->usages_buf, 0, count * sizeof(*impl->usages_buf) );
1246 status = HidP_GetUsagesEx( HidP_Input, 0, impl->usages_buf, &count,
1247 impl->preparsed, report_buf, report_len );
1248 if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_GetUsagesEx returned %#lx\n", status );
1250 if (report_buf[0] == impl->base.device_state_report_id)
1252 params.time = GetCurrentTime();
1253 params.seq = impl->base.dinput->evsequence++;
1254 memcpy( params.old_state, impl->base.device_state, format->dwDataSize );
1255 memset( params.buttons, 0, sizeof(params.buttons) );
1256 memset( impl->base.device_state, 0, format->dwDataSize );
1258 while (count--)
1260 usages = impl->usages_buf + count;
1261 if (usages->UsagePage != HID_USAGE_PAGE_BUTTON)
1262 FIXME( "unimplemented usage page %x.\n", usages->UsagePage );
1263 else if (usages->Usage >= 128)
1264 FIXME( "ignoring extraneous button %d.\n", usages->Usage );
1265 else
1266 params.buttons[usages->Usage - 1] = 0x80;
1269 enum_objects( impl, &filter, DIDFT_AXIS | DIDFT_POV, read_device_state_value, &params );
1270 enum_objects( impl, &filter, DIDFT_BUTTON, check_device_state_button, &params );
1271 if (impl->base.hEvent && memcmp( &params.old_state, impl->base.device_state, format->dwDataSize ))
1272 SetEvent( impl->base.hEvent );
1274 else if (report_buf[0] == impl->pid_effect_state.id && is_exclusively_acquired( impl ))
1276 status = HidP_GetUsageValue( HidP_Input, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX,
1277 &index, impl->preparsed, report_buf, report_len );
1278 if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_GetUsageValue EFFECT_BLOCK_INDEX returned %#lx\n", status );
1280 effect_state = 0;
1281 device_state = impl->base.force_feedback_state & DIGFFS_EMPTY;
1282 while (count--)
1284 USAGE_AND_PAGE *button = impl->usages_buf + count;
1285 if (button->UsagePage != HID_USAGE_PAGE_PID)
1286 FIXME( "unimplemented usage page %#04x.\n", button->UsagePage );
1287 else switch (button->Usage)
1289 case PID_USAGE_DEVICE_PAUSED: device_state |= DIGFFS_PAUSED; break;
1290 case PID_USAGE_ACTUATORS_ENABLED: device_state |= DIGFFS_ACTUATORSON; break;
1291 case PID_USAGE_SAFETY_SWITCH: device_state |= DIGFFS_SAFETYSWITCHON; break;
1292 case PID_USAGE_ACTUATOR_OVERRIDE_SWITCH: device_state |= DIGFFS_USERFFSWITCHON; break;
1293 case PID_USAGE_ACTUATOR_POWER: device_state |= DIGFFS_POWERON; break;
1294 case PID_USAGE_EFFECT_PLAYING: effect_state = DIEGES_PLAYING; break;
1295 default: FIXME( "unimplemented usage %#04x\n", button->Usage ); break;
1298 if (!(device_state & DIGFFS_ACTUATORSON)) device_state |= DIGFFS_ACTUATORSOFF;
1299 if (!(device_state & DIGFFS_SAFETYSWITCHON) && impl->pid_effect_state.safety_switch_caps)
1300 device_state |= DIGFFS_SAFETYSWITCHOFF;
1301 if (!(device_state & DIGFFS_USERFFSWITCHON) && impl->pid_effect_state.actuator_override_switch_caps)
1302 device_state |= DIGFFS_USERFFSWITCHOFF;
1303 if (!(device_state & DIGFFS_POWERON) && impl->pid_effect_state.actuator_power_caps)
1304 device_state |= DIGFFS_POWEROFF;
1306 TRACE( "effect %lu state %#x, device state %#x\n", index, effect_state, device_state );
1308 LIST_FOR_EACH_ENTRY( effect, &impl->effect_list, struct hid_joystick_effect, entry )
1309 if (effect->index == index) effect->status = effect_state;
1310 impl->base.force_feedback_state = device_state;
1313 memset( &impl->read_ovl, 0, sizeof(impl->read_ovl) );
1314 impl->read_ovl.hEvent = impl->base.read_event;
1315 ret = ReadFile( impl->device, report_buf, report_len, &count, &impl->read_ovl );
1318 if (GetLastError() == ERROR_IO_PENDING || GetLastError() == ERROR_OPERATION_ABORTED) hr = DI_OK;
1319 else
1321 WARN( "GetOverlappedResult/ReadFile failed, error %lu\n", GetLastError() );
1322 CloseHandle(impl->device);
1323 impl->device = INVALID_HANDLE_VALUE;
1324 hr = DIERR_INPUTLOST;
1326 LeaveCriticalSection( &impl->base.crit );
1328 return hr;
1331 static HRESULT hid_joystick_enum_objects( IDirectInputDevice8W *iface, const DIPROPHEADER *filter,
1332 DWORD flags, enum_object_callback callback, void *context )
1334 struct hid_joystick *impl = impl_from_IDirectInputDevice8W( iface );
1335 return enum_objects( impl, filter, flags, callback, context );
1338 static const struct dinput_device_vtbl hid_joystick_vtbl =
1340 hid_joystick_destroy,
1341 NULL,
1342 hid_joystick_read,
1343 hid_joystick_acquire,
1344 hid_joystick_unacquire,
1345 hid_joystick_enum_objects,
1346 hid_joystick_get_property,
1347 hid_joystick_get_effect_info,
1348 hid_joystick_create_effect,
1349 hid_joystick_send_force_feedback_command,
1350 hid_joystick_send_device_gain,
1351 hid_joystick_enum_created_effect_objects,
1354 static DWORD device_type_for_version( DWORD type, DWORD version )
1356 if (version >= 0x0800) return type;
1358 switch (GET_DIDEVICE_TYPE( type ))
1360 case DI8DEVTYPE_JOYSTICK:
1361 if (GET_DIDEVICE_SUBTYPE( type ) == DI8DEVTYPEJOYSTICK_LIMITED)
1362 return DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_UNKNOWN << 8) | DIDEVTYPE_HID;
1363 return DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8) | DIDEVTYPE_HID;
1365 case DI8DEVTYPE_GAMEPAD:
1366 return DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_GAMEPAD << 8) | DIDEVTYPE_HID;
1368 case DI8DEVTYPE_DRIVING:
1369 return DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_WHEEL << 8) | DIDEVTYPE_HID;
1371 case DI8DEVTYPE_FLIGHT:
1372 return DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_FLIGHTSTICK << 8) | DIDEVTYPE_HID;
1374 case DI8DEVTYPE_SUPPLEMENTAL:
1375 if (GET_DIDEVICE_SUBTYPE( type ) == DI8DEVTYPESUPPLEMENTAL_HEADTRACKER)
1376 return DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_HEADTRACKER << 8) | DIDEVTYPE_HID;
1377 if (GET_DIDEVICE_SUBTYPE( type ) == DI8DEVTYPESUPPLEMENTAL_RUDDERPEDALS)
1378 return DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_RUDDER << 8) | DIDEVTYPE_HID;
1379 return DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_UNKNOWN << 8) | DIDEVTYPE_HID;
1381 case DI8DEVTYPE_1STPERSON:
1382 return DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_UNKNOWN << 8) | DIDEVTYPE_HID;
1384 default:
1385 return DIDEVTYPE_DEVICE | DIDEVTYPE_HID;
1389 static HRESULT hid_joystick_device_try_open( const WCHAR *path, HANDLE *device, PHIDP_PREPARSED_DATA *preparsed,
1390 HIDD_ATTRIBUTES *attrs, HIDP_CAPS *caps, DIDEVICEINSTANCEW *instance,
1391 DWORD version )
1393 BOOL has_accelerator, has_brake, has_clutch, has_z, has_pov;
1394 PHIDP_PREPARSED_DATA preparsed_data = NULL;
1395 HIDP_LINK_COLLECTION_NODE nodes[256];
1396 DWORD type, size, button_count = 0;
1397 HIDP_BUTTON_CAPS buttons[10];
1398 HIDP_VALUE_CAPS value;
1399 HANDLE device_file;
1400 ULONG node_count;
1401 NTSTATUS status;
1402 UINT32 handle;
1403 USHORT count;
1405 device_file = CreateFileW( path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
1406 NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, 0 );
1407 if (device_file == INVALID_HANDLE_VALUE) return DIERR_DEVICENOTREG;
1409 if (!HidD_GetPreparsedData( device_file, &preparsed_data )) goto failed;
1410 if (!HidD_GetAttributes( device_file, attrs )) goto failed;
1411 if (HidP_GetCaps( preparsed_data, caps ) != HIDP_STATUS_SUCCESS) goto failed;
1413 switch (MAKELONG( caps->Usage, caps->UsagePage ))
1415 case MAKELONG( HID_USAGE_GENERIC_MOUSE, HID_USAGE_PAGE_GENERIC ): goto failed;
1416 case MAKELONG( HID_USAGE_GENERIC_KEYBOARD, HID_USAGE_PAGE_GENERIC ): goto failed;
1417 case MAKELONG( HID_USAGE_GENERIC_GAMEPAD, HID_USAGE_PAGE_GENERIC ): type = DI8DEVTYPE_GAMEPAD; break;
1418 case MAKELONG( HID_USAGE_GENERIC_JOYSTICK, HID_USAGE_PAGE_GENERIC ): type = DI8DEVTYPE_JOYSTICK; break;
1419 default: FIXME( "device usage %04x:%04x not implemented!\n", caps->UsagePage, caps->Usage); goto failed;
1422 if (!HidD_GetProductString( device_file, instance->tszInstanceName, MAX_PATH * sizeof(WCHAR) )) goto failed;
1423 if (!HidD_GetProductString( device_file, instance->tszProductName, MAX_PATH * sizeof(WCHAR) )) goto failed;
1425 if (!DeviceIoControl( device_file, IOCTL_HID_GET_WINE_RAWINPUT_HANDLE, NULL, 0, &handle, sizeof(handle), &size, NULL ))
1427 ERR( "failed to get raw input handle, error %lu\n", GetLastError() );
1428 goto failed;
1431 instance->guidInstance = hid_joystick_guid;
1432 instance->guidInstance.Data1 ^= handle;
1433 instance->guidProduct = dinput_pidvid_guid;
1434 instance->guidProduct.Data1 = MAKELONG( attrs->VendorID, attrs->ProductID );
1435 instance->guidFFDriver = GUID_NULL;
1436 instance->wUsagePage = caps->UsagePage;
1437 instance->wUsage = caps->Usage;
1439 node_count = ARRAY_SIZE(nodes);
1440 status = HidP_GetLinkCollectionNodes( nodes, &node_count, preparsed_data );
1441 if (status != HIDP_STATUS_SUCCESS) node_count = 0;
1442 while (node_count--)
1444 if (nodes[node_count].LinkUsagePage != HID_USAGE_PAGE_SIMULATION) continue;
1445 if (nodes[node_count].LinkUsage == HID_USAGE_SIMULATION_AUTOMOBILE_SIMULATION_DEVICE) type = DI8DEVTYPE_DRIVING;
1446 if (nodes[node_count].LinkUsage == HID_USAGE_SIMULATION_FLIGHT_SIMULATION_DEVICE) type = DI8DEVTYPE_FLIGHT;
1449 count = ARRAY_SIZE(buttons);
1450 status = HidP_GetSpecificButtonCaps( HidP_Output, HID_USAGE_PAGE_PID, 0,
1451 PID_USAGE_DC_DEVICE_RESET, buttons, &count, preparsed_data );
1452 if (status == HIDP_STATUS_SUCCESS && count > 0)
1453 instance->guidFFDriver = IID_IDirectInputPIDDriver;
1455 count = ARRAY_SIZE(buttons);
1456 status = HidP_GetSpecificButtonCaps( HidP_Input, HID_USAGE_PAGE_BUTTON, 0, 0, buttons, &count, preparsed_data );
1457 if (status != HIDP_STATUS_SUCCESS) count = button_count = 0;
1458 while (count--)
1460 if (!buttons[count].IsRange) button_count += 1;
1461 else button_count += buttons[count].Range.UsageMax - buttons[count].Range.UsageMin + 1;
1464 count = 1;
1465 status = HidP_GetSpecificValueCaps( HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_X,
1466 &value, &count, preparsed_data );
1467 if (status != HIDP_STATUS_SUCCESS || !count) type = DI8DEVTYPE_SUPPLEMENTAL;
1469 count = 1;
1470 status = HidP_GetSpecificValueCaps( HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Y,
1471 &value, &count, preparsed_data );
1472 if (status != HIDP_STATUS_SUCCESS || !count) type = DI8DEVTYPE_SUPPLEMENTAL;
1474 count = 1;
1475 status = HidP_GetSpecificValueCaps( HidP_Input, HID_USAGE_PAGE_SIMULATION, 0, HID_USAGE_SIMULATION_STEERING,
1476 &value, &count, preparsed_data );
1477 if (status == HIDP_STATUS_SUCCESS && count) type = DI8DEVTYPE_DRIVING;
1479 switch (GET_DIDEVICE_TYPE(type))
1481 case DI8DEVTYPE_SUPPLEMENTAL:
1482 type |= (DI8DEVTYPESUPPLEMENTAL_UNKNOWN << 8);
1483 break;
1484 case DI8DEVTYPE_GAMEPAD:
1485 if (button_count < 6) type |= (DI8DEVTYPEGAMEPAD_LIMITED << 8);
1486 else type |= (DI8DEVTYPEGAMEPAD_STANDARD << 8);
1487 break;
1488 case DI8DEVTYPE_JOYSTICK:
1489 count = 1;
1490 status = HidP_GetSpecificValueCaps( HidP_Input, HID_USAGE_PAGE_GENERIC, 0,
1491 HID_USAGE_GENERIC_Z, &value, &count, preparsed_data );
1492 has_z = (status == HIDP_STATUS_SUCCESS && count);
1494 count = 1;
1495 status = HidP_GetSpecificValueCaps( HidP_Input, HID_USAGE_PAGE_GENERIC, 0,
1496 HID_USAGE_GENERIC_HATSWITCH, &value, &count, preparsed_data );
1497 has_pov = (status == HIDP_STATUS_SUCCESS && count);
1499 if (button_count < 5 || !has_z || !has_pov)
1500 type |= (DI8DEVTYPEJOYSTICK_LIMITED << 8);
1501 else
1502 type |= (DI8DEVTYPEJOYSTICK_STANDARD << 8);
1503 break;
1504 case DI8DEVTYPE_DRIVING:
1505 count = 1;
1506 status = HidP_GetSpecificValueCaps( HidP_Input, HID_USAGE_PAGE_SIMULATION, 0, HID_USAGE_SIMULATION_ACCELERATOR,
1507 &value, &count, preparsed_data );
1508 has_accelerator = (status == HIDP_STATUS_SUCCESS && count);
1510 count = 1;
1511 status = HidP_GetSpecificValueCaps( HidP_Input, HID_USAGE_PAGE_SIMULATION, 0, HID_USAGE_SIMULATION_BRAKE,
1512 &value, &count, preparsed_data );
1513 has_brake = (status == HIDP_STATUS_SUCCESS && count);
1515 count = 1;
1516 status = HidP_GetSpecificValueCaps( HidP_Input, HID_USAGE_PAGE_SIMULATION, 0, HID_USAGE_SIMULATION_CLUTCH,
1517 &value, &count, preparsed_data );
1518 has_clutch = (status == HIDP_STATUS_SUCCESS && count);
1520 if (button_count < 4)
1521 type |= (DI8DEVTYPEDRIVING_LIMITED << 8);
1522 else if (has_accelerator && has_brake && has_clutch)
1523 type |= (DI8DEVTYPEDRIVING_THREEPEDALS << 8);
1524 else if (has_accelerator && has_brake)
1525 type |= (DI8DEVTYPEDRIVING_DUALPEDALS << 8);
1526 else
1527 type |= (DI8DEVTYPEDRIVING_LIMITED << 8);
1528 break;
1529 case DI8DEVTYPE_FLIGHT:
1530 type |= (DI8DEVTYPEFLIGHT_STICK << 8);
1531 break;
1534 instance->dwDevType = device_type_for_version( type, version ) | DIDEVTYPE_HID;
1535 TRACE("detected device type %#lx\n", instance->dwDevType);
1537 *device = device_file;
1538 *preparsed = preparsed_data;
1539 return DI_OK;
1541 failed:
1542 CloseHandle( device_file );
1543 HidD_FreePreparsedData( preparsed_data );
1544 return DIERR_DEVICENOTREG;
1547 static HRESULT hid_joystick_device_open( int index, const GUID *guid, DIDEVICEINSTANCEW *instance,
1548 WCHAR *device_path, HANDLE *device, PHIDP_PREPARSED_DATA *preparsed,
1549 HIDD_ATTRIBUTES *attrs, HIDP_CAPS *caps, DWORD version )
1551 char buffer[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + MAX_PATH * sizeof(WCHAR)];
1552 SP_DEVICE_INTERFACE_DETAIL_DATA_W *detail = (void *)buffer;
1553 SP_DEVICE_INTERFACE_DATA iface = {.cbSize = sizeof(iface)};
1554 SP_DEVINFO_DATA devinfo = {.cbSize = sizeof(devinfo)};
1555 WCHAR device_id[MAX_PATH], *tmp;
1556 HDEVINFO set, xi_set;
1557 BOOL override;
1558 UINT32 i = 0;
1559 GUID hid;
1561 TRACE( "index %d, guid %s\n", index, debugstr_guid( guid ) );
1563 HidD_GetHidGuid( &hid );
1565 set = SetupDiGetClassDevsW( &hid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT );
1566 if (set == INVALID_HANDLE_VALUE) return DIERR_DEVICENOTREG;
1567 xi_set = SetupDiGetClassDevsW( &GUID_DEVINTERFACE_WINEXINPUT, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT );
1569 *device = NULL;
1570 *preparsed = NULL;
1571 while (SetupDiEnumDeviceInterfaces( set, NULL, &hid, i++, &iface ))
1573 detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
1574 if (!SetupDiGetDeviceInterfaceDetailW( set, &iface, detail, sizeof(buffer), NULL, &devinfo ))
1575 continue;
1576 if (FAILED(hid_joystick_device_try_open( detail->DevicePath, device, preparsed,
1577 attrs, caps, instance, version )))
1578 continue;
1580 if (device_instance_is_disabled( instance, &override ))
1581 goto next;
1583 if (override && SetupDiGetDeviceInstanceIdW( set, &devinfo, device_id, MAX_PATH, NULL ) &&
1584 (tmp = wcsstr( device_id, L"&IG_" )))
1586 memcpy( tmp, L"&XI_", sizeof(L"&XI_") - sizeof(WCHAR) );
1587 if (!SetupDiOpenDeviceInfoW( xi_set, device_id, NULL, 0, &devinfo ))
1588 goto next;
1589 if (!SetupDiEnumDeviceInterfaces( xi_set, &devinfo, &GUID_DEVINTERFACE_WINEXINPUT, 0, &iface ))
1590 goto next;
1591 if (!SetupDiGetDeviceInterfaceDetailW( xi_set, &iface, detail, sizeof(buffer), NULL, &devinfo ))
1592 goto next;
1594 CloseHandle( *device );
1595 HidD_FreePreparsedData( *preparsed );
1596 if (FAILED(hid_joystick_device_try_open( detail->DevicePath, device, preparsed,
1597 attrs, caps, instance, version )))
1598 continue;
1601 /* enumerate device by GUID */
1602 if (IsEqualGUID( guid, &instance->guidProduct ) || IsEqualGUID( guid, &instance->guidInstance )) break;
1604 /* enumerate all devices */
1605 if (index >= 0 && !index--) break;
1607 next:
1608 CloseHandle( *device );
1609 HidD_FreePreparsedData( *preparsed );
1610 *device = NULL;
1611 *preparsed = NULL;
1614 if (xi_set != INVALID_HANDLE_VALUE) SetupDiDestroyDeviceInfoList( xi_set );
1615 SetupDiDestroyDeviceInfoList( set );
1616 if (!*device || !*preparsed) return DIERR_DEVICENOTREG;
1618 lstrcpynW( device_path, detail->DevicePath, MAX_PATH );
1619 return DI_OK;
1622 HRESULT hid_joystick_enum_device( DWORD type, DWORD flags, DIDEVICEINSTANCEW *instance, DWORD version, int index )
1624 HIDD_ATTRIBUTES attrs = {.Size = sizeof(attrs)};
1625 PHIDP_PREPARSED_DATA preparsed;
1626 WCHAR device_path[MAX_PATH];
1627 GUID guid = GUID_NULL;
1628 HIDP_CAPS caps;
1629 HANDLE device;
1630 HRESULT hr;
1632 TRACE( "type %#lx, flags %#lx, instance %p, version %#lx, index %d\n", type, flags, instance, version, index );
1634 hr = hid_joystick_device_open( index, &guid, instance, device_path, &device, &preparsed,
1635 &attrs, &caps, version );
1636 if (hr != DI_OK) return hr;
1638 HidD_FreePreparsedData( preparsed );
1639 CloseHandle( device );
1641 TRACE( "found device %s, usage %04x:%04x, product %s, instance %s, name %s\n", debugstr_w(device_path),
1642 instance->wUsagePage, instance->wUsage, debugstr_guid( &instance->guidProduct ),
1643 debugstr_guid( &instance->guidInstance ), debugstr_w(instance->tszInstanceName) );
1645 return DI_OK;
1648 static BOOL init_object_properties( struct dinput_device *device, UINT index, struct hid_value_caps *caps,
1649 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
1651 struct object_properties *properties;
1652 LONG tmp;
1654 if (index == -1) return DIENUM_STOP;
1655 properties = device->object_properties + index;
1657 properties->bit_size = caps->bit_size;
1658 properties->physical_min = caps->physical_min;
1659 properties->physical_max = caps->physical_max;
1660 properties->logical_min = caps->logical_min;
1661 properties->logical_max = caps->logical_max;
1662 properties->range_min = 0;
1663 properties->range_max = 0;
1665 if (instance->dwType & DIDFT_AXIS) properties->range_max = 65535;
1666 else
1668 properties->range_max = 36000;
1669 tmp = caps->logical_max - caps->logical_min;
1670 if (tmp > 0) properties->range_max -= 36000 / (tmp + 1);
1673 properties->saturation = 10000;
1674 properties->granularity = 1;
1675 return DIENUM_CONTINUE;
1678 static BOOL init_pid_reports( struct dinput_device *device, UINT index, struct hid_value_caps *caps,
1679 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
1681 struct hid_joystick *impl = CONTAINING_RECORD( device, struct hid_joystick, base );
1682 struct pid_set_constant_force *set_constant_force = &impl->pid_set_constant_force;
1683 struct pid_set_ramp_force *set_ramp_force = &impl->pid_set_ramp_force;
1684 struct pid_control_report *device_control = &impl->pid_device_control;
1685 struct pid_control_report *effect_control = &impl->pid_effect_control;
1686 struct pid_effect_update *effect_update = &impl->pid_effect_update;
1687 struct pid_set_condition *set_condition = &impl->pid_set_condition;
1688 struct pid_set_periodic *set_periodic = &impl->pid_set_periodic;
1689 struct pid_set_envelope *set_envelope = &impl->pid_set_envelope;
1690 struct pid_device_gain *device_gain = &impl->pid_device_gain;
1691 struct pid_device_pool *device_pool = &impl->pid_device_pool;
1692 struct pid_block_free *block_free = &impl->pid_block_free;
1693 struct pid_block_load *block_load = &impl->pid_block_load;
1694 struct pid_new_effect *new_effect = &impl->pid_new_effect;
1695 struct pid_effect_state *effect_state = &impl->pid_effect_state;
1697 #define SET_COLLECTION( rep ) \
1698 do \
1700 if (rep->collection) FIXME( "duplicate " #rep " report!\n" ); \
1701 else rep->collection = DIDFT_GETINSTANCE( instance->dwType ); \
1702 } while (0)
1704 #define SET_SUB_COLLECTION( rep, sub ) \
1705 do { \
1706 if (instance->wCollectionNumber != rep->collection) \
1707 FIXME( "unexpected " #rep "." #sub " parent!\n" ); \
1708 else if (rep->sub) \
1709 FIXME( "duplicate " #rep "." #sub " collection!\n" ); \
1710 else \
1711 rep->sub = DIDFT_GETINSTANCE( instance->dwType ); \
1712 } while (0)
1714 if (instance->wUsagePage == HID_USAGE_PAGE_PID)
1716 switch (instance->wUsage)
1718 case PID_USAGE_DEVICE_CONTROL_REPORT: SET_COLLECTION( device_control ); break;
1719 case PID_USAGE_EFFECT_OPERATION_REPORT: SET_COLLECTION( effect_control ); break;
1720 case PID_USAGE_SET_EFFECT_REPORT: SET_COLLECTION( effect_update ); break;
1721 case PID_USAGE_SET_PERIODIC_REPORT: SET_COLLECTION( set_periodic ); break;
1722 case PID_USAGE_SET_ENVELOPE_REPORT: SET_COLLECTION( set_envelope ); break;
1723 case PID_USAGE_SET_CONDITION_REPORT: SET_COLLECTION( set_condition ); break;
1724 case PID_USAGE_SET_CONSTANT_FORCE_REPORT: SET_COLLECTION( set_constant_force ); break;
1725 case PID_USAGE_SET_RAMP_FORCE_REPORT: SET_COLLECTION( set_ramp_force ); break;
1726 case PID_USAGE_DEVICE_GAIN_REPORT: SET_COLLECTION( device_gain ); break;
1727 case PID_USAGE_POOL_REPORT: SET_COLLECTION( device_pool ); break;
1728 case PID_USAGE_BLOCK_FREE_REPORT: SET_COLLECTION( block_free ); break;
1729 case PID_USAGE_BLOCK_LOAD_REPORT: SET_COLLECTION( block_load ); break;
1730 case PID_USAGE_CREATE_NEW_EFFECT_REPORT: SET_COLLECTION( new_effect ); break;
1731 case PID_USAGE_STATE_REPORT: SET_COLLECTION( effect_state ); break;
1733 case PID_USAGE_DEVICE_CONTROL: SET_SUB_COLLECTION( device_control, control_coll ); break;
1734 case PID_USAGE_EFFECT_OPERATION: SET_SUB_COLLECTION( effect_control, control_coll ); break;
1735 case PID_USAGE_EFFECT_TYPE:
1736 if (instance->wCollectionNumber == effect_update->collection)
1737 SET_SUB_COLLECTION( effect_update, type_coll );
1738 else if (instance->wCollectionNumber == new_effect->collection)
1739 SET_SUB_COLLECTION( new_effect, type_coll );
1740 break;
1741 case PID_USAGE_AXES_ENABLE: SET_SUB_COLLECTION( effect_update, axes_coll ); break;
1742 case PID_USAGE_DIRECTION: SET_SUB_COLLECTION( effect_update, direction_coll ); break;
1743 case PID_USAGE_BLOCK_LOAD_STATUS: SET_SUB_COLLECTION( block_load, status_coll ); break;
1747 #undef SET_SUB_COLLECTION
1748 #undef SET_COLLECTION
1750 return DIENUM_CONTINUE;
1753 static BOOL init_pid_caps( struct dinput_device *device, UINT index, struct hid_value_caps *caps,
1754 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
1756 struct hid_joystick *impl = CONTAINING_RECORD( device, struct hid_joystick, base );
1757 struct pid_set_constant_force *set_constant_force = &impl->pid_set_constant_force;
1758 struct pid_set_ramp_force *set_ramp_force = &impl->pid_set_ramp_force;
1759 struct pid_control_report *device_control = &impl->pid_device_control;
1760 struct pid_control_report *effect_control = &impl->pid_effect_control;
1761 struct pid_effect_update *effect_update = &impl->pid_effect_update;
1762 struct pid_set_condition *set_condition = &impl->pid_set_condition;
1763 struct pid_set_periodic *set_periodic = &impl->pid_set_periodic;
1764 struct pid_set_envelope *set_envelope = &impl->pid_set_envelope;
1765 struct pid_device_gain *device_gain = &impl->pid_device_gain;
1766 struct pid_device_pool *device_pool = &impl->pid_device_pool;
1767 struct pid_block_free *block_free = &impl->pid_block_free;
1768 struct pid_block_load *block_load = &impl->pid_block_load;
1769 struct pid_new_effect *new_effect = &impl->pid_new_effect;
1770 struct pid_effect_state *effect_state = &impl->pid_effect_state;
1772 #define SET_REPORT_ID( rep ) \
1773 do \
1775 if (!rep->id) \
1776 rep->id = instance->wReportId; \
1777 else if (rep->id != instance->wReportId) \
1778 FIXME( "multiple " #rep " report ids!\n" ); \
1779 } while (0)
1781 if (!instance->wCollectionNumber)
1782 return DIENUM_CONTINUE;
1784 if (instance->wCollectionNumber == effect_state->collection)
1786 SET_REPORT_ID( effect_state );
1787 if (instance->wUsage == PID_USAGE_SAFETY_SWITCH)
1788 effect_state->safety_switch_caps = caps;
1789 if (instance->wUsage == PID_USAGE_ACTUATOR_POWER)
1790 effect_state->actuator_power_caps = caps;
1791 if (instance->wUsage == PID_USAGE_ACTUATOR_OVERRIDE_SWITCH)
1792 effect_state->actuator_override_switch_caps = caps;
1795 if (!(instance->dwType & DIDFT_OUTPUT)) return DIENUM_CONTINUE;
1797 if (instance->wCollectionNumber == device_control->control_coll)
1798 SET_REPORT_ID( device_control );
1799 if (instance->wCollectionNumber == effect_control->control_coll)
1800 SET_REPORT_ID( effect_control );
1801 if (instance->wCollectionNumber == effect_update->type_coll)
1802 SET_REPORT_ID( effect_update );
1803 if (instance->wCollectionNumber == effect_update->collection)
1805 SET_REPORT_ID( effect_update );
1806 if (instance->wUsage == PID_USAGE_DURATION)
1807 effect_update->duration_caps = caps;
1808 if (instance->wUsage == PID_USAGE_GAIN)
1810 caps->physical_min = 0;
1811 caps->physical_max = 10000;
1812 effect_update->gain_caps = caps;
1814 if (instance->wUsage == PID_USAGE_SAMPLE_PERIOD)
1815 effect_update->sample_period_caps = caps;
1816 if (instance->wUsage == PID_USAGE_START_DELAY)
1817 effect_update->start_delay_caps = caps;
1818 if (instance->wUsage == PID_USAGE_TRIGGER_BUTTON)
1819 effect_update->trigger_button_caps = caps;
1820 if (instance->wUsage == PID_USAGE_TRIGGER_REPEAT_INTERVAL)
1821 effect_update->trigger_repeat_interval_caps = caps;
1823 if (instance->wCollectionNumber == effect_update->axes_coll)
1825 SET_REPORT_ID( effect_update );
1826 if (effect_update->axis_count >= 6) FIXME( "more than 6 PID axes detected\n" );
1827 else effect_update->axis_caps[effect_update->axis_count] = caps;
1828 effect_update->axis_count++;
1830 if (instance->wCollectionNumber == effect_update->direction_coll)
1832 SET_REPORT_ID( effect_update );
1833 caps->physical_min = 0;
1834 caps->physical_max = 35900;
1835 if (effect_update->direction_count >= 6) FIXME( "more than 6 PID directions detected\n" );
1836 else effect_update->direction_caps[effect_update->direction_count] = caps;
1837 effect_update->direction_count++;
1839 if (instance->wCollectionNumber == set_periodic->collection)
1841 SET_REPORT_ID( set_periodic );
1842 if (instance->wUsage == PID_USAGE_MAGNITUDE)
1844 caps->physical_min = 0;
1845 caps->physical_max = 10000;
1846 set_periodic->magnitude_caps = caps;
1848 if (instance->wUsage == PID_USAGE_PERIOD)
1849 set_periodic->period_caps = caps;
1850 if (instance->wUsage == PID_USAGE_PHASE)
1852 caps->physical_min = 0;
1853 caps->physical_max = 35900;
1854 set_periodic->phase_caps = caps;
1856 if (instance->wUsage == PID_USAGE_OFFSET)
1858 caps->physical_min = -10000;
1859 caps->physical_max = 10000;
1860 set_periodic->offset_caps = caps;
1863 if (instance->wCollectionNumber == set_envelope->collection)
1865 SET_REPORT_ID( set_envelope );
1866 if (instance->wUsage == PID_USAGE_ATTACK_LEVEL)
1868 caps->physical_min = 0;
1869 caps->physical_max = 10000;
1870 set_envelope->attack_level_caps = caps;
1872 if (instance->wUsage == PID_USAGE_ATTACK_TIME)
1873 set_envelope->attack_time_caps = caps;
1874 if (instance->wUsage == PID_USAGE_FADE_LEVEL)
1876 caps->physical_min = 0;
1877 caps->physical_max = 10000;
1878 set_envelope->fade_level_caps = caps;
1880 if (instance->wUsage == PID_USAGE_FADE_TIME)
1881 set_envelope->fade_time_caps = caps;
1883 if (instance->wCollectionNumber == set_condition->collection)
1885 SET_REPORT_ID( set_condition );
1886 if (instance->wUsage == PID_USAGE_CP_OFFSET)
1888 caps->physical_min = -10000;
1889 caps->physical_max = 10000;
1890 set_condition->center_point_offset_caps = caps;
1892 if (instance->wUsage == PID_USAGE_POSITIVE_COEFFICIENT)
1894 caps->physical_min = -10000;
1895 caps->physical_max = 10000;
1896 set_condition->positive_coefficient_caps = caps;
1898 if (instance->wUsage == PID_USAGE_NEGATIVE_COEFFICIENT)
1900 caps->physical_min = -10000;
1901 caps->physical_max = 10000;
1902 set_condition->negative_coefficient_caps = caps;
1904 if (instance->wUsage == PID_USAGE_POSITIVE_SATURATION)
1906 caps->physical_min = 0;
1907 caps->physical_max = 10000;
1908 set_condition->positive_saturation_caps = caps;
1910 if (instance->wUsage == PID_USAGE_NEGATIVE_SATURATION)
1912 caps->physical_min = 0;
1913 caps->physical_max = 10000;
1914 set_condition->negative_saturation_caps = caps;
1916 if (instance->wUsage == PID_USAGE_DEAD_BAND)
1918 caps->physical_min = 0;
1919 caps->physical_max = 10000;
1920 set_condition->dead_band_caps = caps;
1923 if (instance->wCollectionNumber == set_constant_force->collection)
1925 SET_REPORT_ID( set_constant_force );
1926 if (instance->wUsage == PID_USAGE_MAGNITUDE)
1928 caps->physical_min = -10000;
1929 caps->physical_max = 10000;
1930 set_constant_force->magnitude_caps = caps;
1933 if (instance->wCollectionNumber == set_ramp_force->collection)
1935 SET_REPORT_ID( set_ramp_force );
1936 if (instance->wUsage == PID_USAGE_RAMP_START)
1938 caps->physical_min = -10000;
1939 caps->physical_max = 10000;
1940 set_ramp_force->start_caps = caps;
1942 if (instance->wUsage == PID_USAGE_RAMP_END)
1944 caps->physical_min = -10000;
1945 caps->physical_max = 10000;
1946 set_ramp_force->end_caps = caps;
1949 if (instance->wCollectionNumber == device_gain->collection)
1951 SET_REPORT_ID( device_gain );
1952 if (instance->wUsage == PID_USAGE_DEVICE_GAIN)
1954 caps->physical_min = 0;
1955 caps->physical_max = 10000;
1956 device_gain->device_gain_caps = caps;
1959 if (instance->wCollectionNumber == device_pool->collection)
1961 SET_REPORT_ID( device_pool );
1962 if (instance->wUsage == PID_USAGE_DEVICE_MANAGED_POOL)
1963 device_pool->device_managed_caps = caps;
1965 if (instance->wCollectionNumber == block_free->collection)
1966 SET_REPORT_ID( block_free );
1967 if (instance->wCollectionNumber == block_load->collection)
1968 SET_REPORT_ID( block_load );
1969 if (instance->wCollectionNumber == block_load->status_coll)
1970 SET_REPORT_ID( block_load );
1971 if (instance->wCollectionNumber == new_effect->collection)
1972 SET_REPORT_ID( new_effect );
1973 if (instance->wCollectionNumber == new_effect->type_coll)
1974 SET_REPORT_ID( new_effect );
1976 #undef SET_REPORT_ID
1978 return DIENUM_CONTINUE;
1981 HRESULT hid_joystick_create_device( struct dinput *dinput, const GUID *guid, IDirectInputDevice8W **out )
1983 static const DIPROPHEADER filter =
1985 .dwSize = sizeof(filter),
1986 .dwHeaderSize = sizeof(filter),
1987 .dwHow = DIPH_DEVICE,
1989 DIPROPRANGE range =
1991 .diph =
1993 .dwSize = sizeof(range),
1994 .dwHeaderSize = sizeof(DIPROPHEADER),
1995 .dwHow = DIPH_DEVICE,
1998 HIDD_ATTRIBUTES attrs = {.Size = sizeof(attrs)};
1999 struct hid_joystick *impl = NULL;
2000 USAGE_AND_PAGE *usages;
2001 char *buffer;
2002 HRESULT hr;
2003 DWORD size;
2005 TRACE( "dinput %p, guid %s, out %p\n", dinput, debugstr_guid( guid ), out );
2007 *out = NULL;
2009 if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY;
2010 dinput_device_init( &impl->base, &hid_joystick_vtbl, guid, dinput );
2011 impl->base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": hid_joystick.base.crit");
2012 impl->base.dwCoopLevel = DISCL_NONEXCLUSIVE | DISCL_BACKGROUND;
2013 impl->base.read_event = CreateEventW( NULL, TRUE, FALSE, NULL );
2015 if (memcmp( device_path_guid.Data4, guid->Data4, sizeof(device_path_guid.Data4) ))
2016 hr = hid_joystick_device_open( -1, guid, &impl->base.instance, impl->device_path, &impl->device, &impl->preparsed,
2017 &attrs, &impl->caps, dinput->dwVersion );
2018 else
2020 wcscpy( impl->device_path, *(const WCHAR **)guid );
2021 hr = hid_joystick_device_try_open( impl->device_path, &impl->device, &impl->preparsed, &attrs,
2022 &impl->caps, &impl->base.instance, dinput->dwVersion );
2024 if (hr != DI_OK) goto failed;
2026 impl->base.caps.dwDevType = impl->base.instance.dwDevType;
2027 impl->attrs = attrs;
2028 list_init( &impl->effect_list );
2030 hr = E_OUTOFMEMORY;
2031 size = impl->caps.InputReportByteLength;
2032 if (!(buffer = malloc( size ))) goto failed;
2033 impl->input_report_buf = buffer;
2034 size = impl->caps.OutputReportByteLength;
2035 if (!(buffer = malloc( size ))) goto failed;
2036 impl->output_report_buf = buffer;
2037 size = impl->caps.FeatureReportByteLength;
2038 if (!(buffer = malloc( size ))) goto failed;
2039 impl->feature_report_buf = buffer;
2040 impl->usages_count = HidP_MaxUsageListLength( HidP_Input, 0, impl->preparsed );
2041 size = impl->usages_count * sizeof(USAGE_AND_PAGE);
2042 if (!(usages = malloc( size ))) goto failed;
2043 impl->usages_buf = usages;
2045 enum_objects( impl, &filter, DIDFT_COLLECTION, init_pid_reports, NULL );
2046 enum_objects( impl, &filter, DIDFT_NODATA | DIDFT_BUTTON | DIDFT_AXIS, init_pid_caps, NULL );
2048 TRACE( "device control id %u, coll %u, control coll %u\n", impl->pid_device_control.id,
2049 impl->pid_device_control.collection, impl->pid_device_control.control_coll );
2050 TRACE( "effect control id %u, coll %u\n", impl->pid_effect_control.id, impl->pid_effect_control.collection );
2051 TRACE( "effect update id %u, coll %u, type_coll %u\n", impl->pid_effect_update.id,
2052 impl->pid_effect_update.collection, impl->pid_effect_update.type_coll );
2053 TRACE( "set periodic id %u, coll %u\n", impl->pid_set_periodic.id, impl->pid_set_periodic.collection );
2054 TRACE( "set envelope id %u, coll %u\n", impl->pid_set_envelope.id, impl->pid_set_envelope.collection );
2055 TRACE( "set condition id %u, coll %u\n", impl->pid_set_condition.id, impl->pid_set_condition.collection );
2056 TRACE( "set constant force id %u, coll %u\n", impl->pid_set_constant_force.id,
2057 impl->pid_set_constant_force.collection );
2058 TRACE( "set ramp force id %u, coll %u\n", impl->pid_set_ramp_force.id, impl->pid_set_ramp_force.collection );
2059 TRACE( "device gain id %u, coll %u\n", impl->pid_device_gain.id, impl->pid_device_gain.collection );
2060 TRACE( "device pool id %u, coll %u\n", impl->pid_device_pool.id, impl->pid_device_pool.collection );
2061 TRACE( "block free id %u, coll %u\n", impl->pid_block_free.id, impl->pid_block_free.collection );
2062 TRACE( "block load id %u, coll %u, status_coll %u\n", impl->pid_block_load.id,
2063 impl->pid_block_load.collection, impl->pid_block_load.status_coll );
2064 TRACE( "create new effect id %u, coll %u, type_coll %u\n", impl->pid_new_effect.id,
2065 impl->pid_new_effect.collection, impl->pid_new_effect.type_coll );
2066 TRACE( "effect state id %u, coll %u\n", impl->pid_effect_state.id, impl->pid_effect_state.collection );
2068 if (impl->pid_device_control.id)
2070 impl->base.caps.dwFlags |= DIDC_FORCEFEEDBACK;
2071 if (impl->pid_effect_update.start_delay_caps)
2072 impl->base.caps.dwFlags |= DIDC_STARTDELAY;
2073 if (impl->pid_set_envelope.attack_level_caps ||
2074 impl->pid_set_envelope.attack_time_caps)
2075 impl->base.caps.dwFlags |= DIDC_FFATTACK;
2076 if (impl->pid_set_envelope.fade_level_caps ||
2077 impl->pid_set_envelope.fade_time_caps)
2078 impl->base.caps.dwFlags |= DIDC_FFFADE;
2079 if (impl->pid_set_condition.positive_saturation_caps ||
2080 impl->pid_set_condition.negative_saturation_caps)
2081 impl->base.caps.dwFlags |= DIDC_SATURATION;
2082 if (impl->pid_set_condition.dead_band_caps)
2083 impl->base.caps.dwFlags |= DIDC_DEADBAND;
2084 impl->base.caps.dwFFSamplePeriod = 1000000;
2085 impl->base.caps.dwFFMinTimeResolution = 1000000;
2086 impl->base.caps.dwHardwareRevision = 1;
2087 impl->base.caps.dwFFDriverVersion = 1;
2090 if (FAILED(hr = dinput_device_init_device_format( &impl->base.IDirectInputDevice8W_iface ))) goto failed;
2091 enum_objects( impl, &filter, DIDFT_AXIS | DIDFT_POV, init_object_properties, NULL );
2093 *out = &impl->base.IDirectInputDevice8W_iface;
2094 return DI_OK;
2096 failed:
2097 IDirectInputDevice_Release( &impl->base.IDirectInputDevice8W_iface );
2098 return hr;
2101 static HRESULT WINAPI hid_joystick_effect_QueryInterface( IDirectInputEffect *iface, REFIID iid, void **out )
2103 TRACE( "iface %p, iid %s, out %p\n", iface, debugstr_guid( iid ), out );
2105 if (IsEqualGUID( iid, &IID_IUnknown ) ||
2106 IsEqualGUID( iid, &IID_IDirectInputEffect ))
2108 IDirectInputEffect_AddRef( iface );
2109 *out = iface;
2110 return S_OK;
2113 FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) );
2114 *out = NULL;
2115 return E_NOINTERFACE;
2118 static ULONG WINAPI hid_joystick_effect_AddRef( IDirectInputEffect *iface )
2120 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
2121 ULONG ref = InterlockedIncrement( &impl->ref );
2122 TRACE( "iface %p, ref %lu.\n", iface, ref );
2123 return ref;
2126 static ULONG WINAPI hid_joystick_effect_Release( IDirectInputEffect *iface )
2128 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
2129 ULONG ref = InterlockedDecrement( &impl->ref );
2130 TRACE( "iface %p, ref %lu.\n", iface, ref );
2131 if (!ref)
2133 IDirectInputEffect_Unload( iface );
2134 EnterCriticalSection( &impl->joystick->base.crit );
2135 list_remove( &impl->entry );
2136 LeaveCriticalSection( &impl->joystick->base.crit );
2137 dinput_device_internal_release( &impl->joystick->base );
2138 free( impl->set_envelope_buf );
2139 free( impl->type_specific_buf );
2140 free( impl->effect_update_buf );
2141 free( impl->effect_control_buf );
2142 free( impl );
2144 return ref;
2147 static HRESULT WINAPI hid_joystick_effect_Initialize( IDirectInputEffect *iface, HINSTANCE inst,
2148 DWORD version, REFGUID guid )
2150 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
2151 struct hid_joystick *joystick = impl->joystick;
2152 ULONG count, report_len = joystick->caps.OutputReportByteLength;
2153 NTSTATUS status;
2154 USAGE type;
2156 TRACE( "iface %p, inst %p, version %#lx, guid %s\n", iface, inst, version, debugstr_guid( guid ) );
2158 if (!inst) return DIERR_INVALIDPARAM;
2159 if (!guid) return E_POINTER;
2160 if (!(type = effect_guid_to_usage( guid ))) return DIERR_DEVICENOTREG;
2162 status = HidP_InitializeReportForID( HidP_Output, joystick->pid_effect_update.id,
2163 joystick->preparsed, impl->effect_update_buf, report_len );
2164 if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG;
2166 impl->type_specific_buf[0] = 0;
2167 impl->set_envelope_buf[0] = 0;
2169 switch (type)
2171 case PID_USAGE_ET_SQUARE:
2172 case PID_USAGE_ET_SINE:
2173 case PID_USAGE_ET_TRIANGLE:
2174 case PID_USAGE_ET_SAWTOOTH_UP:
2175 case PID_USAGE_ET_SAWTOOTH_DOWN:
2176 status = HidP_InitializeReportForID( HidP_Output, joystick->pid_set_periodic.id,
2177 joystick->preparsed, impl->type_specific_buf, report_len );
2178 if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG;
2179 impl->params.lpvTypeSpecificParams = &impl->periodic;
2180 break;
2181 case PID_USAGE_ET_SPRING:
2182 case PID_USAGE_ET_DAMPER:
2183 case PID_USAGE_ET_INERTIA:
2184 case PID_USAGE_ET_FRICTION:
2185 status = HidP_InitializeReportForID( HidP_Output, joystick->pid_set_condition.id, joystick->preparsed,
2186 impl->type_specific_buf, report_len );
2187 if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG;
2188 impl->params.lpvTypeSpecificParams = &impl->condition;
2189 break;
2190 case PID_USAGE_ET_CONSTANT_FORCE:
2191 status = HidP_InitializeReportForID( HidP_Output, joystick->pid_set_constant_force.id, joystick->preparsed,
2192 impl->type_specific_buf, report_len );
2193 if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG;
2194 impl->params.lpvTypeSpecificParams = &impl->constant_force;
2195 break;
2196 case PID_USAGE_ET_RAMP:
2197 status = HidP_InitializeReportForID( HidP_Output, joystick->pid_set_ramp_force.id, joystick->preparsed,
2198 impl->type_specific_buf, report_len );
2199 if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG;
2200 impl->params.lpvTypeSpecificParams = &impl->ramp_force;
2201 break;
2202 case PID_USAGE_ET_CUSTOM_FORCE_DATA:
2203 FIXME( "effect type %#x not implemented!\n", type );
2204 break;
2207 switch (type)
2209 case PID_USAGE_ET_SQUARE:
2210 case PID_USAGE_ET_SINE:
2211 case PID_USAGE_ET_TRIANGLE:
2212 case PID_USAGE_ET_SAWTOOTH_UP:
2213 case PID_USAGE_ET_SAWTOOTH_DOWN:
2214 case PID_USAGE_ET_CONSTANT_FORCE:
2215 case PID_USAGE_ET_RAMP:
2216 status = HidP_InitializeReportForID( HidP_Output, joystick->pid_set_envelope.id, joystick->preparsed,
2217 impl->set_envelope_buf, report_len );
2218 if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG;
2219 break;
2222 count = 1;
2223 status = HidP_SetUsages( HidP_Output, HID_USAGE_PAGE_PID, joystick->pid_effect_update.type_coll,
2224 &type, &count, joystick->preparsed, impl->effect_update_buf, report_len );
2225 if (status != HIDP_STATUS_SUCCESS) return DIERR_DEVICENOTREG;
2227 impl->type = type;
2228 return DI_OK;
2231 static HRESULT WINAPI hid_joystick_effect_GetEffectGuid( IDirectInputEffect *iface, GUID *guid )
2233 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
2235 TRACE( "iface %p, guid %p.\n", iface, guid );
2237 if (!guid) return E_POINTER;
2238 *guid = *effect_usage_to_guid( impl->type );
2240 return DI_OK;
2243 static BOOL get_parameters_object_id( struct dinput_device *device, UINT index, struct hid_value_caps *caps,
2244 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
2246 *(DWORD *)data = instance->dwType;
2247 return DIENUM_STOP;
2250 static BOOL get_parameters_object_ofs( struct dinput_device *device, UINT index, struct hid_value_caps *caps,
2251 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
2253 DIDATAFORMAT *device_format = &device->device_format, *user_format = &device->user_format;
2254 DIOBJECTDATAFORMAT *device_obj, *user_obj;
2256 if (!user_format->rgodf) return DIENUM_CONTINUE;
2258 user_obj = user_format->rgodf + device_format->dwNumObjs;
2259 device_obj = device_format->rgodf + device_format->dwNumObjs;
2260 while (user_obj-- > user_format->rgodf && device_obj-- > device_format->rgodf)
2262 if (!user_obj->dwType) continue;
2263 if (device_obj->dwType == instance->dwType) break;
2265 if (user_obj < user_format->rgodf) return DIENUM_CONTINUE;
2267 *(DWORD *)data = user_obj->dwOfs;
2268 return DIENUM_STOP;
2271 static void convert_directions_to_spherical( const DIEFFECT *in, DIEFFECT *out )
2273 DWORD i, j, direction_flags = DIEFF_CARTESIAN | DIEFF_POLAR | DIEFF_SPHERICAL;
2274 double tmp;
2276 switch (in->dwFlags & direction_flags)
2278 case DIEFF_CARTESIAN:
2279 for (i = 1; i < in->cAxes; ++i)
2281 tmp = in->rglDirection[0];
2282 for (j = 1; j < i; ++j) tmp = sqrt( tmp * tmp + in->rglDirection[j] * in->rglDirection[j] );
2283 tmp = atan2( in->rglDirection[i], tmp );
2284 out->rglDirection[i - 1] = tmp * 18000 / M_PI;
2285 if (out->rglDirection[i - 1] < 0) out->rglDirection[i - 1] += 36000;
2287 if (in->cAxes) out->rglDirection[in->cAxes - 1] = 0;
2288 out->cAxes = in->cAxes;
2289 break;
2290 case DIEFF_POLAR:
2291 out->rglDirection[0] = (in->rglDirection[0] % 36000) - 9000;
2292 if (out->rglDirection[0] < 0) out->rglDirection[0] += 36000;
2293 for (i = 1; i < in->cAxes; ++i) out->rglDirection[i] = 0;
2294 out->cAxes = in->cAxes;
2295 break;
2296 case DIEFF_SPHERICAL:
2297 if (!in->cAxes) i = 0;
2298 else for (i = 0; i < in->cAxes - 1; ++i)
2300 out->rglDirection[i] = in->rglDirection[i] % 36000;
2301 if (out->rglDirection[i] < 0) out->rglDirection[i] += 36000;
2303 out->rglDirection[i] = 0;
2304 out->cAxes = in->cAxes;
2305 break;
2309 static void convert_directions_from_spherical( const DIEFFECT *in, DIEFFECT *out )
2311 DWORD i, j, direction_flags = DIEFF_CARTESIAN | DIEFF_POLAR | DIEFF_SPHERICAL;
2312 LONG tmp;
2314 switch (out->dwFlags & direction_flags)
2316 case DIEFF_CARTESIAN:
2317 out->rglDirection[0] = 10000;
2318 for (i = 1; i <= in->cAxes; ++i)
2320 tmp = cos( in->rglDirection[i - 1] * M_PI / 18000 ) * 10000;
2321 for (j = 0; j < i; ++j)
2322 out->rglDirection[j] = round( out->rglDirection[j] * tmp / 10000.0 );
2323 if (i < in->cAxes)
2324 out->rglDirection[i] = sin( in->rglDirection[i - 1] * M_PI / 18000 ) * 10000;
2326 out->cAxes = in->cAxes;
2327 break;
2328 case DIEFF_POLAR:
2329 out->rglDirection[0] = (in->rglDirection[0] + 9000) % 36000;
2330 if (out->rglDirection[0] < 0) out->rglDirection[0] += 36000;
2331 out->rglDirection[1] = 0;
2332 out->cAxes = 2;
2333 break;
2334 case DIEFF_SPHERICAL:
2335 for (i = 0; i < in->cAxes; ++i)
2337 out->rglDirection[i] = in->rglDirection[i] % 36000;
2338 if (out->rglDirection[i] < 0) out->rglDirection[i] += 36000;
2340 out->cAxes = in->cAxes;
2341 break;
2345 static void convert_directions( const DIEFFECT *in, DIEFFECT *out )
2347 DWORD direction_flags = DIEFF_CARTESIAN | DIEFF_POLAR | DIEFF_SPHERICAL;
2348 LONG directions[6] = {0};
2349 DIEFFECT spherical = {.rglDirection = directions};
2351 switch (in->dwFlags & direction_flags)
2353 case DIEFF_CARTESIAN:
2354 switch (out->dwFlags & direction_flags)
2356 case DIEFF_CARTESIAN:
2357 memcpy( out->rglDirection, in->rglDirection, in->cAxes * sizeof(LONG) );
2358 out->cAxes = in->cAxes;
2359 break;
2360 case DIEFF_POLAR:
2361 convert_directions_to_spherical( in, &spherical );
2362 convert_directions_from_spherical( &spherical, out );
2363 break;
2364 case DIEFF_SPHERICAL:
2365 convert_directions_to_spherical( in, out );
2366 break;
2368 break;
2370 case DIEFF_POLAR:
2371 switch (out->dwFlags & direction_flags)
2373 case DIEFF_POLAR:
2374 memcpy( out->rglDirection, in->rglDirection, in->cAxes * sizeof(LONG) );
2375 out->cAxes = in->cAxes;
2376 break;
2377 case DIEFF_CARTESIAN:
2378 convert_directions_to_spherical( in, &spherical );
2379 convert_directions_from_spherical( &spherical, out );
2380 break;
2381 case DIEFF_SPHERICAL:
2382 convert_directions_to_spherical( in, out );
2383 break;
2385 break;
2387 case DIEFF_SPHERICAL:
2388 switch (out->dwFlags & direction_flags)
2390 case DIEFF_POLAR:
2391 case DIEFF_CARTESIAN:
2392 convert_directions_from_spherical( in, out );
2393 break;
2394 case DIEFF_SPHERICAL:
2395 convert_directions_to_spherical( in, out );
2396 break;
2398 break;
2402 static HRESULT WINAPI hid_joystick_effect_GetParameters( IDirectInputEffect *iface, DIEFFECT *params, DWORD flags )
2404 DIPROPHEADER filter =
2406 .dwSize = sizeof(DIPROPHEADER),
2407 .dwHeaderSize = sizeof(DIPROPHEADER),
2408 .dwHow = DIPH_BYUSAGE,
2410 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
2411 ULONG i, count, capacity, object_flags, direction_flags;
2412 BOOL ret;
2414 TRACE( "iface %p, params %p, flags %#lx.\n", iface, params, flags );
2416 if (!params) return DI_OK;
2417 if (params->dwSize != sizeof(DIEFFECT_DX6) && params->dwSize != sizeof(DIEFFECT_DX5)) return DIERR_INVALIDPARAM;
2418 capacity = params->cAxes;
2419 object_flags = params->dwFlags & (DIEFF_OBJECTIDS | DIEFF_OBJECTOFFSETS);
2420 direction_flags = params->dwFlags & (DIEFF_CARTESIAN | DIEFF_POLAR | DIEFF_SPHERICAL);
2422 if (flags & DIEP_AXES)
2424 if (!object_flags) return DIERR_INVALIDPARAM;
2425 params->cAxes = impl->params.cAxes;
2426 if (capacity < impl->params.cAxes) return DIERR_MOREDATA;
2428 for (i = 0; i < impl->params.cAxes; ++i)
2430 if (!params->rgdwAxes) return DIERR_INVALIDPARAM;
2431 filter.dwObj = impl->params.rgdwAxes[i];
2432 if (object_flags & DIEFF_OBJECTIDS)
2433 ret = enum_objects( impl->joystick, &filter, DIDFT_AXIS, get_parameters_object_id,
2434 &params->rgdwAxes[i] );
2435 else
2436 ret = enum_objects( impl->joystick, &filter, DIDFT_AXIS, get_parameters_object_ofs,
2437 &params->rgdwAxes[i] );
2438 if (ret != DIENUM_STOP) params->rgdwAxes[i] = 0;
2442 if (flags & DIEP_DIRECTION)
2444 if (!direction_flags) return DIERR_INVALIDPARAM;
2446 count = params->cAxes = impl->params.cAxes;
2447 if (!count) params->dwFlags &= ~(DIEFF_CARTESIAN | DIEFF_POLAR | DIEFF_SPHERICAL);
2448 if ((direction_flags & DIEFF_POLAR) && count != 2) return DIERR_INVALIDPARAM;
2449 if (capacity < params->cAxes) return DIERR_MOREDATA;
2451 if (!count) params->rglDirection = NULL;
2452 else if (!params->rglDirection) return DIERR_INVALIDPARAM;
2453 else convert_directions( &impl->params, params );
2456 if (flags & DIEP_TYPESPECIFICPARAMS)
2458 capacity = params->cbTypeSpecificParams;
2459 params->cbTypeSpecificParams = impl->params.cbTypeSpecificParams;
2460 if (capacity < impl->params.cbTypeSpecificParams) return DIERR_MOREDATA;
2461 if (impl->params.lpvTypeSpecificParams)
2463 if (!params->lpvTypeSpecificParams) return E_POINTER;
2464 memcpy( params->lpvTypeSpecificParams, impl->params.lpvTypeSpecificParams,
2465 impl->params.cbTypeSpecificParams );
2469 if (flags & DIEP_ENVELOPE)
2471 if (!params->lpEnvelope) return E_POINTER;
2472 if (params->lpEnvelope->dwSize != sizeof(DIENVELOPE)) return DIERR_INVALIDPARAM;
2473 if (!impl->params.lpEnvelope) params->lpEnvelope = NULL;
2474 else memcpy( params->lpEnvelope, impl->params.lpEnvelope, sizeof(DIENVELOPE) );
2477 if (flags & DIEP_DURATION) params->dwDuration = impl->params.dwDuration;
2478 if (flags & DIEP_GAIN) params->dwGain = impl->params.dwGain;
2479 if (flags & DIEP_SAMPLEPERIOD) params->dwSamplePeriod = impl->params.dwSamplePeriod;
2480 if (flags & DIEP_STARTDELAY)
2482 if (params->dwSize != sizeof(DIEFFECT_DX6)) return DIERR_INVALIDPARAM;
2483 params->dwStartDelay = impl->params.dwStartDelay;
2485 if (flags & DIEP_TRIGGERREPEATINTERVAL) params->dwTriggerRepeatInterval = impl->params.dwTriggerRepeatInterval;
2487 if (flags & DIEP_TRIGGERBUTTON)
2489 if (!object_flags) return DIERR_INVALIDPARAM;
2491 filter.dwObj = impl->params.dwTriggerButton;
2492 if (object_flags & DIEFF_OBJECTIDS)
2493 ret = enum_objects( impl->joystick, &filter, DIDFT_BUTTON, get_parameters_object_id,
2494 &params->dwTriggerButton );
2495 else
2496 ret = enum_objects( impl->joystick, &filter, DIDFT_BUTTON, get_parameters_object_ofs,
2497 &params->dwTriggerButton );
2498 if (ret != DIENUM_STOP) params->dwTriggerButton = -1;
2501 return DI_OK;
2504 static BOOL set_parameters_object( struct dinput_device *device, UINT index, struct hid_value_caps *caps,
2505 const DIDEVICEOBJECTINSTANCEW *instance, void *data )
2507 DWORD usages = MAKELONG( instance->wUsage, instance->wUsagePage );
2508 *(DWORD *)data = usages;
2509 return DIENUM_STOP;
2512 static HRESULT WINAPI hid_joystick_effect_SetParameters( IDirectInputEffect *iface,
2513 const DIEFFECT *params, DWORD flags )
2515 DIPROPHEADER filter =
2517 .dwSize = sizeof(DIPROPHEADER),
2518 .dwHeaderSize = sizeof(DIPROPHEADER),
2519 .dwHow = DIPH_BYUSAGE,
2521 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
2522 ULONG i, count, old_value, object_flags, direction_flags;
2523 HRESULT hr;
2524 BOOL ret;
2526 TRACE( "iface %p, params %p, flags %#lx.\n", iface, params, flags );
2528 if (!params) return E_POINTER;
2529 if (params->dwSize != sizeof(DIEFFECT_DX6) && params->dwSize != sizeof(DIEFFECT_DX5)) return DIERR_INVALIDPARAM;
2530 object_flags = params->dwFlags & (DIEFF_OBJECTIDS | DIEFF_OBJECTOFFSETS);
2531 direction_flags = params->dwFlags & (DIEFF_CARTESIAN | DIEFF_POLAR | DIEFF_SPHERICAL);
2533 if (object_flags & DIEFF_OBJECTIDS) filter.dwHow = DIPH_BYID;
2534 else filter.dwHow = DIPH_BYOFFSET;
2536 if (flags & DIEP_AXES)
2538 if (!object_flags) return DIERR_INVALIDPARAM;
2539 if (!params->rgdwAxes) return DIERR_INVALIDPARAM;
2540 if (impl->params.cAxes) return DIERR_ALREADYINITIALIZED;
2541 count = impl->joystick->pid_effect_update.axis_count;
2542 if (params->cAxes > count) return DIERR_INVALIDPARAM;
2544 impl->params.cAxes = params->cAxes;
2545 for (i = 0; i < params->cAxes; ++i)
2547 filter.dwObj = params->rgdwAxes[i];
2548 ret = enum_objects( impl->joystick, &filter, DIDFT_AXIS, set_parameters_object,
2549 &impl->params.rgdwAxes[i] );
2550 if (ret != DIENUM_STOP) impl->params.rgdwAxes[i] = 0;
2553 impl->modified |= DIEP_AXES;
2556 if (flags & DIEP_DIRECTION)
2558 if (!direction_flags) return DIERR_INVALIDPARAM;
2559 if (!params->rglDirection) return DIERR_INVALIDPARAM;
2561 count = impl->params.cAxes;
2562 if (params->cAxes < count) return DIERR_INVALIDPARAM;
2563 if ((direction_flags & DIEFF_POLAR) && count != 2) return DIERR_INVALIDPARAM;
2564 if ((direction_flags & DIEFF_CARTESIAN) && params->cAxes != count) return DIERR_INVALIDPARAM;
2566 impl->params.dwFlags &= ~(DIEFF_CARTESIAN | DIEFF_POLAR | DIEFF_SPHERICAL);
2567 impl->params.dwFlags |= direction_flags;
2568 if (memcmp( impl->params.rglDirection, params->rglDirection, count * sizeof(LONG) ))
2569 impl->modified |= DIEP_DIRECTION;
2570 memcpy( impl->params.rglDirection, params->rglDirection, count * sizeof(LONG) );
2573 if (flags & DIEP_TYPESPECIFICPARAMS)
2575 if (!params->lpvTypeSpecificParams) return E_POINTER;
2576 switch (impl->type)
2578 case PID_USAGE_ET_SQUARE:
2579 case PID_USAGE_ET_SINE:
2580 case PID_USAGE_ET_TRIANGLE:
2581 case PID_USAGE_ET_SAWTOOTH_UP:
2582 case PID_USAGE_ET_SAWTOOTH_DOWN:
2583 if (params->cbTypeSpecificParams != sizeof(DIPERIODIC))
2584 return DIERR_INVALIDPARAM;
2585 break;
2586 case PID_USAGE_ET_SPRING:
2587 case PID_USAGE_ET_DAMPER:
2588 case PID_USAGE_ET_INERTIA:
2589 case PID_USAGE_ET_FRICTION:
2590 if (params->cbTypeSpecificParams != sizeof(DICONDITION) && impl->params.cAxes &&
2591 params->cbTypeSpecificParams != impl->params.cAxes * sizeof(DICONDITION))
2592 return DIERR_INVALIDPARAM;
2593 break;
2594 case PID_USAGE_ET_CONSTANT_FORCE:
2595 if (params->cbTypeSpecificParams != sizeof(DICONSTANTFORCE))
2596 return DIERR_INVALIDPARAM;
2597 break;
2598 case PID_USAGE_ET_RAMP:
2599 if (params->cbTypeSpecificParams != sizeof(DIRAMPFORCE))
2600 return DIERR_INVALIDPARAM;
2601 break;
2602 case PID_USAGE_ET_CUSTOM_FORCE_DATA:
2603 FIXME( "custom force data not implemented!\n" );
2604 return DIERR_UNSUPPORTED;
2607 if (memcmp( impl->params.lpvTypeSpecificParams, params->lpvTypeSpecificParams,
2608 params->cbTypeSpecificParams ))
2609 impl->modified |= DIEP_TYPESPECIFICPARAMS;
2610 memcpy( impl->params.lpvTypeSpecificParams, params->lpvTypeSpecificParams,
2611 params->cbTypeSpecificParams );
2612 impl->params.cbTypeSpecificParams = params->cbTypeSpecificParams;
2615 if (!(flags & DIEP_ENVELOPE))
2616 TRACE( "Keeping previous effect envelope\n" );
2617 else if (params->lpEnvelope)
2619 if (params->lpEnvelope->dwSize != sizeof(DIENVELOPE)) return DIERR_INVALIDPARAM;
2620 impl->params.lpEnvelope = &impl->envelope;
2621 if (memcmp( impl->params.lpEnvelope, params->lpEnvelope, sizeof(DIENVELOPE) ))
2622 impl->modified |= DIEP_ENVELOPE;
2623 memcpy( impl->params.lpEnvelope, params->lpEnvelope, sizeof(DIENVELOPE) );
2625 else
2627 flags &= ~DIEP_ENVELOPE;
2628 impl->flags &= ~DIEP_ENVELOPE;
2629 impl->modified &= ~DIEP_ENVELOPE;
2630 impl->params.lpEnvelope = NULL;
2633 if (flags & DIEP_DURATION)
2635 impl->modified |= DIEP_DURATION;
2636 impl->params.dwDuration = params->dwDuration;
2638 if (flags & DIEP_GAIN)
2640 if (impl->params.dwGain != params->dwGain) impl->modified |= DIEP_GAIN;
2641 impl->params.dwGain = params->dwGain;
2643 if (flags & DIEP_SAMPLEPERIOD)
2645 if (impl->params.dwSamplePeriod != params->dwSamplePeriod) impl->modified |= DIEP_SAMPLEPERIOD;
2646 impl->params.dwSamplePeriod = params->dwSamplePeriod;
2648 if (flags & DIEP_STARTDELAY)
2650 if (params->dwSize != sizeof(DIEFFECT_DX6)) return DIERR_INVALIDPARAM;
2651 if (impl->params.dwStartDelay != params->dwStartDelay) impl->modified |= DIEP_STARTDELAY;
2652 impl->params.dwStartDelay = params->dwStartDelay;
2654 if (flags & DIEP_TRIGGERREPEATINTERVAL)
2656 if (impl->params.dwTriggerRepeatInterval != params->dwTriggerRepeatInterval)
2657 impl->modified |= DIEP_TRIGGERREPEATINTERVAL;
2658 impl->params.dwTriggerRepeatInterval = params->dwTriggerRepeatInterval;
2661 if (flags & DIEP_TRIGGERBUTTON)
2663 if (!object_flags) return DIERR_INVALIDPARAM;
2665 filter.dwObj = params->dwTriggerButton;
2666 old_value = impl->params.dwTriggerButton;
2667 ret = enum_objects( impl->joystick, &filter, DIDFT_BUTTON, set_parameters_object,
2668 &impl->params.dwTriggerButton );
2669 if (ret != DIENUM_STOP) impl->params.dwTriggerButton = -1;
2670 if (impl->params.dwTriggerButton != old_value) impl->modified |= DIEP_TRIGGERBUTTON;
2673 impl->flags |= flags;
2675 if (flags & DIEP_NODOWNLOAD) return DI_DOWNLOADSKIPPED;
2676 if (flags & DIEP_START) hr = IDirectInputEffect_Start( iface, 1, 0 );
2677 else hr = IDirectInputEffect_Download( iface );
2678 if (hr == DIERR_NOTEXCLUSIVEACQUIRED) return DI_DOWNLOADSKIPPED;
2679 if (FAILED(hr)) return hr;
2680 return DI_OK;
2683 static HRESULT WINAPI hid_joystick_effect_Start( IDirectInputEffect *iface, DWORD iterations, DWORD flags )
2685 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
2686 struct pid_control_report *effect_control = &impl->joystick->pid_effect_control;
2687 ULONG count, report_len = impl->joystick->caps.OutputReportByteLength;
2688 PHIDP_PREPARSED_DATA preparsed = impl->joystick->preparsed;
2689 HANDLE device = impl->joystick->device;
2690 NTSTATUS status;
2691 USAGE control;
2692 HRESULT hr;
2694 TRACE( "iface %p, iterations %lu, flags %#lx.\n", iface, iterations, flags );
2696 if ((flags & ~(DIES_NODOWNLOAD|DIES_SOLO))) return DIERR_INVALIDPARAM;
2697 if (flags & DIES_SOLO) control = PID_USAGE_OP_EFFECT_START_SOLO;
2698 else control = PID_USAGE_OP_EFFECT_START;
2700 EnterCriticalSection( &impl->joystick->base.crit );
2701 if (!is_exclusively_acquired( impl->joystick ))
2702 hr = DIERR_NOTEXCLUSIVEACQUIRED;
2703 else if ((flags & DIES_NODOWNLOAD) && !impl->index)
2704 hr = DIERR_NOTDOWNLOADED;
2705 else if ((flags & DIES_NODOWNLOAD) || SUCCEEDED(hr = IDirectInputEffect_Download( iface )))
2707 count = 1;
2708 status = HidP_InitializeReportForID( HidP_Output, effect_control->id, preparsed,
2709 impl->effect_control_buf, report_len );
2711 if (status != HIDP_STATUS_SUCCESS) hr = status;
2712 else status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX,
2713 impl->index, preparsed, impl->effect_control_buf, report_len );
2715 if (status != HIDP_STATUS_SUCCESS) hr = status;
2716 else status = HidP_SetUsages( HidP_Output, HID_USAGE_PAGE_PID, effect_control->control_coll,
2717 &control, &count, preparsed, impl->effect_control_buf, report_len );
2719 if (status != HIDP_STATUS_SUCCESS) hr = status;
2720 else status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_LOOP_COUNT,
2721 iterations, preparsed, impl->effect_control_buf, report_len );
2723 if (status != HIDP_STATUS_SUCCESS) hr = status;
2724 else if (WriteFile( device, impl->effect_control_buf, report_len, NULL, NULL )) hr = DI_OK;
2725 else hr = DIERR_INPUTLOST;
2727 if (SUCCEEDED(hr)) impl->status |= DIEGES_PLAYING;
2728 else impl->status &= ~DIEGES_PLAYING;
2730 LeaveCriticalSection( &impl->joystick->base.crit );
2732 return hr;
2735 static HRESULT WINAPI hid_joystick_effect_Stop( IDirectInputEffect *iface )
2737 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
2738 struct pid_control_report *effect_control = &impl->joystick->pid_effect_control;
2739 ULONG count, report_len = impl->joystick->caps.OutputReportByteLength;
2740 PHIDP_PREPARSED_DATA preparsed = impl->joystick->preparsed;
2741 HANDLE device = impl->joystick->device;
2742 NTSTATUS status;
2743 USAGE control;
2744 HRESULT hr;
2746 TRACE( "iface %p.\n", iface );
2748 EnterCriticalSection( &impl->joystick->base.crit );
2749 if (!is_exclusively_acquired( impl->joystick ))
2750 hr = DIERR_NOTEXCLUSIVEACQUIRED;
2751 else if (!impl->index)
2752 hr = DIERR_NOTDOWNLOADED;
2753 else
2755 count = 1;
2756 control = PID_USAGE_OP_EFFECT_STOP;
2757 status = HidP_InitializeReportForID( HidP_Output, effect_control->id, preparsed,
2758 impl->effect_control_buf, report_len );
2760 if (status != HIDP_STATUS_SUCCESS) hr = status;
2761 else status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX,
2762 impl->index, preparsed, impl->effect_control_buf, report_len );
2764 if (status != HIDP_STATUS_SUCCESS) hr = status;
2765 else status = HidP_SetUsages( HidP_Output, HID_USAGE_PAGE_PID, effect_control->control_coll,
2766 &control, &count, preparsed, impl->effect_control_buf, report_len );
2768 if (status != HIDP_STATUS_SUCCESS) hr = status;
2769 else status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_LOOP_COUNT,
2770 0, preparsed, impl->effect_control_buf, report_len );
2772 if (status != HIDP_STATUS_SUCCESS) hr = status;
2773 else if (WriteFile( device, impl->effect_control_buf, report_len, NULL, NULL )) hr = DI_OK;
2774 else hr = DIERR_INPUTLOST;
2776 impl->status &= ~DIEGES_PLAYING;
2778 LeaveCriticalSection( &impl->joystick->base.crit );
2780 return hr;
2783 static HRESULT WINAPI hid_joystick_effect_GetEffectStatus( IDirectInputEffect *iface, DWORD *status )
2785 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
2786 HRESULT hr = DI_OK;
2788 TRACE( "iface %p, status %p.\n", iface, status );
2790 if (!status) return E_POINTER;
2791 *status = 0;
2793 EnterCriticalSection( &impl->joystick->base.crit );
2794 if (!is_exclusively_acquired( impl->joystick ))
2795 hr = DIERR_NOTEXCLUSIVEACQUIRED;
2796 else if (!impl->index)
2797 hr = DIERR_NOTDOWNLOADED;
2798 else
2799 *status = impl->status;
2800 LeaveCriticalSection( &impl->joystick->base.crit );
2802 return hr;
2805 static void set_parameter_value( struct hid_joystick_effect *impl, char *report_buf,
2806 struct hid_value_caps *caps, LONG value )
2808 return set_report_value( impl->joystick, report_buf, caps, value );
2811 static void set_parameter_value_angle( struct hid_joystick_effect *impl, char *report_buf,
2812 struct hid_value_caps *caps, LONG value )
2814 LONG exp;
2815 if (!caps) return;
2816 exp = caps->units_exp;
2817 if (caps->units != 0x14) WARN( "unknown angle unit caps %#lx\n", caps->units );
2818 else if (exp < -2) while (exp++ < -2) value *= 10;
2819 else if (exp > -2) while (exp-- > -2) value /= 10;
2820 set_parameter_value( impl, report_buf, caps, value );
2823 static void set_parameter_value_us( struct hid_joystick_effect *impl, char *report_buf,
2824 struct hid_value_caps *caps, LONG value )
2826 LONG exp;
2827 if (!caps) return;
2828 exp = caps->units_exp;
2829 if (value == INFINITE) value = caps->physical_min - 1;
2830 else if (caps->units != 0x1003) WARN( "unknown time unit caps %#lx\n", caps->units );
2831 else if (exp < -6) while (exp++ < -6) value *= 10;
2832 else if (exp > -6) while (exp-- > -6) value /= 10;
2833 set_parameter_value( impl, report_buf, caps, value );
2836 static BOOL is_axis_usage_enabled( struct hid_joystick_effect *impl, USAGE usage )
2838 DWORD i = impl->params.cAxes;
2839 while (i--) if (LOWORD(impl->params.rgdwAxes[i]) == usage) return TRUE;
2840 return FALSE;
2843 static HRESULT WINAPI hid_joystick_effect_Download( IDirectInputEffect *iface )
2845 static const DWORD complete_mask = DIEP_AXES | DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS;
2846 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
2847 struct pid_set_constant_force *set_constant_force = &impl->joystick->pid_set_constant_force;
2848 struct pid_set_ramp_force *set_ramp_force = &impl->joystick->pid_set_ramp_force;
2849 struct pid_effect_update *effect_update = &impl->joystick->pid_effect_update;
2850 struct pid_set_condition *set_condition = &impl->joystick->pid_set_condition;
2851 struct pid_set_periodic *set_periodic = &impl->joystick->pid_set_periodic;
2852 struct pid_set_envelope *set_envelope = &impl->joystick->pid_set_envelope;
2853 ULONG report_len = impl->joystick->caps.OutputReportByteLength;
2854 HANDLE device = impl->joystick->device;
2855 struct hid_value_caps *caps;
2856 LONG directions[4] = {0};
2857 DWORD i, tmp, count;
2858 DIEFFECT spherical;
2859 NTSTATUS status;
2860 USAGE usage;
2861 HRESULT hr;
2863 TRACE( "iface %p\n", iface );
2865 EnterCriticalSection( &impl->joystick->base.crit );
2866 if (impl->modified) hr = DI_OK;
2867 else hr = DI_NOEFFECT;
2869 if (!is_exclusively_acquired( impl->joystick ))
2870 hr = DIERR_NOTEXCLUSIVEACQUIRED;
2871 else if ((impl->flags & complete_mask) != complete_mask)
2872 hr = DIERR_INCOMPLETEEFFECT;
2873 else if (!impl->index && SUCCEEDED(hr = find_next_effect_id( impl->joystick, &impl->index, impl->type )))
2875 if (!impl->type_specific_buf[0]) status = HIDP_STATUS_SUCCESS;
2876 else status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX,
2877 impl->index, impl->joystick->preparsed, impl->type_specific_buf, report_len );
2878 if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_SetUsageValue returned %#lx\n", status );
2880 if (!impl->set_envelope_buf[0]) status = HIDP_STATUS_SUCCESS;
2881 else status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX,
2882 impl->index, impl->joystick->preparsed, impl->set_envelope_buf, report_len );
2883 if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_SetUsageValue returned %#lx\n", status );
2885 status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX,
2886 impl->index, impl->joystick->preparsed, impl->effect_update_buf, report_len );
2887 if (status != HIDP_STATUS_SUCCESS) hr = status;
2888 else hr = DI_OK;
2891 if (hr == DI_OK)
2893 switch (impl->type)
2895 case PID_USAGE_ET_SQUARE:
2896 case PID_USAGE_ET_SINE:
2897 case PID_USAGE_ET_TRIANGLE:
2898 case PID_USAGE_ET_SAWTOOTH_UP:
2899 case PID_USAGE_ET_SAWTOOTH_DOWN:
2900 if (!(impl->modified & DIEP_TYPESPECIFICPARAMS)) break;
2902 set_parameter_value( impl, impl->type_specific_buf, set_periodic->magnitude_caps,
2903 impl->periodic.dwMagnitude );
2904 set_parameter_value_us( impl, impl->type_specific_buf, set_periodic->period_caps,
2905 impl->periodic.dwPeriod );
2906 set_parameter_value( impl, impl->type_specific_buf, set_periodic->phase_caps,
2907 impl->periodic.dwPhase );
2908 set_parameter_value( impl, impl->type_specific_buf, set_periodic->offset_caps,
2909 impl->periodic.lOffset );
2911 if (!WriteFile( device, impl->type_specific_buf, report_len, NULL, NULL )) hr = DIERR_INPUTLOST;
2912 else impl->modified &= ~DIEP_TYPESPECIFICPARAMS;
2913 break;
2914 case PID_USAGE_ET_SPRING:
2915 case PID_USAGE_ET_DAMPER:
2916 case PID_USAGE_ET_INERTIA:
2917 case PID_USAGE_ET_FRICTION:
2918 if (!(impl->modified & DIEP_TYPESPECIFICPARAMS)) break;
2920 for (i = 0; i < impl->params.cbTypeSpecificParams / sizeof(DICONDITION); ++i)
2922 status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_PARAMETER_BLOCK_OFFSET,
2923 i, impl->joystick->preparsed, impl->type_specific_buf, report_len );
2924 if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_SetUsageValue %04x:%04x returned %#lx\n",
2925 HID_USAGE_PAGE_PID, PID_USAGE_PARAMETER_BLOCK_OFFSET, status );
2926 set_parameter_value( impl, impl->type_specific_buf, set_condition->center_point_offset_caps,
2927 impl->condition[i].lOffset );
2928 set_parameter_value( impl, impl->type_specific_buf, set_condition->positive_coefficient_caps,
2929 impl->condition[i].lPositiveCoefficient );
2930 set_parameter_value( impl, impl->type_specific_buf, set_condition->negative_coefficient_caps,
2931 impl->condition[i].lNegativeCoefficient );
2932 set_parameter_value( impl, impl->type_specific_buf, set_condition->positive_saturation_caps,
2933 impl->condition[i].dwPositiveSaturation );
2934 set_parameter_value( impl, impl->type_specific_buf, set_condition->negative_saturation_caps,
2935 impl->condition[i].dwNegativeSaturation );
2936 set_parameter_value( impl, impl->type_specific_buf, set_condition->dead_band_caps,
2937 impl->condition[i].lDeadBand );
2939 if (!WriteFile( device, impl->type_specific_buf, report_len, NULL, NULL )) hr = DIERR_INPUTLOST;
2940 else impl->modified &= ~DIEP_TYPESPECIFICPARAMS;
2942 break;
2943 case PID_USAGE_ET_CONSTANT_FORCE:
2944 if (!(impl->modified & DIEP_TYPESPECIFICPARAMS)) break;
2946 set_parameter_value( impl, impl->type_specific_buf, set_constant_force->magnitude_caps,
2947 impl->constant_force.lMagnitude );
2949 if (!WriteFile( device, impl->type_specific_buf, report_len, NULL, NULL )) hr = DIERR_INPUTLOST;
2950 else impl->modified &= ~DIEP_TYPESPECIFICPARAMS;
2951 break;
2952 case PID_USAGE_ET_RAMP:
2953 if (!(impl->modified & DIEP_TYPESPECIFICPARAMS)) break;
2955 set_parameter_value( impl, impl->type_specific_buf, set_ramp_force->start_caps,
2956 impl->ramp_force.lStart );
2957 set_parameter_value( impl, impl->type_specific_buf, set_ramp_force->end_caps,
2958 impl->ramp_force.lEnd );
2960 if (!WriteFile( device, impl->type_specific_buf, report_len, NULL, NULL )) hr = DIERR_INPUTLOST;
2961 else impl->modified &= ~DIEP_TYPESPECIFICPARAMS;
2962 break;
2966 if (hr == DI_OK)
2968 switch (impl->type)
2970 case PID_USAGE_ET_SQUARE:
2971 case PID_USAGE_ET_SINE:
2972 case PID_USAGE_ET_TRIANGLE:
2973 case PID_USAGE_ET_SAWTOOTH_UP:
2974 case PID_USAGE_ET_SAWTOOTH_DOWN:
2975 case PID_USAGE_ET_CONSTANT_FORCE:
2976 case PID_USAGE_ET_RAMP:
2977 if (!(impl->flags & DIEP_ENVELOPE)) break;
2978 if (!(impl->modified & DIEP_ENVELOPE)) break;
2980 set_parameter_value( impl, impl->set_envelope_buf, set_envelope->attack_level_caps,
2981 impl->envelope.dwAttackLevel );
2982 set_parameter_value_us( impl, impl->set_envelope_buf, set_envelope->attack_time_caps,
2983 impl->envelope.dwAttackTime );
2984 set_parameter_value( impl, impl->set_envelope_buf, set_envelope->fade_level_caps,
2985 impl->envelope.dwFadeLevel );
2986 set_parameter_value_us( impl, impl->set_envelope_buf, set_envelope->fade_time_caps,
2987 impl->envelope.dwFadeTime );
2989 if (!WriteFile( device, impl->set_envelope_buf, report_len, NULL, NULL )) hr = DIERR_INPUTLOST;
2990 else impl->modified &= ~DIEP_ENVELOPE;
2991 break;
2995 if (hr == DI_OK && impl->modified)
2997 set_parameter_value_us( impl, impl->effect_update_buf, effect_update->duration_caps,
2998 impl->params.dwDuration );
2999 set_parameter_value( impl, impl->effect_update_buf, effect_update->gain_caps,
3000 impl->params.dwGain );
3001 set_parameter_value_us( impl, impl->effect_update_buf, effect_update->sample_period_caps,
3002 impl->params.dwSamplePeriod );
3003 set_parameter_value_us( impl, impl->effect_update_buf, effect_update->start_delay_caps,
3004 impl->params.dwStartDelay );
3005 set_parameter_value_us( impl, impl->effect_update_buf, effect_update->trigger_repeat_interval_caps,
3006 impl->params.dwTriggerRepeatInterval );
3008 count = 1;
3009 usage = PID_USAGE_DIRECTION_ENABLE;
3010 status = HidP_SetUsages( HidP_Output, HID_USAGE_PAGE_PID, 0, &usage, &count,
3011 impl->joystick->preparsed, impl->effect_update_buf, report_len );
3012 if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_SetUsages returned %#lx\n", status );
3014 spherical.rglDirection = directions;
3015 convert_directions_to_spherical( &impl->params, &spherical );
3017 /* FIXME: as far as the test cases go, directions are only written if
3018 * either X or Y axes are enabled, maybe need more tests though */
3019 if (!is_axis_usage_enabled( impl, HID_USAGE_GENERIC_X ) &&
3020 !is_axis_usage_enabled( impl, HID_USAGE_GENERIC_Y ))
3021 WARN( "neither X or Y axes are selected, skipping direction\n" );
3022 else for (i = 0; i < min( effect_update->direction_count, spherical.cAxes ); ++i)
3024 tmp = directions[i] + (i == 0 ? 9000 : 0);
3025 caps = effect_update->direction_caps[effect_update->direction_count - i - 1];
3026 set_parameter_value_angle( impl, impl->effect_update_buf, caps, tmp % 36000 );
3029 status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_TRIGGER_BUTTON,
3030 impl->params.dwTriggerButton, impl->joystick->preparsed,
3031 impl->effect_update_buf, report_len );
3032 if (status != HIDP_STATUS_SUCCESS) WARN( "HidP_SetUsageValue returned %#lx\n", status );
3034 if (!WriteFile( device, impl->effect_update_buf, report_len, NULL, NULL )) hr = DIERR_INPUTLOST;
3035 else impl->modified = 0;
3037 if (SUCCEEDED(hr)) impl->joystick->base.force_feedback_state &= ~DIGFFS_EMPTY;
3039 LeaveCriticalSection( &impl->joystick->base.crit );
3041 return hr;
3044 static void check_empty_force_feedback_state( struct hid_joystick *joystick )
3046 struct hid_joystick_effect *effect;
3047 LIST_FOR_EACH_ENTRY( effect, &joystick->effect_list, struct hid_joystick_effect, entry )
3048 if (effect->index) return;
3049 joystick->base.force_feedback_state |= DIGFFS_EMPTY;
3052 static HRESULT WINAPI hid_joystick_effect_Unload( IDirectInputEffect *iface )
3054 struct hid_joystick_effect *impl = impl_from_IDirectInputEffect( iface );
3055 struct hid_joystick *joystick = impl->joystick;
3056 struct pid_device_pool *device_pool = &joystick->pid_device_pool;
3057 struct pid_block_free *block_free = &joystick->pid_block_free;
3058 ULONG report_len = joystick->caps.OutputReportByteLength;
3059 HRESULT hr = DI_OK;
3060 NTSTATUS status;
3062 TRACE( "iface %p\n", iface );
3064 EnterCriticalSection( &joystick->base.crit );
3065 if (!impl->index)
3066 hr = DI_NOEFFECT;
3067 else if (SUCCEEDED(hr = IDirectInputEffect_Stop( iface )))
3069 if (!device_pool->device_managed_caps)
3070 joystick->effect_inuse[impl->index - 1] = FALSE;
3071 else if (block_free->id)
3073 status = HidP_InitializeReportForID( HidP_Output, block_free->id, joystick->preparsed,
3074 joystick->output_report_buf, report_len );
3076 if (status != HIDP_STATUS_SUCCESS) hr = status;
3077 else status = HidP_SetUsageValue( HidP_Output, HID_USAGE_PAGE_PID, 0, PID_USAGE_EFFECT_BLOCK_INDEX,
3078 impl->index, joystick->preparsed, joystick->output_report_buf, report_len );
3080 if (status != HIDP_STATUS_SUCCESS) hr = status;
3081 else if (WriteFile( joystick->device, joystick->output_report_buf, report_len, NULL, NULL )) hr = DI_OK;
3082 else hr = DIERR_INPUTLOST;
3085 impl->modified = ~0;
3086 impl->index = 0;
3087 check_empty_force_feedback_state( joystick );
3089 LeaveCriticalSection( &joystick->base.crit );
3091 return hr;
3094 static HRESULT WINAPI hid_joystick_effect_Escape( IDirectInputEffect *iface, DIEFFESCAPE *escape )
3096 FIXME( "iface %p, escape %p stub!\n", iface, escape );
3097 return DIERR_UNSUPPORTED;
3100 static IDirectInputEffectVtbl hid_joystick_effect_vtbl =
3102 /*** IUnknown methods ***/
3103 hid_joystick_effect_QueryInterface,
3104 hid_joystick_effect_AddRef,
3105 hid_joystick_effect_Release,
3106 /*** IDirectInputEffect methods ***/
3107 hid_joystick_effect_Initialize,
3108 hid_joystick_effect_GetEffectGuid,
3109 hid_joystick_effect_GetParameters,
3110 hid_joystick_effect_SetParameters,
3111 hid_joystick_effect_Start,
3112 hid_joystick_effect_Stop,
3113 hid_joystick_effect_GetEffectStatus,
3114 hid_joystick_effect_Download,
3115 hid_joystick_effect_Unload,
3116 hid_joystick_effect_Escape,
3119 static HRESULT hid_joystick_create_effect( IDirectInputDevice8W *iface, IDirectInputEffect **out )
3121 struct hid_joystick *joystick = impl_from_IDirectInputDevice8W( iface );
3122 struct hid_joystick_effect *impl;
3123 ULONG report_len;
3125 if (!(impl = calloc( 1, sizeof(*impl) ))) return DIERR_OUTOFMEMORY;
3126 impl->IDirectInputEffect_iface.lpVtbl = &hid_joystick_effect_vtbl;
3127 impl->ref = 1;
3128 impl->joystick = joystick;
3129 dinput_device_internal_addref( &joystick->base );
3131 EnterCriticalSection( &joystick->base.crit );
3132 list_add_tail( &joystick->effect_list, &impl->entry );
3133 LeaveCriticalSection( &joystick->base.crit );
3135 report_len = joystick->caps.OutputReportByteLength;
3136 if (!(impl->effect_control_buf = malloc( report_len ))) goto failed;
3137 if (!(impl->effect_update_buf = malloc( report_len ))) goto failed;
3138 if (!(impl->type_specific_buf = malloc( report_len ))) goto failed;
3139 if (!(impl->set_envelope_buf = malloc( report_len ))) goto failed;
3141 impl->envelope.dwSize = sizeof(DIENVELOPE);
3142 impl->params.dwSize = sizeof(DIEFFECT);
3143 impl->params.rgdwAxes = impl->axes;
3144 impl->params.rglDirection = impl->directions;
3145 impl->params.dwTriggerButton = -1;
3146 impl->modified = ~0;
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;