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; +}