Hi,

Please find attached a diff that updates and aligns the gamecontroller
support in godot/pack1 (Godot version 3.x). We have so far had my
homegrown gamecontroller backend for this version, which was inspired
by the SDL backend, but for example didn't implement mapping based on
the SDL_gamepad_db.h (in SDL3)

This diff takes the SDL3 gamecontroller backend that we already have in
place for godot/pack3 and godot/pack2, and adjusts it to work with the
godot/pack1 architecture. Also tosses the homegrown gamecontroller
backend.

I've tested this with Brotato and my PS4 controller. This improves the
face buttons. It seems to confused the Options and Share buttons as
should buttons (RB1, LB1), not sure why, but overall improves the
situation from before.

In the long run, I think this alignment of the gamecontroller codebase
will also make maintenance more manageable.

ok?

? BUILD.log
Index: Makefile
===================================================================
RCS file: /cvs/ports/games/godot/pack1/Makefile,v
diff -u -p -r1.4 Makefile
--- Makefile	16 May 2026 00:44:44 -0000	1.4
+++ Makefile	29 May 2026 20:27:40 -0000
@@ -6,12 +6,12 @@ COMMENT-sharp=	.NET libs for mono/C# mod
 VERSION =	3.6.2
 PKGNAME =	godot3-${VERSION}
 SHARPFILES_V =	3.5.2
-REVISION =	1
+REVISION =	2
 
 MULTI_PACKAGES =	-main -sharp
 
 WANTLIB += ${COMPILER_LIBCXX} BulletCollision BulletDynamics BulletSoftBody
-WANTLIB += GL LinearMath X11 Xcursor Xext
+WANTLIB += GL LinearMath SDL3 X11 Xcursor Xext
 WANTLIB += Xi Xinerama Xrandr Xrender c enet execinfo
 WANTLIB += intl m mbedcrypto mbedtls mbedx509 mpcdec ogg opus
 WANTLIB += opusfile pcre2-32 sndio steam_api theora theoradec
@@ -61,6 +61,7 @@ LIB_DEPENDS =		archivers/zstd \
 			devel/bullet \
 			devel/gettext,-runtime \
 			devel/pcre2 \
+			devel/sdl3 \
 			games/goldberg_emulator \
 			graphics/libwebp \
 			multimedia/libtheora \
@@ -111,8 +112,6 @@ CXXFLAGS +=	-I${LOCALBASE}/include/goldb
 
 post-extract:
 	cp -R	${FILESDIR}/sndio ${WRKDIST}/drivers
-	cp	${FILESDIR}/ujoy/joypad_openbsd.{cpp,h} \
-		${WRKDIST}/platform/x11/
 .if ${PROPERTIES:Mmono}
 	mkdir -p ${PORTHOME}/.nuget
 	mv	${WRKDIR}/godot-${SHARPFILES_V}-nuget-packages ${PORTHOME}/.nuget/packages
