ntdll/tests: Adjust test_virtual_unwind() for Win11 results.
[wine.git] / dlls / joy.cpl / main.c
blob7d724be728007cc3175427be7f99a925beb5217d
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 COBJMACROS
23 #define CONST_VTABLE
25 #include <stdarg.h>
26 #include <windef.h>
27 #include <winbase.h>
28 #include <winuser.h>
29 #include <commctrl.h>
30 #include <dinput.h>
31 #include <cpl.h>
32 #include "ole2.h"
34 #include "wine/debug.h"
35 #include "wine/list.h"
37 #include "joy_private.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(joycpl);
41 struct device
43 struct list entry;
44 IDirectInputDevice8W *device;
47 static HMODULE hcpl;
49 static CRITICAL_SECTION joy_cs;
50 static CRITICAL_SECTION_DEBUG joy_cs_debug =
52 0, 0, &joy_cs,
53 { &joy_cs_debug.ProcessLocksList, &joy_cs_debug.ProcessLocksList },
54 0, 0, { (DWORD_PTR)(__FILE__ ": joy_cs") }
56 static CRITICAL_SECTION joy_cs = { &joy_cs_debug, -1, 0, 0, 0, 0 };
58 static struct list devices = LIST_INIT( devices );
60 /*********************************************************************
61 * DllMain
63 BOOL WINAPI DllMain(HINSTANCE hdll, DWORD reason, LPVOID reserved)
65 TRACE("(%p, %ld, %p)\n", hdll, reason, reserved);
67 switch (reason)
69 case DLL_PROCESS_ATTACH:
70 DisableThreadLibraryCalls(hdll);
71 hcpl = hdll;
73 return TRUE;
76 static BOOL CALLBACK enum_devices( const DIDEVICEINSTANCEW *instance, void *context )
78 DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)};
79 IDirectInput8W *dinput = context;
80 struct device *entry;
82 if (!(entry = calloc( 1, sizeof(*entry) ))) return DIENUM_STOP;
84 IDirectInput8_CreateDevice( dinput, &instance->guidInstance, &entry->device, NULL );
85 IDirectInputDevice8_SetDataFormat( entry->device, &c_dfDIJoystick );
86 IDirectInputDevice8_GetCapabilities( entry->device, &caps );
88 list_add_tail( &devices, &entry->entry );
90 return DIENUM_CONTINUE;
93 static void clear_devices(void)
95 struct device *entry, *next;
97 LIST_FOR_EACH_ENTRY_SAFE( entry, next, &devices, struct device, entry )
99 list_remove( &entry->entry );
100 IDirectInputDevice8_Unacquire( entry->device );
101 IDirectInputDevice8_Release( entry->device );
102 free( entry );
106 /******************************************************************************
107 * get_app_key [internal]
108 * Get the default DirectInput key and the selected app config key.
110 static BOOL get_app_key(HKEY *defkey, HKEY *appkey)
112 *appkey = 0;
114 /* Registry key can be found in HKCU\Software\Wine\DirectInput */
115 if (RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Wine\\DirectInput\\Joysticks", 0, NULL, 0,
116 KEY_SET_VALUE | KEY_READ, NULL, defkey, NULL))
117 *defkey = 0;
119 return *defkey || *appkey;
122 /******************************************************************************
123 * set_config_key [internal]
124 * Writes a string value to a registry key, deletes the key if value == NULL
126 static DWORD set_config_key(HKEY defkey, HKEY appkey, const WCHAR *name, const WCHAR *value)
128 if (value == NULL)
130 if (appkey && !RegDeleteValueW(appkey, name))
131 return 0;
133 if (defkey && !RegDeleteValueW(defkey, name))
134 return 0;
136 else
138 DWORD size = wcslen(value);
140 if (appkey && !RegSetValueExW(appkey, name, 0, REG_SZ, (const BYTE*) value, (size + 1)*sizeof(WCHAR)))
141 return 0;
143 if (defkey && !RegSetValueExW(defkey, name, 0, REG_SZ, (const BYTE*) value, (size + 1)*sizeof(WCHAR)))
144 return 0;
147 return ERROR_FILE_NOT_FOUND;
150 /******************************************************************************
151 * enable_joystick [internal]
152 * Writes to the DirectInput registry key that enables/disables a joystick
153 * from being enumerated.
155 static void enable_joystick(WCHAR *joy_name, BOOL enable)
157 HKEY hkey, appkey;
159 get_app_key(&hkey, &appkey);
161 if (!enable)
162 set_config_key(hkey, appkey, joy_name, L"disabled");
163 else
164 set_config_key(hkey, appkey, joy_name, NULL);
166 if (hkey) RegCloseKey(hkey);
167 if (appkey) RegCloseKey(appkey);
170 static void refresh_joystick_list( HWND hwnd )
172 IDirectInput8W *dinput;
173 struct device *entry;
174 HKEY hkey, appkey;
175 DWORD values = 0;
176 LSTATUS status;
177 DWORD i;
179 clear_devices();
181 DirectInput8Create( GetModuleHandleW( NULL ), DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void **)&dinput, NULL );
182 IDirectInput8_EnumDevices( dinput, DI8DEVCLASS_GAMECTRL, enum_devices, dinput, DIEDFL_ATTACHEDONLY );
183 IDirectInput8_Release( dinput );
185 SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_RESETCONTENT, 0, 0);
186 SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_RESETCONTENT, 0, 0);
187 SendDlgItemMessageW(hwnd, IDC_XINPUTLIST, LB_RESETCONTENT, 0, 0);
189 LIST_FOR_EACH_ENTRY( entry, &devices, struct device, entry )
191 DIDEVICEINSTANCEW info = {.dwSize = sizeof(DIDEVICEINSTANCEW)};
192 DIPROPGUIDANDPATH prop =
194 .diph =
196 .dwSize = sizeof(DIPROPGUIDANDPATH),
197 .dwHeaderSize = sizeof(DIPROPHEADER),
198 .dwHow = DIPH_DEVICE,
202 if (FAILED(IDirectInputDevice8_GetDeviceInfo( entry->device, &info ))) continue;
203 if (FAILED(IDirectInputDevice8_GetProperty( entry->device, DIPROP_GUIDANDPATH, &prop.diph ))) continue;
205 if (wcsstr( prop.wszPath, L"&ig_" )) SendDlgItemMessageW( hwnd, IDC_XINPUTLIST, LB_ADDSTRING, 0, (LPARAM)info.tszInstanceName );
206 else SendDlgItemMessageW( hwnd, IDC_JOYSTICKLIST, LB_ADDSTRING, 0, (LPARAM)info.tszInstanceName );
209 /* Search for disabled joysticks */
210 get_app_key(&hkey, &appkey);
211 RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL, &values, NULL, NULL, NULL, NULL);
213 for (i=0; i < values; i++)
215 DWORD name_len = MAX_PATH, data_len = MAX_PATH;
216 WCHAR buf_name[MAX_PATH + 9], buf_data[MAX_PATH];
218 status = RegEnumValueW(hkey, i, buf_name, &name_len, NULL, NULL, (BYTE*) buf_data, &data_len);
220 if (status == ERROR_SUCCESS && !wcscmp(L"disabled", buf_data))
221 SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_ADDSTRING, 0, (LPARAM) buf_name);
224 if (hkey) RegCloseKey(hkey);
225 if (appkey) RegCloseKey(appkey);
228 static void override_joystick(WCHAR *joy_name, BOOL override)
230 HKEY hkey, appkey;
232 get_app_key(&hkey, &appkey);
234 if (override)
235 set_config_key(hkey, appkey, joy_name, L"override");
236 else
237 set_config_key(hkey, appkey, joy_name, NULL);
239 if (hkey) RegCloseKey(hkey);
240 if (appkey) RegCloseKey(appkey);
243 /*********************************************************************
244 * list_dlgproc [internal]
247 static INT_PTR CALLBACK list_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
249 WCHAR instance_name[MAX_PATH] = {0};
250 int sel;
252 TRACE("(%p, 0x%08x/%d, 0x%Ix)\n", hwnd, msg, msg, lparam);
253 switch (msg)
255 case WM_INITDIALOG:
257 refresh_joystick_list( hwnd );
259 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONENABLE), FALSE);
260 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONDISABLE), FALSE);
261 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONRESET), FALSE);
262 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONOVERRIDE), FALSE);
264 return TRUE;
267 case WM_COMMAND:
269 switch (LOWORD(wparam))
271 case IDC_BUTTONDISABLE:
273 if ((sel = SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_GETCURSEL, 0, 0)) >= 0)
274 SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_GETTEXT, sel, (LPARAM)instance_name);
275 if ((sel = SendDlgItemMessageW(hwnd, IDC_XINPUTLIST, LB_GETCURSEL, 0, 0)) >= 0)
276 SendDlgItemMessageW(hwnd, IDC_XINPUTLIST, LB_GETTEXT, sel, (LPARAM)instance_name);
278 if (instance_name[0])
280 enable_joystick(instance_name, FALSE);
281 refresh_joystick_list( hwnd );
284 break;
286 case IDC_BUTTONENABLE:
288 if ((sel = SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_GETCURSEL, 0, 0)) >= 0)
289 SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_GETTEXT, sel, (LPARAM)instance_name);
291 if (instance_name[0])
293 enable_joystick(instance_name, TRUE);
294 refresh_joystick_list( hwnd );
297 break;
299 case IDC_BUTTONRESET:
301 if ((sel = SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_GETCURSEL, 0, 0)) >= 0)
303 SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_GETTEXT, sel, (LPARAM)instance_name);
304 override_joystick(instance_name, FALSE);
305 refresh_joystick_list( hwnd );
308 break;
310 case IDC_BUTTONOVERRIDE:
312 if ((sel = SendDlgItemMessageW(hwnd, IDC_XINPUTLIST, LB_GETCURSEL, 0, 0)) >= 0)
314 SendDlgItemMessageW(hwnd, IDC_XINPUTLIST, LB_GETTEXT, sel, (LPARAM)instance_name);
315 override_joystick(instance_name, TRUE);
316 refresh_joystick_list( hwnd );
319 break;
321 case IDC_JOYSTICKLIST:
322 SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_SETCURSEL, -1, 0);
323 SendDlgItemMessageW(hwnd, IDC_XINPUTLIST, LB_SETCURSEL, -1, 0);
324 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONENABLE), FALSE);
325 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONDISABLE), TRUE);
326 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONOVERRIDE), FALSE);
327 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONRESET), TRUE);
328 break;
330 case IDC_XINPUTLIST:
331 SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_SETCURSEL, -1, 0);
332 SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_SETCURSEL, -1, 0);
333 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONENABLE), FALSE);
334 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONDISABLE), TRUE);
335 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONOVERRIDE), TRUE);
336 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONRESET), FALSE);
337 break;
339 case IDC_DISABLEDLIST:
340 SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_SETCURSEL, -1, 0);
341 SendDlgItemMessageW(hwnd, IDC_XINPUTLIST, LB_SETCURSEL, -1, 0);
342 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONENABLE), TRUE);
343 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONDISABLE), FALSE);
344 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONOVERRIDE), FALSE);
345 EnableWindow(GetDlgItem(hwnd, IDC_BUTTONRESET), FALSE);
346 break;
349 return TRUE;
351 case WM_NOTIFY:
352 return TRUE;
354 default:
355 break;
357 return FALSE;
361 /******************************************************************************
362 * propsheet_callback [internal]
364 static int CALLBACK propsheet_callback(HWND hwnd, UINT msg, LPARAM lparam)
366 TRACE("(%p, 0x%08x/%d, 0x%Ix)\n", hwnd, msg, msg, lparam);
367 switch (msg)
369 case PSCB_INITIALIZED:
370 break;
372 return 0;
375 static void display_cpl_sheets( HWND parent )
377 INITCOMMONCONTROLSEX init =
379 .dwSize = sizeof(INITCOMMONCONTROLSEX),
380 .dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES,
382 PROPSHEETPAGEW pages[] =
385 .dwSize = sizeof(PROPSHEETPAGEW),
386 .hInstance = hcpl,
387 .pszTemplate = MAKEINTRESOURCEW( IDD_LIST ),
388 .pfnDlgProc = list_dlgproc,
391 .dwSize = sizeof(PROPSHEETPAGEW),
392 .hInstance = hcpl,
393 .pszTemplate = MAKEINTRESOURCEW( IDD_TEST_DI ),
394 .pfnDlgProc = test_di_dialog_proc,
397 .dwSize = sizeof(PROPSHEETPAGEW),
398 .hInstance = hcpl,
399 .pszTemplate = MAKEINTRESOURCEW( IDD_TEST_XI ),
400 .pfnDlgProc = test_xi_dialog_proc,
403 PROPSHEETHEADERW header =
405 .dwSize = sizeof(PROPSHEETHEADERW),
406 .dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID | PSH_USECALLBACK,
407 .hwndParent = parent,
408 .hInstance = hcpl,
409 .pszCaption = MAKEINTRESOURCEW( IDS_CPL_NAME ),
410 .nPages = ARRAY_SIZE(pages),
411 .ppsp = pages,
412 .pfnCallback = propsheet_callback,
414 ACTCTXW context_desc =
416 .cbSize = sizeof(ACTCTXW),
417 .hModule = hcpl,
418 .lpResourceName = MAKEINTRESOURCEW( 124 ),
419 .dwFlags = ACTCTX_FLAG_HMODULE_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID,
421 ULONG_PTR cookie;
422 HANDLE context;
423 BOOL activated;
425 OleInitialize( NULL );
427 context = CreateActCtxW( &context_desc );
428 if (context == INVALID_HANDLE_VALUE) activated = FALSE;
429 else activated = ActivateActCtx( context, &cookie );
431 InitCommonControlsEx( &init );
432 PropertySheetW( &header );
434 if (activated) DeactivateActCtx( 0, cookie );
435 ReleaseActCtx( context );
436 OleUninitialize();
439 static void register_window_class(void)
441 WNDCLASSW xi_class =
443 .hInstance = hcpl,
444 .lpfnWndProc = &test_xi_window_proc,
445 .lpszClassName = L"JoyCplXInput",
447 WNDCLASSW di_axes_class =
449 .hInstance = hcpl,
450 .lpfnWndProc = &test_di_axes_window_proc,
451 .lpszClassName = L"JoyCplDInputAxes",
453 WNDCLASSW di_povs_class =
455 .hInstance = hcpl,
456 .lpfnWndProc = &test_di_povs_window_proc,
457 .lpszClassName = L"JoyCplDInputPOVs",
459 WNDCLASSW di_buttons_class =
461 .hInstance = hcpl,
462 .lpfnWndProc = &test_di_buttons_window_proc,
463 .lpszClassName = L"JoyCplDInputButtons",
466 RegisterClassW( &xi_class );
467 RegisterClassW( &di_axes_class );
468 RegisterClassW( &di_povs_class );
469 RegisterClassW( &di_buttons_class );
472 static void unregister_window_class(void)
474 UnregisterClassW( L"JoyCplDInputAxes", hcpl );
475 UnregisterClassW( L"JoyCplDInputPOVs", hcpl );
476 UnregisterClassW( L"JoyCplDInputButtons", hcpl );
477 UnregisterClassW( L"JoyCplXInput", hcpl );
480 /*********************************************************************
481 * CPlApplet (joy.cpl.@)
483 * Control Panel entry point
485 * PARAMS
486 * hWnd [I] Handle for the Control Panel Window
487 * command [I] CPL_* Command
488 * lParam1 [I] first extra Parameter
489 * lParam2 [I] second extra Parameter
491 * RETURNS
492 * Depends on the command
495 LONG CALLBACK CPlApplet(HWND hwnd, UINT command, LPARAM lParam1, LPARAM lParam2)
497 TRACE("(%p, %u, 0x%Ix, 0x%Ix)\n", hwnd, command, lParam1, lParam2);
499 switch (command)
501 case CPL_INIT:
502 register_window_class();
503 return TRUE;
505 case CPL_GETCOUNT:
506 return 1;
508 case CPL_INQUIRE:
510 CPLINFO *appletInfo = (CPLINFO *) lParam2;
512 appletInfo->idIcon = ICO_MAIN;
513 appletInfo->idName = IDS_CPL_NAME;
514 appletInfo->idInfo = IDS_CPL_INFO;
515 appletInfo->lData = 0;
516 return TRUE;
519 case CPL_DBLCLK:
520 display_cpl_sheets( hwnd );
521 break;
523 case CPL_STOP:
524 clear_devices();
525 unregister_window_class();
526 break;
529 return FALSE;