-- v2: joy.cpl: Remove unnecessary JoystickData structure. joy.cpl: Move dinput test tab logic to dinput.c. joy.cpl: Draw dinput axes state with gdi32. joy.cpl: Draw dinput button state with gdi32. joy.cpl: Add a checkbox to test xinput rumble. joy.cpl: Draw xinput controller state with gdi32. joy.cpl: Add a new xinput controller test tab. joy.cpl: Build without NONAMELESSUNION. joy.cpl: Use designated initializers in display_cpl_sheets. joy.cpl: Add DI_ prefix to dinput dialog constants.
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/joy.cpl/joy.rc | 16 +++++++------- dlls/joy.cpl/main.c | 47 ++++++++++++++++++++--------------------- dlls/joy.cpl/resource.h | 14 ++++++------ 3 files changed, 38 insertions(+), 39 deletions(-)
diff --git a/dlls/joy.cpl/joy.rc b/dlls/joy.cpl/joy.rc index 0d4c719166d..46b0eae532c 100644 --- a/dlls/joy.cpl/joy.rc +++ b/dlls/joy.cpl/joy.rc @@ -49,19 +49,19 @@ FONT 8, "Ms Shell Dlg" LTEXT "After disabling or enabling a device, the connected joysticks won't be updated here until you restart this applet.", IDC_STATIC, 10, 270, 200, 25 }
-IDD_TEST DIALOG 0, 0, 320, 300 +IDD_TEST_DI DIALOG 0, 0, 320, 300 STYLE WS_CAPTION | WS_CHILD | WS_DISABLED -CAPTION "Test Joystick" +CAPTION "DInput" FONT 8, "Ms Shell Dlg" { - COMBOBOX IDC_TESTSELECTCOMBO, 15, 10, 291, 60, CBS_DROPDOWNLIST | CBS_HASSTRINGS + COMBOBOX IDC_DI_DEVICES, 15, 10, 291, 60, CBS_DROPDOWNLIST | CBS_HASSTRINGS GROUPBOX "Buttons", IDC_STATIC, 15, 100, 291, 70 - GROUPBOX "", IDC_TESTGROUPXY, 15, 30, 60, 60 - GROUPBOX "", IDC_TESTGROUPRXRY, 92, 30, 60, 60 - GROUPBOX "", IDC_TESTGROUPZRZ, 169, 30, 60, 60 - GROUPBOX "", IDC_TESTGROUPPOV, 246, 30, 60, 60 + GROUPBOX "", IDC_DI_AXIS_X_Y, 15, 30, 60, 60 + GROUPBOX "", IDC_DI_AXIS_RX_RY, 92, 30, 60, 60 + GROUPBOX "", IDC_DI_AXIS_Z_RZ, 169, 30, 60, 60 + GROUPBOX "", IDC_DI_AXIS_POV_0, 246, 30, 60, 60 LTEXT "Force Feedback Effect", IDC_STATIC, 15, 180, 291, 10 - LISTBOX IDC_FFEFFECTLIST, 15, 190, 291, 70, WS_TABSTOP | WS_VSCROLL | LBS_NOTIFY + LISTBOX IDC_DI_EFFECTS, 15, 190, 291, 70, WS_TABSTOP | WS_VSCROLL | LBS_NOTIFY LTEXT "Press any button in the controller to activate the chosen effect. The effect direction can be changed with the controller axis.", IDC_STATIC, 15, 260, 291, 25 } diff --git a/dlls/joy.cpl/main.c b/dlls/joy.cpl/main.c index e5377a71aa9..279b5c07e22 100644 --- a/dlls/joy.cpl/main.c +++ b/dlls/joy.cpl/main.c @@ -714,8 +714,8 @@ static void initialize_effects_list( HWND hwnd, IDirectInputDevice8W *device )
IDirectInputDevice8_EnumEffects( device, enum_effects, device, 0 );
- SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_RESETCONTENT, 0, 0); - SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_ADDSTRING, 0, (LPARAM)L"None"); + SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_RESETCONTENT, 0, 0 ); + SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_ADDSTRING, 0, (LPARAM)L"None" );
LIST_FOR_EACH_ENTRY( effect, &effects, struct effect, entry ) { @@ -724,7 +724,7 @@ static void initialize_effects_list( HWND hwnd, IDirectInputDevice8W *device )
if (FAILED(IDirectInputEffect_GetEffectGuid( effect->effect, &guid ))) continue; if (FAILED(IDirectInputDevice8_GetEffectInfo( device, &info, &guid ))) continue; - SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_ADDSTRING, 0, (LPARAM)(info.tszName + 5)); + SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_ADDSTRING, 0, (LPARAM)( info.tszName + 5 ) ); } }
@@ -737,7 +737,7 @@ static void test_handle_joychange(HWND hwnd, struct JoystickData *data)
set_selected_device( NULL );
- i = SendDlgItemMessageW( hwnd, IDC_TESTSELECTCOMBO, CB_GETCURSEL, 0, 0 ); + i = SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_GETCURSEL, 0, 0 ); if (i < 0) return;
entry = list_head( &devices ); @@ -760,7 +760,7 @@ static void ff_handle_effectchange( HWND hwnd )
set_selected_effect( NULL );
- sel = SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_GETCURSEL, 0, 0) - 1; + sel = SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_GETCURSEL, 0, 0 ) - 1; if (sel < 0) return;
entry = list_head( &effects ); @@ -825,11 +825,10 @@ static void draw_joystick_buttons(HWND hwnd, struct JoystickData* data)
static void draw_joystick_axes(HWND hwnd, struct JoystickData* data) { + static const WCHAR *axes_names[TEST_MAX_AXES] = {L"X,Y", L"Rx,Ry", L"Z,Rz", L"POV"}; + static const DWORD axes_idc[TEST_MAX_AXES] = {IDC_DI_AXIS_X_Y, IDC_DI_AXIS_RX_RY, IDC_DI_AXIS_Z_RZ, IDC_DI_AXIS_POV_0}; int i; HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE); - static const WCHAR axes_names[TEST_MAX_AXES][7] = { L"X,Y", L"Rx,Ry", L"Z,Rz", L"POV"}; - static const DWORD axes_idc[TEST_MAX_AXES] = { IDC_TESTGROUPXY, IDC_TESTGROUPRXRY, - IDC_TESTGROUPZRZ, IDC_TESTGROUPPOV };
for (i = 0; i < TEST_MAX_AXES; i++) { @@ -853,17 +852,17 @@ static void draw_joystick_axes(HWND hwnd, struct JoystickData* data) * test_dlgproc [internal] * */ -static void refresh_test_joystick_list(HWND hwnd, struct JoystickData *data) +static void di_update_select_combo( HWND hwnd ) { struct device *entry;
- SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_RESETCONTENT, 0, 0); + SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_RESETCONTENT, 0, 0 );
LIST_FOR_EACH_ENTRY( entry, &devices, struct device, entry ) { DIDEVICEINSTANCEW info = {.dwSize = sizeof(DIDEVICEINSTANCEW)}; if (FAILED(IDirectInputDevice8_GetDeviceInfo( entry->device, &info ))) continue; - SendDlgItemMessageW( hwnd, IDC_TESTSELECTCOMBO, CB_ADDSTRING, 0, (LPARAM)info.tszInstanceName ); + SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_ADDSTRING, 0, (LPARAM)info.tszInstanceName ); } }
@@ -879,7 +878,7 @@ static INT_PTR CALLBACK test_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM { data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
- refresh_test_joystick_list(hwnd, data); + di_update_select_combo( hwnd ); draw_joystick_buttons(hwnd, data); draw_joystick_axes(hwnd, data);
@@ -889,16 +888,16 @@ static INT_PTR CALLBACK test_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM case WM_COMMAND: switch(wparam) { - case MAKEWPARAM(IDC_TESTSELECTCOMBO, CBN_SELCHANGE): - test_handle_joychange(hwnd, data); + case MAKEWPARAM( IDC_DI_DEVICES, CBN_SELCHANGE ): + test_handle_joychange( hwnd, data );
- SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_SETCURSEL, 0, 0); - ff_handle_effectchange( hwnd ); - break; + SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_SETCURSEL, 0, 0 ); + ff_handle_effectchange( hwnd ); + break;
- case MAKEWPARAM(IDC_FFEFFECTLIST, LBN_SELCHANGE): - ff_handle_effectchange( hwnd ); - break; + case MAKEWPARAM( IDC_DI_EFFECTS, LBN_SELCHANGE ): + ff_handle_effectchange( hwnd ); + break; } return TRUE;
@@ -909,15 +908,15 @@ static INT_PTR CALLBACK test_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM { DWORD tid;
- refresh_test_joystick_list(hwnd, data); + di_update_select_combo( hwnd );
data->stop = FALSE;
/* Set the first joystick as default */ - SendDlgItemMessageW(hwnd, IDC_TESTSELECTCOMBO, CB_SETCURSEL, 0, 0); + SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_SETCURSEL, 0, 0 ); test_handle_joychange(hwnd, data);
- SendDlgItemMessageW(hwnd, IDC_FFEFFECTLIST, LB_SETCURSEL, 0, 0); + SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_SETCURSEL, 0, 0 ); ff_handle_effectchange( hwnd );
thread = CreateThread(NULL, 0, input_thread, (void*) data, 0, &tid); @@ -997,7 +996,7 @@ static void display_cpl_sheets(HWND parent, struct JoystickData *data)
psp[id].dwSize = sizeof (PROPSHEETPAGEW); psp[id].hInstance = hcpl; - psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_TEST); + psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_TEST_DI); psp[id].pfnDlgProc = test_dlgproc; psp[id].lParam = (INT_PTR) data; id++; diff --git a/dlls/joy.cpl/resource.h b/dlls/joy.cpl/resource.h index c15f8dfbdeb..74a81d74cf0 100644 --- a/dlls/joy.cpl/resource.h +++ b/dlls/joy.cpl/resource.h @@ -35,7 +35,7 @@ #define IDC_STATIC -1
#define IDD_LIST 1000 -#define IDD_TEST 1001 +#define IDD_TEST_DI 1001
#define IDC_JOYSTICKLIST 2000 #define IDC_DISABLEDLIST 2001 @@ -45,12 +45,12 @@ #define IDC_BUTTONRESET 2012 #define IDC_BUTTONOVERRIDE 2013
-#define IDC_TESTSELECTCOMBO 2100 -#define IDC_TESTGROUPXY 2101 -#define IDC_TESTGROUPRXRY 2102 -#define IDC_TESTGROUPZRZ 2103 -#define IDC_TESTGROUPPOV 2104 -#define IDC_FFEFFECTLIST 2105 +#define IDC_DI_DEVICES 2100 +#define IDC_DI_AXIS_X_Y 2101 +#define IDC_DI_AXIS_RX_RY 2102 +#define IDC_DI_AXIS_Z_RZ 2103 +#define IDC_DI_AXIS_POV_0 2104 +#define IDC_DI_EFFECTS 2105
#define ICO_MAIN 100
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/joy.cpl/main.c | 118 ++++++++++++++++++++------------------------ 1 file changed, 54 insertions(+), 64 deletions(-)
diff --git a/dlls/joy.cpl/main.c b/dlls/joy.cpl/main.c index 279b5c07e22..f0620b1e3fa 100644 --- a/dlls/joy.cpl/main.c +++ b/dlls/joy.cpl/main.c @@ -950,73 +950,63 @@ static int CALLBACK propsheet_callback(HWND hwnd, UINT msg, LPARAM lparam) return 0; }
-/****************************************************************************** - * display_cpl_sheets [internal] - * - * Build and display the dialog with all control panel propertysheets - * - */ -static void display_cpl_sheets(HWND parent, struct JoystickData *data) +static void display_cpl_sheets( HWND parent, struct JoystickData *data ) { - INITCOMMONCONTROLSEX icex; - BOOL activated = FALSE; - PROPSHEETPAGEW psp[2]; - PROPSHEETHEADERW psh; + INITCOMMONCONTROLSEX init = + { + .dwSize = sizeof(INITCOMMONCONTROLSEX), + .dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES, + }; + PROPSHEETPAGEW pages[] = + { + { + .dwSize = sizeof(PROPSHEETPAGEW), + .hInstance = hcpl, + .u.pszTemplate = MAKEINTRESOURCEW( IDD_LIST ), + .pfnDlgProc = list_dlgproc, + .lParam = (INT_PTR)data, + }, + { + .dwSize = sizeof(PROPSHEETPAGEW), + .hInstance = hcpl, + .u.pszTemplate = MAKEINTRESOURCEW( IDD_TEST_DI ), + .pfnDlgProc = test_dlgproc, + .lParam = (INT_PTR)data, + }, + }; + PROPSHEETHEADERW header = + { + .dwSize = sizeof(PROPSHEETHEADERW), + .dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID | PSH_USECALLBACK, + .hwndParent = parent, + .hInstance = hcpl, + .pszCaption = MAKEINTRESOURCEW( IDS_CPL_NAME ), + .nPages = ARRAY_SIZE(pages), + .u3.ppsp = pages, + .pfnCallback = propsheet_callback, + }; + ACTCTXW context_desc = + { + .cbSize = sizeof(ACTCTXW), + .hModule = hcpl, + .lpResourceName = MAKEINTRESOURCEW( 124 ), + .dwFlags = ACTCTX_FLAG_HMODULE_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID, + }; ULONG_PTR cookie; - ACTCTXW actctx; HANDLE context; - DWORD id = 0; - - OleInitialize(NULL); - /* Activate context */ - memset(&actctx, 0, sizeof(actctx)); - actctx.cbSize = sizeof(actctx); - actctx.hModule = hcpl; - actctx.lpResourceName = MAKEINTRESOURCEW(124); - actctx.dwFlags = ACTCTX_FLAG_HMODULE_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID; - context = CreateActCtxW(&actctx); - if (context != INVALID_HANDLE_VALUE) - activated = ActivateActCtx(context, &cookie); - - /* Initialize common controls */ - icex.dwSize = sizeof(INITCOMMONCONTROLSEX); - icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES; - InitCommonControlsEx(&icex); - - ZeroMemory(&psh, sizeof(psh)); - ZeroMemory(psp, sizeof(psp)); - - /* Fill out all PROPSHEETPAGE */ - psp[id].dwSize = sizeof (PROPSHEETPAGEW); - psp[id].hInstance = hcpl; - psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_LIST); - psp[id].pfnDlgProc = list_dlgproc; - psp[id].lParam = (INT_PTR) data; - id++; - - psp[id].dwSize = sizeof (PROPSHEETPAGEW); - psp[id].hInstance = hcpl; - psp[id].u.pszTemplate = MAKEINTRESOURCEW(IDD_TEST_DI); - psp[id].pfnDlgProc = test_dlgproc; - psp[id].lParam = (INT_PTR) data; - id++; - - /* Fill out the PROPSHEETHEADER */ - psh.dwSize = sizeof (PROPSHEETHEADERW); - psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID | PSH_USECALLBACK; - psh.hwndParent = parent; - psh.hInstance = hcpl; - psh.pszCaption = MAKEINTRESOURCEW(IDS_CPL_NAME); - psh.nPages = id; - psh.u3.ppsp = psp; - psh.pfnCallback = propsheet_callback; - - /* display the dialog */ - PropertySheetW(&psh); - - if (activated) - DeactivateActCtx(0, cookie); - ReleaseActCtx(context); + BOOL activated; + + OleInitialize( NULL ); + + context = CreateActCtxW( &context_desc ); + if (context == INVALID_HANDLE_VALUE) activated = FALSE; + else activated = ActivateActCtx( context, &cookie ); + + InitCommonControlsEx( &init ); + PropertySheetW( &header ); + + if (activated) DeactivateActCtx( 0, cookie ); + ReleaseActCtx( context ); OleUninitialize(); }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/joy.cpl/main.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/dlls/joy.cpl/main.c b/dlls/joy.cpl/main.c index f0620b1e3fa..7469f8ad460 100644 --- a/dlls/joy.cpl/main.c +++ b/dlls/joy.cpl/main.c @@ -19,7 +19,6 @@ * */
-#define NONAMELESSUNION #define COBJMACROS #define CONST_VTABLE
@@ -962,14 +961,14 @@ static void display_cpl_sheets( HWND parent, struct JoystickData *data ) { .dwSize = sizeof(PROPSHEETPAGEW), .hInstance = hcpl, - .u.pszTemplate = MAKEINTRESOURCEW( IDD_LIST ), + .pszTemplate = MAKEINTRESOURCEW( IDD_LIST ), .pfnDlgProc = list_dlgproc, .lParam = (INT_PTR)data, }, { .dwSize = sizeof(PROPSHEETPAGEW), .hInstance = hcpl, - .u.pszTemplate = MAKEINTRESOURCEW( IDD_TEST_DI ), + .pszTemplate = MAKEINTRESOURCEW( IDD_TEST_DI ), .pfnDlgProc = test_dlgproc, .lParam = (INT_PTR)data, }, @@ -982,7 +981,7 @@ static void display_cpl_sheets( HWND parent, struct JoystickData *data ) .hInstance = hcpl, .pszCaption = MAKEINTRESOURCEW( IDS_CPL_NAME ), .nPages = ARRAY_SIZE(pages), - .u3.ppsp = pages, + .ppsp = pages, .pfnCallback = propsheet_callback, }; ACTCTXW context_desc =
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/joy.cpl/Makefile.in | 5 +- dlls/joy.cpl/joy.rc | 28 +++++++ dlls/joy.cpl/joy_private.h | 33 ++++++++ dlls/joy.cpl/main.c | 8 +- dlls/joy.cpl/resource.h | 10 +++ dlls/joy.cpl/xinput.c | 166 +++++++++++++++++++++++++++++++++++++ 6 files changed, 247 insertions(+), 3 deletions(-) create mode 100644 dlls/joy.cpl/joy_private.h create mode 100644 dlls/joy.cpl/xinput.c
diff --git a/dlls/joy.cpl/Makefile.in b/dlls/joy.cpl/Makefile.in index d9b2aff3724..9a8f2d73528 100644 --- a/dlls/joy.cpl/Makefile.in +++ b/dlls/joy.cpl/Makefile.in @@ -1,10 +1,11 @@ MODULE = joy.cpl -IMPORTS = dxguid dinput dinput8 ole32 comctl32 user32 advapi32 +IMPORTS = dxguid dinput dinput8 ole32 comctl32 user32 advapi32 xinput
EXTRADLLFLAGS = -Wb,--prefer-native
C_SRCS = \ - main.c + main.c \ + xinput.c
RC_SRCS = joy.rc
diff --git a/dlls/joy.cpl/joy.rc b/dlls/joy.cpl/joy.rc index 46b0eae532c..dca694e5347 100644 --- a/dlls/joy.cpl/joy.rc +++ b/dlls/joy.cpl/joy.rc @@ -66,6 +66,34 @@ FONT 8, "Ms Shell Dlg" IDC_STATIC, 15, 260, 291, 25 }
+IDD_TEST_XI DIALOG 0, 0, 320, 300 +STYLE WS_CAPTION | WS_CHILD | WS_DISABLED +CAPTION "XInput" +FONT 8, "Ms Shell Dlg" +{ + GROUPBOX "User #0", IDC_XI_USER_0, 15, 10, 291, 60 + GROUPBOX "User #1", IDC_XI_USER_1, 15, 80, 291, 60 + GROUPBOX "User #2", IDC_XI_USER_2, 15, 150, 291, 60 + GROUPBOX "User #3", IDC_XI_USER_3, 15, 220, 291, 60 + + LTEXT "No user detected on slot #0, make sure your " \ + "gamepad is plugged in, and not overriden for " \ + "DInput in the Joysticks tab.", + IDC_XI_NO_USER_0, 15, 10, 291, 60 + LTEXT "No user detected on slot #1, make sure your " \ + "gamepad is plugged in, and not overriden for " \ + "DInput in the Joysticks tab.", + IDC_XI_NO_USER_1, 15, 80, 291, 60 + LTEXT "No user detected on slot #2, make sure your " \ + "gamepad is plugged in, and not overriden for " \ + "DInput in the Joysticks tab.", + IDC_XI_NO_USER_2, 15, 150, 291, 60 + LTEXT "No user detected on slot #3, make sure your " \ + "gamepad is plugged in, and not overriden for " \ + "DInput in the Joysticks tab.", + IDC_XI_NO_USER_3, 15, 220, 291, 60 +} + LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
/* @makedep: joy.manifest */ diff --git a/dlls/joy.cpl/joy_private.h b/dlls/joy.cpl/joy_private.h new file mode 100644 index 00000000000..09e1855f01d --- /dev/null +++ b/dlls/joy.cpl/joy_private.h @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Rémi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#ifndef __JOY_PRIVATE_H +#define __JOY_PRIVATE_H + +#include <stdarg.h> +#include <stddef.h> + +#include "windef.h" +#include "winbase.h" + +#include "resource.h" + +extern INT_PTR CALLBACK test_xi_dialog_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ); + +#endif /* __JOY_PRIVATE_H */ diff --git a/dlls/joy.cpl/main.c b/dlls/joy.cpl/main.c index 7469f8ad460..abc2f3f4d20 100644 --- a/dlls/joy.cpl/main.c +++ b/dlls/joy.cpl/main.c @@ -34,7 +34,7 @@ #include "wine/debug.h" #include "wine/list.h"
-#include "resource.h" +#include "joy_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(joycpl);
@@ -972,6 +972,12 @@ static void display_cpl_sheets( HWND parent, struct JoystickData *data ) .pfnDlgProc = test_dlgproc, .lParam = (INT_PTR)data, }, + { + .dwSize = sizeof(PROPSHEETPAGEW), + .hInstance = hcpl, + .pszTemplate = MAKEINTRESOURCEW( IDD_TEST_XI ), + .pfnDlgProc = test_xi_dialog_proc, + }, }; PROPSHEETHEADERW header = { diff --git a/dlls/joy.cpl/resource.h b/dlls/joy.cpl/resource.h index 74a81d74cf0..714fcca664d 100644 --- a/dlls/joy.cpl/resource.h +++ b/dlls/joy.cpl/resource.h @@ -36,6 +36,7 @@
#define IDD_LIST 1000 #define IDD_TEST_DI 1001 +#define IDD_TEST_XI 1002
#define IDC_JOYSTICKLIST 2000 #define IDC_DISABLEDLIST 2001 @@ -52,6 +53,15 @@ #define IDC_DI_AXIS_POV_0 2104 #define IDC_DI_EFFECTS 2105
+#define IDC_XI_USER_0 2200 +#define IDC_XI_USER_1 2201 +#define IDC_XI_USER_2 2202 +#define IDC_XI_USER_3 2203 +#define IDC_XI_NO_USER_0 2210 +#define IDC_XI_NO_USER_1 2211 +#define IDC_XI_NO_USER_2 2212 +#define IDC_XI_NO_USER_3 2213 + #define ICO_MAIN 100
#endif diff --git a/dlls/joy.cpl/xinput.c b/dlls/joy.cpl/xinput.c new file mode 100644 index 00000000000..f0e3c8ca437 --- /dev/null +++ b/dlls/joy.cpl/xinput.c @@ -0,0 +1,166 @@ +/* + * Copyright 2022 Rémi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include <stdarg.h> +#include <stddef.h> + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" + +#include "xinput.h" + +#include "wine/debug.h" +#include "wine/list.h" + +#include "joy_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(joycpl); + +struct device_state +{ + XINPUT_CAPABILITIES caps; + XINPUT_STATE state; + DWORD status; +}; + +static CRITICAL_SECTION state_cs; +static CRITICAL_SECTION_DEBUG state_cs_debug = +{ + 0, 0, &state_cs, + { &state_cs_debug.ProcessLocksList, &state_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": state_cs") } +}; +static CRITICAL_SECTION state_cs = { &state_cs_debug, -1, 0, 0, 0, 0 }; + +static struct device_state devices_state[XUSER_MAX_COUNT] = +{ + {.status = ERROR_DEVICE_NOT_CONNECTED}, + {.status = ERROR_DEVICE_NOT_CONNECTED}, + {.status = ERROR_DEVICE_NOT_CONNECTED}, + {.status = ERROR_DEVICE_NOT_CONNECTED}, +}; +static HWND dialog_hwnd; + +static void set_device_state( DWORD index, struct device_state *state ) +{ + BOOL modified; + + EnterCriticalSection( &state_cs ); + modified = memcmp( devices_state + index, state, sizeof(*state) ); + devices_state[index] = *state; + LeaveCriticalSection( &state_cs ); + + if (modified) SendMessageW( dialog_hwnd, WM_USER, index, 0 ); +} + +static void get_device_state( DWORD index, struct device_state *state ) +{ + EnterCriticalSection( &state_cs ); + *state = devices_state[index]; + LeaveCriticalSection( &state_cs ); +} + +static DWORD WINAPI input_thread_proc( void *param ) +{ + HANDLE thread_stop = param; + DWORD i; + + while (WaitForSingleObject( thread_stop, 20 ) == WAIT_TIMEOUT) + { + for (i = 0; i < ARRAY_SIZE(devices_state); ++i) + { + struct device_state state = {0}; + state.status = XInputGetCapabilities( i, 0, &state.caps ); + if (!state.status) state.status = XInputGetState( i, &state.state ); + set_device_state( i, &state ); + } + } + + return 0; +} + +static void create_user_view( HWND hwnd, DWORD index ) +{ + HWND parent; + + parent = GetDlgItem( hwnd, IDC_XI_USER_0 + index ); + + ShowWindow( parent, SW_HIDE ); +} + +static void update_user_view( HWND hwnd, DWORD index ) +{ + struct device_state state; + HWND parent; + + get_device_state( index, &state ); + + parent = GetDlgItem( hwnd, IDC_XI_NO_USER_0 + index ); + ShowWindow( parent, state.status ? SW_SHOW : SW_HIDE ); + + parent = GetDlgItem( hwnd, IDC_XI_USER_0 + index ); + ShowWindow( parent, state.status ? SW_HIDE : SW_SHOW ); +} + +extern INT_PTR CALLBACK test_xi_dialog_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + static HANDLE thread, thread_stop; + + TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); + + switch (msg) + { + case WM_INITDIALOG: + create_user_view( hwnd, 0 ); + create_user_view( hwnd, 1 ); + create_user_view( hwnd, 2 ); + create_user_view( hwnd, 3 ); + return TRUE; + + case WM_COMMAND: + return TRUE; + + case WM_NOTIFY: + switch (((NMHDR *)lparam)->code) + { + case PSN_SETACTIVE: + dialog_hwnd = hwnd; + thread_stop = CreateEventW( NULL, FALSE, FALSE, NULL ); + thread = CreateThread( NULL, 0, input_thread_proc, (void *)thread_stop, 0, NULL ); + break; + + case PSN_RESET: + case PSN_KILLACTIVE: + SetEvent( thread_stop ); + MsgWaitForMultipleObjects( 1, &thread, FALSE, INFINITE, 0 ); + CloseHandle( thread_stop ); + CloseHandle( thread ); + dialog_hwnd = 0; + break; + } + return TRUE; + + case WM_USER: + update_user_view( hwnd, wparam ); + return TRUE; + } + + return FALSE; +}
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/joy.cpl/Makefile.in | 2 +- dlls/joy.cpl/joy_private.h | 1 + dlls/joy.cpl/main.c | 19 +++ dlls/joy.cpl/xinput.c | 238 ++++++++++++++++++++++++++++++++++++- 4 files changed, 258 insertions(+), 2 deletions(-)
diff --git a/dlls/joy.cpl/Makefile.in b/dlls/joy.cpl/Makefile.in index 9a8f2d73528..229697234b0 100644 --- a/dlls/joy.cpl/Makefile.in +++ b/dlls/joy.cpl/Makefile.in @@ -1,5 +1,5 @@ MODULE = joy.cpl -IMPORTS = dxguid dinput dinput8 ole32 comctl32 user32 advapi32 xinput +IMPORTS = dxguid dinput dinput8 ole32 comctl32 user32 gdi32 advapi32 xinput
EXTRADLLFLAGS = -Wb,--prefer-native
diff --git a/dlls/joy.cpl/joy_private.h b/dlls/joy.cpl/joy_private.h index 09e1855f01d..2e9e928fbf7 100644 --- a/dlls/joy.cpl/joy_private.h +++ b/dlls/joy.cpl/joy_private.h @@ -29,5 +29,6 @@ #include "resource.h"
extern INT_PTR CALLBACK test_xi_dialog_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ); +extern LRESULT CALLBACK test_xi_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam );
#endif /* __JOY_PRIVATE_H */ diff --git a/dlls/joy.cpl/main.c b/dlls/joy.cpl/main.c index abc2f3f4d20..5f673749daa 100644 --- a/dlls/joy.cpl/main.c +++ b/dlls/joy.cpl/main.c @@ -1015,6 +1015,23 @@ static void display_cpl_sheets( HWND parent, struct JoystickData *data ) OleUninitialize(); }
+static void register_window_class(void) +{ + WNDCLASSW xi_class = + { + .hInstance = hcpl, + .lpfnWndProc = &test_xi_window_proc, + .lpszClassName = L"JoyCplXInput", + }; + + RegisterClassW( &xi_class ); +} + +static void unregister_window_class(void) +{ + UnregisterClassW( L"JoyCplXInput", hcpl ); +} + /********************************************************************* * CPlApplet (joy.cpl.@) * @@ -1041,6 +1058,7 @@ LONG CALLBACK CPlApplet(HWND hwnd, UINT command, LPARAM lParam1, LPARAM lParam2) { HRESULT hr;
+ register_window_class(); device_state_event = CreateEventW( NULL, FALSE, FALSE, NULL );
/* Initialize dinput */ @@ -1082,6 +1100,7 @@ LONG CALLBACK CPlApplet(HWND hwnd, UINT command, LPARAM lParam1, LPARAM lParam2) IDirectInput8_Release(data.di);
CloseHandle( device_state_event ); + unregister_window_class(); break; }
diff --git a/dlls/joy.cpl/xinput.c b/dlls/joy.cpl/xinput.c index f0e3c8ca437..0d9499c4ab3 100644 --- a/dlls/joy.cpl/xinput.c +++ b/dlls/joy.cpl/xinput.c @@ -19,10 +19,12 @@
#include <stdarg.h> #include <stddef.h> +#include <math.h>
#include "windef.h" #include "winbase.h" #include "winuser.h" +#include "wingdi.h"
#include "xinput.h"
@@ -96,12 +98,240 @@ static DWORD WINAPI input_thread_proc( void *param ) return 0; }
+static void draw_axis_view( HDC hdc, RECT rect, SHORT dx, SHORT dy, BOOL set ) +{ + POINT center = + { + .x = (rect.left + rect.right) / 2, + .y = (rect.top + rect.bottom) / 2, + }; + POINT pos = + { + .x = center.x + MulDiv( dx, rect.right - rect.left - 20, 0xffff ), + .y = center.y - MulDiv( dy, rect.bottom - rect.top - 20, 0xffff ), + }; + + FillRect( hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1) ); + + SetDCBrushColor( hdc, GetSysColor( set ? COLOR_HIGHLIGHT : COLOR_WINDOW ) ); + SetDCPenColor( hdc, GetSysColor( set ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWFRAME ) ); + SelectObject( hdc, GetStockObject( DC_BRUSH ) ); + SelectObject( hdc, GetStockObject( DC_PEN ) ); + + Ellipse( hdc, rect.left, rect.top, rect.right, rect.bottom ); + + MoveToEx( hdc, center.x, center.y - 3, NULL ); + LineTo( hdc, center.x, center.y + 4 ); + MoveToEx( hdc, center.x - 3, center.y, NULL ); + LineTo( hdc, center.x + 4, center.y ); + + if (!set) SetDCPenColor( hdc, GetSysColor( (dx || dy) ? COLOR_HIGHLIGHT : COLOR_WINDOWFRAME ) ); + + MoveToEx( hdc, center.x, center.y, NULL ); + LineTo( hdc, pos.x, pos.y ); + + Ellipse( hdc, pos.x - 4, pos.y - 4, pos.x + 4, pos.y + 4 ); +} + +static void draw_trigger_view( HDC hdc, RECT rect, BYTE dt ) +{ + POINT center = + { + .x = (rect.left + rect.right) / 2, + .y = (rect.top + rect.bottom) / 2, + }; + LONG w = (rect.right - rect.left + 1) / 3; + LONG y = rect.bottom - (w + 1) / 2 - MulDiv( dt, rect.bottom - rect.top - w, 0xff ); + + FillRect( hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1) ); + + SetDCBrushColor( hdc, GetSysColor( COLOR_WINDOW ) ); + SetDCPenColor( hdc, GetSysColor( COLOR_WINDOWFRAME ) ); + SelectObject( hdc, GetStockObject( DC_BRUSH ) ); + SelectObject( hdc, GetStockObject( DC_PEN ) ); + + RoundRect( hdc, rect.left, rect.top, rect.right, rect.bottom, 5, 5 ); + + if (y > center.y) + { + MoveToEx( hdc, center.x - 3, center.y, NULL ); + LineTo( hdc, center.x + 3, center.y ); + } + + SetDCBrushColor( hdc, GetSysColor( dt ? COLOR_HIGHLIGHT : COLOR_WINDOW ) ); + SetDCPenColor( hdc, GetSysColor( dt ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWFRAME ) ); + + Rectangle( hdc, center.x - w, y, center.x + w, rect.bottom ); + + if (y < center.y) + { + MoveToEx( hdc, center.x - 3, center.y, NULL ); + LineTo( hdc, center.x + 3, center.y ); + } +} + +static void draw_button_view( HDC hdc, RECT rect, BOOL set, const WCHAR *name ) +{ + COLORREF color = SetTextColor( hdc, GetSysColor( set ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT ) ); + HFONT font = SelectObject( hdc, GetStockObject( ANSI_VAR_FONT ) ); + INT mode = SetBkMode( hdc, TRANSPARENT ); + + FillRect( hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1) ); + + SetDCBrushColor( hdc, GetSysColor( set ? COLOR_HIGHLIGHT : COLOR_WINDOW ) ); + SetDCPenColor( hdc, GetSysColor( set ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWFRAME ) ); + SelectObject( hdc, GetStockObject( DC_BRUSH ) ); + SelectObject( hdc, GetStockObject( DC_PEN ) ); + + Ellipse( hdc, rect.left, rect.top, rect.right, rect.bottom ); + if (name[0] >= 'A' && name[0] <= 'Z') + DrawTextW( hdc, name, -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOCLIP ); + else if (name[0] == '=') + { + RECT tmp_rect = {.right = 10, .bottom = 2}; + + OffsetRect( &tmp_rect, rect.left, rect.top ); + OffsetRect( &tmp_rect, (rect.right - rect.left) / 2, (rect.bottom - rect.top) / 2 ); + OffsetRect( &tmp_rect, (tmp_rect.left - tmp_rect.right) / 2, (tmp_rect.top - tmp_rect.bottom) / 2 ); + + FillRect( hdc, &tmp_rect, (HBRUSH)((UINT_PTR)(set ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT) + 1) ); + OffsetRect( &tmp_rect, 0, 3 * (tmp_rect.top - tmp_rect.bottom) / 2 ); + FillRect( hdc, &tmp_rect, (HBRUSH)((UINT_PTR)(set ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT) + 1) ); + OffsetRect( &tmp_rect, 0, 6 * (tmp_rect.bottom - tmp_rect.top) / 2 ); + FillRect( hdc, &tmp_rect, (HBRUSH)((UINT_PTR)(set ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT) + 1) ); + } + else + { + LOGFONTW logfont = + { + .lfHeight = 16, + .lfWeight = FW_NORMAL, + .lfCharSet = SYMBOL_CHARSET, + .lfFaceName = L"Marlett", + }; + WCHAR buffer[4] = {0}; + HFONT font; + + font = CreateFontIndirectW( &logfont ); + font = (HFONT)SelectObject( hdc, font ); + + if (name[0] == '#') { buffer[0] = 0x32; OffsetRect( &rect, 1, 0 ); } + if (name[0] == '<') buffer[0] = 0x33; + if (name[0] == '>') buffer[0] = 0x34; + if (name[0] == '^') buffer[0] = 0x35; + if (name[0] == 'v') buffer[0] = 0x36; + if (name[0] == '@') buffer[0] = 0x6e; + DrawTextW( hdc, buffer, -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOCLIP ); + + font = (HFONT)SelectObject( hdc, font ); + DeleteObject( font ); + } + + SetBkMode( hdc, mode ); + SetTextColor( hdc, color ); + SelectObject( hdc, font ); +} + +LRESULT CALLBACK test_xi_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); + + if (msg == WM_PAINT) + { + DWORD index = GetWindowLongW( hwnd, GWLP_USERDATA ); + UINT axis_size, trigger_size, button_size, horiz_space; + struct device_state state; + RECT rect, tmp_rect; + PAINTSTRUCT paint; + HDC hdc; + + GetClientRect( hwnd, &rect ); + axis_size = rect.bottom - rect.top; + button_size = (axis_size - 1) / 3; + trigger_size = axis_size / 4; + horiz_space = (rect.right - rect.left - axis_size * 2 - trigger_size * 2 - button_size * 5) / 10; + + get_device_state( index, &state ); + + hdc = BeginPaint( hwnd, &paint ); + + rect.right = rect.left + axis_size; + OffsetRect( &rect, horiz_space, 0 ); + draw_axis_view( hdc, rect, state.state.Gamepad.sThumbLX, state.state.Gamepad.sThumbLY, + state.state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB ); + OffsetRect( &rect, rect.right - rect.left + horiz_space, 0 ); + draw_axis_view( hdc, rect, state.state.Gamepad.sThumbRX, state.state.Gamepad.sThumbRY, + state.state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB ); + + OffsetRect( &rect, rect.right - rect.left + horiz_space, 0 ); + rect.right = rect.left + trigger_size; + draw_trigger_view( hdc, rect, state.state.Gamepad.bLeftTrigger ); + OffsetRect( &rect, rect.right - rect.left + horiz_space, 0 ); + draw_trigger_view( hdc, rect, state.state.Gamepad.bRightTrigger ); + + OffsetRect( &rect, rect.right - rect.left + horiz_space, 0 ); + rect.right = rect.left + button_size; + rect.bottom = rect.top + button_size; + tmp_rect = rect; + OffsetRect( &rect, (rect.right - rect.left + horiz_space) / 2, 0 ); + draw_button_view( hdc, rect, state.state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP, L"^" ); + OffsetRect( &rect, rect.right - rect.left + horiz_space, 0 ); + draw_button_view( hdc, rect, state.state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER, L"L" ); + OffsetRect( &rect, rect.right - rect.left + horiz_space, 0 ); + draw_button_view( hdc, rect, state.state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER, L"R" ); + OffsetRect( &rect, rect.right - rect.left + horiz_space, 0 ); + draw_button_view( hdc, rect, state.state.Gamepad.wButtons & XINPUT_GAMEPAD_Y, L"Y" ); + + rect = tmp_rect; + OffsetRect( &rect, 0, rect.bottom - rect.top ); + tmp_rect = rect; + draw_button_view( hdc, rect, state.state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT, L"<" ); + OffsetRect( &rect, rect.right - rect.left + horiz_space, 0 ); + draw_button_view( hdc, rect, state.state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT, L">" ); + OffsetRect( &rect, rect.right - rect.left + horiz_space, 0 ); + draw_button_view( hdc, rect, state.state.Gamepad.wButtons & 0x0400, L"@" ); + OffsetRect( &rect, rect.right - rect.left + horiz_space, 0 ); + draw_button_view( hdc, rect, state.state.Gamepad.wButtons & XINPUT_GAMEPAD_X, L"X" ); + OffsetRect( &rect, rect.right - rect.left + horiz_space, 0 ); + draw_button_view( hdc, rect, state.state.Gamepad.wButtons & XINPUT_GAMEPAD_B, L"B" ); + + rect = tmp_rect; + OffsetRect( &rect, (rect.right - rect.left + horiz_space) / 2, rect.bottom - rect.top ); + draw_button_view( hdc, rect, state.state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN, L"v" ); + OffsetRect( &rect, rect.right - rect.left + horiz_space, 0 ); + draw_button_view( hdc, rect, state.state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK, L"#" ); + OffsetRect( &rect, rect.right - rect.left + horiz_space, 0 ); + draw_button_view( hdc, rect, state.state.Gamepad.wButtons & XINPUT_GAMEPAD_START, L"=" ); + OffsetRect( &rect, rect.right - rect.left + horiz_space, 0 ); + draw_button_view( hdc, rect, state.state.Gamepad.wButtons & XINPUT_GAMEPAD_A, L"A" ); + + EndPaint( hwnd, &paint ); + + return 0; + } + + return DefWindowProcW( hwnd, msg, wparam, lparam ); +} + static void create_user_view( HWND hwnd, DWORD index ) { - HWND parent; + HINSTANCE instance = (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ); + HWND parent, view; + LONG margin; + RECT rect;
parent = GetDlgItem( hwnd, IDC_XI_USER_0 + index );
+ GetClientRect( parent, &rect ); + rect.top += 10; + + margin = (rect.bottom - rect.top) * 15 / 100; + InflateRect( &rect, -margin, -margin ); + + view = CreateWindowW( L"JoyCplXInput", NULL, WS_CHILD | WS_VISIBLE, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, parent, NULL, NULL, instance ); + SetWindowLongW( view, GWLP_USERDATA, index ); + ShowWindow( parent, SW_HIDE ); }
@@ -117,6 +347,12 @@ static void update_user_view( HWND hwnd, DWORD index )
parent = GetDlgItem( hwnd, IDC_XI_USER_0 + index ); ShowWindow( parent, state.status ? SW_HIDE : SW_SHOW ); + + if (!state.status) + { + HWND view = FindWindowExW( parent, NULL, L"JoyCplXInput", NULL ); + InvalidateRect( view, NULL, TRUE ); + } }
extern INT_PTR CALLBACK test_xi_dialog_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/joy.cpl/joy.rc | 5 +++++ dlls/joy.cpl/resource.h | 4 ++++ dlls/joy.cpl/xinput.c | 43 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+)
diff --git a/dlls/joy.cpl/joy.rc b/dlls/joy.cpl/joy.rc index dca694e5347..d19651e340c 100644 --- a/dlls/joy.cpl/joy.rc +++ b/dlls/joy.cpl/joy.rc @@ -92,6 +92,11 @@ FONT 8, "Ms Shell Dlg" "gamepad is plugged in, and not overriden for " \ "DInput in the Joysticks tab.", IDC_XI_NO_USER_3, 15, 220, 291, 60 + + AUTOCHECKBOX "Rumble", IDC_XI_RUMBLE_0, 260, 10, 40, 10 + AUTOCHECKBOX "Rumble", IDC_XI_RUMBLE_1, 260, 80, 40, 10 + AUTOCHECKBOX "Rumble", IDC_XI_RUMBLE_2, 260, 150, 40, 10 + AUTOCHECKBOX "Rumble", IDC_XI_RUMBLE_3, 260, 220, 40, 10 }
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL diff --git a/dlls/joy.cpl/resource.h b/dlls/joy.cpl/resource.h index 714fcca664d..17acd178984 100644 --- a/dlls/joy.cpl/resource.h +++ b/dlls/joy.cpl/resource.h @@ -61,6 +61,10 @@ #define IDC_XI_NO_USER_1 2211 #define IDC_XI_NO_USER_2 2212 #define IDC_XI_NO_USER_3 2213 +#define IDC_XI_RUMBLE_0 2220 +#define IDC_XI_RUMBLE_1 2221 +#define IDC_XI_RUMBLE_2 2222 +#define IDC_XI_RUMBLE_3 2223
#define ICO_MAIN 100
diff --git a/dlls/joy.cpl/xinput.c b/dlls/joy.cpl/xinput.c index 0d9499c4ab3..757b99fa333 100644 --- a/dlls/joy.cpl/xinput.c +++ b/dlls/joy.cpl/xinput.c @@ -19,6 +19,7 @@
#include <stdarg.h> #include <stddef.h> +#include <stdlib.h> #include <math.h>
#include "windef.h" @@ -40,6 +41,7 @@ struct device_state XINPUT_CAPABILITIES caps; XINPUT_STATE state; DWORD status; + BOOL rumble; };
static CRITICAL_SECTION state_cs; @@ -65,6 +67,7 @@ static void set_device_state( DWORD index, struct device_state *state ) BOOL modified;
EnterCriticalSection( &state_cs ); + state->rumble = devices_state[index].rumble; modified = memcmp( devices_state + index, state, sizeof(*state) ); devices_state[index] = *state; LeaveCriticalSection( &state_cs ); @@ -88,10 +91,22 @@ static DWORD WINAPI input_thread_proc( void *param ) { for (i = 0; i < ARRAY_SIZE(devices_state); ++i) { + XINPUT_VIBRATION vibration = {0}; struct device_state state = {0}; + state.status = XInputGetCapabilities( i, 0, &state.caps ); if (!state.status) state.status = XInputGetState( i, &state.state ); set_device_state( i, &state ); + + if (state.rumble) + { + vibration.wLeftMotorSpeed = 2 * max( abs( state.state.Gamepad.sThumbLX ), + abs( state.state.Gamepad.sThumbLY ) ) - 1; + vibration.wRightMotorSpeed = 2 * max( abs( state.state.Gamepad.sThumbRX ), + abs( state.state.Gamepad.sThumbRY ) ) - 1; + } + + XInputSetState( i, &vibration ); } }
@@ -333,6 +348,9 @@ static void create_user_view( HWND hwnd, DWORD index ) SetWindowLongW( view, GWLP_USERDATA, index );
ShowWindow( parent, SW_HIDE ); + + parent = GetDlgItem( hwnd, IDC_XI_RUMBLE_0 + index ); + ShowWindow( parent, SW_HIDE ); }
static void update_user_view( HWND hwnd, DWORD index ) @@ -345,6 +363,9 @@ static void update_user_view( HWND hwnd, DWORD index ) parent = GetDlgItem( hwnd, IDC_XI_NO_USER_0 + index ); ShowWindow( parent, state.status ? SW_SHOW : SW_HIDE );
+ parent = GetDlgItem( hwnd, IDC_XI_RUMBLE_0 + index ); + ShowWindow( parent, state.status ? SW_HIDE : SW_SHOW ); + parent = GetDlgItem( hwnd, IDC_XI_USER_0 + index ); ShowWindow( parent, state.status ? SW_HIDE : SW_SHOW );
@@ -355,6 +376,19 @@ static void update_user_view( HWND hwnd, DWORD index ) } }
+static void update_rumble_state( HWND hwnd, DWORD index ) +{ + HWND parent; + LRESULT res; + + parent = GetDlgItem( hwnd, IDC_XI_RUMBLE_0 + index ); + res = SendMessageW( parent, BM_GETCHECK, 0, 0 ); + + EnterCriticalSection( &state_cs ); + devices_state[index].rumble = res == BST_CHECKED; + LeaveCriticalSection( &state_cs ); +} + extern INT_PTR CALLBACK test_xi_dialog_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { static HANDLE thread, thread_stop; @@ -371,6 +405,15 @@ extern INT_PTR CALLBACK test_xi_dialog_proc( HWND hwnd, UINT msg, WPARAM wparam, return TRUE;
case WM_COMMAND: + switch (LOWORD(wparam)) + { + case IDC_XI_RUMBLE_0: + case IDC_XI_RUMBLE_1: + case IDC_XI_RUMBLE_2: + case IDC_XI_RUMBLE_3: + update_rumble_state( hwnd, LOWORD(wparam) - IDC_XI_RUMBLE_0 ); + break; + } return TRUE;
case WM_NOTIFY:
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/joy.cpl/Makefile.in | 1 + dlls/joy.cpl/dinput.c | 145 +++++++++++++++++++++++++++++++++++++ dlls/joy.cpl/joy.rc | 2 +- dlls/joy.cpl/joy_private.h | 5 ++ dlls/joy.cpl/main.c | 108 ++++++++++++--------------- dlls/joy.cpl/resource.h | 3 +- 6 files changed, 202 insertions(+), 62 deletions(-) create mode 100644 dlls/joy.cpl/dinput.c
diff --git a/dlls/joy.cpl/Makefile.in b/dlls/joy.cpl/Makefile.in index 229697234b0..561b407a4ab 100644 --- a/dlls/joy.cpl/Makefile.in +++ b/dlls/joy.cpl/Makefile.in @@ -4,6 +4,7 @@ IMPORTS = dxguid dinput dinput8 ole32 comctl32 user32 gdi32 advapi32 xinput EXTRADLLFLAGS = -Wb,--prefer-native
C_SRCS = \ + dinput.c \ main.c \ xinput.c
diff --git a/dlls/joy.cpl/dinput.c b/dlls/joy.cpl/dinput.c new file mode 100644 index 00000000000..ecec58e0fff --- /dev/null +++ b/dlls/joy.cpl/dinput.c @@ -0,0 +1,145 @@ +/* + * Copyright 2022 Rémi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include <stdarg.h> +#include <stddef.h> + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "wingdi.h" + +#include "dinput.h" + +#include "wine/debug.h" +#include "wine/list.h" + +#include "joy_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(joycpl); + +static CRITICAL_SECTION state_cs; +static CRITICAL_SECTION_DEBUG state_cs_debug = +{ + 0, 0, &state_cs, + { &state_cs_debug.ProcessLocksList, &state_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": state_cs") } +}; +static CRITICAL_SECTION state_cs = { &state_cs_debug, -1, 0, 0, 0, 0 }; + +static DIJOYSTATE last_state; +static DIDEVCAPS last_caps; + +void set_di_device_state( HWND hwnd, DIJOYSTATE *state, DIDEVCAPS *caps ) +{ + BOOL modified; + + EnterCriticalSection( &state_cs ); + modified = memcmp( &last_state, state, sizeof(*state) ) || + memcmp( &last_caps, caps, sizeof(*caps) ); + last_state = *state; + last_caps = *caps; + LeaveCriticalSection( &state_cs ); + + if (modified) SendMessageW( hwnd, WM_USER, 0, 0 ); +} + +static void get_device_state( DIJOYSTATE *state, DIDEVCAPS *caps ) +{ + EnterCriticalSection( &state_cs ); + *state = last_state; + *caps = last_caps; + LeaveCriticalSection( &state_cs ); +} + +static inline void draw_button_view( HDC hdc, RECT rect, BOOL set, const WCHAR *name ) +{ + COLORREF color; + HFONT font; + INT mode; + + FillRect( hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1) ); + + SetDCBrushColor( hdc, GetSysColor( set ? COLOR_HIGHLIGHT : COLOR_WINDOW ) ); + SetDCPenColor( hdc, GetSysColor( set ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWFRAME ) ); + SelectObject( hdc, GetStockObject( DC_BRUSH ) ); + SelectObject( hdc, GetStockObject( DC_PEN ) ); + + Ellipse( hdc, rect.left, rect.top, rect.right, rect.bottom ); + + color = SetTextColor( hdc, GetSysColor( set ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT ) ); + font = SelectObject( hdc, GetStockObject( ANSI_VAR_FONT ) ); + mode = SetBkMode( hdc, TRANSPARENT ); + DrawTextW( hdc, name, -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOCLIP ); + SetBkMode( hdc, mode ); + SetTextColor( hdc, color ); + SelectObject( hdc, font ); +} + +LRESULT CALLBACK test_di_buttons_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); + + if (msg == WM_PAINT) + { + UINT i, j, offs, size, step, space = 2; + PAINTSTRUCT paint; + DIJOYSTATE state; + DIDEVCAPS caps; + RECT rect; + HDC hdc; + + get_device_state( &state, &caps ); + + if (caps.dwButtons <= 48) step = 16; + else step = 32; + + hdc = BeginPaint( hwnd, &paint ); + + GetClientRect( hwnd, &rect ); + + size = (rect.right - rect.left - space) / step; + offs = (rect.right - rect.left - step * size - space) / 2; + OffsetRect( &rect, offs, offs ); + rect.right = rect.left + size - space; + rect.bottom = rect.top + size - space; + + for (i = 0; i < ARRAY_SIZE(state.rgbButtons) && i < caps.dwButtons;) + { + RECT first = rect; + + for (j = 0; j < step && i < caps.dwButtons; j++, i++) + { + WCHAR buffer[3]; + swprintf( buffer, ARRAY_SIZE(buffer), L"%d", i ); + draw_button_view( hdc, rect, state.rgbButtons[i], buffer ); + OffsetRect( &rect, size, 0 ); + } + + rect = first; + OffsetRect( &rect, 0, size ); + } + + EndPaint( hwnd, &paint ); + + return 0; + } + + return DefWindowProcW( hwnd, msg, wparam, lparam ); +} diff --git a/dlls/joy.cpl/joy.rc b/dlls/joy.cpl/joy.rc index d19651e340c..85900cf9576 100644 --- a/dlls/joy.cpl/joy.rc +++ b/dlls/joy.cpl/joy.rc @@ -55,11 +55,11 @@ CAPTION "DInput" FONT 8, "Ms Shell Dlg" { COMBOBOX IDC_DI_DEVICES, 15, 10, 291, 60, CBS_DROPDOWNLIST | CBS_HASSTRINGS - GROUPBOX "Buttons", IDC_STATIC, 15, 100, 291, 70 GROUPBOX "", IDC_DI_AXIS_X_Y, 15, 30, 60, 60 GROUPBOX "", IDC_DI_AXIS_RX_RY, 92, 30, 60, 60 GROUPBOX "", IDC_DI_AXIS_Z_RZ, 169, 30, 60, 60 GROUPBOX "", IDC_DI_AXIS_POV_0, 246, 30, 60, 60 + GROUPBOX "Buttons", IDC_DI_BUTTONS, 15, 100, 291, 70 LTEXT "Force Feedback Effect", IDC_STATIC, 15, 180, 291, 10 LISTBOX IDC_DI_EFFECTS, 15, 190, 291, 70, WS_TABSTOP | WS_VSCROLL | LBS_NOTIFY LTEXT "Press any button in the controller to activate the chosen effect. The effect direction can be changed with the controller axis.", diff --git a/dlls/joy.cpl/joy_private.h b/dlls/joy.cpl/joy_private.h index 2e9e928fbf7..9c8a46c1c05 100644 --- a/dlls/joy.cpl/joy_private.h +++ b/dlls/joy.cpl/joy_private.h @@ -26,8 +26,13 @@ #include "windef.h" #include "winbase.h"
+#include "dinput.h" + #include "resource.h"
+extern void set_di_device_state( HWND hwnd, DIJOYSTATE *state, DIDEVCAPS *caps ); +extern LRESULT CALLBACK test_di_buttons_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ); + extern INT_PTR CALLBACK test_xi_dialog_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ); extern LRESULT CALLBACK test_xi_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam );
diff --git a/dlls/joy.cpl/main.c b/dlls/joy.cpl/main.c index 5f673749daa..ac9544167b6 100644 --- a/dlls/joy.cpl/main.c +++ b/dlls/joy.cpl/main.c @@ -42,14 +42,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(joycpl); #define TEST_MAX_AXES 4 #define TEST_POLL_TIME 100
-#define TEST_BUTTON_COL_MAX 16 -#define TEST_BUTTON_X 24 -#define TEST_BUTTON_Y 112 -#define TEST_NEXT_BUTTON_X 17 -#define TEST_NEXT_BUTTON_Y 15 -#define TEST_BUTTON_SIZE_X 16 -#define TEST_BUTTON_SIZE_Y 14 - #define TEST_AXIS_X 43 #define TEST_AXIS_Y 60 #define TEST_NEXT_AXIS_X 77 @@ -76,7 +68,6 @@ struct device struct Graphics { HWND hwnd; - HWND buttons[TEST_MAX_BUTTONS]; HWND axes[TEST_MAX_AXES]; };
@@ -84,6 +75,7 @@ struct JoystickData { IDirectInput8W *di; struct Graphics graphics; + HWND di_dialog_hwnd; BOOL stop; };
@@ -630,14 +622,14 @@ static DWORD WINAPI input_thread(void *param)
if ((device = get_selected_device())) { + DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)}; + IDirectInputDevice8_GetDeviceState( device, sizeof(state), &state ); + IDirectInputDevice8_GetCapabilities( device, &caps ); IDirectInputDevice8_Release( device );
dump_joy_state(&state); - - /* Indicate pressed buttons */ - for (i = 0; i < TEST_MAX_BUTTONS; i++) - SendMessageW(data->graphics.buttons[i], BM_SETSTATE, !!state.rgbButtons[i], 0); + set_di_device_state( data->di_dialog_hwnd, &state, &caps );
/* Indicate axis positions, axes showing are hardcoded for now */ axes_pos[0][0] = state.lX; @@ -731,6 +723,7 @@ static void test_handle_joychange(HWND hwnd, struct JoystickData *data) { DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)}; IDirectInputDevice8W *device; + DIJOYSTATE state = {0}; struct list *entry; int i;
@@ -746,9 +739,9 @@ static void test_handle_joychange(HWND hwnd, struct JoystickData *data) device = LIST_ENTRY( entry, struct device, entry )->device; if (FAILED(IDirectInputDevice8_GetCapabilities( device, &caps ))) return;
+ set_di_device_state( data->di_dialog_hwnd, &state, &caps ); set_selected_device( device ); initialize_effects_list( hwnd, device ); - for (i = 0; i < TEST_MAX_BUTTONS; i++) ShowWindow( data->graphics.buttons[i], i < caps.dwButtons ); }
static void ff_handle_effectchange( HWND hwnd ) @@ -777,51 +770,6 @@ static void ff_handle_effectchange( HWND hwnd ) } }
-/********************************************************************* - * button_number_to_wchar [internal] - * Transforms an integer in the interval [0,99] into a 2 character WCHAR string - */ -static void button_number_to_wchar(int n, WCHAR str[3]) -{ - str[1] = n % 10 + '0'; - n /= 10; - str[0] = n % 10 + '0'; - str[2] = '\0'; -} - -static void draw_joystick_buttons(HWND hwnd, struct JoystickData* data) -{ - int i; - int row = 0, col = 0; - WCHAR button_label[3]; - HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE); - - for (i = 0; i < TEST_MAX_BUTTONS; i++) - { - RECT r; - - if ((i % TEST_BUTTON_COL_MAX) == 0 && i != 0) - { - row += 1; - col = 0; - } - - r.left = (TEST_BUTTON_X + TEST_NEXT_BUTTON_X*col); - r.top = (TEST_BUTTON_Y + TEST_NEXT_BUTTON_Y*row); - r.right = r.left + TEST_BUTTON_SIZE_X; - r.bottom = r.top + TEST_BUTTON_SIZE_Y; - MapDialogRect(hwnd, &r); - - button_number_to_wchar(i + 1, button_label); - - data->graphics.buttons[i] = CreateWindowW(L"Button", button_label, WS_CHILD, - r.left, r.top, r.right - r.left, r.bottom - r.top, - hwnd, NULL, NULL, hinst); - - col += 1; - } -} - static void draw_joystick_axes(HWND hwnd, struct JoystickData* data) { static const WCHAR *axes_names[TEST_MAX_AXES] = {L"X,Y", L"Rx,Ry", L"Z,Rz", L"POV"}; @@ -865,6 +813,33 @@ static void di_update_select_combo( HWND hwnd ) } }
+static void update_device_views( HWND hwnd ) +{ + HWND parent, view; + + parent = GetDlgItem( hwnd, IDC_DI_BUTTONS ); + view = FindWindowExW( parent, NULL, L"JoyCplDInputButtons", NULL ); + InvalidateRect( view, NULL, TRUE ); +} + +static void create_device_views( HWND hwnd ) +{ + HINSTANCE instance = (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ); + HWND parent; + LONG margin; + RECT rect; + + parent = GetDlgItem( hwnd, IDC_DI_BUTTONS ); + GetClientRect( parent, &rect ); + rect.top += 10; + + margin = (rect.bottom - rect.top) * 10 / 100; + InflateRect( &rect, -margin, -margin ); + + CreateWindowW( L"JoyCplDInputButtons", NULL, WS_CHILD | WS_VISIBLE, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, parent, NULL, NULL, instance ); +} + static INT_PTR CALLBACK test_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { static HANDLE thread; @@ -878,8 +853,8 @@ static INT_PTR CALLBACK test_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
di_update_select_combo( hwnd ); - draw_joystick_buttons(hwnd, data); draw_joystick_axes(hwnd, data); + create_device_views( hwnd );
return TRUE; } @@ -910,6 +885,7 @@ static INT_PTR CALLBACK test_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM di_update_select_combo( hwnd );
data->stop = FALSE; + data->di_dialog_hwnd = hwnd;
/* Set the first joystick as default */ SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_SETCURSEL, 0, 0 ); @@ -931,6 +907,10 @@ static INT_PTR CALLBACK test_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM break; } return TRUE; + + case WM_USER: + update_device_views( hwnd ); + return TRUE; } return FALSE; } @@ -1023,12 +1003,20 @@ static void register_window_class(void) .lpfnWndProc = &test_xi_window_proc, .lpszClassName = L"JoyCplXInput", }; + WNDCLASSW di_buttons_class = + { + .hInstance = hcpl, + .lpfnWndProc = &test_di_buttons_window_proc, + .lpszClassName = L"JoyCplDInputButtons", + };
RegisterClassW( &xi_class ); + RegisterClassW( &di_buttons_class ); }
static void unregister_window_class(void) { + UnregisterClassW( L"JoyCplDInputButtons", hcpl ); UnregisterClassW( L"JoyCplXInput", hcpl ); }
diff --git a/dlls/joy.cpl/resource.h b/dlls/joy.cpl/resource.h index 17acd178984..35047d87642 100644 --- a/dlls/joy.cpl/resource.h +++ b/dlls/joy.cpl/resource.h @@ -51,7 +51,8 @@ #define IDC_DI_AXIS_RX_RY 2102 #define IDC_DI_AXIS_Z_RZ 2103 #define IDC_DI_AXIS_POV_0 2104 -#define IDC_DI_EFFECTS 2105 +#define IDC_DI_BUTTONS 2105 +#define IDC_DI_EFFECTS 2106
#define IDC_XI_USER_0 2200 #define IDC_XI_USER_1 2201
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/joy.cpl/dinput.c | 196 +++++++++++++++++++++++++++++++++++++ dlls/joy.cpl/joy.rc | 6 +- dlls/joy.cpl/joy_private.h | 2 + dlls/joy.cpl/main.c | 137 +++++++++----------------- dlls/joy.cpl/resource.h | 10 +- 5 files changed, 249 insertions(+), 102 deletions(-)
diff --git a/dlls/joy.cpl/dinput.c b/dlls/joy.cpl/dinput.c index ecec58e0fff..bf80584000e 100644 --- a/dlls/joy.cpl/dinput.c +++ b/dlls/joy.cpl/dinput.c @@ -19,6 +19,7 @@
#include <stdarg.h> #include <stddef.h> +#include <math.h>
#include "windef.h" #include "winbase.h" @@ -68,6 +69,104 @@ static void get_device_state( DIJOYSTATE *state, DIDEVCAPS *caps ) LeaveCriticalSection( &state_cs ); }
+static void draw_axis_view( HDC hdc, RECT rect, const WCHAR *name, LONG value ) +{ + POINT center = + { + .x = (rect.left + rect.right) / 2 + 10, + .y = (rect.top + rect.bottom) / 2, + }; + LONG w = (rect.bottom - rect.top + 1) / 3; + LONG x = rect.left + 20 + (w + 1) / 2 + MulDiv( value, rect.right - rect.left - 20 - w, 0xffff ); + COLORREF color; + HFONT font; + + FillRect( hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1) ); + + color = SetTextColor( hdc, GetSysColor( COLOR_WINDOWTEXT ) ); + font = SelectObject( hdc, GetStockObject( ANSI_VAR_FONT ) ); + DrawTextW( hdc, name, -1, &rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOCLIP ); + SetTextColor( hdc, color ); + SelectObject( hdc, font ); + + SetDCBrushColor( hdc, GetSysColor( COLOR_WINDOW ) ); + SetDCPenColor( hdc, GetSysColor( COLOR_WINDOWFRAME ) ); + SelectObject( hdc, GetStockObject( DC_BRUSH ) ); + SelectObject( hdc, GetStockObject( DC_PEN ) ); + + RoundRect( hdc, rect.left + 20, rect.top, rect.right, rect.bottom, 5, 5 ); + + if (x < center.x) + { + MoveToEx( hdc, center.x, center.y - 3, NULL ); + LineTo( hdc, center.x, center.y + 3 ); + } + + SetDCBrushColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) ); + SetDCPenColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) ); + + Rectangle( hdc, rect.left + 20, rect.top + w, x, rect.bottom - w ); + + if (x > center.x) + { + MoveToEx( hdc, center.x, center.y - 3, NULL ); + LineTo( hdc, center.x, center.y + 3 ); + } +} + +static void draw_pov_view( HDC hdc, RECT rect, DWORD value ) +{ + POINT points[] = + { + /* 0° */ + {.x = round( rect.left * 0.71 + rect.right * 0.29 ), .y = round( rect.top * 1.00 + rect.bottom * 0.00 )}, + {.x = round( rect.left * 0.50 + rect.right * 0.50 ), .y = round( rect.top * 0.50 + rect.bottom * 0.50 )}, + /* 45° */ + {.x = round( rect.left * 0.29 + rect.right * 0.71 ), .y = round( rect.top * 1.00 + rect.bottom * 0.00 )}, + {.x = round( rect.left * 0.50 + rect.right * 0.50 ), .y = round( rect.top * 0.50 + rect.bottom * 0.50 )}, + /* 90° */ + {.x = round( rect.left * 0.00 + rect.right * 1.00 ), .y = round( rect.top * 0.71 + rect.bottom * 0.29 )}, + {.x = round( rect.left * 0.50 + rect.right * 0.50 ), .y = round( rect.top * 0.50 + rect.bottom * 0.50 )}, + /* 135° */ + {.x = round( rect.left * 0.00 + rect.right * 1.00 ), .y = round( rect.top * 0.29 + rect.bottom * 0.71 )}, + {.x = round( rect.left * 0.50 + rect.right * 0.50 ), .y = round( rect.top * 0.50 + rect.bottom * 0.50 )}, + /* 180° */ + {.x = round( rect.left * 0.29 + rect.right * 0.71 ), .y = round( rect.top * 0.00 + rect.bottom * 1.00 )}, + {.x = round( rect.left * 0.50 + rect.right * 0.50 ), .y = round( rect.top * 0.50 + rect.bottom * 0.50 )}, + /* 225° */ + {.x = round( rect.left * 0.71 + rect.right * 0.29 ), .y = round( rect.top * 0.00 + rect.bottom * 1.00 )}, + {.x = round( rect.left * 0.50 + rect.right * 0.50 ), .y = round( rect.top * 0.50 + rect.bottom * 0.50 )}, + /* 270° */ + {.x = round( rect.left * 1.00 + rect.right * 0.00 ), .y = round( rect.top * 0.29 + rect.bottom * 0.71 )}, + {.x = round( rect.left * 0.50 + rect.right * 0.50 ), .y = round( rect.top * 0.50 + rect.bottom * 0.50 )}, + /* 315° */ + {.x = round( rect.left * 1.00 + rect.right * 0.00 ), .y = round( rect.top * 0.71 + rect.bottom * 0.29 )}, + {.x = round( rect.left * 0.50 + rect.right * 0.50 ), .y = round( rect.top * 0.50 + rect.bottom * 0.50 )}, + /* 360° */ + {.x = round( rect.left * 0.71 + rect.right * 0.29 ), .y = round( rect.top * 1.00 + rect.bottom * 0.00 )}, + {.x = round( rect.left * 0.50 + rect.right * 0.50 ), .y = round( rect.top * 0.50 + rect.bottom * 0.50 )}, + {.x = round( rect.left * 0.71 + rect.right * 0.29 ), .y = round( rect.top * 1.00 + rect.bottom * 0.00 )}, + }; + DWORD i; + + FillRect( hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1) ); + + SetDCBrushColor( hdc, GetSysColor( COLOR_WINDOW ) ); + SetDCPenColor( hdc, GetSysColor( COLOR_WINDOWFRAME ) ); + SelectObject( hdc, GetStockObject( DC_BRUSH ) ); + SelectObject( hdc, GetStockObject( DC_PEN ) ); + + for (i = 0; i < ARRAY_SIZE(points) - 1; i += 2) + { + MoveToEx( hdc, (points[i].x + points[i + 1].x) / 2, (points[i].y + points[i + 1].y) / 2, NULL ); + LineTo( hdc, points[i].x, points[i].y ); + } + + SetDCPenColor( hdc, GetSysColor( (value != -1) ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWFRAME ) ); + SetDCBrushColor( hdc, GetSysColor( (value != -1) ? COLOR_HIGHLIGHT : COLOR_WINDOW ) ); + if (value != -1) Polygon( hdc, points + value / 4500 * 2, 3 ); +} + static inline void draw_button_view( HDC hdc, RECT rect, BOOL set, const WCHAR *name ) { COLORREF color; @@ -92,6 +191,103 @@ static inline void draw_button_view( HDC hdc, RECT rect, BOOL set, const WCHAR * SelectObject( hdc, font ); }
+LRESULT CALLBACK test_di_axes_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); + + if (msg == WM_PAINT) + { + RECT rect, tmp_rect; + PAINTSTRUCT paint; + DIJOYSTATE state; + DIDEVCAPS caps; + HDC hdc; + + get_device_state( &state, &caps ); + + hdc = BeginPaint( hwnd, &paint ); + + GetClientRect( hwnd, &rect ); + rect.bottom = rect.top + (rect.bottom - rect.top - 2) / 4 - 2; + rect.right = rect.left + (rect.right - rect.left) / 2 - 10; + + OffsetRect( &rect, 5, 2 ); + draw_axis_view( hdc, rect, L"X", state.lX ); + + tmp_rect = rect; + OffsetRect( &rect, rect.right - rect.left + 10, 0 ); + draw_axis_view( hdc, rect, L"Rx", state.lRx ); + rect = tmp_rect; + + OffsetRect( &rect, 0, rect.bottom - rect.top + 2 ); + draw_axis_view( hdc, rect, L"Y", state.lY ); + + tmp_rect = rect; + OffsetRect( &rect, rect.right - rect.left + 10, 0 ); + draw_axis_view( hdc, rect, L"Ry", state.lRy ); + rect = tmp_rect; + + OffsetRect( &rect, 0, rect.bottom - rect.top + 2 ); + draw_axis_view( hdc, rect, L"Z", state.lZ ); + + tmp_rect = rect; + OffsetRect( &rect, rect.right - rect.left + 10, 0 ); + draw_axis_view( hdc, rect, L"Rz", state.lRz ); + rect = tmp_rect; + + OffsetRect( &rect, 0, rect.bottom - rect.top + 2 ); + draw_axis_view( hdc, rect, L"S", state.rglSlider[0] ); + + tmp_rect = rect; + OffsetRect( &rect, rect.right - rect.left + 10, 0 ); + draw_axis_view( hdc, rect, L"Rs", state.rglSlider[1] ); + rect = tmp_rect; + + EndPaint( hwnd, &paint ); + + return 0; + } + + return DefWindowProcW( hwnd, msg, wparam, lparam ); +} + +LRESULT CALLBACK test_di_povs_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); + + if (msg == WM_PAINT) + { + PAINTSTRUCT paint; + DIJOYSTATE state; + DIDEVCAPS caps; + RECT rect; + HDC hdc; + + get_device_state( &state, &caps ); + + hdc = BeginPaint( hwnd, &paint ); + + GetClientRect( hwnd, &rect ); + rect.bottom = rect.top + (rect.bottom - rect.top - 5) / 2 - 5; + rect.right = rect.left + (rect.bottom - rect.top); + + OffsetRect( &rect, 5, 5 ); + draw_pov_view( hdc, rect, state.rgdwPOV[0] ); + OffsetRect( &rect, rect.right - rect.left + 5, 0 ); + draw_pov_view( hdc, rect, state.rgdwPOV[1] ); + OffsetRect( &rect, rect.left - rect.right - 5, rect.bottom - rect.top + 5 ); + draw_pov_view( hdc, rect, state.rgdwPOV[1] ); + OffsetRect( &rect, rect.right - rect.left + 5, 0 ); + draw_pov_view( hdc, rect, state.rgdwPOV[2] ); + + EndPaint( hwnd, &paint ); + + return 0; + } + + return DefWindowProcW( hwnd, msg, wparam, lparam ); +} + LRESULT CALLBACK test_di_buttons_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) { TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); diff --git a/dlls/joy.cpl/joy.rc b/dlls/joy.cpl/joy.rc index 85900cf9576..d74afc38f58 100644 --- a/dlls/joy.cpl/joy.rc +++ b/dlls/joy.cpl/joy.rc @@ -55,10 +55,8 @@ CAPTION "DInput" FONT 8, "Ms Shell Dlg" { COMBOBOX IDC_DI_DEVICES, 15, 10, 291, 60, CBS_DROPDOWNLIST | CBS_HASSTRINGS - GROUPBOX "", IDC_DI_AXIS_X_Y, 15, 30, 60, 60 - GROUPBOX "", IDC_DI_AXIS_RX_RY, 92, 30, 60, 60 - GROUPBOX "", IDC_DI_AXIS_Z_RZ, 169, 30, 60, 60 - GROUPBOX "", IDC_DI_AXIS_POV_0, 246, 30, 60, 60 + GROUPBOX "Axes", IDC_DI_AXES, 15, 30, 214, 60 + GROUPBOX "POVs", IDC_DI_POVS, 246, 30, 60, 60 GROUPBOX "Buttons", IDC_DI_BUTTONS, 15, 100, 291, 70 LTEXT "Force Feedback Effect", IDC_STATIC, 15, 180, 291, 10 LISTBOX IDC_DI_EFFECTS, 15, 190, 291, 70, WS_TABSTOP | WS_VSCROLL | LBS_NOTIFY diff --git a/dlls/joy.cpl/joy_private.h b/dlls/joy.cpl/joy_private.h index 9c8a46c1c05..0dc9a02be9f 100644 --- a/dlls/joy.cpl/joy_private.h +++ b/dlls/joy.cpl/joy_private.h @@ -31,6 +31,8 @@ #include "resource.h"
extern void set_di_device_state( HWND hwnd, DIJOYSTATE *state, DIDEVCAPS *caps ); +extern LRESULT CALLBACK test_di_axes_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ); +extern LRESULT CALLBACK test_di_povs_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ); extern LRESULT CALLBACK test_di_buttons_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam );
extern INT_PTR CALLBACK test_xi_dialog_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ); diff --git a/dlls/joy.cpl/main.c b/dlls/joy.cpl/main.c index ac9544167b6..fb41a8a2860 100644 --- a/dlls/joy.cpl/main.c +++ b/dlls/joy.cpl/main.c @@ -39,17 +39,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(joycpl);
#define TEST_MAX_BUTTONS 32 -#define TEST_MAX_AXES 4 #define TEST_POLL_TIME 100
-#define TEST_AXIS_X 43 -#define TEST_AXIS_Y 60 -#define TEST_NEXT_AXIS_X 77 -#define TEST_AXIS_SIZE_X 3 -#define TEST_AXIS_SIZE_Y 3 -#define TEST_AXIS_MIN -25 -#define TEST_AXIS_MAX 25 - #define FF_PLAY_TIME 2*DI_SECONDS #define FF_PERIOD_TIME FF_PLAY_TIME/4
@@ -68,7 +59,6 @@ struct device struct Graphics { HWND hwnd; - HWND axes[TEST_MAX_AXES]; };
struct JoystickData @@ -245,25 +235,14 @@ static BOOL CALLBACK enum_devices( const DIDEVICEINSTANCEW *instance, void *cont { DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)}; struct JoystickData *data = context; - DIPROPRANGE proprange; struct device *entry;
if (!(entry = calloc( 1, sizeof(*entry) ))) return DIENUM_STOP;
IDirectInput8_CreateDevice( data->di, &instance->guidInstance, &entry->device, NULL ); IDirectInputDevice8_SetDataFormat( entry->device, &c_dfDIJoystick ); - IDirectInputDevice8_GetCapabilities( entry->device, &caps );
- /* Set axis range to ease the GUI visualization */ - proprange.diph.dwSize = sizeof(DIPROPRANGE); - proprange.diph.dwHeaderSize = sizeof(DIPROPHEADER); - proprange.diph.dwHow = DIPH_DEVICE; - proprange.diph.dwObj = 0; - proprange.lMin = TEST_AXIS_MIN; - proprange.lMax = TEST_AXIS_MAX; - - IDirectInputDevice_SetProperty( entry->device, DIPROP_RANGE, &proprange.diph ); list_add_tail( &devices, &entry->entry );
return DIENUM_CONTINUE; @@ -595,28 +574,14 @@ static void dump_joy_state(DIJOYSTATE* st)
static DWORD WINAPI input_thread(void *param) { - int axes_pos[TEST_MAX_AXES][2]; struct JoystickData *data = param;
- /* Setup POV as clock positions - * 0 - * 31500 4500 - * 27000 -1 9000 - * 22500 13500 - * 18000 - */ - int ma = TEST_AXIS_MAX; - int pov_val[9] = {0, 4500, 9000, 13500, - 18000, 22500, 27000, 31500, -1}; - int pov_pos[9][2] = { {0, -ma}, {ma/2, -ma/2}, {ma, 0}, {ma/2, ma/2}, - {0, ma}, {-ma/2, ma/2}, {-ma, 0}, {-ma/2, -ma/2}, {0, 0} }; - while (!data->stop) { IDirectInputDevice8W *device; IDirectInputEffect *effect; DIJOYSTATE state = {0}; - unsigned int i, j; + unsigned int i;
if (WaitForSingleObject( device_state_event, TEST_POLL_TIME ) == WAIT_TIMEOUT) continue;
@@ -630,36 +595,6 @@ static DWORD WINAPI input_thread(void *param)
dump_joy_state(&state); set_di_device_state( data->di_dialog_hwnd, &state, &caps ); - - /* Indicate axis positions, axes showing are hardcoded for now */ - axes_pos[0][0] = state.lX; - axes_pos[0][1] = state.lY; - axes_pos[1][0] = state.lRx; - axes_pos[1][1] = state.lRy; - axes_pos[2][0] = state.lZ; - axes_pos[2][1] = state.lRz; - - /* Set pov values */ - for (j = 0; j < ARRAY_SIZE(pov_val); j++) - { - if (state.rgdwPOV[0] == pov_val[j]) - { - axes_pos[3][0] = pov_pos[j][0]; - axes_pos[3][1] = pov_pos[j][1]; - } - } - - for (i = 0; i < TEST_MAX_AXES; i++) - { - RECT r; - - r.left = (TEST_AXIS_X + TEST_NEXT_AXIS_X*i + axes_pos[i][0]); - r.top = (TEST_AXIS_Y + axes_pos[i][1]); - r.bottom = r.right = 0; /* unused */ - MapDialogRect(data->graphics.hwnd, &r); - - SetWindowPos(data->graphics.axes[i], 0, r.left, r.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); - } }
if ((effect = get_selected_effect())) @@ -770,31 +705,6 @@ static void ff_handle_effectchange( HWND hwnd ) } }
-static void draw_joystick_axes(HWND hwnd, struct JoystickData* data) -{ - static const WCHAR *axes_names[TEST_MAX_AXES] = {L"X,Y", L"Rx,Ry", L"Z,Rz", L"POV"}; - static const DWORD axes_idc[TEST_MAX_AXES] = {IDC_DI_AXIS_X_Y, IDC_DI_AXIS_RX_RY, IDC_DI_AXIS_Z_RZ, IDC_DI_AXIS_POV_0}; - int i; - HINSTANCE hinst = (HINSTANCE) GetWindowLongPtrW(hwnd, GWLP_HINSTANCE); - - for (i = 0; i < TEST_MAX_AXES; i++) - { - RECT r; - /* Set axis box name */ - SetWindowTextW(GetDlgItem(hwnd, axes_idc[i]), axes_names[i]); - - r.left = (TEST_AXIS_X + TEST_NEXT_AXIS_X*i); - r.top = TEST_AXIS_Y; - r.right = r.left + TEST_AXIS_SIZE_X; - r.bottom = r.top + TEST_AXIS_SIZE_Y; - MapDialogRect(hwnd, &r); - - data->graphics.axes[i] = CreateWindowW(L"Button", NULL, WS_CHILD | WS_VISIBLE, - r.left, r.top, r.right - r.left, r.bottom - r.top, - hwnd, NULL, NULL, hinst); - } -} - /********************************************************************* * test_dlgproc [internal] * @@ -817,6 +727,14 @@ static void update_device_views( HWND hwnd ) { HWND parent, view;
+ parent = GetDlgItem( hwnd, IDC_DI_AXES ); + view = FindWindowExW( parent, NULL, L"JoyCplDInputAxes", NULL ); + InvalidateRect( view, NULL, TRUE ); + + parent = GetDlgItem( hwnd, IDC_DI_POVS ); + view = FindWindowExW( parent, NULL, L"JoyCplDInputPOVs", NULL ); + InvalidateRect( view, NULL, TRUE ); + parent = GetDlgItem( hwnd, IDC_DI_BUTTONS ); view = FindWindowExW( parent, NULL, L"JoyCplDInputButtons", NULL ); InvalidateRect( view, NULL, TRUE ); @@ -829,6 +747,26 @@ static void create_device_views( HWND hwnd ) LONG margin; RECT rect;
+ parent = GetDlgItem( hwnd, IDC_DI_AXES ); + GetClientRect( parent, &rect ); + rect.top += 10; + + margin = (rect.bottom - rect.top) * 10 / 100; + InflateRect( &rect, -margin, -margin ); + + CreateWindowW( L"JoyCplDInputAxes", NULL, WS_CHILD | WS_VISIBLE, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, parent, NULL, NULL, instance ); + + parent = GetDlgItem( hwnd, IDC_DI_POVS ); + GetClientRect( parent, &rect ); + rect.top += 10; + + margin = (rect.bottom - rect.top) * 10 / 100; + InflateRect( &rect, -margin, -margin ); + + CreateWindowW( L"JoyCplDInputPOVs", NULL, WS_CHILD | WS_VISIBLE, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, parent, NULL, NULL, instance ); + parent = GetDlgItem( hwnd, IDC_DI_BUTTONS ); GetClientRect( parent, &rect ); rect.top += 10; @@ -853,7 +791,6 @@ static INT_PTR CALLBACK test_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam;
di_update_select_combo( hwnd ); - draw_joystick_axes(hwnd, data); create_device_views( hwnd );
return TRUE; @@ -1003,6 +940,18 @@ static void register_window_class(void) .lpfnWndProc = &test_xi_window_proc, .lpszClassName = L"JoyCplXInput", }; + WNDCLASSW di_axes_class = + { + .hInstance = hcpl, + .lpfnWndProc = &test_di_axes_window_proc, + .lpszClassName = L"JoyCplDInputAxes", + }; + WNDCLASSW di_povs_class = + { + .hInstance = hcpl, + .lpfnWndProc = &test_di_povs_window_proc, + .lpszClassName = L"JoyCplDInputPOVs", + }; WNDCLASSW di_buttons_class = { .hInstance = hcpl, @@ -1011,11 +960,15 @@ static void register_window_class(void) };
RegisterClassW( &xi_class ); + RegisterClassW( &di_axes_class ); + RegisterClassW( &di_povs_class ); RegisterClassW( &di_buttons_class ); }
static void unregister_window_class(void) { + UnregisterClassW( L"JoyCplDInputAxes", hcpl ); + UnregisterClassW( L"JoyCplDInputPOVs", hcpl ); UnregisterClassW( L"JoyCplDInputButtons", hcpl ); UnregisterClassW( L"JoyCplXInput", hcpl ); } diff --git a/dlls/joy.cpl/resource.h b/dlls/joy.cpl/resource.h index 35047d87642..a711617a607 100644 --- a/dlls/joy.cpl/resource.h +++ b/dlls/joy.cpl/resource.h @@ -47,12 +47,10 @@ #define IDC_BUTTONOVERRIDE 2013
#define IDC_DI_DEVICES 2100 -#define IDC_DI_AXIS_X_Y 2101 -#define IDC_DI_AXIS_RX_RY 2102 -#define IDC_DI_AXIS_Z_RZ 2103 -#define IDC_DI_AXIS_POV_0 2104 -#define IDC_DI_BUTTONS 2105 -#define IDC_DI_EFFECTS 2106 +#define IDC_DI_AXES 2101 +#define IDC_DI_POVS 2102 +#define IDC_DI_BUTTONS 2103 +#define IDC_DI_EFFECTS 2104
#define IDC_XI_USER_0 2200 #define IDC_XI_USER_1 2201
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/joy.cpl/dinput.c | 514 +++++++++++++++++++++++++++++++++++-- dlls/joy.cpl/joy_private.h | 2 +- dlls/joy.cpl/main.c | 496 +---------------------------------- 3 files changed, 494 insertions(+), 518 deletions(-)
diff --git a/dlls/joy.cpl/dinput.c b/dlls/joy.cpl/dinput.c index bf80584000e..521634ff7da 100644 --- a/dlls/joy.cpl/dinput.c +++ b/dlls/joy.cpl/dinput.c @@ -35,6 +35,18 @@
WINE_DEFAULT_DEBUG_CHANNEL(joycpl);
+struct effect +{ + struct list entry; + IDirectInputEffect *effect; +}; + +struct device +{ + struct list entry; + IDirectInputDevice8W *device; +}; + static CRITICAL_SECTION state_cs; static CRITICAL_SECTION_DEBUG state_cs_debug = { @@ -44,31 +56,256 @@ static CRITICAL_SECTION_DEBUG state_cs_debug = }; static CRITICAL_SECTION state_cs = { &state_cs_debug, -1, 0, 0, 0, 0 };
-static DIJOYSTATE last_state; -static DIDEVCAPS last_caps; +static struct list effects = LIST_INIT( effects ); +static IDirectInputEffect *effect_selected;
-void set_di_device_state( HWND hwnd, DIJOYSTATE *state, DIDEVCAPS *caps ) +static struct list devices = LIST_INIT( devices ); +static IDirectInputDevice8W *device_selected; + +static HWND dialog_hwnd; +static HANDLE state_event; + +static BOOL CALLBACK enum_effects( const DIEFFECTINFOW *info, void *context ) { - BOOL modified; + IDirectInputDevice8W *device = context; + DWORD axes[2] = {DIJOFS_X, DIJOFS_Y}; + LONG direction[2] = {0}; + DIEFFECT params = + { + .dwSize = sizeof(DIEFFECT), + .dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS, + .dwDuration = 2 * DI_SECONDS, + .dwGain = DI_FFNOMINALMAX, + .rglDirection = direction, + .rgdwAxes = axes, + .cAxes = 2, + }; + DICONSTANTFORCE constant = + { + .lMagnitude = DI_FFNOMINALMAX, + }; + DIPERIODIC periodic = + { + .dwMagnitude = DI_FFNOMINALMAX, + .dwPeriod = DI_SECONDS / 2, + }; + DICONDITION condition = + { + .dwPositiveSaturation = 10000, + .dwNegativeSaturation = 10000, + .lPositiveCoefficient = 10000, + .lNegativeCoefficient = 10000, + }; + DIRAMPFORCE ramp = + { + .lEnd = DI_FFNOMINALMAX, + }; + IDirectInputEffect *effect; + struct effect *entry; + HRESULT hr; + + hr = IDirectInputDevice8_Acquire( device ); + if (FAILED(hr)) return DIENUM_CONTINUE; + + if (!(entry = calloc( 1, sizeof(*entry) ))) return DIENUM_STOP; + + if (IsEqualGUID( &info->guid, &GUID_RampForce )) + { + params.cbTypeSpecificParams = sizeof(ramp); + params.lpvTypeSpecificParams = &ramp; + params.dwFlags |= DIEP_TYPESPECIFICPARAMS; + } + else if (IsEqualGUID( &info->guid, &GUID_ConstantForce )) + { + params.cbTypeSpecificParams = sizeof(constant); + params.lpvTypeSpecificParams = &constant; + params.dwFlags |= DIEP_TYPESPECIFICPARAMS; + } + else if (IsEqualGUID( &info->guid, &GUID_Sine ) || + IsEqualGUID( &info->guid, &GUID_Square ) || + IsEqualGUID( &info->guid, &GUID_Triangle ) || + IsEqualGUID( &info->guid, &GUID_SawtoothUp ) || + IsEqualGUID( &info->guid, &GUID_SawtoothDown )) + { + params.cbTypeSpecificParams = sizeof(periodic); + params.lpvTypeSpecificParams = &periodic; + params.dwFlags |= DIEP_TYPESPECIFICPARAMS; + } + else if (IsEqualGUID( &info->guid, &GUID_Spring ) || + IsEqualGUID( &info->guid, &GUID_Damper ) || + IsEqualGUID( &info->guid, &GUID_Inertia ) || + IsEqualGUID( &info->guid, &GUID_Friction )) + { + params.cbTypeSpecificParams = sizeof(condition); + params.lpvTypeSpecificParams = &condition; + params.dwFlags |= DIEP_TYPESPECIFICPARAMS; + } + + do hr = IDirectInputDevice2_CreateEffect( device, &info->guid, ¶ms, &effect, NULL ); + while (FAILED(hr) && --params.cAxes); + + if (FAILED(hr)) + { + FIXME( "Failed to create effect with type %s, hr %#lx\n", debugstr_guid( &info->guid ), hr ); + free( entry ); + return DIENUM_CONTINUE; + } + + entry->effect = effect; + list_add_tail( &effects, &entry->entry ); + + return DIENUM_CONTINUE; +} + +static void set_selected_effect( IDirectInputEffect *effect ) +{ + IDirectInputEffect *previous;
EnterCriticalSection( &state_cs ); - modified = memcmp( &last_state, state, sizeof(*state) ) || - memcmp( &last_caps, caps, sizeof(*caps) ); - last_state = *state; - last_caps = *caps; + if ((previous = effect_selected)) IDirectInputEffect_Release( previous ); + if ((effect_selected = effect)) IDirectInput_AddRef( effect ); LeaveCriticalSection( &state_cs ); +} + +static IDirectInputEffect *get_selected_effect(void) +{ + IDirectInputEffect *effect; + + EnterCriticalSection( &state_cs ); + if ((effect = effect_selected)) IDirectInputEffect_AddRef( effect ); + LeaveCriticalSection( &state_cs ); + + return effect; +} + +static void clear_effects(void) +{ + struct effect *effect, *next;
- if (modified) SendMessageW( hwnd, WM_USER, 0, 0 ); + set_selected_effect( NULL ); + + LIST_FOR_EACH_ENTRY_SAFE( effect, next, &effects, struct effect, entry ) + { + list_remove( &effect->entry ); + IDirectInputEffect_Release( effect->effect ); + free( effect ); + } }
-static void get_device_state( DIJOYSTATE *state, DIDEVCAPS *caps ) +static void set_selected_device( IDirectInputDevice8W *device ) { + IDirectInputDevice8W *previous; + EnterCriticalSection( &state_cs ); - *state = last_state; - *caps = last_caps; + + set_selected_effect( NULL ); + + if ((previous = device_selected)) + { + IDirectInputDevice8_SetEventNotification( previous, NULL ); + IDirectInputDevice8_Release( previous ); + } + if ((device_selected = device)) + { + IDirectInputDevice8_AddRef( device ); + IDirectInputDevice8_SetEventNotification( device, state_event ); + IDirectInputDevice8_Acquire( device ); + } + LeaveCriticalSection( &state_cs ); }
+static IDirectInputDevice8W *get_selected_device(void) +{ + IDirectInputDevice8W *device; + + EnterCriticalSection( &state_cs ); + device = device_selected; + if (device) IDirectInputDevice8_AddRef( device ); + LeaveCriticalSection( &state_cs ); + + return device; +} + +static BOOL CALLBACK enum_devices( const DIDEVICEINSTANCEW *instance, void *context ) +{ + DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)}; + IDirectInput8W *dinput = context; + struct device *entry; + + if (!(entry = calloc( 1, sizeof(*entry) ))) return DIENUM_STOP; + + IDirectInput8_CreateDevice( dinput, &instance->guidInstance, &entry->device, NULL ); + IDirectInputDevice8_SetDataFormat( entry->device, &c_dfDIJoystick ); + IDirectInputDevice8_GetCapabilities( entry->device, &caps ); + + list_add_tail( &devices, &entry->entry ); + + return DIENUM_CONTINUE; +} + +static void clear_devices(void) +{ + struct device *entry, *next; + + set_selected_device( NULL ); + + LIST_FOR_EACH_ENTRY_SAFE( entry, next, &devices, struct device, entry ) + { + list_remove( &entry->entry ); + IDirectInputDevice8_Unacquire( entry->device ); + IDirectInputDevice8_Release( entry->device ); + free( entry ); + } +} + +static DWORD WINAPI input_thread( void *param ) +{ + HANDLE events[2] = {param, state_event}; + + while (WaitForMultipleObjects( 2, events, FALSE, INFINITE ) != 0) + { + IDirectInputEffect *effect; + DIJOYSTATE state = {0}; + unsigned int i; + + SendMessageW( dialog_hwnd, WM_USER, 0, 0 ); + + if ((effect = get_selected_effect())) + { + DWORD flags = DIEP_AXES | DIEP_DIRECTION | DIEP_NORESTART; + LONG direction[3] = {0}; + DWORD axes[3] = {0}; + DIEFFECT params = + { + .dwSize = sizeof(DIEFFECT), + .dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS, + .rglDirection = direction, + .rgdwAxes = axes, + .cAxes = 3, + }; + + IDirectInputEffect_GetParameters( effect, ¶ms, flags ); + params.rgdwAxes[0] = state.lX; + params.rgdwAxes[1] = state.lY; + + for (i = 0; i < ARRAY_SIZE(state.rgbButtons); i++) + { + if (state.rgbButtons[i]) + { + IDirectInputEffect_SetParameters( effect, ¶ms, flags ); + IDirectInputEffect_Start( effect, 1, 0 ); + break; + } + } + + IDirectInputEffect_Release( effect ); + } + } + + return 0; +} + static void draw_axis_view( HDC hdc, RECT rect, const WCHAR *name, LONG value ) { POINT center = @@ -167,7 +404,7 @@ static void draw_pov_view( HDC hdc, RECT rect, DWORD value ) if (value != -1) Polygon( hdc, points + value / 4500 * 2, 3 ); }
-static inline void draw_button_view( HDC hdc, RECT rect, BOOL set, const WCHAR *name ) +static void draw_button_view( HDC hdc, RECT rect, BOOL set, const WCHAR *name ) { COLORREF color; HFONT font; @@ -197,13 +434,19 @@ LRESULT CALLBACK test_di_axes_window_proc( HWND hwnd, UINT msg, WPARAM wparam, L
if (msg == WM_PAINT) { + DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)}; + IDirectInputDevice8W *device; + DIJOYSTATE state = {0}; RECT rect, tmp_rect; PAINTSTRUCT paint; - DIJOYSTATE state; - DIDEVCAPS caps; HDC hdc;
- get_device_state( &state, &caps ); + if ((device = get_selected_device())) + { + IDirectInputDevice8_GetDeviceState( device, sizeof(state), &state ); + IDirectInputDevice8_GetCapabilities( device, &caps ); + IDirectInputDevice8_Release( device ); + }
hdc = BeginPaint( hwnd, &paint );
@@ -257,13 +500,19 @@ LRESULT CALLBACK test_di_povs_window_proc( HWND hwnd, UINT msg, WPARAM wparam, L
if (msg == WM_PAINT) { + DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)}; + IDirectInputDevice8W *device; + DIJOYSTATE state = {0}; PAINTSTRUCT paint; - DIJOYSTATE state; - DIDEVCAPS caps; RECT rect; HDC hdc;
- get_device_state( &state, &caps ); + if ((device = get_selected_device())) + { + IDirectInputDevice8_GetDeviceState( device, sizeof(state), &state ); + IDirectInputDevice8_GetCapabilities( device, &caps ); + IDirectInputDevice8_Release( device ); + }
hdc = BeginPaint( hwnd, &paint );
@@ -294,14 +543,20 @@ LRESULT CALLBACK test_di_buttons_window_proc( HWND hwnd, UINT msg, WPARAM wparam
if (msg == WM_PAINT) { + DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)}; UINT i, j, offs, size, step, space = 2; + IDirectInputDevice8W *device; + DIJOYSTATE state = {0}; PAINTSTRUCT paint; - DIJOYSTATE state; - DIDEVCAPS caps; RECT rect; HDC hdc;
- get_device_state( &state, &caps ); + if ((device = get_selected_device())) + { + IDirectInputDevice8_GetDeviceState( device, sizeof(state), &state ); + IDirectInputDevice8_GetCapabilities( device, &caps ); + IDirectInputDevice8_Release( device ); + }
if (caps.dwButtons <= 48) step = 16; else step = 32; @@ -339,3 +594,218 @@ LRESULT CALLBACK test_di_buttons_window_proc( HWND hwnd, UINT msg, WPARAM wparam
return DefWindowProcW( hwnd, msg, wparam, lparam ); } + +static void update_di_effects( HWND hwnd, IDirectInputDevice8W *device ) +{ + struct effect *effect; + + clear_effects(); + + IDirectInputDevice8_EnumEffects( device, enum_effects, device, 0 ); + + SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_RESETCONTENT, 0, 0 ); + SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_ADDSTRING, 0, (LPARAM)L"None" ); + + LIST_FOR_EACH_ENTRY( effect, &effects, struct effect, entry ) + { + DIEFFECTINFOW info = {.dwSize = sizeof(DIEFFECTINFOW)}; + GUID guid; + + if (FAILED(IDirectInputEffect_GetEffectGuid( effect->effect, &guid ))) continue; + if (FAILED(IDirectInputDevice8_GetEffectInfo( device, &info, &guid ))) continue; + SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_ADDSTRING, 0, (LPARAM)( info.tszName + 5 ) ); + } +} + +static void handle_di_effects_change( HWND hwnd ) +{ + IDirectInputDevice8W *device; + struct list *entry; + int sel; + + set_selected_effect( NULL ); + + sel = SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_GETCURSEL, 0, 0 ) - 1; + if (sel < 0) return; + + entry = list_head( &effects ); + while (sel-- && entry) entry = list_next( &effects, entry ); + if (!entry) return; + + set_selected_effect( LIST_ENTRY( entry, struct effect, entry )->effect ); + + if ((device = get_selected_device())) + { + IDirectInputDevice8_Unacquire( device ); + IDirectInputDevice8_SetCooperativeLevel( device, GetAncestor( hwnd, GA_ROOT ), DISCL_BACKGROUND | DISCL_EXCLUSIVE ); + IDirectInputDevice8_Acquire( device ); + IDirectInputDevice8_Release( device ); + } +} + +static void create_device_views( HWND hwnd ) +{ + HINSTANCE instance = (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ); + HWND parent; + LONG margin; + RECT rect; + + parent = GetDlgItem( hwnd, IDC_DI_AXES ); + GetClientRect( parent, &rect ); + rect.top += 10; + + margin = (rect.bottom - rect.top) * 10 / 100; + InflateRect( &rect, -margin, -margin ); + + CreateWindowW( L"JoyCplDInputAxes", NULL, WS_CHILD | WS_VISIBLE, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, parent, NULL, NULL, instance ); + + parent = GetDlgItem( hwnd, IDC_DI_POVS ); + GetClientRect( parent, &rect ); + rect.top += 10; + + margin = (rect.bottom - rect.top) * 10 / 100; + InflateRect( &rect, -margin, -margin ); + + CreateWindowW( L"JoyCplDInputPOVs", NULL, WS_CHILD | WS_VISIBLE, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, parent, NULL, NULL, instance ); + + parent = GetDlgItem( hwnd, IDC_DI_BUTTONS ); + GetClientRect( parent, &rect ); + rect.top += 10; + + margin = (rect.bottom - rect.top) * 10 / 100; + InflateRect( &rect, -margin, -margin ); + + CreateWindowW( L"JoyCplDInputButtons", NULL, WS_CHILD | WS_VISIBLE, rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, parent, NULL, NULL, instance ); +} + +static void handle_di_devices_change( HWND hwnd ) +{ + DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)}; + IDirectInputDevice8W *device; + struct list *entry; + int i; + + set_selected_device( NULL ); + + i = SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_GETCURSEL, 0, 0 ); + if (i < 0) return; + + entry = list_head( &devices ); + while (i-- && entry) entry = list_next( &devices, entry ); + if (!entry) return; + + device = LIST_ENTRY( entry, struct device, entry )->device; + if (FAILED(IDirectInputDevice8_GetCapabilities( device, &caps ))) return; + + set_selected_device( device ); + update_di_effects( hwnd, device ); +} + +static void update_di_devices( HWND hwnd ) +{ + IDirectInput8W *dinput; + struct device *entry; + + clear_devices(); + + DirectInput8Create( GetModuleHandleW( NULL ), DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void **)&dinput, NULL ); + IDirectInput8_EnumDevices( dinput, DI8DEVCLASS_GAMECTRL, enum_devices, dinput, DIEDFL_ATTACHEDONLY ); + IDirectInput8_Release( dinput ); + + SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_RESETCONTENT, 0, 0 ); + + LIST_FOR_EACH_ENTRY( entry, &devices, struct device, entry ) + { + DIDEVICEINSTANCEW info = {.dwSize = sizeof(DIDEVICEINSTANCEW)}; + if (FAILED(IDirectInputDevice8_GetDeviceInfo( entry->device, &info ))) continue; + SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_ADDSTRING, 0, (LPARAM)info.tszInstanceName ); + } +} + +static void update_device_views( HWND hwnd ) +{ + HWND parent, view; + + parent = GetDlgItem( hwnd, IDC_DI_AXES ); + view = FindWindowExW( parent, NULL, L"JoyCplDInputAxes", NULL ); + InvalidateRect( view, NULL, TRUE ); + + parent = GetDlgItem( hwnd, IDC_DI_POVS ); + view = FindWindowExW( parent, NULL, L"JoyCplDInputPOVs", NULL ); + InvalidateRect( view, NULL, TRUE ); + + parent = GetDlgItem( hwnd, IDC_DI_BUTTONS ); + view = FindWindowExW( parent, NULL, L"JoyCplDInputButtons", NULL ); + InvalidateRect( view, NULL, TRUE ); +} + +INT_PTR CALLBACK test_di_dialog_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ) +{ + static HANDLE thread, thread_stop; + + TRACE( "hwnd %p, msg %#x, wparam %#Ix, lparam %#Ix\n", hwnd, msg, wparam, lparam ); + + switch (msg) + { + case WM_INITDIALOG: + create_device_views( hwnd ); + return TRUE; + + case WM_COMMAND: + switch (wparam) + { + case MAKEWPARAM( IDC_DI_DEVICES, CBN_SELCHANGE ): + handle_di_devices_change( hwnd ); + + SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_SETCURSEL, 0, 0 ); + handle_di_effects_change( hwnd ); + break; + + case MAKEWPARAM( IDC_DI_EFFECTS, LBN_SELCHANGE ): + handle_di_effects_change( hwnd ); + break; + } + return TRUE; + + case WM_NOTIFY: + switch (((NMHDR *)lparam)->code) + { + case PSN_SETACTIVE: + dialog_hwnd = hwnd; + state_event = CreateEventW( NULL, FALSE, FALSE, NULL ); + thread_stop = CreateEventW( NULL, FALSE, FALSE, NULL ); + + update_di_devices( hwnd ); + + SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_SETCURSEL, 0, 0 ); + handle_di_devices_change( hwnd ); + + SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_SETCURSEL, 0, 0 ); + handle_di_effects_change( hwnd ); + + thread = CreateThread( NULL, 0, input_thread, (void *)thread_stop, 0, NULL ); + break; + + case PSN_RESET: + case PSN_KILLACTIVE: + SetEvent( thread_stop ); + MsgWaitForMultipleObjects( 1, &thread, FALSE, INFINITE, 0 ); + CloseHandle( state_event ); + CloseHandle( thread_stop ); + CloseHandle( thread ); + + clear_effects(); + clear_devices(); + break; + } + return TRUE; + + case WM_USER: + update_device_views( hwnd ); + return TRUE; + } + return FALSE; +} diff --git a/dlls/joy.cpl/joy_private.h b/dlls/joy.cpl/joy_private.h index 0dc9a02be9f..1b960c6ea16 100644 --- a/dlls/joy.cpl/joy_private.h +++ b/dlls/joy.cpl/joy_private.h @@ -30,7 +30,7 @@
#include "resource.h"
-extern void set_di_device_state( HWND hwnd, DIJOYSTATE *state, DIDEVCAPS *caps ); +extern INT_PTR CALLBACK test_di_dialog_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ); extern LRESULT CALLBACK test_di_axes_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ); extern LRESULT CALLBACK test_di_povs_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ); extern LRESULT CALLBACK test_di_buttons_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam ); diff --git a/dlls/joy.cpl/main.c b/dlls/joy.cpl/main.c index fb41a8a2860..8e8d167068e 100644 --- a/dlls/joy.cpl/main.c +++ b/dlls/joy.cpl/main.c @@ -38,35 +38,15 @@
WINE_DEFAULT_DEBUG_CHANNEL(joycpl);
-#define TEST_MAX_BUTTONS 32 -#define TEST_POLL_TIME 100 - -#define FF_PLAY_TIME 2*DI_SECONDS -#define FF_PERIOD_TIME FF_PLAY_TIME/4 - -struct effect -{ - struct list entry; - IDirectInputEffect *effect; -}; - struct device { struct list entry; IDirectInputDevice8W *device; };
-struct Graphics -{ - HWND hwnd; -}; - struct JoystickData { IDirectInput8W *di; - struct Graphics graphics; - HWND di_dialog_hwnd; - BOOL stop; };
static HMODULE hcpl; @@ -80,13 +60,7 @@ static CRITICAL_SECTION_DEBUG joy_cs_debug = }; static CRITICAL_SECTION joy_cs = { &joy_cs_debug, -1, 0, 0, 0, 0 };
-static struct list effects = LIST_INIT( effects ); -static IDirectInputEffect *effect_selected; - static struct list devices = LIST_INIT( devices ); -static IDirectInputDevice8W *device_selected; - -static HANDLE device_state_event;
/********************************************************************* * DllMain @@ -104,133 +78,6 @@ BOOL WINAPI DllMain(HINSTANCE hdll, DWORD reason, LPVOID reserved) return TRUE; }
-static BOOL CALLBACK enum_effects( const DIEFFECTINFOW *info, void *context ) -{ - IDirectInputDevice8W *device = context; - DWORD axes[2] = {DIJOFS_X, DIJOFS_Y}; - LONG direction[2] = {0}; - DIEFFECT params = - { - .dwSize = sizeof(DIEFFECT), - .dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS, - .dwDuration = FF_PLAY_TIME, - .dwGain = DI_FFNOMINALMAX, - .rglDirection = direction, - .rgdwAxes = axes, - .cAxes = 2, - }; - DICONSTANTFORCE constant = - { - .lMagnitude = DI_FFNOMINALMAX, - }; - DIPERIODIC periodic = - { - .dwMagnitude = DI_FFNOMINALMAX, - .dwPeriod = FF_PERIOD_TIME, - }; - DICONDITION condition = - { - .dwPositiveSaturation = 10000, - .dwNegativeSaturation = 10000, - .lPositiveCoefficient = 10000, - .lNegativeCoefficient = 10000, - }; - DIRAMPFORCE ramp = - { - .lEnd = DI_FFNOMINALMAX, - }; - IDirectInputEffect *effect; - struct effect *entry; - HRESULT hr; - - hr = IDirectInputDevice8_Acquire( device ); - if (FAILED(hr)) return DIENUM_CONTINUE; - - if (!(entry = calloc( 1, sizeof(*entry) ))) return DIENUM_STOP; - - if (IsEqualGUID( &info->guid, &GUID_RampForce )) - { - params.cbTypeSpecificParams = sizeof(ramp); - params.lpvTypeSpecificParams = &ramp; - params.dwFlags |= DIEP_TYPESPECIFICPARAMS; - } - else if (IsEqualGUID( &info->guid, &GUID_ConstantForce )) - { - params.cbTypeSpecificParams = sizeof(constant); - params.lpvTypeSpecificParams = &constant; - params.dwFlags |= DIEP_TYPESPECIFICPARAMS; - } - else if (IsEqualGUID( &info->guid, &GUID_Sine ) || - IsEqualGUID( &info->guid, &GUID_Square ) || - IsEqualGUID( &info->guid, &GUID_Triangle ) || - IsEqualGUID( &info->guid, &GUID_SawtoothUp ) || - IsEqualGUID( &info->guid, &GUID_SawtoothDown )) - { - params.cbTypeSpecificParams = sizeof(periodic); - params.lpvTypeSpecificParams = &periodic; - params.dwFlags |= DIEP_TYPESPECIFICPARAMS; - } - else if (IsEqualGUID( &info->guid, &GUID_Spring ) || - IsEqualGUID( &info->guid, &GUID_Damper ) || - IsEqualGUID( &info->guid, &GUID_Inertia ) || - IsEqualGUID( &info->guid, &GUID_Friction )) - { - params.cbTypeSpecificParams = sizeof(condition); - params.lpvTypeSpecificParams = &condition; - params.dwFlags |= DIEP_TYPESPECIFICPARAMS; - } - - do hr = IDirectInputDevice2_CreateEffect( device, &info->guid, ¶ms, &effect, NULL ); - while (FAILED(hr) && --params.cAxes); - - if (FAILED(hr)) - { - FIXME( "Failed to create effect with type %s, hr %#lx\n", debugstr_guid( &info->guid ), hr ); - free( entry ); - return DIENUM_CONTINUE; - } - - entry->effect = effect; - list_add_tail( &effects, &entry->entry ); - - return DIENUM_CONTINUE; -} - -static void set_selected_effect( IDirectInputEffect *effect ) -{ - IDirectInputEffect *previous; - - EnterCriticalSection( &joy_cs ); - if ((previous = effect_selected)) IDirectInputEffect_Release( previous ); - if ((effect_selected = effect)) IDirectInput_AddRef( effect ); - LeaveCriticalSection( &joy_cs ); -} - -static IDirectInputEffect *get_selected_effect(void) -{ - IDirectInputEffect *effect; - - EnterCriticalSection( &joy_cs ); - if ((effect = effect_selected)) IDirectInputEffect_AddRef( effect ); - LeaveCriticalSection( &joy_cs ); - - return effect; -} - -static void clear_effects(void) -{ - struct effect *effect, *next; - - set_selected_effect( NULL ); - - LIST_FOR_EACH_ENTRY_SAFE( effect, next, &effects, struct effect, entry ) - { - list_remove( &effect->entry ); - IDirectInputEffect_Release( effect->effect ); - free( effect ); - } -} - static BOOL CALLBACK enum_devices( const DIDEVICEINSTANCEW *instance, void *context ) { DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)}; @@ -248,47 +95,10 @@ static BOOL CALLBACK enum_devices( const DIDEVICEINSTANCEW *instance, void *cont return DIENUM_CONTINUE; }
-static void set_selected_device( IDirectInputDevice8W *device ) -{ - IDirectInputDevice8W *previous; - - EnterCriticalSection( &joy_cs ); - - set_selected_effect( NULL ); - - if ((previous = device_selected)) - { - IDirectInputDevice8_SetEventNotification( previous, NULL ); - IDirectInputDevice8_Release( previous ); - } - if ((device_selected = device)) - { - IDirectInputDevice8_AddRef( device ); - IDirectInputDevice8_SetEventNotification( device, device_state_event ); - IDirectInputDevice8_Acquire( device ); - } - - LeaveCriticalSection( &joy_cs ); -} - -static IDirectInputDevice8W *get_selected_device(void) -{ - IDirectInputDevice8W *device; - - EnterCriticalSection( &joy_cs ); - device = device_selected; - if (device) IDirectInputDevice8_AddRef( device ); - LeaveCriticalSection( &joy_cs ); - - return device; -} - static void clear_devices(void) { struct device *entry, *next;
- set_selected_device( NULL ); - LIST_FOR_EACH_ENTRY_SAFE( entry, next, &devices, struct device, entry ) { list_remove( &entry->entry ); @@ -368,7 +178,6 @@ static void refresh_joystick_list(HWND hwnd, struct JoystickData *data) LSTATUS status; DWORD i;
- clear_effects(); clear_devices();
IDirectInput8_EnumDevices( data->di, DI8DEVCLASS_GAMECTRL, enum_devices, data, DIEDFL_ATTACHEDONLY ); @@ -455,9 +264,6 @@ static INT_PTR CALLBACK list_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM EnableWindow(GetDlgItem(hwnd, IDC_BUTTONRESET), FALSE); EnableWindow(GetDlgItem(hwnd, IDC_BUTTONOVERRIDE), FALSE);
- /* Store the hwnd to be used with MapDialogRect for unit conversions */ - data->graphics.hwnd = hwnd; - return TRUE; }
@@ -554,303 +360,6 @@ static INT_PTR CALLBACK list_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM return FALSE; }
-/********************************************************************* - * Joystick testing functions - * - */ -static void dump_joy_state(DIJOYSTATE* st) -{ - int i; - TRACE("Ax (% 5ld,% 5ld,% 5ld)\n", st->lX,st->lY, st->lZ); - TRACE("RAx (% 5ld,% 5ld,% 5ld)\n", st->lRx, st->lRy, st->lRz); - TRACE("Slider (% 5ld,% 5ld)\n", st->rglSlider[0], st->rglSlider[1]); - TRACE("Pov (% 5ld,% 5ld,% 5ld,% 5ld)\n", st->rgdwPOV[0], st->rgdwPOV[1], st->rgdwPOV[2], st->rgdwPOV[3]); - - TRACE("Buttons "); - for(i=0; i < TEST_MAX_BUTTONS; i++) - TRACE(" %c",st->rgbButtons[i] ? 'x' : 'o'); - TRACE("\n"); -} - -static DWORD WINAPI input_thread(void *param) -{ - struct JoystickData *data = param; - - while (!data->stop) - { - IDirectInputDevice8W *device; - IDirectInputEffect *effect; - DIJOYSTATE state = {0}; - unsigned int i; - - if (WaitForSingleObject( device_state_event, TEST_POLL_TIME ) == WAIT_TIMEOUT) continue; - - if ((device = get_selected_device())) - { - DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)}; - - IDirectInputDevice8_GetDeviceState( device, sizeof(state), &state ); - IDirectInputDevice8_GetCapabilities( device, &caps ); - IDirectInputDevice8_Release( device ); - - dump_joy_state(&state); - set_di_device_state( data->di_dialog_hwnd, &state, &caps ); - } - - if ((effect = get_selected_effect())) - { - DWORD flags = DIEP_AXES | DIEP_DIRECTION | DIEP_NORESTART; - LONG direction[3] = {0}; - DWORD axes[3] = {0}; - DIEFFECT params = - { - .dwSize = sizeof(DIEFFECT), - .dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS, - .rglDirection = direction, - .rgdwAxes = axes, - .cAxes = 3, - }; - - IDirectInputEffect_GetParameters( effect, ¶ms, flags ); - params.rgdwAxes[0] = state.lX; - params.rgdwAxes[1] = state.lY; - - for (i=0; i < TEST_MAX_BUTTONS; i++) - { - if (state.rgbButtons[i]) - { - IDirectInputEffect_SetParameters( effect, ¶ms, flags ); - IDirectInputEffect_Start( effect, 1, 0 ); - break; - } - } - - IDirectInputEffect_Release( effect ); - } - } - - return 0; -} - -static void initialize_effects_list( HWND hwnd, IDirectInputDevice8W *device ) -{ - struct effect *effect; - - clear_effects(); - - IDirectInputDevice8_EnumEffects( device, enum_effects, device, 0 ); - - SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_RESETCONTENT, 0, 0 ); - SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_ADDSTRING, 0, (LPARAM)L"None" ); - - LIST_FOR_EACH_ENTRY( effect, &effects, struct effect, entry ) - { - DIEFFECTINFOW info = {.dwSize = sizeof(DIEFFECTINFOW)}; - GUID guid; - - if (FAILED(IDirectInputEffect_GetEffectGuid( effect->effect, &guid ))) continue; - if (FAILED(IDirectInputDevice8_GetEffectInfo( device, &info, &guid ))) continue; - SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_ADDSTRING, 0, (LPARAM)( info.tszName + 5 ) ); - } -} - -static void test_handle_joychange(HWND hwnd, struct JoystickData *data) -{ - DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)}; - IDirectInputDevice8W *device; - DIJOYSTATE state = {0}; - struct list *entry; - int i; - - set_selected_device( NULL ); - - i = SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_GETCURSEL, 0, 0 ); - if (i < 0) return; - - entry = list_head( &devices ); - while (i-- && entry) entry = list_next( &devices, entry ); - if (!entry) return; - - device = LIST_ENTRY( entry, struct device, entry )->device; - if (FAILED(IDirectInputDevice8_GetCapabilities( device, &caps ))) return; - - set_di_device_state( data->di_dialog_hwnd, &state, &caps ); - set_selected_device( device ); - initialize_effects_list( hwnd, device ); -} - -static void ff_handle_effectchange( HWND hwnd ) -{ - IDirectInputDevice8W *device; - struct list *entry; - int sel; - - set_selected_effect( NULL ); - - sel = SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_GETCURSEL, 0, 0 ) - 1; - if (sel < 0) return; - - entry = list_head( &effects ); - while (sel-- && entry) entry = list_next( &effects, entry ); - if (!entry) return; - - set_selected_effect( LIST_ENTRY( entry, struct effect, entry )->effect ); - - if ((device = get_selected_device())) - { - IDirectInputDevice8_Unacquire( device ); - IDirectInputDevice8_SetCooperativeLevel( device, GetAncestor( hwnd, GA_ROOT ), DISCL_BACKGROUND | DISCL_EXCLUSIVE ); - IDirectInputDevice8_Acquire( device ); - IDirectInputDevice8_Release( device ); - } -} - -/********************************************************************* - * test_dlgproc [internal] - * - */ -static void di_update_select_combo( HWND hwnd ) -{ - struct device *entry; - - SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_RESETCONTENT, 0, 0 ); - - LIST_FOR_EACH_ENTRY( entry, &devices, struct device, entry ) - { - DIDEVICEINSTANCEW info = {.dwSize = sizeof(DIDEVICEINSTANCEW)}; - if (FAILED(IDirectInputDevice8_GetDeviceInfo( entry->device, &info ))) continue; - SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_ADDSTRING, 0, (LPARAM)info.tszInstanceName ); - } -} - -static void update_device_views( HWND hwnd ) -{ - HWND parent, view; - - parent = GetDlgItem( hwnd, IDC_DI_AXES ); - view = FindWindowExW( parent, NULL, L"JoyCplDInputAxes", NULL ); - InvalidateRect( view, NULL, TRUE ); - - parent = GetDlgItem( hwnd, IDC_DI_POVS ); - view = FindWindowExW( parent, NULL, L"JoyCplDInputPOVs", NULL ); - InvalidateRect( view, NULL, TRUE ); - - parent = GetDlgItem( hwnd, IDC_DI_BUTTONS ); - view = FindWindowExW( parent, NULL, L"JoyCplDInputButtons", NULL ); - InvalidateRect( view, NULL, TRUE ); -} - -static void create_device_views( HWND hwnd ) -{ - HINSTANCE instance = (HINSTANCE)GetWindowLongPtrW( hwnd, GWLP_HINSTANCE ); - HWND parent; - LONG margin; - RECT rect; - - parent = GetDlgItem( hwnd, IDC_DI_AXES ); - GetClientRect( parent, &rect ); - rect.top += 10; - - margin = (rect.bottom - rect.top) * 10 / 100; - InflateRect( &rect, -margin, -margin ); - - CreateWindowW( L"JoyCplDInputAxes", NULL, WS_CHILD | WS_VISIBLE, rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, parent, NULL, NULL, instance ); - - parent = GetDlgItem( hwnd, IDC_DI_POVS ); - GetClientRect( parent, &rect ); - rect.top += 10; - - margin = (rect.bottom - rect.top) * 10 / 100; - InflateRect( &rect, -margin, -margin ); - - CreateWindowW( L"JoyCplDInputPOVs", NULL, WS_CHILD | WS_VISIBLE, rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, parent, NULL, NULL, instance ); - - parent = GetDlgItem( hwnd, IDC_DI_BUTTONS ); - GetClientRect( parent, &rect ); - rect.top += 10; - - margin = (rect.bottom - rect.top) * 10 / 100; - InflateRect( &rect, -margin, -margin ); - - CreateWindowW( L"JoyCplDInputButtons", NULL, WS_CHILD | WS_VISIBLE, rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, parent, NULL, NULL, instance ); -} - -static INT_PTR CALLBACK test_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -{ - static HANDLE thread; - static struct JoystickData *data; - TRACE("(%p, 0x%08x/%d, 0x%Ix)\n", hwnd, msg, msg, lparam); - - switch (msg) - { - case WM_INITDIALOG: - { - data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam; - - di_update_select_combo( hwnd ); - create_device_views( hwnd ); - - return TRUE; - } - - case WM_COMMAND: - switch(wparam) - { - case MAKEWPARAM( IDC_DI_DEVICES, CBN_SELCHANGE ): - test_handle_joychange( hwnd, data ); - - SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_SETCURSEL, 0, 0 ); - ff_handle_effectchange( hwnd ); - break; - - case MAKEWPARAM( IDC_DI_EFFECTS, LBN_SELCHANGE ): - ff_handle_effectchange( hwnd ); - break; - } - return TRUE; - - case WM_NOTIFY: - switch(((LPNMHDR)lparam)->code) - { - case PSN_SETACTIVE: - { - DWORD tid; - - di_update_select_combo( hwnd ); - - data->stop = FALSE; - data->di_dialog_hwnd = hwnd; - - /* Set the first joystick as default */ - SendDlgItemMessageW( hwnd, IDC_DI_DEVICES, CB_SETCURSEL, 0, 0 ); - test_handle_joychange(hwnd, data); - - SendDlgItemMessageW( hwnd, IDC_DI_EFFECTS, LB_SETCURSEL, 0, 0 ); - ff_handle_effectchange( hwnd ); - - thread = CreateThread(NULL, 0, input_thread, (void*) data, 0, &tid); - } - break; - - case PSN_RESET: /* intentional fall-through */ - case PSN_KILLACTIVE: - /* Stop input thread */ - data->stop = TRUE; - MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, 0); - CloseHandle(thread); - break; - } - return TRUE; - - case WM_USER: - update_device_views( hwnd ); - return TRUE; - } - return FALSE; -}
/****************************************************************************** * propsheet_callback [internal] @@ -886,7 +395,7 @@ static void display_cpl_sheets( HWND parent, struct JoystickData *data ) .dwSize = sizeof(PROPSHEETPAGEW), .hInstance = hcpl, .pszTemplate = MAKEINTRESOURCEW( IDD_TEST_DI ), - .pfnDlgProc = test_dlgproc, + .pfnDlgProc = test_di_dialog_proc, .lParam = (INT_PTR)data, }, { @@ -1000,7 +509,6 @@ LONG CALLBACK CPlApplet(HWND hwnd, UINT command, LPARAM lParam1, LPARAM lParam2) HRESULT hr;
register_window_class(); - device_state_event = CreateEventW( NULL, FALSE, FALSE, NULL );
/* Initialize dinput */ hr = DirectInput8Create(GetModuleHandleW(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void**)&data.di, NULL); @@ -1034,13 +542,11 @@ LONG CALLBACK CPlApplet(HWND hwnd, UINT command, LPARAM lParam1, LPARAM lParam2) break;
case CPL_STOP: - clear_effects(); clear_devices();
/* And destroy dinput too */ IDirectInput8_Release(data.di);
- CloseHandle( device_state_event ); unregister_window_class(); break; }
From: Rémi Bernon rbernon@codeweavers.com
--- dlls/joy.cpl/main.c | 57 ++++++++++++--------------------------------- 1 file changed, 15 insertions(+), 42 deletions(-)
diff --git a/dlls/joy.cpl/main.c b/dlls/joy.cpl/main.c index 8e8d167068e..28334ec6f99 100644 --- a/dlls/joy.cpl/main.c +++ b/dlls/joy.cpl/main.c @@ -44,11 +44,6 @@ struct device IDirectInputDevice8W *device; };
-struct JoystickData -{ - IDirectInput8W *di; -}; - static HMODULE hcpl;
static CRITICAL_SECTION joy_cs; @@ -81,12 +76,12 @@ BOOL WINAPI DllMain(HINSTANCE hdll, DWORD reason, LPVOID reserved) static BOOL CALLBACK enum_devices( const DIDEVICEINSTANCEW *instance, void *context ) { DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)}; - struct JoystickData *data = context; + IDirectInput8W *dinput = context; struct device *entry;
if (!(entry = calloc( 1, sizeof(*entry) ))) return DIENUM_STOP;
- IDirectInput8_CreateDevice( data->di, &instance->guidInstance, &entry->device, NULL ); + IDirectInput8_CreateDevice( dinput, &instance->guidInstance, &entry->device, NULL ); IDirectInputDevice8_SetDataFormat( entry->device, &c_dfDIJoystick ); IDirectInputDevice8_GetCapabilities( entry->device, &caps );
@@ -170,8 +165,9 @@ static void enable_joystick(WCHAR *joy_name, BOOL enable) if (appkey) RegCloseKey(appkey); }
-static void refresh_joystick_list(HWND hwnd, struct JoystickData *data) +static void refresh_joystick_list( HWND hwnd ) { + IDirectInput8W *dinput; struct device *entry; HKEY hkey, appkey; DWORD values = 0; @@ -180,7 +176,9 @@ static void refresh_joystick_list(HWND hwnd, struct JoystickData *data)
clear_devices();
- IDirectInput8_EnumDevices( data->di, DI8DEVCLASS_GAMECTRL, enum_devices, data, DIEDFL_ATTACHEDONLY ); + DirectInput8Create( GetModuleHandleW( NULL ), DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void **)&dinput, NULL ); + IDirectInput8_EnumDevices( dinput, DI8DEVCLASS_GAMECTRL, enum_devices, dinput, DIEDFL_ATTACHEDONLY ); + IDirectInput8_Release( dinput );
SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_RESETCONTENT, 0, 0); SendDlgItemMessageW(hwnd, IDC_DISABLEDLIST, LB_RESETCONTENT, 0, 0); @@ -247,7 +245,6 @@ static void override_joystick(WCHAR *joy_name, BOOL override) static INT_PTR CALLBACK list_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { WCHAR instance_name[MAX_PATH] = {0}; - static struct JoystickData *data; int sel;
TRACE("(%p, 0x%08x/%d, 0x%Ix)\n", hwnd, msg, msg, lparam); @@ -255,9 +252,7 @@ static INT_PTR CALLBACK list_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM { case WM_INITDIALOG: { - data = (struct JoystickData*) ((PROPSHEETPAGEW*)lparam)->lParam; - - refresh_joystick_list(hwnd, data); + refresh_joystick_list( hwnd );
EnableWindow(GetDlgItem(hwnd, IDC_BUTTONENABLE), FALSE); EnableWindow(GetDlgItem(hwnd, IDC_BUTTONDISABLE), FALSE); @@ -281,7 +276,7 @@ static INT_PTR CALLBACK list_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM if (instance_name[0]) { enable_joystick(instance_name, FALSE); - refresh_joystick_list(hwnd, data); + refresh_joystick_list( hwnd ); } } break; @@ -294,7 +289,7 @@ static INT_PTR CALLBACK list_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM if (instance_name[0]) { enable_joystick(instance_name, TRUE); - refresh_joystick_list(hwnd, data); + refresh_joystick_list( hwnd ); } } break; @@ -305,7 +300,7 @@ static INT_PTR CALLBACK list_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM { SendDlgItemMessageW(hwnd, IDC_JOYSTICKLIST, LB_GETTEXT, sel, (LPARAM)instance_name); override_joystick(instance_name, FALSE); - refresh_joystick_list(hwnd, data); + refresh_joystick_list( hwnd ); } } break; @@ -316,7 +311,7 @@ static INT_PTR CALLBACK list_dlgproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM { SendDlgItemMessageW(hwnd, IDC_XINPUTLIST, LB_GETTEXT, sel, (LPARAM)instance_name); override_joystick(instance_name, TRUE); - refresh_joystick_list(hwnd, data); + refresh_joystick_list( hwnd ); } } break; @@ -375,7 +370,7 @@ static int CALLBACK propsheet_callback(HWND hwnd, UINT msg, LPARAM lparam) return 0; }
-static void display_cpl_sheets( HWND parent, struct JoystickData *data ) +static void display_cpl_sheets( HWND parent ) { INITCOMMONCONTROLSEX init = { @@ -389,14 +384,12 @@ static void display_cpl_sheets( HWND parent, struct JoystickData *data ) .hInstance = hcpl, .pszTemplate = MAKEINTRESOURCEW( IDD_LIST ), .pfnDlgProc = list_dlgproc, - .lParam = (INT_PTR)data, }, { .dwSize = sizeof(PROPSHEETPAGEW), .hInstance = hcpl, .pszTemplate = MAKEINTRESOURCEW( IDD_TEST_DI ), .pfnDlgProc = test_di_dialog_proc, - .lParam = (INT_PTR)data, }, { .dwSize = sizeof(PROPSHEETPAGEW), @@ -499,30 +492,14 @@ static void unregister_window_class(void) */ LONG CALLBACK CPlApplet(HWND hwnd, UINT command, LPARAM lParam1, LPARAM lParam2) { - static struct JoystickData data; TRACE("(%p, %u, 0x%Ix, 0x%Ix)\n", hwnd, command, lParam1, lParam2);
switch (command) { case CPL_INIT: - { - HRESULT hr; - register_window_class(); - - /* Initialize dinput */ - hr = DirectInput8Create(GetModuleHandleW(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void**)&data.di, NULL); - - if (FAILED(hr)) - { - ERR("Failed to initialize DirectInput: 0x%08lx\n", hr); - return FALSE; - } - - IDirectInput8_EnumDevices( data.di, DI8DEVCLASS_GAMECTRL, enum_devices, &data, DIEDFL_ATTACHEDONLY ); - return TRUE; - } + case CPL_GETCOUNT: return 1;
@@ -538,15 +515,11 @@ LONG CALLBACK CPlApplet(HWND hwnd, UINT command, LPARAM lParam1, LPARAM lParam2) }
case CPL_DBLCLK: - display_cpl_sheets(hwnd, &data); + display_cpl_sheets( hwnd ); break;
case CPL_STOP: clear_devices(); - - /* And destroy dinput too */ - IDirectInput8_Release(data.di); - unregister_window_class(); break; }