winraw_joypad: optionally read Xbox controllers through XInput

Raw input reads an Xbox-compatible pad as a plain HID device whose two
triggers share one axis, so they cannot be told apart when both are
held.

A new setting, "XInput for Xbox Controllers" (input_winraw_xinput_enable,
off by default, Windows only), hands those pads to XInput instead: the
raw input device is skipped and XInput's pad is listed with the XInput
driver's name, buttons, hat and axes, so the existing XInput autoconfig
profiles apply and each trigger has its own axis. Every other controller
stays with raw input. XInput is asked which pads exist when the driver
starts and when raw input reports a device change, not on every poll.

It is off by default because it changes button and axis numbers for
those pads; bindings made by hand under raw input would need redoing.

Rumble for such a pad goes through the same thread as before, with the
XInput pad already known.

Tested under Wine with XInput faked (samples/input/winraw_joypad_rumble)
and end to end with the setting on. Not run with a pad.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-10-04 04:24:15 +02:00
1 parent 71318ef1ec
commit 9ec817d523
10 files changed
+339 -31

No files matched your search

+3 -1
View File
@@ -622,7 +622,9 @@ jobs:
# is faked here: one such pad and one pad of XInput's are
# the same pad; of two, none is rumbled while nothing tells
# them apart, one is as soon as a button is held on it, and
# the other is then the one left.
# the other is then the one left. With "XInput for Xbox
# Controllers" on, XInput's pad is listed and read in the
# XInput driver's layout, triggers on separate axes.
trap 'wineserver -k >/dev/null 2>&1 || true' EXIT
make clean all
WINEDEBUG=-all timeout 300 make check
+1
View File
@@ -755,6 +755,7 @@ typedef struct settings
bool input_hotkey_follows_player1;
#if defined(HAVE_DINPUT) || defined(HAVE_WINRAWINPUT)
bool input_nowinkey_enable;
bool input_winraw_xinput_enable;
#endif
#ifdef UDEV_TOUCH_SUPPORT
bool input_touch_vmouse_pointer;
+230 -28
View File
@@ -170,6 +170,9 @@ typedef struct winraw_joypad_joypad_data
* outside what the report's HID description covers
* (input/common/sony_pad_motion.h). They are read out of a report
* only while a core has asked for them: motion_on. */
/* Which of XInput's pads this is, or -1 for one read from raw
* input. See winraw_joypad_xinput_rescan(). */
int8_t xuser;
uint8_t motion_model; /* enum sony_pad_model, or none */
uint8_t motion_on; /* bit 0 accelerometer, 1 gyroscope */
bool motion_seen; /* a report has had them */
@@ -726,6 +729,24 @@ static void winraw_joypad_out_close(unsigned slot)
SetEvent(winraw_joypad_out_wake);
}
/* The thread is started, if it is not running. */
static bool winraw_joypad_out_start(void)
{
if (winraw_joypad_out_thread)
return true;
retro_atomic_store_release_int(&winraw_joypad_out_quit, 0);
winraw_joypad_out_wake = CreateEventA(NULL, FALSE, FALSE, NULL);
winraw_joypad_out_thread = winraw_joypad_out_wake
? CreateThread(NULL, 0, winraw_joypad_out_run, NULL, 0, NULL)
: NULL;
if (winraw_joypad_out_thread)
return true;
if (winraw_joypad_out_wake)
CloseHandle(winraw_joypad_out_wake);
winraw_joypad_out_wake = NULL;
return false;
}
/* A pad has arrived: if it is one there is a rumble report for, its
* device is opened for writing and handed to the thread. */
static void winraw_joypad_out_open(unsigned slot,
@@ -786,21 +807,10 @@ static void winraw_joypad_out_open(unsigned slot,
}
}
if (!winraw_joypad_out_thread)
if (!winraw_joypad_out_start())
{
retro_atomic_store_release_int(&winraw_joypad_out_quit, 0);
winraw_joypad_out_wake = CreateEventA(NULL, FALSE, FALSE, NULL);
winraw_joypad_out_thread = winraw_joypad_out_wake
? CreateThread(NULL, 0, winraw_joypad_out_run, NULL, 0, NULL)
: NULL;
if (!winraw_joypad_out_thread)
{
if (winraw_joypad_out_wake)
CloseHandle(winraw_joypad_out_wake);
winraw_joypad_out_wake = NULL;
winraw_joypad_out_dev_free(dev);
return;
}
winraw_joypad_out_dev_free(dev);
return;
}
if (xinput)
@@ -827,6 +837,30 @@ static void winraw_joypad_out_open(unsigned slot,
SetEvent(winraw_joypad_out_wake);
}
/* A pad read through XInput: which of XInput's it is is known. */
static void winraw_joypad_out_open_xinput(unsigned slot, int xuser)
{
void *old;
winraw_joypad_out_t *out = &winraw_joypad_out[slot];
winraw_joypad_out_dev_t *dev = (winraw_joypad_out_dev_t*)
calloc(1, sizeof(*dev));
if (!dev || !winraw_joypad_out_start())
{
free(dev);
return;
}
dev->xinput = true;
dev->xuser = xuser;
retro_atomic_store_release_int(&out->want, 0);
old = retro_atomic_exchange_ptr(&out->inbox, dev);
if (old && old != WINRAW_JOYPAD_OUT_CLOSE)
winraw_joypad_out_dev_free((winraw_joypad_out_dev_t*)old);
out->present = true;
out->xinput = false; /* nothing to tell apart: no buttons published */
SetEvent(winraw_joypad_out_wake);
}
/* The driver is going: the thread is stopped, which stills and closes
* every device it holds; one it never took is closed here. */
static void winraw_joypad_out_stop(void)
@@ -913,6 +947,158 @@ static int winraw_joypad_find_free_slot(void)
return -1;
}
/* ------------------------------------------------------------------ */
/* Xbox pads read through XInput */
/* ------------------------------------------------------------------ */
/* With "XInput for Xbox Controllers" on, a pad Windows drives through
* XInput is not taken from raw input, where its triggers are one
* axis; XInput's pads are listed instead, with the XInput driver's
* buttons, axes and name, so its autoconfig profiles apply. */
static bool winraw_joypad_xinput_own;
static HMODULE winraw_joypad_xi_dll;
static winraw_xinput_get_state_t winraw_joypad_xi_get_state;
static bool winraw_joypad_path_is_xinput(const char *path)
{
return path && (strstr(path, "IG_") || strstr(path, "ig_"));
}
/* pad_count is the highest connected slot + 1: left to grow, stale
* slots would look in use to autoconfig during a reconnect. */
static void winraw_joypad_recount(void)
{
unsigned i;
unsigned new_count = 0;
for (i = 0; i < MAX_USERS; i++)
if (winraw_joypad_pads[i].connected)
new_count = i + 1;
winraw_joypad_pad_count = new_count;
}
static void winraw_joypad_xinput_load(void)
{
static const char *names[] = {
"xinput1_4.dll", "xinput1_3.dll", "xinput9_1_0.dll" };
unsigned i;
for (i = 0; i < ARRAY_SIZE(names) && !winraw_joypad_xi_dll; i++)
winraw_joypad_xi_dll = LoadLibraryA(names[i]);
if (winraw_joypad_xi_dll)
winraw_joypad_xi_get_state = (winraw_xinput_get_state_t)
GetProcAddress(winraw_joypad_xi_dll, "XInputGetState");
if (!winraw_joypad_xi_get_state)
{
RARCH_WARN("[RawInput Joypad] XInput could not be loaded; Xbox"
" controllers are read through raw input.\n");
winraw_joypad_xinput_own = false;
}
}
static void winraw_joypad_xinput_fill(winraw_joypad_joypad_data_t *pad,
const winraw_xinput_state_t *st)
{
/* the XInput driver's order: A B X Y LB RB Start Back L3 R3 */
static const WORD bits[10] = { 0x1000, 0x2000, 0x4000, 0x8000,
0x0100, 0x0200, 0x0010, 0x0020, 0x0040, 0x0080 };
unsigned i;
for (i = 0; i < 10; i++)
pad->buttons[i] = (st->buttons & bits[i]) != 0;
pad->hats[0] = (uint8_t)(
((st->buttons & 0x0001) ? (1 << 0) : 0)
| ((st->buttons & 0x0002) ? (1 << 1) : 0)
| ((st->buttons & 0x0004) ? (1 << 2) : 0)
| ((st->buttons & 0x0008) ? (1 << 3) : 0));
pad->axes[0] = st->lx == -32768 ? -32767 : st->lx;
pad->axes[1] = st->ly == -32768 ? -32767 : st->ly;
pad->axes[2] = st->rx == -32768 ? -32767 : st->rx;
pad->axes[3] = st->ry == -32768 ? -32767 : st->ry;
pad->axes[4] = (int16_t)(st->left_trigger * 32767 / 255);
pad->axes[5] = (int16_t)(st->right_trigger * 32767 / 255);
}
/* XInput's four pads are looked at: one that has come is given a
* slot, one that has gone gives its slot up. Called when the driver
* starts and when raw input reports a device coming or going - asking
* XInput about a pad that is not there is slow, and is not done on
* every poll. */
static void winraw_joypad_xinput_rescan(void)
{
unsigned u, i;
if (!winraw_joypad_xinput_own)
return;
for (u = 0; u < WINRAW_XINPUT_PADS; u++)
{
winraw_xinput_state_t st;
winraw_joypad_joypad_data_t *pad = NULL;
bool there;
memset(&st, 0, sizeof(st));
there = winraw_joypad_xi_get_state(u, &st) == ERROR_SUCCESS;
for (i = 0; i < MAX_USERS; i++)
if ( winraw_joypad_pads[i].connected
&& winraw_joypad_pads[i].xuser == (int8_t)u)
pad = &winraw_joypad_pads[i];
if (there && !pad)
{
int slot = winraw_joypad_find_free_slot();
if (slot < 0)
continue;
pad = &winraw_joypad_pads[slot];
memset(pad, 0, sizeof(*pad));
pad->xuser = (int8_t)u;
pad->num_buttons = 10;
pad->num_axes = 6;
pad->num_hats = 1;
strlcpy(pad->name, "XInput Controller", sizeof(pad->name));
winraw_joypad_xinput_fill(pad, &st);
pad->connected = true;
winraw_joypad_recount();
RARCH_LOG("[RawInput Joypad] XInput pad %u connected in slot %d.\n",
u + 1, slot);
input_autoconfigure_connect(pad->name, NULL, NULL, "xinput",
(unsigned)slot, 0, 0);
winraw_joypad_out_open_xinput((unsigned)slot, (int)u);
}
else if (!there && pad)
{
unsigned slot = (unsigned)(pad - winraw_joypad_pads);
RARCH_LOG("[RawInput Joypad] XInput pad %u removed from slot %u.\n",
u + 1, slot);
input_autoconfigure_disconnect(slot, pad->name);
winraw_joypad_out_close(slot);
memset(pad, 0, sizeof(*pad));
winraw_joypad_recount();
}
}
}
/* Once a poll: the state of each XInput pad that is there. */
static void winraw_joypad_xinput_poll(void)
{
unsigned i;
bool gone = false;
if (!winraw_joypad_xinput_own)
return;
for (i = 0; i < winraw_joypad_pad_count; i++)
{
winraw_xinput_state_t st;
winraw_joypad_joypad_data_t *pad = &winraw_joypad_pads[i];
if (!pad->connected || pad->xuser < 0)
continue;
if (winraw_joypad_xi_get_state((DWORD)pad->xuser, &st) == ERROR_SUCCESS)
winraw_joypad_xinput_fill(pad, &st);
else
gone = true;
}
if (gone)
winraw_joypad_xinput_rescan();
}
/* ------------------------------------------------------------------ */
/* Device arrival / removal */
/* ------------------------------------------------------------------ */
@@ -970,6 +1156,15 @@ static bool winraw_joypad_add_device(HANDLE hDevice)
device_path[0] = '\0';
}
/* XInput's to read, not raw input's: XInput's pads are looked at
* again, this one among them. */
if ( winraw_joypad_xinput_own
&& winraw_joypad_path_is_xinput(device_path))
{
winraw_joypad_xinput_rescan();
return false;
}
/* ---- Handle reconnection race condition ----
* Windows can send GIDC_ARRIVAL for a newly-assigned handle *before*
* GIDC_REMOVAL for the old handle of the same physical device.
@@ -1043,6 +1238,7 @@ static bool winraw_joypad_add_device(HANDLE hDevice)
pad->pid = (uint16_t)dev_info.hid.dwProductId;
strlcpy(pad->path, device_path, sizeof(pad->path));
pad->xuser = -1;
/* --- Open the device path for the product string --- */
if (device_path[0])
@@ -1210,7 +1406,11 @@ static void winraw_joypad_remove_device(HANDLE hDevice)
{
int slot = winraw_joypad_find_pad(hDevice);
if (slot < 0)
{
/* not one of raw input's pads: it may have been XInput's */
winraw_joypad_xinput_rescan();
return;
}
{
winraw_joypad_joypad_data_t *pad = &winraw_joypad_pads[slot];
@@ -1233,20 +1433,7 @@ static void winraw_joypad_remove_device(HANDLE hDevice)
winraw_joypad_held[slot].count = 0;
}
/* Recalculate pad_count so it reflects the highest connected slot + 1.
* Without this, pad_count only ever grows, which is benign for
* find_pad / find_free_slot scanning, but can cause autoconfig
* to keep seeing stale slots as "in use" during reconnect. */
{
unsigned i;
unsigned new_count = 0;
for (i = 0; i < MAX_USERS; i++)
{
if (winraw_joypad_pads[i].connected)
new_count = i + 1;
}
winraw_joypad_pad_count = new_count;
}
winraw_joypad_recount();
}
/* ------------------------------------------------------------------ */
@@ -1698,7 +1885,14 @@ static void *winraw_joypad_joypad_init(void *data)
}
}
#if defined(HAVE_DINPUT) || defined(HAVE_WINRAWINPUT)
winraw_joypad_xinput_own = config_get_ptr()->bools.input_winraw_xinput_enable;
#endif
if (winraw_joypad_xinput_own)
winraw_joypad_xinput_load();
winraw_joypad_enumerate_devices();
winraw_joypad_xinput_rescan();
winraw_joypad_initialised = true;
RARCH_LOG("[RawInput Joypad] Initialised, %u device(s) found.\n",
@@ -1736,6 +1930,12 @@ static void winraw_joypad_joypad_destroy(void)
memset(winraw_joypad_held, 0, sizeof(winraw_joypad_held));
winraw_joypad_pad_count = 0;
if (winraw_joypad_xi_dll)
FreeLibrary(winraw_joypad_xi_dll);
winraw_joypad_xi_dll = NULL;
winraw_joypad_xi_get_state = NULL;
winraw_joypad_xinput_own = false;
if (winraw_joypad_msg_window)
{
/* A window is destroyed by the thread that made it. Read by the
@@ -2040,6 +2240,8 @@ static void winraw_joypad_joypad_poll(void)
{
MSG msg;
winraw_joypad_xinput_poll();
if (winraw_joypad_by_poll)
{
if ( !winraw_joypad_msg_window
+4
View File
@@ -505,6 +505,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_cheat_file_save_as, MENU_
#endif
#if defined(HAVE_DINPUT) || defined(HAVE_WINRAWINPUT)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_nowinkey_enable, MENU_ENUM_SUBLABEL_INPUT_NOWINKEY_ENABLE)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_winraw_xinput_enable, MENU_ENUM_SUBLABEL_INPUT_WINRAW_XINPUT_ENABLE)
#endif
#ifdef ANDROID
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_select_physical_keyboard, MENU_ENUM_SUBLABEL_INPUT_SELECT_PHYSICAL_KEYBOARD)
@@ -3202,6 +3203,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_INPUT_NOWINKEY_ENABLE:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_nowinkey_enable);
break;
case MENU_ENUM_LABEL_INPUT_WINRAW_XINPUT_ENABLE:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_winraw_xinput_enable);
break;
#endif
#ifdef ANDROID
case MENU_ENUM_LABEL_INPUT_SELECT_PHYSICAL_KEYBOARD:
+1
View File
@@ -9967,6 +9967,7 @@ unsigned menu_displaylist_build_list(
#endif
#if defined(HAVE_DINPUT) || defined(HAVE_WINRAWINPUT)
{MENU_ENUM_LABEL_INPUT_NOWINKEY_ENABLE, PARSE_ONLY_BOOL, true},
{MENU_ENUM_LABEL_INPUT_WINRAW_XINPUT_ENABLE, PARSE_ONLY_BOOL, true},
#endif
#ifdef ANDROID
{MENU_ENUM_LABEL_ANDROID_INPUT_DISCONNECT_WORKAROUND, PARSE_ONLY_BOOL, true},
+1
View File
@@ -1188,6 +1188,7 @@
#define MENU_ENUM_LABEL_INPUT_MENU_SETTINGS_STR "input_menu_settings"
#define MENU_ENUM_LABEL_INPUT_MOUSE_SCALE_STR "input_mouse_scale"
#define MENU_ENUM_LABEL_INPUT_NOWINKEY_ENABLE_STR "input_nowinkey_enable"
#define MENU_ENUM_LABEL_INPUT_WINRAW_XINPUT_ENABLE_STR "input_winraw_xinput_enable"
#define MENU_ENUM_LABEL_INPUT_OVERLAY_AUTO_SCALE_STR "input_overlay_auto_scale"
#define MENU_ENUM_LABEL_INPUT_OVERLAY_BEHIND_MENU_STR "overlay_behind_menu"
#define MENU_ENUM_LABEL_INPUT_OVERLAY_LIGHTGUN_ALLOW_OFFSCREEN_STR "input_overlay_lightgun_allow_offscreen"
+4
View File
@@ -357,6 +357,10 @@ input_player1_keyboard_device = "dead:beef"' \
EXTRA_CFG='input_player1_mouse_device = "dead:beef"' \
WANT_LOG="Port 1's mouse \"dead:beef\" is not there: the port reads the mouse its Mouse Index names" \
scenario "the first port pinned to a mouse that is away" true 1 2 nomsg
# "XInput for Xbox Controllers" on: XInput is loaded and asked for its
# pads when the joypad driver starts. With none there, nothing changes.
EXTRA_CFG='input_winraw_xinput_enable = "true"' \
scenario "XInput for Xbox controllers on" true 1 2 nomsg
VIDEO_DRIVER=gl VIDEO_STARTED='Found GL context' WANT_VIDEO=2 WANT_WINDOW="1 1 0" \
WANT_MENUS=1 EXTRA_CFG='ui_menubar_enable = "true"' \
scenario "OpenGL" true 1 2 nomsg
+1 -1
View File
@@ -9,7 +9,7 @@ REPO_ROOT := ../../..
CC := x86_64-w64-mingw32-gcc
CFLAGS += -Wall -std=gnu89 -Wdeclaration-after-statement -g -O1 \
-DRARCH_INTERNAL \
-DRARCH_INTERNAL -DHAVE_WINRAWINPUT \
-I$(REPO_ROOT) -I$(REPO_ROOT)/input/include \
-I$(REPO_ROOT)/libretro-common/include -I$(REPO_ROOT)/deps
@@ -42,7 +42,14 @@
* as a button is held on it; the last one left is then known by
* there being no other;
* - unplugging an Xbox pad that is rumbling stills it, and so does
* stopping the driver. */
* stopping the driver;
* - with "XInput for Xbox Controllers" on, an Xbox pad is not taken
* from raw input: XInput's pad is listed, named and laid out as the
* XInput driver has it - ten buttons, the D-pad as a hat, two
* sticks and the two triggers each on its own axis - and is read
* once a poll; another make of pad is raw input's as before; the
* pad rumbles, and one that goes is noticed and its slot given up.
* With the setting off none of that happens. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -151,6 +158,8 @@ static struct
bool connected;
WORD buttons, left, right;
unsigned sets;
BYTE lt, rt;
SHORT lx, ly;
} xpad[4];
static unsigned xinput_loads;
struct fake_xstate { DWORD packet; WORD buttons; BYTE lt, rt; SHORT lx, ly, rx, ry; };
@@ -162,6 +171,10 @@ static DWORD WINAPI fake_XInputGetState(DWORD i, struct fake_xstate *st)
return ERROR_DEVICE_NOT_CONNECTED;
memset(st, 0, sizeof(*st));
st->buttons = xpad[i].buttons;
st->lt = xpad[i].lt;
st->rt = xpad[i].rt;
st->lx = xpad[i].lx;
st->ly = xpad[i].ly;
return ERROR_SUCCESS;
}
static DWORD WINAPI fake_XInputSetState(DWORD i, struct fake_xvib *v)
@@ -217,6 +230,9 @@ bool input_autoconfigure_connect(const char *name, const char *display_name,
}
bool input_autoconfigure_disconnect(unsigned port, const char *name)
{ (void)port; (void)name; return true; }
/* the driver reads one setting: whether Xbox pads are XInput's */
static settings_t stub_settings;
settings_t *config_get_ptr(void) { return &stub_settings; }
bool winraw_raw_input_polled(void) { return true; }
void winraw_queue_read(void) { }
void winraw_queue_claim_thread(bool claim) { (void)claim; }
@@ -470,6 +486,78 @@ int main(void)
CHECK(!winraw_joypad_out_thread && !winraw_xinput_dll, "the driver stopped and left its thread or XInput behind");
printf(" ok an unplugged Xbox pad is stilled and XInput's pad let go; stopping the driver stills the rest\n");
/* ============ Xbox pads read through XInput ================== */
memset(xpad, 0, sizeof(xpad));
fake_n = 0;
fake_plug(H(21), 0x045E, 0x028E, "\\\\?\\HID#VID_045E&PID_028E&IG_00#c"); /* an Xbox pad */
fake_plug(H(22), 0x046D, 0xC216, other); /* another make */
xpad[1].connected = true; /* XInput has it as its second */
xpad[1].buttons = 0x1000 | 0x0001; /* A, and up on the D-pad */
xpad[1].lt = 255;
xpad[1].rt = 128;
xpad[1].lx = -32768;
xpad[1].ly = 1234;
/* ---- the setting off: the Xbox pad is raw input's -------------- */
stub_settings.bools.input_winraw_xinput_enable = false;
winraw_joypad_joypad_init(NULL);
winraw_joypad_add_device(H(21));
CHECK(winraw_joypad_pads[0].connected && winraw_joypad_pads[0].xuser < 0
&& winraw_joypad_pads[0].hDevice == H(21),
"with the setting off the Xbox pad is not raw input's");
winraw_joypad_joypad_destroy();
/* ---- the setting on -------------------------------------------- */
stub_settings.bools.input_winraw_xinput_enable = true;
winraw_joypad_joypad_init(NULL);
CHECK(winraw_joypad_pads[0].connected && winraw_joypad_pads[0].xuser == 1
&& !strcmp(winraw_joypad_pads[0].name, "XInput Controller"),
"XInput's pad is not listed as the XInput driver names it: \"%s\", XInput's %d",
winraw_joypad_pads[0].name, winraw_joypad_pads[0].xuser + 1);
winraw_joypad_add_device(H(21));
winraw_joypad_add_device(H(22));
CHECK(winraw_joypad_pads[1].connected && winraw_joypad_pads[1].hDevice == H(22)
&& !winraw_joypad_pads[2].connected,
"the Xbox pad was taken from raw input as well, or the other pad was not: slots 1 and 2 are %d %d",
winraw_joypad_pads[1].connected, winraw_joypad_pads[2].connected);
winraw_joypad_joypad_poll();
CHECK(winraw_joypad_joypad_button(0, 0) && !winraw_joypad_joypad_button(0, 1),
"A is held and B is not: the pad reads %d %d",
(int)winraw_joypad_joypad_button(0, 0), (int)winraw_joypad_joypad_button(0, 1));
CHECK(winraw_joypad_joypad_button(0, HAT_MAP(0, HAT_UP_MASK))
&& !winraw_joypad_joypad_button(0, HAT_MAP(0, HAT_DOWN_MASK)),
"up on the D-pad is not up on the hat");
CHECK(winraw_joypad_joypad_axis(0, AXIS_POS(4)) == 32767
&& winraw_joypad_joypad_axis(0, AXIS_POS(5)) == 128 * 32767 / 255,
"the triggers, each on its own axis: %d and %d, want 32767 and %d",
winraw_joypad_joypad_axis(0, AXIS_POS(4)), winraw_joypad_joypad_axis(0, AXIS_POS(5)), 128 * 32767 / 255);
CHECK(winraw_joypad_joypad_axis(0, AXIS_NEG(0)) == -32767
&& winraw_joypad_joypad_axis(0, AXIS_POS(1)) == 1234,
"the left stick: %d %d, want -32767 1234",
winraw_joypad_joypad_axis(0, AXIS_NEG(0)), winraw_joypad_joypad_axis(0, AXIS_POS(1)));
printf(" ok with the setting on, XInput's pad is listed and read in the XInput driver's layout, triggers apart; another make is raw input's; with it off the Xbox pad is raw input's\n");
/* it rumbles, as the pad XInput says it is */
ok = winraw_joypad_joypad_set_rumble(0, RETRO_RUMBLE_STRONG, 0x6000);
CHECK(ok && xwait(1, 0x6000, 0), "the XInput pad's motors are at %u %u, want 24576 0", xpad[1].left, xpad[1].right);
/* it goes: noticed at the next poll, stilled, its slot given up */
xpad[1].connected = false;
winraw_joypad_joypad_poll();
CHECK(!winraw_joypad_pads[0].connected && winraw_joypad_pads[1].connected,
"an XInput pad that went is still listed, or took the other pad with it");
CHECK(!winraw_joypad_joypad_set_rumble(0, RETRO_RUMBLE_STRONG, 0xFFFF), "a pad that went can still be rumbled");
/* and comes back, when raw input says a device has come */
xpad[1].connected = true;
xpad[1].buttons = 0;
winraw_joypad_add_device(H(21));
CHECK(winraw_joypad_pads[0].connected && winraw_joypad_pads[0].xuser == 1,
"the XInput pad did not come back to its slot");
winraw_joypad_joypad_destroy();
stub_settings.bools.input_winraw_xinput_enable = false;
printf(" ok an XInput pad rumbles; one that goes is noticed at the next poll and gives its slot up, and comes back to it\n");
DeleteFileA(edge); DeleteFileA(ds4); DeleteFileA(other);
if (failures)
{
+5
View File
@@ -10,4 +10,9 @@ S_BOOL(input_nowinkey_enable, INPUT_NOWINKEY_ENABLE,
false, SD_FLAG_NONE, 0, 0,
"Disable Windows Hotkeys (Restart required)",
"Keep Win-key combinations inside the application.")
S_BOOL(input_winraw_xinput_enable, INPUT_WINRAW_XINPUT_ENABLE,
"input_winraw_xinput_enable",
false, SD_FLAG_NONE, 0, 0,
"XInput for Xbox Controllers (Restart required)",
"With the 'raw' controller driver, read Xbox-compatible controllers through XInput: separate triggers and the XInput button layout. Other controllers are still read by raw input.")
#endif