wbemprox: Add support for enumerating class properties.
[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             return TRUE;
170         }
171
172         case WM_COMMAND:
173
174             switch (LOWORD(wparam))
175             {
176                 case IDC_BUTTONDISABLE:
177                     FIXME("Disable selected joystick from being enumerated\n");
178                     break;
179
180                 case IDC_BUTTONENABLE:
181                     FIXME("Re-Enable selected joystick\n");
182                     break;
183             }
184
185             return TRUE;
186
187         case WM_NOTIFY:
188             return TRUE;
189
190         default:
191             break;
192     }
193     return FALSE;
194 }
195
196 /*********************************************************************
197  * Joystick testing functions
198  *
199  */
200 static void dump_joy_state(DIJOYSTATE* st, int num_buttons)
201 {
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]);
207
208     TRACE("Buttons ");
209     for(i=0; i < num_buttons; i++)
210         TRACE("  %c",st->rgbButtons[i] ? 'x' : 'o');
211     TRACE("\n");
212 }
213
214 static void poll_input(const struct Joystick *joy, DIJOYSTATE *state)
215 {
216     HRESULT  hr;
217
218     hr = IDirectInputDevice8_Poll(joy->device);
219
220     /* If it failed, try to acquire the joystick */
221     if (FAILED(hr))
222     {
223         hr = IDirectInputDevice8_Acquire(joy->device);
224         while (hr == DIERR_INPUTLOST) hr = IDirectInputDevice8_Acquire(joy->device);
225     }
226
227     if (hr == DIERR_OTHERAPPHASPRIO) return;
228
229     IDirectInputDevice8_GetDeviceState(joy->device, sizeof(DIJOYSTATE), state);
230 }
231
232 static DWORD WINAPI input_thread(void *param)
233 {
234     int axes_pos[TEST_MAX_AXES][2];
235     DIJOYSTATE state;
236     struct JoystickData *data = param;
237
238     /* Setup POV as clock positions
239      *         0
240      *   31500    4500
241      * 27000  -1    9000
242      *   22500   13500
243      *       18000
244      */
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} };
250
251     ZeroMemory(&state, sizeof(state));
252
253     while (!data->stop)
254     {
255         int i;
256         poll_input(&data->joysticks[data->chosen_joystick], &state);
257
258         dump_joy_state(&state, data->joysticks[data->chosen_joystick].num_buttons);
259
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);
264
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;
272
273         /* Set pov values */
274         for (i = 0; i < sizeof(pov_val)/sizeof(pov_val[0]); i++)
275         {
276             if (state.rgdwPOV[0] == pov_val[i])
277             {
278                 axes_pos[3][0] = pov_pos[i][0];
279                 axes_pos[3][1] = pov_pos[i][1];
280             }
281         }
282
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);
288
289         Sleep(TEST_POLL_TIME);
290
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);
294     }
295
296     return 0;
297 }
298
299 static void test_handle_joychange(HWND hwnd, struct JoystickData *data)
300 {
301     int i;
302
303     if (data->num_joysticks == 0) return;
304
305     data->chosen_joystick = SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_GETCURSEL, 0, 0);
306
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);
310 }
311
312 /*********************************************************************
313  * button_number_to_wchar [internal]
314  *  Transforms an integer in the interval [0,99] into a 2 character WCHAR string
315  */
316 static void button_number_to_wchar(int n, WCHAR str[3])
317 {
318     str[1] = n % 10 + '0';
319     n /= 10;
320     str[0] = n % 10 + '0';
321     str[2] = '\0';
322 }
323
324 static void draw_joystick_buttons(HWND hwnd, struct JoystickData* data)
325 {
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'};
331
332     for (i = 0; i < TEST_MAX_BUTTONS; i++)
333     {
334         if ((i % TEST_BUTTON_COL_MAX) == 0 && i != 0)
335         {
336             row += 1;
337             col = 0;
338         }
339
340         button_number_to_wchar(i + 1, button_label);
341
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);
346
347         col += 1;
348     }
349 }
350
351 static void draw_joystick_axes(HWND hwnd, struct JoystickData* data)
352 {
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 };
360
361     for (i = 0; i < TEST_MAX_AXES; i++)
362     {
363         /* Set axis box name */
364         SetWindowTextW(GetDlgItem(hwnd, axes_idc[i]), axes_names[i]);
365
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);
370     }
371 }
372
373 /*********************************************************************
374  * test_dlgproc [internal]
375  *
376  */
377 static INT_PTR CALLBACK test_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
378 {
379     static HANDLE thread;
380     static struct JoystickData *data;
381     TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
382
383     switch (msg)
384     {
385         case WM_INITDIALOG:
386         {
387             int i;
388
389             data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
390
391             /* Add enumerated joysticks to the combobox */
392             for (i = 0; i < data->num_joysticks; i++)
393             {
394                 struct Joystick *joy = &data->joysticks[i];
395                 SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
396             }
397
398             draw_joystick_buttons(hwnd, data);
399             draw_joystick_axes(hwnd, data);
400
401             return TRUE;
402         }
403
404         case WM_COMMAND:
405             switch(wparam)
406             {
407                 case MAKEWPARAM(IDC_TESTSELECTCOMBO, CBN_SELCHANGE):
408                     test_handle_joychange(hwnd, data);
409                 break;
410             }
411             return TRUE;
412
413         case WM_NOTIFY:
414             switch(((LPNMHDR)lparam)->code)
415             {
416                 case PSN_SETACTIVE:
417                 {
418                     DWORD tid;
419
420                     /* Initialize input thread */
421                     if (data->num_joysticks > 0)
422                     {
423                         data->stop = FALSE;
424
425                         /* Set the first joystick as default */
426                         SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_SETCURSEL, 0, 0);
427                         test_handle_joychange(hwnd, data);
428
429                         thread = CreateThread(NULL, 0, input_thread, (void*) data, 0, &tid);
430                     }
431                 }
432                 break;
433
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;
441             }
442             return TRUE;
443     }
444     return FALSE;
445 }
446
447 /*********************************************************************
448  * Joystick force feedback testing functions
449  *
450  */
451 static void draw_ff_axis(HWND hwnd, struct JoystickData *data)
452 {
453     HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
454     static WCHAR button_class[] = {'B','u','t','t','o','n','\0'};
455
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);
461 }
462
463 static void initialize_effects_list(HWND hwnd, struct Joystick* joy)
464 {
465     int i;
466
467     SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_RESETCONTENT, 0, 0);
468
469     for (i=0; i < joy->num_effects; i++)
470     {
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);
474     }
475 }
476
477 static void ff_handle_joychange(HWND hwnd, struct JoystickData *data)
478 {
479     int sel;
480
481     if (data->num_ff == 0) return;
482
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]);
486 }
487
488 static void ff_handle_effectchange(HWND hwnd, struct Joystick *joy)
489 {
490     int sel = SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_GETCURSEL, 0, 0);
491
492     if (sel < 0) return;
493
494     joy->chosen_effect = sel;
495 }
496
497 static DWORD WINAPI ff_input_thread(void *param)
498 {
499     struct JoystickData *data = param;
500     DIJOYSTATE state;
501
502     ZeroMemory(&state, sizeof(state));
503
504     while (!data->stop)
505     {
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;
511
512         /* Skip this if we have no effects */
513         if (joy->num_effects == 0 || chosen_effect < 0) continue;
514
515         poll_input(joy, &state);
516
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;
521
522         SetWindowPos(data->ff_axis, 0, FF_AXIS_X + state.lX, FF_AXIS_Y + state.lY,
523                      0, 0, SWP_NOZORDER | SWP_NOSIZE);
524
525         for (i=0; i < joy->num_buttons; i++)
526             if (state.rgbButtons[i])
527             {
528                 IDirectInputEffect_SetParameters(joy->effects[chosen_effect].effect, dieffect, flags);
529                 IDirectInputEffect_Start(joy->effects[chosen_effect].effect, 1, 0);
530                 break;
531             }
532
533         Sleep(TEST_POLL_TIME);
534     }
535
536     return 0;
537 }
538
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.
544  */
545 static BOOL CALLBACK ff_effects_callback(const DIEFFECTINFOW *pdei, void *pvRef)
546 {
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;
552
553     if (joystick->effects == NULL)
554     {
555         joystick->num_effects += 1;
556         return DIENUM_CONTINUE;
557     }
558
559     hr = IDirectInputDevice8_Acquire(joystick->device);
560
561     if (FAILED(hr)) return DIENUM_CONTINUE;
562
563     ZeroMemory(&dieffect, sizeof(dieffect));
564
565     dieffect.dwSize = sizeof(dieffect);
566     dieffect.dwFlags = DIEFF_CARTESIAN;
567     dieffect.dwDuration = FF_PLAY_TIME;
568
569     dieffect.cAxes = 2;
570     dieffect.rgdwAxes = axes;
571     dieffect.rglDirection = direction;
572
573     if (IsEqualGUID(&pdei->guid, &GUID_RampForce))
574     {
575         DIRAMPFORCE rforce;
576
577         rforce.lStart = 0;
578         rforce.lEnd = DI_FFNOMINALMAX;
579
580         dieffect.cbTypeSpecificParams = sizeof(rforce);
581         dieffect.lpvTypeSpecificParams = &rforce;
582         dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
583     }
584     else if (IsEqualGUID(&pdei->guid, &GUID_ConstantForce))
585     {
586         DICONSTANTFORCE cforce;
587
588         cforce.lMagnitude = DI_FFNOMINALMAX;
589
590         dieffect.cbTypeSpecificParams = sizeof(cforce);
591         dieffect.lpvTypeSpecificParams = &cforce;
592         dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
593     }
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))
599     {
600         DIPERIODIC pforce;
601
602         pforce.dwMagnitude = DI_FFNOMINALMAX;
603         pforce.lOffset = 0;
604         pforce.dwPhase = 0;
605         pforce.dwPeriod = FF_PERIOD_TIME;
606
607         dieffect.cbTypeSpecificParams = sizeof(pforce);
608         dieffect.lpvTypeSpecificParams = &pforce;
609         dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS;
610     }
611
612     hr = IDirectInputDevice2_CreateEffect(
613         joystick->device, &pdei->guid, &dieffect, &joystick->effects[joystick->cur_effect].effect, NULL);
614
615     joystick->effects[joystick->cur_effect].params = dieffect;
616     joystick->effects[joystick->cur_effect].info = *pdei;
617     joystick->cur_effect += 1;
618
619     return DIENUM_CONTINUE;
620 }
621
622 /*********************************************************************
623  * ff_dlgproc [internal]
624  *
625  */
626 static INT_PTR CALLBACK ff_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
627 {
628     static HANDLE thread;
629     static struct JoystickData *data;
630     TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
631
632     switch (msg)
633     {
634         case WM_INITDIALOG:
635         {
636             int i, cur = 0;
637
638             data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
639
640             /* Add joysticks with FF support to the combobox and get the effects */
641             for (i = 0; i < data->num_joysticks; i++)
642             {
643                 struct Joystick *joy = &data->joysticks[i];
644
645                 if (joy->forcefeedback)
646                 {
647                     SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
648                     SendDlgItemMessageW(hwnd, IDC_FFSELECTCOMBO, CB_SETITEMDATA, cur, i);
649
650                     cur++;
651
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);
657
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;
662
663                 }
664             }
665
666             draw_ff_axis(hwnd, data);
667
668             return TRUE;
669         }
670
671         case WM_COMMAND:
672             switch(wparam)
673             {
674                 case MAKEWPARAM(IDC_FFSELECTCOMBO, CBN_SELCHANGE):
675                     ff_handle_joychange(hwnd, data);
676
677                     SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_SETCURSEL, 0, 0);
678                     ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
679                 break;
680
681                 case MAKEWPARAM(IDC_FFEFFECTLIST, LBN_SELCHANGE):
682                     ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
683                 break;
684             }
685             return TRUE;
686
687         case WM_NOTIFY:
688             switch(((LPNMHDR)lparam)->code)
689             {
690                 case PSN_SETACTIVE:
691                     if (data->num_ff > 0)
692                     {
693                         DWORD tid;
694
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);
699
700                         SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_SETCURSEL, 0, 0);
701                         ff_handle_effectchange(hwnd, &data->joysticks[data->chosen_joystick]);
702
703                         thread = CreateThread(NULL, 0, ff_input_thread, (void*) data, 0, &tid);
704                     }
705                 break;
706
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;
714             }
715             return TRUE;
716     }
717     return FALSE;
718 }
719
720 /******************************************************************************
721  * propsheet_callback [internal]
722  */
723 static int CALLBACK propsheet_callback(HWND hwnd, UINT msg, LPARAM lparam)
724 {
725     TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
726     switch (msg)
727     {
728         case PSCB_INITIALIZED:
729             break;
730     }
731     return 0;
732 }
733
734 /******************************************************************************
735  * display_cpl_sheets [internal]
736  *
737  * Build and display the dialog with all control panel propertysheets
738  *
739  */
740 static void display_cpl_sheets(HWND parent, struct JoystickData *data)
741 {
742     INITCOMMONCONTROLSEX icex;
743     PROPSHEETPAGEW psp[NUM_PROPERTY_PAGES];
744     PROPSHEETHEADERW psh;
745     DWORD id = 0;
746
747     OleInitialize(NULL);
748     /* Initialize common controls */
749     icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
750     icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES;
751     InitCommonControlsEx(&icex);
752
753     ZeroMemory(&psh, sizeof(psh));
754     ZeroMemory(psp, sizeof(psp));
755
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++;
763
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++;
770
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++;
777
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;
787
788     /* display the dialog */
789     PropertySheetW(&psh);
790
791     OleUninitialize();
792 }
793
794 /*********************************************************************
795  * CPlApplet (joy.cpl.@)
796  *
797  * Control Panel entry point
798  *
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
804  *
805  * RETURNS
806  *  Depends on the command
807  *
808  */
809 LONG CALLBACK CPlApplet(HWND hwnd, UINT command, LPARAM lParam1, LPARAM lParam2)
810 {
811     static struct JoystickData data;
812     TRACE("(%p, %u, 0x%lx, 0x%lx)\n", hwnd, command, lParam1, lParam2);
813
814     switch (command)
815     {
816         case CPL_INIT:
817         {
818             HRESULT hr;
819
820             /* Initialize dinput */
821             hr = DirectInput8Create(GetModuleHandleW(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void**)&data.di, NULL);
822
823             if (FAILED(hr))
824             {
825                 ERR("Failed to initialize DirectInput: 0x%08x\n", hr);
826                 return FALSE;
827             }
828
829             /* Then get all the connected joysticks */
830             initialize_joysticks(&data);
831
832             return TRUE;
833         }
834         case CPL_GETCOUNT:
835             return 1;
836
837         case CPL_INQUIRE:
838         {
839             CPLINFO *appletInfo = (CPLINFO *) lParam2;
840
841             appletInfo->idName = IDS_CPL_NAME;
842             appletInfo->idInfo = IDS_CPL_INFO;
843             appletInfo->lData = 0;
844             return TRUE;
845         }
846
847         case CPL_DBLCLK:
848             display_cpl_sheets(hwnd, &data);
849             break;
850
851         case CPL_STOP:
852             destroy_joysticks(&data);
853
854             /* And destroy dinput too */
855             IDirectInput8_Release(data.di);
856             break;
857     }
858
859     return FALSE;
860 }