include: Add missing enum XHR_PROP_ values.
[wine.git] / dlls / joy.cpl / dinput.c
blob521634ff7da27de6ed712c488b370569714af1a8
1 /*
2 * Copyright 2022 RĂ©mi Bernon for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #include <stdarg.h>
21 #include <stddef.h>
22 #include <math.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winuser.h"
27 #include "wingdi.h"
29 #include "dinput.h"
31 #include "wine/debug.h"
32 #include "wine/list.h"
34 #include "joy_private.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(joycpl);
38 struct effect
40 struct list entry;
41 IDirectInputEffect *effect;
44 struct device
46 struct list entry;
47 IDirectInputDevice8W *device;
50 static CRITICAL_SECTION state_cs;
51 static CRITICAL_SECTION_DEBUG state_cs_debug =
53 0, 0, &state_cs,
54 { &state_cs_debug.ProcessLocksList, &state_cs_debug.ProcessLocksList },
55 0, 0, { (DWORD_PTR)(__FILE__ ": state_cs") }
57 static CRITICAL_SECTION state_cs = { &state_cs_debug, -1, 0, 0, 0, 0 };
59 static struct list effects = LIST_INIT( effects );
60 static IDirectInputEffect *effect_selected;
62 static struct list devices = LIST_INIT( devices );
63 static IDirectInputDevice8W *device_selected;
65 static HWND dialog_hwnd;
66 static HANDLE state_event;
68 static BOOL CALLBACK enum_effects( const DIEFFECTINFOW *info, void *context )
70 IDirectInputDevice8W *device = context;
71 DWORD axes[2] = {DIJOFS_X, DIJOFS_Y};
72 LONG direction[2] = {0};
73 DIEFFECT params =
75 .dwSize = sizeof(DIEFFECT),
76 .dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS,
77 .dwDuration = 2 * DI_SECONDS,
78 .dwGain = DI_FFNOMINALMAX,
79 .rglDirection = direction,
80 .rgdwAxes = axes,
81 .cAxes = 2,
83 DICONSTANTFORCE constant =
85 .lMagnitude = DI_FFNOMINALMAX,
87 DIPERIODIC periodic =
89 .dwMagnitude = DI_FFNOMINALMAX,
90 .dwPeriod = DI_SECONDS / 2,
92 DICONDITION condition =
94 .dwPositiveSaturation = 10000,
95 .dwNegativeSaturation = 10000,
96 .lPositiveCoefficient = 10000,
97 .lNegativeCoefficient = 10000,
99 DIRAMPFORCE ramp =
101 .lEnd = DI_FFNOMINALMAX,
103 IDirectInputEffect *effect;
104 struct effect *entry;
105 HRESULT hr;
107 hr = IDirectInputDevice8_Acquire( device );
108 if (FAILED(hr)) return DIENUM_CONTINUE;
110 if (!(entry = calloc( 1, sizeof(*entry) ))) return DIENUM_STOP;
112 if (IsEqualGUID( &info->guid, &GUID_RampForce ))
114 params.cbTypeSpecificParams = sizeof(ramp);
115 params.lpvTypeSpecificParams = &ramp;
116 params.dwFlags |= DIEP_TYPESPECIFICPARAMS;
118 else if (IsEqualGUID( &info->guid, &GUID_ConstantForce ))
120 params.cbTypeSpecificParams = sizeof(constant);
121 params.lpvTypeSpecificParams = &constant;
122 params.dwFlags |= DIEP_TYPESPECIFICPARAMS;
124 else if (IsEqualGUID( &info->guid, &GUID_Sine ) ||
125 IsEqualGUID( &info->guid, &GUID_Square ) ||
126 IsEqualGUID( &info->guid, &GUID_Triangle ) ||
127 IsEqualGUID( &info->guid, &GUID_SawtoothUp ) ||
128 IsEqualGUID( &info->guid, &GUID_SawtoothDown ))
130 params.cbTypeSpecificParams = sizeof(periodic);
131 params.lpvTypeSpecificParams = &periodic;
132 params.dwFlags |= DIEP_TYPESPECIFICPARAMS;
134 else if (IsEqualGUID( &info->guid, &GUID_Spring ) ||
135 IsEqualGUID( &info->guid, &GUID_Damper ) ||
136 IsEqualGUID( &info->guid, &GUID_Inertia ) ||
137 IsEqualGUID( &info->guid, &GUID_Friction ))
139 params.cbTypeSpecificParams = sizeof(condition);
140 params.lpvTypeSpecificParams = &condition;
141 params.dwFlags |= DIEP_TYPESPECIFICPARAMS;
144 do hr = IDirectInputDevice2_CreateEffect( device, &info->guid, &params, &effect, NULL );
145 while (FAILED(hr) && --params.cAxes);
147 if (FAILED(hr))
149 FIXME( "Failed to create effect with type %s, hr %#lx\n", debugstr_guid( &info->guid ), hr );
150 free( entry );
151 return DIENUM_CONTINUE;
154 entry->effect = effect;
155 list_add_tail( &effects, &entry->entry );
157 return DIENUM_CONTINUE;
160 static void set_selected_effect( IDirectInputEffect *effect )
162 IDirectInputEffect *previous;
164 EnterCriticalSection( &state_cs );
165 if ((previous = effect_selected)) IDirectInputEffect_Release( previous );
166 if ((effect_selected = effect)) IDirectInput_AddRef( effect );
167 LeaveCriticalSection( &state_cs );
170 static IDirectInputEffect *get_selected_effect(void)
172 IDirectInputEffect *effect;
174 EnterCriticalSection( &state_cs );
175 if ((effect = effect_selected)) IDirectInputEffect_AddRef( effect );
176 LeaveCriticalSection( &state_cs );
178 return effect;
181 static void clear_effects(void)
183 struct effect *effect, *next;
185 set_selected_effect( NULL );
187 LIST_FOR_EACH_ENTRY_SAFE( effect, next, &effects, struct effect, entry )
189 list_remove( &effect->entry );
190 IDirectInputEffect_Release( effect->effect );
191 free( effect );
195 static void set_selected_device( IDirectInputDevice8W *device )
197 IDirectInputDevice8W *previous;
199 EnterCriticalSection( &state_cs );
201 set_selected_effect( NULL );
203 if ((previous = device_selected))
205 IDirectInputDevice8_SetEventNotification( previous, NULL );
206 IDirectInputDevice8_Release( previous );
208 if ((device_selected = device))
210 IDirectInputDevice8_AddRef( device );
211 IDirectInputDevice8_SetEventNotification( device, state_event );
212 IDirectInputDevice8_Acquire( device );
215 LeaveCriticalSection( &state_cs );
218 static IDirectInputDevice8W *get_selected_device(void)
220 IDirectInputDevice8W *device;
222 EnterCriticalSection( &state_cs );
223 device = device_selected;
224 if (device) IDirectInputDevice8_AddRef( device );
225 LeaveCriticalSection( &state_cs );
227 return device;
230 static BOOL CALLBACK enum_devices( const DIDEVICEINSTANCEW *instance, void *context )
232 DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)};
233 IDirectInput8W *dinput = context;
234 struct device *entry;
236 if (!(entry = calloc( 1, sizeof(*entry) ))) return DIENUM_STOP;
238 IDirectInput8_CreateDevice( dinput, &instance->guidInstance, &entry->device, NULL );
239 IDirectInputDevice8_SetDataFormat( entry->device, &c_dfDIJoystick );
240 IDirectInputDevice8_GetCapabilities( entry->device, &caps );
242 list_add_tail( &devices, &entry->entry );
244 return DIENUM_CONTINUE;
247 static void clear_devices(void)
249 struct device *entry, *next;
251 set_selected_device( NULL );
253 LIST_FOR_EACH_ENTRY_SAFE( entry, next, &devices, struct device, entry )
255 list_remove( &entry->entry );
256 IDirectInputDevice8_Unacquire( entry->device );
257 IDirectInputDevice8_Release( entry->device );
258 free( entry );
262 static DWORD WINAPI input_thread( void *param )
264 HANDLE events[2] = {param, state_event};
266 while (WaitForMultipleObjects( 2, events, FALSE, INFINITE ) != 0)
268 IDirectInputEffect *effect;
269 DIJOYSTATE state = {0};
270 unsigned int i;
272 SendMessageW( dialog_hwnd, WM_USER, 0, 0 );
274 if ((effect = get_selected_effect()))
276 DWORD flags = DIEP_AXES | DIEP_DIRECTION | DIEP_NORESTART;
277 LONG direction[3] = {0};
278 DWORD axes[3] = {0};
279 DIEFFECT params =
281 .dwSize = sizeof(DIEFFECT),
282 .dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS,
283 .rglDirection = direction,
284 .rgdwAxes = axes,
285 .cAxes = 3,
288 IDirectInputEffect_GetParameters( effect, &params, flags );
289 params.rgdwAxes[0] = state.lX;
290 params.rgdwAxes[1] = state.lY;
292 for (i = 0; i < ARRAY_SIZE(state.rgbButtons); i++)
294 if (state.rgbButtons[i])
296 IDirectInputEffect_SetParameters( effect, &params, flags );
297 IDirectInputEffect_Start( effect, 1, 0 );
298 break;
302 IDirectInputEffect_Release( effect );
306 return 0;
309 static void draw_axis_view( HDC hdc, RECT rect, const WCHAR *name, LONG value )
311 POINT center =
313 .x = (rect.left + rect.right) / 2 + 10,
314 .y = (rect.top + rect.bottom) / 2,
316 LONG w = (rect.bottom - rect.top + 1) / 3;
317 LONG x = rect.left + 20 + (w + 1) / 2 + MulDiv( value, rect.right - rect.left - 20 - w, 0xffff );
318 COLORREF color;
319 HFONT font;
321 FillRect( hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1) );
323 color = SetTextColor( hdc, GetSysColor( COLOR_WINDOWTEXT ) );
324 font = SelectObject( hdc, GetStockObject( ANSI_VAR_FONT ) );
325 DrawTextW( hdc, name, -1, &rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOCLIP );
326 SetTextColor( hdc, color );
327 SelectObject( hdc, font );
329 SetDCBrushColor( hdc, GetSysColor( COLOR_WINDOW ) );
330 SetDCPenColor( hdc, GetSysColor( COLOR_WINDOWFRAME ) );
331 SelectObject( hdc, GetStockObject( DC_BRUSH ) );
332 SelectObject( hdc, GetStockObject( DC_PEN ) );
334 RoundRect( hdc, rect.left + 20, rect.top, rect.right, rect.bottom, 5, 5 );
336 if (x < center.x)
338 MoveToEx( hdc, center.x, center.y - 3, NULL );
339 LineTo( hdc, center.x, center.y + 3 );
342 SetDCBrushColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
343 SetDCPenColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
345 Rectangle( hdc, rect.left + 20, rect.top + w, x, rect.bottom - w );
347 if (x > center.x)
349 MoveToEx( hdc, center.x, center.y - 3, NULL );
350 LineTo( hdc, center.x, center.y + 3 );
354 static void draw_pov_view( HDC hdc, RECT rect, DWORD value )
356 POINT points[] =
358 /* 0° */
359 {.x = round( rect.left * 0.71 + rect.right * 0.29 ), .y = round( rect.top * 1.00 + rect.bottom * 0.00 )},
360 {.x = round( rect.left * 0.50 + rect.right * 0.50 ), .y = round( rect.top * 0.50 + rect.bottom * 0.50 )},
361 /* 45° */
362 {.x = round( rect.left * 0.29 + rect.right * 0.71 ), .y = round( rect.top * 1.00 + rect.bottom * 0.00 )},
363 {.x = round( rect.left * 0.50 + rect.right * 0.50 ), .y = round( rect.top * 0.50 + rect.bottom * 0.50 )},
364 /* 90° */
365 {.x = round( rect.left * 0.00 + rect.right * 1.00 ), .y = round( rect.top * 0.71 + rect.bottom * 0.29 )},
366 {.x = round( rect.left * 0.50 + rect.right * 0.50 ), .y = round( rect.top * 0.50 + rect.bottom * 0.50 )},
367 /* 135° */
368 {.x = round( rect.left * 0.00 + rect.right * 1.00 ), .y = round( rect.top * 0.29 + rect.bottom * 0.71 )},
369 {.x = round( rect.left * 0.50 + rect.right * 0.50 ), .y = round( rect.top * 0.50 + rect.bottom * 0.50 )},
370 /* 180° */
371 {.x = round( rect.left * 0.29 + rect.right * 0.71 ), .y = round( rect.top * 0.00 + rect.bottom * 1.00 )},
372 {.x = round( rect.left * 0.50 + rect.right * 0.50 ), .y = round( rect.top * 0.50 + rect.bottom * 0.50 )},
373 /* 225° */
374 {.x = round( rect.left * 0.71 + rect.right * 0.29 ), .y = round( rect.top * 0.00 + rect.bottom * 1.00 )},
375 {.x = round( rect.left * 0.50 + rect.right * 0.50 ), .y = round( rect.top * 0.50 + rect.bottom * 0.50 )},
376 /* 270° */
377 {.x = round( rect.left * 1.00 + rect.right * 0.00 ), .y = round( rect.top * 0.29 + rect.bottom * 0.71 )},
378 {.x = round( rect.left * 0.50 + rect.right * 0.50 ), .y = round( rect.top * 0.50 + rect.bottom * 0.50 )},
379 /* 315° */
380 {.x = round( rect.left * 1.00 + rect.right * 0.00 ), .y = round( rect.top * 0.71 + rect.bottom * 0.29 )},
381 {.x = round( rect.left * 0.50 + rect.right * 0.50 ), .y = round( rect.top * 0.50 + rect.bottom * 0.50 )},
382 /* 360° */
383 {.x = round( rect.left * 0.71 + rect.right * 0.29 ), .y = round( rect.top * 1.00 + rect.bottom * 0.00 )},
384 {.x = round( rect.left * 0.50 + rect.right * 0.50 ), .y = round( rect.top * 0.50 + rect.bottom * 0.50 )},
385 {.x = round( rect.left * 0.71 + rect.right * 0.29 ), .y = round( rect.top * 1.00 + rect.bottom * 0.00 )},
387 DWORD i;
389 FillRect( hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1) );
391 SetDCBrushColor( hdc, GetSysColor( COLOR_WINDOW ) );
392 SetDCPenColor( hdc, GetSysColor( COLOR_WINDOWFRAME ) );
393 SelectObject( hdc, GetStockObject( DC_BRUSH ) );
394 SelectObject( hdc, GetStockObject( DC_PEN ) );
396 for (i = 0; i < ARRAY_SIZE(points) - 1; i += 2)
398 MoveToEx( hdc, (points[i].x + points[i + 1].x) / 2, (points[i].y + points[i + 1].y) / 2, NULL );
399 LineTo( hdc, points[i].x, points[i].y );
402 SetDCPenColor( hdc, GetSysColor( (value != -1) ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWFRAME ) );
403 SetDCBrushColor( hdc, GetSysColor( (value != -1) ? COLOR_HIGHLIGHT : COLOR_WINDOW ) );
404 if (value != -1) Polygon( hdc, points + value / 4500 * 2, 3 );
407 static void draw_button_view( HDC hdc, RECT rect, BOOL set, const WCHAR *name )
409 COLORREF color;
410 HFONT font;
411 INT mode;
413 FillRect( hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1) );
415 SetDCBrushColor( hdc, GetSysColor( set ? COLOR_HIGHLIGHT : COLOR_WINDOW ) );
416 SetDCPenColor( hdc, GetSysColor( set ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWFRAME ) );
417 SelectObject( hdc, GetStockObject( DC_BRUSH ) );
418 SelectObject( hdc, GetStockObject( DC_PEN ) );
420 Ellipse( hdc, rect.left, rect.top, rect.right, rect.bottom );
422 color = SetTextColor( hdc, GetSysColor( set ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT ) );
423 font = SelectObject( hdc, GetStockObject( ANSI_VAR_FONT ) );
424 mode = SetBkMode( hdc, TRANSPARENT );
425 DrawTextW( hdc, name, -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOCLIP );
426 SetBkMode( hdc, mode );
427 SetTextColor( hdc, color );
428 SelectObject( hdc, font );
431 LRESULT CALLBACK test_di_axes_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
433 TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam );
435 if (msg == WM_PAINT)
437 DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)};
438 IDirectInputDevice8W *device;
439 DIJOYSTATE state = {0};
440 RECT rect, tmp_rect;
441 PAINTSTRUCT paint;
442 HDC hdc;
444 if ((device = get_selected_device()))
446 IDirectInputDevice8_GetDeviceState( device, sizeof(state), &state );
447 IDirectInputDevice8_GetCapabilities( device, &caps );
448 IDirectInputDevice8_Release( device );
451 hdc = BeginPaint( hwnd, &paint );
453 GetClientRect( hwnd, &rect );
454 rect.bottom = rect.top + (rect.bottom - rect.top - 2) / 4 - 2;
455 rect.right = rect.left + (rect.right - rect.left) / 2 - 10;
457 OffsetRect( &rect, 5, 2 );
458 draw_axis_view( hdc, rect, L"X", state.lX );
460 tmp_rect = rect;
461 OffsetRect( &rect, rect.right - rect.left + 10, 0 );
462 draw_axis_view( hdc, rect, L"Rx", state.lRx );
463 rect = tmp_rect;
465 OffsetRect( &rect, 0, rect.bottom - rect.top + 2 );
466 draw_axis_view( hdc, rect, L"Y", state.lY );
468 tmp_rect = rect;
469 OffsetRect( &rect, rect.right - rect.left + 10, 0 );
470 draw_axis_view( hdc, rect, L"Ry", state.lRy );
471 rect = tmp_rect;
473 OffsetRect( &rect, 0, rect.bottom - rect.top + 2 );
474 draw_axis_view( hdc, rect, L"Z", state.lZ );
476 tmp_rect = rect;
477 OffsetRect( &rect, rect.right - rect.left + 10, 0 );
478 draw_axis_view( hdc, rect, L"Rz", state.lRz );
479 rect = tmp_rect;
481 OffsetRect( &rect, 0, rect.bottom - rect.top + 2 );
482 draw_axis_view( hdc, rect, L"S", state.rglSlider[0] );
484 tmp_rect = rect;
485 OffsetRect( &rect, rect.right - rect.left + 10, 0 );
486 draw_axis_view( hdc, rect, L"Rs", state.rglSlider[1] );
487 rect = tmp_rect;
489 EndPaint( hwnd, &paint );
491 return 0;
494 return DefWindowProcW( hwnd, msg, wparam, lparam );
497 LRESULT CALLBACK test_di_povs_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
499 TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam );
501 if (msg == WM_PAINT)
503 DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)};
504 IDirectInputDevice8W *device;
505 DIJOYSTATE state = {0};
506 PAINTSTRUCT paint;
507 RECT rect;
508 HDC hdc;
510 if ((device = get_selected_device()))
512 IDirectInputDevice8_GetDeviceState( device, sizeof(state), &state );
513 IDirectInputDevice8_GetCapabilities( device, &caps );
514 IDirectInputDevice8_Release( device );
517 hdc = BeginPaint( hwnd, &paint );
519 GetClientRect( hwnd, &rect );
520 rect.bottom = rect.top + (rect.bottom - rect.top - 5) / 2 - 5;
521 rect.right = rect.left + (rect.bottom - rect.top);
523 OffsetRect( &rect, 5, 5 );
524 draw_pov_view( hdc, rect, state.rgdwPOV[0] );
525 OffsetRect( &rect, rect.right - rect.left + 5, 0 );
526 draw_pov_view( hdc, rect, state.rgdwPOV[1] );
527 OffsetRect( &rect, rect.left - rect.right - 5, rect.bottom - rect.top + 5 );
528 draw_pov_view( hdc, rect, state.rgdwPOV[1] );
529 OffsetRect( &rect, rect.right - rect.left + 5, 0 );
530 draw_pov_view( hdc, rect, state.rgdwPOV[2] );
532 EndPaint( hwnd, &paint );
534 return 0;
537 return DefWindowProcW( hwnd, msg, wparam, lparam );
540 LRESULT CALLBACK test_di_buttons_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
542 TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam );
544 if (msg == WM_PAINT)
546 DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)};
547 UINT i, j, offs, size, step, space = 2;
548 IDirectInputDevice8W *device;
549 DIJOYSTATE state = {0};
550 PAINTSTRUCT paint;
551 RECT rect;
552 HDC hdc;
554 if ((device = get_selected_device()))
556 IDirectInputDevice8_GetDeviceState( device, sizeof(state), &state );
557 IDirectInputDevice8_GetCapabilities( device, &caps );
558 IDirectInputDevice8_Release( device );
561 if (caps.dwButtons <= 48) step = 16;
562 else step = 32;
564 hdc = BeginPaint( hwnd, &paint );
566 GetClientRect( hwnd, &rect );
568 size = (rect.right - rect.left - space) / step;
569 offs = (rect.right - rect.left - step * size - space) / 2;
570 OffsetRect( &rect, offs, offs );
571 rect.right = rect.left + size - space;
572 rect.bottom = rect.top + size - space;
574 for (i = 0; i < ARRAY_SIZE(state.rgbButtons) && i < caps.dwButtons;)
576 RECT first = rect;
578 for (j = 0; j < step && i < caps.dwButtons; j++, i++)
580 WCHAR buffer[3];
581 swprintf( buffer, ARRAY_SIZE(buffer), L"%d", i );
582 draw_button_view( hdc, rect, state.rgbButtons[i], buffer );
583 OffsetRect( &rect, size, 0 );
586 rect = first;
587 OffsetRect( &rect, 0, size );
590 EndPaint( hwnd, &paint );
592 return 0;
595 return DefWindowProcW( hwnd, msg, wparam, lparam );
598 static void update_di_effects( HWND hwnd, IDirectInputDevice8W *device )
600 struct effect *effect;
602 clear_effects();
604 IDirectInputDevice8_EnumEffects( device, enum_effects, device, 0 );
606 SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_RESETCONTENT, 0, 0 );
607 SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_ADDSTRING, 0, (LPARAM)L"None" );
609 LIST_FOR_EACH_ENTRY( effect, &effects, struct effect, entry )
611 DIEFFECTINFOW info = {.dwSize = sizeof(DIEFFECTINFOW)};
612 GUID guid;
614 if (FAILED(IDirectInputEffect_GetEffectGuid( effect->effect, &guid ))) continue;
615 if (FAILED(IDirectInputDevice8_GetEffectInfo( device, &info, &guid ))) continue;
616 SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_ADDSTRING, 0, (LPARAM)( info.tszName + 5 ) );
620 static void handle_di_effects_change( HWND hwnd )
622 IDirectInputDevice8W *device;
623 struct list *entry;
624 int sel;
626 set_selected_effect( NULL );
628 sel = SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_GETCURSEL, 0, 0 ) - 1;
629 if (sel < 0) return;
631 entry = list_head( &effects );
632 while (sel-- && entry) entry = list_next( &effects, entry );
633 if (!entry) return;
635 set_selected_effect( LIST_ENTRY( entry, struct effect, entry )->effect );
637 if ((device = get_selected_device()))
639 IDirectInputDevice8_Unacquire( device );
640 IDirectInputDevice8_SetCooperativeLevel( device, GetAncestor( hwnd, GA_ROOT ), DISCL_BACKGROUND | DISCL_EXCLUSIVE );
641 IDirectInputDevice8_Acquire( device );
642 IDirectInputDevice8_Release( device );
646 static void create_device_views( HWND hwnd )
648 HINSTANCE instance = (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE );
649 HWND parent;
650 LONG margin;
651 RECT rect;
653 parent = GetDlgItem( hwnd, IDC_DI_AXES );
654 GetClientRect( parent, &rect );
655 rect.top += 10;
657 margin = (rect.bottom - rect.top) * 10 / 100;
658 InflateRect( &rect, -margin, -margin );
660 CreateWindowW( L"JoyCplDInputAxes", NULL, WS_CHILD | WS_VISIBLE, rect.left, rect.top,
661 rect.right - rect.left, rect.bottom - rect.top, parent, NULL, NULL, instance );
663 parent = GetDlgItem( hwnd, IDC_DI_POVS );
664 GetClientRect( parent, &rect );
665 rect.top += 10;
667 margin = (rect.bottom - rect.top) * 10 / 100;
668 InflateRect( &rect, -margin, -margin );
670 CreateWindowW( L"JoyCplDInputPOVs", NULL, WS_CHILD | WS_VISIBLE, rect.left, rect.top,
671 rect.right - rect.left, rect.bottom - rect.top, parent, NULL, NULL, instance );
673 parent = GetDlgItem( hwnd, IDC_DI_BUTTONS );
674 GetClientRect( parent, &rect );
675 rect.top += 10;
677 margin = (rect.bottom - rect.top) * 10 / 100;
678 InflateRect( &rect, -margin, -margin );
680 CreateWindowW( L"JoyCplDInputButtons", NULL, WS_CHILD | WS_VISIBLE, rect.left, rect.top,
681 rect.right - rect.left, rect.bottom - rect.top, parent, NULL, NULL, instance );
684 static void handle_di_devices_change( HWND hwnd )
686 DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)};
687 IDirectInputDevice8W *device;
688 struct list *entry;
689 int i;
691 set_selected_device( NULL );
693 i = SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_GETCURSEL, 0, 0 );
694 if (i < 0) return;
696 entry = list_head( &devices );
697 while (i-- && entry) entry = list_next( &devices, entry );
698 if (!entry) return;
700 device = LIST_ENTRY( entry, struct device, entry )->device;
701 if (FAILED(IDirectInputDevice8_GetCapabilities( device, &caps ))) return;
703 set_selected_device( device );
704 update_di_effects( hwnd, device );
707 static void update_di_devices( HWND hwnd )
709 IDirectInput8W *dinput;
710 struct device *entry;
712 clear_devices();
714 DirectInput8Create( GetModuleHandleW( NULL ), DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void **)&dinput, NULL );
715 IDirectInput8_EnumDevices( dinput, DI8DEVCLASS_GAMECTRL, enum_devices, dinput, DIEDFL_ATTACHEDONLY );
716 IDirectInput8_Release( dinput );
718 SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_RESETCONTENT, 0, 0 );
720 LIST_FOR_EACH_ENTRY( entry, &devices, struct device, entry )
722 DIDEVICEINSTANCEW info = {.dwSize = sizeof(DIDEVICEINSTANCEW)};
723 if (FAILED(IDirectInputDevice8_GetDeviceInfo( entry->device, &info ))) continue;
724 SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_ADDSTRING, 0, (LPARAM)info.tszInstanceName );
728 static void update_device_views( HWND hwnd )
730 HWND parent, view;
732 parent = GetDlgItem( hwnd, IDC_DI_AXES );
733 view = FindWindowExW( parent, NULL, L"JoyCplDInputAxes", NULL );
734 InvalidateRect( view, NULL, TRUE );
736 parent = GetDlgItem( hwnd, IDC_DI_POVS );
737 view = FindWindowExW( parent, NULL, L"JoyCplDInputPOVs", NULL );
738 InvalidateRect( view, NULL, TRUE );
740 parent = GetDlgItem( hwnd, IDC_DI_BUTTONS );
741 view = FindWindowExW( parent, NULL, L"JoyCplDInputButtons", NULL );
742 InvalidateRect( view, NULL, TRUE );
745 INT_PTR CALLBACK test_di_dialog_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
747 static HANDLE thread, thread_stop;
749 TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam );
751 switch (msg)
753 case WM_INITDIALOG:
754 create_device_views( hwnd );
755 return TRUE;
757 case WM_COMMAND:
758 switch (wparam)
760 case MAKEWPARAM( IDC_DI_DEVICES, CBN_SELCHANGE ):
761 handle_di_devices_change( hwnd );
763 SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_SETCURSEL, 0, 0 );
764 handle_di_effects_change( hwnd );
765 break;
767 case MAKEWPARAM( IDC_DI_EFFECTS, LBN_SELCHANGE ):
768 handle_di_effects_change( hwnd );
769 break;
771 return TRUE;
773 case WM_NOTIFY:
774 switch (((NMHDR *)lparam)->code)
776 case PSN_SETACTIVE:
777 dialog_hwnd = hwnd;
778 state_event = CreateEventW( NULL, FALSE, FALSE, NULL );
779 thread_stop = CreateEventW( NULL, FALSE, FALSE, NULL );
781 update_di_devices( hwnd );
783 SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_SETCURSEL, 0, 0 );
784 handle_di_devices_change( hwnd );
786 SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_SETCURSEL, 0, 0 );
787 handle_di_effects_change( hwnd );
789 thread = CreateThread( NULL, 0, input_thread, (void *)thread_stop, 0, NULL );
790 break;
792 case PSN_RESET:
793 case PSN_KILLACTIVE:
794 SetEvent( thread_stop );
795 MsgWaitForMultipleObjects( 1, &thread, FALSE, INFINITE, 0 );
796 CloseHandle( state_event );
797 CloseHandle( thread_stop );
798 CloseHandle( thread );
800 clear_effects();
801 clear_devices();
802 break;
804 return TRUE;
806 case WM_USER:
807 update_device_views( hwnd );
808 return TRUE;
810 return FALSE;