Index: files/ujoy/joypad_openbsd.cpp
===================================================================
RCS file: files/ujoy/joypad_openbsd.cpp
diff -N files/ujoy/joypad_openbsd.cpp
--- files/ujoy/joypad_openbsd.cpp	4 Mar 2026 05:35:04 -0000	1.1.1.1
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,470 +0,0 @@
-/*************************************************************************/
-/*  joypad_openbsd.cpp                                                     */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-//author: Thomas Frohwein <[email protected]>
-#ifdef JOYDEV_ENABLED
-
-#include "joypad_openbsd.h"
-
-#include <sys/param.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-extern "C" {
-	#include <dev/usb/usb.h>
-	#include <dev/usb/usbhid.h>
-	#include <usbhid.h>
-}
-
-#define HUG_DPAD_UP         0x90
-#define HUG_DPAD_DOWN       0x91
-#define HUG_DPAD_RIGHT      0x92
-#define HUG_DPAD_LEFT       0x93
-
-#define HAT_CENTERED        0x00
-#define HAT_UP              0x01
-#define HAT_RIGHT           0x02
-#define HAT_DOWN            0x04
-#define HAT_LEFT            0x08
-#define HAT_RIGHTUP         (HAT_RIGHT|HAT_UP)
-#define HAT_RIGHTDOWN       (HAT_RIGHT|HAT_DOWN)
-#define HAT_LEFTUP          (HAT_LEFT|HAT_UP)
-#define HAT_LEFTDOWN        (HAT_LEFT|HAT_DOWN)
-
-#define REP_BUF_DATA(rep) ((rep)->buf->ucr_data)
-
-static struct
-{
-	int uhid_report;
-	hid_kind_t kind;
-	const char *name;
-} const repinfo[] = {
-	{UHID_INPUT_REPORT, hid_input, "input"},
-	{UHID_OUTPUT_REPORT, hid_output, "output"},
-	{UHID_FEATURE_REPORT, hid_feature, "feature"}
-};
-
-enum
-{
-	REPORT_INPUT,
-	REPORT_OUTPUT,
-	REPORT_FEATURE
-};
-
-enum
-{
-	JOYAXE_X,
-	JOYAXE_Y,
-	JOYAXE_Z,
-	JOYAXE_SLIDER,
-	JOYAXE_WHEEL,
-	JOYAXE_RX,
-	JOYAXE_RY,
-	JOYAXE_RZ,
-	JOYAXE_count
-};
-
-static int
-report_alloc(struct report *r, struct report_desc *rd, int repind)
-{
-	int len;
-	len = hid_report_size(rd, repinfo[repind].kind, r->rid);
-	if (len < 0) {
-		ERR_PRINT("Negative HID report siz");
-		return ERR_PARAMETER_RANGE_ERROR;
-	}
-	r->size = len;
-
-	if (r->size > 0) {
-		r->buf = (usb_ctl_report *)malloc(sizeof(*r->buf) - sizeof(REP_BUF_DATA(r)) + r->size);
-		if (r->buf == NULL)
-			return ERR_OUT_OF_MEMORY;
-	} else {
-		r->buf = NULL;
-	}
-
-	r->status = report::SREPORT_CLEAN;
-	return OK;
-}
-
-static int
-usage_to_joyaxe(unsigned usage)
-{
-	switch (usage) {
-	case HUG_X:
-		return JOYAXE_X;
-	case HUG_Y:
-		return JOYAXE_Y;
-	case HUG_Z:
-		return JOYAXE_Z;
-	case HUG_SLIDER:
-		return JOYAXE_SLIDER;
-	case HUG_WHEEL:
-		return JOYAXE_WHEEL;
-	case HUG_RX:
-		return JOYAXE_RX;
-	case HUG_RY:
-		return JOYAXE_RY;
-	case HUG_RZ:
-		return JOYAXE_RZ;
-	default:
-		return -1;
-	}
-}
-
-static unsigned
-hatval_conversion(int hatval)
-{
-	static const unsigned hat_dir_map[8] = {
-		HAT_UP, HAT_RIGHTUP, HAT_RIGHT, HAT_RIGHTDOWN,
-		HAT_DOWN, HAT_LEFTDOWN, HAT_LEFT, HAT_LEFTUP
-	};
-	if ((hatval & 7) == hatval)
-		return hat_dir_map[hatval];
-	else
-		return HAT_CENTERED;
-}
-
-/* calculate the value from the state of the dpad */
-int
-dpad_conversion(int *dpad)
-{
-	if (dpad[2]) {
-		if (dpad[0])
-			return HAT_RIGHTUP;
-		else if (dpad[1])
-			return HAT_RIGHTDOWN;
-		else
-			return HAT_RIGHT;
-	} else if (dpad[3]) {
-		if (dpad[0])
-			return HAT_LEFTUP;
-		else if (dpad[1])
-			return HAT_LEFTDOWN;
-		else
-			return HAT_LEFT;
-	} else if (dpad[0]) {
-		return HAT_UP;
-	} else if (dpad[1]) {
-		return HAT_DOWN;
-	}
-	return HAT_CENTERED;
-}
-
-JoypadOpenBSD::Joypad::Joypad() {
-	fd = -1;
-	dpad = 0;
-	devpath = "";
-}
-
-JoypadOpenBSD::Joypad::~Joypad() {
-}
-
-void JoypadOpenBSD::Joypad::reset() {
-	dpad = 0;
-	fd = -1;
-
-	for (int i = 0; i < MAX_ABS; ++i) {
-		abs_map[i] = -1;
-		curr_axis[i] = 0;
-	}
-}
-
-JoypadOpenBSD::JoypadOpenBSD(InputDefault *in) {
-	input = in;
-	joy_thread.start(joy_thread_func, this);
-	hid_init(NULL);
-}
-
-JoypadOpenBSD::~JoypadOpenBSD() {
-	exit_monitor.set();
-	joy_thread.wait_to_finish();
-	close_joypad();
-}
-
-void JoypadOpenBSD::joy_thread_func(void *p_user) {
-	if (p_user) {
-		JoypadOpenBSD *joy = (JoypadOpenBSD *)p_user;
-		joy->run_joypad_thread();
-	}
-}
-
-void JoypadOpenBSD::run_joypad_thread() {
-	monitor_joypads();
-}
-
-void JoypadOpenBSD::monitor_joypads() {
-	while (!exit_monitor.is_set()) {
-		joy_mutex.lock();
-		for (int i = 0; i < 32; i++) {
-			char fname[64];
-			sprintf(fname, "/dev/ujoy/%d", i);
-			if (attached_devices.find(fname) == -1) {
-				open_joypad(fname);
-			}
-		}
-		joy_mutex.unlock();
-		usleep(1000000); // 1s
-	}
-}
-
-int JoypadOpenBSD::get_joy_from_path(String p_path) const {
-	for (int i = 0; i < JOYPADS_MAX; i++) {
-		if (joypads[i].devpath == p_path) {
-			return i;
-		}
-	}
-	return -2;
-}
-
-void JoypadOpenBSD::close_joypad(int p_id) {
-	if (p_id == -1) {
-		for (int i = 0; i < JOYPADS_MAX; i++) {
-			close_joypad(i);
-		};
-		return;
-	} else if (p_id < 0)
-		return;
-
-	Joypad &joy = joypads[p_id];
-
-	if (joy.fd != -1) {
-		close(joy.fd);
-		joy.fd = -1;
-		attached_devices.remove(attached_devices.find(joy.devpath));
-		input->joy_connection_changed(p_id, false, "");
-	};
-}
-
-void JoypadOpenBSD::setup_joypad_properties(int p_id) {
-	Error err;
-	Joypad *joy = &joypads[p_id];
-	struct hid_item hitem;
-	struct hid_data *hdata;
-	struct report *rep = NULL;
-	int i;
-	int ax;
-
-	for (ax = 0; ax < JOYAXE_count; ax++)
-		joy->axis_map[ax] = -1;
-
-	joy->type = Joypad::BSDJOY_UHID;	// TODO: hardcoded; later check if /dev/joyX or /dev/ujoy/X
-	joy->repdesc = hid_get_report_desc(joy->fd);
-	if (joy->repdesc == NULL) {
-		ERR_PRINT("getting USB report descriptor");
-	}
-	rep = &joy->inreport;
-	if (ioctl(joy->fd, USB_GET_REPORT_ID, &rep->rid) < 0) {
-		rep->rid = -1;	/* XXX */
-	}
-	err = (Error)report_alloc(rep, joy->repdesc, REPORT_INPUT);
-	if (err != OK) {
-		ERR_PRINT("allocating report descriptor");
-	}
-	if (rep->size <= 0) {
-		ERR_PRINT("input report descriptor has invalid length");
-	}
-	hdata = hid_start_parse(joy->repdesc, 1 << hid_input, rep->rid);
-	if (hdata == NULL) {
-		ERR_PRINT("cannot start HID parser");
-	}
-
-	int num_buttons = 0;
-	int num_axes = 0;
-	int num_hats = 0;
-
-	while (hid_get_item(hdata, &hitem)) {
-		switch (hitem.kind) {
-		case hid_input:
-			switch (HID_PAGE(hitem.usage)) {
-			case HUP_GENERIC_DESKTOP: {
-				unsigned usage = HID_USAGE(hitem.usage);
-				int joyaxe = usage_to_joyaxe(usage);
-				if (joyaxe >= 0) {
-					joy->axis_map[joyaxe] = 1;
-				} else if (usage == HUG_HAT_SWITCH || usage == HUG_DPAD_UP) {
-					num_hats++;
-				}
-				break;
-			}
-			case HUP_BUTTON:
-				num_buttons++;
-				break;
-			default:
-				break;
-			}
-		default:
-			break;
-		}
-	}
-	hid_end_parse(hdata);
-	for (i = 0; i < JOYAXE_count; i++)
-		if (joy->axis_map[i] > 0)
-			joy->axis_map[i] = num_axes++;
-			
-	if (num_axes == 0 && num_buttons == 0 && num_hats == 0) {
-		ERR_PRINT("Not a joystick!");
-	} else {
-		printf("joypad %d: %d axes %d buttons %d hats\n", p_id, num_axes, num_buttons, num_hats);
-	}
-	joy->force_feedback = false;
-	joy->ff_effect_timestamp = 0;
-}
-
-void JoypadOpenBSD::open_joypad(const char *p_path) {
-	int joy_num = input->get_unused_joy_id();
-	if (joy_num < 0) {
-		ERR_PRINT("no ID available to assign to joypad device");
-		return;
-	}
-	int fd = open(p_path, O_RDONLY | O_NONBLOCK);
-	if (fd != -1 && joy_num != -1) {
-		// add to attached devices so we don't try to open it again
-		attached_devices.push_back(String(p_path));
-
-		String name = "";
-		joypads[joy_num].reset();
-
-		Joypad &joy = joypads[joy_num];
-		joy.fd = fd;
-		joy.devpath = String(p_path);
-		setup_joypad_properties(joy_num);
-		String uidname = "00";
-		input->joy_connection_changed(joy_num, true, name, uidname);
-	}
-}
-
-void JoypadOpenBSD::joypad_vibration_start(int p_id, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) {
-	/* not supported */
-}
-
-void JoypadOpenBSD::joypad_vibration_stop(int p_id, uint64_t p_timestamp) {
-	/* not supported */
-}
-
-float JoypadOpenBSD::axis_correct(int min, int max, int p_value) const {
-	// Convert to a value between -1.0 and 1.0f.
-	return 2.0f * (p_value - min) / (max - min) - 1.0f;
-}
-
-void JoypadOpenBSD::process_joypads() {
-	struct hid_item hitem;
-	struct hid_data *hdata;
-	struct report *rep;
-	int nbutton, naxe = -1;
-	int v;
-	int dpad[4] = { 0, 0, 0, 0 };
-	int actualbutton;
-
-	if (joy_mutex.try_lock() != OK) {
-		return;
-	}
-	for (int i = 0; i < JOYPADS_MAX; i++) {
-		if (joypads[i].fd == -1) continue;
-
-		Joypad *joy = &joypads[i];
-		rep = &joy->inreport;
-
-		while (true) {
-			ssize_t r = read(joy->fd, REP_BUF_DATA(rep), rep->size);
-			if (r < 0 || (size_t)r != rep->size) {
-				break;
-			}
-
-			hdata = hid_start_parse(joy->repdesc, 1 << hid_input, rep->rid);
-			if (hdata == NULL) {
-				continue;
-			}
-
-			for (nbutton = 0; hid_get_item(hdata, &hitem) > 0;) {
-				(void)nbutton;
-				switch (hitem.kind) {
-				case hid_input:
-					unsigned usage;
-					int joyaxe;
-					switch (HID_PAGE(hitem.usage)) {
-					case HUP_GENERIC_DESKTOP:
-						usage = HID_USAGE(hitem.usage);
-						joyaxe = usage_to_joyaxe(usage);
-						if (joyaxe >= 0) {
-							naxe = joy->axis_map[joyaxe];
-							(void)naxe;
-							v = hid_get_data(REP_BUF_DATA(rep), &hitem);
-							/* XInput controllermapping relies on inverted Y axes.
-							 * These devices have a 16bit signed space, as opposed
-							 * to older DInput devices (8bit unsigned), so
-							 * hitem.logical_maximum can be used to differentiate them.
-							 */
-							if ((joyaxe == JOYAXE_Y || joyaxe == JOYAXE_RY)
-							    && hitem.logical_maximum > 255) {
-								    if (v != 0)
-									    v = ~v;
-							}
-							input->joy_axis(i, joyaxe, axis_correct(hitem.logical_minimum, hitem.logical_maximum, v));
-							break;
-						} else if (usage == HUG_HAT_SWITCH) {
-							v = hid_get_data(REP_BUF_DATA(rep), &hitem);
-							joy->dpad = hatval_conversion(v);
-						} else if (usage == HUG_DPAD_UP) {
-							dpad[0] = hid_get_data(REP_BUF_DATA(rep), &hitem);
-							joy->dpad = dpad_conversion(dpad);
-						} else if (usage == HUG_DPAD_DOWN) {
-							dpad[1] = hid_get_data(REP_BUF_DATA(rep), &hitem);
-							joy->dpad = dpad_conversion(dpad);
-						} else if (usage == HUG_DPAD_RIGHT) {
-							dpad[2] = hid_get_data(REP_BUF_DATA(rep), &hitem);
-							joy->dpad = dpad_conversion(dpad);
-						} else if (usage == HUG_DPAD_LEFT) {
-							dpad[3] = hid_get_data(REP_BUF_DATA(rep), &hitem);
-							joy->dpad = dpad_conversion(dpad);
-						}
-						input->joy_hat(i, joy->dpad);
-						break;
-					case HUP_BUTTON:
-						v = hid_get_data(REP_BUF_DATA(rep), &hitem);
-						actualbutton = HID_USAGE(hitem.usage) - 1;	// buttons are zero-based
-						input->joy_button(i, actualbutton, v);
-					default:
-						continue;
-					}
-					break;
-				default:
-					break;
-				}
-			}
-			hid_end_parse(hdata);
-		}
-	}
-	joy_mutex.unlock();
-}
-
-#endif
Index: files/ujoy/joypad_openbsd.h
===================================================================
RCS file: files/ujoy/joypad_openbsd.h
diff -N files/ujoy/joypad_openbsd.h
--- files/ujoy/joypad_openbsd.h	4 Mar 2026 05:35:04 -0000	1.1.1.1
+++ /dev/null	1 Jan 1970 00:00:00 -0000
@@ -1,121 +0,0 @@
-/*************************************************************************/
-/*  joypad_openbsd.h                                                       */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-//author: Thomas Frohwein <[email protected]>
-#ifndef JOYPAD_OPENBSD_H
-#define JOYPAD_OPENBSD_H
-
-#ifdef JOYDEV_ENABLED
-#include "core/os/mutex.h"
-#include "core/os/thread.h"
-#include "main/input_default.h"
-
-struct input_absinfo;
-
-struct report
-{
-	struct usb_ctl_report *buf;	/* Buffer */
-	size_t size;			/* Buffer size */
-	int rid;			/* Report ID */
-	enum
-	{
-		SREPORT_UNINIT,
-		SREPORT_CLEAN,
-		SREPORT_DIRTY
-	} status;
-};
-
-class JoypadOpenBSD {
-public:
-	JoypadOpenBSD(InputDefault *in);
-	~JoypadOpenBSD();
-	void process_joypads();
-
-private:
-	enum {
-		JOYPADS_MAX = 16,
-		MAX_ABS = 63,
-		MAX_KEY = 767, // Hack because <linux/input.h> can't be included here
-	};
-
-	struct Joypad {
-		float curr_axis[MAX_ABS];
-		int key_map[MAX_KEY];
-		int abs_map[MAX_ABS];
-		int dpad;
-		int fd;
-
-		char *path;
-		enum
-		{
-			BSDJOY_UHID,		/* uhid(4) */
-			BSDJOY_JOY		/* joy(4) */
-		} type;
-		struct report_desc *repdesc;
-		struct report inreport;
-		int axis_map[8];	/* TODO: replace with JOYAXE_count */
-
-		String devpath;
-		input_absinfo *abs_info[MAX_ABS];
-
-		bool force_feedback;
-		int ff_effect_id;
-		uint64_t ff_effect_timestamp;
-
-		Joypad();
-		~Joypad();
-		void reset();
-	};
-
-	SafeFlag exit_monitor;
-	Mutex joy_mutex;
-	Thread joy_thread;
-	InputDefault *input;
-	Joypad joypads[JOYPADS_MAX];
-	Vector<String> attached_devices;
-
-	static void joy_thread_func(void *p_user);
-
-	int get_joy_from_path(String p_path) const;
-
-	void setup_joypad_properties(int p_id);
-	void close_joypad(int p_id = -1);
-	void monitor_joypads();
-	void run_joypad_thread();
-	void open_joypad(const char *p_path);
-
-	void joypad_vibration_start(int p_id, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
-	void joypad_vibration_stop(int p_id, uint64_t p_timestamp);
-
-	float axis_correct(int min, int max, int p_value) const;
-};
-
-#endif
-#endif // JOYPAD_OPENBSD_H
Index: patches/patch-SConstruct
===================================================================
RCS file: patches/patch-SConstruct
diff -N patches/patch-SConstruct
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ patches/patch-SConstruct	29 May 2026 20:27:40 -0000
@@ -0,0 +1,22 @@
+backport commit 0b3496f:
+	Add support for SDL3 joystick input driver
+
+Index: SConstruct
+--- SConstruct.orig
++++ SConstruct
+@@ -134,6 +134,7 @@ opts.Add(BoolVariable("xaudio2", "Enable the XAudio2 a
+ opts.Add(BoolVariable("disable_exceptions", "Force disabling exception handling code", True))
+ opts.Add("custom_modules", "A list of comma-separated directory paths containing custom modules to build.", "")
+ opts.Add(BoolVariable("custom_modules_recursive", "Detect custom modules recursively for each specified path.", True))
++opts.Add(BoolVariable("sdl", "Enable the SDL3 input driver", True))
+ 
+ # Advanced options
+ opts.Add(BoolVariable("dev", "If yes, alias for verbose=yes warnings=extra werror=yes", False))
+@@ -188,6 +189,7 @@ opts.Add(BoolVariable("builtin_pcre2", "Use the built-
+ opts.Add(BoolVariable("builtin_pcre2_with_jit", "Use JIT compiler for the built-in PCRE2 library", True))
+ opts.Add(BoolVariable("builtin_recast", "Use the built-in Recast library", True))
+ opts.Add(BoolVariable("builtin_rvo2", "Use the built-in RVO2 library", True))
++opts.Add(BoolVariable("builtin_sdl", "Use the built-in SDL library", False))
+ opts.Add(BoolVariable("builtin_squish", "Use the built-in squish library", True))
+ opts.Add(BoolVariable("builtin_xatlas", "Use the built-in xatlas library", True))
+ opts.Add(BoolVariable("builtin_zlib", "Use the built-in zlib library", True))
Index: patches/patch-core_os_input_h
===================================================================
RCS file: patches/patch-core_os_input_h
diff -N patches/patch-core_os_input_h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ patches/patch-core_os_input_h	29 May 2026 20:27:40 -0000
@@ -0,0 +1,15 @@
+backport commit 0b3496f:
+	Add support for SDL3 joystick input driver
+
+Index: core/os/input.h
+--- core/os/input.h.orig
++++ core/os/input.h
+@@ -95,7 +95,7 @@ class Input : public Object { (public)
+ 	virtual float get_joy_axis(int p_device, int p_axis) const = 0;
+ 	virtual String get_joy_name(int p_idx) = 0;
+ 	virtual Array get_connected_joypads() = 0;
+-	virtual void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid) = 0;
++	virtual void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = "", const Dictionary &p_joypad_info = Dictionary()) = 0;
+ 	virtual void add_joy_mapping(String p_mapping, bool p_update_existing = false) = 0;
+ 	virtual void remove_joy_mapping(String p_guid) = 0;
+ 	virtual bool is_joy_known(int p_device) = 0;
Index: patches/patch-drivers_SCsub
===================================================================
RCS file: /cvs/ports/games/godot/pack1/patches/patch-drivers_SCsub,v
diff -u -p -r1.1.1.1 patch-drivers_SCsub
--- patches/patch-drivers_SCsub	4 Mar 2026 05:35:04 -0000	1.1.1.1
+++ patches/patch-drivers_SCsub	29 May 2026 20:27:40 -0000
@@ -1,5 +1,8 @@
 add sndio
 
+backport commit 0b3496f:
+Add support for SDL3 joystick input driver
+
 Index: drivers/SCsub
 --- drivers/SCsub.orig
 +++ drivers/SCsub
@@ -11,3 +14,15 @@ Index: drivers/SCsub
  if env["platform"] == "windows":
      SConscript("wasapi/SCsub")
  if env["xaudio2"]:
+@@ -30,6 +31,11 @@ if env["platform"] != "server":
+     SConscript("gl_context/SCsub")
+ else:
+     SConscript("dummy/SCsub")
++
++# Input drivers
++if env["sdl"] and env["platform"] in ["x11", "macos", "windows"]:
++    # TODO: Evaluate support for Android, iOS, and Web.
++    SConscript("sdl/SCsub")
+ 
+ # Core dependencies
+ SConscript("png/SCsub")
Index: patches/patch-drivers_sdl_SCsub
===================================================================
RCS file: patches/patch-drivers_sdl_SCsub
diff -N patches/patch-drivers_sdl_SCsub
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ patches/patch-drivers_sdl_SCsub	29 May 2026 20:27:40 -0000
@@ -0,0 +1,215 @@
+backport commit 0b3496f:
+Add support for SDL3 joystick input driver
+
+Index: drivers/sdl/SCsub
+--- drivers/sdl/SCsub.orig
++++ drivers/sdl/SCsub
+@@ -0,0 +1,208 @@
++#!/usr/bin/env python
++
++Import("env")
++
++env_sdl = env.Clone()
++
++# Thirdparty source files
++
++thirdparty_obj = []
++
++if env["builtin_sdl"]:
++    thirdparty_dir = "#thirdparty/sdl/"
++
++    # Use our own SDL_build_config_private.h.
++    env_sdl.Prepend(CPPDEFINES=["SDL_PLATFORM_PRIVATE"])
++
++    # Common sources.
++
++    env_sdl.Prepend(
++        CPPPATH=[
++            thirdparty_dir,
++            thirdparty_dir + "include",
++            thirdparty_dir + "include/build_config",
++            ".",  # SDL_build_config_private.h
++        ]
++    )
++
++    thirdparty_sources = [
++        "SDL.c",
++        "SDL_assert.c",
++        "SDL_error.c",
++        "SDL_guid.c",
++        "SDL_hashtable.c",
++        "SDL_hints.c",
++        "SDL_list.c",
++        "SDL_log.c",
++        "SDL_properties.c",
++        "SDL_utils.c",
++        "atomic/SDL_atomic.c",
++        "atomic/SDL_spinlock.c",
++        "events/SDL_events.c",
++        "events/SDL_eventwatch.c",
++        "haptic/SDL_haptic.c",
++        "io/SDL_iostream.c",
++        "joystick/SDL_gamepad.c",
++        "joystick/SDL_joystick.c",
++        "joystick/SDL_steam_virtual_gamepad.c",
++        "joystick/controller_type.c",
++        "libm/e_atan2.c",
++        "libm/e_exp.c",
++        "libm/e_fmod.c",
++        "libm/e_log.c",
++        "libm/e_log10.c",
++        "libm/e_pow.c",
++        "libm/e_rem_pio2.c",
++        "libm/e_sqrt.c",
++        "libm/k_cos.c",
++        "libm/k_rem_pio2.c",
++        "libm/k_sin.c",
++        "libm/k_tan.c",
++        "libm/s_atan.c",
++        "libm/s_copysign.c",
++        "libm/s_cos.c",
++        "libm/s_fabs.c",
++        "libm/s_floor.c",
++        "libm/s_isinf.c",
++        "libm/s_isinff.c",
++        "libm/s_isnan.c",
++        "libm/s_isnanf.c",
++        "libm/s_modf.c",
++        "libm/s_scalbn.c",
++        "libm/s_sin.c",
++        "libm/s_tan.c",
++        "sensor/SDL_sensor.c",
++        "sensor/dummy/SDL_dummysensor.c",
++        "stdlib/SDL_crc16.c",
++        "stdlib/SDL_crc32.c",
++        "stdlib/SDL_getenv.c",
++        "stdlib/SDL_iconv.c",
++        "stdlib/SDL_malloc.c",
++        "stdlib/SDL_memcpy.c",
++        "stdlib/SDL_memmove.c",
++        "stdlib/SDL_memset.c",
++        "stdlib/SDL_mslibc.c",
++        "stdlib/SDL_murmur3.c",
++        "stdlib/SDL_qsort.c",
++        "stdlib/SDL_random.c",
++        "stdlib/SDL_stdlib.c",
++        "stdlib/SDL_string.c",
++        "stdlib/SDL_strtokr.c",
++        "thread/SDL_thread.c",
++        "thread/generic/SDL_syscond.c",
++        "thread/generic/SDL_sysrwlock.c",
++        "thread/generic/SDL_systhread.c",
++        "timer/SDL_timer.c",
++    ]
++
++    # HIDAPI
++    thirdparty_sources += [
++        "hidapi/SDL_hidapi.c",
++        "joystick/hidapi/SDL_hidapi_combined.c",
++        "joystick/hidapi/SDL_hidapi_gamecube.c",
++        "joystick/hidapi/SDL_hidapijoystick.c",
++        "joystick/hidapi/SDL_hidapi_luna.c",
++        "joystick/hidapi/SDL_hidapi_ps3.c",
++        "joystick/hidapi/SDL_hidapi_ps4.c",
++        "joystick/hidapi/SDL_hidapi_ps5.c",
++        "joystick/hidapi/SDL_hidapi_rumble.c",
++        "joystick/hidapi/SDL_hidapi_shield.c",
++        "joystick/hidapi/SDL_hidapi_stadia.c",
++        "joystick/hidapi/SDL_hidapi_steam.c",
++        "joystick/hidapi/SDL_hidapi_steamdeck.c",
++        "joystick/hidapi/SDL_hidapi_steam_hori.c",
++        "joystick/hidapi/SDL_hidapi_switch.c",
++        "joystick/hidapi/SDL_hidapi_wii.c",
++        "joystick/hidapi/SDL_hidapi_xbox360.c",
++        "joystick/hidapi/SDL_hidapi_xbox360w.c",
++        "joystick/hidapi/SDL_hidapi_xboxone.c",
++    ]
++
++    # Platform specific sources.
++
++    if env["platform"] == "linuxbsd":
++        # TODO: Check support for BSD systems.
++        env_sdl.Append(CPPDEFINES=["SDL_PLATFORM_LINUX"])
++        thirdparty_sources += [
++            "core/linux/SDL_dbus.c",
++            "core/linux/SDL_evdev.c",
++            "core/linux/SDL_evdev_capabilities.c",
++            "core/linux/SDL_evdev_kbd.c",
++            "core/linux/SDL_fcitx.c",
++            "core/linux/SDL_ibus.c",
++            "core/linux/SDL_ime.c",
++            "core/linux/SDL_system_theme.c",
++            "core/linux/SDL_threadprio.c",
++            "core/linux/SDL_udev.c",
++            "core/unix/SDL_appid.c",
++            "core/unix/SDL_poll.c",
++            "haptic/linux/SDL_syshaptic.c",
++            "joystick/linux/SDL_sysjoystick.c",
++            "loadso/dlopen/SDL_sysloadso.c",
++            "thread/pthread/SDL_syscond.c",
++            "thread/pthread/SDL_sysmutex.c",
++            "thread/pthread/SDL_sysrwlock.c",
++            "thread/pthread/SDL_syssem.c",
++            "thread/pthread/SDL_systhread.c",
++            "thread/pthread/SDL_systls.c",
++            "timer/unix/SDL_systimer.c",
++        ]
++
++    elif env["platform"] == "macos":
++        env_sdl.Append(CPPDEFINES=["SDL_PLATFORM_MACOS"])
++        thirdparty_sources += [
++            "core/unix/SDL_appid.c",
++            "core/unix/SDL_poll.c",
++            "haptic/darwin/SDL_syshaptic.c",
++            "joystick/darwin/SDL_iokitjoystick.c",
++            "joystick/apple/SDL_mfijoystick.m",
++            "thread/pthread/SDL_syscond.c",
++            "thread/pthread/SDL_sysmutex.c",
++            "thread/pthread/SDL_sysrwlock.c",
++            "thread/pthread/SDL_syssem.c",
++            "thread/pthread/SDL_systhread.c",
++            "thread/pthread/SDL_systls.c",
++            "timer/unix/SDL_systimer.c",
++        ]
++
++    elif env["platform"] == "windows":
++        env_sdl.Append(CPPDEFINES=["SDL_PLATFORM_WINDOWS"])
++        thirdparty_sources += [
++            "core/windows/SDL_gameinput.c",
++            "core/windows/SDL_hid.c",
++            "core/windows/SDL_immdevice.c",
++            "core/windows/SDL_windows.c",
++            "core/windows/SDL_xinput.c",
++            "core/windows/pch.c",
++            "haptic/windows/SDL_dinputhaptic.c",
++            "haptic/windows/SDL_windowshaptic.c",
++            "joystick/windows/SDL_dinputjoystick.c",
++            "joystick/windows/SDL_rawinputjoystick.c",
++            "joystick/windows/SDL_windows_gaming_input.c",
++            "joystick/windows/SDL_windowsjoystick.c",
++            "joystick/windows/SDL_xinputjoystick.c",
++            "thread/windows/SDL_syscond_cv.c",
++            "thread/windows/SDL_sysmutex.c",
++            "thread/windows/SDL_sysrwlock_srw.c",
++            "thread/windows/SDL_syssem.c",
++            "thread/windows/SDL_systhread.c",
++            "thread/windows/SDL_systls.c",
++            "timer/windows/SDL_systimer.c",
++        ]
++
++    thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
++
++    env_thirdparty = env_sdl.Clone()
++    env_thirdparty.disable_warnings()
++    env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
++    env.drivers_sources += thirdparty_obj
++
++# Godot source files
++
++driver_obj = []
++
++env_sdl.add_source_files(driver_obj, "*.cpp")
++env.drivers_sources += driver_obj
++
++# Needed to force rebuilding the driver files when the thirdparty library is updated.
++env.Depends(driver_obj, thirdparty_obj)
Index: patches/patch-drivers_sdl_SDL_build_config_private_h
===================================================================
RCS file: patches/patch-drivers_sdl_SDL_build_config_private_h
diff -N patches/patch-drivers_sdl_SDL_build_config_private_h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ patches/patch-drivers_sdl_SDL_build_config_private_h	29 May 2026 20:27:40 -0000
@@ -0,0 +1,137 @@
+backport commit 0b3496f:
+Add support for SDL3 joystick input driver
+
+Index: drivers/sdl/SDL_build_config_private.h
+--- drivers/sdl/SDL_build_config_private.h.orig
++++ drivers/sdl/SDL_build_config_private.h
+@@ -0,0 +1,130 @@
++/**************************************************************************/
++/*  SDL_build_config_private.h                                            */
++/**************************************************************************/
++/*                         This file is part of:                          */
++/*                             GODOT ENGINE                               */
++/*                        https://godotengine.org                         */
++/**************************************************************************/
++/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
++/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
++/*                                                                        */
++/* Permission is hereby granted, free of charge, to any person obtaining  */
++/* a copy of this software and associated documentation files (the        */
++/* "Software"), to deal in the Software without restriction, including    */
++/* without limitation the rights to use, copy, modify, merge, publish,    */
++/* distribute, sublicense, and/or sell copies of the Software, and to     */
++/* permit persons to whom the Software is furnished to do so, subject to  */
++/* the following conditions:                                              */
++/*                                                                        */
++/* The above copyright notice and this permission notice shall be         */
++/* included in all copies or substantial portions of the Software.        */
++/*                                                                        */
++/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
++/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
++/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
++/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
++/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
++/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
++/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
++/**************************************************************************/
++
++#pragma once
++
++#define SDL_build_config_h_
++
++#include <SDL3/SDL_platform_defines.h>
++
++#define HAVE_STDARG_H 1
++#define HAVE_STDDEF_H 1
++
++// Here we disable SDL subsystems that are not going to be used
++#define SDL_CPUINFO_DISABLED 1
++#define SDL_AUDIO_DISABLED 1
++#define SDL_PROCESS_DUMMY 1
++#define SDL_LOADSO_DUMMY 1
++#define SDL_VIDEO_DISABLED 1
++#define SDL_CAMERA_DISABLED 1
++#define SDL_DIALOG_DISABLED 1
++#define SDL_FILESYSTEM_DUMMY 1
++#define SDL_FSOPS_DUMMY 1
++#define SDL_SENSOR_DISABLED 1
++#define SDL_GPU_DISABLED 1
++#define SDL_RENDER_DISABLED 1
++#define SDL_POWER_DISABLED 1
++#define SDL_LEAN_AND_MEAN 1
++
++// Windows defines
++#if defined(SDL_PLATFORM_WINDOWS)
++
++#define SDL_PLATFORM_PRIVATE_NAME "Windows"
++#define HAVE_LIBC 1
++#define HAVE_DINPUT_H 1
++#define HAVE_XINPUT_H 1
++#if defined(_WIN32_MAXVER) && _WIN32_MAXVER >= 0x0A00 /* Windows 10 SDK */
++#define HAVE_WINDOWS_GAMING_INPUT_H 1
++#define SDL_JOYSTICK_WGI 1
++#endif
++#define SDL_JOYSTICK_DINPUT 1
++#define SDL_JOYSTICK_HIDAPI 1
++#define SDL_JOYSTICK_RAWINPUT 1
++#define SDL_JOYSTICK_XINPUT 1
++#define SDL_HAPTIC_DINPUT 1
++#define SDL_THREAD_GENERIC_COND_SUFFIX 1
++#define SDL_THREAD_GENERIC_RWLOCK_SUFFIX 1
++#define SDL_THREAD_WINDOWS 1
++#define SDL_TIMER_WINDOWS 1
++
++// Linux defines
++#elif defined(SDL_PLATFORM_LINUX)
++
++#define SDL_PLATFORM_PRIVATE_NAME "Linux"
++#define SDL_PLATFORM_UNIX 1
++
++#define HAVE_STDIO_H 1
++#define HAVE_LINUX_INPUT_H 1
++#define HAVE_POLL 1
++
++// TODO: handle dynamic loading with SOWRAP_ENABLED
++
++// (even though DBus can also be loaded with SOWRAP_ENABLED, we load it
++// statically regardless of SOWRAP_ENABLED, because otherwise SDL won't compile)
++#ifdef DBUS_ENABLED
++#define HAVE_DBUS_DBUS_H 1
++#endif
++
++#if defined(UDEV_ENABLED) && !defined(SOWRAP_ENABLED)
++#define HAVE_LIBUDEV_H 1
++#endif
++
++#define SDL_LOADSO_DLOPEN 1
++#define SDL_HAPTIC_LINUX 1
++#define SDL_TIMER_UNIX 1
++#define SDL_JOYSTICK_LINUX 1
++#define SDL_INPUT_LINUXEV 1
++#define SDL_THREAD_PTHREAD 1
++
++// MacOS defines
++#elif defined(SDL_PLATFORM_MACOS)
++
++#define SDL_PLATFORM_PRIVATE_NAME "macOS"
++#define SDL_PLATFORM_UNIX 1
++#define HAVE_STDIO_H 1
++#define HAVE_LIBC 1
++#define SDL_HAPTIC_IOKIT 1
++#define SDL_JOYSTICK_IOKIT 1
++#define SDL_JOYSTICK_MFI 1
++#define SDL_TIMER_UNIX 1
++#define SDL_THREAD_PTHREAD 1
++
++// Other platforms are not supported (for now)
++#else
++#error "No SDL build config was found for this platform. Setup one before compiling the engine."
++#endif
++
++#if !defined(HAVE_STDINT_H) && !defined(_STDINT_H_)
++#define HAVE_STDINT_H 1
++#endif /* !_STDINT_H_ && !HAVE_STDINT_H */
++
++#ifdef __GNUC__
++#define HAVE_GCC_SYNC_LOCK_TEST_AND_SET 1
++#endif
Index: patches/patch-drivers_sdl_joypad_sdl_cpp
===================================================================
RCS file: patches/patch-drivers_sdl_joypad_sdl_cpp
diff -N patches/patch-drivers_sdl_joypad_sdl_cpp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ patches/patch-drivers_sdl_joypad_sdl_cpp	29 May 2026 20:27:40 -0000
@@ -0,0 +1,281 @@
+backport commit 0b3496f:
+	Add support for SDL3 joystick input driver
+
+Index: drivers/sdl/joypad_sdl.cpp
+--- drivers/sdl/joypad_sdl.cpp.orig
++++ drivers/sdl/joypad_sdl.cpp
+@@ -0,0 +1,274 @@
++/**************************************************************************/
++/*  joypad_sdl.cpp                                                        */
++/**************************************************************************/
++/*                         This file is part of:                          */
++/*                             GODOT ENGINE                               */
++/*                        https://godotengine.org                         */
++/**************************************************************************/
++/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
++/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
++/*                                                                        */
++/* Permission is hereby granted, free of charge, to any person obtaining  */
++/* a copy of this software and associated documentation files (the        */
++/* "Software"), to deal in the Software without restriction, including    */
++/* without limitation the rights to use, copy, modify, merge, publish,    */
++/* distribute, sublicense, and/or sell copies of the Software, and to     */
++/* permit persons to whom the Software is furnished to do so, subject to  */
++/* the following conditions:                                              */
++/*                                                                        */
++/* The above copyright notice and this permission notice shall be         */
++/* included in all copies or substantial portions of the Software.        */
++/*                                                                        */
++/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
++/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
++/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
++/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
++/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
++/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
++/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
++/**************************************************************************/
++
++#include "joypad_sdl.h"
++
++#ifdef SDL_ENABLED
++
++#include "core/os/time.h"
++#include "core/dictionary.h"
++#include "main/default_controller_mappings.h"
++
++#include <iterator>
++
++#include <SDL3/SDL.h>
++#include <SDL3/SDL_error.h>
++#include <SDL3/SDL_events.h>
++#include <SDL3/SDL_gamepad.h>
++#include <SDL3/SDL_iostream.h>
++#include <SDL3/SDL_joystick.h>
++
++JoypadSDL *JoypadSDL::singleton = nullptr;
++
++// Macro to skip the SDL joystick event handling if the device is an SDL gamepad, because
++// there are separate events for SDL gamepads
++#define SKIP_EVENT_FOR_GAMEPAD                    \
++	if (SDL_IsGamepad(sdl_event.jdevice.which)) { \
++		continue;                                 \
++	}
++
++JoypadSDL::JoypadSDL(InputDefault *in) {
++	singleton = this;
++	input = in;
++}
++
++JoypadSDL::~JoypadSDL() {
++	// Process any remaining input events
++	process_events();
++	for (int i = 0; i < InputDefault::JOYPADS_MAX; i++) {
++		if (joypads[i].attached) {
++			close_joypad(i);
++		}
++	}
++	SDL_Quit();
++	singleton = nullptr;
++}
++
++JoypadSDL *JoypadSDL::get_singleton() {
++	return singleton;
++}
++
++Error JoypadSDL::initialize() {
++	SDL_SetHint(SDL_HINT_JOYSTICK_THREAD, "1");
++	SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
++	ERR_FAIL_COND_V_MSG(!SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD), FAILED, SDL_GetError());
++
++	// Add Godot's mapping database from memory
++	int i = 0;
++	while (DefaultControllerMappings::mappings[i]) {
++		String mapping_string = DefaultControllerMappings::mappings[i++];
++		CharString data = mapping_string.utf8();
++		SDL_IOStream *rw = SDL_IOFromMem((void *)data.ptr(), data.size());
++		SDL_AddGamepadMappingsFromIO(rw, 1);
++	}
++
++	print_verbose("SDL: Init OK!");
++	return OK;
++}
++
++void JoypadSDL::process_events() {
++	// Update rumble first for it to be applied when we handle SDL events
++	for (int i = 0; i < InputDefault::JOYPADS_MAX; i++) {
++		Joypad &joy = joypads[i];
++		if (joy.attached && joy.supports_force_feedback) {
++			uint64_t timestamp = Input::get_singleton()->get_joy_vibration_timestamp(i);
++
++			// Update the joypad rumble only if there was a new vibration request
++			if (timestamp > joy.ff_effect_timestamp) {
++				joy.ff_effect_timestamp = timestamp;
++
++				SDL_Joystick *sdl_joy = SDL_GetJoystickFromID(joypads[i].sdl_instance_idx);
++				Vector2 strength = Input::get_singleton()->get_joy_vibration_strength(i);
++
++				// If the vibration was requested to start, SDL_RumbleJoystick will start it.
++				// If the vibration was requested to stop, strength and duration will be 0, so SDL will stop the rumble.
++				SDL_RumbleJoystick(
++						sdl_joy,
++						// Rumble strength goes from 0 to 0xFFFF
++						strength.x * UINT16_MAX,
++						strength.y * UINT16_MAX,
++						Input::get_singleton()->get_joy_vibration_duration(i) * 1000);
++			}
++		}
++	}
++
++	SDL_Event sdl_event;
++	while (SDL_PollEvent(&sdl_event)) {
++		// A new joypad was attached
++		if (sdl_event.type == SDL_EVENT_JOYSTICK_ADDED) {
++			int joy_id = input->get_unused_joy_id();
++			if (joy_id == -1) {
++				// There is no space for more joypads...
++				print_error("A new joypad was attached but couldn't allocate a new id for it because joypad limit was reached.");
++			} else {
++				SDL_Joystick *joy = nullptr;
++				String device_name;
++
++				// Gamepads must be opened with SDL_OpenGamepad to get their special remapped events
++				if (SDL_IsGamepad(sdl_event.jdevice.which)) {
++					SDL_Gamepad *gamepad = SDL_OpenGamepad(sdl_event.jdevice.which);
++
++					ERR_CONTINUE_MSG(!gamepad,
++							vformat("Error opening gamepad at index %d: %s", sdl_event.jdevice.which, SDL_GetError()));
++
++					device_name = SDL_GetGamepadName(gamepad);
++					joy = SDL_GetGamepadJoystick(gamepad);
++
++					print_verbose(vformat("SDL: Gamepad %s connected", SDL_GetGamepadName(gamepad)));
++				} else {
++					joy = SDL_OpenJoystick(sdl_event.jdevice.which);
++					ERR_CONTINUE_MSG(!joy,
++							vformat("Error opening joystick at index %d: %s", sdl_event.jdevice.which, SDL_GetError()));
++
++					device_name = SDL_GetJoystickName(joy);
++
++					print_verbose(vformat("SDL: Joystick %s connected", SDL_GetJoystickName(joy)));
++				}
++
++				const int MAX_GUID_SIZE = 64;
++				char guid[MAX_GUID_SIZE] = {};
++
++				SDL_GUIDToString(SDL_GetJoystickGUID(joy), guid, MAX_GUID_SIZE);
++				SDL_PropertiesID propertiesID = SDL_GetJoystickProperties(joy);
++
++				joypads[joy_id].attached = true;
++				joypads[joy_id].sdl_instance_idx = sdl_event.jdevice.which;
++				joypads[joy_id].supports_force_feedback = SDL_GetBooleanProperty(propertiesID, SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, false);
++				joypads[joy_id].guid = StringName(String(guid));
++
++				sdl_instance_id_to_joypad_id[sdl_event.jdevice.which] = joy_id;
++
++				// Skip Godot's mapping system because SDL already handles the joypad's mapping
++				Dictionary joypad_info;
++				joypad_info["mapping_handled"] = true;
++
++				Input::get_singleton()->joy_connection_changed(
++						joy_id,
++						true,
++						device_name,
++						joypads[joy_id].guid,
++						joypad_info);
++			}
++			// An event for an attached joypad
++		} else if (sdl_event.type >= SDL_EVENT_JOYSTICK_AXIS_MOTION && sdl_event.type < SDL_EVENT_FINGER_DOWN && sdl_instance_id_to_joypad_id.has(sdl_event.jdevice.which)) {
++			int joy_id = sdl_instance_id_to_joypad_id.get(sdl_event.jdevice.which);
++
++			switch (sdl_event.type) {
++				case SDL_EVENT_JOYSTICK_REMOVED:
++					Input::get_singleton()->joy_connection_changed(joy_id, false, "");
++					close_joypad(joy_id);
++					break;
++
++				case SDL_EVENT_JOYSTICK_AXIS_MOTION:
++					SKIP_EVENT_FOR_GAMEPAD;
++
++					input->joy_axis(
++							joy_id,
++							sdl_event.jaxis.axis, // Godot joy axis constants are already intentionally the same as SDL's
++							((sdl_event.jaxis.value - SDL_JOYSTICK_AXIS_MIN) / (float)(SDL_JOYSTICK_AXIS_MAX - SDL_JOYSTICK_AXIS_MIN) - 0.5f) * 2.0f);
++					break;
++
++				case SDL_EVENT_JOYSTICK_BUTTON_UP:
++				case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
++					SKIP_EVENT_FOR_GAMEPAD;
++
++					input->joy_button(
++							joy_id,
++							sdl_event.jbutton.button, // Godot button constants are intentionally the same as SDL's, so we can just straight up use them
++							sdl_event.jbutton.down);
++					break;
++
++				case SDL_EVENT_JOYSTICK_HAT_MOTION:
++					SKIP_EVENT_FOR_GAMEPAD;
++
++					input->joy_hat(
++							joy_id,
++							(InputDefault::HatMask)sdl_event.jhat.value // Godot hat masks are identical to SDL hat masks, so we can just use them as-is.
++					);
++					break;
++
++				case SDL_EVENT_GAMEPAD_AXIS_MOTION: {
++					float axis_value;
++
++					if (sdl_event.gaxis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER || sdl_event.gaxis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) {
++						// Gamepad triggers go from 0 to SDL_JOYSTICK_AXIS_MAX
++						axis_value = sdl_event.gaxis.value / (float)SDL_JOYSTICK_AXIS_MAX;
++					} else {
++						// Other axis go from SDL_JOYSTICK_AXIS_MIN to SDL_JOYSTICK_AXIS_MAX
++						axis_value =
++								((sdl_event.gaxis.value - SDL_JOYSTICK_AXIS_MIN) / (float)(SDL_JOYSTICK_AXIS_MAX - SDL_JOYSTICK_AXIS_MIN) - 0.5f) * 2.0f;
++					}
++
++					input->joy_axis(
++							joy_id,
++							sdl_event.gaxis.axis, // Godot joy axis constants are already intentionally the same as SDL's
++							axis_value);
++				} break;
++
++				// Do note SDL gamepads do not have separate events for the dpad
++				case SDL_EVENT_GAMEPAD_BUTTON_UP:
++				case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
++					input->joy_button(
++							joy_id,
++							sdl_event.gbutton.button, // Godot button constants are intentionally the same as SDL's, so we can just straight up use them
++							sdl_event.gbutton.down);
++					break;
++			}
++		}
++	}
++}
++
++#ifdef WINDOWS_ENABLED
++extern "C" {
++HWND SDL_HelperWindow;
++}
++
++// Required for DInput joypads to work
++void JoypadSDL::setup_sdl_helper_window(HWND p_hwnd) {
++	SDL_HelperWindow = p_hwnd;
++}
++#endif
++
++void JoypadSDL::close_joypad(int p_pad_idx) {
++	int sdl_instance_idx = joypads[p_pad_idx].sdl_instance_idx;
++
++	joypads[p_pad_idx].attached = false;
++	sdl_instance_id_to_joypad_id.erase(sdl_instance_idx);
++
++	if (SDL_IsGamepad(sdl_instance_idx)) {
++		SDL_Gamepad *gamepad = SDL_GetGamepadFromID(sdl_instance_idx);
++		SDL_CloseGamepad(gamepad);
++	} else {
++		SDL_Joystick *joy = SDL_GetJoystickFromID(sdl_instance_idx);
++		SDL_CloseJoystick(joy);
++	}
++}
++
++#endif // SDL_ENABLED
Index: patches/patch-drivers_sdl_joypad_sdl_h
===================================================================
RCS file: patches/patch-drivers_sdl_joypad_sdl_h
diff -N patches/patch-drivers_sdl_joypad_sdl_h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ patches/patch-drivers_sdl_joypad_sdl_h	29 May 2026 20:27:40 -0000
@@ -0,0 +1,78 @@
+backport commit 0b3496f:
+	Add support for SDL3 joystick input driver
+
+Index: drivers/sdl/joypad_sdl.h
+--- drivers/sdl/joypad_sdl.h.orig
++++ drivers/sdl/joypad_sdl.h
+@@ -0,0 +1,71 @@
++/**************************************************************************/
++/*  joypad_sdl.h                                                          */
++/**************************************************************************/
++/*                         This file is part of:                          */
++/*                             GODOT ENGINE                               */
++/*                        https://godotengine.org                         */
++/**************************************************************************/
++/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
++/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
++/*                                                                        */
++/* Permission is hereby granted, free of charge, to any person obtaining  */
++/* a copy of this software and associated documentation files (the        */
++/* "Software"), to deal in the Software without restriction, including    */
++/* without limitation the rights to use, copy, modify, merge, publish,    */
++/* distribute, sublicense, and/or sell copies of the Software, and to     */
++/* permit persons to whom the Software is furnished to do so, subject to  */
++/* the following conditions:                                              */
++/*                                                                        */
++/* The above copyright notice and this permission notice shall be         */
++/* included in all copies or substantial portions of the Software.        */
++/*                                                                        */
++/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
++/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
++/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
++/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
++/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
++/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
++/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
++/**************************************************************************/
++
++#pragma once
++
++#include "core/input_map.h"
++#include "core/os/thread.h"
++#include "main/input_default.h"
++
++typedef uint32_t SDL_JoystickID;
++typedef struct HWND__ *HWND;
++
++class JoypadSDL {
++public:
++	JoypadSDL(InputDefault *in);
++	~JoypadSDL();
++
++	static JoypadSDL *get_singleton();
++
++	Error initialize();
++	void process_events();
++#ifdef WINDOWS_ENABLED
++	void setup_sdl_helper_window(HWND p_hwnd);
++#endif
++
++private:
++	struct Joypad {
++		bool attached = false;
++		StringName guid;
++
++		SDL_JoystickID sdl_instance_idx;
++
++		bool supports_force_feedback = false;
++		uint64_t ff_effect_timestamp = 0;
++	};
++
++	static JoypadSDL *singleton;
++	InputDefault *input;
++
++	Joypad joypads[InputDefault::JOYPADS_MAX];
++	HashMap<SDL_JoystickID, int> sdl_instance_id_to_joypad_id;
++
++	void close_joypad(int p_pad_idx);
++};
Index: patches/patch-main_input_default_cpp
===================================================================
RCS file: patches/patch-main_input_default_cpp
diff -N patches/patch-main_input_default_cpp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ patches/patch-main_input_default_cpp	29 May 2026 20:27:40 -0000
@@ -0,0 +1,39 @@
+backport commit 0b3496f:
+	Add support for SDL3 joystick input driver
+
+Index: main/input_default.cpp
+--- main/input_default.cpp.orig
++++ main/input_default.cpp
+@@ -261,11 +261,12 @@ static String _hex_str(uint8_t p_byte) {
+ 	return ret;
+ };
+ 
+-void InputDefault::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid) {
++void InputDefault::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid, const Dictionary &p_joypad_info) {
+ 	_THREAD_SAFE_METHOD_
+ 	Joypad js;
+ 	js.name = p_connected ? p_name : "";
+ 	js.uid = p_connected ? p_guid : "";
++	js.info = p_connected ? p_joypad_info : Dictionary();
+ 
+ 	if (p_connected) {
+ 		String uidname = p_guid;
+@@ -278,10 +279,14 @@ void InputDefault::joy_connection_changed(int p_idx, b
+ 		js.uid = uidname;
+ 		js.connected = true;
+ 		int mapping = fallback_mapping;
+-		for (int i = 0; i < map_db.size(); i++) {
+-			if (js.uid == map_db[i].uid) {
+-				mapping = i;
+-				js.name = map_db[i].name;
++		// Bypass the mapping system if the joypad's mapping is already handled by its driver
++		// (for example, the SDL joypad driver).
++		if (!p_joypad_info.get("mapping_handled", false)) {
++			for (int i = 0; i < map_db.size(); i++) {
++				if (js.uid == map_db[i].uid) {
++					mapping = i;
++					js.name = map_db[i].name;
++				}
+ 			};
+ 		};
+ 		js.mapping = mapping;
Index: patches/patch-main_input_default_h
===================================================================
RCS file: patches/patch-main_input_default_h
diff -N patches/patch-main_input_default_h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ patches/patch-main_input_default_h	29 May 2026 20:27:40 -0000
@@ -0,0 +1,23 @@
+backport commit 0b3496f:
+	Add support for SDL3 joystick input driver
+
+Index: main/input_default.h
+--- main/input_default.h.orig
++++ main/input_default.h
+@@ -92,6 +92,7 @@ class InputDefault : public Input {
+ 		int last_hat;
+ 		int mapping;
+ 		int hat_current;
++		Dictionary info;
+ 
+ 		Joypad() {
+ 			for (int i = 0; i < JOY_AXIS_MAX; i++) {
+@@ -241,7 +242,7 @@ class InputDefault : public Input {
+ 	virtual Vector2 get_joy_vibration_strength(int p_device);
+ 	virtual float get_joy_vibration_duration(int p_device);
+ 	virtual uint64_t get_joy_vibration_timestamp(int p_device);
+-	void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = "");
++	void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = "", const Dictionary &p_joypad_info = Dictionary());
+ 
+ 	virtual Vector3 get_gravity() const;
+ 	virtual Vector3 get_accelerometer() const;
Index: patches/patch-platform_x11_SCsub
===================================================================
RCS file: /cvs/ports/games/godot/pack1/patches/patch-platform_x11_SCsub,v
diff -u -p -r1.1.1.1 patch-platform_x11_SCsub
--- patches/patch-platform_x11_SCsub	4 Mar 2026 05:35:04 -0000	1.1.1.1
+++ patches/patch-platform_x11_SCsub	29 May 2026 20:27:40 -0000
@@ -1,12 +1,14 @@
+backport commit 0b3496f:
+Add support for SDL3 joystick input driver
+
 Index: platform/x11/SCsub
 --- platform/x11/SCsub.orig
 +++ platform/x11/SCsub
-@@ -10,7 +10,7 @@ common_x11 = [
+@@ -10,7 +10,6 @@ common_x11 = [
      "crash_handler_x11.cpp",
      "os_x11.cpp",
      "key_mapping_x11.cpp",
 -    "joypad_linux.cpp",
-+    "joypad_openbsd.cpp",
      "power_x11.cpp",
      "detect_prime.cpp",
  ]
Index: patches/patch-platform_x11_detect_py
===================================================================
RCS file: /cvs/ports/games/godot/pack1/patches/patch-platform_x11_detect_py,v
diff -u -p -r1.1.1.1 patch-platform_x11_detect_py
--- patches/patch-platform_x11_detect_py	4 Mar 2026 05:35:04 -0000	1.1.1.1
+++ patches/patch-platform_x11_detect_py	29 May 2026 20:27:40 -0000
@@ -1,3 +1,6 @@
+backport commit 0b3496f:
+Add support for SDL3 joystick input driver
+
 Index: platform/x11/detect.py
 --- platform/x11/detect.py.orig
 +++ platform/x11/detect.py
@@ -42,13 +45,22 @@ Index: platform/x11/detect.py
      if env["pulseaudio"]:
          if os.system("pkg-config --exists libpulse") == 0:  # 0 means found
              env.Append(CPPDEFINES=["PULSEAUDIO_ENABLED"])
-@@ -398,6 +379,9 @@ def configure(env):
+@@ -398,6 +379,18 @@ def configure(env):
                  print("Warning: libudev development libraries not found. Disabling controller hotplugging support.")
      else:
          env["udev"] = False  # Linux specific
 +
-+    if platform.system() == "OpenBSD":
-+        env.Append(CPPDEFINES=["JOYDEV_ENABLED"])
++    if env["sdl"]:
++        if env["builtin_sdl"]:
++            env.Append(CPPDEFINES=["SDL_ENABLED"])
++        elif os.system("pkg-config --exists fontconfig") == 0:  # 0 means found
++            env.ParseConfig("pkg-config sdl3 --cflags --libs")
++            env.Append(CPPDEFINES=["SDL_ENABLED"])
++        else:
++            print_warning(
++                "SDL3 development libraries not found, and `builtin_sdl` was explicitly disabled. Disabling SDL input driver support."
++            )
++            env["sdl"] = False
  
      # Linkflags below this line should typically stay the last ones
      if not env["builtin_zlib"]:
Index: patches/patch-platform_x11_joypad_linux_cpp
===================================================================
RCS file: patches/patch-platform_x11_joypad_linux_cpp
diff -N patches/patch-platform_x11_joypad_linux_cpp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ patches/patch-platform_x11_joypad_linux_cpp	29 May 2026 20:27:40 -0000
@@ -0,0 +1,602 @@
+backport commit 0b3496f:
+Add support for SDL3 joystick input driver
+
+Index: platform/x11/joypad_linux.cpp
+--- platform/x11/joypad_linux.cpp.orig
++++ platform/x11/joypad_linux.cpp
+@@ -1,595 +1 @@
+-/**************************************************************************/
+-/*  joypad_linux.cpp                                                      */
+-/**************************************************************************/
+-/*                         This file is part of:                          */
+-/*                             GODOT ENGINE                               */
+-/*                        https://godotengine.org                         */
+-/**************************************************************************/
+-/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+-/*                                                                        */
+-/* Permission is hereby granted, free of charge, to any person obtaining  */
+-/* a copy of this software and associated documentation files (the        */
+-/* "Software"), to deal in the Software without restriction, including    */
+-/* without limitation the rights to use, copy, modify, merge, publish,    */
+-/* distribute, sublicense, and/or sell copies of the Software, and to     */
+-/* permit persons to whom the Software is furnished to do so, subject to  */
+-/* the following conditions:                                              */
+-/*                                                                        */
+-/* The above copyright notice and this permission notice shall be         */
+-/* included in all copies or substantial portions of the Software.        */
+-/*                                                                        */
+-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+-/**************************************************************************/
+ 
+-#ifdef JOYDEV_ENABLED
+-
+-#include "joypad_linux.h"
+-
+-#include "core/os/os.h"
+-
+-#include <dirent.h>
+-#include <errno.h>
+-#include <fcntl.h>
+-#include <linux/input.h>
+-#include <unistd.h>
+-
+-#ifdef UDEV_ENABLED
+-#include "libudev-so_wrap.h"
+-#endif
+-
+-#define LONG_BITS (sizeof(long) * 8)
+-#define test_bit(nr, addr) (((1UL << ((nr) % LONG_BITS)) & ((addr)[(nr) / LONG_BITS])) != 0)
+-#define NBITS(x) ((((x)-1) / LONG_BITS) + 1)
+-
+-#ifdef UDEV_ENABLED
+-static const char *ignore_str = "/dev/input/js";
+-#endif
+-
+-JoypadLinux::Joypad::Joypad() {
+-	fd = -1;
+-	dpad = 0;
+-	devpath = "";
+-	for (int i = 0; i < MAX_ABS; i++) {
+-		abs_info[i] = nullptr;
+-	}
+-}
+-
+-JoypadLinux::Joypad::~Joypad() {
+-	for (int i = 0; i < MAX_ABS; i++) {
+-		if (abs_info[i]) {
+-			memdelete(abs_info[i]);
+-		}
+-	}
+-}
+-
+-void JoypadLinux::Joypad::reset() {
+-	dpad = 0;
+-	fd = -1;
+-	for (int i = 0; i < MAX_ABS; i++) {
+-		abs_map[i] = -1;
+-		curr_axis[i] = 0;
+-	}
+-	events.clear();
+-}
+-
+-#ifdef UDEV_ENABLED
+-// This function is derived from SDL:
+-// https://github.com/libsdl-org/SDL/blob/main/src/core/linux/SDL_sandbox.c#L28-L45
+-static bool detect_sandbox() {
+-	if (access("/.flatpak-info", F_OK) == 0) {
+-		return true;
+-	}
+-
+-	// For Snap, we check multiple variables because they might be set for
+-	// unrelated reasons. This is the same thing WebKitGTK does.
+-	if (OS::get_singleton()->has_environment("SNAP") && OS::get_singleton()->has_environment("SNAP_NAME") && OS::get_singleton()->has_environment("SNAP_REVISION")) {
+-		return true;
+-	}
+-
+-	if (access("/run/host/container-manager", F_OK) == 0) {
+-		return true;
+-	}
+-
+-	return false;
+-}
+-#endif // UDEV_ENABLED
+-
+-JoypadLinux::JoypadLinux(InputDefault *in) {
+-#ifdef UDEV_ENABLED
+-	if (detect_sandbox()) {
+-		// Linux binaries in sandboxes / containers need special handling because
+-		// libudev doesn't work there. So we need to fallback to manual parsing
+-		// of /dev/input in such case.
+-		use_udev = false;
+-		print_verbose("JoypadLinux: udev enabled, but detected incompatible sandboxed mode. Falling back to /dev/input to detect joypads.");
+-	} else {
+-		use_udev = initialize_libudev() == 0;
+-		if (use_udev) {
+-			print_verbose("JoypadLinux: udev enabled and loaded successfully.");
+-		} else {
+-			print_verbose("JoypadLinux: udev enabled, but couldn't be loaded. Falling back to /dev/input to detect joypads.");
+-		}
+-	}
+-#else
+-	print_verbose("JoypadLinux: udev disabled, parsing /dev/input to detect joypads.");
+-#endif
+-	input = in;
+-	monitor_joypads_thread.start(monitor_joypads_thread_func, this);
+-	joypad_events_thread.start(joypad_events_thread_func, this);
+-}
+-
+-JoypadLinux::~JoypadLinux() {
+-	monitor_joypads_exit.set();
+-	joypad_events_exit.set();
+-	monitor_joypads_thread.wait_to_finish();
+-	joypad_events_thread.wait_to_finish();
+-	close_joypads();
+-}
+-
+-void JoypadLinux::monitor_joypads_thread_func(void *p_user) {
+-	if (p_user) {
+-		JoypadLinux *joy = (JoypadLinux *)p_user;
+-		joy->monitor_joypads_thread_run();
+-	}
+-}
+-
+-void JoypadLinux::monitor_joypads_thread_run() {
+-#ifdef UDEV_ENABLED
+-	if (use_udev) {
+-		udev *_udev = udev_new();
+-		if (!_udev) {
+-			use_udev = false;
+-			ERR_PRINT("Failed getting an udev context, falling back to parsing /dev/input.");
+-			monitor_joypads();
+-		} else {
+-			enumerate_joypads(_udev);
+-			monitor_joypads(_udev);
+-			udev_unref(_udev);
+-		}
+-	} else {
+-		monitor_joypads();
+-	}
+-#else
+-	monitor_joypads();
+-#endif
+-}
+-
+-#ifdef UDEV_ENABLED
+-void JoypadLinux::enumerate_joypads(udev *p_udev) {
+-	udev_enumerate *enumerate;
+-	udev_list_entry *devices, *dev_list_entry;
+-	udev_device *dev;
+-
+-	enumerate = udev_enumerate_new(p_udev);
+-	udev_enumerate_add_match_subsystem(enumerate, "input");
+-
+-	udev_enumerate_scan_devices(enumerate);
+-	devices = udev_enumerate_get_list_entry(enumerate);
+-	udev_list_entry_foreach(dev_list_entry, devices) {
+-		const char *path = udev_list_entry_get_name(dev_list_entry);
+-		dev = udev_device_new_from_syspath(p_udev, path);
+-		const char *devnode = udev_device_get_devnode(dev);
+-
+-		if (devnode) {
+-			String devnode_str = devnode;
+-			if (devnode_str.find(ignore_str) == -1) {
+-				open_joypad(devnode);
+-			}
+-		}
+-		udev_device_unref(dev);
+-	}
+-	udev_enumerate_unref(enumerate);
+-}
+-
+-void JoypadLinux::monitor_joypads(udev *p_udev) {
+-	udev_device *dev = nullptr;
+-	udev_monitor *mon = udev_monitor_new_from_netlink(p_udev, "udev");
+-	udev_monitor_filter_add_match_subsystem_devtype(mon, "input", nullptr);
+-	udev_monitor_enable_receiving(mon);
+-	int fd = udev_monitor_get_fd(mon);
+-
+-	while (!monitor_joypads_exit.is_set()) {
+-		fd_set fds;
+-		struct timeval tv;
+-		int ret;
+-
+-		FD_ZERO(&fds);
+-		FD_SET(fd, &fds);
+-		tv.tv_sec = 0;
+-		tv.tv_usec = 0;
+-
+-		ret = select(fd + 1, &fds, nullptr, nullptr, &tv);
+-
+-		/* Check if our file descriptor has received data. */
+-		if (ret > 0 && FD_ISSET(fd, &fds)) {
+-			/* Make the call to receive the device.
+-			   select() ensured that this will not block. */
+-			dev = udev_monitor_receive_device(mon);
+-
+-			if (dev && udev_device_get_devnode(dev) != nullptr) {
+-				String action = udev_device_get_action(dev);
+-				const char *devnode = udev_device_get_devnode(dev);
+-				if (devnode) {
+-					String devnode_str = devnode;
+-					if (devnode_str.find(ignore_str) == -1) {
+-						if (action == "add") {
+-							open_joypad(devnode);
+-						} else if (String(action) == "remove") {
+-							close_joypad(devnode);
+-						}
+-					}
+-				}
+-				udev_device_unref(dev);
+-			}
+-		}
+-		usleep(50000);
+-	}
+-	udev_monitor_unref(mon);
+-}
+-#endif
+-
+-void JoypadLinux::monitor_joypads() {
+-	while (!monitor_joypads_exit.is_set()) {
+-		DIR *input_directory;
+-		input_directory = opendir("/dev/input");
+-		if (input_directory) {
+-			struct dirent *current;
+-			char fname[64];
+-
+-			while ((current = readdir(input_directory)) != nullptr) {
+-				if (strncmp(current->d_name, "event", 5) != 0) {
+-					continue;
+-				}
+-				sprintf(fname, "/dev/input/%.*s", 16, current->d_name);
+-				if (attached_devices.find(fname) == -1) {
+-					open_joypad(fname);
+-				}
+-			}
+-		}
+-		closedir(input_directory);
+-		usleep(1000000); // 1s
+-	}
+-}
+-
+-void JoypadLinux::close_joypads() {
+-	for (int i = 0; i < JOYPADS_MAX; i++) {
+-		MutexLock lock(joypads_mutex[i]);
+-		Joypad &joypad = joypads[i];
+-		close_joypad(joypad, i);
+-	}
+-}
+-
+-void JoypadLinux::close_joypad(const char *p_devpath) {
+-	for (int i = 0; i < JOYPADS_MAX; i++) {
+-		MutexLock lock(joypads_mutex[i]);
+-		Joypad &joypad = joypads[i];
+-		if (joypads[i].devpath == p_devpath) {
+-			close_joypad(joypad, i);
+-		}
+-	}
+-}
+-
+-void JoypadLinux::close_joypad(Joypad &p_joypad, int p_id) {
+-	if (p_joypad.fd != -1) {
+-		close(p_joypad.fd);
+-		p_joypad.fd = -1;
+-		attached_devices.erase(p_joypad.devpath);
+-		input->joy_connection_changed(p_id, false, "");
+-	};
+-	p_joypad.events.clear();
+-}
+-
+-static String _hex_str(uint8_t p_byte) {
+-	static const char *dict = "0123456789abcdef";
+-	char ret[3];
+-	ret[2] = 0;
+-
+-	ret[0] = dict[p_byte >> 4];
+-	ret[1] = dict[p_byte & 0xF];
+-
+-	return ret;
+-}
+-
+-void JoypadLinux::setup_joypad_properties(Joypad &p_joypad) {
+-	unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
+-	unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
+-
+-	int num_buttons = 0;
+-	int num_axes = 0;
+-
+-	if ((ioctl(p_joypad.fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) ||
+-			(ioctl(p_joypad.fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) {
+-		return;
+-	}
+-	for (int i = BTN_JOYSTICK; i < KEY_MAX; ++i) {
+-		if (test_bit(i, keybit)) {
+-			p_joypad.key_map[i] = num_buttons++;
+-		}
+-	}
+-	for (int i = BTN_MISC; i < BTN_JOYSTICK; ++i) {
+-		if (test_bit(i, keybit)) {
+-			p_joypad.key_map[i] = num_buttons++;
+-		}
+-	}
+-	for (int i = 0; i < ABS_MISC; ++i) {
+-		/* Skip hats */
+-		if (i == ABS_HAT0X) {
+-			i = ABS_HAT3Y;
+-			continue;
+-		}
+-		if (test_bit(i, absbit)) {
+-			p_joypad.abs_map[i] = num_axes++;
+-			p_joypad.abs_info[i] = memnew(input_absinfo);
+-			if (ioctl(p_joypad.fd, EVIOCGABS(i), p_joypad.abs_info[i]) < 0) {
+-				memdelete(p_joypad.abs_info[i]);
+-				p_joypad.abs_info[i] = nullptr;
+-			}
+-		}
+-	}
+-
+-	p_joypad.force_feedback = false;
+-	p_joypad.ff_effect_timestamp = 0;
+-	unsigned long ffbit[NBITS(FF_CNT)];
+-	if (ioctl(p_joypad.fd, EVIOCGBIT(EV_FF, sizeof(ffbit)), ffbit) != -1) {
+-		if (test_bit(FF_RUMBLE, ffbit)) {
+-			p_joypad.force_feedback = true;
+-		}
+-	}
+-}
+-
+-void JoypadLinux::open_joypad(const char *p_path) {
+-	int joy_num = input->get_unused_joy_id();
+-	int fd = open(p_path, O_RDWR | O_NONBLOCK);
+-	if (fd != -1 && joy_num != -1) {
+-		unsigned long evbit[NBITS(EV_MAX)] = { 0 };
+-		unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
+-		unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
+-
+-		// add to attached devices so we don't try to open it again
+-		attached_devices.push_back(String(p_path));
+-
+-		if ((ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) ||
+-				(ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) ||
+-				(ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) {
+-			close(fd);
+-			return;
+-		}
+-
+-		//check if the device supports basic gamepad events, prevents certain keyboards from
+-		//being detected as joypads
+-		if (!(test_bit(EV_KEY, evbit) && test_bit(EV_ABS, evbit) &&
+-					(test_bit(ABS_X, absbit) || test_bit(ABS_Y, absbit) || test_bit(ABS_HAT0X, absbit) ||
+-							test_bit(ABS_GAS, absbit) || test_bit(ABS_RUDDER, absbit)) &&
+-					(test_bit(BTN_A, keybit) || test_bit(BTN_THUMBL, keybit) ||
+-							test_bit(BTN_TRIGGER, keybit) || test_bit(BTN_1, keybit))) &&
+-				!(test_bit(EV_ABS, evbit) &&
+-						test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit) &&
+-						test_bit(ABS_RX, absbit) && test_bit(ABS_RY, absbit))) {
+-			close(fd);
+-			return;
+-		}
+-
+-		char uid[128];
+-		char namebuf[128];
+-		String name = "";
+-		input_id inpid;
+-		if (ioctl(fd, EVIOCGNAME(sizeof(namebuf)), namebuf) >= 0) {
+-			name = namebuf;
+-		}
+-
+-		if (ioctl(fd, EVIOCGID, &inpid) < 0) {
+-			close(fd);
+-			return;
+-		}
+-
+-		uint16_t vendor = BSWAP16(inpid.vendor);
+-		uint16_t product = BSWAP16(inpid.product);
+-		uint16_t version = BSWAP16(inpid.version);
+-
+-		if (input->should_ignore_device(vendor, product)) {
+-			// This can be true in cases where Steam is passing information into the game to ignore
+-			// original gamepads when using virtual rebindings (See SteamInput).
+-			return;
+-		}
+-
+-		MutexLock lock(joypads_mutex[joy_num]);
+-		Joypad &joypad = joypads[joy_num];
+-		joypad.reset();
+-		joypad.fd = fd;
+-		joypad.devpath = String(p_path);
+-		setup_joypad_properties(joypad);
+-		sprintf(uid, "%04x%04x", BSWAP16(inpid.bustype), 0);
+-		if (inpid.vendor && inpid.product && inpid.version) {
+-			sprintf(uid + String(uid).length(), "%04x%04x%04x%04x%04x%04x", vendor, 0, product, 0, version, 0);
+-			input->joy_connection_changed(joy_num, true, name, uid);
+-		} else {
+-			String uidname = uid;
+-			int uidlen = MIN(name.length(), 11);
+-			for (int i = 0; i < uidlen; i++) {
+-				uidname = uidname + _hex_str(name[i]);
+-			}
+-			uidname += "00";
+-			input->joy_connection_changed(joy_num, true, name, uidname);
+-		}
+-	}
+-}
+-
+-void JoypadLinux::joypad_vibration_start(Joypad &p_joypad, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) {
+-	if (!p_joypad.force_feedback || p_joypad.fd == -1 || p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f) {
+-		return;
+-	}
+-	if (p_joypad.ff_effect_id != -1) {
+-		joypad_vibration_stop(p_joypad, p_timestamp);
+-	}
+-
+-	struct ff_effect effect;
+-	effect.type = FF_RUMBLE;
+-	effect.id = -1;
+-	effect.u.rumble.weak_magnitude = floor(p_weak_magnitude * (float)0xffff);
+-	effect.u.rumble.strong_magnitude = floor(p_strong_magnitude * (float)0xffff);
+-	effect.replay.length = floor(p_duration * 1000);
+-	effect.replay.delay = 0;
+-
+-	if (ioctl(p_joypad.fd, EVIOCSFF, &effect) < 0) {
+-		return;
+-	}
+-
+-	struct input_event play;
+-	play.type = EV_FF;
+-	play.code = effect.id;
+-	play.value = 1;
+-	if (write(p_joypad.fd, (const void *)&play, sizeof(play)) == -1) {
+-		print_verbose("Couldn't write to Joypad device.");
+-	}
+-
+-	p_joypad.ff_effect_id = effect.id;
+-	p_joypad.ff_effect_timestamp = p_timestamp;
+-}
+-
+-void JoypadLinux::joypad_vibration_stop(Joypad &p_joypad, uint64_t p_timestamp) {
+-	if (!p_joypad.force_feedback || p_joypad.fd == -1 || p_joypad.ff_effect_id == -1) {
+-		return;
+-	}
+-
+-	if (ioctl(p_joypad.fd, EVIOCRMFF, p_joypad.ff_effect_id) < 0) {
+-		return;
+-	}
+-
+-	p_joypad.ff_effect_id = -1;
+-	p_joypad.ff_effect_timestamp = p_timestamp;
+-}
+-
+-float JoypadLinux::axis_correct(const input_absinfo *p_abs, int p_value) const {
+-	int min = p_abs->minimum;
+-	int max = p_abs->maximum;
+-	// Convert to a value between -1.0f and 1.0f.
+-	return 2.0f * (p_value - min) / (max - min) - 1.0f;
+-}
+-
+-void JoypadLinux::joypad_events_thread_func(void *p_user) {
+-	if (p_user) {
+-		JoypadLinux *joy = (JoypadLinux *)p_user;
+-		joy->joypad_events_thread_run();
+-	}
+-}
+-
+-void JoypadLinux::joypad_events_thread_run() {
+-	while (!joypad_events_exit.is_set()) {
+-		bool no_events = true;
+-		for (int i = 0; i < JOYPADS_MAX; i++) {
+-			MutexLock lock(joypads_mutex[i]);
+-			Joypad &joypad = joypads[i];
+-			if (joypad.fd == -1) {
+-				continue;
+-			}
+-			input_event event;
+-			while (read(joypad.fd, &event, sizeof(event)) > 0) {
+-				JoypadEvent joypad_event;
+-				joypad_event.type = event.type;
+-				joypad_event.code = event.code;
+-				joypad_event.value = event.value;
+-				joypad.events.push_back(joypad_event);
+-			}
+-			if (errno != EAGAIN) {
+-				close_joypad(joypad, i);
+-			}
+-		}
+-		if (no_events) {
+-			usleep(10000); // 10ms
+-		}
+-	}
+-}
+-
+-void JoypadLinux::process_joypads() {
+-	for (int i = 0; i < JOYPADS_MAX; i++) {
+-		MutexLock lock(joypads_mutex[i]);
+-		Joypad &joypad = joypads[i];
+-		if (joypad.fd == -1) {
+-			continue;
+-		}
+-		for (uint32_t j = 0; j < joypad.events.size(); j++) {
+-			const JoypadEvent &joypad_event = joypad.events[j];
+-			// joypad_event may be tainted and out of MAX_KEY range, which will cause
+-			// joypad.key_map[joypad_event.code] to crash
+-			if (joypad_event.code >= MAX_KEY) {
+-				return;
+-			}
+-
+-			switch (joypad_event.type) {
+-				case EV_KEY:
+-					input->joy_button(i, joypad.key_map[joypad_event.code], joypad_event.value);
+-					break;
+-
+-				case EV_ABS:
+-					switch (joypad_event.code) {
+-						case ABS_HAT0X:
+-							if (joypad_event.value != 0) {
+-								if (joypad_event.value < 0) {
+-									joypad.dpad = (joypad.dpad | InputDefault::HAT_MASK_LEFT) & ~InputDefault::HAT_MASK_RIGHT;
+-								} else {
+-									joypad.dpad = (joypad.dpad | InputDefault::HAT_MASK_RIGHT) & ~InputDefault::HAT_MASK_LEFT;
+-								}
+-							} else {
+-								joypad.dpad &= ~(InputDefault::HAT_MASK_LEFT | InputDefault::HAT_MASK_RIGHT);
+-							}
+-							input->joy_hat(i, joypad.dpad);
+-							break;
+-
+-						case ABS_HAT0Y:
+-							if (joypad_event.value != 0) {
+-								if (joypad_event.value < 0) {
+-									joypad.dpad = (joypad.dpad | InputDefault::HAT_MASK_UP) & ~InputDefault::HAT_MASK_DOWN;
+-								} else {
+-									joypad.dpad = (joypad.dpad | InputDefault::HAT_MASK_DOWN) & ~InputDefault::HAT_MASK_UP;
+-								}
+-							} else {
+-								joypad.dpad &= ~(InputDefault::HAT_MASK_UP | InputDefault::HAT_MASK_DOWN);
+-							}
+-							input->joy_hat(i, joypad.dpad);
+-							break;
+-
+-						default:
+-							if (joypad_event.code >= MAX_ABS) {
+-								return;
+-							}
+-							if (joypad.abs_map[joypad_event.code] != -1 && joypad.abs_info[joypad_event.code]) {
+-								float value = axis_correct(joypad.abs_info[joypad_event.code], joypad_event.value);
+-								joypad.curr_axis[joypad.abs_map[joypad_event.code]] = value;
+-							}
+-							break;
+-					}
+-					break;
+-			}
+-		}
+-		joypad.events.clear();
+-
+-		for (int j = 0; j < MAX_ABS; j++) {
+-			int index = joypad.abs_map[j];
+-			if (index != -1) {
+-				input->joy_axis(i, index, joypad.curr_axis[index]);
+-			}
+-		}
+-
+-		if (joypad.force_feedback) {
+-			uint64_t timestamp = input->get_joy_vibration_timestamp(i);
+-			if (timestamp > joypad.ff_effect_timestamp) {
+-				Vector2 strength = input->get_joy_vibration_strength(i);
+-				float duration = input->get_joy_vibration_duration(i);
+-				if (strength.x == 0 && strength.y == 0) {
+-					joypad_vibration_stop(joypad, timestamp);
+-				} else {
+-					joypad_vibration_start(joypad, strength.x, strength.y, duration, timestamp);
+-				}
+-			}
+-		}
+-	}
+-}
+-
+-#endif // JOYDEV_ENABLED
Index: patches/patch-platform_x11_joypad_linux_h
===================================================================
RCS file: patches/patch-platform_x11_joypad_linux_h
diff -N patches/patch-platform_x11_joypad_linux_h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ patches/patch-platform_x11_joypad_linux_h	29 May 2026 20:27:40 -0000
@@ -0,0 +1,132 @@
+backport commit 0b3496f:
+Add support for SDL3 joystick input driver
+
+Index: platform/x11/joypad_linux.h
+--- platform/x11/joypad_linux.h.orig
++++ platform/x11/joypad_linux.h
+@@ -1,125 +1 @@
+-/**************************************************************************/
+-/*  joypad_linux.h                                                        */
+-/**************************************************************************/
+-/*                         This file is part of:                          */
+-/*                             GODOT ENGINE                               */
+-/*                        https://godotengine.org                         */
+-/**************************************************************************/
+-/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+-/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+-/*                                                                        */
+-/* Permission is hereby granted, free of charge, to any person obtaining  */
+-/* a copy of this software and associated documentation files (the        */
+-/* "Software"), to deal in the Software without restriction, including    */
+-/* without limitation the rights to use, copy, modify, merge, publish,    */
+-/* distribute, sublicense, and/or sell copies of the Software, and to     */
+-/* permit persons to whom the Software is furnished to do so, subject to  */
+-/* the following conditions:                                              */
+-/*                                                                        */
+-/* The above copyright notice and this permission notice shall be         */
+-/* included in all copies or substantial portions of the Software.        */
+-/*                                                                        */
+-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+-/**************************************************************************/
+ 
+-//author: Andreas Haas <hondres,  [email protected]>
+-
+-#ifndef JOYPAD_LINUX_H
+-#define JOYPAD_LINUX_H
+-
+-#ifdef JOYDEV_ENABLED
+-#include "core/os/mutex.h"
+-#include "core/os/thread.h"
+-#include "main/input_default.h"
+-
+-struct input_absinfo;
+-
+-class JoypadLinux {
+-public:
+-	JoypadLinux(InputDefault *in);
+-	~JoypadLinux();
+-	void process_joypads();
+-
+-private:
+-	enum {
+-		JOYPADS_MAX = 16,
+-		MAX_ABS = 63,
+-		MAX_KEY = 767, // Hack because <linux/input.h> can't be included here
+-	};
+-
+-	struct JoypadEvent {
+-		uint16_t type;
+-		uint16_t code;
+-		int32_t value;
+-	};
+-
+-	struct Joypad {
+-		float curr_axis[MAX_ABS];
+-		int key_map[MAX_KEY];
+-		int abs_map[MAX_ABS];
+-		int dpad;
+-		int fd;
+-
+-		String devpath;
+-		input_absinfo *abs_info[MAX_ABS];
+-
+-		bool force_feedback;
+-		int ff_effect_id;
+-		uint64_t ff_effect_timestamp;
+-
+-		LocalVector<JoypadEvent> events;
+-
+-		Joypad();
+-		~Joypad();
+-		void reset();
+-	};
+-
+-#ifdef UDEV_ENABLED
+-	bool use_udev;
+-#endif
+-	InputDefault *input;
+-
+-	SafeFlag monitor_joypads_exit;
+-	SafeFlag joypad_events_exit;
+-	Thread monitor_joypads_thread;
+-	Thread joypad_events_thread;
+-
+-	Joypad joypads[JOYPADS_MAX];
+-	Mutex joypads_mutex[JOYPADS_MAX];
+-
+-	Vector<String> attached_devices;
+-
+-	static void monitor_joypads_thread_func(void *p_user);
+-	void monitor_joypads_thread_run();
+-
+-	void open_joypad(const char *p_path);
+-	void setup_joypad_properties(Joypad &p_joypad);
+-
+-	void close_joypads();
+-	void close_joypad(const char *p_devpath);
+-	void close_joypad(Joypad &p_joypad, int p_id);
+-
+-#ifdef UDEV_ENABLED
+-	void enumerate_joypads(struct udev *p_udev);
+-	void monitor_joypads(struct udev *p_udev);
+-#endif
+-	void monitor_joypads();
+-
+-	void joypad_vibration_start(Joypad &p_joypad, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
+-	void joypad_vibration_stop(Joypad &p_joypad, uint64_t p_timestamp);
+-
+-	static void joypad_events_thread_func(void *p_user);
+-	void joypad_events_thread_run();
+-
+-	float axis_correct(const input_absinfo *p_abs, int p_value) const;
+-};
+-
+-#endif
+-
+-#endif // JOYPAD_LINUX_H
Index: patches/patch-platform_x11_os_x11_cpp
===================================================================
RCS file: /cvs/ports/games/godot/pack1/patches/patch-platform_x11_os_x11_cpp,v
diff -u -p -r1.2 patch-platform_x11_os_x11_cpp
--- patches/patch-platform_x11_os_x11_cpp	21 Mar 2026 20:27:26 -0000	1.2
+++ patches/patch-platform_x11_os_x11_cpp	29 May 2026 20:27:40 -0000
@@ -3,11 +3,23 @@ use OpenBSD joypad class
 suspend X11 screensaver for release builds
 we don't support DRI_PRIME as of March 2026 and setting it can lead to crashes
 	in multi-GPU setups
+backport commit 0b3496f:
+	Add support for SDL3 joystick input driver
 
 Index: platform/x11/os_x11.cpp
 --- platform/x11/os_x11.cpp.orig
 +++ platform/x11/os_x11.cpp
-@@ -55,6 +55,10 @@
+@@ -35,6 +35,9 @@
+ #include "detect_prime.h"
+ #include "drivers/gles2/rasterizer_gles2.h"
+ #include "drivers/gles3/rasterizer_gles3.h"
++#ifdef SDL_ENABLED
++#include "drivers/sdl/joypad_sdl.h"
++#endif
+ #include "key_mapping_x11.h"
+ #include "main/main.h"
+ #include "servers/visual/visual_server_raster.h"
+@@ -55,6 +58,10 @@
  #include <X11/extensions/Xinerama.h>
  #include <X11/extensions/shape.h>
  
@@ -18,7 +30,7 @@ Index: platform/x11/os_x11.cpp
  // ICCCM
  #define WM_NormalState 1L // window normal state
  #define WM_IconicState 3L // window minimized
-@@ -219,7 +223,7 @@ Error OS_X11::initialize(const VideoMode &p_desired, i
+@@ -219,7 +226,7 @@ Error OS_X11::initialize(const VideoMode &p_desired, i
  	int xrandr_minor = 0;
  	int event_base, error_base;
  	xrandr_ext_ok = XRRQueryExtension(x11_display, &event_base, &error_base);
@@ -27,7 +39,7 @@ Index: platform/x11/os_x11.cpp
  	if (!xrandr_handle) {
  		err = dlerror();
  		// For some arcane reason, NetBSD now ships libXrandr.so.3 while the rest of the world has libXrandr.so.2...
-@@ -336,6 +340,7 @@ Error OS_X11::initialize(const VideoMode &p_desired, i
+@@ -336,6 +343,7 @@ Error OS_X11::initialize(const VideoMode &p_desired, i
  			}
  		}
  
@@ -35,7 +47,7 @@ Index: platform/x11/os_x11.cpp
  		if (use_prime == -1) {
  			print_verbose("Detecting GPUs, set DRI_PRIME in the environment to override GPU detection logic.");
  			use_prime = detect_prime();
-@@ -346,6 +351,7 @@ Error OS_X11::initialize(const VideoMode &p_desired, i
+@@ -346,6 +354,7 @@ Error OS_X11::initialize(const VideoMode &p_desired, i
  			print_line("Note: Set DRI_PRIME=0 in the environment to disable Godot from using the discrete GPU.");
  			setenv("DRI_PRIME", "1", 1);
  		}
@@ -43,12 +55,19 @@ Index: platform/x11/os_x11.cpp
  	}
  
  	ContextGL_X11::ContextType opengl_api_type = ContextGL_X11::GLES_3_0_COMPATIBLE;
-@@ -664,9 +670,13 @@ Error OS_X11::initialize(const VideoMode &p_desired, i
+@@ -663,10 +672,19 @@ Error OS_X11::initialize(const VideoMode &p_desired, i
+ 	input = memnew(InputDefault);
  
  	window_has_focus = true; // Set focus to true at init
- #ifdef JOYDEV_ENABLED
+-#ifdef JOYDEV_ENABLED
 -	joypad = memnew(JoypadLinux(input));
-+	joypad = memnew(JoypadOpenBSD(input));
++#ifdef SDL_ENABLED
++	joypad_sdl = memnew(JoypadSDL(input));
++	if (joypad_sdl->initialize() != OK) {
++		ERR_PRINT("Couldn't initialize SDL joypad input driver.");
++		memdelete(joypad_sdl);
++		joypad_sdl = nullptr;
++	}
  #endif
  
 +#ifdef SUSPEND_SCREENSAVER
@@ -58,7 +77,46 @@ Index: platform/x11/os_x11.cpp
  	power_manager = memnew(PowerX11);
  
  	if (p_desired.layered) {
-@@ -4474,6 +4484,11 @@ void OS_X11::update_real_mouse_position() {
+@@ -909,8 +927,10 @@ void OS_X11::finalize() {
+ 	}
+ #endif
+ 
+-#ifdef JOYDEV_ENABLED
+-	memdelete(joypad);
++#ifdef SDL_ENABLED
++	if (joypad_sdl) {
++		memdelete(joypad_sdl);
++	}
+ #endif
+ 
+ 	xi.touch_devices.clear();
+@@ -4020,8 +4040,10 @@ void OS_X11::set_icon(const Ref<Image> &p_icon) {
+ 
+ void OS_X11::force_process_input() {
+ 	process_xevents(); // get rid of pending events
+-#ifdef JOYDEV_ENABLED
+-	joypad->process_joypads();
++#ifdef SDL_ENABLED
++	if (joypad_sdl) {
++		joypad_sdl->process_events();
++	}
+ #endif
+ }
+ 
+@@ -4041,8 +4063,10 @@ void OS_X11::run() {
+ 
+ 	while (!force_quit) {
+ 		process_xevents(); // get rid of pending events
+-#ifdef JOYDEV_ENABLED
+-		joypad->process_joypads();
++#ifdef SDL_ENABLED
++		if (joypad_sdl) {
++			joypad_sdl->process_events();
++		}
+ #endif
+ 		if (Main::iteration()) {
+ 			break;
+@@ -4474,6 +4498,11 @@ void OS_X11::update_real_mouse_position() {
  }
  
  OS_X11::OS_X11() {
Index: patches/patch-platform_x11_os_x11_h
===================================================================
RCS file: /cvs/ports/games/godot/pack1/patches/patch-platform_x11_os_x11_h,v
diff -u -p -r1.1.1.1 patch-platform_x11_os_x11_h
--- patches/patch-platform_x11_os_x11_h	4 Mar 2026 05:35:04 -0000	1.1.1.1
+++ patches/patch-platform_x11_os_x11_h	29 May 2026 20:27:40 -0000
@@ -1,10 +1,12 @@
 load sndio
 use OpenBSD joypad class
+backport commit 0b3496f:
+	Add support for SDL3 joystick input driver
 
 Index: platform/x11/os_x11.h
 --- platform/x11/os_x11.h.orig
 +++ platform/x11/os_x11.h
-@@ -37,9 +37,10 @@
+@@ -37,9 +37,9 @@
  #include "crash_handler_x11.h"
  #include "drivers/alsa/audio_driver_alsa.h"
  #include "drivers/alsamidi/midi_driver_alsamidi.h"
@@ -12,20 +14,30 @@ Index: platform/x11/os_x11.h
  #include "drivers/pulseaudio/audio_driver_pulseaudio.h"
  #include "drivers/unix/os_unix.h"
 -#include "joypad_linux.h"
-+#include "joypad_openbsd.h"
  #include "main/input_default.h"
  #include "power_x11.h"
  #include "servers/audio_server.h"
-@@ -203,7 +204,7 @@ class OS_X11 : public OS_Unix {
+@@ -81,6 +81,8 @@ typedef struct _xrr_monitor_info {
+ 
+ #undef CursorShape
+ 
++class JoypadSDL;
++
+ class OS_X11 : public OS_Unix {
+ 	Atom wm_delete;
+ 	Atom xdnd_enter;
+@@ -202,8 +204,8 @@ class OS_X11 : public OS_Unix {
+ 
  	InputDefault *input;
  
- #ifdef JOYDEV_ENABLED
+-#ifdef JOYDEV_ENABLED
 -	JoypadLinux *joypad;
-+	JoypadOpenBSD *joypad;
++#ifdef SDL_ENABLED
++	JoypadSDL *joypad_sdl = nullptr;
  #endif
  
  #ifdef ALSA_ENABLED
-@@ -212,6 +213,10 @@ class OS_X11 : public OS_Unix {
+@@ -212,6 +214,10 @@ class OS_X11 : public OS_Unix {
  
  #ifdef ALSAMIDI_ENABLED
  	MIDIDriverALSAMidi driver_alsamidi;

Reply via email to