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
26 #define WIN32_NO_STATUS
35 #include "ddk/hidclass.h"
36 #include "ddk/hidsdi.h"
42 #include "dinput_private.h"
43 #include "device_private.h"
47 #include "wine/debug.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
63 struct pid_effect_update
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
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
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
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
118 struct hid_value_caps
*magnitude_caps
;
121 struct pid_set_ramp_force
125 struct hid_value_caps
*start_caps
;
126 struct hid_value_caps
*end_caps
;
129 struct pid_device_gain
133 struct hid_value_caps
*device_gain_caps
;
136 struct pid_device_pool
140 struct hid_value_caps
*device_managed_caps
;
143 struct pid_block_free
149 struct pid_block_load
156 struct pid_new_effect
163 struct pid_effect_state
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
;
174 struct dinput_device base
;
178 PHIDP_PREPARSED_DATA preparsed
;
180 WCHAR device_path
[MAX_PATH
];
181 HIDD_ATTRIBUTES attrs
;
184 char *input_report_buf
;
185 char *output_report_buf
;
186 char *feature_report_buf
;
187 USAGE_AND_PAGE
*usages_buf
;
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
;
222 struct hid_joystick
*joystick
;
226 DICONSTANTFORCE constant_force
;
227 DIRAMPFORCE ramp_force
;
228 DICONDITION condition
[6];
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
)
256 case HID_USAGE_PAGE_BUTTON
: return &GUID_Button
;
257 case HID_USAGE_PAGE_SIMULATION
:
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
;
267 case HID_USAGE_PAGE_GENERIC
:
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
;
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
;
304 static inline const GUID
*effect_usage_to_guid( USAGE 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
;
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
;
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
;
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
;
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
;
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
)
490 return callback( &impl
->base
, index
, caps
, instance
, data
);
492 if (filter
->dwObj
!= instance
->dwOfs
) return DIENUM_CONTINUE
;
493 return callback( &impl
->base
, index
, caps
, instance
, data
);
495 if ((filter
->dwObj
& 0x00ffffff) != (instance
->dwType
& 0x00ffffff)) return DIENUM_CONTINUE
;
496 return callback( &impl
->base
, index
, caps
, instance
, data
);
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
);
502 FIXME( "unimplemented filter dwHow %#lx dwObj %#lx\n", filter
->dwHow
, filter
->dwObj
);
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
;
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
)++ );
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};
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
;
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
;
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
;
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
;
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
;
592 case MAKELONG(HID_USAGE_GENERIC_HATSWITCH
, HID_USAGE_PAGE_GENERIC
):
593 instance
.dwType
= DIDFT_POV
| DIDFT_MAKEINSTANCE( pov
++ );
594 instance
.dwFlags
= 0;
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
;
602 instance
.dwType
= DIDFT_ABSAXIS
| DIDFT_MAKEINSTANCE( 6 + axis
++ );
603 instance
.dwFlags
= 0;
606 instance
.wUsagePage
= caps
->usage_page
;
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
);
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
;
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
;
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
;
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
;
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
)
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
;
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
);
795 case (DWORD_PTR
)DIPROP_PRODUCTNAME
:
797 DIPROPSTRING
*value
= (DIPROPSTRING
*)header
;
798 lstrcpynW( value
->wsz
, impl
->base
.instance
.tszProductName
, MAX_PATH
);
801 case (DWORD_PTR
)DIPROP_INSTANCENAME
:
803 DIPROPSTRING
*value
= (DIPROPSTRING
*)header
;
804 lstrcpynW( value
->wsz
, impl
->base
.instance
.tszInstanceName
, MAX_PATH
);
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
);
814 case (DWORD_PTR
)DIPROP_JOYSTICKID
:
816 DIPROPDWORD
*value
= (DIPROPDWORD
*)header
;
817 value
->dwData
= impl
->base
.instance
.guidInstance
.Data3
;
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
);
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
;
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
;
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
;
861 static HRESULT
hid_joystick_acquire( IDirectInputDevice8W
*iface
)
863 struct hid_joystick
*impl
= impl_from_IDirectInputDevice8W( iface
);
864 ULONG report_len
= impl
->caps
.InputReportByteLength
;
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
);
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
);
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
);
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
;
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
;
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
;
938 case PID_USAGE_ET_CONSTANT_FORCE
:
939 type
= DIEFT_CONSTANTFORCE
;
941 case PID_USAGE_ET_RAMP
:
942 type
= DIEFT_RAMPFORCE
;
944 case PID_USAGE_ET_CUSTOM_FORCE_DATA
:
945 type
= DIEFT_CUSTOMFORCE
;
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
;
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
;
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
;
1025 info
->dwEffType
= type
;
1026 info
->dwStaticParams
= info
->dwDynamicParams
;
1027 lstrcpynW( info
->tszName
, effect_guid_to_string( guid
), MAX_PATH
);
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
;
1048 TRACE( "iface %p, command %#lx.\n", iface
, 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
;
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
);
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;
1095 struct parse_device_state_params
1097 BYTE old_state
[DEVICE_STATE_MAX_SIZE
];
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 );
1159 log_max
= MulDiv( log_min
- log_ctr
, properties
->deadzone
, 10000 );
1160 log_min
= MulDiv( log_min
- log_ctr
, properties
->saturation
, 10000 );
1165 log_min
= MulDiv( log_max
- log_ctr
, properties
->deadzone
, 10000 );
1166 log_max
= MulDiv( log_max
- log_ctr
, properties
->saturation
, 10000 );
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
;
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
;
1226 ret
= GetOverlappedResult( impl
->device
, &impl
->read_ovl
, &count
, FALSE
);
1228 EnterCriticalSection( &impl
->base
.crit
);
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
);
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
);
1266 params
.buttons
[usages
->Usage
- 1] = 0x80;
1269 enum_objects( impl
, &filter
, DIDFT_AXIS
| DIDFT_POV
, read_device_state_value
, ¶ms
);
1270 enum_objects( impl
, &filter
, DIDFT_BUTTON
, check_device_state_button
, ¶ms
);
1271 if (impl
->base
.hEvent
&& memcmp( ¶ms
.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
);
1281 device_state
= impl
->base
.force_feedback_state
& DIGFFS_EMPTY
;
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
;
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
);
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
,
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
;
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
,
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
;
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() );
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;
1460 if (!buttons
[count
].IsRange
) button_count
+= 1;
1461 else button_count
+= buttons
[count
].Range
.UsageMax
- buttons
[count
].Range
.UsageMin
+ 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
;
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
;
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);
1484 case DI8DEVTYPE_GAMEPAD
:
1485 if (button_count
< 6) type
|= (DI8DEVTYPEGAMEPAD_LIMITED
<< 8);
1486 else type
|= (DI8DEVTYPEGAMEPAD_STANDARD
<< 8);
1488 case DI8DEVTYPE_JOYSTICK
:
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
);
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);
1502 type
|= (DI8DEVTYPEJOYSTICK_STANDARD
<< 8);
1504 case DI8DEVTYPE_DRIVING
:
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
);
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
);
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);
1527 type
|= (DI8DEVTYPEDRIVING_LIMITED
<< 8);
1529 case DI8DEVTYPE_FLIGHT
:
1530 type
|= (DI8DEVTYPEFLIGHT_STICK
<< 8);
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
;
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
;
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
);
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
))
1576 if (FAILED(hid_joystick_device_try_open( detail
->DevicePath
, device
, preparsed
,
1577 attrs
, caps
, instance
, version
)))
1580 if (device_instance_is_disabled( instance
, &override
))
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
))
1589 if (!SetupDiEnumDeviceInterfaces( xi_set
, &devinfo
, &GUID_DEVINTERFACE_WINEXINPUT
, 0, &iface
))
1591 if (!SetupDiGetDeviceInterfaceDetailW( xi_set
, &iface
, detail
, sizeof(buffer
), NULL
, &devinfo
))
1594 CloseHandle( *device
);
1595 HidD_FreePreparsedData( *preparsed
);
1596 if (FAILED(hid_joystick_device_try_open( detail
->DevicePath
, device
, preparsed
,
1597 attrs
, caps
, instance
, version
)))
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;
1608 CloseHandle( *device
);
1609 HidD_FreePreparsedData( *preparsed
);
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
);
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
;
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
) );
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
;
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;
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 ) \
1700 if (rep->collection) FIXME( "duplicate " #rep " report!\n" ); \
1701 else rep->collection = DIDFT_GETINSTANCE( instance->dwType ); \
1704 #define SET_SUB_COLLECTION( rep, sub ) \
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" ); \
1711 rep->sub = DIDFT_GETINSTANCE( instance->dwType ); \
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
);
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 ) \
1776 rep->id = instance->wReportId; \
1777 else if (rep->id != instance->wReportId) \
1778 FIXME( "multiple " #rep " report ids!\n" ); \
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
,
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
;
2005 TRACE( "dinput %p, guid %s, out %p\n", dinput
, debugstr_guid( guid
), out
);
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
);
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
);
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
;
2097 IDirectInputDevice_Release( &impl
->base
.IDirectInputDevice8W_iface
);
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
);
2113 FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid
) );
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
);
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
);
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
);
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
;
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;
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
;
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
;
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
;
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
;
2202 case PID_USAGE_ET_CUSTOM_FORCE_DATA
:
2203 FIXME( "effect type %#x not implemented!\n", 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
;
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
;
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
);
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
;
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
;
2271 static void convert_directions_to_spherical( const DIEFFECT
*in
, DIEFFECT
*out
)
2273 DWORD i
, j
, direction_flags
= DIEFF_CARTESIAN
| DIEFF_POLAR
| DIEFF_SPHERICAL
;
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
;
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
;
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
;
2309 static void convert_directions_from_spherical( const DIEFFECT
*in
, DIEFFECT
*out
)
2311 DWORD i
, j
, direction_flags
= DIEFF_CARTESIAN
| DIEFF_POLAR
| DIEFF_SPHERICAL
;
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 );
2324 out
->rglDirection
[i
] = sin( in
->rglDirection
[i
- 1] * M_PI
/ 18000 ) * 10000;
2326 out
->cAxes
= in
->cAxes
;
2329 out
->rglDirection
[0] = (in
->rglDirection
[0] + 9000) % 36000;
2330 if (out
->rglDirection
[0] < 0) out
->rglDirection
[0] += 36000;
2331 out
->rglDirection
[1] = 0;
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
;
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
;
2361 convert_directions_to_spherical( in
, &spherical
);
2362 convert_directions_from_spherical( &spherical
, out
);
2364 case DIEFF_SPHERICAL
:
2365 convert_directions_to_spherical( in
, out
);
2371 switch (out
->dwFlags
& direction_flags
)
2374 memcpy( out
->rglDirection
, in
->rglDirection
, in
->cAxes
* sizeof(LONG
) );
2375 out
->cAxes
= in
->cAxes
;
2377 case DIEFF_CARTESIAN
:
2378 convert_directions_to_spherical( in
, &spherical
);
2379 convert_directions_from_spherical( &spherical
, out
);
2381 case DIEFF_SPHERICAL
:
2382 convert_directions_to_spherical( in
, out
);
2387 case DIEFF_SPHERICAL
:
2388 switch (out
->dwFlags
& direction_flags
)
2391 case DIEFF_CARTESIAN
:
2392 convert_directions_from_spherical( in
, out
);
2394 case DIEFF_SPHERICAL
:
2395 convert_directions_to_spherical( in
, out
);
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
;
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 ¶ms
->rgdwAxes
[i
] );
2436 ret
= enum_objects( impl
->joystick
, &filter
, DIDFT_AXIS
, get_parameters_object_ofs
,
2437 ¶ms
->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 ¶ms
->dwTriggerButton
);
2496 ret
= enum_objects( impl
->joystick
, &filter
, DIDFT_BUTTON
, get_parameters_object_ofs
,
2497 ¶ms
->dwTriggerButton
);
2498 if (ret
!= DIENUM_STOP
) params
->dwTriggerButton
= -1;
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
;
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
;
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
;
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
;
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
;
2594 case PID_USAGE_ET_CONSTANT_FORCE
:
2595 if (params
->cbTypeSpecificParams
!= sizeof(DICONSTANTFORCE
))
2596 return DIERR_INVALIDPARAM
;
2598 case PID_USAGE_ET_RAMP
:
2599 if (params
->cbTypeSpecificParams
!= sizeof(DIRAMPFORCE
))
2600 return DIERR_INVALIDPARAM
;
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
) );
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
;
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
;
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
)))
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
);
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
;
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
;
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
);
2783 static HRESULT WINAPI
hid_joystick_effect_GetEffectStatus( IDirectInputEffect
*iface
, DWORD
*status
)
2785 struct hid_joystick_effect
*impl
= impl_from_IDirectInputEffect( iface
);
2788 TRACE( "iface %p, status %p.\n", iface
, status
);
2790 if (!status
) return E_POINTER
;
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
;
2799 *status
= impl
->status
;
2800 LeaveCriticalSection( &impl
->joystick
->base
.crit
);
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
)
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
)
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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
);
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
);
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
;
3062 TRACE( "iface %p\n", iface
);
3064 EnterCriticalSection( &joystick
->base
.crit
);
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;
3087 check_empty_force_feedback_state( joystick
);
3089 LeaveCriticalSection( &joystick
->base
.crit
);
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
;
3125 if (!(impl
= calloc( 1, sizeof(*impl
) ))) return DIERR_OUTOFMEMORY
;
3126 impl
->IDirectInputEffect_iface
.lpVtbl
= &hid_joystick_effect_vtbl
;
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;
3149 *out
= &impl
->IDirectInputEffect_iface
;
3153 IDirectInputEffect_Release( &impl
->IDirectInputEffect_iface
);
3154 return DIERR_OUTOFMEMORY
;