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, %d, %p)\n", hdll
, reason
, reserved
);
50 case DLL_WINE_PREATTACH
:
51 return FALSE
; /* prefer native version */
53 case DLL_PROCESS_ATTACH
:
54 DisableThreadLibraryCalls(hdll
);
60 /***********************************************************************
61 * enum_callback [internal]
62 * Enumerates, creates and sets the common data format for all the joystick devices.
63 * First time it checks if space for the joysticks was already reserved
64 * and if not, just counts how many there are.
66 static BOOL CALLBACK
enum_callback(const DIDEVICEINSTANCEW
*instance
, void *context
)
68 struct JoystickData
*data
= context
;
69 struct Joystick
*joystick
;
72 if (data
->joysticks
== NULL
)
74 data
->num_joysticks
+= 1;
75 return DIENUM_CONTINUE
;
78 joystick
= &data
->joysticks
[data
->cur_joystick
];
79 data
->cur_joystick
+= 1;
81 IDirectInput8_CreateDevice(data
->di
, &instance
->guidInstance
, &joystick
->device
, NULL
);
82 IDirectInputDevice8_SetDataFormat(joystick
->device
, &c_dfDIJoystick
);
84 joystick
->instance
= *instance
;
86 caps
.dwSize
= sizeof(caps
);
87 IDirectInputDevice8_GetCapabilities(joystick
->device
, &caps
);
89 joystick
->num_buttons
= caps
.dwButtons
;
90 joystick
->num_axes
= caps
.dwAxes
;
92 return DIENUM_CONTINUE
;
95 /***********************************************************************
96 * initialize_joysticks [internal]
98 static void initialize_joysticks(struct JoystickData
*data
)
100 data
->num_joysticks
= 0;
101 data
->cur_joystick
= 0;
102 IDirectInput8_EnumDevices(data
->di
, DI8DEVCLASS_GAMECTRL
, enum_callback
, data
, DIEDFL_ATTACHEDONLY
);
103 data
->joysticks
= HeapAlloc(GetProcessHeap(), 0, sizeof(struct Joystick
) * data
->num_joysticks
);
105 /* Get all the joysticks */
106 IDirectInput8_EnumDevices(data
->di
, DI8DEVCLASS_GAMECTRL
, enum_callback
, data
, DIEDFL_ATTACHEDONLY
);
109 /***********************************************************************
110 * destroy_joysticks [internal]
112 static void destroy_joysticks(struct JoystickData
*data
)
116 for (i
= 0; i
< data
->num_joysticks
; i
++)
118 IDirectInputDevice8_Unacquire(data
->joysticks
[i
].device
);
119 IDirectInputDevice8_Release(data
->joysticks
[i
].device
);
122 HeapFree(GetProcessHeap(), 0, data
->joysticks
);
125 /*********************************************************************
126 * list_dlgproc [internal]
129 static INT_PTR CALLBACK
list_dlgproc(HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
131 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd
, msg
, msg
, lparam
);
137 struct JoystickData
*data
= (struct JoystickData
*) ((PROPSHEETPAGEW
*)lparam
)->lParam
;
139 /* Set dialog information */
140 for (i
= 0; i
< data
->num_joysticks
; i
++)
142 struct Joystick
*joy
= &data
->joysticks
[i
];
143 SendDlgItemMessageW(hwnd
, IDC_JOYSTICKLIST
, LB_ADDSTRING
, 0, (LPARAM
) joy
->instance
.tszInstanceName
);
151 switch (LOWORD(wparam
))
153 case IDC_BUTTONDISABLE
:
154 FIXME("Disable selected joystick from being enumerated\n");
157 case IDC_BUTTONENABLE
:
158 FIXME("Re-Enable selected joystick\n");
173 /*********************************************************************
174 * Joystick testing functions
177 static void dump_joy_state(DIJOYSTATE
* st
, int num_buttons
)
180 TRACE("Ax (% 5d,% 5d,% 5d)\n", st
->lX
,st
->lY
, st
->lZ
);
181 TRACE("RAx (% 5d,% 5d,% 5d)\n", st
->lRx
, st
->lRy
, st
->lRz
);
182 TRACE("Slider (% 5d,% 5d)\n", st
->rglSlider
[0], st
->rglSlider
[1]);
183 TRACE("Pov (% 5d,% 5d,% 5d,% 5d)\n", st
->rgdwPOV
[0], st
->rgdwPOV
[1], st
->rgdwPOV
[2], st
->rgdwPOV
[3]);
186 for(i
=0; i
< num_buttons
; i
++)
187 TRACE(" %c",st
->rgbButtons
[i
] ? 'x' : 'o');
191 static void poll_input(const struct Joystick
*joy
, DIJOYSTATE
*state
)
195 hr
= IDirectInputDevice8_Poll(joy
->device
);
197 /* If it failed, try to acquire the joystick */
200 hr
= IDirectInputDevice8_Acquire(joy
->device
);
201 while (hr
== DIERR_INPUTLOST
) hr
= IDirectInputDevice8_Acquire(joy
->device
);
204 if (hr
== DIERR_OTHERAPPHASPRIO
) return;
206 IDirectInputDevice8_GetDeviceState(joy
->device
, sizeof(DIJOYSTATE
), state
);
209 static DWORD WINAPI
input_thread(void *param
)
211 int axes_pos
[TEST_MAX_AXES
][2];
213 struct JoystickData
*data
= param
;
215 /* Setup POV as clock positions
222 int ma
= TEST_AXIS_MAX
;
223 int pov_val
[9] = {0, 4500, 9000, 13500,
224 18000, 22500, 27000, 31500, -1};
225 int pov_pos
[9][2] = { {0, -ma
}, {ma
/2, -ma
/2}, {ma
, 0}, {ma
/2, ma
/2},
226 {0, ma
}, {-ma
/2, ma
/2}, {-ma
, 0}, {-ma
/2, -ma
/2}, {0, 0} };
228 ZeroMemory(&state
, sizeof(state
));
233 poll_input(&data
->joysticks
[data
->chosen_joystick
], &state
);
235 dump_joy_state(&state
, data
->joysticks
[data
->chosen_joystick
].num_buttons
);
237 /* Indicate pressed buttons */
238 for (i
= 0; i
< data
->joysticks
[data
->chosen_joystick
].num_buttons
; i
++)
239 if (state
.rgbButtons
[i
])
240 SendMessageW(data
->buttons
[i
], BM_SETSTATE
, TRUE
, 0);
242 /* Indicate axis positions, axes showing are hardcoded for now */
243 axes_pos
[0][0] = state
.lX
;
244 axes_pos
[0][1] = state
.lY
;
245 axes_pos
[1][0] = state
.lRx
;
246 axes_pos
[1][1] = state
.lRy
;
247 axes_pos
[2][0] = state
.lZ
;
248 axes_pos
[2][1] = state
.lRz
;
251 for (i
= 0; i
< sizeof(pov_val
)/sizeof(pov_val
[0]); i
++)
253 if (state
.rgdwPOV
[0] == pov_val
[i
])
255 axes_pos
[3][0] = pov_pos
[i
][0];
256 axes_pos
[3][1] = pov_pos
[i
][1];
260 for (i
= 0; i
< TEST_MAX_AXES
; i
++)
261 SetWindowPos(data
->axes
[i
], 0,
262 TEST_AXIS_X
+ TEST_NEXT_AXIS_X
*i
+ axes_pos
[i
][0],
263 TEST_AXIS_Y
+ axes_pos
[i
][1],
264 0, 0, SWP_NOZORDER
| SWP_NOSIZE
);
266 Sleep(TEST_POLL_TIME
);
268 /* Reset button state */
269 for (i
= 0; i
< data
->joysticks
[data
->chosen_joystick
].num_buttons
; i
++)
270 SendMessageW(data
->buttons
[i
], BM_SETSTATE
, FALSE
, 0);
276 static void test_handle_joychange(HWND hwnd
, struct JoystickData
*data
)
280 if (data
->num_joysticks
== 0) return;
282 data
->chosen_joystick
= SendDlgItemMessageW(hwnd
, IDC_TESTSELECTCOMBO
, CB_GETCURSEL
, 0, 0);
284 /* Enable only buttons present in the device */
285 for (i
= 0; i
< TEST_MAX_BUTTONS
; i
++)
286 ShowWindow(data
->buttons
[i
], i
<= data
->joysticks
[data
->chosen_joystick
].num_buttons
);
289 /*********************************************************************
290 * button_number_to_wchar [internal]
291 * Transforms an integer in the interval [0,99] into a 2 character WCHAR string
293 static void button_number_to_wchar(int n
, WCHAR str
[3])
295 str
[1] = n
% 10 + '0';
297 str
[0] = n
% 10 + '0';
301 static void draw_joystick_buttons(HWND hwnd
, struct JoystickData
* data
)
304 int row
= 0, col
= 0;
305 WCHAR button_label
[3];
306 HINSTANCE hinst
= (HINSTANCE
) GetWindowLongPtrW(hwnd
, GWLP_HINSTANCE
);
307 static WCHAR button_class
[] = {'B','u','t','t','o','n','\0'};
309 for (i
= 0; i
< TEST_MAX_BUTTONS
; i
++)
311 if ((i
% TEST_BUTTON_COL_MAX
) == 0 && i
!= 0)
317 button_number_to_wchar(i
+ 1, button_label
);
319 data
->buttons
[i
] = CreateWindowW(button_class
, button_label
, WS_CHILD
,
320 TEST_BUTTON_X
+ TEST_NEXT_BUTTON_X
*col
, TEST_BUTTON_Y
+ TEST_NEXT_BUTTON_Y
*row
,
321 TEST_BUTTON_SIZE_X
, TEST_BUTTON_SIZE_Y
,
322 hwnd
, NULL
, NULL
, hinst
);
328 static void draw_joystick_axes(HWND hwnd
, struct JoystickData
* data
)
331 struct Joystick
*joy
;
332 DIPROPRANGE propRange
;
333 HINSTANCE hinst
= (HINSTANCE
) GetWindowLongPtrW(hwnd
, GWLP_HINSTANCE
);
334 static const WCHAR button_class
[] = {'B','u','t','t','o','n','\0'};
335 static const WCHAR axes_names
[TEST_MAX_AXES
][7] = { {'X',',','Y','\0'}, {'R','x',',','R','y','\0'},
336 {'Z',',','R','z','\0'}, {'P','O','V','\0'} };
337 static const DWORD axes_idc
[TEST_MAX_AXES
] = { IDC_TESTGROUPXY
, IDC_TESTGROUPRXRY
,
338 IDC_TESTGROUPZRZ
, IDC_TESTGROUPPOV
};
340 /* Set axis range to ease the GUI visualization */
341 for (i
= 0; i
< data
->num_joysticks
; i
++)
343 joy
= &data
->joysticks
[i
];
344 propRange
.diph
.dwSize
= sizeof(DIPROPRANGE
);
345 propRange
.diph
.dwHeaderSize
= sizeof(DIPROPHEADER
);
346 propRange
.diph
.dwHow
= DIPH_DEVICE
;
347 propRange
.diph
.dwObj
= 0;
348 propRange
.lMin
= TEST_AXIS_MIN
;
349 propRange
.lMax
= TEST_AXIS_MAX
;
351 IDirectInputDevice_SetProperty(joy
->device
, DIPROP_RANGE
, &propRange
.diph
);
354 for (i
= 0; i
< TEST_MAX_AXES
; i
++)
356 /* Set axis box name */
357 SetWindowTextW(GetDlgItem(hwnd
, axes_idc
[i
]), axes_names
[i
]);
359 data
->axes
[i
] = CreateWindowW( button_class
, NULL
, WS_CHILD
| WS_VISIBLE
,
360 TEST_AXIS_X
+ TEST_NEXT_AXIS_X
*i
, TEST_AXIS_Y
,
361 TEST_AXIS_SIZE_X
, TEST_AXIS_SIZE_Y
,
362 hwnd
, (HMENU
) IDC_JOYSTICKAXES
+ i
, NULL
, hinst
);
366 /*********************************************************************
367 * test_dlgproc [internal]
370 static INT_PTR CALLBACK
test_dlgproc(HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
372 static HANDLE thread
;
373 static struct JoystickData
*data
;
374 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd
, msg
, msg
, lparam
);
382 data
= (struct JoystickData
*) ((PROPSHEETPAGEW
*)lparam
)->lParam
;
384 /* Add enumerated joysticks to the combobox */
385 for (i
= 0; i
< data
->num_joysticks
; i
++)
387 struct Joystick
*joy
= &data
->joysticks
[i
];
388 SendDlgItemMessageW(hwnd
, IDC_TESTSELECTCOMBO
, CB_ADDSTRING
, 0, (LPARAM
) joy
->instance
.tszInstanceName
);
391 draw_joystick_buttons(hwnd
, data
);
392 draw_joystick_axes(hwnd
, data
);
400 case MAKEWPARAM(IDC_TESTSELECTCOMBO
, CBN_SELCHANGE
):
401 test_handle_joychange(hwnd
, data
);
407 switch(((LPNMHDR
)lparam
)->code
)
413 /* Initialize input thread */
414 if (data
->num_joysticks
> 0)
418 /* Set the first joystick as default */
419 SendDlgItemMessageW(hwnd
, IDC_TESTSELECTCOMBO
, CB_SETCURSEL
, 0, 0);
420 test_handle_joychange(hwnd
, data
);
422 thread
= CreateThread(NULL
, 0, input_thread
, (void*) data
, 0, &tid
);
428 /* Stop input thread */
438 /******************************************************************************
439 * propsheet_callback [internal]
441 static int CALLBACK
propsheet_callback(HWND hwnd
, UINT msg
, LPARAM lparam
)
443 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd
, msg
, msg
, lparam
);
446 case PSCB_INITIALIZED
:
452 /******************************************************************************
453 * display_cpl_sheets [internal]
455 * Build and display the dialog with all control panel propertysheets
458 static void display_cpl_sheets(HWND parent
, struct JoystickData
*data
)
460 INITCOMMONCONTROLSEX icex
;
461 PROPSHEETPAGEW psp
[NUM_PROPERTY_PAGES
];
462 PROPSHEETHEADERW psh
;
466 /* Initialize common controls */
467 icex
.dwSize
= sizeof(INITCOMMONCONTROLSEX
);
468 icex
.dwICC
= ICC_LISTVIEW_CLASSES
| ICC_BAR_CLASSES
;
469 InitCommonControlsEx(&icex
);
471 ZeroMemory(&psh
, sizeof(psh
));
472 ZeroMemory(psp
, sizeof(psp
));
474 /* Fill out all PROPSHEETPAGE */
475 psp
[id
].dwSize
= sizeof (PROPSHEETPAGEW
);
476 psp
[id
].hInstance
= hcpl
;
477 psp
[id
].u
.pszTemplate
= MAKEINTRESOURCEW(IDD_LIST
);
478 psp
[id
].pfnDlgProc
= list_dlgproc
;
479 psp
[id
].lParam
= (INT_PTR
) data
;
482 psp
[id
].dwSize
= sizeof (PROPSHEETPAGEW
);
483 psp
[id
].hInstance
= hcpl
;
484 psp
[id
].u
.pszTemplate
= MAKEINTRESOURCEW(IDD_TEST
);
485 psp
[id
].pfnDlgProc
= test_dlgproc
;
486 psp
[id
].lParam
= (INT_PTR
) data
;
489 psp
[id
].dwSize
= sizeof (PROPSHEETPAGEW
);
490 psp
[id
].hInstance
= hcpl
;
491 psp
[id
].u
.pszTemplate
= MAKEINTRESOURCEW(IDD_FORCEFEEDBACK
);
492 psp
[id
].pfnDlgProc
= NULL
;
493 psp
[id
].lParam
= (INT_PTR
) data
;
496 /* Fill out the PROPSHEETHEADER */
497 psh
.dwSize
= sizeof (PROPSHEETHEADERW
);
498 psh
.dwFlags
= PSH_PROPSHEETPAGE
| PSH_USEICONID
| PSH_USECALLBACK
;
499 psh
.hwndParent
= parent
;
500 psh
.hInstance
= hcpl
;
501 psh
.pszCaption
= MAKEINTRESOURCEW(IDS_CPL_NAME
);
504 psh
.pfnCallback
= propsheet_callback
;
506 /* display the dialog */
507 PropertySheetW(&psh
);
512 /*********************************************************************
513 * CPlApplet (joy.cpl.@)
515 * Control Panel entry point
518 * hWnd [I] Handle for the Control Panel Window
519 * command [I] CPL_* Command
520 * lParam1 [I] first extra Parameter
521 * lParam2 [I] second extra Parameter
524 * Depends on the command
527 LONG CALLBACK
CPlApplet(HWND hwnd
, UINT command
, LPARAM lParam1
, LPARAM lParam2
)
529 static struct JoystickData data
;
530 TRACE("(%p, %u, 0x%lx, 0x%lx)\n", hwnd
, command
, lParam1
, lParam2
);
538 /* Initialize dinput */
539 hr
= DirectInput8Create(GetModuleHandleW(NULL
), DIRECTINPUT_VERSION
, &IID_IDirectInput8W
, (void**)&data
.di
, NULL
);
543 ERR("Failed to initialize DirectInput: 0x%08x\n", hr
);
547 /* Then get all the connected joysticks */
548 initialize_joysticks(&data
);
557 CPLINFO
*appletInfo
= (CPLINFO
*) lParam2
;
559 appletInfo
->idName
= IDS_CPL_NAME
;
560 appletInfo
->idInfo
= IDS_CPL_INFO
;
561 appletInfo
->lData
= 0;
566 display_cpl_sheets(hwnd
, &data
);
570 destroy_joysticks(&data
);
572 /* And destroy dinput too */
573 IDirectInput8_Release(data
.di
);