winebus.sys: Map SDL game controllers if requested.

Signed-off-by: Aric Stewart <aric@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Aric Stewart 2018-02-21 06:57:05 -06:00 committed by Alexandre Julliard
parent 67ce6235e8
commit 2505d67cbc
1 changed files with 251 additions and 16 deletions

View File

@ -59,12 +59,30 @@ WINE_DEFAULT_DEBUG_CHANNEL(plugplay);
#ifdef SONAME_LIBSDL2
#define VID_MICROSOFT 0x045e
static const WORD PID_XBOX_CONTROLLERS[] = {
0x0202, /* Xbox Controller */
0x0285, /* Xbox Controller S */
0x0289, /* Xbox Controller S */
0x028e, /* Xbox360 Controller */
0x028f, /* Xbox360 Wireless Controller */
0x02d1, /* Xbox One Controller */
0x02dd, /* Xbox One Controller (Covert Forces/Firmware 2015) */
0x02e3, /* Xbox One Elite Controller */
0x02e6, /* Wireless XBox Controller Dongle */
0x02ea, /* Xbox One S Controller */
0x0719, /* Xbox 360 Wireless Adapter */
};
WINE_DECLARE_DEBUG_CHANNEL(hid_report);
static DRIVER_OBJECT *sdl_driver_obj = NULL;
static const WCHAR sdl_busidW[] = {'S','D','L','J','O','Y',0};
static DWORD map_controllers = 0;
#include "initguid.h"
DEFINE_GUID(GUID_DEVCLASS_SDL, 0x463d60b5,0x802b,0x4bb2,0x8f,0xdb,0x7d,0xa9,0xb9,0x96,0x04,0xd8);
@ -87,6 +105,11 @@ MAKE_FUNCPTR(SDL_JoystickNumBalls);
MAKE_FUNCPTR(SDL_JoystickNumHats);
MAKE_FUNCPTR(SDL_JoystickGetAxis);
MAKE_FUNCPTR(SDL_JoystickGetHat);
MAKE_FUNCPTR(SDL_IsGameController);
MAKE_FUNCPTR(SDL_GameControllerGetAxis);
MAKE_FUNCPTR(SDL_GameControllerName);
MAKE_FUNCPTR(SDL_GameControllerOpen);
MAKE_FUNCPTR(SDL_GameControllerEventState);
#endif
static Uint16 (*pSDL_JoystickGetProduct)(SDL_Joystick * joystick);
static Uint16 (*pSDL_JoystickGetProductVersion)(SDL_Joystick * joystick);
@ -95,6 +118,7 @@ static Uint16 (*pSDL_JoystickGetVendor)(SDL_Joystick * joystick);
struct platform_private
{
SDL_Joystick *sdl_joystick;
SDL_GameController *sdl_controller;
SDL_JoystickID id;
int axis_start;
@ -124,6 +148,51 @@ static const BYTE REPORT_AXIS_TAIL[] = {
};
#define IDX_ABS_AXIS_COUNT 15
static const BYTE CONTROLLER_BUTTONS[] = {
0x05, 0x09, /* USAGE_PAGE (Button) */
0x19, 0x01, /* USAGE_MINIMUM (Button 1) */
0x29, 0x0f, /* USAGE_MAXIMUM (Button 15) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0x35, 0x00, /* LOGICAL_MINIMUM (0) */
0x45, 0x01, /* LOGICAL_MAXIMUM (1) */
0x95, 0x0f, /* REPORT_COUNT (15) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x81, 0x02, /* INPUT (Data,Var,Abs) */
/* padding */
0x95, 0x01, /* REPORT_COUNT (1) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x81, 0x03, /* INPUT (Cnst,Var,Abs) */
};
static const BYTE CONTROLLER_AXIS [] = {
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
0x09, 0x30, /* USAGE (X) */
0x09, 0x31, /* USAGE (Y) */
0x09, 0x33, /* USAGE (RX) */
0x09, 0x34, /* USAGE (RY) */
0x16, 0x00, 0x80, /* LOGICAL_MINIMUM (-32768) */
0x26, 0xff, 0x7f, /* LOGICAL_MAXIMUM (32767) */
0x36, 0x00, 0x80, /* PHYSICAL_MINIMUM (-32768) */
0x46, 0xff, 0x7f, /* PHYSICAL_MAXIMUM (32767) */
0x75, 0x10, /* REPORT_SIZE (16) */
0x95, 0x04, /* REPORT_COUNT (4) */
0x81, 0x02, /* INPUT (Data,Var,Abs) */
};
static const BYTE CONTROLLER_TRIGGERS [] = {
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
0x09, 0x32, /* USAGE (Z) */
0x09, 0x35, /* USAGE (RZ) */
0x16, 0x00, 0x00, /* LOGICAL_MINIMUM (0) */
0x26, 0xff, 0x7f, /* LOGICAL_MAXIMUM (32767) */
0x36, 0x00, 0x00, /* PHYSICAL_MINIMUM (0) */
0x46, 0xff, 0x7f, /* PHYSICAL_MAXIMUM (32767) */
0x75, 0x10, /* REPORT_SIZE (16) */
0x95, 0x02, /* REPORT_COUNT (2) */
0x81, 0x02, /* INPUT (Data,Var,Abs) */
};
static BYTE *add_axis_block(BYTE *report_ptr, BYTE count, BYTE page, const BYTE *usages, BOOL absolute)
{
int i;
@ -329,6 +398,58 @@ static BOOL build_report_descriptor(struct platform_private *ext)
return TRUE;
}
static BOOL build_mapped_report_descriptor(struct platform_private *ext)
{
BYTE *report_ptr;
INT i, descript_size;
descript_size = sizeof(REPORT_HEADER) + sizeof(REPORT_TAIL);
descript_size += sizeof(CONTROLLER_BUTTONS);
descript_size += sizeof(CONTROLLER_AXIS);
descript_size += sizeof(CONTROLLER_TRIGGERS);
ext->axis_start = 2;
ext->buffer_length = 14;
TRACE("Report Descriptor will be %i bytes\n", descript_size);
TRACE("Report will be %i bytes\n", ext->buffer_length);
ext->report_descriptor = HeapAlloc(GetProcessHeap(), 0, descript_size);
if (!ext->report_descriptor)
{
ERR("Failed to alloc report descriptor\n");
return FALSE;
}
report_ptr = ext->report_descriptor;
memcpy(report_ptr, REPORT_HEADER, sizeof(REPORT_HEADER));
report_ptr[IDX_HEADER_PAGE] = HID_USAGE_PAGE_GENERIC;
report_ptr[IDX_HEADER_USAGE] = HID_USAGE_GENERIC_GAMEPAD;
report_ptr += sizeof(REPORT_HEADER);
memcpy(report_ptr, CONTROLLER_BUTTONS, sizeof(CONTROLLER_BUTTONS));
report_ptr += sizeof(CONTROLLER_BUTTONS);
memcpy(report_ptr, CONTROLLER_AXIS, sizeof(CONTROLLER_AXIS));
report_ptr += sizeof(CONTROLLER_AXIS);
memcpy(report_ptr, CONTROLLER_TRIGGERS, sizeof(CONTROLLER_TRIGGERS));
report_ptr += sizeof(CONTROLLER_TRIGGERS);
memcpy(report_ptr, REPORT_TAIL, sizeof(REPORT_TAIL));
ext->report_descriptor_size = descript_size;
ext->report_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ext->buffer_length);
if (ext->report_buffer == NULL)
{
ERR("Failed to alloc report buffer\n");
HeapFree(GetProcessHeap(), 0, ext->report_descriptor);
return FALSE;
}
/* Initialize axis in the report */
for (i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++)
set_axis_value(ext, i, pSDL_GameControllerGetAxis(ext->sdl_controller, i));
return TRUE;
}
static int compare_platform_device(DEVICE_OBJECT *device, void *platform_dev)
{
SDL_JoystickID id1 = impl_from_DEVICE_OBJECT(device)->id;
@ -358,7 +479,10 @@ static NTSTATUS get_string(DEVICE_OBJECT *device, DWORD index, WCHAR *buffer, DW
switch (index)
{
case HID_STRING_ID_IPRODUCT:
str = pSDL_JoystickName(ext->sdl_joystick);
if (ext->sdl_controller)
str = pSDL_GameControllerName(ext->sdl_controller);
else
str = pSDL_JoystickName(ext->sdl_joystick);
break;
case HID_STRING_ID_IMANUFACTURER:
str = "SDL";
@ -426,6 +550,11 @@ static BOOL set_report_from_event(SDL_Event *event)
return FALSE;
}
private = impl_from_DEVICE_OBJECT(device);
if (private->sdl_controller)
{
/* We want mapped events */
return TRUE;
}
switch(event->type)
{
@ -466,6 +595,70 @@ static BOOL set_report_from_event(SDL_Event *event)
process_hid_report(device, private->report_buffer, private->buffer_length);
break;
}
default:
ERR("TODO: Process Report (0x%x)\n",event->type);
}
return FALSE;
}
static BOOL set_mapped_report_from_event(SDL_Event *event)
{
DEVICE_OBJECT *device;
struct platform_private *private;
/* All the events coming in will have 'which' as a 3rd field */
int index = ((SDL_ControllerButtonEvent*)event)->which;
device = bus_find_hid_device(&sdl_vtbl, ULongToPtr(index));
if (!device)
{
ERR("Failed to find device at index %i\n",index);
return FALSE;
}
private = impl_from_DEVICE_OBJECT(device);
switch(event->type)
{
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
{
int usage = -1;
SDL_ControllerButtonEvent *ie = &event->cbutton;
switch (ie->button)
{
case SDL_CONTROLLER_BUTTON_A: usage = 0; break;
case SDL_CONTROLLER_BUTTON_B: usage = 1; break;
case SDL_CONTROLLER_BUTTON_X: usage = 2; break;
case SDL_CONTROLLER_BUTTON_Y: usage = 3; break;
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: usage = 4; break;
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: usage = 5; break;
case SDL_CONTROLLER_BUTTON_LEFTSTICK: usage = 6; break;
case SDL_CONTROLLER_BUTTON_RIGHTSTICK: usage = 7; break;
case SDL_CONTROLLER_BUTTON_START: usage = 8; break;
case SDL_CONTROLLER_BUTTON_BACK: usage = 9; break;
case SDL_CONTROLLER_BUTTON_GUIDE: usage = 10; break;
case SDL_CONTROLLER_BUTTON_DPAD_UP: usage = 11; break;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN: usage = 12; break;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT: usage = 13; break;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: usage = 14; break;
default:
ERR("Unknown Button %i\n",ie->button);
}
if (usage >= 0)
{
set_button_value(usage, ie->state, private->report_buffer);
process_hid_report(device, private->report_buffer, private->buffer_length);
}
break;
}
case SDL_CONTROLLERAXISMOTION:
{
SDL_ControllerAxisEvent *ie = &event->caxis;
set_axis_value(private, ie->axis, ie->value);
process_hid_report(device, private->report_buffer, private->buffer_length);
break;
}
default:
ERR("TODO: Process Report (%x)\n",event->type);
}
@ -491,11 +684,11 @@ static void try_add_device(SDL_JoystickID index)
WCHAR serial[34] = {0};
char guid_str[34];
BOOL is_xbox_gamepad;
int button_count, axis_count;
SDL_Joystick* joystick;
SDL_JoystickID id;
SDL_JoystickGUID guid;
SDL_GameController *controller = NULL;
if ((joystick = pSDL_JoystickOpen(index)) == NULL)
{
@ -503,38 +696,67 @@ static void try_add_device(SDL_JoystickID index)
return;
}
if (map_controllers && pSDL_IsGameController(index))
controller = pSDL_GameControllerOpen(index);
id = index;
if (pSDL_JoystickGetProductVersion != NULL) {
vid = pSDL_JoystickGetVendor(joystick);
pid = pSDL_JoystickGetProduct(joystick);
version = pSDL_JoystickGetProductVersion(joystick);
if (controller)
{
vid = VID_MICROSOFT;
pid = PID_XBOX_CONTROLLERS[3];
version = 0x01;
}
else
{
vid = 0x01;
pid = pSDL_JoystickInstanceID(joystick) + 1;
version = 0;
if (pSDL_JoystickGetProductVersion != NULL) {
vid = pSDL_JoystickGetVendor(joystick);
pid = pSDL_JoystickGetProduct(joystick);
version = pSDL_JoystickGetProductVersion(joystick);
}
else
{
vid = 0x01;
pid = pSDL_JoystickInstanceID(joystick) + 1;
version = 0;
}
}
guid = pSDL_JoystickGetGUID(joystick);
pSDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
MultiByteToWideChar(CP_ACP, 0, guid_str, -1, serial, sizeof(guid_str));
TRACE("Found sdl device %i (vid %04x, pid %04x, version %u, serial %s)\n",
index, vid, pid, version, debugstr_w(serial));
if (controller)
{
TRACE("Found sdl game controller %i (vid %04x, pid %04x, version %u, serial %s)\n",
index, vid, pid, version, debugstr_w(serial));
is_xbox_gamepad = TRUE;
}
else
{
int button_count, axis_count;
axis_count = pSDL_JoystickNumAxes(joystick);
button_count = pSDL_JoystickNumAxes(joystick);
is_xbox_gamepad = (axis_count == 6 && button_count >= 14);
TRACE("Found sdl device %i (vid %04x, pid %04x, version %u, serial %s)\n",
index, vid, pid, version, debugstr_w(serial));
axis_count = pSDL_JoystickNumAxes(joystick);
button_count = pSDL_JoystickNumAxes(joystick);
is_xbox_gamepad = (axis_count == 6 && button_count >= 14);
}
device = bus_create_hid_device(sdl_driver_obj, sdl_busidW, vid, pid, version, 0, serial, is_xbox_gamepad, &GUID_DEVCLASS_SDL, &sdl_vtbl, sizeof(struct platform_private));
if (device)
{
BOOL rc;
struct platform_private *private = impl_from_DEVICE_OBJECT(device);
private->sdl_joystick = joystick;
private->sdl_controller = controller;
private->id = id;
if (!build_report_descriptor(private))
if (controller)
rc = build_mapped_report_descriptor(private);
else
rc = build_report_descriptor(private);
if (!rc)
{
ERR("Building report descriptor failed, removing device\n");
bus_remove_hid_device(device);
@ -559,6 +781,8 @@ static void process_device_event(SDL_Event *event)
try_remove_device(((SDL_JoyDeviceEvent*)event)->which);
else if (event->type >= SDL_JOYAXISMOTION && event->type <= SDL_JOYBUTTONUP)
set_report_from_event(event);
else if (event->type >= SDL_CONTROLLERAXISMOTION && event->type <= SDL_CONTROLLERBUTTONUP)
set_mapped_report_from_event(event);
}
static DWORD CALLBACK deviceloop_thread(void *args)
@ -566,13 +790,14 @@ static DWORD CALLBACK deviceloop_thread(void *args)
HANDLE init_done = args;
SDL_Event event;
if (pSDL_Init(SDL_INIT_JOYSTICK) < 0)
if (pSDL_Init(SDL_INIT_GAMECONTROLLER) < 0)
{
ERR("Can't Init SDL\n");
return STATUS_UNSUCCESSFUL;
}
pSDL_JoystickEventState(SDL_ENABLE);
pSDL_GameControllerEventState(SDL_ENABLE);
SetEvent(init_done);
@ -586,6 +811,9 @@ static DWORD CALLBACK deviceloop_thread(void *args)
NTSTATUS WINAPI sdl_driver_init(DRIVER_OBJECT *driver, UNICODE_STRING *registry_path)
{
static const WCHAR controller_modeW[] = {'M','a','p',' ','C','o','n','t','r','o','l','l','e','r','s',0};
static const UNICODE_STRING controller_mode = {sizeof(controller_modeW) - sizeof(WCHAR), sizeof(controller_modeW), (WCHAR*)controller_modeW};
HANDLE events[2];
DWORD result;
@ -614,6 +842,11 @@ NTSTATUS WINAPI sdl_driver_init(DRIVER_OBJECT *driver, UNICODE_STRING *registry_
LOAD_FUNCPTR(SDL_JoystickNumHats);
LOAD_FUNCPTR(SDL_JoystickGetAxis);
LOAD_FUNCPTR(SDL_JoystickGetHat);
LOAD_FUNCPTR(SDL_IsGameController);
LOAD_FUNCPTR(SDL_GameControllerGetAxis);
LOAD_FUNCPTR(SDL_GameControllerName);
LOAD_FUNCPTR(SDL_GameControllerOpen);
LOAD_FUNCPTR(SDL_GameControllerEventState);
#undef LOAD_FUNCPTR
pSDL_JoystickGetProduct = wine_dlsym(sdl_handle, "SDL_JoystickGetProduct", NULL, 0);
pSDL_JoystickGetProductVersion = wine_dlsym(sdl_handle, "SDL_JoystickGetProductVersion", NULL, 0);
@ -624,6 +857,8 @@ NTSTATUS WINAPI sdl_driver_init(DRIVER_OBJECT *driver, UNICODE_STRING *registry_
driver->MajorFunction[IRP_MJ_PNP] = common_pnp_dispatch;
driver->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = hid_internal_dispatch;
map_controllers = check_bus_option(registry_path, &controller_mode, 1);
if (!(events[0] = CreateEventW(NULL, TRUE, FALSE, NULL)))
goto error;
if (!(events[1] = CreateThread(NULL, 0, deviceloop_thread, events[0], 0, NULL)))