wbemprox: Implement IEnumWbemClassObject::Clone.
[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     DIDEVCAPS caps;
71
72     if (data->joysticks == NULL)
73     {
74         data->num_joysticks += 1;
75         return DIENUM_CONTINUE;
76     }
77
78     joystick = &data->joysticks[data->cur_joystick];
79     data->cur_joystick += 1;
80
81     IDirectInput8_CreateDevice(data->di, &instance->guidInstance, &joystick->device, NULL);
82     IDirectInputDevice8_SetDataFormat(joystick->device, &c_dfDIJoystick);
83
84     joystick->instance = *instance;
85
86     caps.dwSize = sizeof(caps);
87     IDirectInputDevice8_GetCapabilities(joystick->device, &caps);
88
89     joystick->num_buttons = caps.dwButtons;
90     joystick->num_axes = caps.dwAxes;
91
92     return DIENUM_CONTINUE;
93 }
94
95 /***********************************************************************
96  *  initialize_joysticks [internal]
97  */
98 static void initialize_joysticks(struct JoystickData *data)
99 {
100     data->num_joysticks = 0;
101     data->cur_joystick = 0;
102     IDirectInput8_EnumDevices(data->di, DI8DEVCLASS_GAMECTRL, enum_callback, data, DIEDFL_ATTACHEDONLY);
103     data->joysticks = HeapAlloc(GetProcessHeap(), 0, sizeof(struct Joystick) * data->num_joysticks);
104
105     /* Get all the joysticks */
106     IDirectInput8_EnumDevices(data->di, DI8DEVCLASS_GAMECTRL, enum_callback, data, DIEDFL_ATTACHEDONLY);
107 }
108
109 /***********************************************************************
110  *  destroy_joysticks [internal]
111  */
112 static void destroy_joysticks(struct JoystickData *data)
113 {
114     int i;
115
116     for (i = 0; i < data->num_joysticks; i++)
117     {
118         IDirectInputDevice8_Unacquire(data->joysticks[i].device);
119         IDirectInputDevice8_Release(data->joysticks[i].device);
120     }
121
122     HeapFree(GetProcessHeap(), 0, data->joysticks);
123 }
124
125 /*********************************************************************
126  * list_dlgproc [internal]
127  *
128  */
129 static INT_PTR CALLBACK list_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
130 {
131     TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
132     switch (msg)
133     {
134         case WM_INITDIALOG:
135         {
136             int i;
137             struct JoystickData *data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
138
139             /* Set dialog information */
140             for (i = 0; i < data->num_joysticks; i++)
141             {
142                 struct Joystick *joy = &data->joysticks[i];
143                 SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
144             }
145
146             return TRUE;
147         }
148
149         case WM_COMMAND:
150
151             switch (LOWORD(wparam))
152             {
153                 case IDC_BUTTONDISABLE:
154                     FIXME("Disable selected joystick from being enumerated\n");
155                     break;
156
157                 case IDC_BUTTONENABLE:
158                     FIXME("Re-Enable selected joystick\n");
159                     break;
160             }
161
162             return TRUE;
163
164         case WM_NOTIFY:
165             return TRUE;
166
167         default:
168             break;
169     }
170     return FALSE;
171 }
172
173 /*********************************************************************
174  * Joystick testing functions
175  *
176  */
177 static void dump_joy_state(DIJOYSTATE* st, int num_buttons)
178 {
179     int i;
180     TRACE("Ax (% 5d,% 5d,% 5d)\n", st->lX,st->lY, st->lZ);
181     TRACE("RAx (% 5d,% 5d,% 5d)\n", st->lRx, st->lRy, st->lRz);
182     TRACE("Slider (% 5d,% 5d)\n", st->rglSlider[0], st->rglSlider[1]);
183     TRACE("Pov (% 5d,% 5d,% 5d,% 5d)\n", st->rgdwPOV[0], st->rgdwPOV[1], st->rgdwPOV[2], st->rgdwPOV[3]);
184
185     TRACE("Buttons ");
186     for(i=0; i < num_buttons; i++)
187         TRACE("  %c",st->rgbButtons[i] ? 'x' : 'o');
188     TRACE("\n");
189 }
190
191 static void poll_input(const struct Joystick *joy, DIJOYSTATE *state)
192 {
193     HRESULT  hr;
194
195     hr = IDirectInputDevice8_Poll(joy->device);
196
197     /* If it failed, try to acquire the joystick */
198     if (FAILED(hr))
199     {
200         hr = IDirectInputDevice8_Acquire(joy->device);
201         while (hr == DIERR_INPUTLOST) hr = IDirectInputDevice8_Acquire(joy->device);
202     }
203
204     if (hr == DIERR_OTHERAPPHASPRIO) return;
205
206     IDirectInputDevice8_GetDeviceState(joy->device, sizeof(DIJOYSTATE), state);
207 }
208
209 static DWORD WINAPI input_thread(void *param)
210 {
211     int axes_pos[TEST_MAX_AXES][2];
212     DIJOYSTATE state;
213     struct JoystickData *data = param;
214
215     /* Setup POV as clock positions
216      *         0
217      *   31500    4500
218      * 27000  -1    9000
219      *   22500   13500
220      *       18000
221      */
222     int ma = TEST_AXIS_MAX;
223     int pov_val[9] = {0, 4500, 9000, 13500,
224                       18000, 22500, 27000, 31500, -1};
225     int pov_pos[9][2] = { {0, -ma}, {ma/2, -ma/2}, {ma, 0}, {ma/2, ma/2},
226                           {0, ma}, {-ma/2, ma/2}, {-ma, 0}, {-ma/2, -ma/2}, {0, 0} };
227
228     ZeroMemory(&state, sizeof(state));
229
230     while (!data->stop)
231     {
232         int i;
233         poll_input(&data->joysticks[data->chosen_joystick], &state);
234
235         dump_joy_state(&state, data->joysticks[data->chosen_joystick].num_buttons);
236
237         /* Indicate pressed buttons */
238         for (i = 0; i < data->joysticks[data->chosen_joystick].num_buttons; i++)
239             if (state.rgbButtons[i])
240                 SendMessageW(data->buttons[i], BM_SETSTATE, TRUE, 0);
241
242         /* Indicate axis positions, axes showing are hardcoded for now */
243         axes_pos[0][0] = state.lX;
244         axes_pos[0][1] = state.lY;
245         axes_pos[1][0] = state.lRx;
246         axes_pos[1][1] = state.lRy;
247         axes_pos[2][0] = state.lZ;
248         axes_pos[2][1] = state.lRz;
249
250         /* Set pov values */
251         for (i = 0; i < sizeof(pov_val)/sizeof(pov_val[0]); i++)
252         {
253             if (state.rgdwPOV[0] == pov_val[i])
254             {
255                 axes_pos[3][0] = pov_pos[i][0];
256                 axes_pos[3][1] = pov_pos[i][1];
257             }
258         }
259
260         for (i = 0; i < TEST_MAX_AXES; i++)
261             SetWindowPos(data->axes[i], 0,
262                         TEST_AXIS_X + TEST_NEXT_AXIS_X*i + axes_pos[i][0],
263                         TEST_AXIS_Y + axes_pos[i][1],
264                         0, 0, SWP_NOZORDER | SWP_NOSIZE);
265
266         Sleep(TEST_POLL_TIME);
267
268         /* Reset button state */
269         for (i = 0; i < data->joysticks[data->chosen_joystick].num_buttons; i++)
270             SendMessageW(data->buttons[i], BM_SETSTATE, FALSE, 0);
271     }
272
273     return 0;
274 }
275
276 static void test_handle_joychange(HWND hwnd, struct JoystickData *data)
277 {
278     int i;
279
280     if (data->num_joysticks == 0) return;
281
282     data->chosen_joystick = SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_GETCURSEL, 0, 0);
283
284     /* Enable only  buttons present in the device */
285     for (i = 0; i < TEST_MAX_BUTTONS; i++)
286         ShowWindow(data->buttons[i], i <= data->joysticks[data->chosen_joystick].num_buttons);
287 }
288
289 /*********************************************************************
290  * button_number_to_wchar [internal]
291  *  Transforms an integer in the interval [0,99] into a 2 character WCHAR string
292  */
293 static void button_number_to_wchar(int n, WCHAR str[3])
294 {
295     str[1] = n % 10 + '0';
296     n /= 10;
297     str[0] = n % 10 + '0';
298     str[2] = '\0';
299 }
300
301 static void draw_joystick_buttons(HWND hwnd, struct JoystickData* data)
302 {
303     int i;
304     int row = 0, col = 0;
305     WCHAR button_label[3];
306     HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
307     static WCHAR button_class[] = {'B','u','t','t','o','n','\0'};
308
309     for (i = 0; i < TEST_MAX_BUTTONS; i++)
310     {
311         if ((i % TEST_BUTTON_COL_MAX) == 0 && i != 0)
312         {
313             row += 1;
314             col = 0;
315         }
316
317         button_number_to_wchar(i + 1, button_label);
318
319         data->buttons[i] = CreateWindowW(button_class, button_label, WS_CHILD,
320             TEST_BUTTON_X + TEST_NEXT_BUTTON_X*col, TEST_BUTTON_Y + TEST_NEXT_BUTTON_Y*row,
321             TEST_BUTTON_SIZE_X, TEST_BUTTON_SIZE_Y,
322             hwnd, NULL, NULL, hinst);
323
324         col += 1;
325     }
326 }
327
328 static void draw_joystick_axes(HWND hwnd, struct JoystickData* data)
329 {
330     int i;
331     struct Joystick *joy;
332     DIPROPRANGE propRange;
333     HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
334     static const WCHAR button_class[] = {'B','u','t','t','o','n','\0'};
335     static const WCHAR axes_names[TEST_MAX_AXES][7] = { {'X',',','Y','\0'}, {'R','x',',','R','y','\0'},
336                                                         {'Z',',','R','z','\0'}, {'P','O','V','\0'} };
337     static const DWORD axes_idc[TEST_MAX_AXES] = { IDC_TESTGROUPXY, IDC_TESTGROUPRXRY,
338                                                    IDC_TESTGROUPZRZ, IDC_TESTGROUPPOV };
339
340     /* Set axis range to ease the GUI visualization */
341     for (i = 0; i < data->num_joysticks; i++)
342     {
343         joy = &data->joysticks[i];
344         propRange.diph.dwSize = sizeof(DIPROPRANGE);
345         propRange.diph.dwHeaderSize = sizeof(DIPROPHEADER);
346         propRange.diph.dwHow = DIPH_DEVICE;
347         propRange.diph.dwObj = 0;
348         propRange.lMin = TEST_AXIS_MIN;
349         propRange.lMax = TEST_AXIS_MAX;
350
351         IDirectInputDevice_SetProperty(joy->device, DIPROP_RANGE, &propRange.diph);
352     }
353
354     for (i = 0; i < TEST_MAX_AXES; i++)
355     {
356         /* Set axis box name */
357         SetWindowTextW(GetDlgItem(hwnd, axes_idc[i]), axes_names[i]);
358
359         data->axes[i] = CreateWindowW( button_class, NULL, WS_CHILD | WS_VISIBLE,
360             TEST_AXIS_X + TEST_NEXT_AXIS_X*i, TEST_AXIS_Y,
361             TEST_AXIS_SIZE_X, TEST_AXIS_SIZE_Y,
362             hwnd, (HMENU) IDC_JOYSTICKAXES + i, NULL, hinst);
363     }
364 }
365
366 /*********************************************************************
367  * test_dlgproc [internal]
368  *
369  */
370 static INT_PTR CALLBACK test_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
371 {
372     static HANDLE thread;
373     static struct JoystickData *data;
374     TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
375
376     switch (msg)
377     {
378         case WM_INITDIALOG:
379         {
380             int i;
381
382             data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
383
384             /* Add enumerated joysticks to the combobox */
385             for (i = 0; i < data->num_joysticks; i++)
386             {
387                 struct Joystick *joy = &data->joysticks[i];
388                 SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_ADDSTRING, 0, (LPARAM) joy->instance.tszInstanceName);
389             }
390
391             draw_joystick_buttons(hwnd, data);
392             draw_joystick_axes(hwnd, data);
393
394             return TRUE;
395         }
396
397         case WM_COMMAND:
398             switch(wparam)
399             {
400                 case MAKEWPARAM(IDC_TESTSELECTCOMBO, CBN_SELCHANGE):
401                     test_handle_joychange(hwnd, data);
402                 break;
403             }
404             return TRUE;
405
406         case WM_NOTIFY:
407             switch(((LPNMHDR)lparam)->code)
408             {
409                 case PSN_SETACTIVE:
410                 {
411                     DWORD tid;
412
413                     /* Initialize input thread */
414                     if (data->num_joysticks > 0)
415                     {
416                         data->stop = FALSE;
417
418                         /* Set the first joystick as default */
419                         SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_SETCURSEL, 0, 0);
420                         test_handle_joychange(hwnd, data);
421
422                         thread = CreateThread(NULL, 0, input_thread, (void*) data, 0, &tid);
423                     }
424                 }
425                 break;
426
427                 case PSN_RESET:
428                     /* Stop input thread */
429                     data->stop = TRUE;
430                     CloseHandle(thread);
431                 break;
432             }
433             return TRUE;
434     }
435     return FALSE;
436 }
437
438 /******************************************************************************
439  * propsheet_callback [internal]
440  */
441 static int CALLBACK propsheet_callback(HWND hwnd, UINT msg, LPARAM lparam)
442 {
443     TRACE("(%p, 0x%08x/%d, 0x%lx)\n", hwnd, msg, msg, lparam);
444     switch (msg)
445     {
446         case PSCB_INITIALIZED:
447             break;
448     }
449     return 0;
450 }
451
452 /******************************************************************************
453  * display_cpl_sheets [internal]
454  *
455  * Build and display the dialog with all control panel propertysheets
456  *
457  */
458 static void display_cpl_sheets(HWND parent, struct JoystickData *data)
459 {
460     INITCOMMONCONTROLSEX icex;
461     PROPSHEETPAGEW psp[NUM_PROPERTY_PAGES];
462     PROPSHEETHEADERW psh;
463     DWORD id = 0;
464
465     OleInitialize(NULL);
466     /* Initialize common controls */
467     icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
468     icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES;
469     InitCommonControlsEx(&icex);
470
471     ZeroMemory(&psh, sizeof(psh));
472     ZeroMemory(psp, sizeof(psp));
473
474     /* Fill out all PROPSHEETPAGE */
475     psp[id].dwSize = sizeof (PROPSHEETPAGEW);
476     psp[id].hInstance = hcpl;
477     psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_LIST);
478     psp[id].pfnDlgProc = list_dlgproc;
479     psp[id].lParam = (INT_PTR) data;
480     id++;
481
482     psp[id].dwSize = sizeof (PROPSHEETPAGEW);
483     psp[id].hInstance = hcpl;
484     psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_TEST);
485     psp[id].pfnDlgProc = test_dlgproc;
486     psp[id].lParam = (INT_PTR) data;
487     id++;
488
489     psp[id].dwSize = sizeof (PROPSHEETPAGEW);
490     psp[id].hInstance = hcpl;
491     psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_FORCEFEEDBACK);
492     psp[id].pfnDlgProc = NULL;
493     psp[id].lParam = (INT_PTR) data;
494     id++;
495
496     /* Fill out the PROPSHEETHEADER */
497     psh.dwSize = sizeof (PROPSHEETHEADERW);
498     psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID | PSH_USECALLBACK;
499     psh.hwndParent = parent;
500     psh.hInstance = hcpl;
501     psh.pszCaption = MAKEINTRESOURCEW(IDS_CPL_NAME);
502     psh.nPages = id;
503     psh.u3.ppsp = psp;
504     psh.pfnCallback = propsheet_callback;
505
506     /* display the dialog */
507     PropertySheetW(&psh);
508
509     OleUninitialize();
510 }
511
512 /*********************************************************************
513  * CPlApplet (joy.cpl.@)
514  *
515  * Control Panel entry point
516  *
517  * PARAMS
518  *  hWnd    [I] Handle for the Control Panel Window
519  *  command [I] CPL_* Command
520  *  lParam1 [I] first extra Parameter
521  *  lParam2 [I] second extra Parameter
522  *
523  * RETURNS
524  *  Depends on the command
525  *
526  */
527 LONG CALLBACK CPlApplet(HWND hwnd, UINT command, LPARAM lParam1, LPARAM lParam2)
528 {
529     static struct JoystickData data;
530     TRACE("(%p, %u, 0x%lx, 0x%lx)\n", hwnd, command, lParam1, lParam2);
531
532     switch (command)
533     {
534         case CPL_INIT:
535         {
536             HRESULT hr;
537
538             /* Initialize dinput */
539             hr = DirectInput8Create(GetModuleHandleW(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void**)&data.di, NULL);
540
541             if (FAILED(hr))
542             {
543                 ERR("Failed to initialize DirectInput: 0x%08x\n", hr);
544                 return FALSE;
545             }
546
547             /* Then get all the connected joysticks */
548             initialize_joysticks(&data);
549
550             return TRUE;
551         }
552         case CPL_GETCOUNT:
553             return 1;
554
555         case CPL_INQUIRE:
556         {
557             CPLINFO *appletInfo = (CPLINFO *) lParam2;
558
559             appletInfo->idName = IDS_CPL_NAME;
560             appletInfo->idInfo = IDS_CPL_INFO;
561             appletInfo->lData = 0;
562             return TRUE;
563         }
564
565         case CPL_DBLCLK:
566             display_cpl_sheets(hwnd, &data);
567             break;
568
569         case CPL_STOP:
570             destroy_joysticks(&data);
571
572             /* And destroy dinput too */
573             IDirectInput8_Release(data.di);
574             break;
575     }
576
577     return FALSE;
578 }