rasapi32: Update spec file.
[wine/zf.git] / dlls / joy.cpl / main.c
blob3ea417843f48b06bbc0b4c7af79b29a8a6d4a131
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_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 enum_callback(const DIDEVICEINSTANCEW *instance, void *context)
65 struct JoystickData *data = context;
66 struct Joystick *joystick;
67 DIPROPRANGE proprange;
68 DIDEVCAPS caps;
70 if (data->joysticks == NULL)
72 data->num_joysticks += 1;
73 return DIENUM_CONTINUE;
76 joystick = &data->joysticks[data->cur_joystick];
77 data->cur_joystick += 1;
79 IDirectInput8_CreateDevice(data->di, &instance->guidInstance, &joystick->device, NULL);
80 IDirectInputDevice8_SetDataFormat(joystick->device, &c_dfDIJoystick);
82 joystick->instance = *instance;
84 caps.dwSize = sizeof(caps);
85 IDirectInputDevice8_GetCapabilities(joystick->device, &caps);
87 joystick->num_buttons = caps.dwButtons;
88 joystick->num_axes = caps.dwAxes;
89 joystick->forcefeedback = caps.dwFlags & DIDC_FORCEFEEDBACK;
90 joystick->num_effects = 0;
92 if (joystick->forcefeedback) data->num_ff++;
94 /* Set axis range to ease the GUI visualization */
95 proprange.diph.dwSize = sizeof(DIPROPRANGE);
96 proprange.diph.dwHeaderSize = sizeof(DIPROPHEADER);
97 proprange.diph.dwHow = DIPH_DEVICE;
98 proprange.diph.dwObj = 0;
99 proprange.lMin = TEST_AXIS_MIN;
100 proprange.lMax = TEST_AXIS_MAX;
102 IDirectInputDevice_SetProperty(joystick->device, DIPROP_RANGE, &proprange.diph);
104 return DIENUM_CONTINUE;
107 /***********************************************************************
108 * initialize_joysticks [internal]
110 static void initialize_joysticks(struct JoystickData *data)
112 data->num_joysticks = 0;
113 data->cur_joystick = 0;
114 IDirectInput8_EnumDevices(data->di, DI8DEVCLASS_GAMECTRL, enum_callback, data, DIEDFL_ATTACHEDONLY);
115 data->joysticks = HeapAlloc(GetProcessHeap(), 0, sizeof(struct Joystick) * data->num_joysticks);
117 /* Get all the joysticks */
118 IDirectInput8_EnumDevices(data->di, DI8DEVCLASS_GAMECTRL, enum_callback, data, DIEDFL_ATTACHEDONLY);
121 /***********************************************************************
122 * destroy_joysticks [internal]
124 static void destroy_joysticks(struct JoystickData *data)
126 int i, j;
128 for (i = 0; i < data->num_joysticks; i++)
131 if (data->joysticks[i].forcefeedback && data->joysticks[i].num_effects > 0)
133 for (j = 0; j < data->joysticks[i].num_effects; j++)
134 if (data->joysticks[i].effects[j].effect)
135 IDirectInputEffect_Release(data->joysticks[i].effects[j].effect);
137 HeapFree(GetProcessHeap(), 0, data->joysticks[i].effects);
140 IDirectInputDevice8_Unacquire(data->joysticks[i].device);
141 IDirectInputDevice8_Release(data->joysticks[i].device);
144 HeapFree(GetProcessHeap(), 0, data->joysticks);
147 static void initialize_joysticks_list(HWND hwnd, struct JoystickData *data)
149 int i;
151 SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_RESETCONTENT, 0, 0);
153 /* Add enumerated joysticks */
154 for (i = 0; i < data->num_joysticks; i++)
156 struct Joystick *joy = &data->joysticks[i];
157 SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
161 /******************************************************************************
162 * get_app_key [internal]
163 * Get the default DirectInput key and the selected app config key.
165 static BOOL get_app_key(HKEY *defkey, HKEY *appkey)
167 *appkey = 0;
169 /* Registry key can be found in HKCU\Software\Wine\DirectInput */
170 if (RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Wine\\DirectInput\\Joysticks", 0, NULL, 0,
171 KEY_SET_VALUE | KEY_READ, NULL, defkey, NULL))
172 *defkey = 0;
174 return *defkey || *appkey;
177 /******************************************************************************
178 * set_config_key [internal]
179 * Writes a string value to a registry key, deletes the key if value == NULL
181 static DWORD set_config_key(HKEY defkey, HKEY appkey, const WCHAR *name, const WCHAR *value, DWORD size)
183 if (value == NULL)
185 if (appkey && !RegDeleteValueW(appkey, name))
186 return 0;
188 if (defkey && !RegDeleteValueW(defkey, name))
189 return 0;
191 else
193 if (appkey && !RegSetValueExW(appkey, name, 0, REG_SZ, (const BYTE*) value, (size + 1)*sizeof(WCHAR)))
194 return 0;
196 if (defkey && !RegSetValueExW(defkey, name, 0, REG_SZ, (const BYTE*) value, (size + 1)*sizeof(WCHAR)))
197 return 0;
200 return ERROR_FILE_NOT_FOUND;
203 /******************************************************************************
204 * enable_joystick [internal]
205 * Writes to the DirectInput registry key that enables/disables a joystick
206 * from being enumerated.
208 static void enable_joystick(WCHAR *joy_name, BOOL enable)
210 HKEY hkey, appkey;
212 get_app_key(&hkey, &appkey);
214 if (!enable)
215 set_config_key(hkey, appkey, joy_name, L"disabled", lstrlenW(L"disabled"));
216 else
217 set_config_key(hkey, appkey, joy_name, NULL, 0);
219 if (hkey) RegCloseKey(hkey);
220 if (appkey) RegCloseKey(appkey);
223 static void initialize_disabled_joysticks_list(HWND hwnd)
225 HKEY hkey, appkey;
226 DWORD values = 0;
227 LSTATUS status;
228 DWORD i;
230 SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_RESETCONTENT, 0, 0);
232 /* Search for disabled joysticks */
233 get_app_key(&hkey, &appkey);
234 RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL, &values, NULL, NULL, NULL, NULL);
236 for (i=0; i < values; i++)
238 DWORD name_len = MAX_PATH, data_len = MAX_PATH;
239 WCHAR buf_name[MAX_PATH + 9], buf_data[MAX_PATH];
241 status = RegEnumValueW(hkey, i, buf_name, &name_len, NULL, NULL, (BYTE*) buf_data, &data_len);
243 if (status == ERROR_SUCCESS && !lstrcmpW(L"disabled", buf_data))
244 SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_ADDSTRING, 0, (LPARAM) buf_name);
247 if (hkey) RegCloseKey(hkey);
248 if (appkey) RegCloseKey(appkey);
251 /*********************************************************************
252 * list_dlgproc [internal]
255 static INT_PTR CALLBACK list_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
257 static struct JoystickData *data;
258 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
259 switch (msg)
261 case WM_INITDIALOG:
263 data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
265 initialize_joysticks_list(hwnd, data);
266 initialize_disabled_joysticks_list(hwnd);
268 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONENABLE), FALSE);
269 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONDISABLE), FALSE);
271 /* Store the hwnd to be used with MapDialogRect for unit conversions */
272 data->graphics.hwnd = hwnd;
274 return TRUE;
277 case WM_COMMAND:
279 switch (LOWORD(wparam))
281 case IDC_BUTTONDISABLE:
283 int sel = SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_GETCURSEL, 0, 0);
285 if (sel >= 0)
287 enable_joystick(data->joysticks[sel].instance.tszInstanceName, FALSE);
288 initialize_disabled_joysticks_list(hwnd);
291 break;
293 case IDC_BUTTONENABLE:
295 int sel = SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_GETCURSEL, 0, 0);
297 if (sel >= 0)
299 WCHAR text[MAX_PATH];
300 SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_GETTEXT, sel, (LPARAM) text);
301 enable_joystick(text, TRUE);
302 initialize_disabled_joysticks_list(hwnd);
305 break;
307 case IDC_JOYSTICKLIST:
308 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONENABLE), FALSE);
309 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONDISABLE), TRUE);
310 break;
312 case IDC_DISABLEDLIST:
313 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONENABLE), TRUE);
314 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONDISABLE), FALSE);
315 break;
318 return TRUE;
320 case WM_NOTIFY:
321 return TRUE;
323 default:
324 break;
326 return FALSE;
329 /*********************************************************************
330 * Joystick testing functions
333 static void dump_joy_state(DIJOYSTATE* st, int num_buttons)
335 int i;
336 TRACE("Ax (% 5d,% 5d,% 5d)\n", st->lX,st->lY, st->lZ);
337 TRACE("RAx (% 5d,% 5d,% 5d)\n", st->lRx, st->lRy, st->lRz);
338 TRACE("Slider (% 5d,% 5d)\n", st->rglSlider[0], st->rglSlider[1]);
339 TRACE("Pov (% 5d,% 5d,% 5d,% 5d)\n", st->rgdwPOV[0], st->rgdwPOV[1], st->rgdwPOV[2], st->rgdwPOV[3]);
341 TRACE("Buttons ");
342 for(i=0; i < num_buttons; i++)
343 TRACE(" %c",st->rgbButtons[i] ? 'x' : 'o');
344 TRACE("\n");
347 static void poll_input(const struct Joystick *joy, DIJOYSTATE *state)
349 HRESULT hr;
351 hr = IDirectInputDevice8_Poll(joy->device);
353 /* If it failed, try to acquire the joystick */
354 if (FAILED(hr))
356 hr = IDirectInputDevice8_Acquire(joy->device);
357 while (hr == DIERR_INPUTLOST) hr = IDirectInputDevice8_Acquire(joy->device);
360 if (hr == DIERR_OTHERAPPHASPRIO) return;
362 IDirectInputDevice8_GetDeviceState(joy->device, sizeof(DIJOYSTATE), state);
365 static DWORD WINAPI input_thread(void *param)
367 int axes_pos[TEST_MAX_AXES][2];
368 DIJOYSTATE state;
369 struct JoystickData *data = param;
371 /* Setup POV as clock positions
373 * 31500 4500
374 * 27000 -1 9000
375 * 22500 13500
376 * 18000
378 int ma = TEST_AXIS_MAX;
379 int pov_val[9] = {0, 4500, 9000, 13500,
380 18000, 22500, 27000, 31500, -1};
381 int pov_pos[9][2] = { {0, -ma}, {ma/2, -ma/2}, {ma, 0}, {ma/2, ma/2},
382 {0, ma}, {-ma/2, ma/2}, {-ma, 0}, {-ma/2, -ma/2}, {0, 0} };
384 ZeroMemory(&state, sizeof(state));
386 while (!data->stop)
388 int i;
389 unsigned int j;
391 poll_input(&data->joysticks[data->chosen_joystick], &state);
393 dump_joy_state(&state, data->joysticks[data->chosen_joystick].num_buttons);
395 /* Indicate pressed buttons */
396 for (i = 0; i < data->joysticks[data->chosen_joystick].num_buttons; i++)
397 SendMessageW(data->graphics.buttons[i], BM_SETSTATE, !!state.rgbButtons[i], 0);
399 /* Indicate axis positions, axes showing are hardcoded for now */
400 axes_pos[0][0] = state.lX;
401 axes_pos[0][1] = state.lY;
402 axes_pos[1][0] = state.lRx;
403 axes_pos[1][1] = state.lRy;
404 axes_pos[2][0] = state.lZ;
405 axes_pos[2][1] = state.lRz;
407 /* Set pov values */
408 for (j = 0; j < ARRAY_SIZE(pov_val); j++)
410 if (state.rgdwPOV[0] == pov_val[j])
412 axes_pos[3][0] = pov_pos[j][0];
413 axes_pos[3][1] = pov_pos[j][1];
417 for (i = 0; i < TEST_MAX_AXES; i++)
419 RECT r;
421 r.left = (TEST_AXIS_X + TEST_NEXT_AXIS_X*i + axes_pos[i][0]);
422 r.top = (TEST_AXIS_Y + axes_pos[i][1]);
423 r.bottom = r.right = 0; /* unused */
424 MapDialogRect(data->graphics.hwnd, &r);
426 SetWindowPos(data->graphics.axes[i], 0, r.left, r.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
429 Sleep(TEST_POLL_TIME);
432 return 0;
435 static void test_handle_joychange(HWND hwnd, struct JoystickData *data)
437 int i;
439 if (data->num_joysticks == 0) return;
441 data->chosen_joystick = SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_GETCURSEL, 0, 0);
443 /* Enable only buttons present in the device */
444 for (i = 0; i < TEST_MAX_BUTTONS; i++)
445 ShowWindow(data->graphics.buttons[i], i < data->joysticks[data->chosen_joystick].num_buttons);
448 /*********************************************************************
449 * button_number_to_wchar [internal]
450 * Transforms an integer in the interval [0,99] into a 2 character WCHAR string
452 static void button_number_to_wchar(int n, WCHAR str[3])
454 str[1] = n % 10 + '0';
455 n /= 10;
456 str[0] = n % 10 + '0';
457 str[2] = '\0';
460 static void draw_joystick_buttons(HWND hwnd, struct JoystickData* data)
462 int i;
463 int row = 0, col = 0;
464 WCHAR button_label[3];
465 HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
467 for (i = 0; i < TEST_MAX_BUTTONS; i++)
469 RECT r;
471 if ((i % TEST_BUTTON_COL_MAX) == 0 && i != 0)
473 row += 1;
474 col = 0;
477 r.left = (TEST_BUTTON_X + TEST_NEXT_BUTTON_X*col);
478 r.top = (TEST_BUTTON_Y + TEST_NEXT_BUTTON_Y*row);
479 r.right = r.left + TEST_BUTTON_SIZE_X;
480 r.bottom = r.top + TEST_BUTTON_SIZE_Y;
481 MapDialogRect(hwnd, &r);
483 button_number_to_wchar(i + 1, button_label);
485 data->graphics.buttons[i] = CreateWindowW(L"Button", button_label, WS_CHILD,
486 r.left, r.top, r.right - r.left, r.bottom - r.top,
487 hwnd, NULL, NULL, hinst);
489 col += 1;
493 static void draw_joystick_axes(HWND hwnd, struct JoystickData* data)
495 int i;
496 HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
497 static const WCHAR axes_names[TEST_MAX_AXES][7] = { L"X,Y", L"Rx,Ry", L"Z,Rz", L"POV"};
498 static const DWORD axes_idc[TEST_MAX_AXES] = { IDC_TESTGROUPXY, IDC_TESTGROUPRXRY,
499 IDC_TESTGROUPZRZ, IDC_TESTGROUPPOV };
501 for (i = 0; i < TEST_MAX_AXES; i++)
503 RECT r;
504 /* Set axis box name */
505 SetWindowTextW(GetDlgItem(hwnd, axes_idc[i]), axes_names[i]);
507 r.left = (TEST_AXIS_X + TEST_NEXT_AXIS_X*i);
508 r.top = TEST_AXIS_Y;
509 r.right = r.left + TEST_AXIS_SIZE_X;
510 r.bottom = r.top + TEST_AXIS_SIZE_Y;
511 MapDialogRect(hwnd, &r);
513 data->graphics.axes[i] = CreateWindowW(L"Button", NULL, WS_CHILD | WS_VISIBLE,
514 r.left, r.top, r.right - r.left, r.bottom - r.top,
515 hwnd, NULL, NULL, hinst);
519 /*********************************************************************
520 * test_dlgproc [internal]
523 static INT_PTR CALLBACK test_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
525 static HANDLE thread;
526 static struct JoystickData *data;
527 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
529 switch (msg)
531 case WM_INITDIALOG:
533 int i;
535 data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
537 /* Add enumerated joysticks to the combobox */
538 for (i = 0; i < data->num_joysticks; i++)
540 struct Joystick *joy = &data->joysticks[i];
541 SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
544 draw_joystick_buttons(hwnd, data);
545 draw_joystick_axes(hwnd, data);
547 return TRUE;
550 case WM_COMMAND:
551 switch(wparam)
553 case MAKEWPARAM(IDC_TESTSELECTCOMBO, CBN_SELCHANGE):
554 test_handle_joychange(hwnd, data);
555 break;
557 return TRUE;
559 case WM_NOTIFY:
560 switch(((LPNMHDR)lparam)->code)
562 case PSN_SETACTIVE:
564 DWORD tid;
566 /* Initialize input thread */
567 if (data->num_joysticks > 0)
569 data->stop = FALSE;
571 /* Set the first joystick as default */
572 SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_SETCURSEL, 0, 0);
573 test_handle_joychange(hwnd, data);
575 thread = CreateThread(NULL, 0, input_thread, (void*) data, 0, &tid);
578 break;
580 case PSN_RESET: /* intentional fall-through */
581 case PSN_KILLACTIVE:
582 /* Stop input thread */
583 data->stop = TRUE;
584 MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, 0);
585 CloseHandle(thread);
586 break;
588 return TRUE;
590 return FALSE;
593 /*********************************************************************
594 * Joystick force feedback testing functions
597 static void draw_ff_axis(HWND hwnd, struct JoystickData *data)
599 HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
600 RECT r;
602 r.left = FF_AXIS_X;
603 r.top = FF_AXIS_Y;
604 r.right = r.left + FF_AXIS_SIZE_X;
605 r.bottom = r.top + FF_AXIS_SIZE_Y;
606 MapDialogRect(hwnd, &r);
608 /* Draw direction axis */
609 data->graphics.ff_axis = CreateWindowW(L"Button", NULL, WS_CHILD | WS_VISIBLE,
610 r.left, r.top, r.right - r.left, r.bottom - r.top,
611 hwnd, NULL, NULL, hinst);
614 static void initialize_effects_list(HWND hwnd, struct Joystick* joy)
616 int i;
618 SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_RESETCONTENT, 0, 0);
620 for (i=0; i < joy->num_effects; i++)
622 /* Effect names start with GUID_, so we'll skip this part */
623 WCHAR *name = joy->effects[i].info.tszName + 5;
624 SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_ADDSTRING, 0, (LPARAM) name);
628 static void ff_handle_joychange(HWND hwnd, struct JoystickData *data)
630 int sel;
632 if (data->num_ff == 0) return;
634 sel = SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_GETCURSEL, 0, 0);
635 data->chosen_joystick = SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_GETITEMDATA, sel, 0);
636 initialize_effects_list(hwnd, &data->joysticks[data->chosen_joystick]);
639 static void ff_handle_effectchange(HWND hwnd, struct Joystick *joy)
641 int sel = SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_GETCURSEL, 0, 0);
643 if (sel < 0) return;
645 joy->chosen_effect = sel;
648 static DWORD WINAPI ff_input_thread(void *param)
650 struct JoystickData *data = param;
651 DIJOYSTATE state;
653 ZeroMemory(&state, sizeof(state));
655 while (!data->stop)
657 int i;
658 struct Joystick *joy = &data->joysticks[data->chosen_joystick];
659 int chosen_effect = joy->chosen_effect;
660 DIEFFECT *dieffect;
661 DWORD flags = DIEP_AXES | DIEP_DIRECTION | DIEP_NORESTART;
662 RECT r;
664 Sleep(TEST_POLL_TIME);
666 /* Skip this if we have no effects */
667 if (joy->num_effects == 0 || chosen_effect < 0) continue;
669 poll_input(joy, &state);
671 /* Set ff parameters and draw the axis */
672 dieffect = &joy->effects[chosen_effect].params;
673 dieffect->rgdwAxes[0] = state.lX;
674 dieffect->rgdwAxes[1] = state.lY;
676 r.left = FF_AXIS_X + state.lX;
677 r.top = FF_AXIS_Y + state.lY;
678 r.right = r.bottom = 0; /* unused */
679 MapDialogRect(data->graphics.hwnd, &r);
681 SetWindowPos(data->graphics.ff_axis, 0, r.left, r.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
683 for (i=0; i < joy->num_buttons; i++)
684 if (state.rgbButtons[i])
686 IDirectInputEffect_SetParameters(joy->effects[chosen_effect].effect, dieffect, flags);
687 IDirectInputEffect_Start(joy->effects[chosen_effect].effect, 1, 0);
688 break;
692 return 0;
695 /***********************************************************************
696 * ff_effects_callback [internal]
697 * Enumerates, creates, sets the some parameters and stores all ff effects
698 * supported by the joystick. Works like enum_callback, counting the effects
699 * first and then storing them.
701 static BOOL CALLBACK ff_effects_callback(const DIEFFECTINFOW *pdei, void *pvRef)
703 HRESULT hr;
704 DIEFFECT dieffect;
705 DWORD axes[2] = {DIJOFS_X, DIJOFS_Y};
706 int direction[2] = {0, 0};
707 struct Joystick *joystick = pvRef;
708 DIRAMPFORCE rforce;
709 DICONSTANTFORCE cforce;
710 DIPERIODIC pforce;
711 DICONDITION cdforce;
713 if (joystick->effects == NULL)
715 joystick->num_effects += 1;
716 return DIENUM_CONTINUE;
719 hr = IDirectInputDevice8_Acquire(joystick->device);
721 if (FAILED(hr)) return DIENUM_CONTINUE;
723 ZeroMemory(&dieffect, sizeof(dieffect));
725 dieffect.dwSize = sizeof(dieffect);
726 dieffect.dwFlags = DIEFF_CARTESIAN;
727 dieffect.dwDuration = FF_PLAY_TIME;
729 dieffect.cAxes = 2;
730 dieffect.rgdwAxes = axes;
731 dieffect.rglDirection = direction;
733 if (IsEqualGUID(&pdei->guid, &GUID_RampForce))
735 rforce.lStart = 0;
736 rforce.lEnd = DI_FFNOMINALMAX;
738 dieffect.cbTypeSpecificParams = sizeof(rforce);
739 dieffect.lpvTypeSpecificParams = &rforce;
740 dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
742 else if (IsEqualGUID(&pdei->guid, &GUID_ConstantForce))
744 cforce.lMagnitude = DI_FFNOMINALMAX;
746 dieffect.cbTypeSpecificParams = sizeof(cforce);
747 dieffect.lpvTypeSpecificParams = &cforce;
748 dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
750 else if (IsEqualGUID(&pdei->guid, &GUID_Sine) ||
751 IsEqualGUID(&pdei->guid, &GUID_Square) ||
752 IsEqualGUID(&pdei->guid, &GUID_Triangle) ||
753 IsEqualGUID(&pdei->guid, &GUID_SawtoothUp) ||
754 IsEqualGUID(&pdei->guid, &GUID_SawtoothDown))
756 pforce.dwMagnitude = DI_FFNOMINALMAX;
757 pforce.lOffset = 0;
758 pforce.dwPhase = 0;
759 pforce.dwPeriod = FF_PERIOD_TIME;
761 dieffect.cbTypeSpecificParams = sizeof(pforce);
762 dieffect.lpvTypeSpecificParams = &pforce;
763 dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
765 else if (IsEqualGUID(&pdei->guid, &GUID_Spring) ||
766 IsEqualGUID(&pdei->guid, &GUID_Damper) ||
767 IsEqualGUID(&pdei->guid, &GUID_Inertia) ||
768 IsEqualGUID(&pdei->guid, &GUID_Friction))
770 cdforce.dwPositiveSaturation = 10000;
771 cdforce.dwNegativeSaturation = 10000;
772 cdforce.lPositiveCoefficient = 10000;
773 cdforce.lNegativeCoefficient = 10000;
774 cdforce.lDeadBand = 0;
775 cdforce.lOffset = 0;
777 dieffect.cbTypeSpecificParams = sizeof(cdforce);
778 dieffect.lpvTypeSpecificParams = &cdforce;
779 dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
782 hr = IDirectInputDevice2_CreateEffect(
783 joystick->device, &pdei->guid, &dieffect, &joystick->effects[joystick->cur_effect].effect, NULL);
785 joystick->effects[joystick->cur_effect].params = dieffect;
786 joystick->effects[joystick->cur_effect].info = *pdei;
787 joystick->cur_effect += 1;
789 return DIENUM_CONTINUE;
792 /*********************************************************************
793 * ff_dlgproc [internal]
796 static INT_PTR CALLBACK ff_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
798 static HANDLE thread;
799 static struct JoystickData *data;
800 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
802 switch (msg)
804 case WM_INITDIALOG:
806 int i, cur = 0;
808 data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
810 /* Add joysticks with FF support to the combobox and get the effects */
811 for (i = 0; i < data->num_joysticks; i++)
813 struct Joystick *joy = &data->joysticks[i];
815 if (joy->forcefeedback)
817 SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
818 SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_SETITEMDATA, cur, i);
820 cur++;
822 /* Count device effects and then store them */
823 joy->num_effects = 0;
824 joy->effects = NULL;
825 IDirectInputDevice8_EnumEffects(joy->device, ff_effects_callback, (void *) joy, 0);
826 joy->effects = HeapAlloc(GetProcessHeap(), 0, sizeof(struct Effect) * joy->num_effects);
828 joy->cur_effect = 0;
829 IDirectInputDevice8_EnumEffects(joy->device, ff_effects_callback, (void*) joy, 0);
830 joy->num_effects = joy->cur_effect;
834 draw_ff_axis(hwnd, data);
836 return TRUE;
839 case WM_COMMAND:
840 switch(wparam)
842 case MAKEWPARAM(IDC_FFSELECTCOMBO, CBN_SELCHANGE):
843 ff_handle_joychange(hwnd, data);
845 SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_SETCURSEL, 0, 0);
846 ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
847 break;
849 case MAKEWPARAM(IDC_FFEFFECTLIST, LBN_SELCHANGE):
850 ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
851 break;
853 return TRUE;
855 case WM_NOTIFY:
856 switch(((LPNMHDR)lparam)->code)
858 case PSN_SETACTIVE:
859 if (data->num_ff > 0)
861 DWORD tid;
863 data->stop = FALSE;
864 /* Set the first joystick as default */
865 SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_SETCURSEL, 0, 0);
866 ff_handle_joychange(hwnd, data);
868 SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_SETCURSEL, 0, 0);
869 ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
871 thread = CreateThread(NULL, 0, ff_input_thread, (void*) data, 0, &tid);
873 break;
875 case PSN_RESET: /* intentional fall-through */
876 case PSN_KILLACTIVE:
877 /* Stop ff thread */
878 data->stop = TRUE;
879 MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, 0);
880 CloseHandle(thread);
881 break;
883 return TRUE;
885 return FALSE;
888 /******************************************************************************
889 * propsheet_callback [internal]
891 static int CALLBACK propsheet_callback(HWND hwnd, UINT msg, LPARAM lparam)
893 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
894 switch (msg)
896 case PSCB_INITIALIZED:
897 break;
899 return 0;
902 /******************************************************************************
903 * display_cpl_sheets [internal]
905 * Build and display the dialog with all control panel propertysheets
908 static void display_cpl_sheets(HWND parent, struct JoystickData *data)
910 INITCOMMONCONTROLSEX icex;
911 PROPSHEETPAGEW psp[NUM_PROPERTY_PAGES];
912 PROPSHEETHEADERW psh;
913 DWORD id = 0;
915 OleInitialize(NULL);
916 /* Initialize common controls */
917 icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
918 icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES;
919 InitCommonControlsEx(&icex);
921 ZeroMemory(&psh, sizeof(psh));
922 ZeroMemory(psp, sizeof(psp));
924 /* Fill out all PROPSHEETPAGE */
925 psp[id].dwSize = sizeof (PROPSHEETPAGEW);
926 psp[id].hInstance = hcpl;
927 psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_LIST);
928 psp[id].pfnDlgProc = list_dlgproc;
929 psp[id].lParam = (INT_PTR) data;
930 id++;
932 psp[id].dwSize = sizeof (PROPSHEETPAGEW);
933 psp[id].hInstance = hcpl;
934 psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_TEST);
935 psp[id].pfnDlgProc = test_dlgproc;
936 psp[id].lParam = (INT_PTR) data;
937 id++;
939 psp[id].dwSize = sizeof (PROPSHEETPAGEW);
940 psp[id].hInstance = hcpl;
941 psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_FORCEFEEDBACK);
942 psp[id].pfnDlgProc = ff_dlgproc;
943 psp[id].lParam = (INT_PTR) data;
944 id++;
946 /* Fill out the PROPSHEETHEADER */
947 psh.dwSize = sizeof (PROPSHEETHEADERW);
948 psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID | PSH_USECALLBACK;
949 psh.hwndParent = parent;
950 psh.hInstance = hcpl;
951 psh.pszCaption = MAKEINTRESOURCEW(IDS_CPL_NAME);
952 psh.nPages = id;
953 psh.u3.ppsp = psp;
954 psh.pfnCallback = propsheet_callback;
956 /* display the dialog */
957 PropertySheetW(&psh);
959 OleUninitialize();
962 /*********************************************************************
963 * CPlApplet (joy.cpl.@)
965 * Control Panel entry point
967 * PARAMS
968 * hWnd [I] Handle for the Control Panel Window
969 * command [I] CPL_* Command
970 * lParam1 [I] first extra Parameter
971 * lParam2 [I] second extra Parameter
973 * RETURNS
974 * Depends on the command
977 LONG CALLBACK CPlApplet(HWND hwnd, UINT command, LPARAM lParam1, LPARAM lParam2)
979 static struct JoystickData data;
980 TRACE("(%p, %u, 0x%lx, 0x%lx)\n", hwnd, command, lParam1, lParam2);
982 switch (command)
984 case CPL_INIT:
986 HRESULT hr;
988 /* Initialize dinput */
989 hr = DirectInput8Create(GetModuleHandleW(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void**)&data.di, NULL);
991 if (FAILED(hr))
993 ERR("Failed to initialize DirectInput: 0x%08x\n", hr);
994 return FALSE;
997 /* Then get all the connected joysticks */
998 initialize_joysticks(&data);
1000 return TRUE;
1002 case CPL_GETCOUNT:
1003 return 1;
1005 case CPL_INQUIRE:
1007 CPLINFO *appletInfo = (CPLINFO *) lParam2;
1009 appletInfo->idIcon = ICO_MAIN;
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;