mirror of
https://github.com/brunodev85/winlator.git
synced 2026-10-22 22:12:11 +03:00
Move wine_patches to another repository
This commit is contained in:
1 parent
f4afbe0a5a
commit
edcf4bde12
4 files changed
-2932
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -1,747 +0,0 @@
|
|||||||
/* DirectInput Gamepad device
|
|
||||||
*
|
|
||||||
* Copyright 2024 BrunoSX
|
|
||||||
*
|
|
||||||
* 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 <string.h>
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#include "windef.h"
|
|
||||||
#include "winbase.h"
|
|
||||||
#include "wingdi.h"
|
|
||||||
#include "winternl.h"
|
|
||||||
#include "winuser.h"
|
|
||||||
#include "winerror.h"
|
|
||||||
#include "winreg.h"
|
|
||||||
#include "dinput.h"
|
|
||||||
#include "winsock2.h"
|
|
||||||
#include "devguid.h"
|
|
||||||
#include "hidusage.h"
|
|
||||||
|
|
||||||
#include "dinput_private.h"
|
|
||||||
#include "device_private.h"
|
|
||||||
#include "wine/debug.h"
|
|
||||||
|
|
||||||
WINE_DEFAULT_DEBUG_CHANNEL(dinput);
|
|
||||||
|
|
||||||
#define SERVER_PORT 7948
|
|
||||||
#define CLIENT_PORT 7947
|
|
||||||
#define BUFFER_SIZE 64
|
|
||||||
|
|
||||||
#define REQUEST_CODE_GET_GAMEPAD 8
|
|
||||||
#define REQUEST_CODE_GET_GAMEPAD_STATE 9
|
|
||||||
#define REQUEST_CODE_RELEASE_GAMEPAD 10
|
|
||||||
|
|
||||||
#define MAPPER_TYPE_STANDARD 0
|
|
||||||
#define MAPPER_TYPE_XINPUT 1
|
|
||||||
|
|
||||||
#define IDX_BUTTON_A 0
|
|
||||||
#define IDX_BUTTON_B 1
|
|
||||||
#define IDX_BUTTON_X 2
|
|
||||||
#define IDX_BUTTON_Y 3
|
|
||||||
#define IDX_BUTTON_L1 4
|
|
||||||
#define IDX_BUTTON_R1 5
|
|
||||||
#define IDX_BUTTON_L2 10
|
|
||||||
#define IDX_BUTTON_R2 11
|
|
||||||
#define IDX_BUTTON_SELECT 6
|
|
||||||
#define IDX_BUTTON_START 7
|
|
||||||
#define IDX_BUTTON_L3 8
|
|
||||||
#define IDX_BUTTON_R3 9
|
|
||||||
|
|
||||||
struct gamepad_state
|
|
||||||
{
|
|
||||||
short buttons;
|
|
||||||
char dpad;
|
|
||||||
short thumb_lx;
|
|
||||||
short thumb_ly;
|
|
||||||
short thumb_rx;
|
|
||||||
short thumb_ry;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct gamepad
|
|
||||||
{
|
|
||||||
struct dinput_device base;
|
|
||||||
struct gamepad_state state;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct dinput_device_vtbl gamepad_vtbl;
|
|
||||||
static SOCKET server_sock = INVALID_SOCKET;
|
|
||||||
static BOOL winsock_loaded = FALSE;
|
|
||||||
static int connected_gamepad_id = 0;
|
|
||||||
static char mapper_type = MAPPER_TYPE_XINPUT;
|
|
||||||
|
|
||||||
static inline struct gamepad *impl_from_IDirectInputDevice8W( IDirectInputDevice8W *iface )
|
|
||||||
{
|
|
||||||
return CONTAINING_RECORD( CONTAINING_RECORD( iface, struct dinput_device, IDirectInputDevice8W_iface ), struct gamepad, base );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void close_server_socket( void )
|
|
||||||
{
|
|
||||||
if (server_sock != INVALID_SOCKET)
|
|
||||||
{
|
|
||||||
closesocket( server_sock );
|
|
||||||
server_sock = INVALID_SOCKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (winsock_loaded)
|
|
||||||
{
|
|
||||||
WSACleanup();
|
|
||||||
winsock_loaded = FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL create_server_socket( void )
|
|
||||||
{
|
|
||||||
WSADATA wsa_data;
|
|
||||||
struct sockaddr_in server_addr;
|
|
||||||
int res;
|
|
||||||
|
|
||||||
close_server_socket();
|
|
||||||
|
|
||||||
winsock_loaded = WSAStartup( MAKEWORD(2,2), &wsa_data ) == NO_ERROR;
|
|
||||||
if (!winsock_loaded) return FALSE;
|
|
||||||
|
|
||||||
server_addr.sin_family = AF_INET;
|
|
||||||
server_addr.sin_addr.s_addr = inet_addr( "127.0.0.1" );
|
|
||||||
server_addr.sin_port = htons( SERVER_PORT );
|
|
||||||
|
|
||||||
server_sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
|
|
||||||
if (server_sock == INVALID_SOCKET) return FALSE;
|
|
||||||
|
|
||||||
res = bind( server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr) );
|
|
||||||
if (res == SOCKET_ERROR) return FALSE;
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL get_gamepad_request( DIDEVICEINSTANCEW *instance, DWORD version )
|
|
||||||
{
|
|
||||||
int res, client_addr_len, gamepad_id, name_len;
|
|
||||||
char buffer[BUFFER_SIZE];
|
|
||||||
struct sockaddr_in client_addr;
|
|
||||||
DWORD size;
|
|
||||||
|
|
||||||
client_addr.sin_family = AF_INET;
|
|
||||||
client_addr.sin_addr.s_addr = inet_addr( "127.0.0.1" );
|
|
||||||
client_addr.sin_port = htons( CLIENT_PORT );
|
|
||||||
client_addr_len = sizeof(client_addr);
|
|
||||||
|
|
||||||
buffer[0] = REQUEST_CODE_GET_GAMEPAD;
|
|
||||||
buffer[1] = 0;
|
|
||||||
res = sendto( server_sock, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&client_addr, client_addr_len );
|
|
||||||
if (res == SOCKET_ERROR) return FALSE;
|
|
||||||
|
|
||||||
res = recvfrom( server_sock, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&client_addr, &client_addr_len );
|
|
||||||
if (res == SOCKET_ERROR || buffer[0] != REQUEST_CODE_GET_GAMEPAD) return FALSE;
|
|
||||||
|
|
||||||
memcpy( &gamepad_id, buffer + 1, 4 );
|
|
||||||
if (gamepad_id == 0)
|
|
||||||
{
|
|
||||||
connected_gamepad_id = 0;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
connected_gamepad_id = gamepad_id;
|
|
||||||
mapper_type = buffer[5];
|
|
||||||
|
|
||||||
memcpy( &name_len, buffer + 6, 4 );
|
|
||||||
|
|
||||||
char gamepad_name[name_len + 1];
|
|
||||||
gamepad_name[name_len] = '\0';
|
|
||||||
memcpy( gamepad_name, buffer + 10, name_len );
|
|
||||||
|
|
||||||
size = instance->dwSize;
|
|
||||||
memset( instance, 0, size );
|
|
||||||
instance->dwSize = size;
|
|
||||||
instance->guidInstance = GUID_Joystick;
|
|
||||||
instance->guidProduct = GUID_Joystick;
|
|
||||||
instance->guidProduct.Data1 = MAKELONG( 0x045e, 0x028e );
|
|
||||||
if (version >= 0x0800) instance->dwDevType = DIDEVTYPE_HID | DI8DEVTYPE_GAMEPAD | (DI8DEVTYPEGAMEPAD_STANDARD << 8);
|
|
||||||
else instance->dwDevType = DIDEVTYPE_HID | DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_GAMEPAD << 8);
|
|
||||||
instance->wUsagePage = HID_USAGE_PAGE_GENERIC;
|
|
||||||
instance->wUsage = HID_USAGE_GENERIC_GAMEPAD;
|
|
||||||
MultiByteToWideChar( CP_ACP, 0, gamepad_name, -1, instance->tszInstanceName, MAX_PATH );
|
|
||||||
MultiByteToWideChar( CP_ACP, 0, gamepad_name, -1, instance->tszProductName, MAX_PATH );
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static LONG scale_value( LONG value, struct object_properties *properties )
|
|
||||||
{
|
|
||||||
LONG log_min, log_max, phy_min, phy_max;
|
|
||||||
log_min = properties->logical_min;
|
|
||||||
log_max = properties->logical_max;
|
|
||||||
phy_min = properties->range_min;
|
|
||||||
phy_max = properties->range_max;
|
|
||||||
|
|
||||||
return phy_min + MulDiv( value - log_min, phy_max - phy_min, log_max - log_min );
|
|
||||||
}
|
|
||||||
|
|
||||||
static LONG scale_axis_value( LONG value, struct object_properties *properties )
|
|
||||||
{
|
|
||||||
LONG log_ctr, log_min, log_max, phy_ctr, phy_min, phy_max;
|
|
||||||
log_min = properties->logical_min;
|
|
||||||
log_max = properties->logical_max;
|
|
||||||
phy_min = properties->range_min;
|
|
||||||
phy_max = properties->range_max;
|
|
||||||
|
|
||||||
if (phy_min == 0) phy_ctr = phy_max >> 1;
|
|
||||||
else phy_ctr = round( (phy_min + phy_max) / 2.0 );
|
|
||||||
if (log_min == 0) log_ctr = log_max >> 1;
|
|
||||||
else log_ctr = round( (log_min + log_max) / 2.0 );
|
|
||||||
|
|
||||||
value -= log_ctr;
|
|
||||||
if (value <= 0)
|
|
||||||
{
|
|
||||||
log_max = MulDiv( log_min - log_ctr, properties->deadzone, 10000 );
|
|
||||||
log_min = MulDiv( log_min - log_ctr, properties->saturation, 10000 );
|
|
||||||
phy_max = phy_ctr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
log_min = MulDiv( log_max - log_ctr, properties->deadzone, 10000 );
|
|
||||||
log_max = MulDiv( log_max - log_ctr, properties->saturation, 10000 );
|
|
||||||
phy_min = phy_ctr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value <= log_min) return phy_min;
|
|
||||||
if (value >= log_max) return phy_max;
|
|
||||||
return phy_min + MulDiv( value - log_min, phy_max - phy_min, log_max - log_min );
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_gamepad_input( IDirectInputDevice8W *iface, short thumb_lx, short thumb_ly, short thumb_rx, short thumb_ry, short buttons, char dpad )
|
|
||||||
{
|
|
||||||
int i, j;
|
|
||||||
DWORD time, seq;
|
|
||||||
BOOL notify = FALSE;
|
|
||||||
struct gamepad *impl = impl_from_IDirectInputDevice8W( iface );
|
|
||||||
DIJOYSTATE *state = (DIJOYSTATE *)impl->base.device_state;
|
|
||||||
|
|
||||||
time = GetCurrentTime();
|
|
||||||
seq = impl->base.dinput->evsequence++;
|
|
||||||
|
|
||||||
if (mapper_type == MAPPER_TYPE_STANDARD)
|
|
||||||
{
|
|
||||||
if (thumb_lx != impl->state.thumb_lx)
|
|
||||||
{
|
|
||||||
impl->state.thumb_lx = thumb_lx;
|
|
||||||
state->lX = scale_axis_value(thumb_lx, &impl->base.object_properties[0]);
|
|
||||||
queue_event( iface, DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 0 ), state->lX, time, seq );
|
|
||||||
notify = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thumb_ly != impl->state.thumb_ly)
|
|
||||||
{
|
|
||||||
impl->state.thumb_ly = thumb_ly;
|
|
||||||
state->lY = scale_axis_value(thumb_ly, &impl->base.object_properties[1]);
|
|
||||||
queue_event( iface, DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 1 ), state->lY, time, seq );
|
|
||||||
notify = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thumb_rx != impl->state.thumb_rx)
|
|
||||||
{
|
|
||||||
impl->state.thumb_rx = thumb_rx;
|
|
||||||
state->lZ = scale_axis_value(thumb_rx, &impl->base.object_properties[2]);
|
|
||||||
queue_event( iface, DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 ), state->lZ, time, seq );
|
|
||||||
notify = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thumb_ry != impl->state.thumb_ry)
|
|
||||||
{
|
|
||||||
impl->state.thumb_ry = thumb_ry;
|
|
||||||
state->lRz = scale_axis_value(thumb_ry, &impl->base.object_properties[5]);
|
|
||||||
queue_event( iface, DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 3 ), state->lRz, time, seq );
|
|
||||||
notify = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buttons != impl->state.buttons)
|
|
||||||
{
|
|
||||||
impl->state.buttons = buttons;
|
|
||||||
for (i = 0, j = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
switch (i)
|
|
||||||
{
|
|
||||||
case IDX_BUTTON_A: j = 1; break;
|
|
||||||
case IDX_BUTTON_B: j = 2; break;
|
|
||||||
case IDX_BUTTON_X: j = 0; break;
|
|
||||||
case IDX_BUTTON_Y: j = 3; break;
|
|
||||||
case IDX_BUTTON_L1: j = 4; break;
|
|
||||||
case IDX_BUTTON_R1: j = 5; break;
|
|
||||||
case IDX_BUTTON_L2: j = 6; break;
|
|
||||||
case IDX_BUTTON_R2: j = 7; break;
|
|
||||||
case IDX_BUTTON_SELECT: j = 8; break;
|
|
||||||
case IDX_BUTTON_START: j = 9; break;
|
|
||||||
case IDX_BUTTON_L3: j = 10; break;
|
|
||||||
case IDX_BUTTON_R3: j = 11; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->rgbButtons[j] = (buttons & (1<<i)) ? 0x80 : 0x00;
|
|
||||||
queue_event( iface, DIDFT_BUTTON | DIDFT_MAKEINSTANCE( j ), state->rgbButtons[j], time, seq );
|
|
||||||
}
|
|
||||||
notify = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dpad != impl->state.dpad)
|
|
||||||
{
|
|
||||||
impl->state.dpad = dpad;
|
|
||||||
state->rgdwPOV[0] = dpad != -1 ? dpad * 4500 : -1;
|
|
||||||
queue_event( iface, DIDFT_POV | DIDFT_MAKEINSTANCE( 0 ), state->rgdwPOV[0], time, seq );
|
|
||||||
notify = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (mapper_type == MAPPER_TYPE_XINPUT)
|
|
||||||
{
|
|
||||||
if (thumb_lx != impl->state.thumb_lx)
|
|
||||||
{
|
|
||||||
impl->state.thumb_lx = thumb_lx;
|
|
||||||
state->lX = scale_axis_value(thumb_lx, &impl->base.object_properties[0]);
|
|
||||||
queue_event( iface, DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 0 ), state->lX, time, seq );
|
|
||||||
notify = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thumb_ly != impl->state.thumb_ly)
|
|
||||||
{
|
|
||||||
impl->state.thumb_ly = thumb_ly;
|
|
||||||
state->lY = scale_axis_value(thumb_ly, &impl->base.object_properties[1]);
|
|
||||||
queue_event( iface, DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 1 ), state->lY, time, seq );
|
|
||||||
notify = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thumb_rx != impl->state.thumb_rx)
|
|
||||||
{
|
|
||||||
impl->state.thumb_rx = thumb_rx;
|
|
||||||
state->lRx = scale_axis_value(thumb_rx, &impl->base.object_properties[3]);
|
|
||||||
queue_event( iface, DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 ), state->lRx, time, seq );
|
|
||||||
notify = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thumb_ry != impl->state.thumb_ry)
|
|
||||||
{
|
|
||||||
impl->state.thumb_ry = thumb_ry;
|
|
||||||
state->lRy = scale_axis_value(thumb_ry, &impl->base.object_properties[4]);
|
|
||||||
queue_event( iface, DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 4 ), state->lRy, time, seq );
|
|
||||||
notify = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buttons != impl->state.buttons)
|
|
||||||
{
|
|
||||||
impl->state.buttons = buttons;
|
|
||||||
for (i = 0; i < 10; i++)
|
|
||||||
{
|
|
||||||
state->rgbButtons[i] = (buttons & (1<<i)) ? 0x80 : 0x00;
|
|
||||||
queue_event( iface, DIDFT_BUTTON | DIDFT_MAKEINSTANCE( i ), state->rgbButtons[i], time, seq );
|
|
||||||
}
|
|
||||||
|
|
||||||
state->lZ = scale_value((buttons & (1<<10)) ? 32767 : ((buttons & (1<<11)) ? -32768 : 0), &impl->base.object_properties[2]);
|
|
||||||
queue_event( iface, DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 ), state->lZ, time, seq );
|
|
||||||
notify = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dpad != impl->state.dpad)
|
|
||||||
{
|
|
||||||
impl->state.dpad = dpad;
|
|
||||||
state->rgdwPOV[0] = dpad != -1 ? dpad * 4500 : -1;
|
|
||||||
queue_event( iface, DIDFT_POV | DIDFT_MAKEINSTANCE( 0 ), state->rgdwPOV[0], time, seq );
|
|
||||||
notify = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (notify && impl->base.hEvent) SetEvent( impl->base.hEvent );
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL get_gamepad_state_request( IDirectInputDevice8W *iface )
|
|
||||||
{
|
|
||||||
char buffer[BUFFER_SIZE];
|
|
||||||
int res, client_addr_len, gamepad_id;
|
|
||||||
char dpad;
|
|
||||||
short buttons, thumb_lx, thumb_ly, thumb_rx, thumb_ry;
|
|
||||||
struct sockaddr_in client_addr;
|
|
||||||
|
|
||||||
client_addr.sin_family = AF_INET;
|
|
||||||
client_addr.sin_addr.s_addr = inet_addr( "127.0.0.1" );
|
|
||||||
client_addr.sin_port = htons( CLIENT_PORT );
|
|
||||||
client_addr_len = sizeof(client_addr);
|
|
||||||
|
|
||||||
buffer[0] = REQUEST_CODE_GET_GAMEPAD_STATE;
|
|
||||||
memcpy( buffer + 1, &connected_gamepad_id, 4 );
|
|
||||||
|
|
||||||
res = sendto( server_sock, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&client_addr, client_addr_len );
|
|
||||||
if (res == SOCKET_ERROR) return FALSE;
|
|
||||||
|
|
||||||
res = recvfrom( server_sock, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&client_addr, &client_addr_len );
|
|
||||||
if (res == SOCKET_ERROR || buffer[0] != REQUEST_CODE_GET_GAMEPAD_STATE || buffer[1] != 1) return FALSE;
|
|
||||||
|
|
||||||
memcpy( &gamepad_id, buffer + 2, 4 );
|
|
||||||
if (gamepad_id != connected_gamepad_id) return FALSE;
|
|
||||||
|
|
||||||
memcpy( &buttons, buffer + 6, 2 );
|
|
||||||
|
|
||||||
dpad = buffer[8];
|
|
||||||
|
|
||||||
memcpy( &thumb_lx, buffer + 9, 2 );
|
|
||||||
memcpy( &thumb_ly, buffer + 11, 2 );
|
|
||||||
memcpy( &thumb_rx, buffer + 13, 2 );
|
|
||||||
memcpy( &thumb_ry, buffer + 15, 2 );
|
|
||||||
|
|
||||||
handle_gamepad_input( iface, thumb_lx, thumb_ly, thumb_rx, thumb_ry, buttons, dpad );
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void release_gamepad_request( void )
|
|
||||||
{
|
|
||||||
char buffer[BUFFER_SIZE];
|
|
||||||
struct sockaddr_in client_addr;
|
|
||||||
int client_addr_len;
|
|
||||||
|
|
||||||
client_addr.sin_family = AF_INET;
|
|
||||||
client_addr.sin_addr.s_addr = inet_addr( "127.0.0.1" );
|
|
||||||
client_addr.sin_port = htons( CLIENT_PORT );
|
|
||||||
client_addr_len = sizeof(client_addr);
|
|
||||||
|
|
||||||
buffer[0] = REQUEST_CODE_RELEASE_GAMEPAD;
|
|
||||||
sendto( server_sock, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&client_addr, client_addr_len );
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT gamepad_enum_device( DWORD type, DWORD flags, DIDEVICEINSTANCEW *instance, DWORD version )
|
|
||||||
{
|
|
||||||
if (!create_server_socket()) return DIERR_INPUTLOST;
|
|
||||||
return get_gamepad_request( instance, version ) ? DI_OK : DIERR_INPUTLOST;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL CALLBACK init_object_properties( const DIDEVICEOBJECTINSTANCEW *instance, void *data )
|
|
||||||
{
|
|
||||||
struct gamepad *impl = (struct gamepad *)data;
|
|
||||||
struct object_properties *properties = impl->base.object_properties + instance->dwOfs / sizeof(LONG);
|
|
||||||
|
|
||||||
properties->logical_min = -32768;
|
|
||||||
properties->logical_max = 32767;
|
|
||||||
properties->range_min = 0;
|
|
||||||
properties->range_max = 65535;
|
|
||||||
properties->saturation = 10000;
|
|
||||||
properties->granularity = 1;
|
|
||||||
|
|
||||||
return DIENUM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT gamepad_create_device( struct dinput *dinput, const GUID *guid, IDirectInputDevice8W **out )
|
|
||||||
{
|
|
||||||
struct gamepad *impl;
|
|
||||||
|
|
||||||
*out = NULL;
|
|
||||||
if (!IsEqualGUID( &GUID_Joystick, guid )) return DIERR_DEVICENOTREG;
|
|
||||||
|
|
||||||
if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY;
|
|
||||||
dinput_device_init( &impl->base, &gamepad_vtbl, guid, dinput );
|
|
||||||
impl->base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": struct gamepad*->base.crit");
|
|
||||||
impl->base.read_event = CreateEventW( NULL, TRUE, FALSE, NULL );
|
|
||||||
|
|
||||||
gamepad_enum_device( 0, 0, &impl->base.instance, dinput->dwVersion );
|
|
||||||
impl->base.caps.dwDevType = impl->base.instance.dwDevType;
|
|
||||||
impl->base.caps.dwFirmwareRevision = 100;
|
|
||||||
impl->base.caps.dwHardwareRevision = 100;
|
|
||||||
impl->base.dwCoopLevel = DISCL_NONEXCLUSIVE | DISCL_BACKGROUND;
|
|
||||||
|
|
||||||
impl->base.object_properties = calloc( 6, sizeof(struct object_properties) );
|
|
||||||
if (!impl->base.object_properties)
|
|
||||||
{
|
|
||||||
IDirectInputDevice_Release( &impl->base.IDirectInputDevice8W_iface );
|
|
||||||
return E_OUTOFMEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
IDirectInputDevice8_EnumObjects( &impl->base.IDirectInputDevice8W_iface, init_object_properties, impl, DIDFT_ABSAXIS );
|
|
||||||
|
|
||||||
if (dinput->dwVersion >= 0x0800)
|
|
||||||
{
|
|
||||||
impl->base.raw_device.usUsagePage = HID_USAGE_PAGE_GENERIC;
|
|
||||||
impl->base.raw_device.usUsage = HID_USAGE_GENERIC_GAMEPAD;
|
|
||||||
}
|
|
||||||
|
|
||||||
*out = &impl->base.IDirectInputDevice8W_iface;
|
|
||||||
return DI_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gamepad_release( IDirectInputDevice8W *iface )
|
|
||||||
{
|
|
||||||
struct gamepad *impl = impl_from_IDirectInputDevice8W( iface );
|
|
||||||
CloseHandle( impl->base.read_event );
|
|
||||||
}
|
|
||||||
|
|
||||||
static HRESULT gamepad_read( IDirectInputDevice8W *iface )
|
|
||||||
{
|
|
||||||
get_gamepad_state_request( iface );
|
|
||||||
Sleep( 16 );
|
|
||||||
return DI_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HRESULT gamepad_acquire( IDirectInputDevice8W *iface )
|
|
||||||
{
|
|
||||||
struct gamepad *impl = impl_from_IDirectInputDevice8W( iface );
|
|
||||||
SetEvent( impl->base.read_event );
|
|
||||||
return DI_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HRESULT gamepad_unacquire( IDirectInputDevice8W *iface )
|
|
||||||
{
|
|
||||||
struct gamepad *impl = impl_from_IDirectInputDevice8W( iface );
|
|
||||||
WaitForSingleObject( impl->base.read_event, INFINITE );
|
|
||||||
|
|
||||||
connected_gamepad_id = 0;
|
|
||||||
release_gamepad_request();
|
|
||||||
close_server_socket();
|
|
||||||
return DI_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL try_enum_object( const DIPROPHEADER *filter, DWORD flags, LPDIENUMDEVICEOBJECTSCALLBACKW callback,
|
|
||||||
DIDEVICEOBJECTINSTANCEW *instance, void *data )
|
|
||||||
{
|
|
||||||
if (flags != DIDFT_ALL && !(flags & DIDFT_GETTYPE( instance->dwType ))) return DIENUM_CONTINUE;
|
|
||||||
|
|
||||||
switch (filter->dwHow)
|
|
||||||
{
|
|
||||||
case DIPH_DEVICE:
|
|
||||||
return callback( instance, data );
|
|
||||||
case DIPH_BYOFFSET:
|
|
||||||
if (filter->dwObj != instance->dwOfs) return DIENUM_CONTINUE;
|
|
||||||
return callback( instance, data );
|
|
||||||
case DIPH_BYID:
|
|
||||||
if ((filter->dwObj & 0x00ffffff) != (instance->dwType & 0x00ffffff)) return DIENUM_CONTINUE;
|
|
||||||
return callback( instance, data );
|
|
||||||
}
|
|
||||||
|
|
||||||
return DIENUM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void get_device_objects( int *instance_count, DIDEVICEOBJECTINSTANCEW **out )
|
|
||||||
{
|
|
||||||
int i, index = 0;
|
|
||||||
|
|
||||||
*instance_count = 0;
|
|
||||||
*out = NULL;
|
|
||||||
|
|
||||||
if (mapper_type == MAPPER_TYPE_STANDARD)
|
|
||||||
{
|
|
||||||
*instance_count = 17;
|
|
||||||
DIDEVICEOBJECTINSTANCEW instances[*instance_count];
|
|
||||||
|
|
||||||
instances[index].guidType = GUID_XAxis;
|
|
||||||
instances[index].dwOfs = DIJOFS_X;
|
|
||||||
instances[index].dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 0 );
|
|
||||||
instances[index].dwFlags = DIDOI_ASPECTPOSITION;
|
|
||||||
swprintf( instances[index].tszName, MAX_PATH, L"X Axis" );
|
|
||||||
instances[index].wUsagePage = HID_USAGE_PAGE_GENERIC;
|
|
||||||
instances[index].wUsage = HID_USAGE_GENERIC_X;
|
|
||||||
index++;
|
|
||||||
|
|
||||||
instances[index].guidType = GUID_YAxis;
|
|
||||||
instances[index].dwOfs = DIJOFS_Y;
|
|
||||||
instances[index].dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 1 );
|
|
||||||
instances[index].dwFlags = DIDOI_ASPECTPOSITION;
|
|
||||||
swprintf( instances[index].tszName, MAX_PATH, L"Y Axis" );
|
|
||||||
instances[index].wUsagePage = HID_USAGE_PAGE_GENERIC;
|
|
||||||
instances[index].wUsage = HID_USAGE_GENERIC_Y;
|
|
||||||
index++;
|
|
||||||
|
|
||||||
instances[index].guidType = GUID_ZAxis;
|
|
||||||
instances[index].dwOfs = DIJOFS_Z;
|
|
||||||
instances[index].dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 );
|
|
||||||
instances[index].dwFlags = DIDOI_ASPECTPOSITION;
|
|
||||||
swprintf( instances[index].tszName, MAX_PATH, L"Z Axis" );
|
|
||||||
instances[index].wUsagePage = HID_USAGE_PAGE_GENERIC;
|
|
||||||
instances[index].wUsage = HID_USAGE_GENERIC_Z;
|
|
||||||
index++;
|
|
||||||
|
|
||||||
instances[index].guidType = GUID_RzAxis;
|
|
||||||
instances[index].dwOfs = DIJOFS_RZ;
|
|
||||||
instances[index].dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 3 );
|
|
||||||
instances[index].dwFlags = DIDOI_ASPECTPOSITION;
|
|
||||||
swprintf( instances[index].tszName, MAX_PATH, L"Rz Axis" );
|
|
||||||
instances[index].wUsagePage = HID_USAGE_PAGE_GENERIC;
|
|
||||||
instances[index].wUsage = HID_USAGE_GENERIC_RZ;
|
|
||||||
index++;
|
|
||||||
|
|
||||||
for (i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
instances[index].guidType = GUID_Button,
|
|
||||||
instances[index].dwOfs = DIJOFS_BUTTON( i ),
|
|
||||||
instances[index].dwType = DIDFT_BUTTON | DIDFT_MAKEINSTANCE( i ),
|
|
||||||
swprintf( instances[index].tszName, MAX_PATH, L"Button %d", i );
|
|
||||||
instances[index].wUsagePage = HID_USAGE_PAGE_BUTTON;
|
|
||||||
instances[index].wUsage = i + 1;
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
instances[index].guidType = GUID_POV;
|
|
||||||
instances[index].dwOfs = DIJOFS_POV( 0 );
|
|
||||||
instances[index].dwType = DIDFT_POV | DIDFT_MAKEINSTANCE( 0 );
|
|
||||||
swprintf( instances[index].tszName, MAX_PATH, L"POV" );
|
|
||||||
instances[index].wUsagePage = HID_USAGE_PAGE_GENERIC;
|
|
||||||
instances[index].wUsage = HID_USAGE_GENERIC_HATSWITCH;
|
|
||||||
|
|
||||||
*out = instances;
|
|
||||||
}
|
|
||||||
else if (mapper_type == MAPPER_TYPE_XINPUT)
|
|
||||||
{
|
|
||||||
*instance_count = 16;
|
|
||||||
DIDEVICEOBJECTINSTANCEW instances[*instance_count];
|
|
||||||
|
|
||||||
instances[index].guidType = GUID_XAxis;
|
|
||||||
instances[index].dwOfs = DIJOFS_X;
|
|
||||||
instances[index].dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 0 );
|
|
||||||
instances[index].dwFlags = DIDOI_ASPECTPOSITION;
|
|
||||||
swprintf( instances[index].tszName, MAX_PATH, L"X Axis" );
|
|
||||||
instances[index].wUsagePage = HID_USAGE_PAGE_GENERIC;
|
|
||||||
instances[index].wUsage = HID_USAGE_GENERIC_X;
|
|
||||||
index++;
|
|
||||||
|
|
||||||
instances[index].guidType = GUID_YAxis;
|
|
||||||
instances[index].dwOfs = DIJOFS_Y;
|
|
||||||
instances[index].dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 1 );
|
|
||||||
instances[index].dwFlags = DIDOI_ASPECTPOSITION;
|
|
||||||
swprintf( instances[index].tszName, MAX_PATH, L"Y Axis" );
|
|
||||||
instances[index].wUsagePage = HID_USAGE_PAGE_GENERIC;
|
|
||||||
instances[index].wUsage = HID_USAGE_GENERIC_Y;
|
|
||||||
index++;
|
|
||||||
|
|
||||||
instances[index].guidType = GUID_ZAxis;
|
|
||||||
instances[index].dwOfs = DIJOFS_Z;
|
|
||||||
instances[index].dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 2 );
|
|
||||||
instances[index].dwFlags = DIDOI_ASPECTPOSITION;
|
|
||||||
swprintf( instances[index].tszName, MAX_PATH, L"Z Axis" );
|
|
||||||
instances[index].wUsagePage = HID_USAGE_PAGE_GENERIC;
|
|
||||||
instances[index].wUsage = HID_USAGE_GENERIC_Z;
|
|
||||||
index++;
|
|
||||||
|
|
||||||
instances[index].guidType = GUID_RxAxis;
|
|
||||||
instances[index].dwOfs = DIJOFS_RX;
|
|
||||||
instances[index].dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 3 );
|
|
||||||
instances[index].dwFlags = DIDOI_ASPECTPOSITION;
|
|
||||||
swprintf( instances[index].tszName, MAX_PATH, L"Rx Axis" );
|
|
||||||
instances[index].wUsagePage = HID_USAGE_PAGE_GENERIC;
|
|
||||||
instances[index].wUsage = HID_USAGE_GENERIC_RX;
|
|
||||||
index++;
|
|
||||||
|
|
||||||
instances[index].guidType = GUID_RyAxis;
|
|
||||||
instances[index].dwOfs = DIJOFS_RY;
|
|
||||||
instances[index].dwType = DIDFT_ABSAXIS | DIDFT_MAKEINSTANCE( 4 );
|
|
||||||
instances[index].dwFlags = DIDOI_ASPECTPOSITION;
|
|
||||||
swprintf( instances[index].tszName, MAX_PATH, L"Ry Axis" );
|
|
||||||
instances[index].wUsagePage = HID_USAGE_PAGE_GENERIC;
|
|
||||||
instances[index].wUsage = HID_USAGE_GENERIC_RY;
|
|
||||||
index++;
|
|
||||||
|
|
||||||
for (i = 0; i < 10; i++)
|
|
||||||
{
|
|
||||||
instances[index].guidType = GUID_Button,
|
|
||||||
instances[index].dwOfs = DIJOFS_BUTTON( i ),
|
|
||||||
instances[index].dwType = DIDFT_BUTTON | DIDFT_MAKEINSTANCE( i ),
|
|
||||||
swprintf( instances[index].tszName, MAX_PATH, L"Button %d", i );
|
|
||||||
instances[index].wUsagePage = HID_USAGE_PAGE_BUTTON;
|
|
||||||
instances[index].wUsage = i + 1;
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
instances[index].guidType = GUID_POV;
|
|
||||||
instances[index].dwOfs = DIJOFS_POV( 0 );
|
|
||||||
instances[index].dwType = DIDFT_POV | DIDFT_MAKEINSTANCE( 0 );
|
|
||||||
swprintf( instances[index].tszName, MAX_PATH, L"POV" );
|
|
||||||
instances[index].wUsagePage = HID_USAGE_PAGE_GENERIC;
|
|
||||||
instances[index].wUsage = HID_USAGE_GENERIC_HATSWITCH;
|
|
||||||
|
|
||||||
*out = instances;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static HRESULT gamepad_enum_objects( IDirectInputDevice8W *iface, const DIPROPHEADER *filter,
|
|
||||||
DWORD flags, LPDIENUMDEVICEOBJECTSCALLBACKW callback, void *context )
|
|
||||||
{
|
|
||||||
int instance_count;
|
|
||||||
DIDEVICEOBJECTINSTANCEW* instances;
|
|
||||||
BOOL ret;
|
|
||||||
DWORD i;
|
|
||||||
|
|
||||||
get_device_objects( &instance_count, &instances );
|
|
||||||
|
|
||||||
for (i = 0; i < instance_count; i++)
|
|
||||||
{
|
|
||||||
instances[i].dwSize = sizeof(DIDEVICEOBJECTINSTANCEW);
|
|
||||||
instances[i].wReportId = 1;
|
|
||||||
|
|
||||||
ret = try_enum_object( filter, flags, callback, instances + i, context );
|
|
||||||
if (ret != DIENUM_CONTINUE) return DIENUM_STOP;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DIENUM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HRESULT gamepad_get_property(IDirectInputDevice8W *iface, DWORD property,
|
|
||||||
DIPROPHEADER *header, const DIDEVICEOBJECTINSTANCEW *instance)
|
|
||||||
{
|
|
||||||
struct gamepad *impl = impl_from_IDirectInputDevice8W( iface );
|
|
||||||
|
|
||||||
switch (property)
|
|
||||||
{
|
|
||||||
case (DWORD_PTR)DIPROP_PRODUCTNAME:
|
|
||||||
{
|
|
||||||
DIPROPSTRING *value = (DIPROPSTRING *)header;
|
|
||||||
lstrcpynW( value->wsz, impl->base.instance.tszProductName, MAX_PATH );
|
|
||||||
return DI_OK;
|
|
||||||
}
|
|
||||||
case (DWORD_PTR)DIPROP_INSTANCENAME:
|
|
||||||
{
|
|
||||||
DIPROPSTRING *value = (DIPROPSTRING *)header;
|
|
||||||
lstrcpynW( value->wsz, impl->base.instance.tszInstanceName, MAX_PATH );
|
|
||||||
return DI_OK;
|
|
||||||
}
|
|
||||||
case (DWORD_PTR)DIPROP_VIDPID:
|
|
||||||
{
|
|
||||||
DIPROPDWORD *value = (DIPROPDWORD *)header;
|
|
||||||
value->dwData = MAKELONG( 0x045e, 0x028e );
|
|
||||||
return DI_OK;
|
|
||||||
}
|
|
||||||
case (DWORD_PTR)DIPROP_JOYSTICKID:
|
|
||||||
{
|
|
||||||
DIPROPDWORD *value = (DIPROPDWORD *)header;
|
|
||||||
value->dwData = connected_gamepad_id;
|
|
||||||
return DI_OK;
|
|
||||||
}
|
|
||||||
case (DWORD_PTR)DIPROP_GUIDANDPATH:
|
|
||||||
{
|
|
||||||
DIPROPGUIDANDPATH *value = (DIPROPGUIDANDPATH *)header;
|
|
||||||
value->guidClass = GUID_DEVCLASS_HIDCLASS;
|
|
||||||
lstrcpynW( value->wszPath, L"virtual#vid_045e&pid_028e&ig_00", MAX_PATH );
|
|
||||||
return DI_OK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return DIERR_UNSUPPORTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct dinput_device_vtbl gamepad_vtbl =
|
|
||||||
{
|
|
||||||
gamepad_release,
|
|
||||||
NULL,
|
|
||||||
gamepad_read,
|
|
||||||
gamepad_acquire,
|
|
||||||
gamepad_unacquire,
|
|
||||||
gamepad_enum_objects,
|
|
||||||
gamepad_get_property,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
};
|
|
||||||
@@ -1,195 +0,0 @@
|
|||||||
/*
|
|
||||||
* Desktop window class.
|
|
||||||
*
|
|
||||||
* Copyright 1994 Alexandre Julliard
|
|
||||||
*
|
|
||||||
* 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 <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "windef.h"
|
|
||||||
#include "winbase.h"
|
|
||||||
#include "wingdi.h"
|
|
||||||
#include "winnls.h"
|
|
||||||
#include "controls.h"
|
|
||||||
|
|
||||||
static HBRUSH hbrushPattern;
|
|
||||||
static HBITMAP hbitmapWallPaper;
|
|
||||||
static SIZE bitmapSize;
|
|
||||||
static BOOL fTileWallPaper;
|
|
||||||
|
|
||||||
|
|
||||||
/***********************************************************************
|
|
||||||
* DESKTOP_LoadBitmap
|
|
||||||
*/
|
|
||||||
static HBITMAP DESKTOP_LoadBitmap( const WCHAR *filename )
|
|
||||||
{
|
|
||||||
HBITMAP hbitmap;
|
|
||||||
|
|
||||||
if (!filename[0]) return 0;
|
|
||||||
hbitmap = LoadImageW( 0, filename, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE );
|
|
||||||
if (!hbitmap)
|
|
||||||
{
|
|
||||||
WCHAR buffer[MAX_PATH];
|
|
||||||
UINT len = GetWindowsDirectoryW( buffer, MAX_PATH - 2 );
|
|
||||||
if (buffer[len - 1] != '\\') buffer[len++] = '\\';
|
|
||||||
lstrcpynW( buffer + len, filename, MAX_PATH - len );
|
|
||||||
hbitmap = LoadImageW( 0, buffer, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE );
|
|
||||||
}
|
|
||||||
return hbitmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***********************************************************************
|
|
||||||
* init_wallpaper
|
|
||||||
*/
|
|
||||||
static void init_wallpaper( const WCHAR *wallpaper )
|
|
||||||
{
|
|
||||||
HBITMAP hbitmap = DESKTOP_LoadBitmap( wallpaper );
|
|
||||||
|
|
||||||
if (hbitmapWallPaper) DeleteObject( hbitmapWallPaper );
|
|
||||||
hbitmapWallPaper = hbitmap;
|
|
||||||
if (hbitmap)
|
|
||||||
{
|
|
||||||
BITMAP bmp;
|
|
||||||
GetObjectA( hbitmap, sizeof(bmp), &bmp );
|
|
||||||
bitmapSize.cx = (bmp.bmWidth != 0) ? bmp.bmWidth : 1;
|
|
||||||
bitmapSize.cy = (bmp.bmHeight != 0) ? bmp.bmHeight : 1;
|
|
||||||
fTileWallPaper = GetProfileIntA( "desktop", "TileWallPaper", 0 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/***********************************************************************
|
|
||||||
* DesktopWndProc
|
|
||||||
*/
|
|
||||||
LRESULT WINAPI DesktopWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
|
|
||||||
{
|
|
||||||
switch (message)
|
|
||||||
{
|
|
||||||
case WM_NCCREATE:
|
|
||||||
case WM_NCCALCSIZE:
|
|
||||||
case WM_PARENTNOTIFY:
|
|
||||||
case WM_DISPLAYCHANGE:
|
|
||||||
return NtUserMessageCall( hwnd, message, wParam, lParam, 0, NtUserDesktopWindowProc, FALSE );
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (message < WM_USER)
|
|
||||||
return DefWindowProcW( hwnd, message, wParam, lParam );
|
|
||||||
return NtUserMessageCall( hwnd, message, wParam, lParam, 0, NtUserDesktopWindowProc, FALSE );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/***********************************************************************
|
|
||||||
* PaintDesktop (USER32.@)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
BOOL WINAPI PaintDesktop(HDC hdc)
|
|
||||||
{
|
|
||||||
HWND hwnd = GetDesktopWindow();
|
|
||||||
|
|
||||||
/* check for an owning thread; otherwise don't paint anything (non-desktop mode) */
|
|
||||||
if (GetWindowThreadProcessId( hwnd, NULL ))
|
|
||||||
{
|
|
||||||
RECT rect;
|
|
||||||
|
|
||||||
GetClientRect( hwnd, &rect );
|
|
||||||
|
|
||||||
/* Paint desktop pattern (only if wall paper does not cover everything) */
|
|
||||||
|
|
||||||
if (!hbitmapWallPaper ||
|
|
||||||
(!fTileWallPaper && ((bitmapSize.cx < rect.right) || (bitmapSize.cy < rect.bottom))))
|
|
||||||
{
|
|
||||||
HBRUSH brush = hbrushPattern;
|
|
||||||
if (!brush) brush = (HBRUSH)GetClassLongPtrW( hwnd, GCLP_HBRBACKGROUND );
|
|
||||||
/* Set colors in case pattern is a monochrome bitmap */
|
|
||||||
SetBkColor( hdc, RGB(0,0,0) );
|
|
||||||
SetTextColor( hdc, GetSysColor(COLOR_BACKGROUND) );
|
|
||||||
FillRect( hdc, &rect, brush );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Paint wall paper */
|
|
||||||
|
|
||||||
if (hbitmapWallPaper)
|
|
||||||
{
|
|
||||||
INT x, y;
|
|
||||||
HDC hMemDC = CreateCompatibleDC( hdc );
|
|
||||||
|
|
||||||
SelectObject( hMemDC, hbitmapWallPaper );
|
|
||||||
|
|
||||||
if (fTileWallPaper)
|
|
||||||
{
|
|
||||||
for (y = 0; y < rect.bottom; y += bitmapSize.cy)
|
|
||||||
for (x = 0; x < rect.right; x += bitmapSize.cx)
|
|
||||||
BitBlt( hdc, x, y, bitmapSize.cx, bitmapSize.cy, hMemDC, 0, 0, SRCCOPY );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SIZE deskSize;
|
|
||||||
deskSize.cx = rect.right - rect.left;
|
|
||||||
deskSize.cy = rect.bottom - rect.top;
|
|
||||||
|
|
||||||
StretchBlt( hdc, 0, 0, deskSize.cx, deskSize.cy, hMemDC, 0, 0, bitmapSize.cx, bitmapSize.cy, SRCCOPY );
|
|
||||||
}
|
|
||||||
DeleteDC( hMemDC );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***********************************************************************
|
|
||||||
* SetDeskWallPaper (USER32.@)
|
|
||||||
*
|
|
||||||
* FIXME: is there a unicode version?
|
|
||||||
*/
|
|
||||||
BOOL WINAPI SetDeskWallPaper( LPCSTR filename )
|
|
||||||
{
|
|
||||||
return SystemParametersInfoA( SPI_SETDESKWALLPAPER, MAX_PATH, (void *)filename, SPIF_UPDATEINIFILE );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***********************************************************************
|
|
||||||
* update_wallpaper
|
|
||||||
*/
|
|
||||||
BOOL update_wallpaper( const WCHAR *wallpaper, const WCHAR *pattern )
|
|
||||||
{
|
|
||||||
int pat[8];
|
|
||||||
|
|
||||||
if (hbrushPattern) DeleteObject( hbrushPattern );
|
|
||||||
hbrushPattern = 0;
|
|
||||||
memset( pat, 0, sizeof(pat) );
|
|
||||||
if (pattern)
|
|
||||||
{
|
|
||||||
char buffer[64];
|
|
||||||
WideCharToMultiByte( CP_ACP, 0, pattern, -1, buffer, sizeof(buffer), NULL, NULL );
|
|
||||||
if (sscanf( buffer, " %d %d %d %d %d %d %d %d",
|
|
||||||
&pat[0], &pat[1], &pat[2], &pat[3],
|
|
||||||
&pat[4], &pat[5], &pat[6], &pat[7] ))
|
|
||||||
{
|
|
||||||
WORD ptrn[8];
|
|
||||||
HBITMAP hbitmap;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < 8; i++) ptrn[i] = pat[i] & 0xffff;
|
|
||||||
hbitmap = CreateBitmap( 8, 8, 1, 1, ptrn );
|
|
||||||
hbrushPattern = CreatePatternBrush( hbitmap );
|
|
||||||
DeleteObject( hbitmap );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
init_wallpaper( wallpaper );
|
|
||||||
NtUserRedrawWindow( GetDesktopWindow(), 0, 0, RDW_INVALIDATE | RDW_ERASE | RDW_NOCHILDREN );
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
@@ -1,533 +0,0 @@
|
|||||||
/*
|
|
||||||
* The Wine project - Xinput Joystick Library
|
|
||||||
* Copyright 2008 Andrew Fenn
|
|
||||||
* Copyright 2018 Aric Stewart
|
|
||||||
* Copyright 2021 Rémi Bernon for CodeWeavers
|
|
||||||
* Copyright 2024 BrunoSX
|
|
||||||
*
|
|
||||||
* 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 <assert.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "windef.h"
|
|
||||||
#include "winbase.h"
|
|
||||||
#include "winerror.h"
|
|
||||||
#include "winuser.h"
|
|
||||||
#include "winreg.h"
|
|
||||||
#include "wingdi.h"
|
|
||||||
#include "winnls.h"
|
|
||||||
#include "winternl.h"
|
|
||||||
#include "winsock2.h"
|
|
||||||
|
|
||||||
#include "dbt.h"
|
|
||||||
#include "setupapi.h"
|
|
||||||
#include "initguid.h"
|
|
||||||
#include "devguid.h"
|
|
||||||
#include "xinput.h"
|
|
||||||
|
|
||||||
#include "wine/debug.h"
|
|
||||||
|
|
||||||
/* Not defined in the headers, used only by XInputGetStateEx */
|
|
||||||
#define XINPUT_GAMEPAD_GUIDE 0x0400
|
|
||||||
|
|
||||||
#define SERVER_PORT 7949
|
|
||||||
#define CLIENT_PORT 7947
|
|
||||||
#define BUFFER_SIZE 64
|
|
||||||
|
|
||||||
#define REQUEST_CODE_GET_GAMEPAD 8
|
|
||||||
#define REQUEST_CODE_GET_GAMEPAD_STATE 9
|
|
||||||
#define REQUEST_CODE_RELEASE_GAMEPAD 10
|
|
||||||
|
|
||||||
#define IDX_BUTTON_A 0
|
|
||||||
#define IDX_BUTTON_B 1
|
|
||||||
#define IDX_BUTTON_X 2
|
|
||||||
#define IDX_BUTTON_Y 3
|
|
||||||
#define IDX_BUTTON_L1 4
|
|
||||||
#define IDX_BUTTON_R1 5
|
|
||||||
#define IDX_BUTTON_L2 10
|
|
||||||
#define IDX_BUTTON_R2 11
|
|
||||||
#define IDX_BUTTON_SELECT 6
|
|
||||||
#define IDX_BUTTON_START 7
|
|
||||||
#define IDX_BUTTON_L3 8
|
|
||||||
#define IDX_BUTTON_R3 9
|
|
||||||
|
|
||||||
WINE_DEFAULT_DEBUG_CHANNEL(xinput);
|
|
||||||
|
|
||||||
struct xinput_controller
|
|
||||||
{
|
|
||||||
XINPUT_CAPABILITIES caps;
|
|
||||||
XINPUT_STATE state;
|
|
||||||
BOOL enabled;
|
|
||||||
BOOL connected;
|
|
||||||
int id;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct xinput_controller controller =
|
|
||||||
{
|
|
||||||
.enabled = FALSE,
|
|
||||||
.connected = FALSE,
|
|
||||||
.id = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
static HMODULE xinput_instance;
|
|
||||||
static HANDLE start_event;
|
|
||||||
static HANDLE stop_event;
|
|
||||||
static HANDLE done_event;
|
|
||||||
static HANDLE update_event;
|
|
||||||
|
|
||||||
static SOCKET server_sock = INVALID_SOCKET;
|
|
||||||
static BOOL winsock_loaded = FALSE;
|
|
||||||
static char xinput_min_index = 3;
|
|
||||||
|
|
||||||
static void close_server_socket(void)
|
|
||||||
{
|
|
||||||
if (server_sock != INVALID_SOCKET)
|
|
||||||
{
|
|
||||||
closesocket(server_sock);
|
|
||||||
server_sock = INVALID_SOCKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (winsock_loaded)
|
|
||||||
{
|
|
||||||
WSACleanup();
|
|
||||||
winsock_loaded = FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL create_server_socket(void)
|
|
||||||
{
|
|
||||||
WSADATA wsa_data;
|
|
||||||
struct sockaddr_in server_addr;
|
|
||||||
int res;
|
|
||||||
|
|
||||||
close_server_socket();
|
|
||||||
|
|
||||||
winsock_loaded = WSAStartup(MAKEWORD(2,2), &wsa_data) == NO_ERROR;
|
|
||||||
if (!winsock_loaded) return FALSE;
|
|
||||||
|
|
||||||
server_addr.sin_family = AF_INET;
|
|
||||||
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
|
||||||
server_addr.sin_port = htons(SERVER_PORT);
|
|
||||||
|
|
||||||
server_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
||||||
if (server_sock == INVALID_SOCKET) return FALSE;
|
|
||||||
|
|
||||||
res = bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr));
|
|
||||||
if (res == SOCKET_ERROR) return FALSE;
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int get_gamepad_request(void)
|
|
||||||
{
|
|
||||||
int res, client_addr_len, gamepad_id;
|
|
||||||
char buffer[BUFFER_SIZE];
|
|
||||||
struct sockaddr_in client_addr;
|
|
||||||
|
|
||||||
client_addr.sin_family = AF_INET;
|
|
||||||
client_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
|
||||||
client_addr.sin_port = htons(CLIENT_PORT);
|
|
||||||
client_addr_len = sizeof(client_addr);
|
|
||||||
|
|
||||||
buffer[0] = REQUEST_CODE_GET_GAMEPAD;
|
|
||||||
buffer[1] = 1;
|
|
||||||
res = sendto(server_sock, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&client_addr, client_addr_len);
|
|
||||||
if (res == SOCKET_ERROR) return 0;
|
|
||||||
|
|
||||||
res = recvfrom(server_sock, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&client_addr, &client_addr_len);
|
|
||||||
if (res == SOCKET_ERROR || buffer[0] != REQUEST_CODE_GET_GAMEPAD) return 0;
|
|
||||||
|
|
||||||
memcpy(&gamepad_id, buffer + 1, 4);
|
|
||||||
return gamepad_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void release_gamepad_request(void)
|
|
||||||
{
|
|
||||||
char buffer[BUFFER_SIZE];
|
|
||||||
struct sockaddr_in client_addr;
|
|
||||||
int client_addr_len;
|
|
||||||
|
|
||||||
client_addr.sin_family = AF_INET;
|
|
||||||
client_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
|
||||||
client_addr.sin_port = htons(CLIENT_PORT);
|
|
||||||
client_addr_len = sizeof(client_addr);
|
|
||||||
|
|
||||||
buffer[0] = REQUEST_CODE_RELEASE_GAMEPAD;
|
|
||||||
sendto(server_sock, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&client_addr, client_addr_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL controller_check_caps(void)
|
|
||||||
{
|
|
||||||
XINPUT_CAPABILITIES *caps = &controller.caps;
|
|
||||||
memset(caps, 0, sizeof(XINPUT_CAPABILITIES));
|
|
||||||
|
|
||||||
caps->Gamepad.wButtons = 0xffff;
|
|
||||||
caps->Gamepad.bLeftTrigger = (1u << (sizeof(caps->Gamepad.bLeftTrigger) + 1)) - 1;
|
|
||||||
caps->Gamepad.bRightTrigger = (1u << (sizeof(caps->Gamepad.bRightTrigger) + 1)) - 1;
|
|
||||||
caps->Gamepad.sThumbLX = (1u << (sizeof(caps->Gamepad.sThumbLX) + 1)) - 1;
|
|
||||||
caps->Gamepad.sThumbLY = (1u << (sizeof(caps->Gamepad.sThumbLY) + 1)) - 1;
|
|
||||||
caps->Gamepad.sThumbRX = (1u << (sizeof(caps->Gamepad.sThumbRX) + 1)) - 1;
|
|
||||||
caps->Gamepad.sThumbRY = (1u << (sizeof(caps->Gamepad.sThumbRY) + 1)) - 1;
|
|
||||||
|
|
||||||
caps->Type = XINPUT_DEVTYPE_GAMEPAD;
|
|
||||||
caps->SubType = XINPUT_DEVSUBTYPE_GAMEPAD;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void controller_disable(void)
|
|
||||||
{
|
|
||||||
if (!controller.enabled) return;
|
|
||||||
controller.enabled = FALSE;
|
|
||||||
SetEvent(update_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void controller_destroy(void)
|
|
||||||
{
|
|
||||||
release_gamepad_request();
|
|
||||||
xinput_min_index = 3;
|
|
||||||
|
|
||||||
if (controller.connected)
|
|
||||||
{
|
|
||||||
controller_disable();
|
|
||||||
controller.connected = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
close_server_socket();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void controller_enable(void)
|
|
||||||
{
|
|
||||||
if (controller.enabled) return;
|
|
||||||
controller.enabled = TRUE;
|
|
||||||
SetEvent(update_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void controller_init(void)
|
|
||||||
{
|
|
||||||
memset(&controller.state, 0, sizeof(controller.state));
|
|
||||||
controller.enabled = FALSE;
|
|
||||||
controller.connected = TRUE;
|
|
||||||
controller_check_caps();
|
|
||||||
controller_enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void controller_check_connection(void)
|
|
||||||
{
|
|
||||||
controller.id = 0;
|
|
||||||
if (server_sock == INVALID_SOCKET) create_server_socket();
|
|
||||||
|
|
||||||
int gamepad_id = get_gamepad_request();
|
|
||||||
if (gamepad_id > 0)
|
|
||||||
{
|
|
||||||
controller.id = gamepad_id;
|
|
||||||
if (!controller.connected)
|
|
||||||
{
|
|
||||||
controller.id = gamepad_id;
|
|
||||||
controller_init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void stop_update_thread(void)
|
|
||||||
{
|
|
||||||
ResetEvent(update_event);
|
|
||||||
SetEvent(stop_event);
|
|
||||||
WaitForSingleObject(done_event, INFINITE);
|
|
||||||
|
|
||||||
CloseHandle(start_event);
|
|
||||||
CloseHandle(stop_event);
|
|
||||||
CloseHandle(done_event);
|
|
||||||
CloseHandle(update_event);
|
|
||||||
|
|
||||||
controller_destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void read_controller_state(void)
|
|
||||||
{
|
|
||||||
char buffer[BUFFER_SIZE];
|
|
||||||
int i, res, client_addr_len, gamepad_id;
|
|
||||||
char dpad;
|
|
||||||
short buttons, thumb_lx, thumb_ly, thumb_rx, thumb_ry;
|
|
||||||
struct sockaddr_in client_addr;
|
|
||||||
XINPUT_STATE state;
|
|
||||||
|
|
||||||
client_addr.sin_family = AF_INET;
|
|
||||||
client_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
|
||||||
client_addr.sin_port = htons(CLIENT_PORT);
|
|
||||||
client_addr_len = sizeof(client_addr);
|
|
||||||
|
|
||||||
buffer[0] = REQUEST_CODE_GET_GAMEPAD_STATE;
|
|
||||||
memcpy(buffer + 1, &controller.id, 4);
|
|
||||||
|
|
||||||
res = sendto(server_sock, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&client_addr, client_addr_len);
|
|
||||||
if (res == SOCKET_ERROR) return;
|
|
||||||
|
|
||||||
res = recvfrom(server_sock, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&client_addr, &client_addr_len);
|
|
||||||
if (res == SOCKET_ERROR || buffer[0] != REQUEST_CODE_GET_GAMEPAD_STATE || buffer[1] != 1) return;
|
|
||||||
|
|
||||||
memcpy(&gamepad_id, buffer + 2, 4);
|
|
||||||
if (gamepad_id != controller.id) return;
|
|
||||||
|
|
||||||
memcpy(&buttons, buffer + 6, 2);
|
|
||||||
|
|
||||||
dpad = buffer[8];
|
|
||||||
|
|
||||||
memcpy(&thumb_lx, buffer + 9, 2);
|
|
||||||
memcpy(&thumb_ly, buffer + 11, 2);
|
|
||||||
memcpy(&thumb_rx, buffer + 13, 2);
|
|
||||||
memcpy(&thumb_ry, buffer + 15, 2);
|
|
||||||
|
|
||||||
state.Gamepad.wButtons = 0;
|
|
||||||
for (i = 0; i < 10; i++)
|
|
||||||
{
|
|
||||||
if ((buttons & (1<<i))) {
|
|
||||||
switch (i)
|
|
||||||
{
|
|
||||||
case IDX_BUTTON_A: state.Gamepad.wButtons |= XINPUT_GAMEPAD_A; break;
|
|
||||||
case IDX_BUTTON_B: state.Gamepad.wButtons |= XINPUT_GAMEPAD_B; break;
|
|
||||||
case IDX_BUTTON_X: state.Gamepad.wButtons |= XINPUT_GAMEPAD_X; break;
|
|
||||||
case IDX_BUTTON_Y: state.Gamepad.wButtons |= XINPUT_GAMEPAD_Y; break;
|
|
||||||
case IDX_BUTTON_L1: state.Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_SHOULDER; break;
|
|
||||||
case IDX_BUTTON_R1: state.Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_SHOULDER; break;
|
|
||||||
case IDX_BUTTON_SELECT: state.Gamepad.wButtons |= XINPUT_GAMEPAD_BACK; break;
|
|
||||||
case IDX_BUTTON_START: state.Gamepad.wButtons |= XINPUT_GAMEPAD_START; break;
|
|
||||||
case IDX_BUTTON_L3: state.Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_THUMB; break;
|
|
||||||
case IDX_BUTTON_R3: state.Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_THUMB; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state.Gamepad.bLeftTrigger = (buttons & (1<<10)) ? 255 : 0;
|
|
||||||
state.Gamepad.bRightTrigger = (buttons & (1<<11)) ? 255 : 0;
|
|
||||||
|
|
||||||
switch (dpad)
|
|
||||||
{
|
|
||||||
case 0: state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_UP; break;
|
|
||||||
case 1: state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_UP | XINPUT_GAMEPAD_DPAD_RIGHT; break;
|
|
||||||
case 2: state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT; break;
|
|
||||||
case 3: state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT | XINPUT_GAMEPAD_DPAD_DOWN; break;
|
|
||||||
case 4: state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_DOWN; break;
|
|
||||||
case 5: state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_DOWN | XINPUT_GAMEPAD_DPAD_LEFT; break;
|
|
||||||
case 6: state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_LEFT; break;
|
|
||||||
case 7: state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_LEFT | XINPUT_GAMEPAD_DPAD_UP; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
state.Gamepad.sThumbLX = thumb_lx;
|
|
||||||
state.Gamepad.sThumbLY = -thumb_ly;
|
|
||||||
state.Gamepad.sThumbRX = thumb_rx;
|
|
||||||
state.Gamepad.sThumbRY = -thumb_ry;
|
|
||||||
state.dwPacketNumber = controller.state.dwPacketNumber + 1;
|
|
||||||
|
|
||||||
controller.state = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
static DWORD WINAPI controller_update_thread_proc(void *param)
|
|
||||||
{
|
|
||||||
DWORD last_time, curr_time;
|
|
||||||
const int num_events = 2;
|
|
||||||
HANDLE events[num_events];
|
|
||||||
DWORD ret = WAIT_TIMEOUT;
|
|
||||||
|
|
||||||
SetThreadDescription(GetCurrentThread(), L"wine_xinput_controller_update");
|
|
||||||
SetEvent(start_event);
|
|
||||||
|
|
||||||
last_time = GetCurrentTime();
|
|
||||||
do
|
|
||||||
{
|
|
||||||
curr_time = GetCurrentTime();
|
|
||||||
if (ret == WAIT_TIMEOUT || (curr_time - last_time) >= 2000) {
|
|
||||||
controller_check_connection();
|
|
||||||
last_time = curr_time;
|
|
||||||
}
|
|
||||||
if (controller.connected && controller.enabled)
|
|
||||||
{
|
|
||||||
read_controller_state();
|
|
||||||
Sleep(16);
|
|
||||||
}
|
|
||||||
|
|
||||||
events[0] = update_event;
|
|
||||||
events[1] = stop_event;
|
|
||||||
}
|
|
||||||
while ((ret = MsgWaitForMultipleObjectsEx(num_events, events, 2000, QS_ALLINPUT, MWMO_ALERTABLE)) < num_events - 1 ||
|
|
||||||
ret == num_events || ret == WAIT_TIMEOUT);
|
|
||||||
|
|
||||||
if (ret != num_events - 1) ERR("update thread exited unexpectedly, ret %lu\n", ret);
|
|
||||||
SetEvent(done_event);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL WINAPI start_update_thread_once(INIT_ONCE *once, void *param, void **context)
|
|
||||||
{
|
|
||||||
HANDLE thread;
|
|
||||||
|
|
||||||
start_event = CreateEventA(NULL, FALSE, FALSE, NULL);
|
|
||||||
if (!start_event) ERR("failed to create start event, error %lu\n", GetLastError());
|
|
||||||
|
|
||||||
stop_event = CreateEventA(NULL, FALSE, FALSE, NULL);
|
|
||||||
if (!stop_event) ERR("failed to create stop event, error %lu\n", GetLastError());
|
|
||||||
|
|
||||||
done_event = CreateEventA(NULL, FALSE, FALSE, NULL);
|
|
||||||
if (!done_event) ERR("failed to create done event, error %lu\n", GetLastError());
|
|
||||||
|
|
||||||
update_event = CreateEventA(NULL, TRUE, FALSE, NULL);
|
|
||||||
if (!update_event) ERR("failed to create update event, error %lu\n", GetLastError());
|
|
||||||
|
|
||||||
thread = CreateThread(NULL, 0, controller_update_thread_proc, NULL, 0, NULL);
|
|
||||||
if (!thread) ERR("failed to create update thread, error %lu\n", GetLastError());
|
|
||||||
CloseHandle(thread);
|
|
||||||
|
|
||||||
WaitForSingleObject(start_event, INFINITE);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void start_update_thread(void)
|
|
||||||
{
|
|
||||||
static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT;
|
|
||||||
InitOnceExecuteOnce(&init_once, start_update_thread_once, NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL controller_is_connected(DWORD index)
|
|
||||||
{
|
|
||||||
return index == 0 && controller.connected;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved)
|
|
||||||
{
|
|
||||||
TRACE("inst %p, reason %lu, reserved %p.\n", inst, reason, reserved);
|
|
||||||
|
|
||||||
switch (reason)
|
|
||||||
{
|
|
||||||
case DLL_PROCESS_ATTACH:
|
|
||||||
xinput_instance = inst;
|
|
||||||
DisableThreadLibraryCalls(inst);
|
|
||||||
break;
|
|
||||||
case DLL_PROCESS_DETACH:
|
|
||||||
if (reserved) break;
|
|
||||||
stop_update_thread();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WINAPI DECLSPEC_HOTPATCH XInputEnable(BOOL enable)
|
|
||||||
{
|
|
||||||
TRACE("enable %d.\n", enable);
|
|
||||||
|
|
||||||
/* Setting to false will stop messages from XInputSetState being sent
|
|
||||||
to the controllers. Setting to true will send the last vibration
|
|
||||||
value (sent to XInputSetState) to the controller and allow messages to
|
|
||||||
be sent */
|
|
||||||
start_update_thread();
|
|
||||||
|
|
||||||
if (!controller.connected) return;
|
|
||||||
if (enable) controller_enable();
|
|
||||||
else controller_disable();
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD WINAPI DECLSPEC_HOTPATCH XInputSetState(DWORD index, XINPUT_VIBRATION *vibration)
|
|
||||||
{
|
|
||||||
TRACE("index %lu, vibration %p.\n", index, vibration);
|
|
||||||
|
|
||||||
start_update_thread();
|
|
||||||
|
|
||||||
if (index >= XUSER_MAX_COUNT) return ERROR_BAD_ARGUMENTS;
|
|
||||||
if (!controller_is_connected(index)) return ERROR_DEVICE_NOT_CONNECTED;
|
|
||||||
|
|
||||||
return ERROR_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Some versions of SteamOverlayRenderer hot-patch XInputGetStateEx() and call
|
|
||||||
* XInputGetState() in the hook, so we need a wrapper. */
|
|
||||||
static DWORD xinput_get_state(DWORD index, XINPUT_STATE *state)
|
|
||||||
{
|
|
||||||
if (!state) return ERROR_BAD_ARGUMENTS;
|
|
||||||
|
|
||||||
start_update_thread();
|
|
||||||
|
|
||||||
if (index >= XUSER_MAX_COUNT) return ERROR_BAD_ARGUMENTS;
|
|
||||||
if (index < xinput_min_index) xinput_min_index = index;
|
|
||||||
if (index == xinput_min_index) index = 0;
|
|
||||||
if (!controller_is_connected(index)) return ERROR_DEVICE_NOT_CONNECTED;
|
|
||||||
|
|
||||||
*state = controller.state;
|
|
||||||
return ERROR_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD WINAPI DECLSPEC_HOTPATCH XInputGetState(DWORD index, XINPUT_STATE *state)
|
|
||||||
{
|
|
||||||
DWORD ret;
|
|
||||||
|
|
||||||
TRACE("index %lu, state %p.\n", index, state);
|
|
||||||
|
|
||||||
ret = xinput_get_state(index, state);
|
|
||||||
if (ret != ERROR_SUCCESS) return ret;
|
|
||||||
|
|
||||||
/* The main difference between this and the Ex version is the media guide button */
|
|
||||||
state->Gamepad.wButtons &= ~XINPUT_GAMEPAD_GUIDE;
|
|
||||||
|
|
||||||
return ERROR_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD WINAPI DECLSPEC_HOTPATCH XInputGetStateEx(DWORD index, XINPUT_STATE *state)
|
|
||||||
{
|
|
||||||
TRACE("index %lu, state %p.\n", index, state);
|
|
||||||
|
|
||||||
return xinput_get_state(index, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD WINAPI DECLSPEC_HOTPATCH XInputGetKeystroke(DWORD index, DWORD reserved, PXINPUT_KEYSTROKE keystroke)
|
|
||||||
{
|
|
||||||
TRACE("index %lu, reserved %lu, keystroke %p.\n", index, reserved, keystroke);
|
|
||||||
|
|
||||||
if (index >= XUSER_MAX_COUNT && index != XUSER_INDEX_ANY) return ERROR_BAD_ARGUMENTS;
|
|
||||||
if (!controller_is_connected(index)) return ERROR_DEVICE_NOT_CONNECTED;
|
|
||||||
|
|
||||||
return ERROR_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD WINAPI DECLSPEC_HOTPATCH XInputGetCapabilities(DWORD index, DWORD flags, XINPUT_CAPABILITIES *capabilities)
|
|
||||||
{
|
|
||||||
TRACE("index %lu, flags %#lx, capabilities %p.\n", index, flags, capabilities);
|
|
||||||
|
|
||||||
start_update_thread();
|
|
||||||
|
|
||||||
if (index >= XUSER_MAX_COUNT) return ERROR_BAD_ARGUMENTS;
|
|
||||||
if (!controller_is_connected(index)) return ERROR_DEVICE_NOT_CONNECTED;
|
|
||||||
|
|
||||||
if (flags & XINPUT_FLAG_GAMEPAD && controller.caps.SubType != XINPUT_DEVSUBTYPE_GAMEPAD) return ERROR_DEVICE_NOT_CONNECTED;
|
|
||||||
memcpy(capabilities, &controller.caps, sizeof(*capabilities));
|
|
||||||
|
|
||||||
return ERROR_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD WINAPI DECLSPEC_HOTPATCH XInputGetDSoundAudioDeviceGuids(DWORD index, GUID *render_guid, GUID *capture_guid)
|
|
||||||
{
|
|
||||||
if (index >= XUSER_MAX_COUNT) return ERROR_BAD_ARGUMENTS;
|
|
||||||
if (!controller_is_connected(index)) return ERROR_DEVICE_NOT_CONNECTED;
|
|
||||||
|
|
||||||
return ERROR_NOT_SUPPORTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD WINAPI DECLSPEC_HOTPATCH XInputGetBatteryInformation(DWORD index, BYTE type, XINPUT_BATTERY_INFORMATION* battery)
|
|
||||||
{
|
|
||||||
if (index >= XUSER_MAX_COUNT) return ERROR_BAD_ARGUMENTS;
|
|
||||||
if (!controller_is_connected(index)) return ERROR_DEVICE_NOT_CONNECTED;
|
|
||||||
|
|
||||||
return ERROR_NOT_SUPPORTED;
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user