Merge branch 'main' of github.com:libsdl-org/SDL

This commit is contained in:
Coder2 2026-03-18 21:17:35 +08:00
commit 53423ec173
No known key found for this signature in database
GPG key ID: 98C112D1F699AA4C
16 changed files with 241 additions and 66 deletions

View file

@ -1342,6 +1342,23 @@ extern "C" {
*/
#define SDL_HINT_JOYSTICK_DEVICE "SDL_JOYSTICK_DEVICE"
/**
* A variable containing a list of drum style controllers.
*
* The format of the string is a comma separated list of USB VID/PID pairs in
* hexadecimal form, e.g.
*
* `0xAAAA/0xBBBB,0xCCCC/0xDDDD`
*
* The variable can also take the form of "@file", in which case the named
* file will be loaded and interpreted as the value of the variable.
*
* This hint can be set anytime.
*
* \since This hint is available since SDL 3.4.4.
*/
#define SDL_HINT_JOYSTICK_DRUM_DEVICES "SDL_JOYSTICK_DRUM_DEVICES"
/**
* A variable controlling whether enhanced reports should be used for
* controllers when using the HIDAPI driver.
@ -1461,6 +1478,23 @@ extern "C" {
*/
#define SDL_HINT_JOYSTICK_GAMECUBE_DEVICES_EXCLUDED "SDL_JOYSTICK_GAMECUBE_DEVICES_EXCLUDED"
/**
* A variable containing a list of guitar style controllers.
*
* The format of the string is a comma separated list of USB VID/PID pairs in
* hexadecimal form, e.g.
*
* `0xAAAA/0xBBBB,0xCCCC/0xDDDD`
*
* The variable can also take the form of "@file", in which case the named
* file will be loaded and interpreted as the value of the variable.
*
* This hint can be set anytime.
*
* \since This hint is available since SDL 3.4.4.
*/
#define SDL_HINT_JOYSTICK_GUITAR_DEVICES "SDL_JOYSTICK_GUITAR_DEVICES"
/**
* A variable controlling whether the HIDAPI joystick drivers should be used.
*

View file

@ -564,6 +564,7 @@ static Uint32 initial_wheel_devices[] = {
MAKE_VIDPID(0x046d, 0xc29a), // Logitech Driving Force GT
MAKE_VIDPID(0x046d, 0xc29b), // Logitech G27
MAKE_VIDPID(0x046d, 0xca03), // Logitech Momo Racing
MAKE_VIDPID(0x045e, 0x02a2), // Xbox 360 Wireless Racing Wheel
MAKE_VIDPID(0x0483, 0x0522), // Simagic Wheelbase (including M10, Alpha Mini, Alpha, Alpha U)
MAKE_VIDPID(0x0483, 0xa355), // VRS DirectForce Pro Wheel Base
MAKE_VIDPID(0x0583, 0xa132), // Padix USB Wireless 2.4GHz Wheelpad
@ -610,6 +611,55 @@ static SDL_vidpid_list wheel_devices = {
false
};
static Uint32 initial_guitar_devices[] = {
MAKE_VIDPID(0x12ba, 0x0100), // PS3 Guitar Hero Guitar
MAKE_VIDPID(0x12ba, 0x0200), // PS3 Rock Band Guitar
MAKE_VIDPID(0x12ba, 0x074b), // PS3 / Wii U Guitar Hero Live Guitar
MAKE_VIDPID(0x1BAD, 0x0004), // Wii RB1 Guitar (Uses PS3 protocol)
MAKE_VIDPID(0x1BAD, 0x3010), // Wii RB2 Guitar (Uses PS3 protocol)
MAKE_VIDPID(0x0351, 0x1000), // CRKD Guitar
MAKE_VIDPID(0x0351, 0x2000), // CRKD Guitar
MAKE_VIDPID(0x0738, 0x02A6), // Mad Catz Wireless Rock Band Guitar
MAKE_VIDPID(0x0738, 0x02AB), // Mad Catz Wireless Precision Bass Guitar
MAKE_VIDPID(0x0738, 0x9806), // Mad Catz Precision Bass Guitar
MAKE_VIDPID(0x1430, 0x02a7), // Guitar Hero Wireless Guitar (Linux)
MAKE_VIDPID(0x1430, 0x0705), // Guitar Hero 5 Guitar
MAKE_VIDPID(0x1430, 0x070B), // Guitar Hero Live Guitar
MAKE_VIDPID(0x1430, 0x4734), // Guitar Hero World Tour Kiosk
MAKE_VIDPID(0x1430, 0x4748), // RedOctane Guitar Hero X-plorer
MAKE_VIDPID(0x1bad, 0x02a6), // Rock Band 2 Wireless Guitar (Linux)
MAKE_VIDPID(0x1bad, 0x02ab), // Rock Band Wireless Bass Guitar (Linux)
MAKE_VIDPID(0x2068, 0x0001), // Power Gig Guitar
MAKE_VIDPID(0x3651, 0x1000), // CRKD Guitar
MAKE_VIDPID(0x3651, 0x6000), // CRKD Guitar
};
static SDL_vidpid_list guitar_devices = {
SDL_HINT_JOYSTICK_GUITAR_DEVICES, 0, 0, NULL,
NULL, 0, 0, NULL,
SDL_arraysize(initial_guitar_devices), initial_guitar_devices,
false
};
static Uint32 initial_drum_devices[] = {
MAKE_VIDPID(0x12ba, 0x0120), // PS3 Guitar Hero Drums
MAKE_VIDPID(0x12ba, 0x0210), // PS3 Rock Band Drums
MAKE_VIDPID(0x12ba, 0x0218), // PS3 Midi Pro Adapter - Drums Mode
MAKE_VIDPID(0x1BAD, 0x0005), // Wii RB1 Drums (Uses PS3 protocol)
MAKE_VIDPID(0x1BAD, 0x3110), // Wii RB2 Drums (Uses PS3 protocol)
MAKE_VIDPID(0x1BAD, 0x3138), // Wii RB3 Midi Pro Adapter - Drums Mode (Uses PS3 protocol)
MAKE_VIDPID(0x1430, 0x02a8), // Guitar Hero Wireless Drum Kit (Linux)
MAKE_VIDPID(0x1430, 0x0805), // Band Hero Wireless Drum Kit
MAKE_VIDPID(0x1bad, 0x0003), // Harmonix Rock Band Drumkit
MAKE_VIDPID(0x1bad, 0x0130), // ION Drum Rocker
MAKE_VIDPID(0x2068, 0x0002), // Power Gig Drums
};
static SDL_vidpid_list drum_devices = {
SDL_HINT_JOYSTICK_DRUM_DEVICES, 0, 0, NULL,
NULL, 0, 0, NULL,
SDL_arraysize(initial_drum_devices), initial_drum_devices,
false
};
static Uint32 initial_zero_centered_devices[] = {
MAKE_VIDPID(0x05a0, 0x3232), // 8Bitdo Zero Gamepad
MAKE_VIDPID(0x0e8f, 0x3013), // HuiJia SNES USB adapter
@ -851,8 +901,10 @@ bool SDL_InitJoysticks(void)
SDL_LoadVIDPIDList(&old_xboxone_controllers);
SDL_LoadVIDPIDList(&arcadestick_devices);
SDL_LoadVIDPIDList(&blacklist_devices);
SDL_LoadVIDPIDList(&drum_devices);
SDL_LoadVIDPIDList(&flightstick_devices);
SDL_LoadVIDPIDList(&gamecube_devices);
SDL_LoadVIDPIDList(&guitar_devices);
SDL_LoadVIDPIDList(&rog_gamepad_mice);
SDL_LoadVIDPIDList(&throttle_devices);
SDL_LoadVIDPIDList(&wheel_devices);
@ -2284,8 +2336,10 @@ void SDL_QuitJoysticks(void)
SDL_FreeVIDPIDList(&old_xboxone_controllers);
SDL_FreeVIDPIDList(&arcadestick_devices);
SDL_FreeVIDPIDList(&blacklist_devices);
SDL_FreeVIDPIDList(&drum_devices);
SDL_FreeVIDPIDList(&flightstick_devices);
SDL_FreeVIDPIDList(&gamecube_devices);
SDL_FreeVIDPIDList(&guitar_devices);
SDL_FreeVIDPIDList(&rog_gamepad_mice);
SDL_FreeVIDPIDList(&throttle_devices);
SDL_FreeVIDPIDList(&wheel_devices);
@ -3391,6 +3445,16 @@ static bool SDL_IsJoystickThrottle(Uint16 vendor_id, Uint16 product_id)
return SDL_VIDPIDInList(vendor_id, product_id, &throttle_devices);
}
static bool SDL_IsJoystickGuitar(Uint16 vendor_id, Uint16 product_id)
{
return SDL_VIDPIDInList(vendor_id, product_id, &guitar_devices);
}
static bool SDL_IsJoystickDrumKit(Uint16 vendor_id, Uint16 product_id)
{
return SDL_VIDPIDInList(vendor_id, product_id, &drum_devices);
}
static SDL_JoystickType SDL_GetJoystickGUIDType(SDL_GUID guid)
{
Uint16 vendor;
@ -3414,6 +3478,14 @@ static SDL_JoystickType SDL_GetJoystickGUIDType(SDL_GUID guid)
return SDL_JOYSTICK_TYPE_THROTTLE;
}
if (SDL_IsJoystickGuitar(vendor, product)) {
return SDL_JOYSTICK_TYPE_GUITAR;
}
if (SDL_IsJoystickDrumKit(vendor, product)) {
return SDL_JOYSTICK_TYPE_DRUM_KIT;
}
if (SDL_IsJoystickXInput(guid)) {
// XInput GUID, get the type based on the XInput device subtype
switch (guid.data[15]) {

View file

@ -172,7 +172,7 @@ extern bool SDL_IsJoystickRAWINPUT(SDL_GUID guid);
extern bool SDL_IsJoystickVIRTUAL(SDL_GUID guid);
// Function to return whether a joystick is a wheel
bool SDL_IsJoystickWheel(Uint16 vendor_id, Uint16 product_id);
extern bool SDL_IsJoystickWheel(Uint16 vendor_id, Uint16 product_id);
// Function to return whether a joystick should be ignored
extern bool SDL_ShouldIgnoreJoystick(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name);

View file

@ -193,6 +193,8 @@ static const ControllerDescription_t arrControllers[] = {
{ MAKE_CONTROLLER_ID( 0x0079, 0x0006 ), k_eControllerType_UnknownNonSteamController, NULL }, // DragonRise Generic USB PCB, sometimes configured as a PC Twin Shock Controller - looks like a DS3 but the face buttons are 1-4 instead of symbols
{ MAKE_CONTROLLER_ID( 0x0079, 0x18d4 ), k_eControllerType_XBox360Controller, NULL }, // GPD Win 2 X-Box Controller
{ MAKE_CONTROLLER_ID( 0x0351, 0x1000 ), k_eControllerType_XBox360Controller, NULL }, // CRKD Guitar
{ MAKE_CONTROLLER_ID( 0x0351, 0x2000 ), k_eControllerType_XBox360Controller, NULL }, // CRKD Guitar
{ MAKE_CONTROLLER_ID( 0x03eb, 0xff02 ), k_eControllerType_XBox360Controller, NULL }, // Wooting Two
{ MAKE_CONTROLLER_ID( 0x044f, 0xb326 ), k_eControllerType_XBox360Controller, NULL }, // Thrustmaster Gamepad GP XID
{ MAKE_CONTROLLER_ID( 0x045e, 0x028e ), k_eControllerType_XBox360Controller, "Xbox 360 Controller" }, // Microsoft Xbox 360 Wired Controller
@ -200,6 +202,7 @@ static const ControllerDescription_t arrControllers[] = {
{ MAKE_CONTROLLER_ID( 0x045e, 0x0291 ), k_eControllerType_XBox360Controller, "Xbox 360 Wireless Controller" }, // X-box 360 Wireless Receiver (third party knockoff)
{ MAKE_CONTROLLER_ID( 0x045e, 0x02a0 ), k_eControllerType_XBox360Controller, NULL }, // Microsoft Xbox 360 Big Button IR
{ MAKE_CONTROLLER_ID( 0x045e, 0x02a1 ), k_eControllerType_XBox360Controller, "Xbox 360 Wireless Controller" }, // Microsoft Xbox 360 Wireless Controller with XUSB driver on Windows
{ MAKE_CONTROLLER_ID( 0x045e, 0x02a2 ), k_eControllerType_XBox360Controller, "Xbox 360 Wireless Racing Wheel" }, // Xbox 360 Wireless Racing Wheel with XUSB driver on Windows
{ MAKE_CONTROLLER_ID( 0x045e, 0x02a9 ), k_eControllerType_XBox360Controller, "Xbox 360 Wireless Controller" }, // X-box 360 Wireless Receiver (third party knockoff)
{ MAKE_CONTROLLER_ID( 0x045e, 0x0719 ), k_eControllerType_XBox360Controller, "Xbox 360 Wireless Controller" }, // Microsoft Xbox 360 Wireless Receiver
{ MAKE_CONTROLLER_ID( 0x046d, 0xc21d ), k_eControllerType_XBox360Controller, NULL }, // Logitech Gamepad F310
@ -209,6 +212,8 @@ static const ControllerDescription_t arrControllers[] = {
{ MAKE_CONTROLLER_ID( 0x056e, 0x2004 ), k_eControllerType_XBox360Controller, NULL }, // Elecom JC-U3613M
// This isn't actually an Xbox 360 controller, it just looks like one
// { MAKE_CONTROLLER_ID( 0x06a3, 0xf51a ), k_eControllerType_XBox360Controller, NULL }, // Saitek P3600
{ MAKE_CONTROLLER_ID( 0x0738, 0x02A6 ), k_eControllerType_XBox360Controller, NULL }, // Mad Catz Wireless Rock Band Guitar
{ MAKE_CONTROLLER_ID( 0x0738, 0x02AB ), k_eControllerType_XBox360Controller, NULL }, // Mad Catz Wireless Precision Bass Guitar
{ MAKE_CONTROLLER_ID( 0x0738, 0x4716 ), k_eControllerType_XBox360Controller, NULL }, // Mad Catz Wired Xbox 360 Controller
{ MAKE_CONTROLLER_ID( 0x0738, 0x4718 ), k_eControllerType_XBox360Controller, NULL }, // Mad Catz Street Fighter IV FightStick SE
{ MAKE_CONTROLLER_ID( 0x0738, 0x4726 ), k_eControllerType_XBox360Controller, NULL }, // Mad Catz Xbox 360 Controller
@ -216,6 +221,7 @@ static const ControllerDescription_t arrControllers[] = {
{ MAKE_CONTROLLER_ID( 0x0738, 0x4736 ), k_eControllerType_XBox360Controller, NULL }, // Mad Catz MicroCon Gamepad
{ MAKE_CONTROLLER_ID( 0x0738, 0x4738 ), k_eControllerType_XBox360Controller, NULL }, // Mad Catz Wired Xbox 360 Controller (SFIV)
{ MAKE_CONTROLLER_ID( 0x0738, 0x4740 ), k_eControllerType_XBox360Controller, NULL }, // Mad Catz Beat Pad
{ MAKE_CONTROLLER_ID( 0x0738, 0x9806 ), k_eControllerType_XBox360Controller, NULL }, // Mad Catz Precision Bass Guitar
{ MAKE_CONTROLLER_ID( 0x0738, 0xb726 ), k_eControllerType_XBox360Controller, NULL }, // Mad Catz Xbox controller - MW2
{ MAKE_CONTROLLER_ID( 0x0738, 0xbeef ), k_eControllerType_XBox360Controller, NULL }, // Mad Catz JOYTECH NEO SE Advanced GamePad
{ MAKE_CONTROLLER_ID( 0x0738, 0xcb02 ), k_eControllerType_XBox360Controller, NULL }, // Saitek Cyborg Rumble Pad - PC/Xbox 360
@ -259,6 +265,14 @@ static const ControllerDescription_t arrControllers[] = {
{ MAKE_CONTROLLER_ID( 0x12ab, 0x0301 ), k_eControllerType_XBox360Controller, NULL }, // PDP AFTERGLOW AX.1
{ MAKE_CONTROLLER_ID( 0x12ab, 0x0303 ), k_eControllerType_XBox360Controller, NULL }, // Mortal Kombat Klassic FightStick
{ MAKE_CONTROLLER_ID( 0x1430, 0x02a0 ), k_eControllerType_XBox360Controller, NULL }, // RedOctane Controller Adapter
{ MAKE_CONTROLLER_ID( 0x1430, 0x02a7 ), k_eControllerType_XBox360Controller, NULL }, // Guitar Hero Wireless Guitar (Linux)
{ MAKE_CONTROLLER_ID( 0x1430, 0x02a8 ), k_eControllerType_XBox360Controller, NULL }, // Guitar Hero Wireless Drum Kit (Linux)
{ MAKE_CONTROLLER_ID( 0x1430, 0x0705 ), k_eControllerType_XBox360Controller, NULL }, // Guitar Hero 5 Guitar
{ MAKE_CONTROLLER_ID( 0x1430, 0x070B ), k_eControllerType_XBox360Controller, NULL }, // Guitar Hero Live Guitar
{ MAKE_CONTROLLER_ID( 0x1430, 0x0805 ), k_eControllerType_XBox360Controller, NULL }, // Band Hero Wireless Drum Kit
{ MAKE_CONTROLLER_ID( 0x1430, 0x1715 ), k_eControllerType_XBox360Controller, NULL }, // DJ Hero Turntable (Wired)
{ MAKE_CONTROLLER_ID( 0x1430, 0x1705 ), k_eControllerType_XBox360Controller, NULL }, // DJ Hero Turntable (Wireless)
{ MAKE_CONTROLLER_ID( 0x1430, 0x4734 ), k_eControllerType_XBox360Controller, NULL }, // Guitar Hero World Tour Kiosk
{ MAKE_CONTROLLER_ID( 0x1430, 0x4748 ), k_eControllerType_XBox360Controller, NULL }, // RedOctane Guitar Hero X-plorer
{ MAKE_CONTROLLER_ID( 0x1430, 0xf801 ), k_eControllerType_XBox360Controller, NULL }, // RedOctane Controller
{ MAKE_CONTROLLER_ID( 0x146b, 0x0601 ), k_eControllerType_XBox360Controller, NULL }, // BigBen Interactive XBOX 360 Controller
@ -271,8 +285,17 @@ static const ControllerDescription_t arrControllers[] = {
{ MAKE_CONTROLLER_ID( 0x1689, 0xfd01 ), k_eControllerType_XBox360Controller, NULL }, // Razer Onza Classic Edition
{ MAKE_CONTROLLER_ID( 0x1689, 0xfe00 ), k_eControllerType_XBox360Controller, NULL }, // Razer Sabertooth
{ MAKE_CONTROLLER_ID( 0x1949, 0x041a ), k_eControllerType_XBox360Controller, "Amazon Luna Controller" }, // Amazon Luna Controller
{ MAKE_CONTROLLER_ID( 0x1bad, 0x0002 ), k_eControllerType_XBox360Controller, NULL }, // Harmonix Rock Band Guitar
{ MAKE_CONTROLLER_ID( 0x1bad, 0x0002 ), k_eControllerType_XBox360Controller, NULL }, // Harmonix Rock Band 1 Guitar
{ MAKE_CONTROLLER_ID( 0x1bad, 0x0003 ), k_eControllerType_XBox360Controller, NULL }, // Harmonix Rock Band Drumkit
{ MAKE_CONTROLLER_ID( 0x1bad, 0x0130 ), k_eControllerType_XBox360Controller, NULL }, // ION Drum Rocker
{ MAKE_CONTROLLER_ID( 0x1bad, 0x02a6 ), k_eControllerType_XBox360Controller, NULL }, // Rock Band 2 Wireless Guitar (Linux)
{ MAKE_CONTROLLER_ID( 0x1bad, 0x02ab ), k_eControllerType_XBox360Controller, NULL }, // Rock Band Wireless Bass Guitar (Linux)
{ MAKE_CONTROLLER_ID( 0x1bad, 0x1330 ), k_eControllerType_XBox360Controller, NULL }, // Rock Band Keyboard (Mustang)
{ MAKE_CONTROLLER_ID( 0x1bad, 0x1338 ), k_eControllerType_XBox360Controller, NULL }, // Rock Band MIDI Pro Adapter (Keyboard)
{ MAKE_CONTROLLER_ID( 0x1bad, 0x1430 ), k_eControllerType_XBox360Controller, NULL }, // Rock Band Pro Guitar (Mustang)
{ MAKE_CONTROLLER_ID( 0x1bad, 0x1530 ), k_eControllerType_XBox360Controller, NULL }, // Rock Band Pro Guitar (Squire)
{ MAKE_CONTROLLER_ID( 0x1bad, 0x1438 ), k_eControllerType_XBox360Controller, NULL }, // Rock Band MIDI Pro Adapter (Mustang Guitar)
{ MAKE_CONTROLLER_ID( 0x1bad, 0x1538 ), k_eControllerType_XBox360Controller, NULL }, // Rock Band MIDI Pro Adapter (Squire Guitar)
{ MAKE_CONTROLLER_ID( 0x1bad, 0xf016 ), k_eControllerType_XBox360Controller, NULL }, // Mad Catz Xbox 360 Controller
{ MAKE_CONTROLLER_ID( 0x1bad, 0xf018 ), k_eControllerType_XBox360Controller, NULL }, // Mad Catz Street Fighter IV SE Fighting Stick
{ MAKE_CONTROLLER_ID( 0x1bad, 0xf019 ), k_eControllerType_XBox360Controller, NULL }, // Mad Catz Brawlstick for Xbox 360
@ -306,6 +329,8 @@ static const ControllerDescription_t arrControllers[] = {
{ MAKE_CONTROLLER_ID( 0x1bad, 0xfa01 ), k_eControllerType_XBox360Controller, NULL }, // MadCatz GamePad
{ MAKE_CONTROLLER_ID( 0x1bad, 0xfd00 ), k_eControllerType_XBox360Controller, NULL }, // Razer Onza TE
{ MAKE_CONTROLLER_ID( 0x1bad, 0xfd01 ), k_eControllerType_XBox360Controller, NULL }, // Razer Onza
{ MAKE_CONTROLLER_ID( 0x2068, 0x0001 ), k_eControllerType_XBox360Controller, NULL }, // Power Gig Guitar
{ MAKE_CONTROLLER_ID( 0x2068, 0x0002 ), k_eControllerType_XBox360Controller, NULL }, // Power Gig Drums
{ MAKE_CONTROLLER_ID( 0x24c6, 0x5000 ), k_eControllerType_XBox360Controller, NULL }, // Razer Atrox Arcade Stick
{ MAKE_CONTROLLER_ID( 0x24c6, 0x5300 ), k_eControllerType_XBox360Controller, NULL }, // PowerA MINI PROEX Controller
{ MAKE_CONTROLLER_ID( 0x24c6, 0x5303 ), k_eControllerType_XBox360Controller, NULL }, // Xbox Airflo wired controller
@ -330,6 +355,8 @@ static const ControllerDescription_t arrControllers[] = {
{ MAKE_CONTROLLER_ID( 0x24c6, 0xfafc ), k_eControllerType_XBox360Controller, NULL }, // Afterglow Gamepad 1
{ MAKE_CONTROLLER_ID( 0x24c6, 0xfafd ), k_eControllerType_XBox360Controller, NULL }, // Afterglow Gamepad 3
{ MAKE_CONTROLLER_ID( 0x24c6, 0xfafe ), k_eControllerType_XBox360Controller, NULL }, // Rock Candy Gamepad for Xbox 360
{ MAKE_CONTROLLER_ID( 0x3651, 0x1000 ), k_eControllerType_XBox360Controller, NULL }, // CRKD Guitar
{ MAKE_CONTROLLER_ID( 0x3651, 0x6000 ), k_eControllerType_XBox360Controller, NULL }, // CRKD Guitar
{ MAKE_CONTROLLER_ID( 0x03f0, 0x0495 ), k_eControllerType_XBoxOneController, NULL }, // HP HyperX Clutch Gladiate
{ MAKE_CONTROLLER_ID( 0x044f, 0xd012 ), k_eControllerType_XBoxOneController, NULL }, // ThrustMaster eSwap PRO Controller Xbox
@ -470,7 +497,6 @@ static const ControllerDescription_t arrControllers[] = {
{ MAKE_CONTROLLER_ID( 0x146b, 0x0611 ), k_eControllerType_XBoxOneController, NULL }, // Xbox Controller Mode for NACON Revolution 3
// These have been added via Minidump for unrecognized Xinput controller assert
{ MAKE_CONTROLLER_ID( 0x045e, 0x02a2 ), k_eControllerType_XBox360Controller, NULL }, // Unknown Controller - Microsoft VID
{ MAKE_CONTROLLER_ID( 0x0e6f, 0x1414 ), k_eControllerType_XBox360Controller, NULL }, // Unknown Controller
{ MAKE_CONTROLLER_ID( 0x0e6f, 0x0159 ), k_eControllerType_XBox360Controller, NULL }, // Unknown Controller
{ MAKE_CONTROLLER_ID( 0x24c6, 0xfaff ), k_eControllerType_XBox360Controller, NULL }, // Unknown Controller

View file

@ -69,6 +69,11 @@
size.width *= self.layer.contentsScale;
size.height *= self.layer.contentsScale;
// Skip invalid sizes (can happen on visionOS before scene geometry is applied)
if (size.width <= 0 || size.height <= 0) {
return;
}
CAMetalLayer *metallayer = ((CAMetalLayer *)self.layer);
if (metallayer.drawableSize.width != size.width ||
metallayer.drawableSize.height != size.height) {

View file

@ -213,7 +213,9 @@ SDL_SystemTheme UIKit_GetSystemTheme(void)
#ifdef SDL_PLATFORM_VISIONOS
CGRect UIKit_ComputeViewFrame(SDL_Window *window)
{
return CGRectMake(window->x, window->y, window->w, window->h);
// View origin is always (0,0) relative to the UIWindow.
// window->x/y are screen-level positions (often SDL_WINDOWPOS_UNDEFINED).
return CGRectMake(0, 0, window->w, window->h);
}
#else
CGRect UIKit_ComputeViewFrame(SDL_Window *window, UIScreen *screen)

View file

@ -186,6 +186,23 @@ bool UIKit_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properti
}
if (scene) {
uiwindow = [[UIWindow alloc] initWithWindowScene:scene];
#ifdef SDL_PLATFORM_VISIONOS
/* On visionOS, the window scene may not have its final geometry yet
* when the UIWindow is first created. Request the desired size now
* and set the UIWindow frame to match so views have valid initial
* dimensions before the async geometry update completes. */
CGSize desiredSize = CGSizeMake(window->w, window->h);
uiwindow.frame = CGRectMake(0, 0, desiredSize.width, desiredSize.height);
UIWindowSceneGeometryPreferences *preferences =
[[UIWindowSceneGeometryPreferencesVision alloc] initWithSize:desiredSize];
[scene requestGeometryUpdateWithPreferences:preferences errorHandler:^(NSError * _Nonnull error) {
SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO,
"Initial geometry request failed: %s",
[[error localizedDescription] UTF8String]);
}];
#endif
}
}
if (!uiwindow) {
@ -214,10 +231,6 @@ bool UIKit_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_Properti
if (!SetupWindowData(_this, window, uiwindow, true)) {
return false;
}
#ifdef SDL_PLATFORM_VISIONOS
SDL_SetWindowSize(window, window->w, window->h);
#endif
}
return true;

