joy.cpl: Added force feedback direction control.
[wine/multimedia.git] / dlls / joy.cpl / main.c
blobc8b4a270cfeae9288e6660dacf8da95560d209dc
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;
94 if (joystick->forcefeedback) data->num_ff++;
96 /* Set axis range to ease the GUI visualization */
97 proprange.diph.dwSize = sizeof(DIPROPRANGE);
98 proprange.diph.dwHeaderSize = sizeof(DIPROPHEADER);
99 proprange.diph.dwHow = DIPH_DEVICE;
100 proprange.diph.dwObj = 0;
101 proprange.lMin = TEST_AXIS_MIN;
102 proprange.lMax = TEST_AXIS_MAX;
104 IDirectInputDevice_SetProperty(joystick->device, DIPROP_RANGE, &proprange.diph);
106 return DIENUM_CONTINUE;
109 /***********************************************************************
110 * initialize_joysticks [internal]
112 static void initialize_joysticks(struct JoystickData *data)
114 data->num_joysticks = 0;
115 data->cur_joystick = 0;
116 IDirectInput8_EnumDevices(data->di, DI8DEVCLASS_GAMECTRL, enum_callback, data, DIEDFL_ATTACHEDONLY);
117 data->joysticks = HeapAlloc(GetProcessHeap(), 0, sizeof(struct Joystick) * data->num_joysticks);
119 /* Get all the joysticks */
120 IDirectInput8_EnumDevices(data->di, DI8DEVCLASS_GAMECTRL, enum_callback, data, DIEDFL_ATTACHEDONLY);
123 /***********************************************************************
124 * destroy_joysticks [internal]
126 static void destroy_joysticks(struct JoystickData *data)
128 int i, j;
130 for (i = 0; i < data->num_joysticks; i++)
133 if (data->joysticks[i].forcefeedback)
135 for (j = 0; j < data->joysticks[i].num_effects; j++)
136 IDirectInputEffect_Release(data->joysticks[i].effects[j].effect);
138 HeapFree(GetProcessHeap(), 0, data->joysticks[i].effects);
141 IDirectInputDevice8_Unacquire(data->joysticks[i].device);
142 IDirectInputDevice8_Release(data->joysticks[i].device);
145 HeapFree(GetProcessHeap(), 0, data->joysticks);
148 /*********************************************************************
149 * list_dlgproc [internal]
152 static INT_PTR CALLBACK list_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
154 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
155 switch (msg)
157 case WM_INITDIALOG:
159 int i;
160 struct JoystickData *data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
162 /* Set dialog information */
163 for (i = 0; i < data->num_joysticks; i++)
165 struct Joystick *joy = &data->joysticks[i];
166 SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
169 return TRUE;
172 case WM_COMMAND:
174 switch (LOWORD(wparam))
176 case IDC_BUTTONDISABLE:
177 FIXME("Disable selected joystick from being enumerated\n");
178 break;
180 case IDC_BUTTONENABLE:
181 FIXME("Re-Enable selected joystick\n");
182 break;
185 return TRUE;
187 case WM_NOTIFY:
188 return TRUE;
190 default:
191 break;
193 return FALSE;
196 /*********************************************************************
197 * Joystick testing functions
200 static void dump_joy_state(DIJOYSTATE* st, int num_buttons)
202 int i;
203 TRACE("Ax (% 5d,% 5d,% 5d)\n", st->lX,st->lY, st->lZ);
204 TRACE("RAx (% 5d,% 5d,% 5d)\n", st->lRx, st->lRy, st->lRz);
205 TRACE("Slider (% 5d,% 5d)\n", st->rglSlider[0], st->rglSlider[1]);
206 TRACE("Pov (% 5d,% 5d,% 5d,% 5d)\n", st->rgdwPOV[0], st->rgdwPOV[1], st->rgdwPOV[2], st->rgdwPOV[3]);
208 TRACE("Buttons ");
209 for(i=0; i < num_buttons; i++)
210 TRACE(" %c",st->rgbButtons[i] ? 'x' : 'o');
211 TRACE("\n");
214 static void poll_input(const struct Joystick *joy, DIJOYSTATE *state)
216 HRESULT hr;
218 hr = IDirectInputDevice8_Poll(joy->device);
220 /* If it failed, try to acquire the joystick */
221 if (FAILED(hr))
223 hr = IDirectInputDevice8_Acquire(joy->device);
224 while (hr == DIERR_INPUTLOST) hr = IDirectInputDevice8_Acquire(joy->device);
227 if (hr == DIERR_OTHERAPPHASPRIO) return;
229 IDirectInputDevice8_GetDeviceState(joy->device, sizeof(DIJOYSTATE), state);
232 static DWORD WINAPI input_thread(void *param)
234 int axes_pos[TEST_MAX_AXES][2];
235 DIJOYSTATE state;
236 struct JoystickData *data = param;
238 /* Setup POV as clock positions
240 * 31500 4500
241 * 27000 -1 9000
242 * 22500 13500
243 * 18000
245 int ma = TEST_AXIS_MAX;
246 int pov_val[9] = {0, 4500, 9000, 13500,
247 18000, 22500, 27000, 31500, -1};
248 int pov_pos[9][2] = { {0, -ma}, {ma/2, -ma/2}, {ma, 0}, {ma/2, ma/2},
249 {0, ma}, {-ma/2, ma/2}, {-ma, 0}, {-ma/2, -ma/2}, {0, 0} };
251 ZeroMemory(&state, sizeof(state));
253 while (!data->stop)
255 int i;
256 poll_input(&data->joysticks[data->chosen_joystick], &state);
258 dump_joy_state(&state, data->joysticks[data->chosen_joystick].num_buttons);
260 /* Indicate pressed buttons */
261 for (i = 0; i < data->joysticks[data->chosen_joystick].num_buttons; i++)
262 if (state.rgbButtons[i])
263 SendMessageW(data->buttons[i], BM_SETSTATE, TRUE, 0);
265 /* Indicate axis positions, axes showing are hardcoded for now */
266 axes_pos[0][0] = state.lX;
267 axes_pos[0][1] = state.lY;
268 axes_pos[1][0] = state.lRx;
269 axes_pos[1][1] = state.lRy;
270 axes_pos[2][0] = state.lZ;
271 axes_pos[2][1] = state.lRz;
273 /* Set pov values */
274 for (i = 0; i < sizeof(pov_val)/sizeof(pov_val[0]); i++)
276 if (state.rgdwPOV[0] == pov_val[i])
278 axes_pos[3][0] = pov_pos[i][0];
279 axes_pos[3][1] = pov_pos[i][1];
283 for (i = 0; i < TEST_MAX_AXES; i++)
284 SetWindowPos(data->axes[i], 0,
285 TEST_AXIS_X + TEST_NEXT_AXIS_X*i + axes_pos[i][0],
286 TEST_AXIS_Y + axes_pos[i][1],
287 0, 0, SWP_NOZORDER | SWP_NOSIZE);
289 Sleep(TEST_POLL_TIME);
291 /* Reset button state */
292 for (i = 0; i < data->joysticks[data->chosen_joystick].num_buttons; i++)
293 SendMessageW(data->buttons[i], BM_SETSTATE, FALSE, 0);
296 return 0;
299 static void test_handle_joychange(HWND hwnd, struct JoystickData *data)
301 int i;
303 if (data->num_joysticks == 0) return;
305 data->chosen_joystick = SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_GETCURSEL, 0, 0);
307 /* Enable only buttons present in the device */
308 for (i = 0; i < TEST_MAX_BUTTONS; i++)
309 ShowWindow(data->buttons[i], i <= data->joysticks[data->chosen_joystick].num_buttons);
312 /*********************************************************************
313 * button_number_to_wchar [internal]
314 * Transforms an integer in the interval [0,99] into a 2 character WCHAR string
316 static void button_number_to_wchar(int n, WCHAR str[3])
318 str[1] = n % 10 + '0';
319 n /= 10;
320 str[0] = n % 10 + '0';
321 str[2] = '\0';
324 static void draw_joystick_buttons(HWND hwnd, struct JoystickData* data)
326 int i;
327 int row = 0, col = 0;
328 WCHAR button_label[3];
329 HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
330 static WCHAR button_class[] = {'B','u','t','t','o','n','\0'};
332 for (i = 0; i < TEST_MAX_BUTTONS; i++)
334 if ((i % TEST_BUTTON_COL_MAX) == 0 && i != 0)
336 row += 1;
337 col = 0;
340 button_number_to_wchar(i + 1, button_label);
342 data->buttons[i] = CreateWindowW(button_class, button_label, WS_CHILD,
343 TEST_BUTTON_X + TEST_NEXT_BUTTON_X*col, TEST_BUTTON_Y + TEST_NEXT_BUTTON_Y*row,
344 TEST_BUTTON_SIZE_X, TEST_BUTTON_SIZE_Y,
345 hwnd, NULL, NULL, hinst);
347 col += 1;
351 static void draw_joystick_axes(HWND hwnd, struct JoystickData* data)
353 int i;
354 HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
355 static const WCHAR button_class[] = {'B','u','t','t','o','n','\0'};
356 static const WCHAR axes_names[TEST_MAX_AXES][7] = { {'X',',','Y','\0'}, {'R','x',',','R','y','\0'},
357 {'Z',',','R','z','\0'}, {'P','O','V','\0'} };
358 static const DWORD axes_idc[TEST_MAX_AXES] = { IDC_TESTGROUPXY, IDC_TESTGROUPRXRY,
359 IDC_TESTGROUPZRZ, IDC_TESTGROUPPOV };
361 for (i = 0; i < TEST_MAX_AXES; i++)
363 /* Set axis box name */
364 SetWindowTextW(GetDlgItem(hwnd, axes_idc[i]), axes_names[i]);
366 data->axes[i] = CreateWindowW( button_class, NULL, WS_CHILD | WS_VISIBLE,
367 TEST_AXIS_X + TEST_NEXT_AXIS_X*i, TEST_AXIS_Y,
368 TEST_AXIS_SIZE_X, TEST_AXIS_SIZE_Y,
369 hwnd, (HMENU) IDC_JOYSTICKAXES + i, NULL, hinst);
373 /*********************************************************************
374 * test_dlgproc [internal]
377 static INT_PTR CALLBACK test_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
379 static HANDLE thread;
380 static struct JoystickData *data;
381 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
383 switch (msg)
385 case WM_INITDIALOG:
387 int i;
389 data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
391 /* Add enumerated joysticks to the combobox */
392 for (i = 0; i < data->num_joysticks; i++)
394 struct Joystick *joy = &data->joysticks[i];
395 SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
398 draw_joystick_buttons(hwnd, data);
399 draw_joystick_axes(hwnd, data);
401 return TRUE;
404 case WM_COMMAND:
405 switch(wparam)
407 case MAKEWPARAM(IDC_TESTSELECTCOMBO, CBN_SELCHANGE):
408 test_handle_joychange(hwnd, data);
409 break;
411 return TRUE;
413 case WM_NOTIFY:
414 switch(((LPNMHDR)lparam)->code)
416 case PSN_SETACTIVE:
418 DWORD tid;
420 /* Initialize input thread */
421 if (data->num_joysticks > 0)
423 data->stop = FALSE;
425 /* Set the first joystick as default */
426 SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_SETCURSEL, 0, 0);
427 test_handle_joychange(hwnd, data);
429 thread = CreateThread(NULL, 0, input_thread, (void*) data, 0, &tid);
432 break;
434 case PSN_RESET: /* intentional fall-through */
435 case PSN_KILLACTIVE:
436 /* Stop input thread */
437 data->stop = TRUE;
438 MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, 0);
439 CloseHandle(thread);
440 break;
442 return TRUE;
444 return FALSE;
447 /*********************************************************************
448 * Joystick force feedback testing functions
451 static void draw_ff_axis(HWND hwnd, struct JoystickData *data)
453 HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
454 static WCHAR button_class[] = {'B','u','t','t','o','n','\0'};
456 /* Draw direction axis */
457 data->ff_axis = CreateWindowW( button_class, NULL, WS_CHILD | WS_VISIBLE,
458 FF_AXIS_X, FF_AXIS_Y,
459 FF_AXIS_SIZE_X, FF_AXIS_SIZE_Y,
460 hwnd, (HMENU) IDC_FFAXIS, NULL, hinst);
463 static void initialize_effects_list(HWND hwnd, struct Joystick* joy)
465 int i;
467 SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_RESETCONTENT, 0, 0);
469 for (i=0; i < joy->num_effects; i++)
471 /* Effect names start with GUID_, so we'll skip this part */
472 WCHAR *name = joy->effects[i].info.tszName + 5;
473 SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_ADDSTRING, 0, (LPARAM) name);
477 static void ff_handle_joychange(HWND hwnd, struct JoystickData *data)
479 int sel;
481 if (data->num_ff == 0) return;
483 sel = SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_GETCURSEL, 0, 0);
484 data->chosen_joystick = SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_GETITEMDATA, sel, 0);
485 initialize_effects_list(hwnd, &data->joysticks[data->chosen_joystick]);
488 static void ff_handle_effectchange(HWND hwnd, struct Joystick *joy)
490 int sel = SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_GETCURSEL, 0, 0);
492 if (sel < 0) return;
494 joy->chosen_effect = sel;
497 static DWORD WINAPI ff_input_thread(void *param)
499 struct JoystickData *data = param;
500 DIJOYSTATE state;
502 ZeroMemory(&state, sizeof(state));
504 while (!data->stop)
506 int i;
507 struct Joystick *joy = &data->joysticks[data->chosen_joystick];
508 int chosen_effect = joy->chosen_effect;
509 DIEFFECT *dieffect;
510 DWORD flags = DIEP_AXES | DIEP_DIRECTION | DIEP_NORESTART;
512 /* Skip this if we have no effects */
513 if (joy->num_effects == 0 || chosen_effect < 0) continue;
515 poll_input(joy, &state);
517 /* Set ff parameters and draw the axis */
518 dieffect = &joy->effects[chosen_effect].params;
519 dieffect->rgdwAxes[0] = state.lX;
520 dieffect->rgdwAxes[1] = state.lY;
522 SetWindowPos(data->ff_axis, 0, FF_AXIS_X + state.lX, FF_AXIS_Y + state.lY,
523 0, 0, SWP_NOZORDER | SWP_NOSIZE);
525 for (i=0; i < joy->num_buttons; i++)
526 if (state.rgbButtons[i])
528 IDirectInputEffect_SetParameters(joy->effects[chosen_effect].effect, dieffect, flags);
529 IDirectInputEffect_Start(joy->effects[chosen_effect].effect, 1, 0);
530 break;
533 Sleep(TEST_POLL_TIME);
536 return 0;
539 /***********************************************************************
540 * ff_effects_callback [internal]
541 * Enumerates, creates, sets the some parameters and stores all ff effects
542 * supported by the joystick. Works like enum_callback, counting the effects
543 * first and then storing them.
545 static BOOL CALLBACK ff_effects_callback(const DIEFFECTINFOW *pdei, void *pvRef)
547 HRESULT hr;
548 DIEFFECT dieffect;
549 DWORD axes[2] = {DIJOFS_X, DIJOFS_Y};
550 int direction[2] = {0, 0};
551 struct Joystick *joystick = pvRef;
553 if (joystick->effects == NULL)
555 joystick->num_effects += 1;
556 return DIENUM_CONTINUE;
559 hr = IDirectInputDevice8_Acquire(joystick->device);
561 if (FAILED(hr)) return DIENUM_CONTINUE;
563 ZeroMemory(&dieffect, sizeof(dieffect));
565 dieffect.dwSize = sizeof(dieffect);
566 dieffect.dwFlags = DIEFF_CARTESIAN;
567 dieffect.dwDuration = FF_PLAY_TIME;
569 dieffect.cAxes = 2;
570 dieffect.rgdwAxes = axes;
571 dieffect.rglDirection = direction;
573 if (IsEqualGUID(&pdei->guid, &GUID_RampForce))
575 DIRAMPFORCE rforce;
577 rforce.lStart = 0;
578 rforce.lEnd = DI_FFNOMINALMAX;
580 dieffect.cbTypeSpecificParams = sizeof(rforce);
581 dieffect.lpvTypeSpecificParams = &rforce;
582 dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
584 else if (IsEqualGUID(&pdei->guid, &GUID_ConstantForce))
586 DICONSTANTFORCE cforce;
588 cforce.lMagnitude = DI_FFNOMINALMAX;
590 dieffect.cbTypeSpecificParams = sizeof(cforce);
591 dieffect.lpvTypeSpecificParams = &cforce;
592 dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
594 else if (IsEqualGUID(&pdei->guid, &GUID_Sine) ||
595 IsEqualGUID(&pdei->guid, &GUID_Square) ||
596 IsEqualGUID(&pdei->guid, &GUID_Triangle) ||
597 IsEqualGUID(&pdei->guid, &GUID_SawtoothUp) ||
598 IsEqualGUID(&pdei->guid, &GUID_SawtoothDown))
600 DIPERIODIC pforce;
602 pforce.dwMagnitude = DI_FFNOMINALMAX;
603 pforce.lOffset = 0;
604 pforce.dwPhase = 0;
605 pforce.dwPeriod = FF_PERIOD_TIME;
607 dieffect.cbTypeSpecificParams = sizeof(pforce);
608 dieffect.lpvTypeSpecificParams = &pforce;
609 dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
612 hr = IDirectInputDevice2_CreateEffect(
613 joystick->device, &pdei->guid, &dieffect, &joystick->effects[joystick->cur_effect].effect, NULL);
615 joystick->effects[joystick->cur_effect].params = dieffect;
616 joystick->effects[joystick->cur_effect].info = *pdei;
617 joystick->cur_effect += 1;
619 return DIENUM_CONTINUE;
622 /*********************************************************************
623 * ff_dlgproc [internal]
626 static INT_PTR CALLBACK ff_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
628 static HANDLE thread;
629 static struct JoystickData *data;
630 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
632 switch (msg)
634 case WM_INITDIALOG:
636 int i, cur = 0;
638 data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
640 /* Add joysticks with FF support to the combobox and get the effects */
641 for (i = 0; i < data->num_joysticks; i++)
643 struct Joystick *joy = &data->joysticks[i];
645 if (joy->forcefeedback)
647 SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
648 SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_SETITEMDATA, cur, i);
650 cur++;
652 /* Count device effects and then store them */
653 joy->num_effects = 0;
654 joy->effects = NULL;
655 IDirectInputDevice8_EnumEffects(joy->device, ff_effects_callback, (void *) joy, 0);
656 joy->effects = HeapAlloc(GetProcessHeap(), 0, sizeof(struct Effect) * joy->num_effects);
658 joy->cur_effect = 0;
659 IDirectInputDevice8_EnumEffects(joy->device, ff_effects_callback, (void*) joy, 0);
660 FIXME("%d --- %d\n", joy->num_effects, joy->cur_effect);
661 joy->num_effects = joy->cur_effect;
666 draw_ff_axis(hwnd, data);
668 return TRUE;
671 case WM_COMMAND:
672 switch(wparam)
674 case MAKEWPARAM(IDC_FFSELECTCOMBO, CBN_SELCHANGE):
675 ff_handle_joychange(hwnd, data);
677 SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_SETCURSEL, 0, 0);
678 ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
679 break;
681 case MAKEWPARAM(IDC_FFEFFECTLIST, LBN_SELCHANGE):
682 ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
683 break;
685 return TRUE;
687 case WM_NOTIFY:
688 switch(((LPNMHDR)lparam)->code)
690 case PSN_SETACTIVE:
691 if (data->num_ff > 0)
693 DWORD tid;
695 data->stop = FALSE;
696 /* Set the first joystick as default */
697 SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_SETCURSEL, 0, 0);
698 ff_handle_joychange(hwnd, data);
700 SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_SETCURSEL, 0, 0);
701 ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
703 thread = CreateThread(NULL, 0, ff_input_thread, (void*) data, 0, &tid);
705 break;
707 case PSN_RESET: /* intentional fall-through */
708 case PSN_KILLACTIVE:
709 /* Stop ff thread */
710 data->stop = TRUE;
711 MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, 0);
712 CloseHandle(thread);
713 break;
715 return TRUE;
717 return FALSE;
720 /******************************************************************************
721 * propsheet_callback [internal]
723 static int CALLBACK propsheet_callback(HWND hwnd, UINT msg, LPARAM lparam)
725 TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
726 switch (msg)
728 case PSCB_INITIALIZED:
729 break;
731 return 0;
734 /******************************************************************************
735 * display_cpl_sheets [internal]
737 * Build and display the dialog with all control panel propertysheets
740 static void display_cpl_sheets(HWND parent, struct JoystickData *data)
742 INITCOMMONCONTROLSEX icex;
743 PROPSHEETPAGEW psp[NUM_PROPERTY_PAGES];
744 PROPSHEETHEADERW psh;
745 DWORD id = 0;
747 OleInitialize(NULL);
748 /* Initialize common controls */
749 icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
750 icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES;
751 InitCommonControlsEx(&icex);
753 ZeroMemory(&psh, sizeof(psh));
754 ZeroMemory(psp, sizeof(psp));
756 /* Fill out all PROPSHEETPAGE */
757 psp[id].dwSize = sizeof (PROPSHEETPAGEW);
758 psp[id].hInstance = hcpl;
759 psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_LIST);
760 psp[id].pfnDlgProc = list_dlgproc;
761 psp[id].lParam = (INT_PTR) data;
762 id++;
764 psp[id].dwSize = sizeof (PROPSHEETPAGEW);
765 psp[id].hInstance = hcpl;
766 psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_TEST);
767 psp[id].pfnDlgProc = test_dlgproc;
768 psp[id].lParam = (INT_PTR) data;
769 id++;
771 psp[id].dwSize = sizeof (PROPSHEETPAGEW);
772 psp[id].hInstance = hcpl;
773 psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_FORCEFEEDBACK);
774 psp[id].pfnDlgProc = ff_dlgproc;
775 psp[id].lParam = (INT_PTR) data;
776 id++;
778 /* Fill out the PROPSHEETHEADER */
779 psh.dwSize = sizeof (PROPSHEETHEADERW);
780 psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID | PSH_USECALLBACK;
781 psh.hwndParent = parent;
782 psh.hInstance = hcpl;
783 psh.pszCaption = MAKEINTRESOURCEW(IDS_CPL_NAME);
784 psh.nPages = id;
785 psh.u3.ppsp = psp;
786 psh.pfnCallback = propsheet_callback;
788 /* display the dialog */
789 PropertySheetW(&psh);
791 OleUninitialize();
794 /*********************************************************************
795 * CPlApplet (joy.cpl.@)
797 * Control Panel entry point
799 * PARAMS
800 * hWnd [I] Handle for the Control Panel Window
801 * command [I] CPL_* Command
802 * lParam1 [I] first extra Parameter
803 * lParam2 [I] second extra Parameter
805 * RETURNS
806 * Depends on the command
809 LONG CALLBACK CPlApplet(HWND hwnd, UINT command, LPARAM lParam1, LPARAM lParam2)
811 static struct JoystickData data;
812 TRACE("(%p, %u, 0x%lx, 0x%lx)\n", hwnd, command, lParam1, lParam2);
814 switch (command)
816 case CPL_INIT:
818 HRESULT hr;
820 /* Initialize dinput */
821 hr = DirectInput8Create(GetModuleHandleW(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void**)&data.di, NULL);
823 if (FAILED(hr))
825 ERR("Failed to initialize DirectInput: 0x%08x\n", hr);
826 return FALSE;
829 /* Then get all the connected joysticks */
830 initialize_joysticks(&data);
832 return TRUE;
834 case CPL_GETCOUNT:
835 return 1;
837 case CPL_INQUIRE:
839 CPLINFO *appletInfo = (CPLINFO *) lParam2;
841 appletInfo->idName = IDS_CPL_NAME;
842 appletInfo->idInfo = IDS_CPL_INFO;
843 appletInfo->lData = 0;
844 return TRUE;
847 case CPL_DBLCLK:
848 display_cpl_sheets(hwnd, &data);
849 break;
851 case CPL_STOP:
852 destroy_joysticks(&data);
854 /* And destroy dinput too */
855 IDirectInput8_Release(data.di);
856 break;
859 return FALSE;