2 * Joystick testing control panel applet
4 * Copyright 2012 Lucas Fialho Zawacki
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #define NONAMELESSUNION
34 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(joycpl
);
39 DECLSPEC_HIDDEN HMODULE hcpl
;
41 static const WCHAR button_class
[] = {'B','u','t','t','o','n','\0'};
43 /*********************************************************************
46 BOOL WINAPI
DllMain(HINSTANCE hdll
, DWORD reason
, LPVOID reserved
)
48 TRACE("(%p, %d, %p)\n", hdll
, reason
, reserved
);
52 case DLL_WINE_PREATTACH
:
53 return FALSE
; /* prefer native version */
55 case DLL_PROCESS_ATTACH
:
56 DisableThreadLibraryCalls(hdll
);
62 /***********************************************************************
63 * enum_callback [internal]
64 * Enumerates, creates and sets the common data format for all the joystick devices.
65 * First time it checks if space for the joysticks was already reserved
66 * and if not, just counts how many there are.
68 static BOOL CALLBACK
enum_callback(const DIDEVICEINSTANCEW
*instance
, void *context
)
70 struct JoystickData
*data
= context
;
71 struct Joystick
*joystick
;
72 DIPROPRANGE proprange
;
75 if (data
->joysticks
== NULL
)
77 data
->num_joysticks
+= 1;
78 return DIENUM_CONTINUE
;
81 joystick
= &data
->joysticks
[data
->cur_joystick
];
82 data
->cur_joystick
+= 1;
84 IDirectInput8_CreateDevice(data
->di
, &instance
->guidInstance
, &joystick
->device
, NULL
);
85 IDirectInputDevice8_SetDataFormat(joystick
->device
, &c_dfDIJoystick
);
87 joystick
->instance
= *instance
;
89 caps
.dwSize
= sizeof(caps
);
90 IDirectInputDevice8_GetCapabilities(joystick
->device
, &caps
);
92 joystick
->num_buttons
= caps
.dwButtons
;
93 joystick
->num_axes
= caps
.dwAxes
;
94 joystick
->forcefeedback
= caps
.dwFlags
& DIDC_FORCEFEEDBACK
;
95 joystick
->num_effects
= 0;
97 if (joystick
->forcefeedback
) data
->num_ff
++;
99 /* Set axis range to ease the GUI visualization */
100 proprange
.diph
.dwSize
= sizeof(DIPROPRANGE
);
101 proprange
.diph
.dwHeaderSize
= sizeof(DIPROPHEADER
);
102 proprange
.diph
.dwHow
= DIPH_DEVICE
;
103 proprange
.diph
.dwObj
= 0;
104 proprange
.lMin
= TEST_AXIS_MIN
;
105 proprange
.lMax
= TEST_AXIS_MAX
;
107 IDirectInputDevice_SetProperty(joystick
->device
, DIPROP_RANGE
, &proprange
.diph
);
109 return DIENUM_CONTINUE
;
112 /***********************************************************************
113 * initialize_joysticks [internal]
115 static void initialize_joysticks(struct JoystickData
*data
)
117 data
->num_joysticks
= 0;
118 data
->cur_joystick
= 0;
119 IDirectInput8_EnumDevices(data
->di
, DI8DEVCLASS_GAMECTRL
, enum_callback
, data
, DIEDFL_ATTACHEDONLY
);
120 data
->joysticks
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct Joystick
) * data
->num_joysticks
);
122 /* Get all the joysticks */
123 IDirectInput8_EnumDevices(data
->di
, DI8DEVCLASS_GAMECTRL
, enum_callback
, data
, DIEDFL_ATTACHEDONLY
);
126 /***********************************************************************
127 * destroy_joysticks [internal]
129 static void destroy_joysticks(struct JoystickData
*data
)
133 for (i
= 0; i
< data
->num_joysticks
; i
++)
136 if (data
->joysticks
[i
].forcefeedback
&& data
->joysticks
[i
].num_effects
> 0)
138 for (j
= 0; j
< data
->joysticks
[i
].num_effects
; j
++)
139 if (data
->joysticks
[i
].effects
[j
].effect
)
140 IDirectInputEffect_Release(data
->joysticks
[i
].effects
[j
].effect
);
142 HeapFree(GetProcessHeap(), 0, data
->joysticks
[i
].effects
);
145 IDirectInputDevice8_Unacquire(data
->joysticks
[i
].device
);
146 IDirectInputDevice8_Release(data
->joysticks
[i
].device
);
149 HeapFree(GetProcessHeap(), 0, data
->joysticks
);
152 static void initialize_joysticks_list(HWND hwnd
, struct JoystickData
*data
)
156 SendDlgItemMessageW(hwnd
, IDC_JOYSTICKLIST
, LB_RESETCONTENT
, 0, 0);
158 /* Add enumerated joysticks */
159 for (i
= 0; i
< data
->num_joysticks
; i
++)
161 struct Joystick
*joy
= &data
->joysticks
[i
];
162 SendDlgItemMessageW(hwnd
, IDC_JOYSTICKLIST
, LB_ADDSTRING
, 0, (LPARAM
) joy
->instance
.tszInstanceName
);
166 /******************************************************************************
167 * get_app_key [internal]
168 * Get the default DirectInput key and the selected app config key.
170 static BOOL
get_app_key(HKEY
*defkey
, HKEY
*appkey
)
172 static const WCHAR reg_key
[] = { 'S','o','f','t','w','a','r','e','\\',
173 'W','i','n','e','\\',
174 'D','i','r','e','c','t','I','n','p','u','t','\\',
175 'J','o','y','s','t','i','c','k','s','\0' };
178 /* Registry key can be found in HKCU\Software\Wine\DirectInput */
179 if (RegCreateKeyExW(HKEY_CURRENT_USER
, reg_key
, 0, NULL
, 0, KEY_SET_VALUE
| KEY_READ
, NULL
, defkey
, NULL
))
182 return *defkey
|| *appkey
;
185 /******************************************************************************
186 * set_config_key [internal]
187 * Writes a string value to a registry key, deletes the key if value == NULL
189 static DWORD
set_config_key(HKEY defkey
, HKEY appkey
, const WCHAR
*name
, const WCHAR
*value
, DWORD size
)
193 if (appkey
&& !RegDeleteValueW(appkey
, name
))
196 if (defkey
&& !RegDeleteValueW(defkey
, name
))
201 if (appkey
&& !RegSetValueExW(appkey
, name
, 0, REG_SZ
, (const BYTE
*) value
, (size
+ 1)*sizeof(WCHAR
)))
204 if (defkey
&& !RegSetValueExW(defkey
, name
, 0, REG_SZ
, (const BYTE
*) value
, (size
+ 1)*sizeof(WCHAR
)))
208 return ERROR_FILE_NOT_FOUND
;
211 /******************************************************************************
212 * enable_joystick [internal]
213 * Writes to the DirectInput registry key that enables/disables a joystick
214 * from being enumerated.
216 static void enable_joystick(WCHAR
*joy_name
, BOOL enable
)
218 static const WCHAR disabled_str
[] = {'d','i','s','a','b','l','e','d','\0'};
221 get_app_key(&hkey
, &appkey
);
224 set_config_key(hkey
, appkey
, joy_name
, disabled_str
, lstrlenW(disabled_str
));
226 set_config_key(hkey
, appkey
, joy_name
, NULL
, 0);
228 if (hkey
) RegCloseKey(hkey
);
229 if (appkey
) RegCloseKey(appkey
);
232 static void initialize_disabled_joysticks_list(HWND hwnd
)
234 static const WCHAR disabled_str
[] = {'d','i','s','a','b','l','e','d','\0'};
240 SendDlgItemMessageW(hwnd
, IDC_DISABLEDLIST
, LB_RESETCONTENT
, 0, 0);
242 /* Search for disabled joysticks */
243 get_app_key(&hkey
, &appkey
);
244 RegQueryInfoKeyW(hkey
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &values
, NULL
, NULL
, NULL
, NULL
);
246 for (i
=0; i
< values
; i
++)
248 DWORD name_len
= MAX_PATH
, data_len
= MAX_PATH
;
249 WCHAR buf_name
[MAX_PATH
+ 9], buf_data
[MAX_PATH
];
251 status
= RegEnumValueW(hkey
, i
, buf_name
, &name_len
, NULL
, NULL
, (BYTE
*) buf_data
, &data_len
);
253 if (status
== ERROR_SUCCESS
&& !lstrcmpW(disabled_str
, buf_data
))
254 SendDlgItemMessageW(hwnd
, IDC_DISABLEDLIST
, LB_ADDSTRING
, 0, (LPARAM
) buf_name
);
257 if (hkey
) RegCloseKey(hkey
);
258 if (appkey
) RegCloseKey(appkey
);
261 /*********************************************************************
262 * list_dlgproc [internal]
265 static INT_PTR CALLBACK
list_dlgproc(HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
267 static struct JoystickData
*data
;
268 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd
, msg
, msg
, lparam
);
273 data
= (struct JoystickData
*) ((PROPSHEETPAGEW
*)lparam
)->lParam
;
275 initialize_joysticks_list(hwnd
, data
);
276 initialize_disabled_joysticks_list(hwnd
);
278 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONENABLE
), FALSE
);
279 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONDISABLE
), FALSE
);
281 /* Store the hwnd to be used with MapDialogRect for unit conversions */
282 data
->graphics
.hwnd
= hwnd
;
289 switch (LOWORD(wparam
))
291 case IDC_BUTTONDISABLE
:
293 int sel
= SendDlgItemMessageW(hwnd
, IDC_JOYSTICKLIST
, LB_GETCURSEL
, 0, 0);
297 enable_joystick(data
->joysticks
[sel
].instance
.tszInstanceName
, FALSE
);
298 initialize_disabled_joysticks_list(hwnd
);
303 case IDC_BUTTONENABLE
:
305 int sel
= SendDlgItemMessageW(hwnd
, IDC_DISABLEDLIST
, LB_GETCURSEL
, 0, 0);
309 WCHAR text
[MAX_PATH
];
310 SendDlgItemMessageW(hwnd
, IDC_DISABLEDLIST
, LB_GETTEXT
, sel
, (LPARAM
) text
);
311 enable_joystick(text
, TRUE
);
312 initialize_disabled_joysticks_list(hwnd
);
317 case IDC_JOYSTICKLIST
:
318 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONENABLE
), FALSE
);
319 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONDISABLE
), TRUE
);
322 case IDC_DISABLEDLIST
:
323 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONENABLE
), TRUE
);
324 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONDISABLE
), FALSE
);
339 /*********************************************************************
340 * Joystick testing functions
343 static void dump_joy_state(DIJOYSTATE
* st
, int num_buttons
)
346 TRACE("Ax (% 5d,% 5d,% 5d)\n", st
->lX
,st
->lY
, st
->lZ
);
347 TRACE("RAx (% 5d,% 5d,% 5d)\n", st
->lRx
, st
->lRy
, st
->lRz
);
348 TRACE("Slider (% 5d,% 5d)\n", st
->rglSlider
[0], st
->rglSlider
[1]);
349 TRACE("Pov (% 5d,% 5d,% 5d,% 5d)\n", st
->rgdwPOV
[0], st
->rgdwPOV
[1], st
->rgdwPOV
[2], st
->rgdwPOV
[3]);
352 for(i
=0; i
< num_buttons
; i
++)
353 TRACE(" %c",st
->rgbButtons
[i
] ? 'x' : 'o');
357 static void poll_input(const struct Joystick
*joy
, DIJOYSTATE
*state
)
361 hr
= IDirectInputDevice8_Poll(joy
->device
);
363 /* If it failed, try to acquire the joystick */
366 hr
= IDirectInputDevice8_Acquire(joy
->device
);
367 while (hr
== DIERR_INPUTLOST
) hr
= IDirectInputDevice8_Acquire(joy
->device
);
370 if (hr
== DIERR_OTHERAPPHASPRIO
) return;
372 IDirectInputDevice8_GetDeviceState(joy
->device
, sizeof(DIJOYSTATE
), state
);
375 static DWORD WINAPI
input_thread(void *param
)
377 int axes_pos
[TEST_MAX_AXES
][2];
379 struct JoystickData
*data
= param
;
381 /* Setup POV as clock positions
388 int ma
= TEST_AXIS_MAX
;
389 int pov_val
[9] = {0, 4500, 9000, 13500,
390 18000, 22500, 27000, 31500, -1};
391 int pov_pos
[9][2] = { {0, -ma
}, {ma
/2, -ma
/2}, {ma
, 0}, {ma
/2, ma
/2},
392 {0, ma
}, {-ma
/2, ma
/2}, {-ma
, 0}, {-ma
/2, -ma
/2}, {0, 0} };
394 ZeroMemory(&state
, sizeof(state
));
401 poll_input(&data
->joysticks
[data
->chosen_joystick
], &state
);
403 dump_joy_state(&state
, data
->joysticks
[data
->chosen_joystick
].num_buttons
);
405 /* Indicate pressed buttons */
406 for (i
= 0; i
< data
->joysticks
[data
->chosen_joystick
].num_buttons
; i
++)
407 SendMessageW(data
->graphics
.buttons
[i
], BM_SETSTATE
, !!state
.rgbButtons
[i
], 0);
409 /* Indicate axis positions, axes showing are hardcoded for now */
410 axes_pos
[0][0] = state
.lX
;
411 axes_pos
[0][1] = state
.lY
;
412 axes_pos
[1][0] = state
.lRx
;
413 axes_pos
[1][1] = state
.lRy
;
414 axes_pos
[2][0] = state
.lZ
;
415 axes_pos
[2][1] = state
.lRz
;
418 for (j
= 0; j
< ARRAY_SIZE(pov_val
); j
++)
420 if (state
.rgdwPOV
[0] == pov_val
[j
])
422 axes_pos
[3][0] = pov_pos
[j
][0];
423 axes_pos
[3][1] = pov_pos
[j
][1];
427 for (i
= 0; i
< TEST_MAX_AXES
; i
++)
431 r
.left
= (TEST_AXIS_X
+ TEST_NEXT_AXIS_X
*i
+ axes_pos
[i
][0]);
432 r
.top
= (TEST_AXIS_Y
+ axes_pos
[i
][1]);
433 r
.bottom
= r
.right
= 0; /* unused */
434 MapDialogRect(data
->graphics
.hwnd
, &r
);
436 SetWindowPos(data
->graphics
.axes
[i
], 0, r
.left
, r
.top
, 0, 0, SWP_NOZORDER
| SWP_NOSIZE
);
439 Sleep(TEST_POLL_TIME
);
445 static void test_handle_joychange(HWND hwnd
, struct JoystickData
*data
)
449 if (data
->num_joysticks
== 0) return;
451 data
->chosen_joystick
= SendDlgItemMessageW(hwnd
, IDC_TESTSELECTCOMBO
, CB_GETCURSEL
, 0, 0);
453 /* Enable only buttons present in the device */
454 for (i
= 0; i
< TEST_MAX_BUTTONS
; i
++)
455 ShowWindow(data
->graphics
.buttons
[i
], i
< data
->joysticks
[data
->chosen_joystick
].num_buttons
);
458 /*********************************************************************
459 * button_number_to_wchar [internal]
460 * Transforms an integer in the interval [0,99] into a 2 character WCHAR string
462 static void button_number_to_wchar(int n
, WCHAR str
[3])
464 str
[1] = n
% 10 + '0';
466 str
[0] = n
% 10 + '0';
470 static void draw_joystick_buttons(HWND hwnd
, struct JoystickData
* data
)
473 int row
= 0, col
= 0;
474 WCHAR button_label
[3];
475 HINSTANCE hinst
= (HINSTANCE
) GetWindowLongPtrW(hwnd
, GWLP_HINSTANCE
);
477 for (i
= 0; i
< TEST_MAX_BUTTONS
; i
++)
481 if ((i
% TEST_BUTTON_COL_MAX
) == 0 && i
!= 0)
487 r
.left
= (TEST_BUTTON_X
+ TEST_NEXT_BUTTON_X
*col
);
488 r
.top
= (TEST_BUTTON_Y
+ TEST_NEXT_BUTTON_Y
*row
);
489 r
.right
= r
.left
+ TEST_BUTTON_SIZE_X
;
490 r
.bottom
= r
.top
+ TEST_BUTTON_SIZE_Y
;
491 MapDialogRect(hwnd
, &r
);
493 button_number_to_wchar(i
+ 1, button_label
);
495 data
->graphics
.buttons
[i
] = CreateWindowW(button_class
, button_label
, WS_CHILD
,
496 r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
,
497 hwnd
, NULL
, NULL
, hinst
);
503 static void draw_joystick_axes(HWND hwnd
, struct JoystickData
* data
)
506 HINSTANCE hinst
= (HINSTANCE
) GetWindowLongPtrW(hwnd
, GWLP_HINSTANCE
);
507 static const WCHAR axes_names
[TEST_MAX_AXES
][7] = { {'X',',','Y','\0'}, {'R','x',',','R','y','\0'},
508 {'Z',',','R','z','\0'}, {'P','O','V','\0'} };
509 static const DWORD axes_idc
[TEST_MAX_AXES
] = { IDC_TESTGROUPXY
, IDC_TESTGROUPRXRY
,
510 IDC_TESTGROUPZRZ
, IDC_TESTGROUPPOV
};
512 for (i
= 0; i
< TEST_MAX_AXES
; i
++)
515 /* Set axis box name */
516 SetWindowTextW(GetDlgItem(hwnd
, axes_idc
[i
]), axes_names
[i
]);
518 r
.left
= (TEST_AXIS_X
+ TEST_NEXT_AXIS_X
*i
);
520 r
.right
= r
.left
+ TEST_AXIS_SIZE_X
;
521 r
.bottom
= r
.top
+ TEST_AXIS_SIZE_Y
;
522 MapDialogRect(hwnd
, &r
);
524 data
->graphics
.axes
[i
] = CreateWindowW( button_class
, NULL
, WS_CHILD
| WS_VISIBLE
,
525 r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
,
526 hwnd
, NULL
, NULL
, hinst
);
530 /*********************************************************************
531 * test_dlgproc [internal]
534 static INT_PTR CALLBACK
test_dlgproc(HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
536 static HANDLE thread
;
537 static struct JoystickData
*data
;
538 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd
, msg
, msg
, lparam
);
546 data
= (struct JoystickData
*) ((PROPSHEETPAGEW
*)lparam
)->lParam
;
548 /* Add enumerated joysticks to the combobox */
549 for (i
= 0; i
< data
->num_joysticks
; i
++)
551 struct Joystick
*joy
= &data
->joysticks
[i
];
552 SendDlgItemMessageW(hwnd
, IDC_TESTSELECTCOMBO
, CB_ADDSTRING
, 0, (LPARAM
) joy
->instance
.tszInstanceName
);
555 draw_joystick_buttons(hwnd
, data
);
556 draw_joystick_axes(hwnd
, data
);
564 case MAKEWPARAM(IDC_TESTSELECTCOMBO
, CBN_SELCHANGE
):
565 test_handle_joychange(hwnd
, data
);
571 switch(((LPNMHDR
)lparam
)->code
)
577 /* Initialize input thread */
578 if (data
->num_joysticks
> 0)
582 /* Set the first joystick as default */
583 SendDlgItemMessageW(hwnd
, IDC_TESTSELECTCOMBO
, CB_SETCURSEL
, 0, 0);
584 test_handle_joychange(hwnd
, data
);
586 thread
= CreateThread(NULL
, 0, input_thread
, (void*) data
, 0, &tid
);
591 case PSN_RESET
: /* intentional fall-through */
593 /* Stop input thread */
595 MsgWaitForMultipleObjects(1, &thread
, FALSE
, INFINITE
, 0);
604 /*********************************************************************
605 * Joystick force feedback testing functions
608 static void draw_ff_axis(HWND hwnd
, struct JoystickData
*data
)
610 HINSTANCE hinst
= (HINSTANCE
) GetWindowLongPtrW(hwnd
, GWLP_HINSTANCE
);
615 r
.right
= r
.left
+ FF_AXIS_SIZE_X
;
616 r
.bottom
= r
.top
+ FF_AXIS_SIZE_Y
;
617 MapDialogRect(hwnd
, &r
);
619 /* Draw direction axis */
620 data
->graphics
.ff_axis
= CreateWindowW( button_class
, NULL
, WS_CHILD
| WS_VISIBLE
,
621 r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
,
622 hwnd
, NULL
, NULL
, hinst
);
625 static void initialize_effects_list(HWND hwnd
, struct Joystick
* joy
)
629 SendDlgItemMessageW(hwnd
, IDC_FFEFFECTLIST
, LB_RESETCONTENT
, 0, 0);
631 for (i
=0; i
< joy
->num_effects
; i
++)
633 /* Effect names start with GUID_, so we'll skip this part */
634 WCHAR
*name
= joy
->effects
[i
].info
.tszName
+ 5;
635 SendDlgItemMessageW(hwnd
, IDC_FFEFFECTLIST
, LB_ADDSTRING
, 0, (LPARAM
) name
);
639 static void ff_handle_joychange(HWND hwnd
, struct JoystickData
*data
)
643 if (data
->num_ff
== 0) return;
645 sel
= SendDlgItemMessageW(hwnd
, IDC_FFSELECTCOMBO
, CB_GETCURSEL
, 0, 0);
646 data
->chosen_joystick
= SendDlgItemMessageW(hwnd
, IDC_FFSELECTCOMBO
, CB_GETITEMDATA
, sel
, 0);
647 initialize_effects_list(hwnd
, &data
->joysticks
[data
->chosen_joystick
]);
650 static void ff_handle_effectchange(HWND hwnd
, struct Joystick
*joy
)
652 int sel
= SendDlgItemMessageW(hwnd
, IDC_FFEFFECTLIST
, LB_GETCURSEL
, 0, 0);
656 joy
->chosen_effect
= sel
;
659 static DWORD WINAPI
ff_input_thread(void *param
)
661 struct JoystickData
*data
= param
;
664 ZeroMemory(&state
, sizeof(state
));
669 struct Joystick
*joy
= &data
->joysticks
[data
->chosen_joystick
];
670 int chosen_effect
= joy
->chosen_effect
;
672 DWORD flags
= DIEP_AXES
| DIEP_DIRECTION
| DIEP_NORESTART
;
675 Sleep(TEST_POLL_TIME
);
677 /* Skip this if we have no effects */
678 if (joy
->num_effects
== 0 || chosen_effect
< 0) continue;
680 poll_input(joy
, &state
);
682 /* Set ff parameters and draw the axis */
683 dieffect
= &joy
->effects
[chosen_effect
].params
;
684 dieffect
->rgdwAxes
[0] = state
.lX
;
685 dieffect
->rgdwAxes
[1] = state
.lY
;
687 r
.left
= FF_AXIS_X
+ state
.lX
;
688 r
.top
= FF_AXIS_Y
+ state
.lY
;
689 r
.right
= r
.bottom
= 0; /* unused */
690 MapDialogRect(data
->graphics
.hwnd
, &r
);
692 SetWindowPos(data
->graphics
.ff_axis
, 0, r
.left
, r
.top
, 0, 0, SWP_NOZORDER
| SWP_NOSIZE
);
694 for (i
=0; i
< joy
->num_buttons
; i
++)
695 if (state
.rgbButtons
[i
])
697 IDirectInputEffect_SetParameters(joy
->effects
[chosen_effect
].effect
, dieffect
, flags
);
698 IDirectInputEffect_Start(joy
->effects
[chosen_effect
].effect
, 1, 0);
706 /***********************************************************************
707 * ff_effects_callback [internal]
708 * Enumerates, creates, sets the some parameters and stores all ff effects
709 * supported by the joystick. Works like enum_callback, counting the effects
710 * first and then storing them.
712 static BOOL CALLBACK
ff_effects_callback(const DIEFFECTINFOW
*pdei
, void *pvRef
)
716 DWORD axes
[2] = {DIJOFS_X
, DIJOFS_Y
};
717 int direction
[2] = {0, 0};
718 struct Joystick
*joystick
= pvRef
;
720 DICONSTANTFORCE cforce
;
724 if (joystick
->effects
== NULL
)
726 joystick
->num_effects
+= 1;
727 return DIENUM_CONTINUE
;
730 hr
= IDirectInputDevice8_Acquire(joystick
->device
);
732 if (FAILED(hr
)) return DIENUM_CONTINUE
;
734 ZeroMemory(&dieffect
, sizeof(dieffect
));
736 dieffect
.dwSize
= sizeof(dieffect
);
737 dieffect
.dwFlags
= DIEFF_CARTESIAN
;
738 dieffect
.dwDuration
= FF_PLAY_TIME
;
741 dieffect
.rgdwAxes
= axes
;
742 dieffect
.rglDirection
= direction
;
744 if (IsEqualGUID(&pdei
->guid
, &GUID_RampForce
))
747 rforce
.lEnd
= DI_FFNOMINALMAX
;
749 dieffect
.cbTypeSpecificParams
= sizeof(rforce
);
750 dieffect
.lpvTypeSpecificParams
= &rforce
;
751 dieffect
.dwFlags
|= DIEP_TYPESPECIFICPARAMS
;
753 else if (IsEqualGUID(&pdei
->guid
, &GUID_ConstantForce
))
755 cforce
.lMagnitude
= DI_FFNOMINALMAX
;
757 dieffect
.cbTypeSpecificParams
= sizeof(cforce
);
758 dieffect
.lpvTypeSpecificParams
= &cforce
;
759 dieffect
.dwFlags
|= DIEP_TYPESPECIFICPARAMS
;
761 else if (IsEqualGUID(&pdei
->guid
, &GUID_Sine
) ||
762 IsEqualGUID(&pdei
->guid
, &GUID_Square
) ||
763 IsEqualGUID(&pdei
->guid
, &GUID_Triangle
) ||
764 IsEqualGUID(&pdei
->guid
, &GUID_SawtoothUp
) ||
765 IsEqualGUID(&pdei
->guid
, &GUID_SawtoothDown
))
767 pforce
.dwMagnitude
= DI_FFNOMINALMAX
;
770 pforce
.dwPeriod
= FF_PERIOD_TIME
;
772 dieffect
.cbTypeSpecificParams
= sizeof(pforce
);
773 dieffect
.lpvTypeSpecificParams
= &pforce
;
774 dieffect
.dwFlags
|= DIEP_TYPESPECIFICPARAMS
;
776 else if (IsEqualGUID(&pdei
->guid
, &GUID_Spring
) ||
777 IsEqualGUID(&pdei
->guid
, &GUID_Damper
) ||
778 IsEqualGUID(&pdei
->guid
, &GUID_Inertia
) ||
779 IsEqualGUID(&pdei
->guid
, &GUID_Friction
))
781 cdforce
.dwPositiveSaturation
= 10000;
782 cdforce
.dwNegativeSaturation
= 10000;
783 cdforce
.lPositiveCoefficient
= 10000;
784 cdforce
.lNegativeCoefficient
= 10000;
785 cdforce
.lDeadBand
= 0;
788 dieffect
.cbTypeSpecificParams
= sizeof(cdforce
);
789 dieffect
.lpvTypeSpecificParams
= &cdforce
;
790 dieffect
.dwFlags
|= DIEP_TYPESPECIFICPARAMS
;
793 hr
= IDirectInputDevice2_CreateEffect(
794 joystick
->device
, &pdei
->guid
, &dieffect
, &joystick
->effects
[joystick
->cur_effect
].effect
, NULL
);
796 joystick
->effects
[joystick
->cur_effect
].params
= dieffect
;
797 joystick
->effects
[joystick
->cur_effect
].info
= *pdei
;
798 joystick
->cur_effect
+= 1;
800 return DIENUM_CONTINUE
;
803 /*********************************************************************
804 * ff_dlgproc [internal]
807 static INT_PTR CALLBACK
ff_dlgproc(HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
809 static HANDLE thread
;
810 static struct JoystickData
*data
;
811 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd
, msg
, msg
, lparam
);
819 data
= (struct JoystickData
*) ((PROPSHEETPAGEW
*)lparam
)->lParam
;
821 /* Add joysticks with FF support to the combobox and get the effects */
822 for (i
= 0; i
< data
->num_joysticks
; i
++)
824 struct Joystick
*joy
= &data
->joysticks
[i
];
826 if (joy
->forcefeedback
)
828 SendDlgItemMessageW(hwnd
, IDC_FFSELECTCOMBO
, CB_ADDSTRING
, 0, (LPARAM
) joy
->instance
.tszInstanceName
);
829 SendDlgItemMessageW(hwnd
, IDC_FFSELECTCOMBO
, CB_SETITEMDATA
, cur
, i
);
833 /* Count device effects and then store them */
834 joy
->num_effects
= 0;
836 IDirectInputDevice8_EnumEffects(joy
->device
, ff_effects_callback
, (void *) joy
, 0);
837 joy
->effects
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct Effect
) * joy
->num_effects
);
840 IDirectInputDevice8_EnumEffects(joy
->device
, ff_effects_callback
, (void*) joy
, 0);
841 joy
->num_effects
= joy
->cur_effect
;
845 draw_ff_axis(hwnd
, data
);
853 case MAKEWPARAM(IDC_FFSELECTCOMBO
, CBN_SELCHANGE
):
854 ff_handle_joychange(hwnd
, data
);
856 SendDlgItemMessageW(hwnd
, IDC_FFEFFECTLIST
, LB_SETCURSEL
, 0, 0);
857 ff_handle_effectchange(hwnd
, &data
->joysticks
[data
->chosen_joystick
]);
860 case MAKEWPARAM(IDC_FFEFFECTLIST
, LBN_SELCHANGE
):
861 ff_handle_effectchange(hwnd
, &data
->joysticks
[data
->chosen_joystick
]);
867 switch(((LPNMHDR
)lparam
)->code
)
870 if (data
->num_ff
> 0)
875 /* Set the first joystick as default */
876 SendDlgItemMessageW(hwnd
, IDC_FFSELECTCOMBO
, CB_SETCURSEL
, 0, 0);
877 ff_handle_joychange(hwnd
, data
);
879 SendDlgItemMessageW(hwnd
, IDC_FFEFFECTLIST
, LB_SETCURSEL
, 0, 0);
880 ff_handle_effectchange(hwnd
, &data
->joysticks
[data
->chosen_joystick
]);
882 thread
= CreateThread(NULL
, 0, ff_input_thread
, (void*) data
, 0, &tid
);
886 case PSN_RESET
: /* intentional fall-through */
890 MsgWaitForMultipleObjects(1, &thread
, FALSE
, INFINITE
, 0);
899 /******************************************************************************
900 * propsheet_callback [internal]
902 static int CALLBACK
propsheet_callback(HWND hwnd
, UINT msg
, LPARAM lparam
)
904 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd
, msg
, msg
, lparam
);
907 case PSCB_INITIALIZED
:
913 /******************************************************************************
914 * display_cpl_sheets [internal]
916 * Build and display the dialog with all control panel propertysheets
919 static void display_cpl_sheets(HWND parent
, struct JoystickData
*data
)
921 INITCOMMONCONTROLSEX icex
;
922 PROPSHEETPAGEW psp
[NUM_PROPERTY_PAGES
];
923 PROPSHEETHEADERW psh
;
927 /* Initialize common controls */
928 icex
.dwSize
= sizeof(INITCOMMONCONTROLSEX
);
929 icex
.dwICC
= ICC_LISTVIEW_CLASSES
| ICC_BAR_CLASSES
;
930 InitCommonControlsEx(&icex
);
932 ZeroMemory(&psh
, sizeof(psh
));
933 ZeroMemory(psp
, sizeof(psp
));
935 /* Fill out all PROPSHEETPAGE */
936 psp
[id
].dwSize
= sizeof (PROPSHEETPAGEW
);
937 psp
[id
].hInstance
= hcpl
;
938 psp
[id
].u
.pszTemplate
= MAKEINTRESOURCEW(IDD_LIST
);
939 psp
[id
].pfnDlgProc
= list_dlgproc
;
940 psp
[id
].lParam
= (INT_PTR
) data
;
943 psp
[id
].dwSize
= sizeof (PROPSHEETPAGEW
);
944 psp
[id
].hInstance
= hcpl
;
945 psp
[id
].u
.pszTemplate
= MAKEINTRESOURCEW(IDD_TEST
);
946 psp
[id
].pfnDlgProc
= test_dlgproc
;
947 psp
[id
].lParam
= (INT_PTR
) data
;
950 psp
[id
].dwSize
= sizeof (PROPSHEETPAGEW
);
951 psp
[id
].hInstance
= hcpl
;
952 psp
[id
].u
.pszTemplate
= MAKEINTRESOURCEW(IDD_FORCEFEEDBACK
);
953 psp
[id
].pfnDlgProc
= ff_dlgproc
;
954 psp
[id
].lParam
= (INT_PTR
) data
;
957 /* Fill out the PROPSHEETHEADER */
958 psh
.dwSize
= sizeof (PROPSHEETHEADERW
);
959 psh
.dwFlags
= PSH_PROPSHEETPAGE
| PSH_USEICONID
| PSH_USECALLBACK
;
960 psh
.hwndParent
= parent
;
961 psh
.hInstance
= hcpl
;
962 psh
.pszCaption
= MAKEINTRESOURCEW(IDS_CPL_NAME
);
965 psh
.pfnCallback
= propsheet_callback
;
967 /* display the dialog */
968 PropertySheetW(&psh
);
973 /*********************************************************************
974 * CPlApplet (joy.cpl.@)
976 * Control Panel entry point
979 * hWnd [I] Handle for the Control Panel Window
980 * command [I] CPL_* Command
981 * lParam1 [I] first extra Parameter
982 * lParam2 [I] second extra Parameter
985 * Depends on the command
988 LONG CALLBACK
CPlApplet(HWND hwnd
, UINT command
, LPARAM lParam1
, LPARAM lParam2
)
990 static struct JoystickData data
;
991 TRACE("(%p, %u, 0x%lx, 0x%lx)\n", hwnd
, command
, lParam1
, lParam2
);
999 /* Initialize dinput */
1000 hr
= DirectInput8Create(GetModuleHandleW(NULL
), DIRECTINPUT_VERSION
, &IID_IDirectInput8W
, (void**)&data
.di
, NULL
);
1004 ERR("Failed to initialize DirectInput: 0x%08x\n", hr
);
1008 /* Then get all the connected joysticks */
1009 initialize_joysticks(&data
);
1018 CPLINFO
*appletInfo
= (CPLINFO
*) lParam2
;
1020 appletInfo
->idIcon
= ICO_MAIN
;
1021 appletInfo
->idName
= IDS_CPL_NAME
;
1022 appletInfo
->idInfo
= IDS_CPL_INFO
;
1023 appletInfo
->lData
= 0;
1028 display_cpl_sheets(hwnd
, &data
);
1032 destroy_joysticks(&data
);
1034 /* And destroy dinput too */
1035 IDirectInput8_Release(data
.di
);