View file

@ -1103,12 +1103,14 @@ static void Wayland_CursorStateSetCursor(SDL_WaylandCursorState *state, const Wa
dst_height = dst_width;
} else {
// If viewports aren't available, the scale is always 1.0.
state->scale = viddata->viewporter && focus ? focus->scale_factor : 1.0;
dst_width = cursor_data->cursor_data.custom.width;
dst_height = cursor_data->cursor_data.custom.height;
hot_x = cursor_data->cursor_data.custom.hot_x;
hot_y = cursor_data->cursor_data.custom.hot_y;
/* If viewports aren't available, the scale is always 1.0.
* The dimensions are scaled by the pointer scale, so custom cursors will be scaled relative to the window size.
*/
state->scale = viddata->viewporter && focus ? SDL_min(focus->pointer_scale.x, focus->pointer_scale.y) : 1.0;
dst_width = SDL_max((int)SDL_lround((double)cursor_data->cursor_data.custom.width / state->scale), 1);
dst_height = SDL_max((int)SDL_lround((double)cursor_data->cursor_data.custom.height / state->scale), 1);
hot_x = (int)SDL_lround((double)cursor_data->cursor_data.custom.hot_x / state->scale);
hot_y = (int)SDL_lround((double)cursor_data->cursor_data.custom.hot_y / state->scale);
}
state->current_cursor = cursor_data;
@ -1180,6 +1182,34 @@ static void Wayland_CursorStateResetCursor(SDL_WaylandCursorState *state)
state->current_frame = -1;
}
void Wayland_DisplayUpdatePointerFocusedScale(SDL_WindowData *updated_window)
{
SDL_VideoData *viddata = updated_window->waylandData;
SDL_WaylandSeat *seat;
const double new_scale = SDL_min(updated_window->pointer_scale.x, updated_window->pointer_scale.y);
wl_list_for_each (seat, &viddata->seat_list, link) {
if (seat->pointer.focus == updated_window) {
SDL_WaylandCursorState *state = &seat->pointer.cursor_state;
if (state->current_cursor && !state->current_cursor->is_system_cursor && state->scale != new_scale) {
Wayland_CursorStateResetCursor(state);
Wayland_SeatUpdatePointerCursor(seat);
}
}
SDL_WaylandPenTool *tool;
wl_list_for_each (tool, &seat->tablet.tool_list, link) {
if (tool->focus == updated_window) {
SDL_WaylandCursorState *state = &tool->cursor_state;
if (state->current_cursor && !state->current_cursor->is_system_cursor && state->scale != new_scale) {
Wayland_CursorStateResetCursor(&tool->cursor_state);
Wayland_TabletToolUpdateCursor(tool);
}
}
}
}
}
static bool Wayland_ShowCursor(SDL_Cursor *cursor)
{
SDL_VideoDevice *vd = SDL_GetVideoDevice();

View file

@ -29,6 +29,7 @@ extern void Wayland_FiniMouse(SDL_VideoData *data);
extern void Wayland_SeatUpdatePointerCursor(SDL_WaylandSeat *seat);
extern void Wayland_SeatSetDefaultCursor(SDL_WaylandSeat *seat);
extern void Wayland_SeatResetCursor(SDL_WaylandSeat *seat);
extern void Wayland_DisplayUpdatePointerFocusedScale(SDL_WindowData *updated_window);
extern void Wayland_TabletToolUpdateCursor(SDL_WaylandPenTool *tool);
extern void Wayland_SeatWarpMouse(SDL_WaylandSeat *seat, SDL_WindowData *window, float x, float y);
extern void Wayland_CursorStateSetFrameCallback(SDL_WaylandCursorState *state, void *userdata);

View file

@ -30,6 +30,7 @@
#include "../../core/unix/SDL_appid.h"
#include "../SDL_egl_c.h"
#include "SDL_waylandevents_c.h"
#include "SDL_waylandmouse.h"
#include "SDL_waylandwindow.h"
#include "SDL_waylandvideo.h"
#include "../../SDL_hints_c.h"
@ -287,6 +288,8 @@ static void ConfigureWindowGeometry(SDL_Window *window)
{
SDL_WindowData *data = window->internal;
const double scale_factor = GetWindowScale(window);
const double prev_pointer_scale_x = data->pointer_scale.x;
const double prev_pointer_scale_y = data->pointer_scale.y;
const int old_pixel_width = data->current.pixel_width;
const int old_pixel_height = data->current.pixel_height;
int window_width = 0;
@ -566,6 +569,11 @@ static void ConfigureWindowGeometry(SDL_Window *window)
}
}
// Update the scale for any focused cursors.
if (prev_pointer_scale_x != data->pointer_scale.x || prev_pointer_scale_y != data->pointer_scale.y) {
Wayland_DisplayUpdatePointerFocusedScale(data);
}
/* Update the min/max dimensions, primarily if the state was changed, and for non-resizable
* xdg-toplevel windows where the limits should match the window size.
*/

