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