qdvd: Add IDvdGraphBuilder stub.
[wine/zf.git] / dlls / joy.cpl / main.c
blob3921f59cbb5923161390394fb50f9cdeab9d64be
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 static const WCHAR button_class[] = {'B','u','t','t','o','n','\0'};
43 /*********************************************************************
44 * DllMain
46 BOOL WINAPI DllMain(HINSTANCE hdll, DWORD reason, LPVOID reserved)
48 TRACE("(%p, %d, %p)\n", hdll, reason, reserved);
50 switch (reason)
52 case DLL_WINE_PREATTACH:
53 return FALSE; /* prefer native version */
55 case DLL_PROCESS_ATTACH:
56 DisableThreadLibraryCalls(hdll);
57 hcpl = hdll;
59 return TRUE;
62 /***********************************************************************
63 * enum_callback [internal]
64 * Enumerates, creates and sets the common data format for all the joystick devices.
65 * First time it checks if space for the joysticks was already reserved
66 * and if not, just counts how many there are.
68 static BOOL CALLBACK enum_callback(const DIDEVICEINSTANCEW *instance, void *context)
70 struct JoystickData *data = context;
71 struct Joystick *joystick;
72 DIPROPRANGE proprange;
73 DIDEVCAPS caps;
75 if (data->joysticks == NULL)
77 data->num_joysticks += 1;
78 return DIENUM_CONTINUE;
81 joystick = &data->joysticks[data->cur_joystick];
82 data->cur_joystick += 1;
84 IDirectInput8_CreateDevice(data->di, &instance->guidInstance, &joystick->device, NULL);
85 IDirectInputDevice8_SetDataFormat(joystick->device, &c_dfDIJoystick);
87 joystick->instance = *instance;
89 caps.dwSize = sizeof(caps);
90 IDirectInputDevice8_GetCapabilities(joystick->device, &caps);
92 joystick->num_buttons = caps.dwButtons;
93 joystick->num_axes = caps.dwAxes;
94 joystick->forcefeedback = caps.dwFlags & DIDC_FORCEFEEDBACK;
95 joystick->num_effects = 0;
97 if (joystick->forcefeedback) data->num_ff++;
99 /* Set axis range to ease the GUI visualization */
100 proprange.diph.dwSize = sizeof(DIPROPRANGE);
101 proprange.diph.dwHeaderSize = sizeof(DIPROPHEADER);
102 proprange.diph.dwHow = DIPH_DEVICE;
103 proprange.diph.dwObj = 0;
104 proprange.lMin = TEST_AXIS_MIN;
105 proprange.lMax = TEST_AXIS_MAX;
107 IDirectInputDevice_SetProperty(joystick->device, DIPROP_RANGE, &proprange.diph);
109 return DIENUM_CONTINUE;
112 /***********************************************************************
113 * initialize_joysticks [internal]
115 static void initialize_joysticks(struct JoystickData *data)
117 data->num_joysticks = 0;
118 data->cur_joystick = 0;
119 IDirectInput8_EnumDevices(data->di, DI8DEVCLASS_GAMECTRL, enum_callback, data, DIEDFL_ATTACHEDONLY);
120 data->joysticks = HeapAlloc(GetProcessHeap(), 0, sizeof(struct Joystick) * data->num_joysticks);
122 /* Get all the joysticks */
123 IDirectInput8_EnumDevices(data->di, DI8DEVCLASS_GAMECTRL, enum_callback, data, DIEDFL_ATTACHEDONLY);
126 /***********************************************************************
127 * destroy_joysticks [internal]
129 static void destroy_joysticks(struct JoystickData *data)
131 int i, j;
133 for (i = 0; i < data->num_joysticks; i++)
136 if (data->joysticks[i].forcefeedback && data->joysticks[i].num_effects > 0)
138 for (j = 0; j < data->joysticks[i].num_effects; j++)
139 if (data->joysticks[i].effects[j].effect)
140 IDirectInputEffect_Release(data->joysticks[i].effects[j].effect);
142 HeapFree(GetProcessHeap(), 0, data->joysticks[i].effects);
145 IDirectInputDevice8_Unacquire(data->joysticks[i].device);
146 IDirectInputDevice8_Release(data->joysticks[i].device);
149 HeapFree(GetProcessHeap(), 0, data->joysticks);
152 static void initialize_joysticks_list(HWND hwnd, struct JoystickData *data)
154 int i;
156 SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_RESETCONTENT, 0, 0);
158 /* Add enumerated joysticks */
159 for (i = 0; i < data->num_joysticks; i++)
161 struct Joystick *joy = &data->joysticks[i];
162 SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
166 /******************************************************************************
167 * get_app_key [internal]
168 * Get the default DirectInput key and the selected app config key.
170 static BOOL get_app_key(HKEY *defkey, HKEY *appkey)
172 static const WCHAR reg_key[] = { 'S','o','f','t','w','a','r','e','\\',
173 'W','i','n','e','\\',
174 'D','i','r','e','c','t','I','n','p','u','t','\\',
175 'J','o','y','s','t','i','c','k','s','\0' };
176 *appkey = 0;
178 /* Registry key can be found in HKCU\Software\Wine\DirectInput */
179 if (RegCreateKeyExW(HKEY_CURRENT_USER, reg_key, 0, NULL, 0, KEY_SET_VALUE | KEY_READ, NULL, defkey, NULL))
180 *defkey = 0;
182 return *defkey || *appkey;
185 /******************************************************************************
186 * set_config_key [internal]
187 * Writes a string value to a registry key, deletes the key if value == NULL
189 static DWORD set_config_key(HKEY defkey, HKEY appkey, const WCHAR *name, const WCHAR *value, DWORD size)
191 if (value == NULL)
193 if (appkey && !RegDeleteValueW(appkey, name))
194 return 0;
196 if (defkey && !RegDeleteValueW(defkey, name))
197 return 0;
199 else
201 if (appkey && !RegSetValueExW(appkey, name, 0, REG_SZ, (const BYTE*) value, (size + 1)*sizeof(WCHAR)))
202 return 0;
204 if (defkey && !RegSetValueExW(defkey, name, 0, REG_SZ, (const BYTE*) value, (size + 1)*sizeof(WCHAR)))
205 return 0;
208 return ERROR_FILE_NOT_FOUND;
211 /******************************************************************************
212 * enable_joystick [internal]
213 * Writes to the DirectInput registry key that enables/disables a joystick
214 * from being enumerated.
216 static void enable_joystick(WCHAR *joy_name, BOOL enable)
218 static const WCHAR disabled_str[] = {'d','i','s','a','b','l','e','d','\0'};
219 HKEY hkey, appkey;
221 get_app_key(&hkey, &appkey);
223 if (!enable)
224 set_config_key(hkey, appkey, joy_name, disabled_str, lstrlenW(disabled_str));
225 else
226 set_config_key(hkey, appkey, joy_name, NULL, 0);
228 if (hkey) RegCloseKey(hkey);
229 if (appkey) RegCloseKey(appkey);
232 static void initialize_disabled_joysticks_list(HWND hwnd)
234 static const WCHAR disabled_str[] = {'d','i','s','a','b','l','e','d','\0'};
235 HKEY hkey, appkey;
236 DWORD values = 0;
237 LSTATUS status;
238 DWORD i;
240 SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_RESETCONTENT, 0, 0);
242 /* Search for disabled joysticks */
243 get_app_key(&hkey, &appkey);
244 RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL, &values, NULL, NULL, NULL, NULL);
246 for (i=0; i < values; i++)
248 DWORD name_len = MAX_PATH, data_len = MAX_PATH;
249 WCHAR buf_name[MAX_PATH + 9], buf_data[MAX_PATH];
251 status = RegEnumValueW(hkey, i, buf_name, &name_len, NULL, NULL, (BYTE*) buf_data, &data_len);
253 if (status == ERROR_SUCCESS && !lstrcmpW(disabled_str, buf_data))
254 SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_ADDSTRING, 0, (LPARAM) buf_name);
257 if (hkey) RegCloseKey(hkey);
258 if (appkey) RegCloseKey(appkey);
261 /*********************************************************************
262 * list_dlgproc [internal]
265 static INT_PTR CALLBACK list_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
267 static struct JoystickData *data;
268 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
269 switch (msg)
271 case WM_INITDIALOG:
273 data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
275 initialize_joysticks_list(hwnd, data);
276 initialize_disabled_joysticks_list(hwnd);
278 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONENABLE), FALSE);
279 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONDISABLE), FALSE);
281 /* Store the hwnd to be used with MapDialogRect for unit conversions */
282 data->graphics.hwnd = hwnd;
284 return TRUE;
287 case WM_COMMAND:
289 switch (LOWORD(wparam))
291 case IDC_BUTTONDISABLE:
293 int sel = SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_GETCURSEL, 0, 0);
295 if (sel >= 0)
297 enable_joystick(data->joysticks[sel].instance.tszInstanceName, FALSE);
298 initialize_disabled_joysticks_list(hwnd);
301 break;
303 case IDC_BUTTONENABLE:
305 int sel = SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_GETCURSEL, 0, 0);
307 if (sel >= 0)
309 WCHAR text[MAX_PATH];
310 SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_GETTEXT, sel, (LPARAM) text);
311 enable_joystick(text, TRUE);
312 initialize_disabled_joysticks_list(hwnd);
315 break;
317 case IDC_JOYSTICKLIST:
318 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONENABLE), FALSE);
319 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONDISABLE), TRUE);
320 break;
322 case IDC_DISABLEDLIST:
323 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONENABLE), TRUE);
324 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONDISABLE), FALSE);
325 break;
328 return TRUE;
330 case WM_NOTIFY:
331 return TRUE;
333 default:
334 break;
336 return FALSE;
339 /*********************************************************************
340 * Joystick testing functions
343 static void dump_joy_state(DIJOYSTATE* st, int num_buttons)
345 int i;
346 TRACE("Ax (% 5d,% 5d,% 5d)\n", st->lX,st->lY, st->lZ);
347 TRACE("RAx (% 5d,% 5d,% 5d)\n", st->lRx, st->lRy, st->lRz);
348 TRACE("Slider (% 5d,% 5d)\n", st->rglSlider[0], st->rglSlider[1]);
349 TRACE("Pov (% 5d,% 5d,% 5d,% 5d)\n", st->rgdwPOV[0], st->rgdwPOV[1], st->rgdwPOV[2], st->rgdwPOV[3]);
351 TRACE("Buttons ");
352 for(i=0; i < num_buttons; i++)
353 TRACE(" %c",st->rgbButtons[i] ? 'x' : 'o');
354 TRACE("\n");
357 static void poll_input(const struct Joystick *joy, DIJOYSTATE *state)
359 HRESULT hr;
361 hr = IDirectInputDevice8_Poll(joy->device);
363 /* If it failed, try to acquire the joystick */
364 if (FAILED(hr))
366 hr = IDirectInputDevice8_Acquire(joy->device);
367 while (hr == DIERR_INPUTLOST) hr = IDirectInputDevice8_Acquire(joy->device);
370 if (hr == DIERR_OTHERAPPHASPRIO) return;
372 IDirectInputDevice8_GetDeviceState(joy->device, sizeof(DIJOYSTATE), state);
375 static DWORD WINAPI input_thread(void *param)
377 int axes_pos[TEST_MAX_AXES][2];
378 DIJOYSTATE state;
379 struct JoystickData *data = param;
381 /* Setup POV as clock positions
383 * 31500 4500
384 * 27000 -1 9000
385 * 22500 13500
386 * 18000
388 int ma = TEST_AXIS_MAX;
389 int pov_val[9] = {0, 4500, 9000, 13500,
390 18000, 22500, 27000, 31500, -1};
391 int pov_pos[9][2] = { {0, -ma}, {ma/2, -ma/2}, {ma, 0}, {ma/2, ma/2},
392 {0, ma}, {-ma/2, ma/2}, {-ma, 0}, {-ma/2, -ma/2}, {0, 0} };
394 ZeroMemory(&state, sizeof(state));
396 while (!data->stop)
398 int i;
399 unsigned int j;
401 poll_input(&data->joysticks[data->chosen_joystick], &state);
403 dump_joy_state(&state, data->joysticks[data->chosen_joystick].num_buttons);
405 /* Indicate pressed buttons */
406 for (i = 0; i < data->joysticks[data->chosen_joystick].num_buttons; i++)
407 SendMessageW(data->graphics.buttons[i], BM_SETSTATE, !!state.rgbButtons[i], 0);
409 /* Indicate axis positions, axes showing are hardcoded for now */
410 axes_pos[0][0] = state.lX;
411 axes_pos[0][1] = state.lY;
412 axes_pos[1][0] = state.lRx;
413 axes_pos[1][1] = state.lRy;
414 axes_pos[2][0] = state.lZ;
415 axes_pos[2][1] = state.lRz;
417 /* Set pov values */
418 for (j = 0; j < ARRAY_SIZE(pov_val); j++)
420 if (state.rgdwPOV[0] == pov_val[j])
422 axes_pos[3][0] = pov_pos[j][0];
423 axes_pos[3][1] = pov_pos[j][1];
427 for (i = 0; i < TEST_MAX_AXES; i++)
429 RECT r;
431 r.left = (TEST_AXIS_X + TEST_NEXT_AXIS_X*i + axes_pos[i][0]);
432 r.top = (TEST_AXIS_Y + axes_pos[i][1]);
433 r.bottom = r.right = 0; /* unused */
434 MapDialogRect(data->graphics.hwnd, &r);
436 SetWindowPos(data->graphics.axes[i], 0, r.left, r.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
439 Sleep(TEST_POLL_TIME);
442 return 0;
445 static void test_handle_joychange(HWND hwnd, struct JoystickData *data)
447 int i;
449 if (data->num_joysticks == 0) return;
451 data->chosen_joystick = SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_GETCURSEL, 0, 0);
453 /* Enable only buttons present in the device */
454 for (i = 0; i < TEST_MAX_BUTTONS; i++)
455 ShowWindow(data->graphics.buttons[i], i < data->joysticks[data->chosen_joystick].num_buttons);
458 /*********************************************************************
459 * button_number_to_wchar [internal]
460 * Transforms an integer in the interval [0,99] into a 2 character WCHAR string
462 static void button_number_to_wchar(int n, WCHAR str[3])
464 str[1] = n % 10 + '0';
465 n /= 10;
466 str[0] = n % 10 + '0';
467 str[2] = '\0';
470 static void draw_joystick_buttons(HWND hwnd, struct JoystickData* data)
472 int i;
473 int row = 0, col = 0;
474 WCHAR button_label[3];
475 HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
477 for (i = 0; i < TEST_MAX_BUTTONS; i++)
479 RECT r;
481 if ((i % TEST_BUTTON_COL_MAX) == 0 && i != 0)
483 row += 1;
484 col = 0;
487 r.left = (TEST_BUTTON_X + TEST_NEXT_BUTTON_X*col);
488 r.top = (TEST_BUTTON_Y + TEST_NEXT_BUTTON_Y*row);
489 r.right = r.left + TEST_BUTTON_SIZE_X;
490 r.bottom = r.top + TEST_BUTTON_SIZE_Y;
491 MapDialogRect(hwnd, &r);
493 button_number_to_wchar(i + 1, button_label);
495 data->graphics.buttons[i] = CreateWindowW(button_class, button_label, WS_CHILD,
496 r.left, r.top, r.right - r.left, r.bottom - r.top,
497 hwnd, NULL, NULL, hinst);
499 col += 1;
503 static void draw_joystick_axes(HWND hwnd, struct JoystickData* data)
505 int i;
506 HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
507 static const WCHAR axes_names[TEST_MAX_AXES][7] = { {'X',',','Y','\0'}, {'R','x',',','R','y','\0'},
508 {'Z',',','R','z','\0'}, {'P','O','V','\0'} };
509 static const DWORD axes_idc[TEST_MAX_AXES] = { IDC_TESTGROUPXY, IDC_TESTGROUPRXRY,
510 IDC_TESTGROUPZRZ, IDC_TESTGROUPPOV };
512 for (i = 0; i < TEST_MAX_AXES; i++)
514 RECT r;
515 /* Set axis box name */
516 SetWindowTextW(GetDlgItem(hwnd, axes_idc[i]), axes_names[i]);
518 r.left = (TEST_AXIS_X + TEST_NEXT_AXIS_X*i);
519 r.top = TEST_AXIS_Y;
520 r.right = r.left + TEST_AXIS_SIZE_X;
521 r.bottom = r.top + TEST_AXIS_SIZE_Y;
522 MapDialogRect(hwnd, &r);
524 data->graphics.axes[i] = CreateWindowW( button_class, NULL, WS_CHILD | WS_VISIBLE,
525 r.left, r.top, r.right - r.left, r.bottom - r.top,
526 hwnd, NULL, NULL, hinst);
530 /*********************************************************************
531 * test_dlgproc [internal]
534 static INT_PTR CALLBACK test_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
536 static HANDLE thread;
537 static struct JoystickData *data;
538 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
540 switch (msg)
542 case WM_INITDIALOG:
544 int i;
546 data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
548 /* Add enumerated joysticks to the combobox */
549 for (i = 0; i < data->num_joysticks; i++)
551 struct Joystick *joy = &data->joysticks[i];
552 SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
555 draw_joystick_buttons(hwnd, data);
556 draw_joystick_axes(hwnd, data);
558 return TRUE;
561 case WM_COMMAND:
562 switch(wparam)
564 case MAKEWPARAM(IDC_TESTSELECTCOMBO, CBN_SELCHANGE):
565 test_handle_joychange(hwnd, data);
566 break;
568 return TRUE;
570 case WM_NOTIFY:
571 switch(((LPNMHDR)lparam)->code)
573 case PSN_SETACTIVE:
575 DWORD tid;
577 /* Initialize input thread */
578 if (data->num_joysticks > 0)
580 data->stop = FALSE;
582 /* Set the first joystick as default */
583 SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_SETCURSEL, 0, 0);
584 test_handle_joychange(hwnd, data);
586 thread = CreateThread(NULL, 0, input_thread, (void*) data, 0, &tid);
589 break;
591 case PSN_RESET: /* intentional fall-through */
592 case PSN_KILLACTIVE:
593 /* Stop input thread */
594 data->stop = TRUE;
595 MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, 0);
596 CloseHandle(thread);
597 break;
599 return TRUE;
601 return FALSE;
604 /*********************************************************************
605 * Joystick force feedback testing functions
608 static void draw_ff_axis(HWND hwnd, struct JoystickData *data)
610 HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
611 RECT r;
613 r.left = FF_AXIS_X;
614 r.top = FF_AXIS_Y;
615 r.right = r.left + FF_AXIS_SIZE_X;
616 r.bottom = r.top + FF_AXIS_SIZE_Y;
617 MapDialogRect(hwnd, &r);
619 /* Draw direction axis */
620 data->graphics.ff_axis = CreateWindowW( button_class, NULL, WS_CHILD | WS_VISIBLE,
621 r.left, r.top, r.right - r.left, r.bottom - r.top,
622 hwnd, NULL, NULL, hinst);
625 static void initialize_effects_list(HWND hwnd, struct Joystick* joy)
627 int i;
629 SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_RESETCONTENT, 0, 0);
631 for (i=0; i < joy->num_effects; i++)
633 /* Effect names start with GUID_, so we'll skip this part */
634 WCHAR *name = joy->effects[i].info.tszName + 5;
635 SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_ADDSTRING, 0, (LPARAM) name);
639 static void ff_handle_joychange(HWND hwnd, struct JoystickData *data)
641 int sel;
643 if (data->num_ff == 0) return;
645 sel = SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_GETCURSEL, 0, 0);
646 data->chosen_joystick = SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_GETITEMDATA, sel, 0);
647 initialize_effects_list(hwnd, &data->joysticks[data->chosen_joystick]);
650 static void ff_handle_effectchange(HWND hwnd, struct Joystick *joy)
652 int sel = SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_GETCURSEL, 0, 0);
654 if (sel < 0) return;
656 joy->chosen_effect = sel;
659 static DWORD WINAPI ff_input_thread(void *param)
661 struct JoystickData *data = param;
662 DIJOYSTATE state;
664 ZeroMemory(&state, sizeof(state));
666 while (!data->stop)
668 int i;
669 struct Joystick *joy = &data->joysticks[data->chosen_joystick];
670 int chosen_effect = joy->chosen_effect;
671 DIEFFECT *dieffect;
672 DWORD flags = DIEP_AXES | DIEP_DIRECTION | DIEP_NORESTART;
673 RECT r;
675 Sleep(TEST_POLL_TIME);
677 /* Skip this if we have no effects */
678 if (joy->num_effects == 0 || chosen_effect < 0) continue;
680 poll_input(joy, &state);
682 /* Set ff parameters and draw the axis */
683 dieffect = &joy->effects[chosen_effect].params;
684 dieffect->rgdwAxes[0] = state.lX;
685 dieffect->rgdwAxes[1] = state.lY;
687 r.left = FF_AXIS_X + state.lX;
688 r.top = FF_AXIS_Y + state.lY;
689 r.right = r.bottom = 0; /* unused */
690 MapDialogRect(data->graphics.hwnd, &r);
692 SetWindowPos(data->graphics.ff_axis, 0, r.left, r.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
694 for (i=0; i < joy->num_buttons; i++)
695 if (state.rgbButtons[i])
697 IDirectInputEffect_SetParameters(joy->effects[chosen_effect].effect, dieffect, flags);
698 IDirectInputEffect_Start(joy->effects[chosen_effect].effect, 1, 0);
699 break;
703 return 0;
706 /***********************************************************************
707 * ff_effects_callback [internal]
708 * Enumerates, creates, sets the some parameters and stores all ff effects
709 * supported by the joystick. Works like enum_callback, counting the effects
710 * first and then storing them.
712 static BOOL CALLBACK ff_effects_callback(const DIEFFECTINFOW *pdei, void *pvRef)
714 HRESULT hr;
715 DIEFFECT dieffect;
716 DWORD axes[2] = {DIJOFS_X, DIJOFS_Y};
717 int direction[2] = {0, 0};
718 struct Joystick *joystick = pvRef;
719 DIRAMPFORCE rforce;
720 DICONSTANTFORCE cforce;
721 DIPERIODIC pforce;
722 DICONDITION cdforce;
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 rforce.lStart = 0;
747 rforce.lEnd = DI_FFNOMINALMAX;
749 dieffect.cbTypeSpecificParams = sizeof(rforce);
750 dieffect.lpvTypeSpecificParams = &rforce;
751 dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
753 else if (IsEqualGUID(&pdei->guid, &GUID_ConstantForce))
755 cforce.lMagnitude = DI_FFNOMINALMAX;
757 dieffect.cbTypeSpecificParams = sizeof(cforce);
758 dieffect.lpvTypeSpecificParams = &cforce;
759 dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
761 else if (IsEqualGUID(&pdei->guid, &GUID_Sine) ||
762 IsEqualGUID(&pdei->guid, &GUID_Square) ||
763 IsEqualGUID(&pdei->guid, &GUID_Triangle) ||
764 IsEqualGUID(&pdei->guid, &GUID_SawtoothUp) ||
765 IsEqualGUID(&pdei->guid, &GUID_SawtoothDown))
767 pforce.dwMagnitude = DI_FFNOMINALMAX;
768 pforce.lOffset = 0;
769 pforce.dwPhase = 0;
770 pforce.dwPeriod = FF_PERIOD_TIME;
772 dieffect.cbTypeSpecificParams = sizeof(pforce);
773 dieffect.lpvTypeSpecificParams = &pforce;
774 dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
776 else if (IsEqualGUID(&pdei->guid, &GUID_Spring) ||
777 IsEqualGUID(&pdei->guid, &GUID_Damper) ||
778 IsEqualGUID(&pdei->guid, &GUID_Inertia) ||
779 IsEqualGUID(&pdei->guid, &GUID_Friction))
781 cdforce.dwPositiveSaturation = 10000;
782 cdforce.dwNegativeSaturation = 10000;
783 cdforce.lPositiveCoefficient = 10000;
784 cdforce.lNegativeCoefficient = 10000;
785 cdforce.lDeadBand = 0;
786 cdforce.lOffset = 0;
788 dieffect.cbTypeSpecificParams = sizeof(cdforce);
789 dieffect.lpvTypeSpecificParams = &cdforce;
790 dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
793 hr = IDirectInputDevice2_CreateEffect(
794 joystick->device, &pdei->guid, &dieffect, &joystick->effects[joystick->cur_effect].effect, NULL);
796 joystick->effects[joystick->cur_effect].params = dieffect;
797 joystick->effects[joystick->cur_effect].info = *pdei;
798 joystick->cur_effect += 1;
800 return DIENUM_CONTINUE;
803 /*********************************************************************
804 * ff_dlgproc [internal]
807 static INT_PTR CALLBACK ff_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
809 static HANDLE thread;
810 static struct JoystickData *data;
811 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
813 switch (msg)
815 case WM_INITDIALOG:
817 int i, cur = 0;
819 data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
821 /* Add joysticks with FF support to the combobox and get the effects */
822 for (i = 0; i < data->num_joysticks; i++)
824 struct Joystick *joy = &data->joysticks[i];
826 if (joy->forcefeedback)
828 SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
829 SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_SETITEMDATA, cur, i);
831 cur++;
833 /* Count device effects and then store them */
834 joy->num_effects = 0;
835 joy->effects = NULL;
836 IDirectInputDevice8_EnumEffects(joy->device, ff_effects_callback, (void *) joy, 0);
837 joy->effects = HeapAlloc(GetProcessHeap(), 0, sizeof(struct Effect) * joy->num_effects);
839 joy->cur_effect = 0;
840 IDirectInputDevice8_EnumEffects(joy->device, ff_effects_callback, (void*) joy, 0);
841 joy->num_effects = joy->cur_effect;
845 draw_ff_axis(hwnd, data);
847 return TRUE;
850 case WM_COMMAND:
851 switch(wparam)
853 case MAKEWPARAM(IDC_FFSELECTCOMBO, CBN_SELCHANGE):
854 ff_handle_joychange(hwnd, data);
856 SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_SETCURSEL, 0, 0);
857 ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
858 break;
860 case MAKEWPARAM(IDC_FFEFFECTLIST, LBN_SELCHANGE):
861 ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
862 break;
864 return TRUE;
866 case WM_NOTIFY:
867 switch(((LPNMHDR)lparam)->code)
869 case PSN_SETACTIVE:
870 if (data->num_ff > 0)
872 DWORD tid;
874 data->stop = FALSE;
875 /* Set the first joystick as default */
876 SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_SETCURSEL, 0, 0);
877 ff_handle_joychange(hwnd, data);
879 SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_SETCURSEL, 0, 0);
880 ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
882 thread = CreateThread(NULL, 0, ff_input_thread, (void*) data, 0, &tid);
884 break;
886 case PSN_RESET: /* intentional fall-through */
887 case PSN_KILLACTIVE:
888 /* Stop ff thread */
889 data->stop = TRUE;
890 MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, 0);
891 CloseHandle(thread);
892 break;
894 return TRUE;
896 return FALSE;
899 /******************************************************************************
900 * propsheet_callback [internal]
902 static int CALLBACK propsheet_callback(HWND hwnd, UINT msg, LPARAM lparam)
904 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
905 switch (msg)
907 case PSCB_INITIALIZED:
908 break;
910 return 0;
913 /******************************************************************************
914 * display_cpl_sheets [internal]
916 * Build and display the dialog with all control panel propertysheets
919 static void display_cpl_sheets(HWND parent, struct JoystickData *data)
921 INITCOMMONCONTROLSEX icex;
922 PROPSHEETPAGEW psp[NUM_PROPERTY_PAGES];
923 PROPSHEETHEADERW psh;
924 DWORD id = 0;
926 OleInitialize(NULL);
927 /* Initialize common controls */
928 icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
929 icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES;
930 InitCommonControlsEx(&icex);
932 ZeroMemory(&psh, sizeof(psh));
933 ZeroMemory(psp, sizeof(psp));
935 /* Fill out all PROPSHEETPAGE */
936 psp[id].dwSize = sizeof (PROPSHEETPAGEW);
937 psp[id].hInstance = hcpl;
938 psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_LIST);
939 psp[id].pfnDlgProc = list_dlgproc;
940 psp[id].lParam = (INT_PTR) data;
941 id++;
943 psp[id].dwSize = sizeof (PROPSHEETPAGEW);
944 psp[id].hInstance = hcpl;
945 psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_TEST);
946 psp[id].pfnDlgProc = test_dlgproc;
947 psp[id].lParam = (INT_PTR) data;
948 id++;
950 psp[id].dwSize = sizeof (PROPSHEETPAGEW);
951 psp[id].hInstance = hcpl;
952 psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_FORCEFEEDBACK);
953 psp[id].pfnDlgProc = ff_dlgproc;
954 psp[id].lParam = (INT_PTR) data;
955 id++;
957 /* Fill out the PROPSHEETHEADER */
958 psh.dwSize = sizeof (PROPSHEETHEADERW);
959 psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID | PSH_USECALLBACK;
960 psh.hwndParent = parent;
961 psh.hInstance = hcpl;
962 psh.pszCaption = MAKEINTRESOURCEW(IDS_CPL_NAME);
963 psh.nPages = id;
964 psh.u3.ppsp = psp;
965 psh.pfnCallback = propsheet_callback;
967 /* display the dialog */
968 PropertySheetW(&psh);
970 OleUninitialize();
973 /*********************************************************************
974 * CPlApplet (joy.cpl.@)
976 * Control Panel entry point
978 * PARAMS
979 * hWnd [I] Handle for the Control Panel Window
980 * command [I] CPL_* Command
981 * lParam1 [I] first extra Parameter
982 * lParam2 [I] second extra Parameter
984 * RETURNS
985 * Depends on the command
988 LONG CALLBACK CPlApplet(HWND hwnd, UINT command, LPARAM lParam1, LPARAM lParam2)
990 static struct JoystickData data;
991 TRACE("(%p, %u, 0x%lx, 0x%lx)\n", hwnd, command, lParam1, lParam2);
993 switch (command)
995 case CPL_INIT:
997 HRESULT hr;
999 /* Initialize dinput */
1000 hr = DirectInput8Create(GetModuleHandleW(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void**)&data.di, NULL);
1002 if (FAILED(hr))
1004 ERR("Failed to initialize DirectInput: 0x%08x\n", hr);
1005 return FALSE;
1008 /* Then get all the connected joysticks */
1009 initialize_joysticks(&data);
1011 return TRUE;
1013 case CPL_GETCOUNT:
1014 return 1;
1016 case CPL_INQUIRE:
1018 CPLINFO *appletInfo = (CPLINFO *) lParam2;
1020 appletInfo->idIcon = ICO_MAIN;
1021 appletInfo->idName = IDS_CPL_NAME;
1022 appletInfo->idInfo = IDS_CPL_INFO;
1023 appletInfo->lData = 0;
1024 return TRUE;
1027 case CPL_DBLCLK:
1028 display_cpl_sheets(hwnd, &data);
1029 break;
1031 case CPL_STOP:
1032 destroy_joysticks(&data);
1034 /* And destroy dinput too */
1035 IDirectInput8_Release(data.di);
1036 break;
1039 return FALSE;