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 /*********************************************************************
44 BOOL WINAPI
DllMain(HINSTANCE hdll
, DWORD reason
, LPVOID reserved
)
46 TRACE("(%p, %ld, %p)\n", hdll
, reason
, reserved
);
50 case DLL_PROCESS_ATTACH
:
51 DisableThreadLibraryCalls(hdll
);
57 /***********************************************************************
58 * enum_callback [internal]
59 * Enumerates, creates and sets the common data format for all the joystick devices.
60 * First time it checks if space for the joysticks was already reserved
61 * and if not, just counts how many there are.
63 static BOOL CALLBACK
ff_effects_callback(const DIEFFECTINFOW
*pdei
, void *pvRef
);
64 static BOOL CALLBACK
enum_callback(const DIDEVICEINSTANCEW
*instance
, void *context
)
66 DIPROPGUIDANDPATH prop_guid_path
=
70 .dwSize
= sizeof(DIPROPGUIDANDPATH
),
71 .dwHeaderSize
= sizeof(DIPROPHEADER
),
75 struct JoystickData
*data
= context
;
76 struct Joystick
*joystick
;
77 DIPROPRANGE proprange
;
80 if (data
->joysticks
== NULL
)
82 data
->num_joysticks
+= 1;
83 return DIENUM_CONTINUE
;
86 joystick
= &data
->joysticks
[data
->cur_joystick
];
87 data
->cur_joystick
+= 1;
89 IDirectInput8_CreateDevice(data
->di
, &instance
->guidInstance
, &joystick
->device
, NULL
);
90 IDirectInputDevice8_SetDataFormat(joystick
->device
, &c_dfDIJoystick
);
92 joystick
->instance
= *instance
;
94 caps
.dwSize
= sizeof(caps
);
95 IDirectInputDevice8_GetCapabilities(joystick
->device
, &caps
);
97 joystick
->num_buttons
= caps
.dwButtons
;
98 joystick
->num_axes
= caps
.dwAxes
;
99 joystick
->forcefeedback
= caps
.dwFlags
& DIDC_FORCEFEEDBACK
;
100 joystick
->num_effects
= 0;
102 IDirectInputDevice8_GetProperty(joystick
->device
, DIPROP_GUIDANDPATH
, &prop_guid_path
.diph
);
103 joystick
->is_xinput
= wcsstr(prop_guid_path
.wszPath
, L
"&ig_") != NULL
;
105 if (joystick
->forcefeedback
) data
->num_ff
++;
107 /* Set axis range to ease the GUI visualization */
108 proprange
.diph
.dwSize
= sizeof(DIPROPRANGE
);
109 proprange
.diph
.dwHeaderSize
= sizeof(DIPROPHEADER
);
110 proprange
.diph
.dwHow
= DIPH_DEVICE
;
111 proprange
.diph
.dwObj
= 0;
112 proprange
.lMin
= TEST_AXIS_MIN
;
113 proprange
.lMax
= TEST_AXIS_MAX
;
115 IDirectInputDevice_SetProperty(joystick
->device
, DIPROP_RANGE
, &proprange
.diph
);
117 if (!joystick
->forcefeedback
) return DIENUM_CONTINUE
;
119 /* Count device effects and then store them */
120 joystick
->num_effects
= 0;
121 joystick
->effects
= NULL
;
122 IDirectInputDevice8_EnumEffects(joystick
->device
, ff_effects_callback
, (void *)joystick
, 0);
123 joystick
->effects
= malloc(sizeof(struct Effect
) * joystick
->num_effects
);
125 joystick
->cur_effect
= 0;
126 IDirectInputDevice8_EnumEffects(joystick
->device
, ff_effects_callback
, (void*)joystick
, 0);
127 joystick
->num_effects
= joystick
->cur_effect
;
129 return DIENUM_CONTINUE
;
132 /***********************************************************************
133 * initialize_joysticks [internal]
135 static void initialize_joysticks(struct JoystickData
*data
)
137 data
->num_joysticks
= 0;
138 data
->cur_joystick
= 0;
139 IDirectInput8_EnumDevices(data
->di
, DI8DEVCLASS_GAMECTRL
, enum_callback
, data
, DIEDFL_ATTACHEDONLY
);
140 data
->joysticks
= malloc(sizeof(struct Joystick
) * data
->num_joysticks
);
142 /* Get all the joysticks */
143 IDirectInput8_EnumDevices(data
->di
, DI8DEVCLASS_GAMECTRL
, enum_callback
, data
, DIEDFL_ATTACHEDONLY
);
146 /***********************************************************************
147 * destroy_joysticks [internal]
149 static void destroy_joysticks(struct JoystickData
*data
)
153 for (i
= 0; i
< data
->num_joysticks
; i
++)
156 if (data
->joysticks
[i
].forcefeedback
&& data
->joysticks
[i
].num_effects
> 0)
158 for (j
= 0; j
< data
->joysticks
[i
].num_effects
; j
++)
159 if (data
->joysticks
[i
].effects
[j
].effect
)
160 IDirectInputEffect_Release(data
->joysticks
[i
].effects
[j
].effect
);
162 free(data
->joysticks
[i
].effects
);
165 IDirectInputDevice8_Unacquire(data
->joysticks
[i
].device
);
166 IDirectInputDevice8_Release(data
->joysticks
[i
].device
);
169 free(data
->joysticks
);
170 data
->joysticks
= NULL
;
173 /******************************************************************************
174 * get_app_key [internal]
175 * Get the default DirectInput key and the selected app config key.
177 static BOOL
get_app_key(HKEY
*defkey
, HKEY
*appkey
)
181 /* Registry key can be found in HKCU\Software\Wine\DirectInput */
182 if (RegCreateKeyExW(HKEY_CURRENT_USER
, L
"Software\\Wine\\DirectInput\\Joysticks", 0, NULL
, 0,
183 KEY_SET_VALUE
| KEY_READ
, NULL
, defkey
, NULL
))
186 return *defkey
|| *appkey
;
189 /******************************************************************************
190 * set_config_key [internal]
191 * Writes a string value to a registry key, deletes the key if value == NULL
193 static DWORD
set_config_key(HKEY defkey
, HKEY appkey
, const WCHAR
*name
, const WCHAR
*value
, DWORD size
)
197 if (appkey
&& !RegDeleteValueW(appkey
, name
))
200 if (defkey
&& !RegDeleteValueW(defkey
, name
))
205 if (appkey
&& !RegSetValueExW(appkey
, name
, 0, REG_SZ
, (const BYTE
*) value
, (size
+ 1)*sizeof(WCHAR
)))
208 if (defkey
&& !RegSetValueExW(defkey
, name
, 0, REG_SZ
, (const BYTE
*) value
, (size
+ 1)*sizeof(WCHAR
)))
212 return ERROR_FILE_NOT_FOUND
;
215 /******************************************************************************
216 * enable_joystick [internal]
217 * Writes to the DirectInput registry key that enables/disables a joystick
218 * from being enumerated.
220 static void enable_joystick(WCHAR
*joy_name
, BOOL enable
)
224 get_app_key(&hkey
, &appkey
);
227 set_config_key(hkey
, appkey
, joy_name
, L
"disabled", wcslen(L
"disabled"));
229 set_config_key(hkey
, appkey
, joy_name
, NULL
, 0);
231 if (hkey
) RegCloseKey(hkey
);
232 if (appkey
) RegCloseKey(appkey
);
235 static void refresh_joystick_list(HWND hwnd
, struct JoystickData
*data
)
237 struct Joystick
*joy
, *joy_end
;
243 destroy_joysticks(data
);
244 initialize_joysticks(data
);
246 SendDlgItemMessageW(hwnd
, IDC_JOYSTICKLIST
, LB_RESETCONTENT
, 0, 0);
247 SendDlgItemMessageW(hwnd
, IDC_DISABLEDLIST
, LB_RESETCONTENT
, 0, 0);
248 SendDlgItemMessageW(hwnd
, IDC_XINPUTLIST
, LB_RESETCONTENT
, 0, 0);
250 for (joy
= data
->joysticks
, joy_end
= joy
+ data
->num_joysticks
; joy
!= joy_end
; ++joy
)
252 if (joy
->is_xinput
) SendDlgItemMessageW(hwnd
, IDC_XINPUTLIST
, LB_ADDSTRING
, 0, (LPARAM
) joy
->instance
.tszInstanceName
);
253 else SendDlgItemMessageW(hwnd
, IDC_JOYSTICKLIST
, LB_ADDSTRING
, 0, (LPARAM
) joy
->instance
.tszInstanceName
);
256 /* Search for disabled joysticks */
257 get_app_key(&hkey
, &appkey
);
258 RegQueryInfoKeyW(hkey
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &values
, NULL
, NULL
, NULL
, NULL
);
260 for (i
=0; i
< values
; i
++)
262 DWORD name_len
= MAX_PATH
, data_len
= MAX_PATH
;
263 WCHAR buf_name
[MAX_PATH
+ 9], buf_data
[MAX_PATH
];
265 status
= RegEnumValueW(hkey
, i
, buf_name
, &name_len
, NULL
, NULL
, (BYTE
*) buf_data
, &data_len
);
267 if (status
== ERROR_SUCCESS
&& !wcscmp(L
"disabled", buf_data
))
268 SendDlgItemMessageW(hwnd
, IDC_DISABLEDLIST
, LB_ADDSTRING
, 0, (LPARAM
) buf_name
);
271 if (hkey
) RegCloseKey(hkey
);
272 if (appkey
) RegCloseKey(appkey
);
275 static void override_joystick(WCHAR
*joy_name
, BOOL override
)
279 get_app_key(&hkey
, &appkey
);
282 set_config_key(hkey
, appkey
, joy_name
, L
"override", wcslen(L
"override"));
284 set_config_key(hkey
, appkey
, joy_name
, NULL
, 0);
286 if (hkey
) RegCloseKey(hkey
);
287 if (appkey
) RegCloseKey(appkey
);
290 /*********************************************************************
291 * list_dlgproc [internal]
294 static INT_PTR CALLBACK
list_dlgproc(HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
296 WCHAR instance_name
[MAX_PATH
] = {0};
297 static struct JoystickData
*data
;
300 TRACE("(%p, 0x%08x/%d, 0x%Ix)\n", hwnd
, msg
, msg
, lparam
);
305 data
= (struct JoystickData
*) ((PROPSHEETPAGEW
*)lparam
)->lParam
;
307 refresh_joystick_list(hwnd
, data
);
309 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONENABLE
), FALSE
);
310 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONDISABLE
), FALSE
);
311 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONRESET
), FALSE
);
312 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONOVERRIDE
), FALSE
);
314 /* Store the hwnd to be used with MapDialogRect for unit conversions */
315 data
->graphics
.hwnd
= hwnd
;
322 switch (LOWORD(wparam
))
324 case IDC_BUTTONDISABLE
:
326 if ((sel
= SendDlgItemMessageW(hwnd
, IDC_JOYSTICKLIST
, LB_GETCURSEL
, 0, 0)) >= 0)
327 SendDlgItemMessageW(hwnd
, IDC_JOYSTICKLIST
, LB_GETTEXT
, sel
, (LPARAM
)instance_name
);
328 if ((sel
= SendDlgItemMessageW(hwnd
, IDC_XINPUTLIST
, LB_GETCURSEL
, 0, 0)) >= 0)
329 SendDlgItemMessageW(hwnd
, IDC_XINPUTLIST
, LB_GETTEXT
, sel
, (LPARAM
)instance_name
);
331 if (instance_name
[0])
333 enable_joystick(instance_name
, FALSE
);
334 refresh_joystick_list(hwnd
, data
);
339 case IDC_BUTTONENABLE
:
341 if ((sel
= SendDlgItemMessageW(hwnd
, IDC_DISABLEDLIST
, LB_GETCURSEL
, 0, 0)) >= 0)
342 SendDlgItemMessageW(hwnd
, IDC_DISABLEDLIST
, LB_GETTEXT
, sel
, (LPARAM
)instance_name
);
344 if (instance_name
[0])
346 enable_joystick(instance_name
, TRUE
);
347 refresh_joystick_list(hwnd
, data
);
352 case IDC_BUTTONRESET
:
354 if ((sel
= SendDlgItemMessageW(hwnd
, IDC_JOYSTICKLIST
, LB_GETCURSEL
, 0, 0)) >= 0)
356 SendDlgItemMessageW(hwnd
, IDC_JOYSTICKLIST
, LB_GETTEXT
, sel
, (LPARAM
)instance_name
);
357 override_joystick(instance_name
, FALSE
);
358 refresh_joystick_list(hwnd
, data
);
363 case IDC_BUTTONOVERRIDE
:
365 if ((sel
= SendDlgItemMessageW(hwnd
, IDC_XINPUTLIST
, LB_GETCURSEL
, 0, 0)) >= 0)
367 SendDlgItemMessageW(hwnd
, IDC_XINPUTLIST
, LB_GETTEXT
, sel
, (LPARAM
)instance_name
);
368 override_joystick(instance_name
, TRUE
);
369 refresh_joystick_list(hwnd
, data
);
374 case IDC_JOYSTICKLIST
:
375 SendDlgItemMessageW(hwnd
, IDC_DISABLEDLIST
, LB_SETCURSEL
, -1, 0);
376 SendDlgItemMessageW(hwnd
, IDC_XINPUTLIST
, LB_SETCURSEL
, -1, 0);
377 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONENABLE
), FALSE
);
378 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONDISABLE
), TRUE
);
379 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONOVERRIDE
), FALSE
);
380 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONRESET
), TRUE
);
384 SendDlgItemMessageW(hwnd
, IDC_JOYSTICKLIST
, LB_SETCURSEL
, -1, 0);
385 SendDlgItemMessageW(hwnd
, IDC_DISABLEDLIST
, LB_SETCURSEL
, -1, 0);
386 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONENABLE
), FALSE
);
387 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONDISABLE
), TRUE
);
388 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONOVERRIDE
), TRUE
);
389 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONRESET
), FALSE
);
392 case IDC_DISABLEDLIST
:
393 SendDlgItemMessageW(hwnd
, IDC_JOYSTICKLIST
, LB_SETCURSEL
, -1, 0);
394 SendDlgItemMessageW(hwnd
, IDC_XINPUTLIST
, LB_SETCURSEL
, -1, 0);
395 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONENABLE
), TRUE
);
396 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONDISABLE
), FALSE
);
397 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONOVERRIDE
), FALSE
);
398 EnableWindow(GetDlgItem(hwnd
, IDC_BUTTONRESET
), FALSE
);
413 /*********************************************************************
414 * Joystick testing functions
417 static void dump_joy_state(DIJOYSTATE
* st
)
420 TRACE("Ax (% 5ld,% 5ld,% 5ld)\n", st
->lX
,st
->lY
, st
->lZ
);
421 TRACE("RAx (% 5ld,% 5ld,% 5ld)\n", st
->lRx
, st
->lRy
, st
->lRz
);
422 TRACE("Slider (% 5ld,% 5ld)\n", st
->rglSlider
[0], st
->rglSlider
[1]);
423 TRACE("Pov (% 5ld,% 5ld,% 5ld,% 5ld)\n", st
->rgdwPOV
[0], st
->rgdwPOV
[1], st
->rgdwPOV
[2], st
->rgdwPOV
[3]);
426 for(i
=0; i
< TEST_MAX_BUTTONS
; i
++)
427 TRACE(" %c",st
->rgbButtons
[i
] ? 'x' : 'o');
431 static void poll_input(const struct Joystick
*joy
, DIJOYSTATE
*state
)
435 hr
= IDirectInputDevice8_Poll(joy
->device
);
437 /* If it failed, try to acquire the joystick */
440 hr
= IDirectInputDevice8_Acquire(joy
->device
);
441 while (hr
== DIERR_INPUTLOST
) hr
= IDirectInputDevice8_Acquire(joy
->device
);
444 if (hr
== DIERR_OTHERAPPHASPRIO
) return;
446 IDirectInputDevice8_GetDeviceState(joy
->device
, sizeof(DIJOYSTATE
), state
);
449 static DWORD WINAPI
input_thread(void *param
)
451 int axes_pos
[TEST_MAX_AXES
][2];
453 struct JoystickData
*data
= param
;
455 /* Setup POV as clock positions
462 int ma
= TEST_AXIS_MAX
;
463 int pov_val
[9] = {0, 4500, 9000, 13500,
464 18000, 22500, 27000, 31500, -1};
465 int pov_pos
[9][2] = { {0, -ma
}, {ma
/2, -ma
/2}, {ma
, 0}, {ma
/2, ma
/2},
466 {0, ma
}, {-ma
/2, ma
/2}, {-ma
, 0}, {-ma
/2, -ma
/2}, {0, 0} };
468 ZeroMemory(&state
, sizeof(state
));
475 poll_input(&data
->joysticks
[data
->chosen_joystick
], &state
);
477 dump_joy_state(&state
);
479 /* Indicate pressed buttons */
480 for (i
= 0; i
< TEST_MAX_BUTTONS
; i
++)
481 SendMessageW(data
->graphics
.buttons
[i
], BM_SETSTATE
, !!state
.rgbButtons
[i
], 0);
483 /* Indicate axis positions, axes showing are hardcoded for now */
484 axes_pos
[0][0] = state
.lX
;
485 axes_pos
[0][1] = state
.lY
;
486 axes_pos
[1][0] = state
.lRx
;
487 axes_pos
[1][1] = state
.lRy
;
488 axes_pos
[2][0] = state
.lZ
;
489 axes_pos
[2][1] = state
.lRz
;
492 for (j
= 0; j
< ARRAY_SIZE(pov_val
); j
++)
494 if (state
.rgdwPOV
[0] == pov_val
[j
])
496 axes_pos
[3][0] = pov_pos
[j
][0];
497 axes_pos
[3][1] = pov_pos
[j
][1];
501 for (i
= 0; i
< TEST_MAX_AXES
; i
++)
505 r
.left
= (TEST_AXIS_X
+ TEST_NEXT_AXIS_X
*i
+ axes_pos
[i
][0]);
506 r
.top
= (TEST_AXIS_Y
+ axes_pos
[i
][1]);
507 r
.bottom
= r
.right
= 0; /* unused */
508 MapDialogRect(data
->graphics
.hwnd
, &r
);
510 SetWindowPos(data
->graphics
.axes
[i
], 0, r
.left
, r
.top
, 0, 0, SWP_NOZORDER
| SWP_NOSIZE
);
513 Sleep(TEST_POLL_TIME
);
519 static void test_handle_joychange(HWND hwnd
, struct JoystickData
*data
)
523 if (data
->num_joysticks
== 0) return;
525 data
->chosen_joystick
= SendDlgItemMessageW(hwnd
, IDC_TESTSELECTCOMBO
, CB_GETCURSEL
, 0, 0);
527 /* Enable only buttons present in the device */
528 for (i
= 0; i
< TEST_MAX_BUTTONS
; i
++)
529 ShowWindow(data
->graphics
.buttons
[i
], i
< data
->joysticks
[data
->chosen_joystick
].num_buttons
);
532 /*********************************************************************
533 * button_number_to_wchar [internal]
534 * Transforms an integer in the interval [0,99] into a 2 character WCHAR string
536 static void button_number_to_wchar(int n
, WCHAR str
[3])
538 str
[1] = n
% 10 + '0';
540 str
[0] = n
% 10 + '0';
544 static void draw_joystick_buttons(HWND hwnd
, struct JoystickData
* data
)
547 int row
= 0, col
= 0;
548 WCHAR button_label
[3];
549 HINSTANCE hinst
= (HINSTANCE
) GetWindowLongPtrW(hwnd
, GWLP_HINSTANCE
);
551 for (i
= 0; i
< TEST_MAX_BUTTONS
; i
++)
555 if ((i
% TEST_BUTTON_COL_MAX
) == 0 && i
!= 0)
561 r
.left
= (TEST_BUTTON_X
+ TEST_NEXT_BUTTON_X
*col
);
562 r
.top
= (TEST_BUTTON_Y
+ TEST_NEXT_BUTTON_Y
*row
);
563 r
.right
= r
.left
+ TEST_BUTTON_SIZE_X
;
564 r
.bottom
= r
.top
+ TEST_BUTTON_SIZE_Y
;
565 MapDialogRect(hwnd
, &r
);
567 button_number_to_wchar(i
+ 1, button_label
);
569 data
->graphics
.buttons
[i
] = CreateWindowW(L
"Button", button_label
, WS_CHILD
,
570 r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
,
571 hwnd
, NULL
, NULL
, hinst
);
577 static void draw_joystick_axes(HWND hwnd
, struct JoystickData
* data
)
580 HINSTANCE hinst
= (HINSTANCE
) GetWindowLongPtrW(hwnd
, GWLP_HINSTANCE
);
581 static const WCHAR axes_names
[TEST_MAX_AXES
][7] = { L
"X,Y", L
"Rx,Ry", L
"Z,Rz", L
"POV"};
582 static const DWORD axes_idc
[TEST_MAX_AXES
] = { IDC_TESTGROUPXY
, IDC_TESTGROUPRXRY
,
583 IDC_TESTGROUPZRZ
, IDC_TESTGROUPPOV
};
585 for (i
= 0; i
< TEST_MAX_AXES
; i
++)
588 /* Set axis box name */
589 SetWindowTextW(GetDlgItem(hwnd
, axes_idc
[i
]), axes_names
[i
]);
591 r
.left
= (TEST_AXIS_X
+ TEST_NEXT_AXIS_X
*i
);
593 r
.right
= r
.left
+ TEST_AXIS_SIZE_X
;
594 r
.bottom
= r
.top
+ TEST_AXIS_SIZE_Y
;
595 MapDialogRect(hwnd
, &r
);
597 data
->graphics
.axes
[i
] = CreateWindowW(L
"Button", NULL
, WS_CHILD
| WS_VISIBLE
,
598 r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
,
599 hwnd
, NULL
, NULL
, hinst
);
603 /*********************************************************************
604 * test_dlgproc [internal]
607 static void refresh_test_joystick_list(HWND hwnd
, struct JoystickData
*data
)
609 struct Joystick
*joy
, *joy_end
;
610 SendDlgItemMessageW(hwnd
, IDC_TESTSELECTCOMBO
, CB_RESETCONTENT
, 0, 0);
611 for (joy
= data
->joysticks
, joy_end
= joy
+ data
->num_joysticks
; joy
!= joy_end
; ++joy
)
612 SendDlgItemMessageW(hwnd
, IDC_TESTSELECTCOMBO
, CB_ADDSTRING
, 0, (LPARAM
)joy
->instance
.tszInstanceName
);
615 static INT_PTR CALLBACK
test_dlgproc(HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
617 static HANDLE thread
;
618 static struct JoystickData
*data
;
619 TRACE("(%p, 0x%08x/%d, 0x%Ix)\n", hwnd
, msg
, msg
, lparam
);
625 data
= (struct JoystickData
*) ((PROPSHEETPAGEW
*)lparam
)->lParam
;
627 refresh_test_joystick_list(hwnd
, data
);
628 draw_joystick_buttons(hwnd
, data
);
629 draw_joystick_axes(hwnd
, data
);
637 case MAKEWPARAM(IDC_TESTSELECTCOMBO
, CBN_SELCHANGE
):
638 test_handle_joychange(hwnd
, data
);
644 switch(((LPNMHDR
)lparam
)->code
)
650 refresh_test_joystick_list(hwnd
, data
);
652 /* Initialize input thread */
653 if (data
->num_joysticks
> 0)
657 /* Set the first joystick as default */
658 SendDlgItemMessageW(hwnd
, IDC_TESTSELECTCOMBO
, CB_SETCURSEL
, 0, 0);
659 test_handle_joychange(hwnd
, data
);
661 thread
= CreateThread(NULL
, 0, input_thread
, (void*) data
, 0, &tid
);
666 case PSN_RESET
: /* intentional fall-through */
668 /* Stop input thread */
670 MsgWaitForMultipleObjects(1, &thread
, FALSE
, INFINITE
, 0);
679 /*********************************************************************
680 * Joystick force feedback testing functions
683 static void draw_ff_axis(HWND hwnd
, struct JoystickData
*data
)
685 HINSTANCE hinst
= (HINSTANCE
) GetWindowLongPtrW(hwnd
, GWLP_HINSTANCE
);
690 r
.right
= r
.left
+ FF_AXIS_SIZE_X
;
691 r
.bottom
= r
.top
+ FF_AXIS_SIZE_Y
;
692 MapDialogRect(hwnd
, &r
);
694 /* Draw direction axis */
695 data
->graphics
.ff_axis
= CreateWindowW(L
"Button", NULL
, WS_CHILD
| WS_VISIBLE
,
696 r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
,
697 hwnd
, NULL
, NULL
, hinst
);
700 static void initialize_effects_list(HWND hwnd
, struct Joystick
* joy
)
704 SendDlgItemMessageW(hwnd
, IDC_FFEFFECTLIST
, LB_RESETCONTENT
, 0, 0);
706 for (i
=0; i
< joy
->num_effects
; i
++)
708 /* Effect names start with GUID_, so we'll skip this part */
709 WCHAR
*name
= joy
->effects
[i
].info
.tszName
+ 5;
710 SendDlgItemMessageW(hwnd
, IDC_FFEFFECTLIST
, LB_ADDSTRING
, 0, (LPARAM
) name
);
714 static void ff_handle_joychange(HWND hwnd
, struct JoystickData
*data
)
716 if (data
->num_ff
== 0) return;
718 data
->chosen_joystick
= SendDlgItemMessageW(hwnd
, IDC_FFSELECTCOMBO
, CB_GETCURSEL
, 0, 0);
719 initialize_effects_list(hwnd
, &data
->joysticks
[data
->chosen_joystick
]);
722 static void ff_handle_effectchange(HWND hwnd
, struct Joystick
*joy
)
724 int sel
= SendDlgItemMessageW(hwnd
, IDC_FFEFFECTLIST
, LB_GETCURSEL
, 0, 0);
728 joy
->chosen_effect
= sel
;
729 IDirectInputDevice8_Unacquire(joy
->device
);
730 IDirectInputDevice8_SetCooperativeLevel(joy
->device
, GetAncestor(hwnd
, GA_ROOT
), DISCL_BACKGROUND
|DISCL_EXCLUSIVE
);
731 IDirectInputDevice8_Acquire(joy
->device
);
734 static DWORD WINAPI
ff_input_thread(void *param
)
736 struct JoystickData
*data
= param
;
739 ZeroMemory(&state
, sizeof(state
));
744 struct Joystick
*joy
= &data
->joysticks
[data
->chosen_joystick
];
745 int chosen_effect
= joy
->chosen_effect
;
747 DWORD flags
= DIEP_AXES
| DIEP_DIRECTION
| DIEP_NORESTART
;
750 Sleep(TEST_POLL_TIME
);
752 /* Skip this if we have no effects */
753 if (joy
->num_effects
== 0 || chosen_effect
< 0) continue;
755 poll_input(joy
, &state
);
757 /* Set ff parameters and draw the axis */
758 dieffect
= &joy
->effects
[chosen_effect
].params
;
759 dieffect
->rgdwAxes
[0] = state
.lX
;
760 dieffect
->rgdwAxes
[1] = state
.lY
;
762 r
.left
= FF_AXIS_X
+ state
.lX
;
763 r
.top
= FF_AXIS_Y
+ state
.lY
;
764 r
.right
= r
.bottom
= 0; /* unused */
765 MapDialogRect(data
->graphics
.hwnd
, &r
);
767 SetWindowPos(data
->graphics
.ff_axis
, 0, r
.left
, r
.top
, 0, 0, SWP_NOZORDER
| SWP_NOSIZE
);
769 for (i
=0; i
< TEST_MAX_BUTTONS
; i
++)
770 if (state
.rgbButtons
[i
])
772 IDirectInputEffect_SetParameters(joy
->effects
[chosen_effect
].effect
, dieffect
, flags
);
773 IDirectInputEffect_Start(joy
->effects
[chosen_effect
].effect
, 1, 0);
781 /***********************************************************************
782 * ff_effects_callback [internal]
783 * Enumerates, creates, sets the some parameters and stores all ff effects
784 * supported by the joystick. Works like enum_callback, counting the effects
785 * first and then storing them.
787 static BOOL CALLBACK
ff_effects_callback(const DIEFFECTINFOW
*pdei
, void *pvRef
)
791 DWORD axes
[2] = {DIJOFS_X
, DIJOFS_Y
};
792 LONG direction
[2] = {0, 0};
794 struct Joystick
*joystick
= pvRef
;
796 DICONSTANTFORCE cforce
;
800 if (joystick
->effects
== NULL
)
802 joystick
->num_effects
+= 1;
803 return DIENUM_CONTINUE
;
806 hr
= IDirectInputDevice8_Acquire(joystick
->device
);
808 if (FAILED(hr
)) return DIENUM_CONTINUE
;
810 ZeroMemory(&dieffect
, sizeof(dieffect
));
812 dieffect
.dwSize
= sizeof(dieffect
);
813 dieffect
.dwFlags
= DIEFF_CARTESIAN
|DIEFF_OBJECTOFFSETS
;
814 dieffect
.dwDuration
= FF_PLAY_TIME
;
815 dieffect
.dwGain
= DI_FFNOMINALMAX
;
817 dieffect
.rgdwAxes
= axes
;
818 dieffect
.rglDirection
= direction
;
820 if (IsEqualGUID(&pdei
->guid
, &GUID_RampForce
))
823 rforce
.lEnd
= DI_FFNOMINALMAX
;
825 dieffect
.cbTypeSpecificParams
= sizeof(rforce
);
826 dieffect
.lpvTypeSpecificParams
= &rforce
;
827 dieffect
.dwFlags
|= DIEP_TYPESPECIFICPARAMS
;
829 else if (IsEqualGUID(&pdei
->guid
, &GUID_ConstantForce
))
831 cforce
.lMagnitude
= DI_FFNOMINALMAX
;
833 dieffect
.cbTypeSpecificParams
= sizeof(cforce
);
834 dieffect
.lpvTypeSpecificParams
= &cforce
;
835 dieffect
.dwFlags
|= DIEP_TYPESPECIFICPARAMS
;
837 else if (IsEqualGUID(&pdei
->guid
, &GUID_Sine
) ||
838 IsEqualGUID(&pdei
->guid
, &GUID_Square
) ||
839 IsEqualGUID(&pdei
->guid
, &GUID_Triangle
) ||
840 IsEqualGUID(&pdei
->guid
, &GUID_SawtoothUp
) ||
841 IsEqualGUID(&pdei
->guid
, &GUID_SawtoothDown
))
843 pforce
.dwMagnitude
= DI_FFNOMINALMAX
;
846 pforce
.dwPeriod
= FF_PERIOD_TIME
;
848 dieffect
.cbTypeSpecificParams
= sizeof(pforce
);
849 dieffect
.lpvTypeSpecificParams
= &pforce
;
850 dieffect
.dwFlags
|= DIEP_TYPESPECIFICPARAMS
;
852 else if (IsEqualGUID(&pdei
->guid
, &GUID_Spring
) ||
853 IsEqualGUID(&pdei
->guid
, &GUID_Damper
) ||
854 IsEqualGUID(&pdei
->guid
, &GUID_Inertia
) ||
855 IsEqualGUID(&pdei
->guid
, &GUID_Friction
))
857 cdforce
.dwPositiveSaturation
= 10000;
858 cdforce
.dwNegativeSaturation
= 10000;
859 cdforce
.lPositiveCoefficient
= 10000;
860 cdforce
.lNegativeCoefficient
= 10000;
861 cdforce
.lDeadBand
= 0;
864 dieffect
.cbTypeSpecificParams
= sizeof(cdforce
);
865 dieffect
.lpvTypeSpecificParams
= &cdforce
;
866 dieffect
.dwFlags
|= DIEP_TYPESPECIFICPARAMS
;
871 dieffect
.cAxes
= num_axes
--;
872 hr
= IDirectInputDevice2_CreateEffect(
873 joystick
->device
, &pdei
->guid
, &dieffect
, &joystick
->effects
[joystick
->cur_effect
].effect
, NULL
);
875 while (FAILED(hr
) && num_axes
);
879 FIXME("Failed to create effect with type %s, hr %#lx\n", debugstr_guid(&pdei
->guid
), hr
);
880 return DIENUM_CONTINUE
;
883 joystick
->effects
[joystick
->cur_effect
].params
= dieffect
;
884 joystick
->effects
[joystick
->cur_effect
].info
= *pdei
;
885 joystick
->cur_effect
+= 1;
887 return DIENUM_CONTINUE
;
890 /*********************************************************************
891 * ff_dlgproc [internal]
894 static void refresh_ff_joystick_list(HWND hwnd
, struct JoystickData
*data
)
896 struct Joystick
*joy
, *joy_end
;
897 SendDlgItemMessageW(hwnd
, IDC_FFSELECTCOMBO
, CB_RESETCONTENT
, 0, 0);
898 for (joy
= data
->joysticks
, joy_end
= joy
+ data
->num_joysticks
; joy
!= joy_end
; ++joy
)
899 SendDlgItemMessageW(hwnd
, IDC_FFSELECTCOMBO
, CB_ADDSTRING
, 0, (LPARAM
)joy
->instance
.tszInstanceName
);
902 static INT_PTR CALLBACK
ff_dlgproc(HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
904 static HANDLE thread
;
905 static struct JoystickData
*data
;
906 TRACE("(%p, 0x%08x/%d, 0x%Ix)\n", hwnd
, msg
, msg
, lparam
);
912 data
= (struct JoystickData
*) ((PROPSHEETPAGEW
*)lparam
)->lParam
;
914 refresh_ff_joystick_list(hwnd
, data
);
915 draw_ff_axis(hwnd
, data
);
923 case MAKEWPARAM(IDC_FFSELECTCOMBO
, CBN_SELCHANGE
):
924 ff_handle_joychange(hwnd
, data
);
926 SendDlgItemMessageW(hwnd
, IDC_FFEFFECTLIST
, LB_SETCURSEL
, 0, 0);
927 ff_handle_effectchange(hwnd
, &data
->joysticks
[data
->chosen_joystick
]);
930 case MAKEWPARAM(IDC_FFEFFECTLIST
, LBN_SELCHANGE
):
931 ff_handle_effectchange(hwnd
, &data
->joysticks
[data
->chosen_joystick
]);
937 switch(((LPNMHDR
)lparam
)->code
)
940 refresh_ff_joystick_list(hwnd
, data
);
942 if (data
->num_ff
> 0)
947 /* Set the first joystick as default */
948 SendDlgItemMessageW(hwnd
, IDC_FFSELECTCOMBO
, CB_SETCURSEL
, 0, 0);
949 ff_handle_joychange(hwnd
, data
);
951 SendDlgItemMessageW(hwnd
, IDC_FFEFFECTLIST
, LB_SETCURSEL
, 0, 0);
952 ff_handle_effectchange(hwnd
, &data
->joysticks
[data
->chosen_joystick
]);
954 thread
= CreateThread(NULL
, 0, ff_input_thread
, (void*) data
, 0, &tid
);
958 case PSN_RESET
: /* intentional fall-through */
962 MsgWaitForMultipleObjects(1, &thread
, FALSE
, INFINITE
, 0);
971 /******************************************************************************
972 * propsheet_callback [internal]
974 static int CALLBACK
propsheet_callback(HWND hwnd
, UINT msg
, LPARAM lparam
)
976 TRACE("(%p, 0x%08x/%d, 0x%Ix)\n", hwnd
, msg
, msg
, lparam
);
979 case PSCB_INITIALIZED
:
985 /******************************************************************************
986 * display_cpl_sheets [internal]
988 * Build and display the dialog with all control panel propertysheets
991 static void display_cpl_sheets(HWND parent
, struct JoystickData
*data
)
993 INITCOMMONCONTROLSEX icex
;
994 PROPSHEETPAGEW psp
[NUM_PROPERTY_PAGES
];
995 BOOL activated
= FALSE
;
996 PROPSHEETHEADERW psh
;
1002 OleInitialize(NULL
);
1003 /* Activate context */
1004 memset(&actctx
, 0, sizeof(actctx
));
1005 actctx
.cbSize
= sizeof(actctx
);
1006 actctx
.hModule
= hcpl
;
1007 actctx
.lpResourceName
= MAKEINTRESOURCEW(124);
1008 actctx
.dwFlags
= ACTCTX_FLAG_HMODULE_VALID
| ACTCTX_FLAG_RESOURCE_NAME_VALID
;
1009 context
= CreateActCtxW(&actctx
);
1010 if (context
!= INVALID_HANDLE_VALUE
)
1011 activated
= ActivateActCtx(context
, &cookie
);
1013 /* Initialize common controls */
1014 icex
.dwSize
= sizeof(INITCOMMONCONTROLSEX
);
1015 icex
.dwICC
= ICC_LISTVIEW_CLASSES
| ICC_BAR_CLASSES
;
1016 InitCommonControlsEx(&icex
);
1018 ZeroMemory(&psh
, sizeof(psh
));
1019 ZeroMemory(psp
, sizeof(psp
));
1021 /* Fill out all PROPSHEETPAGE */
1022 psp
[id
].dwSize
= sizeof (PROPSHEETPAGEW
);
1023 psp
[id
].hInstance
= hcpl
;
1024 psp
[id
].u
.pszTemplate
= MAKEINTRESOURCEW(IDD_LIST
);
1025 psp
[id
].pfnDlgProc
= list_dlgproc
;
1026 psp
[id
].lParam
= (INT_PTR
) data
;
1029 psp
[id
].dwSize
= sizeof (PROPSHEETPAGEW
);
1030 psp
[id
].hInstance
= hcpl
;
1031 psp
[id
].u
.pszTemplate
= MAKEINTRESOURCEW(IDD_TEST
);
1032 psp
[id
].pfnDlgProc
= test_dlgproc
;
1033 psp
[id
].lParam
= (INT_PTR
) data
;
1036 psp
[id
].dwSize
= sizeof (PROPSHEETPAGEW
);
1037 psp
[id
].hInstance
= hcpl
;
1038 psp
[id
].u
.pszTemplate
= MAKEINTRESOURCEW(IDD_FORCEFEEDBACK
);
1039 psp
[id
].pfnDlgProc
= ff_dlgproc
;
1040 psp
[id
].lParam
= (INT_PTR
) data
;
1043 /* Fill out the PROPSHEETHEADER */
1044 psh
.dwSize
= sizeof (PROPSHEETHEADERW
);
1045 psh
.dwFlags
= PSH_PROPSHEETPAGE
| PSH_USEICONID
| PSH_USECALLBACK
;
1046 psh
.hwndParent
= parent
;
1047 psh
.hInstance
= hcpl
;
1048 psh
.pszCaption
= MAKEINTRESOURCEW(IDS_CPL_NAME
);
1051 psh
.pfnCallback
= propsheet_callback
;
1053 /* display the dialog */
1054 PropertySheetW(&psh
);
1057 DeactivateActCtx(0, cookie
);
1058 ReleaseActCtx(context
);
1062 /*********************************************************************
1063 * CPlApplet (joy.cpl.@)
1065 * Control Panel entry point
1068 * hWnd [I] Handle for the Control Panel Window
1069 * command [I] CPL_* Command
1070 * lParam1 [I] first extra Parameter
1071 * lParam2 [I] second extra Parameter
1074 * Depends on the command
1077 LONG CALLBACK
CPlApplet(HWND hwnd
, UINT command
, LPARAM lParam1
, LPARAM lParam2
)
1079 static struct JoystickData data
;
1080 TRACE("(%p, %u, 0x%Ix, 0x%Ix)\n", hwnd
, command
, lParam1
, lParam2
);
1088 /* Initialize dinput */
1089 hr
= DirectInput8Create(GetModuleHandleW(NULL
), DIRECTINPUT_VERSION
, &IID_IDirectInput8W
, (void**)&data
.di
, NULL
);
1093 ERR("Failed to initialize DirectInput: 0x%08lx\n", hr
);
1097 /* Then get all the connected joysticks */
1098 initialize_joysticks(&data
);
1107 CPLINFO
*appletInfo
= (CPLINFO
*) lParam2
;
1109 appletInfo
->idIcon
= ICO_MAIN
;
1110 appletInfo
->idName
= IDS_CPL_NAME
;
1111 appletInfo
->idInfo
= IDS_CPL_INFO
;
1112 appletInfo
->lData
= 0;
1117 display_cpl_sheets(hwnd
, &data
);
1121 destroy_joysticks(&data
);
1123 /* And destroy dinput too */
1124 IDirectInput8_Release(data
.di
);