wineps.drv: Remove DECLSPEC_HIDDEN usage.
[wine.git] / dlls / joy.cpl / dinput.c
blobf624e650147e1a9bb2b165f7748ca7308a91fb1c
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_dfDIJoystick2 );
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 DIJOYSTATE2 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 if (rect.right - rect.left < 16) Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
421 else Ellipse( hdc, rect.left, rect.top, rect.right, rect.bottom );
423 color = SetTextColor( hdc, GetSysColor( set ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT ) );
424 font = SelectObject( hdc, GetStockObject( ANSI_VAR_FONT ) );
425 mode = SetBkMode( hdc, TRANSPARENT );
426 DrawTextW( hdc, name, -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOCLIP );
427 SetBkMode( hdc, mode );
428 SetTextColor( hdc, color );
429 SelectObject( hdc, font );
432 LRESULT CALLBACK test_di_axes_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
434 TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam );
436 if (msg == WM_PAINT)
438 DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)};
439 IDirectInputDevice8W *device;
440 DIJOYSTATE2 state = {0};
441 RECT rect, tmp_rect;
442 PAINTSTRUCT paint;
443 HDC hdc;
445 if ((device = get_selected_device()))
447 IDirectInputDevice8_GetDeviceState( device, sizeof(state), &state );
448 IDirectInputDevice8_GetCapabilities( device, &caps );
449 IDirectInputDevice8_Release( device );
452 hdc = BeginPaint( hwnd, &paint );
454 GetClientRect( hwnd, &rect );
455 rect.bottom = rect.top + (rect.bottom - rect.top - 2) / 4 - 2;
456 rect.right = rect.left + (rect.right - rect.left) / 2 - 10;
458 OffsetRect( &rect, 5, 2 );
459 draw_axis_view( hdc, rect, L"X", state.lX );
461 tmp_rect = rect;
462 OffsetRect( &rect, rect.right - rect.left + 10, 0 );
463 draw_axis_view( hdc, rect, L"Rx", state.lRx );
464 rect = tmp_rect;
466 OffsetRect( &rect, 0, rect.bottom - rect.top + 2 );
467 draw_axis_view( hdc, rect, L"Y", state.lY );
469 tmp_rect = rect;
470 OffsetRect( &rect, rect.right - rect.left + 10, 0 );
471 draw_axis_view( hdc, rect, L"Ry", state.lRy );
472 rect = tmp_rect;
474 OffsetRect( &rect, 0, rect.bottom - rect.top + 2 );
475 draw_axis_view( hdc, rect, L"Z", state.lZ );
477 tmp_rect = rect;
478 OffsetRect( &rect, rect.right - rect.left + 10, 0 );
479 draw_axis_view( hdc, rect, L"Rz", state.lRz );
480 rect = tmp_rect;
482 OffsetRect( &rect, 0, rect.bottom - rect.top + 2 );
483 draw_axis_view( hdc, rect, L"S", state.rglSlider[0] );
485 tmp_rect = rect;
486 OffsetRect( &rect, rect.right - rect.left + 10, 0 );
487 draw_axis_view( hdc, rect, L"Rs", state.rglSlider[1] );
488 rect = tmp_rect;
490 EndPaint( hwnd, &paint );
492 return 0;
495 return DefWindowProcW( hwnd, msg, wparam, lparam );
498 LRESULT CALLBACK test_di_povs_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
500 TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam );
502 if (msg == WM_PAINT)
504 DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)};
505 IDirectInputDevice8W *device;
506 DIJOYSTATE2 state = {0};
507 PAINTSTRUCT paint;
508 RECT rect;
509 HDC hdc;
511 if ((device = get_selected_device()))
513 IDirectInputDevice8_GetDeviceState( device, sizeof(state), &state );
514 IDirectInputDevice8_GetCapabilities( device, &caps );
515 IDirectInputDevice8_Release( device );
518 hdc = BeginPaint( hwnd, &paint );
520 GetClientRect( hwnd, &rect );
521 rect.bottom = rect.top + (rect.bottom - rect.top - 5) / 2 - 5;
522 rect.right = rect.left + (rect.bottom - rect.top);
524 OffsetRect( &rect, 5, 5 );
525 draw_pov_view( hdc, rect, state.rgdwPOV[0] );
526 OffsetRect( &rect, rect.right - rect.left + 5, 0 );
527 draw_pov_view( hdc, rect, state.rgdwPOV[1] );
528 OffsetRect( &rect, rect.left - rect.right - 5, rect.bottom - rect.top + 5 );
529 draw_pov_view( hdc, rect, state.rgdwPOV[1] );
530 OffsetRect( &rect, rect.right - rect.left + 5, 0 );
531 draw_pov_view( hdc, rect, state.rgdwPOV[2] );
533 EndPaint( hwnd, &paint );
535 return 0;
538 return DefWindowProcW( hwnd, msg, wparam, lparam );
541 LRESULT CALLBACK test_di_buttons_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
543 TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam );
545 if (msg == WM_PAINT)
547 DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)};
548 UINT i, j, offs, size, step, space = 2;
549 IDirectInputDevice8W *device;
550 DIJOYSTATE2 state = {0};
551 PAINTSTRUCT paint;
552 RECT rect;
553 HDC hdc;
555 if ((device = get_selected_device()))
557 IDirectInputDevice8_GetDeviceState( device, sizeof(state), &state );
558 IDirectInputDevice8_GetCapabilities( device, &caps );
559 IDirectInputDevice8_Release( device );
562 if (caps.dwButtons <= 48) step = 16;
563 else step = 24;
565 hdc = BeginPaint( hwnd, &paint );
567 GetClientRect( hwnd, &rect );
568 FillRect( hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1) );
570 size = (rect.right - rect.left - space) / step;
571 offs = (rect.right - rect.left - step * size - space) / 2;
572 OffsetRect( &rect, offs, offs );
573 rect.right = rect.left + size - space;
574 rect.bottom = rect.top + size - space;
576 for (i = 0; i < ARRAY_SIZE(state.rgbButtons) && i < caps.dwButtons;)
578 RECT first = rect;
580 for (j = 0; j < step && i < caps.dwButtons; j++, i++)
582 WCHAR buffer[3];
583 if (step == 24) swprintf( buffer, ARRAY_SIZE(buffer), L"%02x", i );
584 else swprintf( buffer, ARRAY_SIZE(buffer), L"%d", i );
585 draw_button_view( hdc, rect, state.rgbButtons[i], buffer );
586 OffsetRect( &rect, size, 0 );
589 rect = first;
590 OffsetRect( &rect, 0, size );
593 EndPaint( hwnd, &paint );
595 return 0;
598 return DefWindowProcW( hwnd, msg, wparam, lparam );
601 static void update_di_effects( HWND hwnd, IDirectInputDevice8W *device )
603 struct effect *effect;
605 clear_effects();
607 IDirectInputDevice8_EnumEffects( device, enum_effects, device, 0 );
609 SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_RESETCONTENT, 0, 0 );
610 SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_ADDSTRING, 0, (LPARAM)L"None" );
612 LIST_FOR_EACH_ENTRY( effect, &effects, struct effect, entry )
614 DIEFFECTINFOW info = {.dwSize = sizeof(DIEFFECTINFOW)};
615 GUID guid;
617 if (FAILED(IDirectInputEffect_GetEffectGuid( effect->effect, &guid ))) continue;
618 if (FAILED(IDirectInputDevice8_GetEffectInfo( device, &info, &guid ))) continue;
619 SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_ADDSTRING, 0, (LPARAM)( info.tszName + 5 ) );
623 static void handle_di_effects_change( HWND hwnd )
625 IDirectInputDevice8W *device;
626 struct list *entry;
627 int sel;
629 set_selected_effect( NULL );
631 sel = SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_GETCURSEL, 0, 0 ) - 1;
632 if (sel < 0) return;
634 entry = list_head( &effects );
635 while (sel-- && entry) entry = list_next( &effects, entry );
636 if (!entry) return;
638 set_selected_effect( LIST_ENTRY( entry, struct effect, entry )->effect );
640 if ((device = get_selected_device()))
642 IDirectInputDevice8_Unacquire( device );
643 IDirectInputDevice8_SetCooperativeLevel( device, GetAncestor( hwnd, GA_ROOT ), DISCL_BACKGROUND | DISCL_EXCLUSIVE );
644 IDirectInputDevice8_Acquire( device );
645 IDirectInputDevice8_Release( device );
649 static void create_device_views( HWND hwnd )
651 HINSTANCE instance = (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE );
652 HWND parent;
653 LONG margin;
654 RECT rect;
656 parent = GetDlgItem( hwnd, IDC_DI_AXES );
657 GetClientRect( parent, &rect );
658 rect.top += 10;
660 margin = (rect.bottom - rect.top) * 10 / 100;
661 InflateRect( &rect, -margin, -margin );
663 CreateWindowW( L"JoyCplDInputAxes", NULL, WS_CHILD | WS_VISIBLE, rect.left, rect.top,
664 rect.right - rect.left, rect.bottom - rect.top, parent, NULL, NULL, instance );
666 parent = GetDlgItem( hwnd, IDC_DI_POVS );
667 GetClientRect( parent, &rect );
668 rect.top += 10;
670 margin = (rect.bottom - rect.top) * 10 / 100;
671 InflateRect( &rect, -margin, -margin );
673 CreateWindowW( L"JoyCplDInputPOVs", NULL, WS_CHILD | WS_VISIBLE, rect.left, rect.top,
674 rect.right - rect.left, rect.bottom - rect.top, parent, NULL, NULL, instance );
676 parent = GetDlgItem( hwnd, IDC_DI_BUTTONS );
677 GetClientRect( parent, &rect );
678 rect.top += 10;
680 margin = (rect.bottom - rect.top) * 5 / 100;
681 InflateRect( &rect, -margin, -margin );
683 CreateWindowW( L"JoyCplDInputButtons", NULL, WS_CHILD | WS_VISIBLE, rect.left, rect.top,
684 rect.right - rect.left, rect.bottom - rect.top, parent, NULL, NULL, instance );
687 static void handle_di_devices_change( HWND hwnd )
689 DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)};
690 IDirectInputDevice8W *device;
691 struct list *entry;
692 int i;
694 set_selected_device( NULL );
696 i = SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_GETCURSEL, 0, 0 );
697 if (i < 0) return;
699 entry = list_head( &devices );
700 while (i-- && entry) entry = list_next( &devices, entry );
701 if (!entry) return;
703 device = LIST_ENTRY( entry, struct device, entry )->device;
704 if (FAILED(IDirectInputDevice8_GetCapabilities( device, &caps ))) return;
706 set_selected_device( device );
707 update_di_effects( hwnd, device );
710 static void update_di_devices( HWND hwnd )
712 IDirectInput8W *dinput;
713 struct device *entry;
715 clear_devices();
717 DirectInput8Create( GetModuleHandleW( NULL ), DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void **)&dinput, NULL );
718 IDirectInput8_EnumDevices( dinput, DI8DEVCLASS_GAMECTRL, enum_devices, dinput, DIEDFL_ATTACHEDONLY );
719 IDirectInput8_Release( dinput );
721 SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_RESETCONTENT, 0, 0 );
723 LIST_FOR_EACH_ENTRY( entry, &devices, struct device, entry )
725 DIDEVICEINSTANCEW info = {.dwSize = sizeof(DIDEVICEINSTANCEW)};
726 if (FAILED(IDirectInputDevice8_GetDeviceInfo( entry->device, &info ))) continue;
727 SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_ADDSTRING, 0, (LPARAM)info.tszInstanceName );
731 static void update_device_views( HWND hwnd )
733 HWND parent, view;
735 parent = GetDlgItem( hwnd, IDC_DI_AXES );
736 view = FindWindowExW( parent, NULL, L"JoyCplDInputAxes", NULL );
737 InvalidateRect( view, NULL, TRUE );
739 parent = GetDlgItem( hwnd, IDC_DI_POVS );
740 view = FindWindowExW( parent, NULL, L"JoyCplDInputPOVs", NULL );
741 InvalidateRect( view, NULL, TRUE );
743 parent = GetDlgItem( hwnd, IDC_DI_BUTTONS );
744 view = FindWindowExW( parent, NULL, L"JoyCplDInputButtons", NULL );
745 InvalidateRect( view, NULL, TRUE );
748 INT_PTR CALLBACK test_di_dialog_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
750 static HANDLE thread, thread_stop;
752 TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam );
754 switch (msg)
756 case WM_INITDIALOG:
757 create_device_views( hwnd );
758 return TRUE;
760 case WM_COMMAND:
761 switch (wparam)
763 case MAKEWPARAM( IDC_DI_DEVICES, CBN_SELCHANGE ):
764 handle_di_devices_change( hwnd );
766 SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_SETCURSEL, 0, 0 );
767 handle_di_effects_change( hwnd );
769 update_device_views( hwnd );
770 break;
772 case MAKEWPARAM( IDC_DI_EFFECTS, LBN_SELCHANGE ):
773 handle_di_effects_change( hwnd );
774 break;
776 return TRUE;
778 case WM_NOTIFY:
779 switch (((NMHDR *)lparam)->code)
781 case PSN_SETACTIVE:
782 dialog_hwnd = hwnd;
783 state_event = CreateEventW( NULL, FALSE, FALSE, NULL );
784 thread_stop = CreateEventW( NULL, FALSE, FALSE, NULL );
786 update_di_devices( hwnd );
788 SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_SETCURSEL, 0, 0 );
789 handle_di_devices_change( hwnd );
791 SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_SETCURSEL, 0, 0 );
792 handle_di_effects_change( hwnd );
794 thread = CreateThread( NULL, 0, input_thread, (void *)thread_stop, 0, NULL );
795 break;
797 case PSN_RESET:
798 case PSN_KILLACTIVE:
799 SetEvent( thread_stop );
800 /* wait for the input thread to stop, processing any WM_USER message from it */
801 while (MsgWaitForMultipleObjects( 1, &thread, FALSE, INFINITE, QS_ALLINPUT ) == 1)
803 MSG msg;
804 while (PeekMessageW( &msg, 0, 0, 0, PM_REMOVE ))
806 TranslateMessage( &msg );
807 DispatchMessageW( &msg );
810 CloseHandle( state_event );
811 CloseHandle( thread_stop );
812 CloseHandle( thread );
814 clear_effects();
815 clear_devices();
816 break;
818 return TRUE;
820 case WM_USER:
821 update_device_views( hwnd );
822 return TRUE;
824 return FALSE;