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..9ca7fac3fa4 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 )