msvcrt: Don't call invalid parameter handler in str{n,i}cmp functions.
[wine/multimedia.git] / dlls / joy.cpl / main.c
blobdec83ff1933c6d64a04902bdc45664e3c8e5e844
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 "wine/unicode.h"
36 #include "joy.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(joycpl);
40 DECLSPEC_HIDDEN HMODULE hcpl;
42 /*********************************************************************
43 * DllMain
45 BOOL WINAPI DllMain(HINSTANCE hdll, DWORD reason, LPVOID reserved)
47 TRACE("(%p, %d, %p)\n", hdll, reason, reserved);
49 switch (reason)
51 case DLL_WINE_PREATTACH:
52 return FALSE; /* prefer native version */
54 case DLL_PROCESS_ATTACH:
55 DisableThreadLibraryCalls(hdll);
56 hcpl = hdll;
58 return TRUE;
61 /***********************************************************************
62 * enum_callback [internal]
63 * Enumerates, creates and sets the common data format for all the joystick devices.
64 * First time it checks if space for the joysticks was already reserved
65 * and if not, just counts how many there are.
67 static BOOL CALLBACK enum_callback(const DIDEVICEINSTANCEW *instance, void *context)
69 struct JoystickData *data = context;
70 struct Joystick *joystick;
71 DIPROPRANGE proprange;
72 DIDEVCAPS caps;
74 if (data->joysticks == NULL)
76 data->num_joysticks += 1;
77 return DIENUM_CONTINUE;
80 joystick = &data->joysticks[data->cur_joystick];
81 data->cur_joystick += 1;
83 IDirectInput8_CreateDevice(data->di, &instance->guidInstance, &joystick->device, NULL);
84 IDirectInputDevice8_SetDataFormat(joystick->device, &c_dfDIJoystick);
86 joystick->instance = *instance;
88 caps.dwSize = sizeof(caps);
89 IDirectInputDevice8_GetCapabilities(joystick->device, &caps);
91 joystick->num_buttons = caps.dwButtons;
92 joystick->num_axes = caps.dwAxes;
93 joystick->forcefeedback = caps.dwFlags & DIDC_FORCEFEEDBACK;
94 joystick->num_effects = 0;
96 if (joystick->forcefeedback) data->num_ff++;
98 /* Set axis range to ease the GUI visualization */
99 proprange.diph.dwSize = sizeof(DIPROPRANGE);
100 proprange.diph.dwHeaderSize = sizeof(DIPROPHEADER);
101 proprange.diph.dwHow = DIPH_DEVICE;
102 proprange.diph.dwObj = 0;
103 proprange.lMin = TEST_AXIS_MIN;
104 proprange.lMax = TEST_AXIS_MAX;
106 IDirectInputDevice_SetProperty(joystick->device, DIPROP_RANGE, &proprange.diph);
108 return DIENUM_CONTINUE;
111 /***********************************************************************
112 * initialize_joysticks [internal]
114 static void initialize_joysticks(struct JoystickData *data)
116 data->num_joysticks = 0;
117 data->cur_joystick = 0;
118 IDirectInput8_EnumDevices(data->di, DI8DEVCLASS_GAMECTRL, enum_callback, data, DIEDFL_ATTACHEDONLY);
119 data->joysticks = HeapAlloc(GetProcessHeap(), 0, sizeof(struct Joystick) * data->num_joysticks);
121 /* Get all the joysticks */
122 IDirectInput8_EnumDevices(data->di, DI8DEVCLASS_GAMECTRL, enum_callback, data, DIEDFL_ATTACHEDONLY);
125 /***********************************************************************
126 * destroy_joysticks [internal]
128 static void destroy_joysticks(struct JoystickData *data)
130 int i, j;
132 for (i = 0; i < data->num_joysticks; i++)
135 if (data->joysticks[i].forcefeedback && data->joysticks[i].num_effects > 0)
137 for (j = 0; j < data->joysticks[i].num_effects; j++)
138 IDirectInputEffect_Release(data->joysticks[i].effects[j].effect);
140 HeapFree(GetProcessHeap(), 0, data->joysticks[i].effects);
143 IDirectInputDevice8_Unacquire(data->joysticks[i].device);
144 IDirectInputDevice8_Release(data->joysticks[i].device);
147 HeapFree(GetProcessHeap(), 0, data->joysticks);
150 static void initialize_joysticks_list(HWND hwnd, struct JoystickData *data)
152 int i;
154 SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_RESETCONTENT, 0, 0);
156 /* Add enumerated joysticks */
157 for (i = 0; i < data->num_joysticks; i++)
159 struct Joystick *joy = &data->joysticks[i];
160 SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
164 /******************************************************************************
165 * get_app_key [internal]
166 * Get the default DirectInput key and the selected app config key.
168 static BOOL get_app_key(HKEY *defkey, HKEY *appkey)
170 static const WCHAR reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
171 'W','i','n','e','\\',
172 'D','i','r','e','c','t','I','n','p','u','t','\\',
173 'J','o','y','s','t','i','c','k','s','\0' };
174 *appkey = 0;
176 /* Registry key can be found in HKCU\Software\Wine\DirectInput */
177 if (RegCreateKeyExW(HKEY_CURRENT_USER, reg_key, 0, NULL, 0, KEY_SET_VALUE | KEY_READ, NULL, defkey, NULL))
178 *defkey = 0;
180 return *defkey || *appkey;
183 /******************************************************************************
184 * set_config_key [internal]
185 * Writes a string value to a registry key, deletes the key if value == NULL
187 static DWORD set_config_key(HKEY defkey, HKEY appkey, const WCHAR *name, const WCHAR *value, DWORD size)
189 if (value == NULL)
191 if (appkey && !RegDeleteValueW(appkey, name))
192 return 0;
194 if (defkey && !RegDeleteValueW(defkey, name))
195 return 0;
197 else
199 if (appkey && !RegSetValueExW(appkey, name, 0, REG_SZ, (const BYTE*) value, (size + 1)*sizeof(WCHAR)))
200 return 0;
202 if (defkey && !RegSetValueExW(defkey, name, 0, REG_SZ, (const BYTE*) value, (size + 1)*sizeof(WCHAR)))
203 return 0;
206 return ERROR_FILE_NOT_FOUND;
209 /******************************************************************************
210 * enable_joystick [internal]
211 * Writes to the DirectInput registry key that enables/disables a joystick
212 * from being enumerated.
214 static void enable_joystick(WCHAR *joy_name, BOOL enable)
216 static const WCHAR disabled_str[] = {'d','i','s','a','b','l','e','d','\0'};
217 HKEY hkey, appkey;
219 get_app_key(&hkey, &appkey);
221 if (!enable)
222 set_config_key(hkey, appkey, joy_name, disabled_str, lstrlenW(disabled_str));
223 else
224 set_config_key(hkey, appkey, joy_name, NULL, 0);
226 if (hkey) RegCloseKey(hkey);
227 if (appkey) RegCloseKey(appkey);
230 static void initialize_disabled_joysticks_list(HWND hwnd)
232 static const WCHAR disabled_str[] = {'d','i','s','a','b','l','e','d','\0'};
233 HKEY hkey, appkey;
234 DWORD values = 0;
235 HRESULT hr;
236 int i;
238 SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_RESETCONTENT, 0, 0);
240 /* Search for disabled joysticks */
241 get_app_key(&hkey, &appkey);
242 RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL, &values, NULL, NULL, NULL, NULL);
244 for (i=0; i < values; i++)
246 DWORD name_len = MAX_PATH, data_len = MAX_PATH;
247 WCHAR buf_name[MAX_PATH + 9], buf_data[MAX_PATH];
249 hr = RegEnumValueW(hkey, i, buf_name, &name_len, NULL, NULL, (BYTE*) buf_data, &data_len);
251 if (SUCCEEDED(hr) && !lstrcmpW(disabled_str, buf_data))
252 SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_ADDSTRING, 0, (LPARAM) buf_name);
255 if (hkey) RegCloseKey(hkey);
256 if (appkey) RegCloseKey(appkey);
259 /*********************************************************************
260 * list_dlgproc [internal]
263 static INT_PTR CALLBACK list_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
265 static struct JoystickData *data;
266 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
267 switch (msg)
269 case WM_INITDIALOG:
271 data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
273 initialize_joysticks_list(hwnd, data);
274 initialize_disabled_joysticks_list(hwnd);
276 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONENABLE), FALSE);
277 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONDISABLE), FALSE);
279 /* Store the hwnd to be used with MapDialogRect for unit conversions */
280 data->graphics.hwnd = hwnd;
282 return TRUE;
285 case WM_COMMAND:
287 switch (LOWORD(wparam))
289 case IDC_BUTTONDISABLE:
291 int sel = SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_GETCURSEL, 0, 0);
293 if (sel >= 0)
295 enable_joystick(data->joysticks[sel].instance.tszInstanceName, FALSE);
296 initialize_disabled_joysticks_list(hwnd);
299 break;
301 case IDC_BUTTONENABLE:
303 int sel = SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_GETCURSEL, 0, 0);
305 if (sel >= 0)
307 WCHAR text[MAX_PATH];
308 SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_GETTEXT, sel, (LPARAM) text);
309 enable_joystick(text, TRUE);
310 initialize_disabled_joysticks_list(hwnd);
313 break;
315 case IDC_JOYSTICKLIST:
316 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONENABLE), FALSE);
317 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONDISABLE), TRUE);
318 break;
320 case IDC_DISABLEDLIST:
321 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONENABLE), TRUE);
322 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONDISABLE), FALSE);
323 break;
326 return TRUE;
328 case WM_NOTIFY:
329 return TRUE;
331 default:
332 break;
334 return FALSE;
337 /*********************************************************************
338 * Joystick testing functions
341 static void dump_joy_state(DIJOYSTATE* st, int num_buttons)
343 int i;
344 TRACE("Ax (% 5d,% 5d,% 5d)\n", st->lX,st->lY, st->lZ);
345 TRACE("RAx (% 5d,% 5d,% 5d)\n", st->lRx, st->lRy, st->lRz);
346 TRACE("Slider (% 5d,% 5d)\n", st->rglSlider[0], st->rglSlider[1]);
347 TRACE("Pov (% 5d,% 5d,% 5d,% 5d)\n", st->rgdwPOV[0], st->rgdwPOV[1], st->rgdwPOV[2], st->rgdwPOV[3]);
349 TRACE("Buttons ");
350 for(i=0; i < num_buttons; i++)
351 TRACE(" %c",st->rgbButtons[i] ? 'x' : 'o');
352 TRACE("\n");
355 static void poll_input(const struct Joystick *joy, DIJOYSTATE *state)
357 HRESULT hr;
359 hr = IDirectInputDevice8_Poll(joy->device);
361 /* If it failed, try to acquire the joystick */
362 if (FAILED(hr))
364 hr = IDirectInputDevice8_Acquire(joy->device);
365 while (hr == DIERR_INPUTLOST) hr = IDirectInputDevice8_Acquire(joy->device);
368 if (hr == DIERR_OTHERAPPHASPRIO) return;
370 IDirectInputDevice8_GetDeviceState(joy->device, sizeof(DIJOYSTATE), state);
373 static DWORD WINAPI input_thread(void *param)
375 int axes_pos[TEST_MAX_AXES][2];
376 DIJOYSTATE state;
377 struct JoystickData *data = param;
379 /* Setup POV as clock positions
381 * 31500 4500
382 * 27000 -1 9000
383 * 22500 13500
384 * 18000
386 int ma = TEST_AXIS_MAX;
387 int pov_val[9] = {0, 4500, 9000, 13500,
388 18000, 22500, 27000, 31500, -1};
389 int pov_pos[9][2] = { {0, -ma}, {ma/2, -ma/2}, {ma, 0}, {ma/2, ma/2},
390 {0, ma}, {-ma/2, ma/2}, {-ma, 0}, {-ma/2, -ma/2}, {0, 0} };
392 ZeroMemory(&state, sizeof(state));
394 while (!data->stop)
396 int i;
397 poll_input(&data->joysticks[data->chosen_joystick], &state);
399 dump_joy_state(&state, data->joysticks[data->chosen_joystick].num_buttons);
401 /* Indicate pressed buttons */
402 for (i = 0; i < data->joysticks[data->chosen_joystick].num_buttons; i++)
403 if (state.rgbButtons[i])
404 SendMessageW(data->graphics.buttons[i], BM_SETSTATE, TRUE, 0);
406 /* Indicate axis positions, axes showing are hardcoded for now */
407 axes_pos[0][0] = state.lX;
408 axes_pos[0][1] = state.lY;
409 axes_pos[1][0] = state.lRx;
410 axes_pos[1][1] = state.lRy;
411 axes_pos[2][0] = state.lZ;
412 axes_pos[2][1] = state.lRz;
414 /* Set pov values */
415 for (i = 0; i < sizeof(pov_val)/sizeof(pov_val[0]); i++)
417 if (state.rgdwPOV[0] == pov_val[i])
419 axes_pos[3][0] = pov_pos[i][0];
420 axes_pos[3][1] = pov_pos[i][1];
424 for (i = 0; i < TEST_MAX_AXES; i++)
426 RECT r;
428 r.left = (TEST_AXIS_X + TEST_NEXT_AXIS_X*i + axes_pos[i][0]);
429 r.top = (TEST_AXIS_Y + axes_pos[i][1]);
430 r.bottom = r.right = 0; /* unused */
431 MapDialogRect(data->graphics.hwnd, &r);
433 SetWindowPos(data->graphics.axes[i], 0, r.left, r.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
436 Sleep(TEST_POLL_TIME);
438 /* Reset button state */
439 for (i = 0; i < data->joysticks[data->chosen_joystick].num_buttons; i++)
440 SendMessageW(data->graphics.buttons[i], BM_SETSTATE, FALSE, 0);
443 return 0;
446 static void test_handle_joychange(HWND hwnd, struct JoystickData *data)
448 int i;
450 if (data->num_joysticks == 0) return;
452 data->chosen_joystick = SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_GETCURSEL, 0, 0);
454 /* Enable only buttons present in the device */
455 for (i = 0; i < TEST_MAX_BUTTONS; i++)
456 ShowWindow(data->graphics.buttons[i], i <= data->joysticks[data->chosen_joystick].num_buttons);
459 /*********************************************************************
460 * button_number_to_wchar [internal]
461 * Transforms an integer in the interval [0,99] into a 2 character WCHAR string
463 static void button_number_to_wchar(int n, WCHAR str[3])
465 str[1] = n % 10 + '0';
466 n /= 10;
467 str[0] = n % 10 + '0';
468 str[2] = '\0';
471 static void draw_joystick_buttons(HWND hwnd, struct JoystickData* data)
473 int i;
474 int row = 0, col = 0;
475 WCHAR button_label[3];
476 HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
477 static WCHAR button_class[] = {'B','u','t','t','o','n','\0'};
479 for (i = 0; i < TEST_MAX_BUTTONS; i++)
481 RECT r;
483 if ((i % TEST_BUTTON_COL_MAX) == 0 && i != 0)
485 row += 1;
486 col = 0;
489 r.left = (TEST_BUTTON_X + TEST_NEXT_BUTTON_X*col);
490 r.top = (TEST_BUTTON_Y + TEST_NEXT_BUTTON_Y*row);
491 r.right = r.left + TEST_BUTTON_SIZE_X;
492 r.bottom = r.top + TEST_BUTTON_SIZE_Y;
493 MapDialogRect(hwnd, &r);
495 button_number_to_wchar(i + 1, button_label);
497 data->graphics.buttons[i] = CreateWindowW(button_class, button_label, WS_CHILD,
498 r.left, r.top, r.right - r.left, r.bottom - r.top,
499 hwnd, NULL, NULL, hinst);
501 col += 1;
505 static void draw_joystick_axes(HWND hwnd, struct JoystickData* data)
507 int i;
508 HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
509 static const WCHAR button_class[] = {'B','u','t','t','o','n','\0'};
510 static const WCHAR axes_names[TEST_MAX_AXES][7] = { {'X',',','Y','\0'}, {'R','x',',','R','y','\0'},
511 {'Z',',','R','z','\0'}, {'P','O','V','\0'} };
512 static const DWORD axes_idc[TEST_MAX_AXES] = { IDC_TESTGROUPXY, IDC_TESTGROUPRXRY,
513 IDC_TESTGROUPZRZ, IDC_TESTGROUPPOV };
515 for (i = 0; i < TEST_MAX_AXES; i++)
517 RECT r;
518 /* Set axis box name */
519 SetWindowTextW(GetDlgItem(hwnd, axes_idc[i]), axes_names[i]);
521 r.left = (TEST_AXIS_X + TEST_NEXT_AXIS_X*i);
522 r.top = TEST_AXIS_Y;
523 r.right = r.left + TEST_AXIS_SIZE_X;
524 r.bottom = r.top + TEST_AXIS_SIZE_Y;
525 MapDialogRect(hwnd, &r);
527 data->graphics.axes[i] = CreateWindowW( button_class, NULL, WS_CHILD | WS_VISIBLE,
528 r.left, r.top, r.right - r.left, r.bottom - r.top,
529 hwnd, NULL, NULL, hinst);
533 /*********************************************************************
534 * test_dlgproc [internal]
537 static INT_PTR CALLBACK test_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
539 static HANDLE thread;
540 static struct JoystickData *data;
541 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
543 switch (msg)
545 case WM_INITDIALOG:
547 int i;
549 data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
551 /* Add enumerated joysticks to the combobox */
552 for (i = 0; i < data->num_joysticks; i++)
554 struct Joystick *joy = &data->joysticks[i];
555 SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
558 draw_joystick_buttons(hwnd, data);
559 draw_joystick_axes(hwnd, data);
561 return TRUE;
564 case WM_COMMAND:
565 switch(wparam)
567 case MAKEWPARAM(IDC_TESTSELECTCOMBO, CBN_SELCHANGE):
568 test_handle_joychange(hwnd, data);
569 break;
571 return TRUE;
573 case WM_NOTIFY:
574 switch(((LPNMHDR)lparam)->code)
576 case PSN_SETACTIVE:
578 DWORD tid;
580 /* Initialize input thread */
581 if (data->num_joysticks > 0)
583 data->stop = FALSE;
585 /* Set the first joystick as default */
586 SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_SETCURSEL, 0, 0);
587 test_handle_joychange(hwnd, data);
589 thread = CreateThread(NULL, 0, input_thread, (void*) data, 0, &tid);
592 break;
594 case PSN_RESET: /* intentional fall-through */
595 case PSN_KILLACTIVE:
596 /* Stop input thread */
597 data->stop = TRUE;
598 MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, 0);
599 CloseHandle(thread);
600 break;
602 return TRUE;
604 return FALSE;
607 /*********************************************************************
608 * Joystick force feedback testing functions
611 static void draw_ff_axis(HWND hwnd, struct JoystickData *data)
613 HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
614 static WCHAR button_class[] = {'B','u','t','t','o','n','\0'};
615 RECT r;
617 r.left = FF_AXIS_X;
618 r.top = FF_AXIS_Y;
619 r.right = r.left + FF_AXIS_SIZE_X;
620 r.bottom = r.top + FF_AXIS_SIZE_Y;
621 MapDialogRect(hwnd, &r);
623 /* Draw direction axis */
624 data->graphics.ff_axis = CreateWindowW( button_class, NULL, WS_CHILD | WS_VISIBLE,
625 r.left, r.top, r.right - r.left, r.bottom - r.top,
626 hwnd, NULL, NULL, hinst);
629 static void initialize_effects_list(HWND hwnd, struct Joystick* joy)
631 int i;
633 SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_RESETCONTENT, 0, 0);
635 for (i=0; i < joy->num_effects; i++)
637 /* Effect names start with GUID_, so we'll skip this part */
638 WCHAR *name = joy->effects[i].info.tszName + 5;
639 SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_ADDSTRING, 0, (LPARAM) name);
643 static void ff_handle_joychange(HWND hwnd, struct JoystickData *data)
645 int sel;
647 if (data->num_ff == 0) return;
649 sel = SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_GETCURSEL, 0, 0);
650 data->chosen_joystick = SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_GETITEMDATA, sel, 0);
651 initialize_effects_list(hwnd, &data->joysticks[data->chosen_joystick]);
654 static void ff_handle_effectchange(HWND hwnd, struct Joystick *joy)
656 int sel = SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_GETCURSEL, 0, 0);
658 if (sel < 0) return;
660 joy->chosen_effect = sel;
663 static DWORD WINAPI ff_input_thread(void *param)
665 struct JoystickData *data = param;
666 DIJOYSTATE state;
668 ZeroMemory(&state, sizeof(state));
670 while (!data->stop)
672 int i;
673 struct Joystick *joy = &data->joysticks[data->chosen_joystick];
674 int chosen_effect = joy->chosen_effect;
675 DIEFFECT *dieffect;
676 DWORD flags = DIEP_AXES | DIEP_DIRECTION | DIEP_NORESTART;
677 RECT r;
679 /* Skip this if we have no effects */
680 if (joy->num_effects == 0 || chosen_effect < 0) continue;
682 poll_input(joy, &state);
684 /* Set ff parameters and draw the axis */
685 dieffect = &joy->effects[chosen_effect].params;
686 dieffect->rgdwAxes[0] = state.lX;
687 dieffect->rgdwAxes[1] = state.lY;
689 r.left = FF_AXIS_X + state.lX;
690 r.top = FF_AXIS_Y + state.lY;
691 r.right = r.bottom = 0; /* unused */
692 MapDialogRect(data->graphics.hwnd, &r);
694 SetWindowPos(data->graphics.ff_axis, 0, r.left, r.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
696 for (i=0; i < joy->num_buttons; i++)
697 if (state.rgbButtons[i])
699 IDirectInputEffect_SetParameters(joy->effects[chosen_effect].effect, dieffect, flags);
700 IDirectInputEffect_Start(joy->effects[chosen_effect].effect, 1, 0);
701 break;
704 Sleep(TEST_POLL_TIME);
707 return 0;
710 /***********************************************************************
711 * ff_effects_callback [internal]
712 * Enumerates, creates, sets the some parameters and stores all ff effects
713 * supported by the joystick. Works like enum_callback, counting the effects
714 * first and then storing them.
716 static BOOL CALLBACK ff_effects_callback(const DIEFFECTINFOW *pdei, void *pvRef)
718 HRESULT hr;
719 DIEFFECT dieffect;
720 DWORD axes[2] = {DIJOFS_X, DIJOFS_Y};
721 int direction[2] = {0, 0};
722 struct Joystick *joystick = pvRef;
724 if (joystick->effects == NULL)
726 joystick->num_effects += 1;
727 return DIENUM_CONTINUE;
730 hr = IDirectInputDevice8_Acquire(joystick->device);
732 if (FAILED(hr)) return DIENUM_CONTINUE;
734 ZeroMemory(&dieffect, sizeof(dieffect));
736 dieffect.dwSize = sizeof(dieffect);
737 dieffect.dwFlags = DIEFF_CARTESIAN;
738 dieffect.dwDuration = FF_PLAY_TIME;
740 dieffect.cAxes = 2;
741 dieffect.rgdwAxes = axes;
742 dieffect.rglDirection = direction;
744 if (IsEqualGUID(&pdei->guid, &GUID_RampForce))
746 DIRAMPFORCE rforce;
748 rforce.lStart = 0;
749 rforce.lEnd = DI_FFNOMINALMAX;
751 dieffect.cbTypeSpecificParams = sizeof(rforce);
752 dieffect.lpvTypeSpecificParams = &rforce;
753 dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
755 else if (IsEqualGUID(&pdei->guid, &GUID_ConstantForce))
757 DICONSTANTFORCE cforce;
759 cforce.lMagnitude = DI_FFNOMINALMAX;
761 dieffect.cbTypeSpecificParams = sizeof(cforce);
762 dieffect.lpvTypeSpecificParams = &cforce;
763 dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
765 else if (IsEqualGUID(&pdei->guid, &GUID_Sine) ||
766 IsEqualGUID(&pdei->guid, &GUID_Square) ||
767 IsEqualGUID(&pdei->guid, &GUID_Triangle) ||
768 IsEqualGUID(&pdei->guid, &GUID_SawtoothUp) ||
769 IsEqualGUID(&pdei->guid, &GUID_SawtoothDown))
771 DIPERIODIC pforce;
773 pforce.dwMagnitude = DI_FFNOMINALMAX;
774 pforce.lOffset = 0;
775 pforce.dwPhase = 0;
776 pforce.dwPeriod = FF_PERIOD_TIME;
778 dieffect.cbTypeSpecificParams = sizeof(pforce);
779 dieffect.lpvTypeSpecificParams = &pforce;
780 dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
783 hr = IDirectInputDevice2_CreateEffect(
784 joystick->device, &pdei->guid, &dieffect, &joystick->effects[joystick->cur_effect].effect, NULL);
786 joystick->effects[joystick->cur_effect].params = dieffect;
787 joystick->effects[joystick->cur_effect].info = *pdei;
788 joystick->cur_effect += 1;
790 return DIENUM_CONTINUE;
793 /*********************************************************************
794 * ff_dlgproc [internal]
797 static INT_PTR CALLBACK ff_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
799 static HANDLE thread;
800 static struct JoystickData *data;
801 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
803 switch (msg)
805 case WM_INITDIALOG:
807 int i, cur = 0;
809 data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
811 /* Add joysticks with FF support to the combobox and get the effects */
812 for (i = 0; i < data->num_joysticks; i++)
814 struct Joystick *joy = &data->joysticks[i];
816 if (joy->forcefeedback)
818 SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
819 SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_SETITEMDATA, cur, i);
821 cur++;
823 /* Count device effects and then store them */
824 joy->num_effects = 0;
825 joy->effects = NULL;
826 IDirectInputDevice8_EnumEffects(joy->device, ff_effects_callback, (void *) joy, 0);
827 joy->effects = HeapAlloc(GetProcessHeap(), 0, sizeof(struct Effect) * joy->num_effects);
829 joy->cur_effect = 0;
830 IDirectInputDevice8_EnumEffects(joy->device, ff_effects_callback, (void*) joy, 0);
831 joy->num_effects = joy->cur_effect;
835 draw_ff_axis(hwnd, data);
837 return TRUE;
840 case WM_COMMAND:
841 switch(wparam)
843 case MAKEWPARAM(IDC_FFSELECTCOMBO, CBN_SELCHANGE):
844 ff_handle_joychange(hwnd, data);
846 SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_SETCURSEL, 0, 0);
847 ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
848 break;
850 case MAKEWPARAM(IDC_FFEFFECTLIST, LBN_SELCHANGE):
851 ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
852 break;
854 return TRUE;
856 case WM_NOTIFY:
857 switch(((LPNMHDR)lparam)->code)
859 case PSN_SETACTIVE:
860 if (data->num_ff > 0)
862 DWORD tid;
864 data->stop = FALSE;
865 /* Set the first joystick as default */
866 SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_SETCURSEL, 0, 0);
867 ff_handle_joychange(hwnd, data);
869 SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_SETCURSEL, 0, 0);
870 ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
872 thread = CreateThread(NULL, 0, ff_input_thread, (void*) data, 0, &tid);
874 break;
876 case PSN_RESET: /* intentional fall-through */
877 case PSN_KILLACTIVE:
878 /* Stop ff thread */
879 data->stop = TRUE;
880 MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, 0);
881 CloseHandle(thread);
882 break;
884 return TRUE;
886 return FALSE;
889 /******************************************************************************
890 * propsheet_callback [internal]
892 static int CALLBACK propsheet_callback(HWND hwnd, UINT msg, LPARAM lparam)
894 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
895 switch (msg)
897 case PSCB_INITIALIZED:
898 break;
900 return 0;
903 /******************************************************************************
904 * display_cpl_sheets [internal]
906 * Build and display the dialog with all control panel propertysheets
909 static void display_cpl_sheets(HWND parent, struct JoystickData *data)
911 INITCOMMONCONTROLSEX icex;
912 PROPSHEETPAGEW psp[NUM_PROPERTY_PAGES];
913 PROPSHEETHEADERW psh;
914 DWORD id = 0;
916 OleInitialize(NULL);
917 /* Initialize common controls */
918 icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
919 icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES;
920 InitCommonControlsEx(&icex);
922 ZeroMemory(&psh, sizeof(psh));
923 ZeroMemory(psp, sizeof(psp));
925 /* Fill out all PROPSHEETPAGE */
926 psp[id].dwSize = sizeof (PROPSHEETPAGEW);
927 psp[id].hInstance = hcpl;
928 psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_LIST);
929 psp[id].pfnDlgProc = list_dlgproc;
930 psp[id].lParam = (INT_PTR) data;
931 id++;
933 psp[id].dwSize = sizeof (PROPSHEETPAGEW);
934 psp[id].hInstance = hcpl;
935 psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_TEST);
936 psp[id].pfnDlgProc = test_dlgproc;
937 psp[id].lParam = (INT_PTR) data;
938 id++;
940 psp[id].dwSize = sizeof (PROPSHEETPAGEW);
941 psp[id].hInstance = hcpl;
942 psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_FORCEFEEDBACK);
943 psp[id].pfnDlgProc = ff_dlgproc;
944 psp[id].lParam = (INT_PTR) data;
945 id++;
947 /* Fill out the PROPSHEETHEADER */
948 psh.dwSize = sizeof (PROPSHEETHEADERW);
949 psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID | PSH_USECALLBACK;
950 psh.hwndParent = parent;
951 psh.hInstance = hcpl;
952 psh.pszCaption = MAKEINTRESOURCEW(IDS_CPL_NAME);
953 psh.nPages = id;
954 psh.u3.ppsp = psp;
955 psh.pfnCallback = propsheet_callback;
957 /* display the dialog */
958 PropertySheetW(&psh);
960 OleUninitialize();
963 /*********************************************************************
964 * CPlApplet (joy.cpl.@)
966 * Control Panel entry point
968 * PARAMS
969 * hWnd [I] Handle for the Control Panel Window
970 * command [I] CPL_* Command
971 * lParam1 [I] first extra Parameter
972 * lParam2 [I] second extra Parameter
974 * RETURNS
975 * Depends on the command
978 LONG CALLBACK CPlApplet(HWND hwnd, UINT command, LPARAM lParam1, LPARAM lParam2)
980 static struct JoystickData data;
981 TRACE("(%p, %u, 0x%lx, 0x%lx)\n", hwnd, command, lParam1, lParam2);
983 switch (command)
985 case CPL_INIT:
987 HRESULT hr;
989 /* Initialize dinput */
990 hr = DirectInput8Create(GetModuleHandleW(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void**)&data.di, NULL);
992 if (FAILED(hr))
994 ERR("Failed to initialize DirectInput: 0x%08x\n", hr);
995 return FALSE;
998 /* Then get all the connected joysticks */
999 initialize_joysticks(&data);
1001 return TRUE;
1003 case CPL_GETCOUNT:
1004 return 1;
1006 case CPL_INQUIRE:
1008 CPLINFO *appletInfo = (CPLINFO *) lParam2;
1010 appletInfo->idName = IDS_CPL_NAME;
1011 appletInfo->idInfo = IDS_CPL_INFO;
1012 appletInfo->lData = 0;
1013 return TRUE;
1016 case CPL_DBLCLK:
1017 display_cpl_sheets(hwnd, &data);
1018 break;
1020 case CPL_STOP:
1021 destroy_joysticks(&data);
1023 /* And destroy dinput too */
1024 IDirectInput8_Release(data.di);
1025 break;
1028 return FALSE;