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