mshtml: Added IHTMLWindow6::get_sessionStorage implementation.
[wine] / dlls / joy.cpl / main.c
1 /*
2  * Joystick testing control panel applet
3  *
4  * Copyright 2012 Lucas Fialho Zawacki
5  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #define NONAMELESSUNION
23 #define COBJMACROS
24 #define CONST_VTABLE
25
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"
33
34 #include "wine/debug.h"
35 #include "joy.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(joycpl);
38
39 DECLSPEC_HIDDEN HMODULE hcpl;
40
41 /*********************************************************************
42  *  DllMain
43  */
44 BOOL WINAPI DllMain(HINSTANCE hdll, DWORD reason, LPVOID reserved)
45 {
46     TRACE("(%p, %d, %p)\n", hdll, reason, reserved);
47
48     switch (reason)
49     {
50         case DLL_WINE_PREATTACH:
51             return FALSE;  /* prefer native version */
52
53         case DLL_PROCESS_ATTACH:
54             DisableThreadLibraryCalls(hdll);
55             hcpl = hdll;
56     }
57     return TRUE;
58 }
59
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.
65  */
66 static BOOL CALLBACK enum_callback(const DIDEVICEINSTANCEW *instance, void *context)
67 {
68     struct JoystickData *data = context;
69     struct Joystick *joystick;
70     DIPROPRANGE proprange;
71     DIDEVCAPS caps;
72
73     if (data->joysticks == NULL)
74     {
75         data->num_joysticks += 1;
76         return DIENUM_CONTINUE;
77     }
78
79     joystick = &data->joysticks[data->cur_joystick];
80     data->cur_joystick += 1;
81
82     IDirectInput8_CreateDevice(data->di, &instance->guidInstance, &joystick->device, NULL);
83     IDirectInputDevice8_SetDataFormat(joystick->device, &c_dfDIJoystick);
84
85     joystick->instance = *instance;
86
87     caps.dwSize = sizeof(caps);
88     IDirectInputDevice8_GetCapabilities(joystick->device, &caps);
89
90     joystick->num_buttons = caps.dwButtons;
91     joystick->num_axes = caps.dwAxes;
92     joystick->forcefeedback = caps.dwFlags & DIDC_FORCEFEEDBACK;
93
94     if (joystick->forcefeedback) data->num_ff++;
95
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;
103
104     IDirectInputDevice_SetProperty(joystick->device, DIPROP_RANGE, &proprange.diph);
105
106     return DIENUM_CONTINUE;
107 }
108
109 /***********************************************************************
110  *  initialize_joysticks [internal]
111  */
112 static void initialize_joysticks(struct JoystickData *data)
113 {
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);
118
119     /* Get all the joysticks */
120     IDirectInput8_EnumDevices(data->di, DI8DEVCLASS_GAMECTRL, enum_callback, data, DIEDFL_ATTACHEDONLY);
121 }
122
123 /***********************************************************************
124  *  destroy_joysticks [internal]
125  */
126 static void destroy_joysticks(struct JoystickData *data)
127 {
128     int i, j;
129
130     for (i = 0; i < data->num_joysticks; i++)
131     {
132
133         if (data->joysticks[i].forcefeedback)
134         {
135             for (j = 0; j < data->joysticks[i].num_effects; j++)
136             IDirectInputEffect_Release(data->joysticks[i].effects[j].effect);
137
138             HeapFree(GetProcessHeap(), 0, data->joysticks[i].effects);
139         }
140
141         IDirectInputDevice8_Unacquire(data->joysticks[i].device);
142         IDirectInputDevice8_Release(data->joysticks[i].device);
143     }
144
145     HeapFree(GetProcessHeap(), 0, data->joysticks);
146 }
147
148 /*********************************************************************
149  * list_dlgproc [internal]
150  *
151  */
152 static INT_PTR CALLBACK list_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
153 {
154     TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
155     switch (msg)
156     {
157         case WM_INITDIALOG:
158         {
159             int i;
160             struct JoystickData *data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
161
162             /* Set dialog information */
163             for (i = 0; i < data->num_joysticks; i++)
164             {
165                 struct Joystick *joy = &data->joysticks[i];
166                 SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
167             }
168
169             /* Store the hwnd to be used with MapDialogRect for unit conversions */
170             data->graphics.hwnd = hwnd;
171
172             return TRUE;
173         }
174
175         case WM_COMMAND:
176
177             switch (LOWORD(wparam))
178             {
179                 case IDC_BUTTONDISABLE:
180                     FIXME("Disable selected joystick from being enumerated\n");
181                     break;
182
183                 case IDC_BUTTONENABLE:
184                     FIXME("Re-Enable selected joystick\n");
185                     break;
186             }
187
188             return TRUE;
189
190         case WM_NOTIFY:
191             return TRUE;
192
193         default:
194             break;
195     }
196     return FALSE;
197 }
198
199 /*********************************************************************
200  * Joystick testing functions
201  *
202  */
203 static void dump_joy_state(DIJOYSTATE* st, int num_buttons)
204 {
205     int i;
206     TRACE("Ax (% 5d,% 5d,% 5d)\n", st->lX,st->lY, st->lZ);
207     TRACE("RAx (% 5d,% 5d,% 5d)\n", st->lRx, st->lRy, st->lRz);
208     TRACE("Slider (% 5d,% 5d)\n", st->rglSlider[0], st->rglSlider[1]);
209     TRACE("Pov (% 5d,% 5d,% 5d,% 5d)\n", st->rgdwPOV[0], st->rgdwPOV[1], st->rgdwPOV[2], st->rgdwPOV[3]);
210
211     TRACE("Buttons ");
212     for(i=0; i < num_buttons; i++)
213         TRACE("  %c",st->rgbButtons[i] ? 'x' : 'o');
214     TRACE("\n");
215 }
216
217 static void poll_input(const struct Joystick *joy, DIJOYSTATE *state)
218 {
219     HRESULT  hr;
220
221     hr = IDirectInputDevice8_Poll(joy->device);
222
223     /* If it failed, try to acquire the joystick */
224     if (FAILED(hr))
225     {
226         hr = IDirectInputDevice8_Acquire(joy->device);
227         while (hr == DIERR_INPUTLOST) hr = IDirectInputDevice8_Acquire(joy->device);
228     }
229
230     if (hr == DIERR_OTHERAPPHASPRIO) return;
231
232     IDirectInputDevice8_GetDeviceState(joy->device, sizeof(DIJOYSTATE), state);
233 }
234
235 static DWORD WINAPI input_thread(void *param)
236 {
237     int axes_pos[TEST_MAX_AXES][2];
238     DIJOYSTATE state;
239     struct JoystickData *data = param;
240
241     /* Setup POV as clock positions
242      *         0
243      *   31500    4500
244      * 27000  -1    9000
245      *   22500   13500
246      *       18000
247      */
248     int ma = TEST_AXIS_MAX;
249     int pov_val[9] = {0, 4500, 9000, 13500,
250                       18000, 22500, 27000, 31500, -1};
251     int pov_pos[9][2] = { {0, -ma}, {ma/2, -ma/2}, {ma, 0}, {ma/2, ma/2},
252                           {0, ma}, {-ma/2, ma/2}, {-ma, 0}, {-ma/2, -ma/2}, {0, 0} };
253
254     ZeroMemory(&state, sizeof(state));
255
256     while (!data->stop)
257     {
258         int i;
259         poll_input(&data->joysticks[data->chosen_joystick], &state);
260
261         dump_joy_state(&state, data->joysticks[data->chosen_joystick].num_buttons);
262
263         /* Indicate pressed buttons */
264         for (i = 0; i < data->joysticks[data->chosen_joystick].num_buttons; i++)
265             if (state.rgbButtons[i])
266                 SendMessageW(data->graphics.buttons[i], BM_SETSTATE, TRUE, 0);
267
268         /* Indicate axis positions, axes showing are hardcoded for now */
269         axes_pos[0][0] = state.lX;
270         axes_pos[0][1] = state.lY;
271         axes_pos[1][0] = state.lRx;
272         axes_pos[1][1] = state.lRy;
273         axes_pos[2][0] = state.lZ;
274         axes_pos[2][1] = state.lRz;
275
276         /* Set pov values */
277         for (i = 0; i < sizeof(pov_val)/sizeof(pov_val[0]); i++)
278         {
279             if (state.rgdwPOV[0] == pov_val[i])
280             {
281                 axes_pos[3][0] = pov_pos[i][0];
282                 axes_pos[3][1] = pov_pos[i][1];
283             }
284         }
285
286         for (i = 0; i < TEST_MAX_AXES; i++)
287         {
288             RECT r;
289
290             r.left = (TEST_AXIS_X + TEST_NEXT_AXIS_X*i + axes_pos[i][0]);
291             r.top = (TEST_AXIS_Y + axes_pos[i][1]);
292             r.bottom = r.right = 0; /* unused */
293             MapDialogRect(data->graphics.hwnd, &r);
294
295             SetWindowPos(data->graphics.axes[i], 0, r.left, r.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
296         }
297
298         Sleep(TEST_POLL_TIME);
299
300         /* Reset button state */
301         for (i = 0; i < data->joysticks[data->chosen_joystick].num_buttons; i++)
302             SendMessageW(data->graphics.buttons[i], BM_SETSTATE, FALSE, 0);
303     }
304
305     return 0;
306 }
307
308 static void test_handle_joychange(HWND hwnd, struct JoystickData *data)
309 {
310     int i;
311
312     if (data->num_joysticks == 0) return;
313
314     data->chosen_joystick = SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_GETCURSEL, 0, 0);
315
316     /* Enable only  buttons present in the device */
317     for (i = 0; i < TEST_MAX_BUTTONS; i++)
318         ShowWindow(data->graphics.buttons[i], i <= data->joysticks[data->chosen_joystick].num_buttons);
319 }
320
321 /*********************************************************************
322  * button_number_to_wchar [internal]
323  *  Transforms an integer in the interval [0,99] into a 2 character WCHAR string
324  */
325 static void button_number_to_wchar(int n, WCHAR str[3])
326 {
327     str[1] = n % 10 + '0';
328     n /= 10;
329     str[0] = n % 10 + '0';
330     str[2] = '\0';
331 }
332
333 static void draw_joystick_buttons(HWND hwnd, struct JoystickData* data)
334 {
335     int i;
336     int row = 0, col = 0;
337     WCHAR button_label[3];
338     HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
339     static WCHAR button_class[] = {'B','u','t','t','o','n','\0'};
340
341     for (i = 0; i < TEST_MAX_BUTTONS; i++)
342     {
343         RECT r;
344
345         if ((i % TEST_BUTTON_COL_MAX) == 0 && i != 0)
346         {
347             row += 1;
348             col = 0;
349         }
350
351         r.left = (TEST_BUTTON_X + TEST_NEXT_BUTTON_X*col);
352         r.top = (TEST_BUTTON_Y + TEST_NEXT_BUTTON_Y*row);
353         r.right = r.left + TEST_BUTTON_SIZE_X;
354         r.bottom = r.top + TEST_BUTTON_SIZE_Y;
355         MapDialogRect(hwnd, &r);
356
357         button_number_to_wchar(i + 1, button_label);
358
359         data->graphics.buttons[i] = CreateWindowW(button_class, button_label, WS_CHILD,
360             r.left, r.top, r.right - r.left, r.bottom - r.top,
361             hwnd, NULL, NULL, hinst);
362
363         col += 1;
364     }
365 }
366
367 static void draw_joystick_axes(HWND hwnd, struct JoystickData* data)
368 {
369     int i;
370     HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
371     static const WCHAR button_class[] = {'B','u','t','t','o','n','\0'};
372     static const WCHAR axes_names[TEST_MAX_AXES][7] = { {'X',',','Y','\0'}, {'R','x',',','R','y','\0'},
373                                                         {'Z',',','R','z','\0'}, {'P','O','V','\0'} };
374     static const DWORD axes_idc[TEST_MAX_AXES] = { IDC_TESTGROUPXY, IDC_TESTGROUPRXRY,
375                                                    IDC_TESTGROUPZRZ, IDC_TESTGROUPPOV };
376
377     for (i = 0; i < TEST_MAX_AXES; i++)
378     {
379         RECT r;
380         /* Set axis box name */
381         SetWindowTextW(GetDlgItem(hwnd, axes_idc[i]), axes_names[i]);
382
383         r.left = (TEST_AXIS_X + TEST_NEXT_AXIS_X*i);
384         r.top = TEST_AXIS_Y;
385         r.right = r.left + TEST_AXIS_SIZE_X;
386         r.bottom = r.top + TEST_AXIS_SIZE_Y;
387         MapDialogRect(hwnd, &r);
388
389         data->graphics.axes[i] = CreateWindowW( button_class, NULL, WS_CHILD | WS_VISIBLE,
390             r.left, r.top, r.right - r.left, r.bottom - r.top,
391             hwnd, NULL, NULL, hinst);
392     }
393 }
394
395 /*********************************************************************
396  * test_dlgproc [internal]
397  *
398  */
399 static INT_PTR CALLBACK test_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
400 {
401     static HANDLE thread;
402     static struct JoystickData *data;
403     TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
404
405     switch (msg)
406     {
407         case WM_INITDIALOG:
408         {
409             int i;
410
411             data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
412
413             /* Add enumerated joysticks to the combobox */
414             for (i = 0; i < data->num_joysticks; i++)
415             {
416                 struct Joystick *joy = &data->joysticks[i];
417                 SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
418             }
419
420             draw_joystick_buttons(hwnd, data);
421             draw_joystick_axes(hwnd, data);
422
423             return TRUE;
424         }
425
426         case WM_COMMAND:
427             switch(wparam)
428             {
429                 case MAKEWPARAM(IDC_TESTSELECTCOMBO, CBN_SELCHANGE):
430                     test_handle_joychange(hwnd, data);
431                 break;
432             }
433             return TRUE;
434
435         case WM_NOTIFY:
436             switch(((LPNMHDR)lparam)->code)
437             {
438                 case PSN_SETACTIVE:
439                 {
440                     DWORD tid;
441
442                     /* Initialize input thread */
443                     if (data->num_joysticks > 0)
444                     {
445                         data->stop = FALSE;
446
447                         /* Set the first joystick as default */
448                         SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_SETCURSEL, 0, 0);
449                         test_handle_joychange(hwnd, data);
450
451                         thread = CreateThread(NULL, 0, input_thread, (void*) data, 0, &tid);
452                     }
453                 }
454                 break;
455
456                 case PSN_RESET: /* intentional fall-through */
457                 case PSN_KILLACTIVE:
458                     /* Stop input thread */
459                     data->stop = TRUE;
460                     MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, 0);
461                     CloseHandle(thread);
462                 break;
463             }
464             return TRUE;
465     }
466     return FALSE;
467 }
468
469 /*********************************************************************
470  * Joystick force feedback testing functions
471  *
472  */
473 static void draw_ff_axis(HWND hwnd, struct JoystickData *data)
474 {
475     HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
476     static WCHAR button_class[] = {'B','u','t','t','o','n','\0'};
477     RECT r;
478
479     r.left = FF_AXIS_X;
480     r.top = FF_AXIS_Y;
481     r.right = r.left + FF_AXIS_SIZE_X;
482     r.bottom = r.top + FF_AXIS_SIZE_Y;
483     MapDialogRect(hwnd, &r);
484
485     /* Draw direction axis */
486     data->graphics.ff_axis = CreateWindowW( button_class, NULL, WS_CHILD | WS_VISIBLE,
487         r.left, r.top, r.right - r.left, r.bottom - r.top,
488         hwnd, NULL, NULL, hinst);
489 }
490
491 static void initialize_effects_list(HWND hwnd, struct Joystick* joy)
492 {
493     int i;
494
495     SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_RESETCONTENT, 0, 0);
496
497     for (i=0; i < joy->num_effects; i++)
498     {
499         /* Effect names start with GUID_, so we'll skip this part */
500         WCHAR *name = joy->effects[i].info.tszName + 5;
501         SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_ADDSTRING, 0, (LPARAM) name);
502     }
503 }
504
505 static void ff_handle_joychange(HWND hwnd, struct JoystickData *data)
506 {
507     int sel;
508
509     if (data->num_ff == 0) return;
510
511     sel = SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_GETCURSEL, 0, 0);
512     data->chosen_joystick = SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_GETITEMDATA, sel, 0);
513     initialize_effects_list(hwnd, &data->joysticks[data->chosen_joystick]);
514 }
515
516 static void ff_handle_effectchange(HWND hwnd, struct Joystick *joy)
517 {
518     int sel = SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_GETCURSEL, 0, 0);
519
520     if (sel < 0) return;
521
522     joy->chosen_effect = sel;
523 }
524
525 static DWORD WINAPI ff_input_thread(void *param)
526 {
527     struct JoystickData *data = param;
528     DIJOYSTATE state;
529
530     ZeroMemory(&state, sizeof(state));
531
532     while (!data->stop)
533     {
534         int i;
535         struct Joystick *joy = &data->joysticks[data->chosen_joystick];
536         int chosen_effect = joy->chosen_effect;
537         DIEFFECT *dieffect;
538         DWORD flags = DIEP_AXES | DIEP_DIRECTION | DIEP_NORESTART;
539         RECT r;
540
541         /* Skip this if we have no effects */
542         if (joy->num_effects == 0 || chosen_effect < 0) continue;
543
544         poll_input(joy, &state);
545
546         /* Set ff parameters and draw the axis */
547         dieffect = &joy->effects[chosen_effect].params;
548         dieffect->rgdwAxes[0] = state.lX;
549         dieffect->rgdwAxes[1] = state.lY;
550
551         r.left = FF_AXIS_X + state.lX;
552         r.top = FF_AXIS_Y + state.lY;
553         r.right = r.bottom = 0; /* unused */
554         MapDialogRect(data->graphics.hwnd, &r);
555
556         SetWindowPos(data->graphics.ff_axis, 0, r.left, r.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
557
558         for (i=0; i < joy->num_buttons; i++)
559             if (state.rgbButtons[i])
560             {
561                 IDirectInputEffect_SetParameters(joy->effects[chosen_effect].effect, dieffect, flags);
562                 IDirectInputEffect_Start(joy->effects[chosen_effect].effect, 1, 0);
563                 break;
564             }
565
566         Sleep(TEST_POLL_TIME);
567     }
568
569     return 0;
570 }
571
572 /***********************************************************************
573  *  ff_effects_callback [internal]
574  *   Enumerates, creates, sets the some parameters and stores all ff effects
575  *   supported by the joystick. Works like enum_callback, counting the effects
576  *   first and then storing them.
577  */
578 static BOOL CALLBACK ff_effects_callback(const DIEFFECTINFOW *pdei, void *pvRef)
579 {
580     HRESULT hr;
581     DIEFFECT dieffect;
582     DWORD axes[2] = {DIJOFS_X, DIJOFS_Y};
583     int direction[2] = {0, 0};
584     struct Joystick *joystick = pvRef;
585
586     if (joystick->effects == NULL)
587     {
588         joystick->num_effects += 1;
589         return DIENUM_CONTINUE;
590     }
591
592     hr = IDirectInputDevice8_Acquire(joystick->device);
593
594     if (FAILED(hr)) return DIENUM_CONTINUE;
595
596     ZeroMemory(&dieffect, sizeof(dieffect));
597
598     dieffect.dwSize = sizeof(dieffect);
599     dieffect.dwFlags = DIEFF_CARTESIAN;
600     dieffect.dwDuration = FF_PLAY_TIME;
601
602     dieffect.cAxes = 2;
603     dieffect.rgdwAxes = axes;
604     dieffect.rglDirection = direction;
605
606     if (IsEqualGUID(&pdei->guid, &GUID_RampForce))
607     {
608         DIRAMPFORCE rforce;
609
610         rforce.lStart = 0;
611         rforce.lEnd = DI_FFNOMINALMAX;
612
613         dieffect.cbTypeSpecificParams = sizeof(rforce);
614         dieffect.lpvTypeSpecificParams = &rforce;
615         dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
616     }
617     else if (IsEqualGUID(&pdei->guid, &GUID_ConstantForce))
618     {
619         DICONSTANTFORCE cforce;
620
621         cforce.lMagnitude = DI_FFNOMINALMAX;
622
623         dieffect.cbTypeSpecificParams = sizeof(cforce);
624         dieffect.lpvTypeSpecificParams = &cforce;
625         dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
626     }
627     else if (IsEqualGUID(&pdei->guid, &GUID_Sine) ||
628              IsEqualGUID(&pdei->guid, &GUID_Square) ||
629              IsEqualGUID(&pdei->guid, &GUID_Triangle) ||
630              IsEqualGUID(&pdei->guid, &GUID_SawtoothUp) ||
631              IsEqualGUID(&pdei->guid, &GUID_SawtoothDown))
632     {
633         DIPERIODIC pforce;
634
635         pforce.dwMagnitude = DI_FFNOMINALMAX;
636         pforce.lOffset = 0;
637         pforce.dwPhase = 0;
638         pforce.dwPeriod = FF_PERIOD_TIME;
639
640         dieffect.cbTypeSpecificParams = sizeof(pforce);
641         dieffect.lpvTypeSpecificParams = &pforce;
642         dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
643     }
644
645     hr = IDirectInputDevice2_CreateEffect(
646         joystick->device, &pdei->guid, &dieffect, &joystick->effects[joystick->cur_effect].effect, NULL);
647
648     joystick->effects[joystick->cur_effect].params = dieffect;
649     joystick->effects[joystick->cur_effect].info = *pdei;
650     joystick->cur_effect += 1;
651
652     return DIENUM_CONTINUE;
653 }
654
655 /*********************************************************************
656  * ff_dlgproc [internal]
657  *
658  */
659 static INT_PTR CALLBACK ff_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
660 {
661     static HANDLE thread;
662     static struct JoystickData *data;
663     TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
664
665     switch (msg)
666     {
667         case WM_INITDIALOG:
668         {
669             int i, cur = 0;
670
671             data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
672
673             /* Add joysticks with FF support to the combobox and get the effects */
674             for (i = 0; i < data->num_joysticks; i++)
675             {
676                 struct Joystick *joy = &data->joysticks[i];
677
678                 if (joy->forcefeedback)
679                 {
680                     SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
681                     SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_SETITEMDATA, cur, i);
682
683                     cur++;
684
685                     /* Count device effects and then store them */
686                     joy->num_effects = 0;
687                     joy->effects = NULL;
688                     IDirectInputDevice8_EnumEffects(joy->device, ff_effects_callback, (void *) joy, 0);
689                     joy->effects = HeapAlloc(GetProcessHeap(), 0, sizeof(struct Effect) * joy->num_effects);
690
691                     joy->cur_effect = 0;
692                     IDirectInputDevice8_EnumEffects(joy->device, ff_effects_callback, (void*) joy, 0);
693                     joy->num_effects = joy->cur_effect;
694                 }
695             }
696
697             draw_ff_axis(hwnd, data);
698
699             return TRUE;
700         }
701
702         case WM_COMMAND:
703             switch(wparam)
704             {
705                 case MAKEWPARAM(IDC_FFSELECTCOMBO, CBN_SELCHANGE):
706                     ff_handle_joychange(hwnd, data);
707
708                     SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_SETCURSEL, 0, 0);
709                     ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
710                 break;
711
712                 case MAKEWPARAM(IDC_FFEFFECTLIST, LBN_SELCHANGE):
713                     ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
714                 break;
715             }
716             return TRUE;
717
718         case WM_NOTIFY:
719             switch(((LPNMHDR)lparam)->code)
720             {
721                 case PSN_SETACTIVE:
722                     if (data->num_ff > 0)
723                     {
724                         DWORD tid;
725
726                         data->stop = FALSE;
727                         /* Set the first joystick as default */
728                         SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_SETCURSEL, 0, 0);
729                         ff_handle_joychange(hwnd, data);
730
731                         SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_SETCURSEL, 0, 0);
732                         ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
733
734                         thread = CreateThread(NULL, 0, ff_input_thread, (void*) data, 0, &tid);
735                     }
736                 break;
737
738                 case PSN_RESET: /* intentional fall-through */
739                 case PSN_KILLACTIVE:
740                     /* Stop ff thread */
741                     data->stop = TRUE;
742                     MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, 0);
743                     CloseHandle(thread);
744                 break;
745             }
746             return TRUE;
747     }
748     return FALSE;
749 }
750
751 /******************************************************************************
752  * propsheet_callback [internal]
753  */
754 static int CALLBACK propsheet_callback(HWND hwnd, UINT msg, LPARAM lparam)
755 {
756     TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
757     switch (msg)
758     {
759         case PSCB_INITIALIZED:
760             break;
761     }
762     return 0;
763 }
764
765 /******************************************************************************
766  * display_cpl_sheets [internal]
767  *
768  * Build and display the dialog with all control panel propertysheets
769  *
770  */
771 static void display_cpl_sheets(HWND parent, struct JoystickData *data)
772 {
773     INITCOMMONCONTROLSEX icex;
774     PROPSHEETPAGEW psp[NUM_PROPERTY_PAGES];
775     PROPSHEETHEADERW psh;
776     DWORD id = 0;
777
778     OleInitialize(NULL);
779     /* Initialize common controls */
780     icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
781     icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES;
782     InitCommonControlsEx(&icex);
783
784     ZeroMemory(&psh, sizeof(psh));
785     ZeroMemory(psp, sizeof(psp));
786
787     /* Fill out all PROPSHEETPAGE */
788     psp[id].dwSize = sizeof (PROPSHEETPAGEW);
789     psp[id].hInstance = hcpl;
790     psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_LIST);
791     psp[id].pfnDlgProc = list_dlgproc;
792     psp[id].lParam = (INT_PTR) data;
793     id++;
794
795     psp[id].dwSize = sizeof (PROPSHEETPAGEW);
796     psp[id].hInstance = hcpl;
797     psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_TEST);
798     psp[id].pfnDlgProc = test_dlgproc;
799     psp[id].lParam = (INT_PTR) data;
800     id++;
801
802     psp[id].dwSize = sizeof (PROPSHEETPAGEW);
803     psp[id].hInstance = hcpl;
804     psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_FORCEFEEDBACK);
805     psp[id].pfnDlgProc = ff_dlgproc;
806     psp[id].lParam = (INT_PTR) data;
807     id++;
808
809     /* Fill out the PROPSHEETHEADER */
810     psh.dwSize = sizeof (PROPSHEETHEADERW);
811     psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID | PSH_USECALLBACK;
812     psh.hwndParent = parent;
813     psh.hInstance = hcpl;
814     psh.pszCaption = MAKEINTRESOURCEW(IDS_CPL_NAME);
815     psh.nPages = id;
816     psh.u3.ppsp = psp;
817     psh.pfnCallback = propsheet_callback;
818
819     /* display the dialog */
820     PropertySheetW(&psh);
821
822     OleUninitialize();
823 }
824
825 /*********************************************************************
826  * CPlApplet (joy.cpl.@)
827  *
828  * Control Panel entry point
829  *
830  * PARAMS
831  *  hWnd    [I] Handle for the Control Panel Window
832  *  command [I] CPL_* Command
833  *  lParam1 [I] first extra Parameter
834  *  lParam2 [I] second extra Parameter
835  *
836  * RETURNS
837  *  Depends on the command
838  *
839  */
840 LONG CALLBACK CPlApplet(HWND hwnd, UINT command, LPARAM lParam1, LPARAM lParam2)
841 {
842     static struct JoystickData data;
843     TRACE("(%p, %u, 0x%lx, 0x%lx)\n", hwnd, command, lParam1, lParam2);
844
845     switch (command)
846     {
847         case CPL_INIT:
848         {
849             HRESULT hr;
850
851             /* Initialize dinput */
852             hr = DirectInput8Create(GetModuleHandleW(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void**)&data.di, NULL);
853
854             if (FAILED(hr))
855             {
856                 ERR("Failed to initialize DirectInput: 0x%08x\n", hr);
857                 return FALSE;
858             }
859
860             /* Then get all the connected joysticks */
861             initialize_joysticks(&data);
862
863             return TRUE;
864         }
865         case CPL_GETCOUNT:
866             return 1;
867
868         case CPL_INQUIRE:
869         {
870             CPLINFO *appletInfo = (CPLINFO *) lParam2;
871
872             appletInfo->idName = IDS_CPL_NAME;
873             appletInfo->idInfo = IDS_CPL_INFO;
874             appletInfo->lData = 0;
875             return TRUE;
876         }
877
878         case CPL_DBLCLK:
879             display_cpl_sheets(hwnd, &data);
880             break;
881
882         case CPL_STOP:
883             destroy_joysticks(&data);
884
885             /* And destroy dinput too */
886             IDirectInput8_Release(data.di);
887             break;
888     }
889
890     return FALSE;
891 }