diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h index 4ac405d243..0596171b98 100644 --- a/include/SDL3/SDL_hints.h +++ b/include/SDL3/SDL_hints.h @@ -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. * diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index 4b52980d5a..dde9927008 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -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]) { diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h index 8e3639b94a..46acbc5ca8 100644 --- a/src/joystick/SDL_joystick_c.h +++ b/src/joystick/SDL_joystick_c.h @@ -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); diff --git a/src/joystick/controller_list.h b/src/joystick/controller_list.h index ca7b85646e..c1d0da9b9f 100644 --- a/src/joystick/controller_list.h +++ b/src/joystick/controller_list.h @@ -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 diff --git a/src/video/uikit/SDL_uikitmetalview.m b/src/video/uikit/SDL_uikitmetalview.m index da0a8edd4f..596b311165 100644 --- a/src/video/uikit/SDL_uikitmetalview.m +++ b/src/video/uikit/SDL_uikitmetalview.m @@ -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) { diff --git a/src/video/uikit/SDL_uikitvideo.m b/src/video/uikit/SDL_uikitvideo.m index 43fab3afcd..d31f70a13d 100644 --- a/src/video/uikit/SDL_uikitvideo.m +++ b/src/video/uikit/SDL_uikitvideo.m @@ -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) diff --git a/src/video/uikit/SDL_uikitwindow.m b/src/video/uikit/SDL_uikitwindow.m index dd6dc27bd7..3f1f1b464d 100644 --- a/src/video/uikit/SDL_uikitwindow.m +++ b/src/video/uikit/SDL_uikitwindow.m @@ -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; diff --git a/src/video/wayland/SDL_waylandmouse.c b/src/video/wayland/SDL_waylandmouse.c index 72b9d14d5e..2ae9a418ae 100644 --- a/src/video/wayland/SDL_waylandmouse.c +++ b/src/video/wayland/SDL_waylandmouse.c @@ -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(); diff --git a/src/video/wayland/SDL_waylandmouse.h b/src/video/wayland/SDL_waylandmouse.h index 85ba5eef24..1707445a35 100644 --- a/src/video/wayland/SDL_waylandmouse.h +++ b/src/video/wayland/SDL_waylandmouse.h @@ -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); diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index d2ad64974d..20da36b633 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -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. */ diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index ea57c1ca42..19f49e9b36 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -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)) { diff --git a/src/video/windows/SDL_windowsevents.h b/src/video/windows/SDL_windowsevents.h index 21bd7a420c..45f5527dc6 100644 --- a/src/video/windows/SDL_windowsevents.h +++ b/src/video/windows/SDL_windowsevents.h @@ -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); diff --git a/src/video/windows/SDL_windowsrawinput.c b/src/video/windows/SDL_windowsrawinput.c index 3c5d80ab2b..5ae2f51d23 100644 --- a/src/video/windows/SDL_windowsrawinput.c +++ b/src/video/windows/SDL_windowsrawinput.c @@ -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(); } diff --git a/src/video/windows/SDL_windowsrawinput.h b/src/video/windows/SDL_windowsrawinput.h index e970666a9a..1adeb45f05 100644 --- a/src/video/windows/SDL_windowsrawinput.h +++ b/src/video/windows/SDL_windowsrawinput.h @@ -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); diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index edb28e838b..019a23c2d6 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -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) diff --git a/src/video/x11/SDL_x11xinput2.c b/src/video/x11/SDL_x11xinput2.c index c8b37cec98..5cf75901f2 100644 --- a/src/video/x11/SDL_x11xinput2.c +++ b/src/video/x11/SDL_x11xinput2.c @@ -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)