mstask: Use wide-char string literals.
[wine.git] / dlls / joy.cpl / main.c
blob66ac58f70384a0d6ea8ed8ecda19444fd30b7bdb
1 /*
2 * Joystick testing control panel applet
4 * Copyright 2012 Lucas Fialho Zawacki
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #define NONAMELESSUNION
23 #define COBJMACROS
24 #define CONST_VTABLE
26 #include <stdarg.h>
27 #include <windef.h>
28 #include <winbase.h>
29 #include <winuser.h>
30 #include <commctrl.h>
31 #include <cpl.h>
32 #include "ole2.h"
34 #include "wine/debug.h"
35 #include "joy.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(joycpl);
39 DECLSPEC_HIDDEN HMODULE hcpl;
41 /*********************************************************************
42 * DllMain
44 BOOL WINAPI DllMain(HINSTANCE hdll, DWORD reason, LPVOID reserved)
46 TRACE("(%p, %d, %p)\n", hdll, reason, reserved);
48 switch (reason)
50 case DLL_WINE_PREATTACH:
51 return FALSE; /* prefer native version */
53 case DLL_PROCESS_ATTACH:
54 DisableThreadLibraryCalls(hdll);
55 hcpl = hdll;
57 return TRUE;
60 /***********************************************************************
61 * enum_callback [internal]
62 * Enumerates, creates and sets the common data format for all the joystick devices.
63 * First time it checks if space for the joysticks was already reserved
64 * and if not, just counts how many there are.
66 static BOOL CALLBACK enum_callback(const DIDEVICEINSTANCEW *instance, void *context)
68 struct JoystickData *data = context;
69 struct Joystick *joystick;
70 DIPROPRANGE proprange;
71 DIDEVCAPS caps;
73 if (data->joysticks == NULL)
75 data->num_joysticks += 1;
76 return DIENUM_CONTINUE;
79 joystick = &data->joysticks[data->cur_joystick];
80 data->cur_joystick += 1;
82 IDirectInput8_CreateDevice(data->di, &instance->guidInstance, &joystick->device, NULL);
83 IDirectInputDevice8_SetDataFormat(joystick->device, &c_dfDIJoystick);
85 joystick->instance = *instance;
87 caps.dwSize = sizeof(caps);
88 IDirectInputDevice8_GetCapabilities(joystick->device, &caps);
90 joystick->num_buttons = caps.dwButtons;
91 joystick->num_axes = caps.dwAxes;
92 joystick->forcefeedback = caps.dwFlags & DIDC_FORCEFEEDBACK;
93 joystick->num_effects = 0;
95 if (joystick->forcefeedback) data->num_ff++;
97 /* Set axis range to ease the GUI visualization */
98 proprange.diph.dwSize = sizeof(DIPROPRANGE);
99 proprange.diph.dwHeaderSize = sizeof(DIPROPHEADER);
100 proprange.diph.dwHow = DIPH_DEVICE;
101 proprange.diph.dwObj = 0;
102 proprange.lMin = TEST_AXIS_MIN;
103 proprange.lMax = TEST_AXIS_MAX;
105 IDirectInputDevice_SetProperty(joystick->device, DIPROP_RANGE, &proprange.diph);
107 return DIENUM_CONTINUE;
110 /***********************************************************************
111 * initialize_joysticks [internal]
113 static void initialize_joysticks(struct JoystickData *data)
115 data->num_joysticks = 0;
116 data->cur_joystick = 0;
117 IDirectInput8_EnumDevices(data->di, DI8DEVCLASS_GAMECTRL, enum_callback, data, DIEDFL_ATTACHEDONLY);
118 data->joysticks = HeapAlloc(GetProcessHeap(), 0, sizeof(struct Joystick) * data->num_joysticks);
120 /* Get all the joysticks */
121 IDirectInput8_EnumDevices(data->di, DI8DEVCLASS_GAMECTRL, enum_callback, data, DIEDFL_ATTACHEDONLY);
124 /***********************************************************************
125 * destroy_joysticks [internal]
127 static void destroy_joysticks(struct JoystickData *data)
129 int i, j;
131 for (i = 0; i < data->num_joysticks; i++)
134 if (data->joysticks[i].forcefeedback && data->joysticks[i].num_effects > 0)
136 for (j = 0; j < data->joysticks[i].num_effects; j++)
137 if (data->joysticks[i].effects[j].effect)
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 *appkey = 0;
172 /* Registry key can be found in HKCU\Software\Wine\DirectInput */
173 if (RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Wine\\DirectInput\\Joysticks", 0, NULL, 0,
174 KEY_SET_VALUE | KEY_READ, NULL, defkey, NULL))
175 *defkey = 0;
177 return *defkey || *appkey;
180 /******************************************************************************
181 * set_config_key [internal]
182 * Writes a string value to a registry key, deletes the key if value == NULL
184 static DWORD set_config_key(HKEY defkey, HKEY appkey, const WCHAR *name, const WCHAR *value, DWORD size)
186 if (value == NULL)
188 if (appkey && !RegDeleteValueW(appkey, name))
189 return 0;
191 if (defkey && !RegDeleteValueW(defkey, name))
192 return 0;
194 else
196 if (appkey && !RegSetValueExW(appkey, name, 0, REG_SZ, (const BYTE*) value, (size + 1)*sizeof(WCHAR)))
197 return 0;
199 if (defkey && !RegSetValueExW(defkey, name, 0, REG_SZ, (const BYTE*) value, (size + 1)*sizeof(WCHAR)))
200 return 0;
203 return ERROR_FILE_NOT_FOUND;
206 /******************************************************************************
207 * enable_joystick [internal]
208 * Writes to the DirectInput registry key that enables/disables a joystick
209 * from being enumerated.
211 static void enable_joystick(WCHAR *joy_name, BOOL enable)
213 HKEY hkey, appkey;
215 get_app_key(&hkey, &appkey);
217 if (!enable)
218 set_config_key(hkey, appkey, joy_name, L"disabled", lstrlenW(L"disabled"));
219 else
220 set_config_key(hkey, appkey, joy_name, NULL, 0);
222 if (hkey) RegCloseKey(hkey);
223 if (appkey) RegCloseKey(appkey);
226 static void initialize_disabled_joysticks_list(HWND hwnd)
228 HKEY hkey, appkey;
229 DWORD values = 0;
230 LSTATUS status;
231 DWORD i;
233 SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_RESETCONTENT, 0, 0);
235 /* Search for disabled joysticks */
236 get_app_key(&hkey, &appkey);
237 RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL, &values, NULL, NULL, NULL, NULL);
239 for (i=0; i < values; i++)
241 DWORD name_len = MAX_PATH, data_len = MAX_PATH;
242 WCHAR buf_name[MAX_PATH + 9], buf_data[MAX_PATH];
244 status = RegEnumValueW(hkey, i, buf_name, &name_len, NULL, NULL, (BYTE*) buf_data, &data_len);
246 if (status == ERROR_SUCCESS && !lstrcmpW(L"disabled", buf_data))
247 SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_ADDSTRING, 0, (LPARAM) buf_name);
250 if (hkey) RegCloseKey(hkey);
251 if (appkey) RegCloseKey(appkey);
254 /*********************************************************************
255 * list_dlgproc [internal]
258 static INT_PTR CALLBACK list_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
260 static struct JoystickData *data;
261 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
262 switch (msg)
264 case WM_INITDIALOG:
266 data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
268 initialize_joysticks_list(hwnd, data);
269 initialize_disabled_joysticks_list(hwnd);
271 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONENABLE), FALSE);
272 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONDISABLE), FALSE);
274 /* Store the hwnd to be used with MapDialogRect for unit conversions */
275 data->graphics.hwnd = hwnd;
277 return TRUE;
280 case WM_COMMAND:
282 switch (LOWORD(wparam))
284 case IDC_BUTTONDISABLE:
286 int sel = SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_GETCURSEL, 0, 0);
288 if (sel >= 0)
290 enable_joystick(data->joysticks[sel].instance.tszInstanceName, FALSE);
291 initialize_disabled_joysticks_list(hwnd);
294 break;
296 case IDC_BUTTONENABLE:
298 int sel = SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_GETCURSEL, 0, 0);
300 if (sel >= 0)
302 WCHAR text[MAX_PATH];
303 SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_GETTEXT, sel, (LPARAM) text);
304 enable_joystick(text, TRUE);
305 initialize_disabled_joysticks_list(hwnd);
308 break;
310 case IDC_JOYSTICKLIST:
311 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONENABLE), FALSE);
312 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONDISABLE), TRUE);
313 break;
315 case IDC_DISABLEDLIST:
316 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONENABLE), TRUE);
317 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONDISABLE), FALSE);
318 break;
321 return TRUE;
323 case WM_NOTIFY:
324 return TRUE;
326 default:
327 break;
329 return FALSE;
332 /*********************************************************************
333 * Joystick testing functions
336 static void dump_joy_state(DIJOYSTATE* st, int num_buttons)
338 int i;
339 TRACE("Ax (% 5d,% 5d,% 5d)\n", st->lX,st->lY, st->lZ);
340 TRACE("RAx (% 5d,% 5d,% 5d)\n", st->lRx, st->lRy, st->lRz);
341 TRACE("Slider (% 5d,% 5d)\n", st->rglSlider[0], st->rglSlider[1]);
342 TRACE("Pov (% 5d,% 5d,% 5d,% 5d)\n", st->rgdwPOV[0], st->rgdwPOV[1], st->rgdwPOV[2], st->rgdwPOV[3]);
344 TRACE("Buttons ");
345 for(i=0; i < num_buttons; i++)
346 TRACE(" %c",st->rgbButtons[i] ? 'x' : 'o');
347 TRACE("\n");
350 static void poll_input(const struct Joystick *joy, DIJOYSTATE *state)
352 HRESULT hr;
354 hr = IDirectInputDevice8_Poll(joy->device);
356 /* If it failed, try to acquire the joystick */
357 if (FAILED(hr))
359 hr = IDirectInputDevice8_Acquire(joy->device);
360 while (hr == DIERR_INPUTLOST) hr = IDirectInputDevice8_Acquire(joy->device);
363 if (hr == DIERR_OTHERAPPHASPRIO) return;
365 IDirectInputDevice8_GetDeviceState(joy->device, sizeof(DIJOYSTATE), state);
368 static DWORD WINAPI input_thread(void *param)
370 int axes_pos[TEST_MAX_AXES][2];
371 DIJOYSTATE state;
372 struct JoystickData *data = param;
374 /* Setup POV as clock positions
376 * 31500 4500
377 * 27000 -1 9000
378 * 22500 13500
379 * 18000
381 int ma = TEST_AXIS_MAX;
382 int pov_val[9] = {0, 4500, 9000, 13500,
383 18000, 22500, 27000, 31500, -1};
384 int pov_pos[9][2] = { {0, -ma}, {ma/2, -ma/2}, {ma, 0}, {ma/2, ma/2},
385 {0, ma}, {-ma/2, ma/2}, {-ma, 0}, {-ma/2, -ma/2}, {0, 0} };
387 ZeroMemory(&state, sizeof(state));
389 while (!data->stop)
391 int i;
392 unsigned int j;
394 poll_input(&data->joysticks[data->chosen_joystick], &state);
396 dump_joy_state(&state, data->joysticks[data->chosen_joystick].num_buttons);
398 /* Indicate pressed buttons */
399 for (i = 0; i < data->joysticks[data->chosen_joystick].num_buttons; i++)
400 SendMessageW(data->graphics.buttons[i], BM_SETSTATE, !!state.rgbButtons[i], 0);
402 /* Indicate axis positions, axes showing are hardcoded for now */
403 axes_pos[0][0] = state.lX;
404 axes_pos[0][1] = state.lY;
405 axes_pos[1][0] = state.lRx;
406 axes_pos[1][1] = state.lRy;
407 axes_pos[2][0] = state.lZ;
408 axes_pos[2][1] = state.lRz;
410 /* Set pov values */
411 for (j = 0; j < ARRAY_SIZE(pov_val); j++)
413 if (state.rgdwPOV[0] == pov_val[j])
415 axes_pos[3][0] = pov_pos[j][0];
416 axes_pos[3][1] = pov_pos[j][1];
420 for (i = 0; i < TEST_MAX_AXES; i++)
422 RECT r;
424 r.left = (TEST_AXIS_X + TEST_NEXT_AXIS_X*i + axes_pos[i][0]);
425 r.top = (TEST_AXIS_Y + axes_pos[i][1]);
426 r.bottom = r.right = 0; /* unused */
427 MapDialogRect(data->graphics.hwnd, &r);
429 SetWindowPos(data->graphics.axes[i], 0, r.left, r.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
432 Sleep(TEST_POLL_TIME);
435 return 0;
438 static void test_handle_joychange(HWND hwnd, struct JoystickData *data)
440 int i;
442 if (data->num_joysticks == 0) return;
444 data->chosen_joystick = SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_GETCURSEL, 0, 0);
446 /* Enable only buttons present in the device */
447 for (i = 0; i < TEST_MAX_BUTTONS; i++)
448 ShowWindow(data->graphics.buttons[i], i < data->joysticks[data->chosen_joystick].num_buttons);
451 /*********************************************************************
452 * button_number_to_wchar [internal]
453 * Transforms an integer in the interval [0,99] into a 2 character WCHAR string
455 static void button_number_to_wchar(int n, WCHAR str[3])
457 str[1] = n % 10 + '0';
458 n /= 10;
459 str[0] = n % 10 + '0';
460 str[2] = '\0';
463 static void draw_joystick_buttons(HWND hwnd, struct JoystickData* data)
465 int i;
466 int row = 0, col = 0;
467 WCHAR button_label[3];
468 HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
470 for (i = 0; i < TEST_MAX_BUTTONS; i++)
472 RECT r;
474 if ((i % TEST_BUTTON_COL_MAX) == 0 && i != 0)
476 row += 1;
477 col = 0;
480 r.left = (TEST_BUTTON_X + TEST_NEXT_BUTTON_X*col);
481 r.top = (TEST_BUTTON_Y + TEST_NEXT_BUTTON_Y*row);
482 r.right = r.left + TEST_BUTTON_SIZE_X;
483 r.bottom = r.top + TEST_BUTTON_SIZE_Y;
484 MapDialogRect(hwnd, &r);
486 button_number_to_wchar(i + 1, button_label);
488 data->graphics.buttons[i] = CreateWindowW(L"Button", button_label, WS_CHILD,
489 r.left, r.top, r.right - r.left, r.bottom - r.top,
490 hwnd, NULL, NULL, hinst);
492 col += 1;
496 static void draw_joystick_axes(HWND hwnd, struct JoystickData* data)
498 int i;
499 HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
500 static const WCHAR axes_names[TEST_MAX_AXES][7] = { L"X,Y", L"Rx,Ry", L"Z,Rz", L"POV"};
501 static const DWORD axes_idc[TEST_MAX_AXES] = { IDC_TESTGROUPXY, IDC_TESTGROUPRXRY,
502 IDC_TESTGROUPZRZ, IDC_TESTGROUPPOV };
504 for (i = 0; i < TEST_MAX_AXES; i++)
506 RECT r;
507 /* Set axis box name */
508 SetWindowTextW(GetDlgItem(hwnd, axes_idc[i]), axes_names[i]);
510 r.left = (TEST_AXIS_X + TEST_NEXT_AXIS_X*i);
511 r.top = TEST_AXIS_Y;
512 r.right = r.left + TEST_AXIS_SIZE_X;
513 r.bottom = r.top + TEST_AXIS_SIZE_Y;
514 MapDialogRect(hwnd, &r);
516 data->graphics.axes[i] = CreateWindowW(L"Button", NULL, WS_CHILD | WS_VISIBLE,
517 r.left, r.top, r.right - r.left, r.bottom - r.top,
518 hwnd, NULL, NULL, hinst);
522 /*********************************************************************
523 * test_dlgproc [internal]
526 static INT_PTR CALLBACK test_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
528 static HANDLE thread;
529 static struct JoystickData *data;
530 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
532 switch (msg)
534 case WM_INITDIALOG:
536 int i;
538 data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
540 /* Add enumerated joysticks to the combobox */
541 for (i = 0; i < data->num_joysticks; i++)
543 struct Joystick *joy = &data->joysticks[i];
544 SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
547 draw_joystick_buttons(hwnd, data);
548 draw_joystick_axes(hwnd, data);
550 return TRUE;
553 case WM_COMMAND:
554 switch(wparam)
556 case MAKEWPARAM(IDC_TESTSELECTCOMBO, CBN_SELCHANGE):
557 test_handle_joychange(hwnd, data);
558 break;
560 return TRUE;
562 case WM_NOTIFY:
563 switch(((LPNMHDR)lparam)->code)
565 case PSN_SETACTIVE:
567 DWORD tid;
569 /* Initialize input thread */
570 if (data->num_joysticks > 0)
572 data->stop = FALSE;
574 /* Set the first joystick as default */
575 SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_SETCURSEL, 0, 0);
576 test_handle_joychange(hwnd, data);
578 thread = CreateThread(NULL, 0, input_thread, (void*) data, 0, &tid);
581 break;
583 case PSN_RESET: /* intentional fall-through */
584 case PSN_KILLACTIVE:
585 /* Stop input thread */
586 data->stop = TRUE;
587 MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, 0);
588 CloseHandle(thread);
589 break;
591 return TRUE;
593 return FALSE;
596 /*********************************************************************
597 * Joystick force feedback testing functions
600 static void draw_ff_axis(HWND hwnd, struct JoystickData *data)
602 HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
603 RECT r;
605 r.left = FF_AXIS_X;
606 r.top = FF_AXIS_Y;
607 r.right = r.left + FF_AXIS_SIZE_X;
608 r.bottom = r.top + FF_AXIS_SIZE_Y;
609 MapDialogRect(hwnd, &r);
611 /* Draw direction axis */
612 data->graphics.ff_axis = CreateWindowW(L"Button", NULL, WS_CHILD | WS_VISIBLE,
613 r.left, r.top, r.right - r.left, r.bottom - r.top,
614 hwnd, NULL, NULL, hinst);
617 static void initialize_effects_list(HWND hwnd, struct Joystick* joy)
619 int i;
621 SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_RESETCONTENT, 0, 0);
623 for (i=0; i < joy->num_effects; i++)
625 /* Effect names start with GUID_, so we'll skip this part */
626 WCHAR *name = joy->effects[i].info.tszName + 5;
627 SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_ADDSTRING, 0, (LPARAM) name);
631 static void ff_handle_joychange(HWND hwnd, struct JoystickData *data)
633 int sel;
635 if (data->num_ff == 0) return;
637 sel = SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_GETCURSEL, 0, 0);
638 data->chosen_joystick = SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_GETITEMDATA, sel, 0);
639 initialize_effects_list(hwnd, &data->joysticks[data->chosen_joystick]);
642 static void ff_handle_effectchange(HWND hwnd, struct Joystick *joy)
644 int sel = SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_GETCURSEL, 0, 0);
646 if (sel < 0) return;
648 joy->chosen_effect = sel;
651 static DWORD WINAPI ff_input_thread(void *param)
653 struct JoystickData *data = param;
654 DIJOYSTATE state;
656 ZeroMemory(&state, sizeof(state));
658 while (!data->stop)
660 int i;
661 struct Joystick *joy = &data->joysticks[data->chosen_joystick];
662 int chosen_effect = joy->chosen_effect;
663 DIEFFECT *dieffect;
664 DWORD flags = DIEP_AXES | DIEP_DIRECTION | DIEP_NORESTART;
665 RECT r;
667 Sleep(TEST_POLL_TIME);
669 /* Skip this if we have no effects */
670 if (joy->num_effects == 0 || chosen_effect < 0) continue;
672 poll_input(joy, &state);
674 /* Set ff parameters and draw the axis */
675 dieffect = &joy->effects[chosen_effect].params;
676 dieffect->rgdwAxes[0] = state.lX;
677 dieffect->rgdwAxes[1] = state.lY;
679 r.left = FF_AXIS_X + state.lX;
680 r.top = FF_AXIS_Y + state.lY;
681 r.right = r.bottom = 0; /* unused */
682 MapDialogRect(data->graphics.hwnd, &r);
684 SetWindowPos(data->graphics.ff_axis, 0, r.left, r.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
686 for (i=0; i < joy->num_buttons; i++)
687 if (state.rgbButtons[i])
689 IDirectInputEffect_SetParameters(joy->effects[chosen_effect].effect, dieffect, flags);
690 IDirectInputEffect_Start(joy->effects[chosen_effect].effect, 1, 0);
691 break;
695 return 0;
698 /***********************************************************************
699 * ff_effects_callback [internal]
700 * Enumerates, creates, sets the some parameters and stores all ff effects
701 * supported by the joystick. Works like enum_callback, counting the effects
702 * first and then storing them.
704 static BOOL CALLBACK ff_effects_callback(const DIEFFECTINFOW *pdei, void *pvRef)
706 HRESULT hr;
707 DIEFFECT dieffect;
708 DWORD axes[2] = {DIJOFS_X, DIJOFS_Y};
709 int direction[2] = {0, 0};
710 struct Joystick *joystick = pvRef;
711 DIRAMPFORCE rforce;
712 DICONSTANTFORCE cforce;
713 DIPERIODIC pforce;
714 DICONDITION cdforce;
716 if (joystick->effects == NULL)
718 joystick->num_effects += 1;
719 return DIENUM_CONTINUE;
722 hr = IDirectInputDevice8_Acquire(joystick->device);
724 if (FAILED(hr)) return DIENUM_CONTINUE;
726 ZeroMemory(&dieffect, sizeof(dieffect));
728 dieffect.dwSize = sizeof(dieffect);
729 dieffect.dwFlags = DIEFF_CARTESIAN;
730 dieffect.dwDuration = FF_PLAY_TIME;
732 dieffect.cAxes = 2;
733 dieffect.rgdwAxes = axes;
734 dieffect.rglDirection = direction;
736 if (IsEqualGUID(&pdei->guid, &GUID_RampForce))
738 rforce.lStart = 0;
739 rforce.lEnd = DI_FFNOMINALMAX;
741 dieffect.cbTypeSpecificParams = sizeof(rforce);
742 dieffect.lpvTypeSpecificParams = &rforce;
743 dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
745 else if (IsEqualGUID(&pdei->guid, &GUID_ConstantForce))
747 cforce.lMagnitude = DI_FFNOMINALMAX;
749 dieffect.cbTypeSpecificParams = sizeof(cforce);
750 dieffect.lpvTypeSpecificParams = &cforce;
751 dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
753 else if (IsEqualGUID(&pdei->guid, &GUID_Sine) ||
754 IsEqualGUID(&pdei->guid, &GUID_Square) ||
755 IsEqualGUID(&pdei->guid, &GUID_Triangle) ||
756 IsEqualGUID(&pdei->guid, &GUID_SawtoothUp) ||
757 IsEqualGUID(&pdei->guid, &GUID_SawtoothDown))
759 pforce.dwMagnitude = DI_FFNOMINALMAX;
760 pforce.lOffset = 0;
761 pforce.dwPhase = 0;
762 pforce.dwPeriod = FF_PERIOD_TIME;
764 dieffect.cbTypeSpecificParams = sizeof(pforce);
765 dieffect.lpvTypeSpecificParams = &pforce;
766 dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
768 else if (IsEqualGUID(&pdei->guid, &GUID_Spring) ||
769 IsEqualGUID(&pdei->guid, &GUID_Damper) ||
770 IsEqualGUID(&pdei->guid, &GUID_Inertia) ||
771 IsEqualGUID(&pdei->guid, &GUID_Friction))
773 cdforce.dwPositiveSaturation = 10000;
774 cdforce.dwNegativeSaturation = 10000;
775 cdforce.lPositiveCoefficient = 10000;
776 cdforce.lNegativeCoefficient = 10000;
777 cdforce.lDeadBand = 0;
778 cdforce.lOffset = 0;
780 dieffect.cbTypeSpecificParams = sizeof(cdforce);
781 dieffect.lpvTypeSpecificParams = &cdforce;
782 dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
785 hr = IDirectInputDevice2_CreateEffect(
786 joystick->device, &pdei->guid, &dieffect, &joystick->effects[joystick->cur_effect].effect, NULL);
788 joystick->effects[joystick->cur_effect].params = dieffect;
789 joystick->effects[joystick->cur_effect].info = *pdei;
790 joystick->cur_effect += 1;
792 return DIENUM_CONTINUE;
795 /*********************************************************************
796 * ff_dlgproc [internal]
799 static INT_PTR CALLBACK ff_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
801 static HANDLE thread;
802 static struct JoystickData *data;
803 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
805 switch (msg)
807 case WM_INITDIALOG:
809 int i, cur = 0;
811 data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
813 /* Add joysticks with FF support to the combobox and get the effects */
814 for (i = 0; i < data->num_joysticks; i++)
816 struct Joystick *joy = &data->joysticks[i];
818 if (joy->forcefeedback)
820 SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
821 SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_SETITEMDATA, cur, i);
823 cur++;
825 /* Count device effects and then store them */
826 joy->num_effects = 0;
827 joy->effects = NULL;
828 IDirectInputDevice8_EnumEffects(joy->device, ff_effects_callback, (void *) joy, 0);
829 joy->effects = HeapAlloc(GetProcessHeap(), 0, sizeof(struct Effect) * joy->num_effects);
831 joy->cur_effect = 0;
832 IDirectInputDevice8_EnumEffects(joy->device, ff_effects_callback, (void*) joy, 0);
833 joy->num_effects = joy->cur_effect;
837 draw_ff_axis(hwnd, data);
839 return TRUE;
842 case WM_COMMAND:
843 switch(wparam)
845 case MAKEWPARAM(IDC_FFSELECTCOMBO, CBN_SELCHANGE):
846 ff_handle_joychange(hwnd, data);
848 SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_SETCURSEL, 0, 0);
849 ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
850 break;
852 case MAKEWPARAM(IDC_FFEFFECTLIST, LBN_SELCHANGE):
853 ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
854 break;
856 return TRUE;
858 case WM_NOTIFY:
859 switch(((LPNMHDR)lparam)->code)
861 case PSN_SETACTIVE:
862 if (data->num_ff > 0)
864 DWORD tid;
866 data->stop = FALSE;
867 /* Set the first joystick as default */
868 SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_SETCURSEL, 0, 0);
869 ff_handle_joychange(hwnd, data);
871 SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_SETCURSEL, 0, 0);
872 ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
874 thread = CreateThread(NULL, 0, ff_input_thread, (void*) data, 0, &tid);
876 break;
878 case PSN_RESET: /* intentional fall-through */
879 case PSN_KILLACTIVE:
880 /* Stop ff thread */
881 data->stop = TRUE;
882 MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, 0);
883 CloseHandle(thread);
884 break;
886 return TRUE;
888 return FALSE;
891 /******************************************************************************
892 * propsheet_callback [internal]
894 static int CALLBACK propsheet_callback(HWND hwnd, UINT msg, LPARAM lparam)
896 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
897 switch (msg)
899 case PSCB_INITIALIZED:
900 break;
902 return 0;
905 /******************************************************************************
906 * display_cpl_sheets [internal]
908 * Build and display the dialog with all control panel propertysheets
911 static void display_cpl_sheets(HWND parent, struct JoystickData *data)
913 INITCOMMONCONTROLSEX icex;
914 PROPSHEETPAGEW psp[NUM_PROPERTY_PAGES];
915 PROPSHEETHEADERW psh;
916 DWORD id = 0;
918 OleInitialize(NULL);
919 /* Initialize common controls */
920 icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
921 icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES;
922 InitCommonControlsEx(&icex);
924 ZeroMemory(&psh, sizeof(psh));
925 ZeroMemory(psp, sizeof(psp));
927 /* Fill out all PROPSHEETPAGE */
928 psp[id].dwSize = sizeof (PROPSHEETPAGEW);
929 psp[id].hInstance = hcpl;
930 psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_LIST);
931 psp[id].pfnDlgProc = list_dlgproc;
932 psp[id].lParam = (INT_PTR) data;
933 id++;
935 psp[id].dwSize = sizeof (PROPSHEETPAGEW);
936 psp[id].hInstance = hcpl;
937 psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_TEST);
938 psp[id].pfnDlgProc = test_dlgproc;
939 psp[id].lParam = (INT_PTR) data;
940 id++;
942 psp[id].dwSize = sizeof (PROPSHEETPAGEW);
943 psp[id].hInstance = hcpl;
944 psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_FORCEFEEDBACK);
945 psp[id].pfnDlgProc = ff_dlgproc;
946 psp[id].lParam = (INT_PTR) data;
947 id++;
949 /* Fill out the PROPSHEETHEADER */
950 psh.dwSize = sizeof (PROPSHEETHEADERW);
951 psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID | PSH_USECALLBACK;
952 psh.hwndParent = parent;
953 psh.hInstance = hcpl;
954 psh.pszCaption = MAKEINTRESOURCEW(IDS_CPL_NAME);
955 psh.nPages = id;
956 psh.u3.ppsp = psp;
957 psh.pfnCallback = propsheet_callback;
959 /* display the dialog */
960 PropertySheetW(&psh);
962 OleUninitialize();
965 /*********************************************************************
966 * CPlApplet (joy.cpl.@)
968 * Control Panel entry point
970 * PARAMS
971 * hWnd [I] Handle for the Control Panel Window
972 * command [I] CPL_* Command
973 * lParam1 [I] first extra Parameter
974 * lParam2 [I] second extra Parameter
976 * RETURNS
977 * Depends on the command
980 LONG CALLBACK CPlApplet(HWND hwnd, UINT command, LPARAM lParam1, LPARAM lParam2)
982 static struct JoystickData data;
983 TRACE("(%p, %u, 0x%lx, 0x%lx)\n", hwnd, command, lParam1, lParam2);
985 switch (command)
987 case CPL_INIT:
989 HRESULT hr;
991 /* Initialize dinput */
992 hr = DirectInput8Create(GetModuleHandleW(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void**)&data.di, NULL);
994 if (FAILED(hr))
996 ERR("Failed to initialize DirectInput: 0x%08x\n", hr);
997 return FALSE;
1000 /* Then get all the connected joysticks */
1001 initialize_joysticks(&data);
1003 return TRUE;
1005 case CPL_GETCOUNT:
1006 return 1;
1008 case CPL_INQUIRE:
1010 CPLINFO *appletInfo = (CPLINFO *) lParam2;
1012 appletInfo->idIcon = ICO_MAIN;
1013 appletInfo->idName = IDS_CPL_NAME;
1014 appletInfo->idInfo = IDS_CPL_INFO;
1015 appletInfo->lData = 0;
1016 return TRUE;
1019 case CPL_DBLCLK:
1020 display_cpl_sheets(hwnd, &data);
1021 break;
1023 case CPL_STOP:
1024 destroy_joysticks(&data);
1026 /* And destroy dinput too */
1027 IDirectInput8_Release(data.di);
1028 break;
1031 return FALSE;