View file

@ -786,7 +786,7 @@ static void WIN_HandleRawKeyboardInput(Uint64 timestamp, SDL_VideoData *data, HA
SDL_SendKeyboardKey(timestamp, keyboardID, rawcode, code, down);
}
void WIN_PollRawInput(SDL_VideoDevice *_this, Uint64 poll_start, bool process_input)
void WIN_PollRawInput(SDL_VideoDevice *_this, Uint64 poll_start)
{
SDL_VideoData *data = _this->internal;
UINT size, i, count, total = 0;
@ -832,7 +832,7 @@ void WIN_PollRawInput(SDL_VideoDevice *_this, Uint64 poll_start, bool process_in
}
}
if (total > 0 && process_input) {
if (total > 0) {
Uint64 delta = poll_finish - poll_start;
UINT mouse_total = 0;
for (i = 0, input = (RAWINPUT *)data->rawinput; i < total; ++i, input = NEXTRAWINPUTBLOCK(input)) {

View file

@ -29,7 +29,7 @@ extern HINSTANCE SDL_Instance;
extern LRESULT CALLBACK WIN_KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam);
extern LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
extern void WIN_PollRawInput(SDL_VideoDevice *_this, Uint64 poll_start, bool process_input);
extern void WIN_PollRawInput(SDL_VideoDevice *_this, Uint64 poll_start);
extern void WIN_PumpEvents(SDL_VideoDevice *_this);
extern void WIN_PumpEventsForHWND(SDL_VideoDevice *_this, HWND hwnd);
extern void WIN_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window);

View file

@ -40,7 +40,6 @@
typedef struct
{
bool done;
bool paused;
Uint32 flags;
HANDLE ready_event;
HANDLE signal_event;
@ -48,7 +47,6 @@ typedef struct
} RawInputThreadData;
static RawInputThreadData thread_data = {
false,
false,
0,
INVALID_HANDLE_VALUE,
@ -106,34 +104,22 @@ static DWORD WINAPI WIN_RawInputThread(LPVOID param)
// Tell the parent we're ready to go!
SetEvent(data->ready_event);
while (!data->done) {
Uint64 idle_begin = SDL_GetTicksNS();
while (!data->done && !data->paused &&
// The high-order word of GetQueueStatus() will let us know if there's currently raw input to be processed.
// If there isn't, then we'll wait for new data to arrive with MsgWaitForMultipleObjects().
((HIWORD(GetQueueStatus(QS_RAWINPUT)) & QS_RAWINPUT) ||
(MsgWaitForMultipleObjects(1, &data->signal_event, FALSE, INFINITE, QS_RAWINPUT) == WAIT_OBJECT_0 + 1))) {
Uint64 idle_begin = SDL_GetTicksNS();
while (!data->done &&
// The high-order word of GetQueueStatus() will let us know if there's currently raw input to be processed.
// If there isn't, then we'll wait for new data to arrive with MsgWaitForMultipleObjects().
((HIWORD(GetQueueStatus(QS_RAWINPUT)) & QS_RAWINPUT) ||
(MsgWaitForMultipleObjects(1, &data->signal_event, FALSE, INFINITE, QS_RAWINPUT) == WAIT_OBJECT_0 + 1))) {
Uint64 idle_end = SDL_GetTicksNS();
Uint64 idle_time = idle_end - idle_begin;
Uint64 usb_8khz_interval = SDL_US_TO_NS(125);
Uint64 poll_start = idle_time < usb_8khz_interval ? _this->internal->last_rawinput_poll : idle_end;
Uint64 idle_end = SDL_GetTicksNS();
Uint64 idle_time = idle_end - idle_begin;
Uint64 usb_8khz_interval = SDL_US_TO_NS(125);
Uint64 poll_start = idle_time < usb_8khz_interval ? _this->internal->last_rawinput_poll : idle_end;
WIN_PollRawInput(_this, poll_start, true);
WIN_PollRawInput(_this, poll_start);
// Reset idle_begin for the next go-around
idle_begin = SDL_GetTicksNS();
}
if (data->paused) {
// Wait for the resume signal
while (data->paused) {
WaitForSingleObject(data->signal_event, INFINITE);
}
// Flush raw input queued while paused
WIN_PollRawInput(_this, SDL_GetTicksNS(), false);
}
// Reset idle_begin for the next go-around
idle_begin = SDL_GetTicksNS();
}
if (_this->internal->raw_input_fake_pen_id) {
@ -155,7 +141,6 @@ static DWORD WINAPI WIN_RawInputThread(LPVOID param)
static void CleanupRawInputThreadData(RawInputThreadData *data)
{
if (data->thread != INVALID_HANDLE_VALUE) {
data->paused = false;
data->done = true;
SetEvent(data->signal_event);
WaitForSingleObject(data->thread, 3000);
@ -174,20 +159,10 @@ static void CleanupRawInputThreadData(RawInputThreadData *data)
}
}
bool WIN_SetRawInputEnabled(SDL_VideoDevice *_this, Uint32 flags, bool force)
bool WIN_SetRawInputEnabled(SDL_VideoDevice *_this, Uint32 flags)
{
bool result = false;
if (thread_data.thread != INVALID_HANDLE_VALUE && !force) {
if (flags) {
thread_data.paused = false;
} else {
thread_data.paused = true;
}
SetEvent(thread_data.signal_event);
return true;
}
CleanupRawInputThreadData(&thread_data);
if (flags) {
@ -248,13 +223,14 @@ static bool WIN_UpdateRawInputEnabled(SDL_VideoDevice *_this)
if (data->raw_keyboard_flag_inputsink) {
flags |= RAW_KEYBOARD_FLAG_INPUTSINK;
}
// Leave the thread running, as it takes several ms to shut it down and spin it up.
// We'll continue processing them so they don't back up in the thread event queue,
// but we won't deliver raw events in the application.
flags |= (data->raw_input_enabled & (ENABLE_RAW_MOUSE_INPUT | ENABLE_RAW_KEYBOARD_INPUT));
if (flags != data->raw_input_enabled) {
bool force = false;
if ((flags ^ data->raw_input_enabled) & (RAW_KEYBOARD_FLAG_NOHOTKEYS | RAW_KEYBOARD_FLAG_INPUTSINK)) {
// The keyboard flags have changed
force = true;
}
if (WIN_SetRawInputEnabled(_this, flags, force)) {
if (WIN_SetRawInputEnabled(_this, flags)) {
data->raw_input_enabled = flags;
} else {
return false;
@ -338,7 +314,7 @@ bool WIN_SetRawKeyboardFlag_Inputsink(SDL_VideoDevice *_this, bool enabled)
#else
bool WIN_SetRawInputEnabled(SDL_VideoDevice *_this, Uint32 flags, bool force)
bool WIN_SetRawInputEnabled(SDL_VideoDevice *_this, Uint32 flags)
{
return SDL_Unsupported();
}

View file

@ -23,7 +23,7 @@
#ifndef SDL_windowsrawinput_h_
#define SDL_windowsrawinput_h_
extern bool WIN_SetRawInputEnabled(SDL_VideoDevice *_this, Uint32 flags, bool force);
extern bool WIN_SetRawInputEnabled(SDL_VideoDevice *_this, Uint32 flags);
extern bool WIN_SetRawMouseEnabled(SDL_VideoDevice *_this, bool enabled);
extern bool WIN_SetRawKeyboardEnabled(SDL_VideoDevice *_this, bool enabled);
extern bool WIN_SetRawKeyboardFlag_NoHotkeys(SDL_VideoDevice *_this, bool enabled);

View file

@ -671,7 +671,9 @@ void WIN_VideoQuit(SDL_VideoDevice *_this)
SDL_RemoveHintCallback(SDL_HINT_WINDOWS_ENABLE_MENU_MNEMONICS, UpdateWindowsEnableMenuMnemonics, NULL);
SDL_RemoveHintCallback(SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN, UpdateWindowFrameUsableWhileCursorHidden, NULL);
WIN_SetRawInputEnabled(_this, 0, true);
WIN_SetRawMouseEnabled(_this, false);
WIN_SetRawKeyboardEnabled(_this, false);
WIN_SetRawInputEnabled(_this, 0);
WIN_QuitGameInput(_this);
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)

View file

@ -34,7 +34,6 @@
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
static bool xinput2_initialized;
#endif
#if defined(SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO) || defined(SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH)
static bool xinput2_scrolling_supported;
static bool xinput2_multitouch_supported;
@ -137,6 +136,7 @@ static SDL_Window *xinput2_get_sdlwindow(SDL_VideoData *videodata, Window window
const SDL_WindowData *windowdata = X11_FindWindow(videodata, window);
return windowdata ? windowdata->window : NULL;
}
#endif // SDL_VIDEO_DRIVER_X11_XINPUT2
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO
static void xinput2_reset_scrollable_valuators(void)
@ -751,12 +751,15 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
void X11_InitXinput2Multitouch(SDL_VideoDevice *_this)
{
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
xinput2_grabbed_touch_raised = false;
xinput2_active_touch_count = 0;
#endif
}
bool X11_Xinput2HandlesMotionForWindow(SDL_WindowData *window_data)
{
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
/* Send the active flag once more after the touch count is zero, to suppress the
* emulated motion event when the last touch is raised.
*/
@ -765,6 +768,9 @@ bool X11_Xinput2HandlesMotionForWindow(SDL_WindowData *window_data)
xinput2_grabbed_touch_raised = false;
return ret;
#else
return false;
#endif // SDL_VIDEO_DRIVER_X11_XINPUT2
}
void X11_Xinput2Select(SDL_VideoDevice *_this, SDL_Window *window)