joy.cpl: Correct joystick testing thread behavior.
[wine.git] / dlls / joy.cpl / main.c
blobe9b3a720e4335af2d607cb4c3a9d5529ceb9c847
1 /*
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
23 #define COBJMACROS
24 #define CONST_VTABLE
26 #include <stdarg.h>
27 #include <windef.h>
28 #include <winbase.h>
29 #include <winuser.h>
30 #include <commctrl.h>
31 #include <cpl.h>
32 #include "ole2.h"
34 #include "wine/debug.h"
35 #include "joy.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(joycpl);
39 DECLSPEC_HIDDEN HMODULE hcpl;
41 /*********************************************************************
42 * DllMain
44 BOOL WINAPI DllMain(HINSTANCE hdll, DWORD reason, LPVOID reserved)
46 TRACE("(%p, %d, %p)\n", hdll, reason, reserved);
48 switch (reason)
50 case DLL_WINE_PREATTACH:
51 return FALSE; /* prefer native version */
53 case DLL_PROCESS_ATTACH:
54 DisableThreadLibraryCalls(hdll);
55 hcpl = hdll;
57 return TRUE;
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;
70 DIDEVCAPS caps;
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)
114 int i;
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);
132 switch (msg)
134 case WM_INITDIALOG:
136 int i;
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);
146 return TRUE;
149 case WM_COMMAND:
151 switch (LOWORD(wparam))
153 case IDC_BUTTONDISABLE:
154 FIXME("Disable selected joystick from being enumerated\n");
155 break;
157 case IDC_BUTTONENABLE:
158 FIXME("Re-Enable selected joystick\n");
159 break;
162 return TRUE;
164 case WM_NOTIFY:
165 return TRUE;
167 default:
168 break;
170 return FALSE;
173 /*********************************************************************
174 * Joystick testing functions
177 static void dump_joy_state(DIJOYSTATE* st, int num_buttons)
179 int i;
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]);
185 TRACE("Buttons ");
186 for(i=0; i < num_buttons; i++)
187 TRACE(" %c",st->rgbButtons[i] ? 'x' : 'o');
188 TRACE("\n");
191 static void poll_input(const struct Joystick *joy, DIJOYSTATE *state)
193 HRESULT hr;
195 hr = IDirectInputDevice8_Poll(joy->device);
197 /* If it failed, try to acquire the joystick */
198 if (FAILED(hr))
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];
212 DIJOYSTATE state;
213 struct JoystickData *data = param;
215 /* Setup POV as clock positions
217 * 31500 4500
218 * 27000 -1 9000
219 * 22500 13500
220 * 18000
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));
230 while (!data->stop)
232 int i;
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;
250 /* Set pov values */
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);
273 return 0;
276 static void test_handle_joychange(HWND hwnd, struct JoystickData *data)
278 int i;
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';
296 n /= 10;
297 str[0] = n % 10 + '0';
298 str[2] = '\0';
301 static void draw_joystick_buttons(HWND hwnd, struct JoystickData* data)
303 int i;
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)
313 row += 1;
314 col = 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);
324 col += 1;
328 static void draw_joystick_axes(HWND hwnd, struct JoystickData* data)
330 int i;
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);
376 switch (msg)
378 case WM_INITDIALOG:
380 int i;
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);
394 return TRUE;
397 case WM_COMMAND:
398 switch(wparam)
400 case MAKEWPARAM(IDC_TESTSELECTCOMBO, CBN_SELCHANGE):
401 test_handle_joychange(hwnd, data);
402 break;
404 return TRUE;
406 case WM_NOTIFY:
407 switch(((LPNMHDR)lparam)->code)
409 case PSN_SETACTIVE:
411 DWORD tid;
413 /* Initialize input thread */
414 if (data->num_joysticks > 0)
416 data->stop = FALSE;
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);
425 break;
427 case PSN_RESET: /* intentional fall-through */
428 case PSN_KILLACTIVE:
429 /* Stop input thread */
430 data->stop = TRUE;
431 MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, 0);
432 CloseHandle(thread);
433 break;
435 return TRUE;
437 return FALSE;
440 /******************************************************************************
441 * propsheet_callback [internal]
443 static int CALLBACK propsheet_callback(HWND hwnd, UINT msg, LPARAM lparam)
445 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
446 switch (msg)
448 case PSCB_INITIALIZED:
449 break;
451 return 0;
454 /******************************************************************************
455 * display_cpl_sheets [internal]
457 * Build and display the dialog with all control panel propertysheets
460 static void display_cpl_sheets(HWND parent, struct JoystickData *data)
462 INITCOMMONCONTROLSEX icex;
463 PROPSHEETPAGEW psp[NUM_PROPERTY_PAGES];
464 PROPSHEETHEADERW psh;
465 DWORD id = 0;
467 OleInitialize(NULL);
468 /* Initialize common controls */
469 icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
470 icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES;
471 InitCommonControlsEx(&icex);
473 ZeroMemory(&psh, sizeof(psh));
474 ZeroMemory(psp, sizeof(psp));
476 /* Fill out all PROPSHEETPAGE */
477 psp[id].dwSize = sizeof (PROPSHEETPAGEW);
478 psp[id].hInstance = hcpl;
479 psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_LIST);
480 psp[id].pfnDlgProc = list_dlgproc;
481 psp[id].lParam = (INT_PTR) data;
482 id++;
484 psp[id].dwSize = sizeof (PROPSHEETPAGEW);
485 psp[id].hInstance = hcpl;
486 psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_TEST);
487 psp[id].pfnDlgProc = test_dlgproc;
488 psp[id].lParam = (INT_PTR) data;
489 id++;
491 psp[id].dwSize = sizeof (PROPSHEETPAGEW);
492 psp[id].hInstance = hcpl;
493 psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_FORCEFEEDBACK);
494 psp[id].pfnDlgProc = NULL;
495 psp[id].lParam = (INT_PTR) data;
496 id++;
498 /* Fill out the PROPSHEETHEADER */
499 psh.dwSize = sizeof (PROPSHEETHEADERW);
500 psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID | PSH_USECALLBACK;
501 psh.hwndParent = parent;
502 psh.hInstance = hcpl;
503 psh.pszCaption = MAKEINTRESOURCEW(IDS_CPL_NAME);
504 psh.nPages = id;
505 psh.u3.ppsp = psp;
506 psh.pfnCallback = propsheet_callback;
508 /* display the dialog */
509 PropertySheetW(&psh);
511 OleUninitialize();
514 /*********************************************************************
515 * CPlApplet (joy.cpl.@)
517 * Control Panel entry point
519 * PARAMS
520 * hWnd [I] Handle for the Control Panel Window
521 * command [I] CPL_* Command
522 * lParam1 [I] first extra Parameter
523 * lParam2 [I] second extra Parameter
525 * RETURNS
526 * Depends on the command
529 LONG CALLBACK CPlApplet(HWND hwnd, UINT command, LPARAM lParam1, LPARAM lParam2)
531 static struct JoystickData data;
532 TRACE("(%p, %u, 0x%lx, 0x%lx)\n", hwnd, command, lParam1, lParam2);
534 switch (command)
536 case CPL_INIT:
538 HRESULT hr;
540 /* Initialize dinput */
541 hr = DirectInput8Create(GetModuleHandleW(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void**)&data.di, NULL);
543 if (FAILED(hr))
545 ERR("Failed to initialize DirectInput: 0x%08x\n", hr);
546 return FALSE;
549 /* Then get all the connected joysticks */
550 initialize_joysticks(&data);
552 return TRUE;
554 case CPL_GETCOUNT:
555 return 1;
557 case CPL_INQUIRE:
559 CPLINFO *appletInfo = (CPLINFO *) lParam2;
561 appletInfo->idName = IDS_CPL_NAME;
562 appletInfo->idInfo = IDS_CPL_INFO;
563 appletInfo->lData = 0;
564 return TRUE;
567 case CPL_DBLCLK:
568 display_cpl_sheets(hwnd, &data);
569 break;
571 case CPL_STOP:
572 destroy_joysticks(&data);
574 /* And destroy dinput too */
575 IDirectInput8_Release(data.di);
576 break;
579 return FALSE;