maintainers: Update the Direct3D section.
[wine.git] / dlls / joy.cpl / main.c
blobc1f41fb0e56add2c071988dfe70c1e89676d555a
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, %ld, %p)\n", hdll, reason, reserved);
48 switch (reason)
50 case DLL_PROCESS_ATTACH:
51 DisableThreadLibraryCalls(hdll);
52 hcpl = hdll;
54 return TRUE;
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 =
68 .diph =
70 .dwSize = sizeof(DIPROPGUIDANDPATH),
71 .dwHeaderSize = sizeof(DIPROPHEADER),
72 .dwHow = DIPH_DEVICE,
75 struct JoystickData *data = context;
76 struct Joystick *joystick;
77 DIPROPRANGE proprange;
78 DIDEVCAPS caps;
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)
151 int i, j;
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)
179 *appkey = 0;
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))
184 *defkey = 0;
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)
195 if (value == NULL)
197 if (appkey && !RegDeleteValueW(appkey, name))
198 return 0;
200 if (defkey && !RegDeleteValueW(defkey, name))
201 return 0;
203 else
205 if (appkey && !RegSetValueExW(appkey, name, 0, REG_SZ, (const BYTE*) value, (size + 1)*sizeof(WCHAR)))
206 return 0;
208 if (defkey && !RegSetValueExW(defkey, name, 0, REG_SZ, (const BYTE*) value, (size + 1)*sizeof(WCHAR)))
209 return 0;
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)
222 HKEY hkey, appkey;
224 get_app_key(&hkey, &appkey);
226 if (!enable)
227 set_config_key(hkey, appkey, joy_name, L"disabled", wcslen(L"disabled"));
228 else
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;
238 HKEY hkey, appkey;
239 DWORD values = 0;
240 LSTATUS status;
241 DWORD i;
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)
277 HKEY hkey, appkey;
279 get_app_key(&hkey, &appkey);
281 if (override)
282 set_config_key(hkey, appkey, joy_name, L"override", wcslen(L"override"));
283 else
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;
298 int sel;
300 TRACE("(%p, 0x%08x/%d, 0x%Ix)\n", hwnd, msg, msg, lparam);
301 switch (msg)
303 case WM_INITDIALOG:
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;
317 return TRUE;
320 case WM_COMMAND:
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);
337 break;
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);
350 break;
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);
361 break;
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);
372 break;
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);
381 break;
383 case IDC_XINPUTLIST:
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);
390 break;
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);
399 break;
402 return TRUE;
404 case WM_NOTIFY:
405 return TRUE;
407 default:
408 break;
410 return FALSE;
413 /*********************************************************************
414 * Joystick testing functions
417 static void dump_joy_state(DIJOYSTATE* st)
419 int i;
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]);
425 TRACE("Buttons ");
426 for(i=0; i < TEST_MAX_BUTTONS; i++)
427 TRACE(" %c",st->rgbButtons[i] ? 'x' : 'o');
428 TRACE("\n");
431 static void poll_input(const struct Joystick *joy, DIJOYSTATE *state)
433 HRESULT hr;
435 hr = IDirectInputDevice8_Poll(joy->device);
437 /* If it failed, try to acquire the joystick */
438 if (FAILED(hr))
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];
452 DIJOYSTATE state;
453 struct JoystickData *data = param;
455 /* Setup POV as clock positions
457 * 31500 4500
458 * 27000 -1 9000
459 * 22500 13500
460 * 18000
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));
470 while (!data->stop)
472 int i;
473 unsigned int j;
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;
491 /* Set pov values */
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++)
503 RECT r;
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);
516 return 0;
519 static void test_handle_joychange(HWND hwnd, struct JoystickData *data)
521 int i;
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';
539 n /= 10;
540 str[0] = n % 10 + '0';
541 str[2] = '\0';
544 static void draw_joystick_buttons(HWND hwnd, struct JoystickData* data)
546 int i;
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++)
553 RECT r;
555 if ((i % TEST_BUTTON_COL_MAX) == 0 && i != 0)
557 row += 1;
558 col = 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);
573 col += 1;
577 static void draw_joystick_axes(HWND hwnd, struct JoystickData* data)
579 int i;
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++)
587 RECT r;
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);
592 r.top = TEST_AXIS_Y;
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);
621 switch (msg)
623 case WM_INITDIALOG:
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);
631 return TRUE;
634 case WM_COMMAND:
635 switch(wparam)
637 case MAKEWPARAM(IDC_TESTSELECTCOMBO, CBN_SELCHANGE):
638 test_handle_joychange(hwnd, data);
639 break;
641 return TRUE;
643 case WM_NOTIFY:
644 switch(((LPNMHDR)lparam)->code)
646 case PSN_SETACTIVE:
648 DWORD tid;
650 refresh_test_joystick_list(hwnd, data);
652 /* Initialize input thread */
653 if (data->num_joysticks > 0)
655 data->stop = FALSE;
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);
664 break;
666 case PSN_RESET: /* intentional fall-through */
667 case PSN_KILLACTIVE:
668 /* Stop input thread */
669 data->stop = TRUE;
670 MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, 0);
671 CloseHandle(thread);
672 break;
674 return TRUE;
676 return FALSE;
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);
686 RECT r;
688 r.left = FF_AXIS_X;
689 r.top = FF_AXIS_Y;
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)
702 int i;
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);
726 if (sel < 0) return;
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;
737 DIJOYSTATE state;
739 ZeroMemory(&state, sizeof(state));
741 while (!data->stop)
743 int i;
744 struct Joystick *joy = &data->joysticks[data->chosen_joystick];
745 int chosen_effect = joy->chosen_effect;
746 DIEFFECT *dieffect;
747 DWORD flags = DIEP_AXES | DIEP_DIRECTION | DIEP_NORESTART;
748 RECT r;
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);
774 break;
778 return 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)
789 HRESULT hr;
790 DIEFFECT dieffect;
791 DWORD axes[2] = {DIJOFS_X, DIJOFS_Y};
792 LONG direction[2] = {0, 0};
793 int num_axes = 2;
794 struct Joystick *joystick = pvRef;
795 DIRAMPFORCE rforce;
796 DICONSTANTFORCE cforce;
797 DIPERIODIC pforce;
798 DICONDITION cdforce;
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))
822 rforce.lStart = 0;
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;
844 pforce.lOffset = 0;
845 pforce.dwPhase = 0;
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;
862 cdforce.lOffset = 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);
877 if (FAILED(hr))
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);
908 switch (msg)
910 case WM_INITDIALOG:
912 data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
914 refresh_ff_joystick_list(hwnd, data);
915 draw_ff_axis(hwnd, data);
917 return TRUE;
920 case WM_COMMAND:
921 switch(wparam)
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]);
928 break;
930 case MAKEWPARAM(IDC_FFEFFECTLIST, LBN_SELCHANGE):
931 ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
932 break;
934 return TRUE;
936 case WM_NOTIFY:
937 switch(((LPNMHDR)lparam)->code)
939 case PSN_SETACTIVE:
940 refresh_ff_joystick_list(hwnd, data);
942 if (data->num_ff > 0)
944 DWORD tid;
946 data->stop = FALSE;
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);
956 break;
958 case PSN_RESET: /* intentional fall-through */
959 case PSN_KILLACTIVE:
960 /* Stop ff thread */
961 data->stop = TRUE;
962 MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, 0);
963 CloseHandle(thread);
964 break;
966 return TRUE;
968 return FALSE;
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);
977 switch (msg)
979 case PSCB_INITIALIZED:
980 break;
982 return 0;
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;
997 ULONG_PTR cookie;
998 ACTCTXW actctx;
999 HANDLE context;
1000 DWORD id = 0;
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;
1027 id++;
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;
1034 id++;
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;
1041 id++;
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);
1049 psh.nPages = id;
1050 psh.u3.ppsp = psp;
1051 psh.pfnCallback = propsheet_callback;
1053 /* display the dialog */
1054 PropertySheetW(&psh);
1056 if (activated)
1057 DeactivateActCtx(0, cookie);
1058 ReleaseActCtx(context);
1059 OleUninitialize();
1062 /*********************************************************************
1063 * CPlApplet (joy.cpl.@)
1065 * Control Panel entry point
1067 * PARAMS
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
1073 * RETURNS
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);
1082 switch (command)
1084 case CPL_INIT:
1086 HRESULT hr;
1088 /* Initialize dinput */
1089 hr = DirectInput8Create(GetModuleHandleW(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void**)&data.di, NULL);
1091 if (FAILED(hr))
1093 ERR("Failed to initialize DirectInput: 0x%08lx\n", hr);
1094 return FALSE;
1097 /* Then get all the connected joysticks */
1098 initialize_joysticks(&data);
1100 return TRUE;
1102 case CPL_GETCOUNT:
1103 return 1;
1105 case CPL_INQUIRE:
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;
1113 return TRUE;
1116 case CPL_DBLCLK:
1117 display_cpl_sheets(hwnd, &data);
1118 break;
1120 case CPL_STOP:
1121 destroy_joysticks(&data);
1123 /* And destroy dinput too */
1124 IDirectInput8_Release(data.di);
1125 break;
1128 return FALSE;