Browse Source

Add support for SDL3 joystick input driver

Made possible by EIREXE, xsellier and the SDL team.

This commit includes statically linked SDL3 for Windows, Linux and macOS.
The vendored copy of SDL3 was setup to only build the required subsystems
for gamepad/joystick support, with some patches to be able to make it as
minimal as possible and reduce the impact on binary size and code size.

Co-authored-by: Álex Román Núñez <[email protected]>
Co-authored-by: Xavier Sellier <[email protected]>
Co-authored-by: Rémi Verschelde <[email protected]>
Nintorch 3 months ago
parent
commit
0b3496fb4f
100 changed files with 26456 additions and 1561 deletions
  1. 10 0
      COPYRIGHT.txt
  2. 6 4
      SConstruct
  3. 7 3
      core/input/input.cpp
  4. 5 0
      drivers/SCsub
  5. 209 0
      drivers/sdl/SCsub
  6. 130 0
      drivers/sdl/SDL_build_config_private.h
  7. 273 0
      drivers/sdl/joypad_sdl.cpp
  8. 69 0
      drivers/sdl/joypad_sdl.h
  9. 0 1
      platform/linuxbsd/SCsub
  10. 12 0
      platform/linuxbsd/detect.py
  11. 0 613
      platform/linuxbsd/joypad_linux.cpp
  12. 0 136
      platform/linuxbsd/joypad_linux.h
  13. 17 7
      platform/linuxbsd/os_linuxbsd.cpp
  14. 4 3
      platform/linuxbsd/os_linuxbsd.h
  15. 0 2
      platform/linuxbsd/x11/display_server_x11.h
  16. 4 0
      platform/macos/detect.py
  17. 5 2
      platform/macos/os_macos.h
  18. 21 4
      platform/macos/os_macos.mm
  19. 0 1
      platform/windows/SCsub
  20. 6 0
      platform/windows/detect.py
  21. 24 6
      platform/windows/display_server_windows.cpp
  22. 5 2
      platform/windows/display_server_windows.h
  23. 1 0
      platform/windows/drop_target_windows.cpp
  24. 0 625
      platform/windows/joypad_windows.cpp
  25. 0 149
      platform/windows/joypad_windows.h
  26. 0 1
      platform/windows/os_windows.cpp
  27. 8 2
      platform/windows/os_windows.h
  28. 33 0
      thirdparty/README.md
  29. 38 0
      thirdparty/sdl/CREDITS.md
  30. 18 0
      thirdparty/sdl/LICENSE.txt
  31. 841 0
      thirdparty/sdl/SDL.c
  32. 443 0
      thirdparty/sdl/SDL_assert.c
  33. 28 0
      thirdparty/sdl/SDL_assert_c.h
  34. 112 0
      thirdparty/sdl/SDL_error.c
  35. 61 0
      thirdparty/sdl/SDL_error_c.h
  36. 88 0
      thirdparty/sdl/SDL_guid.c
  37. 543 0
      thirdparty/sdl/SDL_hashtable.c
  38. 633 0
      thirdparty/sdl/SDL_hashtable.h
  39. 403 0
      thirdparty/sdl/SDL_hints.c
  40. 33 0
      thirdparty/sdl/SDL_hints_c.h
  41. 289 0
      thirdparty/sdl/SDL_internal.h
  42. 86 0
      thirdparty/sdl/SDL_list.c
  43. 36 0
      thirdparty/sdl/SDL_list.h
  44. 805 0
      thirdparty/sdl/SDL_log.c
  45. 31 0
      thirdparty/sdl/SDL_log_c.h
  46. 824 0
      thirdparty/sdl/SDL_properties.c
  47. 26 0
      thirdparty/sdl/SDL_properties_c.h
  48. 554 0
      thirdparty/sdl/SDL_utils.c
  49. 78 0
      thirdparty/sdl/SDL_utils_c.h
  50. 382 0
      thirdparty/sdl/atomic/SDL_atomic.c
  51. 203 0
      thirdparty/sdl/atomic/SDL_spinlock.c
  52. 641 0
      thirdparty/sdl/core/linux/SDL_dbus.c
  53. 114 0
      thirdparty/sdl/core/linux/SDL_dbus.h
  54. 1037 0
      thirdparty/sdl/core/linux/SDL_evdev.c
  55. 41 0
      thirdparty/sdl/core/linux/SDL_evdev.h
  56. 168 0
      thirdparty/sdl/core/linux/SDL_evdev_capabilities.c
  57. 76 0
      thirdparty/sdl/core/linux/SDL_evdev_capabilities.h
  58. 997 0
      thirdparty/sdl/core/linux/SDL_evdev_kbd.c
  59. 35 0
      thirdparty/sdl/core/linux/SDL_evdev_kbd.h
  60. 282 0
      thirdparty/sdl/core/linux/SDL_evdev_kbd_default_accents.h
  61. 4765 0
      thirdparty/sdl/core/linux/SDL_evdev_kbd_default_keymap.h
  62. 414 0
      thirdparty/sdl/core/linux/SDL_fcitx.c
  63. 35 0
      thirdparty/sdl/core/linux/SDL_fcitx.h
  64. 694 0
      thirdparty/sdl/core/linux/SDL_ibus.c
  65. 55 0
      thirdparty/sdl/core/linux/SDL_ibus.h
  66. 150 0
      thirdparty/sdl/core/linux/SDL_ime.c
  67. 35 0
      thirdparty/sdl/core/linux/SDL_ime.h
  68. 156 0
      thirdparty/sdl/core/linux/SDL_system_theme.c
  69. 30 0
      thirdparty/sdl/core/linux/SDL_system_theme.h
  70. 345 0
      thirdparty/sdl/core/linux/SDL_threadprio.c
  71. 596 0
      thirdparty/sdl/core/linux/SDL_udev.c
  72. 113 0
      thirdparty/sdl/core/linux/SDL_udev.h
  73. 75 0
      thirdparty/sdl/core/unix/SDL_appid.c
  74. 30 0
      thirdparty/sdl/core/unix/SDL_appid.h
  75. 95 0
      thirdparty/sdl/core/unix/SDL_poll.c
  76. 33 0
      thirdparty/sdl/core/unix/SDL_poll.h
  77. 112 0
      thirdparty/sdl/core/windows/SDL_directx.h
  78. 98 0
      thirdparty/sdl/core/windows/SDL_gameinput.c
  79. 36 0
      thirdparty/sdl/core/windows/SDL_gameinput.h
  80. 254 0
      thirdparty/sdl/core/windows/SDL_hid.c
  81. 215 0
      thirdparty/sdl/core/windows/SDL_hid.h
  82. 434 0
      thirdparty/sdl/core/windows/SDL_immdevice.c
  83. 45 0
      thirdparty/sdl/core/windows/SDL_immdevice.h
  84. 375 0
      thirdparty/sdl/core/windows/SDL_windows.c
  85. 172 0
      thirdparty/sdl/core/windows/SDL_windows.h
  86. 140 0
      thirdparty/sdl/core/windows/SDL_xinput.c
  87. 276 0
      thirdparty/sdl/core/windows/SDL_xinput.h
  88. 21 0
      thirdparty/sdl/core/windows/pch.c
  89. 21 0
      thirdparty/sdl/core/windows/pch_cpp.cpp
  90. 1833 0
      thirdparty/sdl/events/SDL_events.c
  91. 59 0
      thirdparty/sdl/events/SDL_events_c.h
  92. 143 0
      thirdparty/sdl/events/SDL_eventwatch.c
  93. 45 0
      thirdparty/sdl/events/SDL_eventwatch_c.h
  94. 215 0
      thirdparty/sdl/events/SDL_mouse_c.h
  95. 788 0
      thirdparty/sdl/haptic/SDL_haptic.c
  96. 28 0
      thirdparty/sdl/haptic/SDL_haptic_c.h
  97. 194 0
      thirdparty/sdl/haptic/SDL_syshaptic.h
  98. 1373 0
      thirdparty/sdl/haptic/darwin/SDL_syshaptic.c
  99. 29 0
      thirdparty/sdl/haptic/darwin/SDL_syshaptic_c.h
  100. 1134 0
      thirdparty/sdl/haptic/linux/SDL_syshaptic.c

+ 10 - 0
COPYRIGHT.txt

@@ -541,6 +541,16 @@ Comment: RVO2
 Copyright: 2016, University of North Carolina at Chapel Hill
 License: Apache-2.0
 
+Files: thirdparty/sdl/*
+Comment: SDL
+Copyright: 1997-2025, Sam Lantinga
+License: Zlib
+
+Files: thirdparty/sdl/hidapi/*
+Comment: hidapi
+Copyright: 2010, Alan Ott, Signal 11 Software
+License: BSD-3-Clause
+
 Files: thirdparty/spirv-cross/*
 Comment: SPIRV-Cross
 Copyright: 2015-2021, Arm Limited

+ 6 - 4
SConstruct

@@ -194,16 +194,14 @@ opts.Add(
 opts.Add(BoolVariable("minizip", "Enable ZIP archive support using minizip", True))
 opts.Add(BoolVariable("brotli", "Enable Brotli for decompression and WOFF2 fonts support", True))
 opts.Add(BoolVariable("xaudio2", "Enable the XAudio2 audio driver on supported platforms", False))
-opts.Add(BoolVariable("vulkan", "Enable the vulkan rendering driver", True))
+opts.Add(BoolVariable("vulkan", "Enable the Vulkan rendering driver", True))
 opts.Add(BoolVariable("opengl3", "Enable the OpenGL/GLES3 rendering driver", True))
 opts.Add(BoolVariable("d3d12", "Enable the Direct3D 12 rendering driver on supported platforms", False))
 opts.Add(BoolVariable("metal", "Enable the Metal rendering driver on supported platforms (Apple arm64 only)", False))
 opts.Add(BoolVariable("use_volk", "Use the volk library to load the Vulkan loader dynamically", True))
-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("accesskit", "Use AccessKit C SDK", True))
 opts.Add(("accesskit_sdk_path", "Path to the AccessKit C SDK", ""))
+opts.Add(BoolVariable("sdl", "Enable the SDL3 input driver", True))
 
 # Advanced options
 opts.Add(
@@ -233,6 +231,7 @@ opts.Add("object_prefix", "Custom prefix added to the base filename of all gener
 opts.Add(BoolVariable("vsproj", "Generate a Visual Studio solution", False))
 opts.Add("vsproj_name", "Name of the Visual Studio solution", "godot")
 opts.Add("import_env_vars", "A comma-separated list of environment variables to copy from the outer environment.", "")
+opts.Add(BoolVariable("disable_exceptions", "Force disabling exception handling code", True))
 opts.Add(BoolVariable("disable_3d", "Disable 3D nodes for a smaller executable", False))
 opts.Add(BoolVariable("disable_advanced_gui", "Disable advanced GUI nodes and behaviors", False))
 opts.Add(BoolVariable("disable_physics_2d", "Disable 2D physics nodes and server", False))
@@ -241,6 +240,8 @@ opts.Add(BoolVariable("disable_navigation_2d", "Disable 2D navigation features",
 opts.Add(BoolVariable("disable_navigation_3d", "Disable 3D navigation features", False))
 opts.Add(BoolVariable("disable_xr", "Disable XR nodes and server", False))
 opts.Add("build_profile", "Path to a file containing a feature build profile", "")
+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("modules_enabled_by_default", "If no, disable all modules except ones explicitly enabled", True))
 opts.Add(BoolVariable("no_editor_splash", "Don't use the custom splash screen for the editor", True))
 opts.Add(
@@ -275,6 +276,7 @@ opts.Add(BoolVariable("builtin_msdfgen", "Use the built-in MSDFgen library", Tru
 opts.Add(BoolVariable("builtin_glslang", "Use the built-in glslang library", True))
 opts.Add(BoolVariable("builtin_graphite", "Use the built-in Graphite library", True))
 opts.Add(BoolVariable("builtin_harfbuzz", "Use the built-in HarfBuzz library", True))
+opts.Add(BoolVariable("builtin_sdl", "Use the built-in SDL library", True))
 opts.Add(BoolVariable("builtin_icu4c", "Use the built-in ICU library", True))
 opts.Add(BoolVariable("builtin_libjpeg_turbo", "Use the built-in libjpeg-turbo library", True))
 opts.Add(BoolVariable("builtin_libogg", "Use the built-in libogg library", True))

+ 7 - 3
core/input/input.cpp

@@ -587,9 +587,13 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, const String &p_
 		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;
+		// 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;
+				}
 			}
 		}
 		_set_joypad_mapping(js, mapping);

+ 5 - 0
drivers/SCsub

@@ -60,6 +60,11 @@ if env["metal"]:
         Exit(255)
     SConscript("metal/SCsub")
 
+# Input drivers
+if env["sdl"] and env["platform"] in ["linuxbsd", "macos", "windows"]:
+    # TODO: Evaluate support for Android, iOS, and Web.
+    SConscript("sdl/SCsub")
+
 # Core dependencies
 SConscript("png/SCsub")
 

+ 209 - 0
drivers/sdl/SCsub

@@ -0,0 +1,209 @@
+#!/usr/bin/env python
+from misc.utility.scons_hints import *
+
+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)

+ 130 - 0
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

+ 273 - 0
drivers/sdl/joypad_sdl.cpp

@@ -0,0 +1,273 @@
+/**************************************************************************/
+/*  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/input/default_controller_mappings.h"
+#include "core/os/time.h"
+#include "core/variant/dictionary.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() {
+	singleton = this;
+}
+
+JoypadSDL::~JoypadSDL() {
+	// Process any remaining input events
+	process_events();
+	for (int i = 0; i < Input::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 < Input::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_singleton()->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.insert(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::get_singleton()->joy_axis(
+							joy_id,
+							static_cast<JoyAxis>(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::get_singleton()->joy_button(
+							joy_id,
+							static_cast<JoyButton>(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::get_singleton()->joy_hat(
+							joy_id,
+							(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::get_singleton()->joy_axis(
+							joy_id,
+							static_cast<JoyAxis>(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::get_singleton()->joy_button(
+							joy_id,
+							static_cast<JoyButton>(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

+ 69 - 0
drivers/sdl/joypad_sdl.h

@@ -0,0 +1,69 @@
+/**************************************************************************/
+/*  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/input.h"
+#include "core/os/thread.h"
+
+typedef uint32_t SDL_JoystickID;
+typedef struct HWND__ *HWND;
+
+class JoypadSDL {
+public:
+	JoypadSDL();
+	~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;
+
+	Joypad joypads[Input::JOYPADS_MAX];
+	HashMap<SDL_JoystickID, int> sdl_instance_id_to_joypad_id;
+
+	void close_joypad(int p_pad_idx);
+};

+ 0 - 1
platform/linuxbsd/SCsub

@@ -8,7 +8,6 @@ import platform_linuxbsd_builders
 common_linuxbsd = [
     "crash_handler_linuxbsd.cpp",
     "os_linuxbsd.cpp",
-    "joypad_linux.cpp",
     "freedesktop_portal_desktop.cpp",
     "freedesktop_screensaver.cpp",
     "freedesktop_at_spi_monitor.cpp",

+ 12 - 0
platform/linuxbsd/detect.py

@@ -394,6 +394,18 @@ def configure(env: "SConsEnvironment"):
     else:
         env["udev"] = False  # Linux specific
 
+    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"]:
         env.ParseConfig("pkg-config zlib --cflags --libs")

+ 0 - 613
platform/linuxbsd/joypad_linux.cpp

@@ -1,613 +0,0 @@
-/**************************************************************************/
-/*  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 <fcntl.h>
-#include <linux/input.h>
-#include <unistd.h>
-#include <cerrno>
-
-#ifdef UDEV_ENABLED
-#ifdef SOWRAP_ENABLED
-#include "libudev-so_wrap.h"
-#else
-#include <libudev.h>
-#endif
-#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
-
-// On Linux with Steam Input Xbox 360 devices have an index appended to their device name, this index is
-// the Steam Input gamepad index
-#define VALVE_GAMEPAD_NAME_PREFIX "Microsoft X-Box 360 pad "
-// IDs used by Steam Input virtual controllers.
-// See https://partner.steamgames.com/doc/features/steam_controller/steam_input_gamepad_emulation_bestpractices
-#define VALVE_GAMEPAD_VID 0x28DE
-#define VALVE_GAMEPAD_PID 0x11FF
-
-JoypadLinux::Joypad::~Joypad() {
-	for (int i = 0; i < MAX_ABS; i++) {
-		if (abs_info[i]) {
-			memdelete(abs_info[i]);
-		}
-	}
-}
-
-void JoypadLinux::Joypad::reset() {
-	dpad.clear();
-	fd = -1;
-	for (int i = 0; i < MAX_ABS; i++) {
-		abs_map[i] = -1;
-		curr_axis[i] = 0;
-	}
-	events.clear();
-}
-
-JoypadLinux::JoypadLinux(Input *in) {
-#ifdef UDEV_ENABLED
-	if (OS::get_singleton()->is_sandboxed()) {
-		// 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.");
-	}
-#ifdef SOWRAP_ENABLED
-	else {
-#ifdef DEBUG_ENABLED
-		int dylibloader_verbose = 1;
-#else
-		int dylibloader_verbose = 0;
-#endif
-		use_udev = initialize_libudev(dylibloader_verbose) == 0;
-		if (use_udev) {
-			if (!udev_new || !udev_unref || !udev_enumerate_new || !udev_enumerate_add_match_subsystem || !udev_enumerate_scan_devices || !udev_enumerate_get_list_entry || !udev_list_entry_get_next || !udev_list_entry_get_name || !udev_device_new_from_syspath || !udev_device_get_devnode || !udev_device_get_action || !udev_device_unref || !udev_enumerate_unref || !udev_monitor_new_from_netlink || !udev_monitor_filter_add_match_subsystem_devtype || !udev_monitor_enable_receiving || !udev_monitor_get_fd || !udev_monitor_receive_device || !udev_monitor_unref) {
-				// There's no API to check version, check if functions are available instead.
-				use_udev = false;
-				print_verbose("JoypadLinux: Unsupported udev library version!");
-			} else {
-				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.");
-		}
-	}
-#endif // SOWRAP_ENABLED
-#else
-	print_verbose("JoypadLinux: udev disabled, parsing /dev/input to detect joypads.");
-#endif // UDEV_ENABLED
-
-	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 = static_cast<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.contains(ignore_str)) {
-				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.contains(ignore_str)) {
-						if (action == "add") {
-							open_joypad(devnode);
-						} else if (String(action) == "remove") {
-							close_joypad(devnode);
-						}
-					}
-				}
-				udev_device_unref(dev);
-			}
-		}
-		OS::get_singleton()->delay_usec(50'000);
-	}
-	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.has(fname)) {
-					open_joypad(fname);
-				}
-			}
-		}
-		closedir(input_directory);
-		OS::get_singleton()->delay_usec(1'000'000);
-	}
-}
-
-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
-		bool has_abs_left = (test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit));
-		bool has_abs_right = (test_bit(ABS_RX, absbit) && test_bit(ABS_RY, absbit));
-		if (!(test_bit(EV_KEY, evbit) && test_bit(EV_ABS, evbit) && (has_abs_left || has_abs_right))) {
-			close(fd);
-			return;
-		}
-
-		char uid[128];
-		char namebuf[128];
-		String name = "";
-		input_id inpid;
-		if (ioctl(fd, EVIOCGNAME(sizeof(namebuf)), namebuf) >= 0) {
-			name = namebuf;
-		}
-
-		for (const String &word : name.to_lower().split(" ")) {
-			if (banned_words.has(word)) {
-				return;
-			}
-		}
-
-		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) {
-			Dictionary joypad_info;
-			joypad_info["vendor_id"] = inpid.vendor;
-			joypad_info["product_id"] = inpid.product;
-			joypad_info["raw_name"] = name;
-
-			sprintf(uid + String(uid).length(), "%04x%04x%04x%04x%04x%04x", vendor, 0, product, 0, version, 0);
-
-			if (inpid.vendor == VALVE_GAMEPAD_VID && inpid.product == VALVE_GAMEPAD_PID) {
-				if (name.begins_with(VALVE_GAMEPAD_NAME_PREFIX)) {
-					String idx_str = name.substr(strlen(VALVE_GAMEPAD_NAME_PREFIX));
-					if (idx_str.is_valid_int()) {
-						joypad_info["steam_input_index"] = idx_str.to_int();
-					}
-				}
-			}
-
-			input->joy_connection_changed(joy_num, true, name, uid, joypad_info);
-		} 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 = std::floor(p_weak_magnitude * (float)0xffff);
-	effect.u.rumble.strong_magnitude = std::floor(p_strong_magnitude * (float)0xffff);
-	effect.replay.length = std::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) {
-				no_events = false;
-				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) {
-			OS::get_singleton()->delay_usec(10'000);
-		}
-	}
-}
-
-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, (JoyButton)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.set_flag(HatMask::LEFT);
-									joypad.dpad.clear_flag(HatMask::RIGHT);
-								} else {
-									joypad.dpad.set_flag(HatMask::RIGHT);
-									joypad.dpad.clear_flag(HatMask::LEFT);
-								}
-							} else {
-								joypad.dpad.clear_flag(HatMask::LEFT);
-								joypad.dpad.clear_flag(HatMask::RIGHT);
-							}
-							input->joy_hat(i, joypad.dpad);
-							break;
-
-						case ABS_HAT0Y:
-							if (joypad_event.value != 0) {
-								if (joypad_event.value < 0) {
-									joypad.dpad.set_flag(HatMask::UP);
-									joypad.dpad.clear_flag(HatMask::DOWN);
-								} else {
-									joypad.dpad.set_flag(HatMask::DOWN);
-									joypad.dpad.clear_flag(HatMask::UP);
-								}
-							} else {
-								joypad.dpad.clear_flag(HatMask::UP);
-								joypad.dpad.clear_flag(HatMask::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, (JoyAxis)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

+ 0 - 136
platform/linuxbsd/joypad_linux.h

@@ -1,136 +0,0 @@
-/**************************************************************************/
-/*  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.                 */
-/**************************************************************************/
-
-#pragma once
-
-#ifdef JOYDEV_ENABLED
-
-#include "core/input/input.h"
-#include "core/os/mutex.h"
-#include "core/os/thread.h"
-#include "core/templates/local_vector.h"
-
-struct input_absinfo;
-
-class JoypadLinux {
-public:
-	JoypadLinux(Input *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];
-		BitField<HatMask> dpad = HatMask::CENTER;
-		int fd = -1;
-
-		String devpath;
-		input_absinfo *abs_info[MAX_ABS] = {};
-
-		bool force_feedback = false;
-		int ff_effect_id = 0;
-		uint64_t ff_effect_timestamp = 0;
-
-		LocalVector<JoypadEvent> events;
-
-		~Joypad();
-		void reset();
-	};
-
-#ifdef UDEV_ENABLED
-	bool use_udev = false;
-#endif
-	Input *input = nullptr;
-
-	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;
-
-	// List of lowercase words that will prevent the controller from being recognized if its name matches.
-	// This is done to prevent trackpads, graphics tablets and motherboard LED controllers from being
-	// recognized as controllers (and taking up controller ID slots as a result).
-	// Only whole words are matched within the controller name string. The match is case-insensitive.
-	const Vector<String> banned_words = {
-		"touchpad", // Matches e.g. "SynPS/2 Synaptics TouchPad", "Sony Interactive Entertainment DualSense Wireless Controller Touchpad"
-		"trackpad",
-		"clickpad",
-		"keyboard", // Matches e.g. "PG-90215 Keyboard", "Usb Keyboard Usb Keyboard Consumer Control"
-		"mouse", // Matches e.g. "Mouse passthrough"
-		"pen", // Matches e.g. "Wacom One by Wacom S Pen"
-		"finger", // Matches e.g. "Wacom HID 495F Finger"
-		"led", // Matches e.g. "ASRock LED Controller"
-	};
-
-	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 // JOYDEV_ENABLED

+ 17 - 7
platform/linuxbsd/os_linuxbsd.cpp

@@ -32,6 +32,9 @@
 
 #include "core/io/certs_compressed.gen.h"
 #include "core/io/dir_access.h"
+#ifdef SDL_ENABLED
+#include "drivers/sdl/joypad_sdl.h"
+#endif
 #include "main/main.h"
 #include "servers/display_server.h"
 #include "servers/rendering_server.h"
@@ -158,8 +161,13 @@ void OS_LinuxBSD::initialize() {
 }
 
 void OS_LinuxBSD::initialize_joypads() {
-#ifdef JOYDEV_ENABLED
-	joypad = memnew(JoypadLinux(Input::get_singleton()));
+#ifdef SDL_ENABLED
+	joypad_sdl = memnew(JoypadSDL());
+	if (joypad_sdl->initialize() != OK) {
+		ERR_PRINT("Couldn't initialize SDL joypad input driver.");
+		memdelete(joypad_sdl);
+		joypad_sdl = nullptr;
+	}
 #endif
 }
 
@@ -241,9 +249,9 @@ void OS_LinuxBSD::finalize() {
 	driver_alsamidi.close();
 #endif
 
-#ifdef JOYDEV_ENABLED
-	if (joypad) {
-		memdelete(joypad);
+#ifdef SDL_ENABLED
+	if (joypad_sdl) {
+		memdelete(joypad_sdl);
 	}
 #endif
 }
@@ -973,8 +981,10 @@ void OS_LinuxBSD::run() {
 
 	while (true) {
 		DisplayServer::get_singleton()->process_events(); // 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;

+ 4 - 3
platform/linuxbsd/os_linuxbsd.h

@@ -31,7 +31,6 @@
 #pragma once
 
 #include "crash_handler_linuxbsd.h"
-#include "joypad_linux.h"
 
 #include "core/input/input.h"
 #include "drivers/alsa/audio_driver_alsa.h"
@@ -48,6 +47,8 @@
 #endif
 #endif
 
+class JoypadSDL;
+
 class OS_LinuxBSD : public OS_Unix {
 	virtual void delete_main_loop() override;
 
@@ -60,8 +61,8 @@ class OS_LinuxBSD : public OS_Unix {
 	int _stretch_to_fc(int p_stretch) const;
 #endif
 
-#ifdef JOYDEV_ENABLED
-	JoypadLinux *joypad = nullptr;
+#ifdef SDL_ENABLED
+	JoypadSDL *joypad_sdl = nullptr;
 #endif
 
 #ifdef ALSA_ENABLED

+ 0 - 2
platform/linuxbsd/x11/display_server_x11.h

@@ -32,8 +32,6 @@
 
 #ifdef X11_ENABLED
 
-#include "joypad_linux.h"
-
 #include "core/input/input.h"
 #include "core/os/mutex.h"
 #include "core/os/thread.h"

+ 4 - 0
platform/macos/detect.py

@@ -197,6 +197,10 @@ def configure(env: "SConsEnvironment"):
     if env["builtin_libtheora"] and env["arch"] == "x86_64":
         env["x86_libtheora_opt_gcc"] = True
 
+    if env["sdl"]:
+        env.Append(CPPDEFINES=["SDL_ENABLED"])
+        env.Append(LINKFLAGS=["-framework", "ForceFeedback"])
+
     ## Flags
 
     env.Prepend(CPPPATH=["#platform/macos"])

+ 5 - 2
platform/macos/os_macos.h

@@ -33,12 +33,13 @@
 #include "crash_handler_macos.h"
 
 #include "core/input/input.h"
-#import "drivers/apple/joypad_apple.h"
 #import "drivers/coreaudio/audio_driver_coreaudio.h"
 #import "drivers/coremidi/midi_driver_coremidi.h"
 #include "drivers/unix/os_unix.h"
 #include "servers/audio_server.h"
 
+class JoypadSDL;
+
 class OS_MacOS : public OS_Unix {
 #ifdef COREAUDIO_ENABLED
 	AudioDriverCoreAudio audio_driver;
@@ -62,7 +63,9 @@ protected:
 	int argc = 0;
 	char **argv = nullptr;
 
-	JoypadApple *joypad_apple = nullptr;
+#ifdef SDL_ENABLED
+	JoypadSDL *joypad_sdl = nullptr;
+#endif
 	MainLoop *main_loop = nullptr;
 	CFRunLoopTimerRef wait_timer = nil;
 

+ 21 - 4
platform/macos/os_macos.mm

@@ -43,6 +43,10 @@
 #include "drivers/apple/os_log_logger.h"
 #include "main/main.h"
 
+#ifdef SDL_ENABLED
+#include "drivers/sdl/joypad_sdl.h"
+#endif
+
 #include <dlfcn.h>
 #include <libproc.h>
 #import <mach-o/dyld.h>
@@ -230,13 +234,22 @@ void OS_MacOS::finalize() {
 
 	delete_main_loop();
 
-	if (joypad_apple) {
-		memdelete(joypad_apple);
+#ifdef SDL_ENABLED
+	if (joypad_sdl) {
+		memdelete(joypad_sdl);
 	}
+#endif
 }
 
 void OS_MacOS::initialize_joypads() {
-	joypad_apple = memnew(JoypadApple());
+#ifdef SDL_ENABLED
+	joypad_sdl = memnew(JoypadSDL());
+	if (joypad_sdl->initialize() != OK) {
+		ERR_PRINT("Couldn't initialize SDL joypad input driver.");
+		memdelete(joypad_sdl);
+		joypad_sdl = nullptr;
+	}
+#endif
 }
 
 void OS_MacOS::set_main_loop(MainLoop *p_main_loop) {
@@ -1078,7 +1091,11 @@ void OS_MacOS_NSApp::start_main() {
 							} else if (ds) {
 								ds->process_events();
 							}
-							joypad_apple->process_joypads();
+#ifdef SDL_ENABLED
+							if (joypad_sdl) {
+								joypad_sdl->process_events();
+							}
+#endif
 
 							if (Main::iteration() || sig_received) {
 								terminate();

+ 0 - 1
platform/windows/SCsub

@@ -17,7 +17,6 @@ common_win = [
     "os_windows.cpp",
     "display_server_windows.cpp",
     "key_mapping_windows.cpp",
-    "joypad_windows.cpp",
     "tts_windows.cpp",
     "windows_terminal_logger.cpp",
     "windows_utils.cpp",

+ 6 - 0
platform/windows/detect.py

@@ -480,6 +480,9 @@ def configure_msvc(env: "SConsEnvironment"):
         if not env["use_volk"]:
             LIBS += ["vulkan"]
 
+    if env["sdl"]:
+        env.Append(CPPDEFINES=["SDL_ENABLED"])
+
     if env["d3d12"]:
         check_d3d12_installed(env, env["arch"] + "-msvc")
 
@@ -867,6 +870,9 @@ def configure_mingw(env: "SConsEnvironment"):
         if not env["use_volk"]:
             env.Append(LIBS=["vulkan"])
 
+    if env["sdl"]:
+        env.Append(CPPDEFINES=["SDL_ENABLED"])
+
     if env["d3d12"]:
         if env["use_llvm"]:
             check_d3d12_installed(env, env["arch"] + "-llvm")

+ 24 - 6
platform/windows/display_server_windows.cpp

@@ -43,6 +43,10 @@
 #include "main/main.h"
 #include "scene/resources/texture.h"
 
+#ifdef SDL_ENABLED
+#include "drivers/sdl/joypad_sdl.h"
+#endif
+
 #include "servers/rendering/dummy/rasterizer_dummy.h"
 
 #if defined(VULKAN_ENABLED)
@@ -3771,7 +3775,11 @@ void DisplayServerWindows::process_events() {
 	ERR_FAIL_COND(!Thread::is_main_thread());
 
 	if (!drop_events) {
-		joypad->process_joypads();
+#ifdef SDL_ENABLED
+		if (joypad_sdl) {
+			joypad_sdl->process_events();
+		}
+#endif
 	}
 
 	_THREAD_SAFE_LOCK_
@@ -5962,9 +5970,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
 			}
 
 		} break;
-		case WM_DEVICECHANGE: {
-			joypad->probe_joypads();
-		} break;
 		case WM_DESTROY: {
 #ifdef ACCESSKIT_ENABLED
 			if (accessibility_driver) {
@@ -7155,7 +7160,16 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
 		ERR_FAIL_MSG("Failed to create main window.");
 	}
 
-	joypad = new JoypadWindows(&windows[MAIN_WINDOW_ID].hWnd);
+#ifdef SDL_ENABLED
+	joypad_sdl = memnew(JoypadSDL());
+	if (joypad_sdl->initialize() == OK) {
+		joypad_sdl->setup_sdl_helper_window(windows[MAIN_WINDOW_ID].hWnd);
+	} else {
+		ERR_PRINT("Couldn't initialize SDL joypad input driver.");
+		memdelete(joypad_sdl);
+		joypad_sdl = nullptr;
+	}
+#endif
 
 	for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
 		if (p_flags & (1 << i)) {
@@ -7309,7 +7323,11 @@ DisplayServerWindows::~DisplayServerWindows() {
 		E->erase();
 	}
 
-	delete joypad;
+#ifdef SDL_ENABLED
+	if (joypad_sdl) {
+		memdelete(joypad_sdl);
+	}
+#endif
 	touch_state.clear();
 
 	cursors_cache.clear();

+ 5 - 2
platform/windows/display_server_windows.h

@@ -31,7 +31,6 @@
 #pragma once
 
 #include "crash_handler_windows.h"
-#include "joypad_windows.h"
 #include "key_mapping_windows.h"
 #include "tts_windows.h"
 
@@ -196,6 +195,8 @@ class DropTargetWindows;
 #define WDA_EXCLUDEFROMCAPTURE 0x00000011
 #endif
 
+class JoypadSDL;
+
 class DisplayServerWindows : public DisplayServer {
 	GDSOFTCLASS(DisplayServerWindows, DisplayServer);
 
@@ -375,7 +376,9 @@ class DisplayServerWindows : public DisplayServer {
 		HWND parent_hwnd = 0;
 	};
 
-	JoypadWindows *joypad = nullptr;
+#ifdef SDL_ENABLED
+	JoypadSDL *joypad_sdl = nullptr;
+#endif
 	HHOOK mouse_monitor = nullptr;
 	List<WindowID> popup_list;
 	uint64_t time_since_popup = 0;

+ 1 - 0
platform/windows/drop_target_windows.cpp

@@ -35,6 +35,7 @@
 #include "core/os/time.h"
 
 #include <fileapi.h>
+#include <shellapi.h>
 
 // Helpers
 

+ 0 - 625
platform/windows/joypad_windows.cpp

@@ -1,625 +0,0 @@
-/**************************************************************************/
-/*  joypad_windows.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_windows.h"
-
-#include <oleauto.h>
-#include <wbemidl.h>
-
-DWORD WINAPI _xinput_get_state(DWORD dwUserIndex, XINPUT_STATE *pState) {
-	return ERROR_DEVICE_NOT_CONNECTED;
-}
-
-DWORD WINAPI _xinput_set_state(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration) {
-	return ERROR_DEVICE_NOT_CONNECTED;
-}
-
-MMRESULT WINAPI _winmm_get_joycaps(UINT uJoyID, LPJOYCAPSW pjc, UINT cbjc) {
-	return MMSYSERR_NODRIVER;
-}
-
-JoypadWindows::JoypadWindows() {
-}
-
-JoypadWindows::JoypadWindows(HWND *hwnd) {
-	input = Input::get_singleton();
-	hWnd = hwnd;
-	x_joypad_probe_count = 0;
-	d_joypad_count = 0;
-	dinput = nullptr;
-	xinput_dll = nullptr;
-	xinput_get_state = nullptr;
-	xinput_set_state = nullptr;
-	winmm_get_joycaps = nullptr;
-
-	load_xinput();
-
-	for (int i = 0; i < JOYPADS_MAX; i++) {
-		attached_joypads[i] = false;
-	}
-
-	HRESULT result = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&dinput, nullptr);
-	if (result == DI_OK) {
-		probe_joypads();
-	} else {
-		ERR_PRINT("Couldn't initialize DirectInput. Error: " + itos(result));
-		if (result == DIERR_OUTOFMEMORY) {
-			ERR_PRINT("The Windows DirectInput subsystem could not allocate sufficient memory.");
-			ERR_PRINT("Rebooting your PC may solve this issue.");
-		}
-		// Ensure dinput is still a nullptr.
-		dinput = nullptr;
-	}
-}
-
-JoypadWindows::~JoypadWindows() {
-	close_d_joypad();
-	if (dinput) {
-		dinput->Release();
-	}
-	unload_winmm();
-	unload_xinput();
-}
-
-bool JoypadWindows::is_d_joypad_known(const GUID &p_guid) {
-	for (int i = 0; i < JOYPADS_MAX; i++) {
-		if (d_joypads[i].guid == p_guid) {
-			d_joypads[i].confirmed = true;
-			return true;
-		}
-	}
-	return false;
-}
-
-// adapted from SDL2, works a lot better than the MSDN version
-bool JoypadWindows::is_xinput_joypad(const GUID *p_guid) {
-	static GUID IID_ValveStreamingGamepad = { MAKELONG(0x28DE, 0x11FF), 0x28DE, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
-	static GUID IID_X360WiredGamepad = { MAKELONG(0x045E, 0x02A1), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
-	static GUID IID_X360WirelessGamepad = { MAKELONG(0x045E, 0x028E), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
-	static GUID IID_XSWirelessGamepad = { MAKELONG(0x045E, 0x0B13), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
-	static GUID IID_XEliteWirelessGamepad = { MAKELONG(0x045E, 0x0B05), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
-	static GUID IID_XOneWiredGamepad = { MAKELONG(0x045E, 0x02FF), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
-	static GUID IID_XOneWirelessGamepad = { MAKELONG(0x045E, 0x02DD), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
-	static GUID IID_XOneNewWirelessGamepad = { MAKELONG(0x045E, 0x02D1), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
-	static GUID IID_XOneSWirelessGamepad = { MAKELONG(0x045E, 0x02EA), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
-	static GUID IID_XOneSBluetoothGamepad = { MAKELONG(0x045E, 0x02E0), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
-	static GUID IID_XOneEliteWirelessGamepad = { MAKELONG(0x045E, 0x02E3), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
-	static GUID IID_XOneElite2WirelessGamepad = { MAKELONG(0x045E, 0x0B22), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
-
-	if (memcmp(p_guid, &IID_ValveStreamingGamepad, sizeof(*p_guid)) == 0 ||
-			memcmp(p_guid, &IID_X360WiredGamepad, sizeof(*p_guid)) == 0 ||
-			memcmp(p_guid, &IID_X360WirelessGamepad, sizeof(*p_guid)) == 0 ||
-			memcmp(p_guid, &IID_XSWirelessGamepad, sizeof(*p_guid)) == 0 ||
-			memcmp(p_guid, &IID_XEliteWirelessGamepad, sizeof(*p_guid)) == 0 ||
-			memcmp(p_guid, &IID_XOneWiredGamepad, sizeof(*p_guid)) == 0 ||
-			memcmp(p_guid, &IID_XOneWirelessGamepad, sizeof(*p_guid)) == 0 ||
-			memcmp(p_guid, &IID_XOneNewWirelessGamepad, sizeof(*p_guid)) == 0 ||
-			memcmp(p_guid, &IID_XOneSWirelessGamepad, sizeof(*p_guid)) == 0 ||
-			memcmp(p_guid, &IID_XOneSBluetoothGamepad, sizeof(*p_guid)) == 0 ||
-			memcmp(p_guid, &IID_XOneEliteWirelessGamepad, sizeof(*p_guid)) == 0 ||
-			memcmp(p_guid, &IID_XOneElite2WirelessGamepad, sizeof(*p_guid)) == 0) {
-		return true;
-	}
-
-	PRAWINPUTDEVICELIST dev_list = nullptr;
-	unsigned int dev_list_count = 0;
-
-	if (GetRawInputDeviceList(nullptr, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1) {
-		return false;
-	}
-	dev_list = (PRAWINPUTDEVICELIST)memalloc(sizeof(RAWINPUTDEVICELIST) * dev_list_count);
-	ERR_FAIL_NULL_V_MSG(dev_list, false, "Out of memory.");
-
-	if (GetRawInputDeviceList(dev_list, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1) {
-		memfree(dev_list);
-		return false;
-	}
-	for (unsigned int i = 0; i < dev_list_count; i++) {
-		RID_DEVICE_INFO rdi;
-		char dev_name[128];
-		UINT rdiSize = sizeof(rdi);
-		UINT nameSize = sizeof(dev_name);
-
-		rdi.cbSize = rdiSize;
-		if ((dev_list[i].dwType == RIM_TYPEHID) &&
-				(GetRawInputDeviceInfoA(dev_list[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != (UINT)-1) &&
-				(MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == (LONG)p_guid->Data1) &&
-				(GetRawInputDeviceInfoA(dev_list[i].hDevice, RIDI_DEVICENAME, &dev_name, &nameSize) != (UINT)-1) &&
-				(strstr(dev_name, "IG_") != nullptr)) {
-			memfree(dev_list);
-			return true;
-		}
-	}
-	memfree(dev_list);
-	return false;
-}
-
-void JoypadWindows::probe_xinput_joypad(const String &name) {
-	if (x_joypad_probe_count >= XUSER_MAX_COUNT) {
-		return;
-	}
-	int i = x_joypad_probe_count;
-	x_joypad_probe_count++;
-
-	ZeroMemory(&x_joypads[i].state, sizeof(XINPUT_STATE));
-
-	DWORD dwResult = xinput_get_state(i, &x_joypads[i].state);
-	if (dwResult == ERROR_SUCCESS) {
-		int id = input->get_unused_joy_id();
-		if (id != -1 && !x_joypads[i].attached) {
-			x_joypads[i].attached = true;
-			x_joypads[i].id = id;
-			x_joypads[i].ff_timestamp = 0;
-			x_joypads[i].ff_end_timestamp = 0;
-			x_joypads[i].vibrating = false;
-			attached_joypads[id] = true;
-			Dictionary joypad_info;
-			String joypad_name;
-
-			joypad_info["xinput_index"] = (int)i;
-
-			JOYCAPSW jc;
-			memset(&jc, 0, sizeof(JOYCAPSW));
-			MMRESULT jcResult = winmm_get_joycaps((UINT)id, &jc, sizeof(JOYCAPSW));
-			if (jcResult == JOYERR_NOERROR) {
-				joypad_info["vendor_id"] = itos(jc.wMid);
-				joypad_info["product_id"] = itos(jc.wPid);
-				if (!name.is_empty()) {
-					joypad_name = name.trim_prefix("Controller (").trim_suffix(")");
-				}
-			}
-
-			input->joy_connection_changed(id, true, joypad_name, "__XINPUT_DEVICE__", joypad_info);
-		}
-	} else if (x_joypads[i].attached) {
-		x_joypads[i].attached = false;
-		attached_joypads[x_joypads[i].id] = false;
-		input->joy_connection_changed(x_joypads[i].id, false, "");
-	}
-}
-
-bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) {
-	ERR_FAIL_NULL_V_MSG(dinput, false, "DirectInput not initialized. Rebooting your PC may solve this issue.");
-	HRESULT hr;
-	int num = input->get_unused_joy_id();
-
-	if (is_d_joypad_known(instance->guidInstance) || num == -1) {
-		return false;
-	}
-
-	d_joypads[num] = dinput_gamepad();
-	dinput_gamepad *joy = &d_joypads[num];
-
-	const DWORD devtype = (instance->dwDevType & 0xFF);
-
-	if ((devtype != DI8DEVTYPE_JOYSTICK) && (devtype != DI8DEVTYPE_GAMEPAD) && (devtype != DI8DEVTYPE_1STPERSON) && (devtype != DI8DEVTYPE_DRIVING)) {
-		return false;
-	}
-
-	hr = dinput->CreateDevice(instance->guidInstance, &joy->di_joy, nullptr);
-
-	if (FAILED(hr)) {
-		return false;
-	}
-
-	const GUID &guid = instance->guidProduct;
-	char uid[128];
-
-	ERR_FAIL_COND_V_MSG(memcmp(&guid.Data4[2], "PIDVID", 6), false, "DirectInput device not recognized.");
-	WORD type = BSWAP16(0x03);
-	WORD vendor = BSWAP16(LOWORD(guid.Data1));
-	WORD product = BSWAP16(HIWORD(guid.Data1));
-	WORD version = 0;
-	sprintf_s(uid, "%04x%04x%04x%04x%04x%04x%04x%04x", type, 0, vendor, 0, product, 0, version, 0);
-
-	Dictionary joypad_info;
-	joypad_info["vendor_id"] = itos(vendor);
-	joypad_info["product_id"] = itos(product);
-
-	id_to_change = num;
-	slider_count = 0;
-
-	joy->di_joy->SetDataFormat(&c_dfDIJoystick2);
-	joy->di_joy->SetCooperativeLevel(*hWnd, DISCL_FOREGROUND);
-	joy->di_joy->EnumObjects(objectsCallback, this, 0);
-	joy->joy_axis.sort();
-
-	joy->guid = instance->guidInstance;
-	const String &name = String(instance->tszProductName).trim_prefix("Controller (").trim_suffix(")");
-	input->joy_connection_changed(num, true, name, uid, joypad_info);
-	joy->attached = true;
-	joy->id = num;
-	attached_joypads[num] = true;
-	joy->confirmed = true;
-	d_joypad_count++;
-	return true;
-}
-
-void JoypadWindows::setup_d_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_joy_id) {
-	if (ob->dwType & DIDFT_AXIS) {
-		HRESULT res;
-		DIPROPRANGE prop_range;
-		DIPROPDWORD dilong;
-		LONG ofs;
-		if (ob->guidType == GUID_XAxis) {
-			ofs = DIJOFS_X;
-		} else if (ob->guidType == GUID_YAxis) {
-			ofs = DIJOFS_Y;
-		} else if (ob->guidType == GUID_ZAxis) {
-			ofs = DIJOFS_Z;
-		} else if (ob->guidType == GUID_RxAxis) {
-			ofs = DIJOFS_RX;
-		} else if (ob->guidType == GUID_RyAxis) {
-			ofs = DIJOFS_RY;
-		} else if (ob->guidType == GUID_RzAxis) {
-			ofs = DIJOFS_RZ;
-		} else if (ob->guidType == GUID_Slider) {
-			if (slider_count < 2) {
-				ofs = DIJOFS_SLIDER(slider_count);
-				slider_count++;
-			} else {
-				return;
-			}
-		} else {
-			return;
-		}
-		prop_range.diph.dwSize = sizeof(DIPROPRANGE);
-		prop_range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
-		prop_range.diph.dwObj = ob->dwType;
-		prop_range.diph.dwHow = DIPH_BYID;
-		prop_range.lMin = -MAX_JOY_AXIS;
-		prop_range.lMax = +MAX_JOY_AXIS;
-
-		dinput_gamepad &joy = d_joypads[p_joy_id];
-
-		res = IDirectInputDevice8_SetProperty(joy.di_joy, DIPROP_RANGE, &prop_range.diph);
-		if (FAILED(res)) {
-			return;
-		}
-
-		dilong.diph.dwSize = sizeof(dilong);
-		dilong.diph.dwHeaderSize = sizeof(dilong.diph);
-		dilong.diph.dwObj = ob->dwType;
-		dilong.diph.dwHow = DIPH_BYID;
-		dilong.dwData = 0;
-
-		res = IDirectInputDevice8_SetProperty(joy.di_joy, DIPROP_DEADZONE, &dilong.diph);
-		if (FAILED(res)) {
-			return;
-		}
-
-		joy.joy_axis.push_back(ofs);
-	}
-}
-
-BOOL CALLBACK JoypadWindows::enumCallback(const DIDEVICEINSTANCE *p_instance, void *p_context) {
-	JoypadWindows *self = static_cast<JoypadWindows *>(p_context);
-	if (self->is_xinput_joypad(&p_instance->guidProduct)) {
-		self->probe_xinput_joypad(p_instance->tszProductName);
-		return DIENUM_CONTINUE;
-	}
-	self->setup_dinput_joypad(p_instance);
-	return DIENUM_CONTINUE;
-}
-
-BOOL CALLBACK JoypadWindows::objectsCallback(const DIDEVICEOBJECTINSTANCE *p_instance, void *p_context) {
-	JoypadWindows *self = static_cast<JoypadWindows *>(p_context);
-	self->setup_d_joypad_object(p_instance, self->id_to_change);
-
-	return DIENUM_CONTINUE;
-}
-
-void JoypadWindows::close_d_joypad(int id) {
-	if (id == -1) {
-		for (int i = 0; i < JOYPADS_MAX; i++) {
-			close_d_joypad(i);
-		}
-		return;
-	}
-
-	if (!d_joypads[id].attached) {
-		return;
-	}
-
-	d_joypads[id].di_joy->Unacquire();
-	d_joypads[id].di_joy->Release();
-	d_joypads[id].attached = false;
-	attached_joypads[d_joypads[id].id] = false;
-	d_joypads[id].guid.Data1 = d_joypads[id].guid.Data2 = d_joypads[id].guid.Data3 = 0;
-	input->joy_connection_changed(d_joypads[id].id, false, "");
-	d_joypad_count--;
-}
-
-void JoypadWindows::probe_joypads() {
-	ERR_FAIL_NULL_MSG(dinput, "DirectInput not initialized. Rebooting your PC may solve this issue.");
-
-	for (int i = 0; i < d_joypad_count; i++) {
-		d_joypads[i].confirmed = false; // Flag DirectInput devices for re-checking their availability.
-	}
-
-	x_joypad_probe_count = 0;
-	// Probe _all attached_ joypad devices.
-	dinput->EnumDevices(DI8DEVCLASS_GAMECTRL, enumCallback, this, DIEDFL_ATTACHEDONLY);
-
-	for (int i = x_joypad_probe_count; i < XUSER_MAX_COUNT; i++) {
-		// Handle disconnect of XInput devices.
-		// And act as a fallback, just in case DirectInput could not find the device.
-		probe_xinput_joypad();
-	}
-
-	for (int i = 0; i < d_joypad_count; i++) {
-		if (!d_joypads[i].confirmed) {
-			close_d_joypad(i); // Any DirectInput device not found during probing is considered as disconnected.
-		}
-	}
-}
-
-void JoypadWindows::process_joypads() {
-	HRESULT hr;
-
-	// Handle XInput joypads.
-	for (int i = 0; i < XUSER_MAX_COUNT; i++) {
-		xinput_gamepad &joy = x_joypads[i];
-		if (!joy.attached) {
-			continue;
-		}
-		ZeroMemory(&joy.state, sizeof(XINPUT_STATE));
-
-		xinput_get_state(i, &joy.state);
-		if (joy.state.dwPacketNumber != joy.last_packet) {
-			int button_mask = XINPUT_GAMEPAD_DPAD_UP;
-			for (int j = 0; j <= 16; j++) {
-				input->joy_button(joy.id, (JoyButton)j, joy.state.Gamepad.wButtons & button_mask);
-				button_mask = button_mask * 2;
-			}
-
-			input->joy_axis(joy.id, JoyAxis::LEFT_X, axis_correct(joy.state.Gamepad.sThumbLX, true));
-			input->joy_axis(joy.id, JoyAxis::LEFT_Y, axis_correct(joy.state.Gamepad.sThumbLY, true, false, true));
-			input->joy_axis(joy.id, JoyAxis::RIGHT_X, axis_correct(joy.state.Gamepad.sThumbRX, true));
-			input->joy_axis(joy.id, JoyAxis::RIGHT_Y, axis_correct(joy.state.Gamepad.sThumbRY, true, false, true));
-			input->joy_axis(joy.id, JoyAxis::TRIGGER_LEFT, axis_correct(joy.state.Gamepad.bLeftTrigger, true, true));
-			input->joy_axis(joy.id, JoyAxis::TRIGGER_RIGHT, axis_correct(joy.state.Gamepad.bRightTrigger, true, true));
-			joy.last_packet = joy.state.dwPacketNumber;
-		}
-		uint64_t timestamp = input->get_joy_vibration_timestamp(joy.id);
-		if (timestamp > joy.ff_timestamp) {
-			Vector2 strength = input->get_joy_vibration_strength(joy.id);
-			float duration = input->get_joy_vibration_duration(joy.id);
-			if (strength.x == 0 && strength.y == 0) {
-				joypad_vibration_stop_xinput(i, timestamp);
-			} else {
-				joypad_vibration_start_xinput(i, strength.x, strength.y, duration, timestamp);
-			}
-		} else if (joy.vibrating && joy.ff_end_timestamp != 0) {
-			uint64_t current_time = OS::get_singleton()->get_ticks_usec();
-			if (current_time >= joy.ff_end_timestamp) {
-				joypad_vibration_stop_xinput(i, current_time);
-			}
-		}
-	}
-
-	// Handle DirectIndput joypads.
-	for (int i = 0; i < JOYPADS_MAX; i++) {
-		dinput_gamepad *joy = &d_joypads[i];
-
-		if (!joy->attached) {
-			continue;
-		}
-
-		DIJOYSTATE2 js;
-		hr = joy->di_joy->Poll();
-		if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED) {
-			IDirectInputDevice8_Acquire(joy->di_joy);
-			joy->di_joy->Poll();
-		}
-
-		hr = joy->di_joy->GetDeviceState(sizeof(DIJOYSTATE2), &js);
-		if (FAILED(hr)) {
-			continue;
-		}
-
-		post_hat(joy->id, js.rgdwPOV[0]);
-
-		for (int j = 0; j < 128; j++) {
-			if (js.rgbButtons[j] & 0x80) {
-				if (!joy->last_buttons[j]) {
-					input->joy_button(joy->id, (JoyButton)j, true);
-					joy->last_buttons[j] = true;
-				}
-			} else {
-				if (joy->last_buttons[j]) {
-					input->joy_button(joy->id, (JoyButton)j, false);
-					joy->last_buttons[j] = false;
-				}
-			}
-		}
-
-		// on mingw, these constants are not constants
-		int count = 8;
-		const LONG axes[] = { DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ, (LONG)DIJOFS_SLIDER(0), (LONG)DIJOFS_SLIDER(1) };
-		int values[] = { js.lX, js.lY, js.lZ, js.lRx, js.lRy, js.lRz, js.rglSlider[0], js.rglSlider[1] };
-
-		for (uint32_t j = 0; j < joy->joy_axis.size(); j++) {
-			for (int k = 0; k < count; k++) {
-				if (joy->joy_axis[j] == axes[k]) {
-					input->joy_axis(joy->id, (JoyAxis)j, axis_correct(values[k]));
-					break;
-				}
-			}
-		}
-	}
-	return;
-}
-
-void JoypadWindows::post_hat(int p_device, DWORD p_dpad) {
-	BitField<HatMask> dpad_val = HatMask::CENTER;
-
-	// Should be -1 when centered, but according to docs:
-	// "Some drivers report the centered position of the POV indicator as 65,535. Determine whether the indicator is centered as follows:
-	//  BOOL POVCentered = (LOWORD(dwPOV) == 0xFFFF);"
-	// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee416628(v%3Dvs.85)#remarks
-	if (LOWORD(p_dpad) == 0xFFFF) {
-		// Do nothing.
-		// dpad_val.set_flag(HatMask::CENTER);
-	}
-	if (p_dpad == 0) {
-		dpad_val.set_flag(HatMask::UP);
-
-	} else if (p_dpad == 4500) {
-		dpad_val.set_flag(HatMask::UP);
-		dpad_val.set_flag(HatMask::RIGHT);
-
-	} else if (p_dpad == 9000) {
-		dpad_val.set_flag(HatMask::RIGHT);
-
-	} else if (p_dpad == 13500) {
-		dpad_val.set_flag(HatMask::RIGHT);
-		dpad_val.set_flag(HatMask::DOWN);
-
-	} else if (p_dpad == 18000) {
-		dpad_val.set_flag(HatMask::DOWN);
-
-	} else if (p_dpad == 22500) {
-		dpad_val.set_flag(HatMask::DOWN);
-		dpad_val.set_flag(HatMask::LEFT);
-
-	} else if (p_dpad == 27000) {
-		dpad_val.set_flag(HatMask::LEFT);
-
-	} else if (p_dpad == 31500) {
-		dpad_val.set_flag(HatMask::LEFT);
-		dpad_val.set_flag(HatMask::UP);
-	}
-	input->joy_hat(p_device, dpad_val);
-}
-
-float JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const {
-	if (Math::abs(p_val) < MIN_JOY_AXIS) {
-		return p_trigger ? -1.0f : 0.0f;
-	}
-	if (!p_xinput) {
-		return p_val / (float)MAX_JOY_AXIS;
-	}
-	if (p_trigger) {
-		// Convert to a value between -1.0f and 1.0f.
-		return 2.0f * p_val / (float)MAX_TRIGGER - 1.0f;
-	}
-	float value;
-	if (p_val < 0) {
-		value = p_val / (float)MAX_JOY_AXIS;
-	} else {
-		value = p_val / (float)(MAX_JOY_AXIS - 1);
-	}
-	if (p_negate) {
-		value = -value;
-	}
-	return value;
-}
-
-void JoypadWindows::joypad_vibration_start_xinput(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) {
-	xinput_gamepad &joy = x_joypads[p_device];
-	if (joy.attached) {
-		XINPUT_VIBRATION effect;
-		effect.wLeftMotorSpeed = (65535 * p_strong_magnitude);
-		effect.wRightMotorSpeed = (65535 * p_weak_magnitude);
-		if (xinput_set_state(p_device, &effect) == ERROR_SUCCESS) {
-			joy.ff_timestamp = p_timestamp;
-			joy.ff_end_timestamp = p_duration == 0 ? 0 : p_timestamp + (uint64_t)(p_duration * 1000000.0);
-			joy.vibrating = true;
-		}
-	}
-}
-
-void JoypadWindows::joypad_vibration_stop_xinput(int p_device, uint64_t p_timestamp) {
-	xinput_gamepad &joy = x_joypads[p_device];
-	if (joy.attached) {
-		XINPUT_VIBRATION effect;
-		effect.wLeftMotorSpeed = 0;
-		effect.wRightMotorSpeed = 0;
-		if (xinput_set_state(p_device, &effect) == ERROR_SUCCESS) {
-			joy.ff_timestamp = p_timestamp;
-			joy.vibrating = false;
-		}
-	}
-}
-
-void JoypadWindows::load_xinput() {
-	xinput_get_state = &_xinput_get_state;
-	xinput_set_state = &_xinput_set_state;
-	winmm_get_joycaps = &_winmm_get_joycaps;
-	bool legacy_xinput = false;
-
-	xinput_dll = LoadLibrary("XInput1_4.dll");
-	if (!xinput_dll) {
-		xinput_dll = LoadLibrary("XInput1_3.dll");
-		if (!xinput_dll) {
-			xinput_dll = LoadLibrary("XInput9_1_0.dll");
-			legacy_xinput = true;
-		}
-	}
-
-	if (!xinput_dll) {
-		print_verbose("Could not find XInput, using DirectInput only");
-		return;
-	}
-
-	// (LPCSTR)100 is the magic number to get XInputGetStateEx, which also provides the state for the guide button
-	LPCSTR get_state_func_name = legacy_xinput ? "XInputGetState" : (LPCSTR)100;
-	XInputGetState_t func = (XInputGetState_t)(void *)GetProcAddress((HMODULE)xinput_dll, get_state_func_name);
-	XInputSetState_t set_func = (XInputSetState_t)(void *)GetProcAddress((HMODULE)xinput_dll, "XInputSetState");
-	if (!func || !set_func) {
-		unload_xinput();
-		return;
-	}
-	xinput_get_state = func;
-	xinput_set_state = set_func;
-
-	winmm_dll = LoadLibrary("Winmm.dll");
-	if (winmm_dll) {
-		joyGetDevCaps_t caps_func = (joyGetDevCaps_t)(void *)GetProcAddress((HMODULE)winmm_dll, "joyGetDevCapsW");
-		if (caps_func) {
-			winmm_get_joycaps = caps_func;
-		} else {
-			unload_winmm();
-		}
-	}
-}
-
-void JoypadWindows::unload_xinput() {
-	if (xinput_dll) {
-		FreeLibrary((HMODULE)xinput_dll);
-	}
-}
-
-void JoypadWindows::unload_winmm() {
-	if (winmm_dll) {
-		FreeLibrary((HMODULE)winmm_dll);
-	}
-}

+ 0 - 149
platform/windows/joypad_windows.h

@@ -1,149 +0,0 @@
-/**************************************************************************/
-/*  joypad_windows.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 "os_windows.h"
-
-#define DIRECTINPUT_VERSION 0x0800
-#include <dinput.h>
-#include <xinput.h>
-
-#include <mmsystem.h>
-
-#ifndef SAFE_RELEASE // when Windows Media Device M? is not present
-#define SAFE_RELEASE(x) \
-	if (x != nullptr) { \
-		x->Release();   \
-		x = nullptr;    \
-	}
-#endif
-
-#ifndef XUSER_MAX_COUNT
-#define XUSER_MAX_COUNT 4
-#endif
-
-class JoypadWindows {
-public:
-	JoypadWindows();
-	JoypadWindows(HWND *hwnd);
-	~JoypadWindows();
-
-	void probe_joypads();
-	void process_joypads();
-
-private:
-	enum {
-		JOYPADS_MAX = 16,
-		JOY_AXIS_COUNT = 6,
-		MIN_JOY_AXIS = 10,
-		MAX_JOY_AXIS = 32768,
-		MAX_JOY_BUTTONS = 128,
-		KEY_EVENT_BUFFER_SIZE = 512,
-		MAX_TRIGGER = 255
-	};
-
-	struct dinput_gamepad {
-		int id;
-		bool attached;
-		bool confirmed;
-		bool last_buttons[MAX_JOY_BUTTONS];
-		DWORD last_pad;
-
-		LPDIRECTINPUTDEVICE8 di_joy;
-		LocalVector<LONG> joy_axis;
-		GUID guid;
-
-		dinput_gamepad() {
-			id = -1;
-			last_pad = -1;
-			attached = false;
-			confirmed = false;
-			di_joy = nullptr;
-			guid = {};
-
-			for (int i = 0; i < MAX_JOY_BUTTONS; i++) {
-				last_buttons[i] = false;
-			}
-		}
-	};
-
-	struct xinput_gamepad {
-		int id = 0;
-		bool attached = false;
-		bool vibrating = false;
-		DWORD last_packet = 0;
-		XINPUT_STATE state;
-		uint64_t ff_timestamp = 0;
-		uint64_t ff_end_timestamp = 0;
-	};
-
-	typedef DWORD(WINAPI *XInputGetState_t)(DWORD dwUserIndex, XINPUT_STATE *pState);
-	typedef DWORD(WINAPI *XInputSetState_t)(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration);
-
-	typedef MMRESULT(WINAPI *joyGetDevCaps_t)(UINT uJoyID, LPJOYCAPSW pjc, UINT cbjc);
-
-	HWND *hWnd = nullptr;
-	HANDLE xinput_dll;
-	HANDLE winmm_dll;
-	LPDIRECTINPUT8 dinput;
-	Input *input = nullptr;
-
-	int id_to_change;
-	int slider_count;
-	int x_joypad_probe_count; // XInput equivalent to dinput_gamepad.confirmed.
-	int d_joypad_count;
-	bool attached_joypads[JOYPADS_MAX];
-	dinput_gamepad d_joypads[JOYPADS_MAX];
-	xinput_gamepad x_joypads[XUSER_MAX_COUNT];
-
-	static BOOL CALLBACK enumCallback(const DIDEVICEINSTANCE *p_instance, void *p_context);
-	static BOOL CALLBACK objectsCallback(const DIDEVICEOBJECTINSTANCE *instance, void *context);
-
-	void setup_d_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_joy_id);
-	void close_d_joypad(int id = -1);
-	void load_xinput();
-	void unload_xinput();
-	void unload_winmm();
-
-	void post_hat(int p_device, DWORD p_dpad);
-
-	bool is_d_joypad_known(const GUID &p_guid);
-	bool is_xinput_joypad(const GUID *p_guid);
-	bool setup_dinput_joypad(const DIDEVICEINSTANCE *instance);
-	void probe_xinput_joypad(const String &name = ""); // Handles connect, disconnect & re-connect for XInput joypads.
-	void joypad_vibration_start_xinput(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
-	void joypad_vibration_stop_xinput(int p_device, uint64_t p_timestamp);
-
-	float axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const;
-	XInputGetState_t xinput_get_state;
-	XInputSetState_t xinput_set_state;
-	joyGetDevCaps_t winmm_get_joycaps; // Only for reading info on XInput joypads.
-};

+ 0 - 1
platform/windows/os_windows.cpp

@@ -31,7 +31,6 @@
 #include "os_windows.h"
 
 #include "display_server_windows.h"
-#include "joypad_windows.h"
 #include "lang_table.h"
 #include "windows_terminal_logger.h"
 #include "windows_utils.h"

+ 8 - 2
platform/windows/os_windows.h

@@ -67,6 +67,14 @@
 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
 #endif
 
+#ifndef SAFE_RELEASE // when Windows Media Device M? is not present
+#define SAFE_RELEASE(x) \
+	if (x != nullptr) { \
+		x->Release();   \
+		x = nullptr;    \
+	}
+#endif
+
 template <typename T>
 class ComAutoreleaseRef {
 public:
@@ -90,8 +98,6 @@ public:
 	}
 };
 
-class JoypadWindows;
-
 class OS_Windows : public OS {
 	uint64_t target_ticks = 0;
 	uint64_t ticks_start = 0;

+ 33 - 0
thirdparty/README.md

@@ -447,6 +447,20 @@ Files extracted from upstream source:
   - Except `main.cc`, `harfbuzz*.cc`, `failing-alloc.c`, `test*.cc`, `hb-wasm*.*`, `wasm/*`
 
 
+## hidapi
+
+- Upstream: https://github.com/libsdl-org/SDL/tree/main/src/hidapi
+- Version: 0.14.0 (8d604353a53853fa56d1bdce0363535605ca868f, 2025)
+- License: BSD-3-Clause
+
+Files extracted from upstream source:
+
+- See `thirdparty/sdl/update-sdl.sh`
+
+The source code of this library is being bundled with SDL's source code files.
+The files of hidapi are stored in `thirdparty/sdl/hidapi/` folder.
+
+
 ## icu4c
 
 - Upstream: https://github.com/unicode-org/icu
@@ -942,6 +956,25 @@ Files extracted from upstream source:
 - Textures generated using the Python scripts in the `Scripts` folder
 
 
+## sdl
+
+- Upstream: https://github.com/libsdl-org/SDL
+- Version: 3.2.14 (8d604353a53853fa56d1bdce0363535605ca868f, 2025)
+- License: Zlib
+
+Files extracted from upstream source:
+
+- See `thirdparty/sdl/update-sdl.sh`
+
+Patches:
+
+- `0001-remove-unnecessary-subsystems.patch` (GH-106218)
+- `0002-msvc-constants-fpstrict.patch` (GH-106218)
+
+The SDL source code folder includes `hidapi` library inside of folder `thirdparty/sdl/hidapi/`.
+Its version and license is described in this file under `hidapi`.
+
+
 ## spirv-cross
 
 - Upstream: https://github.com/KhronosGroup/SPIRV-Cross

+ 38 - 0
thirdparty/sdl/CREDITS.md

@@ -0,0 +1,38 @@
+# Simple DirectMedia Layer CREDITS
+
+Thanks to everyone who made this possible, including:
+
+- Cliff Matthews, for giving me a reason to start this project. :)  -- Executor rocks!  *grin*
+- Ryan Gordon for helping everybody out and keeping the dream alive. :)
+- Frank Praznik for his Wayland support and general windowing development.
+- Ozkan Sezer for sanity checks and make sure the i's are dotted and t's are crossed.
+- Anonymous Maarten for CMake support and build system development.
+- Evan Hemsley, Caleb Cornett, and Ethan Lee for SDL GPU development.
+- Gabriel Jacobo for his work on the Android port and generally helping out all around.
+- Philipp Wiesemann for his attention to detail reviewing the entire SDL code base and proposes patches.
+- Andreas Schiffler for his dedication to unit tests, Visual Studio projects, and managing the Google Summer of Code.
+- Mike Sartain for incorporating SDL into Team Fortress 2 and cheering me on at Valve.
+- Alfred Reynolds for the game controller API and general (in)sanity
+- Jørgen Tjernø¸ for numerous magical macOS fixes.
+- Pierre-Loup Griffais for his deep knowledge of OpenGL drivers.
+- Julian Winter for the SDL 2.0 website.
+- Sheena Smith for many months of great work on the SDL wiki creating the API documentation and style guides.
+- Paul Hunkin for his port of SDL to Android during the Google Summer of Code 2010.
+- Eli Gottlieb for his work on shaped windows during the Google Summer of Code 2010.
+- Jim Grandpre for his work on multi-touch and gesture recognition during
+  the Google Summer of Code 2010.
+- Edgar "bobbens" Simo for his force feedback API development during the
+  Google Summer of Code 2008.
+- Aaron Wishnick for his work on audio resampling and pitch shifting during
+  the Google Summer of Code 2008.
+- Holmes Futrell for his port of SDL to the iPhone and iPod Touch during the
+  Google Summer of Code 2008.
+- Jon Atkins for SDL_image, SDL_mixer and SDL_net documentation.
+- Everybody at Loki Software, Inc. and Valve Corporation for their great contributions!
+
+ And a big hand to everyone else who has contributed over the years.
+
+THANKS! :)
+
+    -- Sam Lantinga <[email protected]>
+

+ 18 - 0
thirdparty/sdl/LICENSE.txt

@@ -0,0 +1,18 @@
+Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+  
+This software is provided 'as-is', without any express or implied
+warranty.  In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+  
+1. The origin of this software must not be misrepresented; you must not
+   claim that you wrote the original software. If you use this software
+   in a product, an acknowledgment in the product documentation would be
+   appreciated but is not required. 
+2. Altered source versions must be plainly marked as such, and must not be
+   misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+

+ 841 - 0
thirdparty/sdl/SDL.c

@@ -0,0 +1,841 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+#include "SDL3/SDL_revision.h"
+
+#if defined(SDL_PLATFORM_WINDOWS)
+#include "core/windows/SDL_windows.h"
+#else
+#include <unistd.h> // _exit(), etc.
+#endif
+
+// this checks for HAVE_DBUS_DBUS_H internally.
+#include "core/linux/SDL_dbus.h"
+
+#ifdef SDL_PLATFORM_EMSCRIPTEN
+#include <emscripten.h>
+#endif
+
+// Initialization code for SDL
+
+#include "SDL_assert_c.h"
+#include "SDL_hints_c.h"
+#include "SDL_log_c.h"
+#include "SDL_properties_c.h"
+//#include "audio/SDL_sysaudio.h"
+#include "events/SDL_events_c.h"
+#include "haptic/SDL_haptic_c.h"
+#include "joystick/SDL_gamepad_c.h"
+#include "joystick/SDL_joystick_c.h"
+#include "sensor/SDL_sensor_c.h"
+#include "stdlib/SDL_getenv_c.h"
+#include "thread/SDL_thread_c.h"
+#ifdef SDL_PLATFORM_ANDROID
+#include "core/android/SDL_android.h"
+#endif
+
+#define SDL_INIT_EVERYTHING ~0U
+
+// Initialization/Cleanup routines
+#include "timer/SDL_timer_c.h"
+#ifdef SDL_VIDEO_DRIVER_WINDOWS
+extern bool SDL_HelperWindowCreate(void);
+extern void SDL_HelperWindowDestroy(void);
+#endif
+
+#ifdef SDL_BUILD_MAJOR_VERSION
+SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MAJOR_VERSION,
+                        SDL_MAJOR_VERSION == SDL_BUILD_MAJOR_VERSION);
+SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MINOR_VERSION,
+                        SDL_MINOR_VERSION == SDL_BUILD_MINOR_VERSION);
+SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MICRO_VERSION,
+                        SDL_MICRO_VERSION == SDL_BUILD_MICRO_VERSION);
+#endif
+
+// Limited by its encoding in SDL_VERSIONNUM
+SDL_COMPILE_TIME_ASSERT(SDL_MAJOR_VERSION_min, SDL_MAJOR_VERSION >= 0);
+SDL_COMPILE_TIME_ASSERT(SDL_MAJOR_VERSION_max, SDL_MAJOR_VERSION <= 10);
+SDL_COMPILE_TIME_ASSERT(SDL_MINOR_VERSION_min, SDL_MINOR_VERSION >= 0);
+SDL_COMPILE_TIME_ASSERT(SDL_MINOR_VERSION_max, SDL_MINOR_VERSION <= 999);
+SDL_COMPILE_TIME_ASSERT(SDL_MICRO_VERSION_min, SDL_MICRO_VERSION >= 0);
+SDL_COMPILE_TIME_ASSERT(SDL_MICRO_VERSION_max, SDL_MICRO_VERSION <= 999);
+
+/* This is not declared in any header, although it is shared between some
+    parts of SDL, because we don't want anything calling it without an
+    extremely good reason. */
+extern SDL_NORETURN void SDL_ExitProcess(int exitcode);
+SDL_NORETURN void SDL_ExitProcess(int exitcode)
+{
+#if defined(SDL_PLATFORM_WINDOWS)
+    /* "if you do not know the state of all threads in your process, it is
+       better to call TerminateProcess than ExitProcess"
+       https://msdn.microsoft.com/en-us/library/windows/desktop/ms682658(v=vs.85).aspx */
+    TerminateProcess(GetCurrentProcess(), exitcode);
+    /* MingW doesn't have TerminateProcess marked as noreturn, so add an
+       ExitProcess here that will never be reached but make MingW happy. */
+    ExitProcess(exitcode);
+#elif defined(SDL_PLATFORM_EMSCRIPTEN)
+    emscripten_cancel_main_loop();   // this should "kill" the app.
+    emscripten_force_exit(exitcode); // this should "kill" the app.
+    exit(exitcode);
+#elif defined(SDL_PLATFORM_HAIKU)  // Haiku has _Exit, but it's not marked noreturn.
+    _exit(exitcode);
+#elif defined(HAVE__EXIT) // Upper case _Exit()
+    _Exit(exitcode);
+#else
+    _exit(exitcode);
+#endif
+}
+
+// App metadata
+
+bool SDL_SetAppMetadata(const char *appname, const char *appversion, const char *appidentifier)
+{
+    SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING, appname);
+    SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_VERSION_STRING, appversion);
+    SDL_SetAppMetadataProperty(SDL_PROP_APP_METADATA_IDENTIFIER_STRING, appidentifier);
+    return true;
+}
+
+static bool SDL_ValidMetadataProperty(const char *name)
+{
+    if (!name || !*name) {
+        return false;
+    }
+
+    if (SDL_strcmp(name, SDL_PROP_APP_METADATA_NAME_STRING) == 0 ||
+        SDL_strcmp(name, SDL_PROP_APP_METADATA_VERSION_STRING) == 0 ||
+        SDL_strcmp(name, SDL_PROP_APP_METADATA_IDENTIFIER_STRING) == 0 ||
+        SDL_strcmp(name, SDL_PROP_APP_METADATA_CREATOR_STRING) == 0 ||
+        SDL_strcmp(name, SDL_PROP_APP_METADATA_COPYRIGHT_STRING) == 0 ||
+        SDL_strcmp(name, SDL_PROP_APP_METADATA_URL_STRING) == 0 ||
+        SDL_strcmp(name, SDL_PROP_APP_METADATA_TYPE_STRING) == 0) {
+        return true;
+    }
+    return false;
+}
+
+bool SDL_SetAppMetadataProperty(const char *name, const char *value)
+{
+    if (!SDL_ValidMetadataProperty(name)) {
+        return SDL_InvalidParamError("name");
+    }
+
+    return SDL_SetStringProperty(SDL_GetGlobalProperties(), name, value);
+}
+
+const char *SDL_GetAppMetadataProperty(const char *name)
+{
+    if (!SDL_ValidMetadataProperty(name)) {
+        SDL_InvalidParamError("name");
+        return NULL;
+    }
+
+    const char *value = NULL;
+    if (SDL_strcmp(name, SDL_PROP_APP_METADATA_NAME_STRING) == 0) {
+        value = SDL_GetHint(SDL_HINT_APP_NAME);
+    } else if (SDL_strcmp(name, SDL_PROP_APP_METADATA_IDENTIFIER_STRING) == 0) {
+        value = SDL_GetHint(SDL_HINT_APP_ID);
+    }
+    if (!value || !*value) {
+        value = SDL_GetStringProperty(SDL_GetGlobalProperties(), name, NULL);
+    }
+    if (!value || !*value) {
+        if (SDL_strcmp(name, SDL_PROP_APP_METADATA_NAME_STRING) == 0) {
+            value = "SDL Application";
+        } else if (SDL_strcmp(name, SDL_PROP_APP_METADATA_TYPE_STRING) == 0) {
+            value = "application";
+        }
+    }
+    return value;
+}
+
+
+// The initialized subsystems
+#ifdef SDL_MAIN_NEEDED
+static bool SDL_MainIsReady = false;
+#else
+static bool SDL_MainIsReady = true;
+#endif
+static SDL_ThreadID SDL_MainThreadID = 0;
+static bool SDL_bInMainQuit = false;
+static Uint8 SDL_SubsystemRefCount[32];
+
+// Private helper to increment a subsystem's ref counter.
+static void SDL_IncrementSubsystemRefCount(Uint32 subsystem)
+{
+    const int subsystem_index = SDL_MostSignificantBitIndex32(subsystem);
+    SDL_assert((subsystem_index < 0) || (SDL_SubsystemRefCount[subsystem_index] < 255));
+    if (subsystem_index >= 0) {
+        ++SDL_SubsystemRefCount[subsystem_index];
+    }
+}
+
+// Private helper to decrement a subsystem's ref counter.
+static void SDL_DecrementSubsystemRefCount(Uint32 subsystem)
+{
+    const int subsystem_index = SDL_MostSignificantBitIndex32(subsystem);
+    if ((subsystem_index >= 0) && (SDL_SubsystemRefCount[subsystem_index] > 0)) {
+        if (SDL_bInMainQuit) {
+            SDL_SubsystemRefCount[subsystem_index] = 0;
+        } else {
+            --SDL_SubsystemRefCount[subsystem_index];
+        }
+    }
+}
+
+// Private helper to check if a system needs init.
+static bool SDL_ShouldInitSubsystem(Uint32 subsystem)
+{
+    const int subsystem_index = SDL_MostSignificantBitIndex32(subsystem);
+    SDL_assert((subsystem_index < 0) || (SDL_SubsystemRefCount[subsystem_index] < 255));
+    return ((subsystem_index >= 0) && (SDL_SubsystemRefCount[subsystem_index] == 0));
+}
+
+// Private helper to check if a system needs to be quit.
+static bool SDL_ShouldQuitSubsystem(Uint32 subsystem)
+{
+    const int subsystem_index = SDL_MostSignificantBitIndex32(subsystem);
+    if ((subsystem_index >= 0) && (SDL_SubsystemRefCount[subsystem_index] == 0)) {
+        return false;
+    }
+
+    /* If we're in SDL_Quit, we shut down every subsystem, even if refcount
+     * isn't zero.
+     */
+    return (((subsystem_index >= 0) && (SDL_SubsystemRefCount[subsystem_index] == 1)) || SDL_bInMainQuit);
+}
+
+/* Private helper to either increment's existing ref counter,
+ * or fully init a new subsystem. */
+static bool SDL_InitOrIncrementSubsystem(Uint32 subsystem)
+{
+    int subsystem_index = SDL_MostSignificantBitIndex32(subsystem);
+    SDL_assert((subsystem_index < 0) || (SDL_SubsystemRefCount[subsystem_index] < 255));
+    if (subsystem_index < 0) {
+        return false;
+    }
+    if (SDL_SubsystemRefCount[subsystem_index] > 0) {
+        ++SDL_SubsystemRefCount[subsystem_index];
+        return true;
+    }
+    return SDL_InitSubSystem(subsystem);
+}
+
+void SDL_SetMainReady(void)
+{
+    SDL_MainIsReady = true;
+
+    if (SDL_MainThreadID == 0) {
+        SDL_MainThreadID = SDL_GetCurrentThreadID();
+    }
+}
+
+bool SDL_IsMainThread(void)
+{
+    if (SDL_MainThreadID == 0) {
+        // Not initialized yet?
+        return true;
+    }
+    if (SDL_MainThreadID == SDL_GetCurrentThreadID()) {
+        return true;
+    }
+    return false;
+}
+
+// Initialize all the subsystems that require initialization before threads start
+void SDL_InitMainThread(void)
+{
+    static bool done_info = false;
+
+    SDL_InitTLSData();
+    SDL_InitEnvironment();
+    SDL_InitTicks();
+    //SDL_InitFilesystem();
+
+    if (!done_info) {
+        const char *value;
+
+        value = SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING);
+        SDL_LogInfo(SDL_LOG_CATEGORY_SYSTEM, "App name: %s", value ? value : "<unspecified>");
+        value = SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_VERSION_STRING);
+        SDL_LogInfo(SDL_LOG_CATEGORY_SYSTEM, "App version: %s", value ? value : "<unspecified>");
+        value = SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_IDENTIFIER_STRING);
+        SDL_LogInfo(SDL_LOG_CATEGORY_SYSTEM, "App ID: %s", value ? value : "<unspecified>");
+        SDL_LogInfo(SDL_LOG_CATEGORY_SYSTEM, "SDL revision: %s", SDL_REVISION);
+
+        done_info = true;
+    }
+}
+
+static void SDL_QuitMainThread(void)
+{
+    //SDL_QuitFilesystem();
+    SDL_QuitTicks();
+    SDL_QuitEnvironment();
+    SDL_QuitTLSData();
+}
+
+bool SDL_InitSubSystem(SDL_InitFlags flags)
+{
+    Uint32 flags_initialized = 0;
+
+    if (!SDL_MainIsReady) {
+        return SDL_SetError("Application didn't initialize properly, did you include SDL_main.h in the file containing your main() function?");
+    }
+
+    SDL_InitMainThread();
+
+#ifdef SDL_USE_LIBDBUS
+    SDL_DBus_Init();
+#endif
+
+#ifdef SDL_VIDEO_DRIVER_WINDOWS
+    if (flags & (SDL_INIT_HAPTIC | SDL_INIT_JOYSTICK)) {
+        if (!SDL_HelperWindowCreate()) {
+            goto quit_and_error;
+        }
+    }
+#endif
+
+    // Initialize the event subsystem
+    if (flags & SDL_INIT_EVENTS) {
+        if (SDL_ShouldInitSubsystem(SDL_INIT_EVENTS)) {
+            SDL_IncrementSubsystemRefCount(SDL_INIT_EVENTS);
+            if (!SDL_InitEvents()) {
+                SDL_DecrementSubsystemRefCount(SDL_INIT_EVENTS);
+                goto quit_and_error;
+            }
+        } else {
+            SDL_IncrementSubsystemRefCount(SDL_INIT_EVENTS);
+        }
+        flags_initialized |= SDL_INIT_EVENTS;
+    }
+
+    // Initialize the video subsystem
+    if (flags & SDL_INIT_VIDEO) {
+#ifndef SDL_VIDEO_DISABLED
+        if (SDL_ShouldInitSubsystem(SDL_INIT_VIDEO)) {
+            // video implies events
+            if (!SDL_InitOrIncrementSubsystem(SDL_INIT_EVENTS)) {
+                goto quit_and_error;
+            }
+
+            // We initialize video on the main thread
+            // On Apple platforms this is a requirement.
+            // On other platforms, this is the definition.
+            SDL_MainThreadID = SDL_GetCurrentThreadID();
+
+            SDL_IncrementSubsystemRefCount(SDL_INIT_VIDEO);
+            if (!SDL_VideoInit(NULL)) {
+                SDL_DecrementSubsystemRefCount(SDL_INIT_VIDEO);
+                SDL_PushError();
+                SDL_QuitSubSystem(SDL_INIT_EVENTS);
+                SDL_PopError();
+                goto quit_and_error;
+            }
+        } else {
+            SDL_IncrementSubsystemRefCount(SDL_INIT_VIDEO);
+        }
+        flags_initialized |= SDL_INIT_VIDEO;
+#else
+        SDL_SetError("SDL not built with video support");
+        goto quit_and_error;
+#endif
+    }
+
+    // Initialize the audio subsystem
+    if (flags & SDL_INIT_AUDIO) {
+#ifndef SDL_AUDIO_DISABLED
+        if (SDL_ShouldInitSubsystem(SDL_INIT_AUDIO)) {
+            // audio implies events
+            if (!SDL_InitOrIncrementSubsystem(SDL_INIT_EVENTS)) {
+                goto quit_and_error;
+            }
+
+            SDL_IncrementSubsystemRefCount(SDL_INIT_AUDIO);
+            if (!SDL_InitAudio(NULL)) {
+                SDL_DecrementSubsystemRefCount(SDL_INIT_AUDIO);
+                SDL_PushError();
+                SDL_QuitSubSystem(SDL_INIT_EVENTS);
+                SDL_PopError();
+                goto quit_and_error;
+            }
+        } else {
+            SDL_IncrementSubsystemRefCount(SDL_INIT_AUDIO);
+        }
+        flags_initialized |= SDL_INIT_AUDIO;
+#else
+        SDL_SetError("SDL not built with audio support");
+        goto quit_and_error;
+#endif
+    }
+
+    // Initialize the joystick subsystem
+    if (flags & SDL_INIT_JOYSTICK) {
+#ifndef SDL_JOYSTICK_DISABLED
+        if (SDL_ShouldInitSubsystem(SDL_INIT_JOYSTICK)) {
+            // joystick implies events
+            if (!SDL_InitOrIncrementSubsystem(SDL_INIT_EVENTS)) {
+                goto quit_and_error;
+            }
+
+            SDL_IncrementSubsystemRefCount(SDL_INIT_JOYSTICK);
+            if (!SDL_InitJoysticks()) {
+                SDL_DecrementSubsystemRefCount(SDL_INIT_JOYSTICK);
+                SDL_PushError();
+                SDL_QuitSubSystem(SDL_INIT_EVENTS);
+                SDL_PopError();
+                goto quit_and_error;
+            }
+        } else {
+            SDL_IncrementSubsystemRefCount(SDL_INIT_JOYSTICK);
+        }
+        flags_initialized |= SDL_INIT_JOYSTICK;
+#else
+        SDL_SetError("SDL not built with joystick support");
+        goto quit_and_error;
+#endif
+    }
+
+    if (flags & SDL_INIT_GAMEPAD) {
+#ifndef SDL_JOYSTICK_DISABLED
+        if (SDL_ShouldInitSubsystem(SDL_INIT_GAMEPAD)) {
+            // game controller implies joystick
+            if (!SDL_InitOrIncrementSubsystem(SDL_INIT_JOYSTICK)) {
+                goto quit_and_error;
+            }
+
+            SDL_IncrementSubsystemRefCount(SDL_INIT_GAMEPAD);
+            if (!SDL_InitGamepads()) {
+                SDL_DecrementSubsystemRefCount(SDL_INIT_GAMEPAD);
+                SDL_PushError();
+                SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
+                SDL_PopError();
+                goto quit_and_error;
+            }
+        } else {
+            SDL_IncrementSubsystemRefCount(SDL_INIT_GAMEPAD);
+        }
+        flags_initialized |= SDL_INIT_GAMEPAD;
+#else
+        SDL_SetError("SDL not built with joystick support");
+        goto quit_and_error;
+#endif
+    }
+
+    // Initialize the haptic subsystem
+    if (flags & SDL_INIT_HAPTIC) {
+#ifndef SDL_HAPTIC_DISABLED
+        if (SDL_ShouldInitSubsystem(SDL_INIT_HAPTIC)) {
+            SDL_IncrementSubsystemRefCount(SDL_INIT_HAPTIC);
+            if (!SDL_InitHaptics()) {
+                SDL_DecrementSubsystemRefCount(SDL_INIT_HAPTIC);
+                goto quit_and_error;
+            }
+        } else {
+            SDL_IncrementSubsystemRefCount(SDL_INIT_HAPTIC);
+        }
+        flags_initialized |= SDL_INIT_HAPTIC;
+#else
+        SDL_SetError("SDL not built with haptic (force feedback) support");
+        goto quit_and_error;
+#endif
+    }
+
+    // Initialize the sensor subsystem
+    if (flags & SDL_INIT_SENSOR) {
+#ifndef SDL_SENSOR_DISABLED
+        if (SDL_ShouldInitSubsystem(SDL_INIT_SENSOR)) {
+            SDL_IncrementSubsystemRefCount(SDL_INIT_SENSOR);
+            if (!SDL_InitSensors()) {
+                SDL_DecrementSubsystemRefCount(SDL_INIT_SENSOR);
+                goto quit_and_error;
+            }
+        } else {
+            SDL_IncrementSubsystemRefCount(SDL_INIT_SENSOR);
+        }
+        flags_initialized |= SDL_INIT_SENSOR;
+#else
+        SDL_SetError("SDL not built with sensor support");
+        goto quit_and_error;
+#endif
+    }
+
+    // Initialize the camera subsystem
+    if (flags & SDL_INIT_CAMERA) {
+#ifndef SDL_CAMERA_DISABLED
+        if (SDL_ShouldInitSubsystem(SDL_INIT_CAMERA)) {
+            // camera implies events
+            if (!SDL_InitOrIncrementSubsystem(SDL_INIT_EVENTS)) {
+                goto quit_and_error;
+            }
+
+            SDL_IncrementSubsystemRefCount(SDL_INIT_CAMERA);
+            if (!SDL_CameraInit(NULL)) {
+                SDL_DecrementSubsystemRefCount(SDL_INIT_CAMERA);
+                SDL_PushError();
+                SDL_QuitSubSystem(SDL_INIT_EVENTS);
+                SDL_PopError();
+                goto quit_and_error;
+            }
+        } else {
+            SDL_IncrementSubsystemRefCount(SDL_INIT_CAMERA);
+        }
+        flags_initialized |= SDL_INIT_CAMERA;
+#else
+        SDL_SetError("SDL not built with camera support");
+        goto quit_and_error;
+#endif
+    }
+
+    (void)flags_initialized; // make static analysis happy, since this only gets used in error cases.
+
+    return SDL_ClearError();
+
+quit_and_error:
+    {
+        SDL_PushError();
+        SDL_QuitSubSystem(flags_initialized);
+        SDL_PopError();
+    }
+    return false;
+}
+
+bool SDL_Init(SDL_InitFlags flags)
+{
+    return SDL_InitSubSystem(flags);
+}
+
+void SDL_QuitSubSystem(SDL_InitFlags flags)
+{
+    // Shut down requested initialized subsystems
+
+#ifndef SDL_CAMERA_DISABLED
+    if (flags & SDL_INIT_CAMERA) {
+        if (SDL_ShouldQuitSubsystem(SDL_INIT_CAMERA)) {
+            SDL_QuitCamera();
+            // camera implies events
+            SDL_QuitSubSystem(SDL_INIT_EVENTS);
+        }
+        SDL_DecrementSubsystemRefCount(SDL_INIT_CAMERA);
+    }
+#endif
+
+#ifndef SDL_SENSOR_DISABLED
+    if (flags & SDL_INIT_SENSOR) {
+        if (SDL_ShouldQuitSubsystem(SDL_INIT_SENSOR)) {
+            SDL_QuitSensors();
+        }
+        SDL_DecrementSubsystemRefCount(SDL_INIT_SENSOR);
+    }
+#endif
+
+#ifndef SDL_JOYSTICK_DISABLED
+    if (flags & SDL_INIT_GAMEPAD) {
+        if (SDL_ShouldQuitSubsystem(SDL_INIT_GAMEPAD)) {
+            SDL_QuitGamepads();
+            // game controller implies joystick
+            SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
+        }
+        SDL_DecrementSubsystemRefCount(SDL_INIT_GAMEPAD);
+    }
+
+    if (flags & SDL_INIT_JOYSTICK) {
+        if (SDL_ShouldQuitSubsystem(SDL_INIT_JOYSTICK)) {
+            SDL_QuitJoysticks();
+            // joystick implies events
+            SDL_QuitSubSystem(SDL_INIT_EVENTS);
+        }
+        SDL_DecrementSubsystemRefCount(SDL_INIT_JOYSTICK);
+    }
+#endif
+
+#ifndef SDL_HAPTIC_DISABLED
+    if (flags & SDL_INIT_HAPTIC) {
+        if (SDL_ShouldQuitSubsystem(SDL_INIT_HAPTIC)) {
+            SDL_QuitHaptics();
+        }
+        SDL_DecrementSubsystemRefCount(SDL_INIT_HAPTIC);
+    }
+#endif
+
+#ifndef SDL_AUDIO_DISABLED
+    if (flags & SDL_INIT_AUDIO) {
+        if (SDL_ShouldQuitSubsystem(SDL_INIT_AUDIO)) {
+            SDL_QuitAudio();
+            // audio implies events
+            SDL_QuitSubSystem(SDL_INIT_EVENTS);
+        }
+        SDL_DecrementSubsystemRefCount(SDL_INIT_AUDIO);
+    }
+#endif
+
+#ifndef SDL_VIDEO_DISABLED
+    if (flags & SDL_INIT_VIDEO) {
+        if (SDL_ShouldQuitSubsystem(SDL_INIT_VIDEO)) {
+            SDL_QuitRender();
+            SDL_VideoQuit();
+            // video implies events
+            SDL_QuitSubSystem(SDL_INIT_EVENTS);
+        }
+        SDL_DecrementSubsystemRefCount(SDL_INIT_VIDEO);
+    }
+#endif
+
+    if (flags & SDL_INIT_EVENTS) {
+        if (SDL_ShouldQuitSubsystem(SDL_INIT_EVENTS)) {
+            SDL_QuitEvents();
+        }
+        SDL_DecrementSubsystemRefCount(SDL_INIT_EVENTS);
+    }
+}
+
+Uint32 SDL_WasInit(SDL_InitFlags flags)
+{
+    int i;
+    int num_subsystems = SDL_arraysize(SDL_SubsystemRefCount);
+    Uint32 initialized = 0;
+
+    // Fast path for checking one flag
+    if (SDL_HasExactlyOneBitSet32(flags)) {
+        int subsystem_index = SDL_MostSignificantBitIndex32(flags);
+        return SDL_SubsystemRefCount[subsystem_index] ? flags : 0;
+    }
+
+    if (!flags) {
+        flags = SDL_INIT_EVERYTHING;
+    }
+
+    num_subsystems = SDL_min(num_subsystems, SDL_MostSignificantBitIndex32(flags) + 1);
+
+    // Iterate over each bit in flags, and check the matching subsystem.
+    for (i = 0; i < num_subsystems; ++i) {
+        if ((flags & 1) && SDL_SubsystemRefCount[i] > 0) {
+            initialized |= (1 << i);
+        }
+
+        flags >>= 1;
+    }
+
+    return initialized;
+}
+
+void SDL_Quit(void)
+{
+    SDL_bInMainQuit = true;
+
+    // Quit all subsystems
+#ifdef SDL_VIDEO_DRIVER_WINDOWS
+    SDL_HelperWindowDestroy();
+#endif
+    SDL_QuitSubSystem(SDL_INIT_EVERYTHING);
+    //SDL_CleanupTrays();
+
+#ifdef SDL_USE_LIBDBUS
+    SDL_DBus_Quit();
+#endif
+
+    SDL_QuitTimers();
+    //SDL_QuitAsyncIO();
+
+    SDL_SetObjectsInvalid();
+    SDL_AssertionsQuit();
+
+    //SDL_QuitPixelFormatDetails();
+
+    //SDL_QuitCPUInfo();
+
+    /* Now that every subsystem has been quit, we reset the subsystem refcount
+     * and the list of initialized subsystems.
+     */
+    SDL_memset(SDL_SubsystemRefCount, 0x0, sizeof(SDL_SubsystemRefCount));
+
+    SDL_QuitLog();
+    SDL_QuitHints();
+    SDL_QuitProperties();
+
+    SDL_QuitMainThread();
+
+    SDL_bInMainQuit = false;
+}
+
+// Get the library version number
+int SDL_GetVersion(void)
+{
+    return SDL_VERSION;
+}
+
+// Get the library source revision
+const char *SDL_GetRevision(void)
+{
+    return SDL_REVISION;
+}
+
+// Get the name of the platform
+const char *SDL_GetPlatform(void)
+{
+#if defined(SDL_PLATFORM_PRIVATE)
+    return SDL_PLATFORM_PRIVATE_NAME;
+#elif defined(SDL_PLATFORM_AIX)
+    return "AIX";
+#elif defined(SDL_PLATFORM_ANDROID)
+    return "Android";
+#elif defined(SDL_PLATFORM_BSDI)
+    return "BSDI";
+#elif defined(SDL_PLATFORM_EMSCRIPTEN)
+    return "Emscripten";
+#elif defined(SDL_PLATFORM_FREEBSD)
+    return "FreeBSD";
+#elif defined(SDL_PLATFORM_HAIKU)
+    return "Haiku";
+#elif defined(SDL_PLATFORM_HPUX)
+    return "HP-UX";
+#elif defined(SDL_PLATFORM_IRIX)
+    return "Irix";
+#elif defined(SDL_PLATFORM_LINUX)
+    return "Linux";
+#elif defined(__MINT__)
+    return "Atari MiNT";
+#elif defined(SDL_PLATFORM_MACOS)
+    return "macOS";
+#elif defined(SDL_PLATFORM_NETBSD)
+    return "NetBSD";
+#elif defined(SDL_PLATFORM_OPENBSD)
+    return "OpenBSD";
+#elif defined(SDL_PLATFORM_OS2)
+    return "OS/2";
+#elif defined(SDL_PLATFORM_OSF)
+    return "OSF/1";
+#elif defined(SDL_PLATFORM_QNXNTO)
+    return "QNX Neutrino";
+#elif defined(SDL_PLATFORM_RISCOS)
+    return "RISC OS";
+#elif defined(SDL_PLATFORM_SOLARIS)
+    return "Solaris";
+#elif defined(SDL_PLATFORM_WIN32)
+    return "Windows";
+#elif defined(SDL_PLATFORM_WINGDK)
+    return "WinGDK";
+#elif defined(SDL_PLATFORM_XBOXONE)
+    return "Xbox One";
+#elif defined(SDL_PLATFORM_XBOXSERIES)
+    return "Xbox Series X|S";
+#elif defined(SDL_PLATFORM_IOS)
+    return "iOS";
+#elif defined(SDL_PLATFORM_TVOS)
+    return "tvOS";
+#elif defined(SDL_PLATFORM_PS2)
+    return "PlayStation 2";
+#elif defined(SDL_PLATFORM_PSP)
+    return "PlayStation Portable";
+#elif defined(SDL_PLATFORM_VITA)
+    return "PlayStation Vita";
+#elif defined(SDL_PLATFORM_3DS)
+    return "Nintendo 3DS";
+#elif defined(__managarm__)
+    return "Managarm";
+#else
+    return "Unknown (see SDL_platform.h)";
+#endif
+}
+
+bool SDL_IsTablet(void)
+{
+#ifdef SDL_PLATFORM_ANDROID
+    return SDL_IsAndroidTablet();
+#elif defined(SDL_PLATFORM_IOS)
+    extern bool SDL_IsIPad(void);
+    return SDL_IsIPad();
+#else
+    return false;
+#endif
+}
+
+bool SDL_IsTV(void)
+{
+#ifdef SDL_PLATFORM_ANDROID
+    return SDL_IsAndroidTV();
+#elif defined(SDL_PLATFORM_IOS)
+    extern bool SDL_IsAppleTV(void);
+    return SDL_IsAppleTV();
+#else
+    return false;
+#endif
+}
+
+static SDL_Sandbox SDL_DetectSandbox(void)
+{
+#if defined(SDL_PLATFORM_LINUX)
+    if (access("/.flatpak-info", F_OK) == 0) {
+        return SDL_SANDBOX_FLATPAK;
+    }
+
+    /* For Snap, we check multiple variables because they might be set for
+     * unrelated reasons. This is the same thing WebKitGTK does. */
+    if (SDL_getenv("SNAP") && SDL_getenv("SNAP_NAME") && SDL_getenv("SNAP_REVISION")) {
+        return SDL_SANDBOX_SNAP;
+    }
+
+    if (access("/run/host/container-manager", F_OK) == 0) {
+        return SDL_SANDBOX_UNKNOWN_CONTAINER;
+    }
+
+#elif defined(SDL_PLATFORM_MACOS)
+    if (SDL_getenv("APP_SANDBOX_CONTAINER_ID")) {
+        return SDL_SANDBOX_MACOS;
+    }
+#endif
+
+    return SDL_SANDBOX_NONE;
+}
+
+SDL_Sandbox SDL_GetSandbox(void)
+{
+    static SDL_Sandbox sandbox;
+    static bool sandbox_initialized;
+
+    if (!sandbox_initialized) {
+        sandbox = SDL_DetectSandbox();
+        sandbox_initialized = true;
+    }
+    return sandbox;
+}
+
+#ifdef SDL_PLATFORM_WIN32
+
+#if (!defined(HAVE_LIBC) || defined(__WATCOMC__)) && !defined(SDL_STATIC_LIB)
+// FIXME: Still need to include DllMain() on Watcom C ?
+
+BOOL APIENTRY MINGW32_FORCEALIGN _DllMainCRTStartup(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
+{
+    switch (ul_reason_for_call) {
+    case DLL_PROCESS_ATTACH:
+    case DLL_THREAD_ATTACH:
+    case DLL_THREAD_DETACH:
+    case DLL_PROCESS_DETACH:
+        break;
+    }
+    return TRUE;
+}
+#endif // Building DLL
+
+#endif // defined(SDL_PLATFORM_WIN32)

+ 443 - 0
thirdparty/sdl/SDL_assert.c

@@ -0,0 +1,443 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#if defined(SDL_PLATFORM_WINDOWS)
+#include "core/windows/SDL_windows.h"
+#endif
+
+#include "SDL_assert_c.h"
+//#include "video/SDL_sysvideo.h"
+
+#if defined(SDL_PLATFORM_WINDOWS)
+#ifndef WS_OVERLAPPEDWINDOW
+#define WS_OVERLAPPEDWINDOW 0
+#endif
+#endif
+
+#ifdef SDL_PLATFORM_EMSCRIPTEN
+#include <emscripten.h>
+#endif
+
+// The size of the stack buffer to use for rendering assert messages.
+#define SDL_MAX_ASSERT_MESSAGE_STACK 256
+
+static SDL_AssertState SDLCALL SDL_PromptAssertion(const SDL_AssertData *data, void *userdata);
+
+/*
+ * We keep all triggered assertions in a singly-linked list so we can
+ *  generate a report later.
+ */
+static SDL_AssertData *triggered_assertions = NULL;
+
+#ifndef SDL_THREADS_DISABLED
+static SDL_Mutex *assertion_mutex = NULL;
+#endif
+
+static SDL_AssertionHandler assertion_handler = SDL_PromptAssertion;
+static void *assertion_userdata = NULL;
+
+#ifdef __GNUC__
+static void debug_print(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+#endif
+
+static void debug_print(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    SDL_LogMessageV(SDL_LOG_CATEGORY_ASSERT, SDL_LOG_PRIORITY_WARN, fmt, ap);
+    va_end(ap);
+}
+
+static void SDL_AddAssertionToReport(SDL_AssertData *data)
+{
+    /* (data) is always a static struct defined with the assert macros, so
+       we don't have to worry about copying or allocating them. */
+    data->trigger_count++;
+    if (data->trigger_count == 1) { // not yet added?
+        data->next = triggered_assertions;
+        triggered_assertions = data;
+    }
+}
+
+#if defined(SDL_PLATFORM_WINDOWS)
+#define ENDLINE "\r\n"
+#else
+#define ENDLINE "\n"
+#endif
+
+static int SDL_RenderAssertMessage(char *buf, size_t buf_len, const SDL_AssertData *data)
+{
+    return SDL_snprintf(buf, buf_len,
+                        "Assertion failure at %s (%s:%d), triggered %u %s:" ENDLINE "  '%s'",
+                        data->function, data->filename, data->linenum,
+                        data->trigger_count, (data->trigger_count == 1) ? "time" : "times",
+                        data->condition);
+}
+
+static void SDL_GenerateAssertionReport(void)
+{
+    const SDL_AssertData *item = triggered_assertions;
+
+    // only do this if the app hasn't assigned an assertion handler.
+    if ((item) && (assertion_handler != SDL_PromptAssertion)) {
+        debug_print("\n\nSDL assertion report.\n");
+        debug_print("All SDL assertions between last init/quit:\n\n");
+
+        while (item) {
+            debug_print(
+                "'%s'\n"
+                "    * %s (%s:%d)\n"
+                "    * triggered %u time%s.\n"
+                "    * always ignore: %s.\n",
+                item->condition, item->function, item->filename,
+                item->linenum, item->trigger_count,
+                (item->trigger_count == 1) ? "" : "s",
+                item->always_ignore ? "yes" : "no");
+            item = item->next;
+        }
+        debug_print("\n");
+
+        SDL_ResetAssertionReport();
+    }
+}
+
+/* This is not declared in any header, although it is shared between some
+    parts of SDL, because we don't want anything calling it without an
+    extremely good reason. */
+#ifdef __WATCOMC__
+extern void SDL_ExitProcess(int exitcode);
+#pragma aux SDL_ExitProcess aborts;
+#endif
+extern SDL_NORETURN void SDL_ExitProcess(int exitcode);
+
+#ifdef __WATCOMC__
+static void SDL_AbortAssertion(void);
+#pragma aux SDL_AbortAssertion aborts;
+#endif
+static SDL_NORETURN void SDL_AbortAssertion(void)
+{
+    SDL_Quit();
+    SDL_ExitProcess(42);
+}
+
+static SDL_AssertState SDLCALL SDL_PromptAssertion(const SDL_AssertData *data, void *userdata)
+{
+    SDL_AssertState state = SDL_ASSERTION_ABORT;
+    SDL_Window *window;
+    SDL_MessageBoxData messagebox;
+    SDL_MessageBoxButtonData buttons[] = {
+        { 0, SDL_ASSERTION_RETRY, "Retry" },
+        { 0, SDL_ASSERTION_BREAK, "Break" },
+        { 0, SDL_ASSERTION_ABORT, "Abort" },
+        { SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
+          SDL_ASSERTION_IGNORE, "Ignore" },
+        { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
+          SDL_ASSERTION_ALWAYS_IGNORE, "Always Ignore" }
+    };
+    int selected;
+
+    char stack_buf[SDL_MAX_ASSERT_MESSAGE_STACK];
+    char *message = stack_buf;
+    size_t buf_len = sizeof(stack_buf);
+    int len;
+
+    (void)userdata; // unused in default handler.
+
+    // Assume the output will fit...
+    len = SDL_RenderAssertMessage(message, buf_len, data);
+
+    // .. and if it didn't, try to allocate as much room as we actually need.
+    if (len >= (int)buf_len) {
+        if (SDL_size_add_check_overflow(len, 1, &buf_len)) {
+            message = (char *)SDL_malloc(buf_len);
+            if (message) {
+                len = SDL_RenderAssertMessage(message, buf_len, data);
+            } else {
+                message = stack_buf;
+            }
+        }
+    }
+
+    // Something went very wrong
+    if (len < 0) {
+        if (message != stack_buf) {
+            SDL_free(message);
+        }
+        return SDL_ASSERTION_ABORT;
+    }
+
+    debug_print("\n\n%s\n\n", message);
+
+    // let env. variable override, so unit tests won't block in a GUI.
+    const char *hint = SDL_GetHint(SDL_HINT_ASSERT);
+    if (hint) {
+        if (message != stack_buf) {
+            SDL_free(message);
+        }
+
+        if (SDL_strcmp(hint, "abort") == 0) {
+            return SDL_ASSERTION_ABORT;
+        } else if (SDL_strcmp(hint, "break") == 0) {
+            return SDL_ASSERTION_BREAK;
+        } else if (SDL_strcmp(hint, "retry") == 0) {
+            return SDL_ASSERTION_RETRY;
+        } else if (SDL_strcmp(hint, "ignore") == 0) {
+            return SDL_ASSERTION_IGNORE;
+        } else if (SDL_strcmp(hint, "always_ignore") == 0) {
+            return SDL_ASSERTION_ALWAYS_IGNORE;
+        } else {
+            return SDL_ASSERTION_ABORT; // oh well.
+        }
+    }
+
+    // Show a messagebox if we can, otherwise fall back to stdio
+    SDL_zero(messagebox);
+    messagebox.flags = SDL_MESSAGEBOX_WARNING;
+    messagebox.window = window;
+    messagebox.title = "Assertion Failed";
+    messagebox.message = message;
+    messagebox.numbuttons = SDL_arraysize(buttons);
+    messagebox.buttons = buttons;
+
+    //if (SDL_ShowMessageBox(&messagebox, &selected)) {
+    if (false) {
+        if (selected == -1) {
+            state = SDL_ASSERTION_IGNORE;
+        } else {
+            state = (SDL_AssertState)selected;
+        }
+    } else {
+#ifdef SDL_PLATFORM_PRIVATE_ASSERT
+        SDL_PRIVATE_PROMPTASSERTION();
+#elif defined(SDL_PLATFORM_EMSCRIPTEN)
+        // This is nasty, but we can't block on a custom UI.
+        for (;;) {
+            bool okay = true;
+            /* *INDENT-OFF* */ // clang-format off
+            int reply = MAIN_THREAD_EM_ASM_INT({
+                var str =
+                    UTF8ToString($0) + '\n\n' +
+                    'Abort/Retry/Ignore/AlwaysIgnore? [ariA] :';
+                var reply = window.prompt(str, "i");
+                if (reply === null) {
+                    reply = "i";
+                }
+                return reply.length === 1 ? reply.charCodeAt(0) : -1;
+            }, message);
+            /* *INDENT-ON* */ // clang-format on
+
+            switch (reply) {
+            case 'a':
+                state = SDL_ASSERTION_ABORT;
+                break;
+#if 0 // (currently) no break functionality on Emscripten
+            case 'b':
+                state = SDL_ASSERTION_BREAK;
+                break;
+#endif
+            case 'r':
+                state = SDL_ASSERTION_RETRY;
+                break;
+            case 'i':
+                state = SDL_ASSERTION_IGNORE;
+                break;
+            case 'A':
+                state = SDL_ASSERTION_ALWAYS_IGNORE;
+                break;
+            default:
+                okay = false;
+                break;
+            }
+
+            if (okay) {
+                break;
+            }
+        }
+#elif defined(HAVE_STDIO_H) && !defined(SDL_PLATFORM_3DS)
+        // this is a little hacky.
+        for (;;) {
+            char buf[32];
+            (void)fprintf(stderr, "Abort/Break/Retry/Ignore/AlwaysIgnore? [abriA] : ");
+            (void)fflush(stderr);
+            if (fgets(buf, sizeof(buf), stdin) == NULL) {
+                break;
+            }
+
+            if (SDL_strncmp(buf, "a", 1) == 0) {
+                state = SDL_ASSERTION_ABORT;
+                break;
+            } else if (SDL_strncmp(buf, "b", 1) == 0) {
+                state = SDL_ASSERTION_BREAK;
+                break;
+            } else if (SDL_strncmp(buf, "r", 1) == 0) {
+                state = SDL_ASSERTION_RETRY;
+                break;
+            } else if (SDL_strncmp(buf, "i", 1) == 0) {
+                state = SDL_ASSERTION_IGNORE;
+                break;
+            } else if (SDL_strncmp(buf, "A", 1) == 0) {
+                state = SDL_ASSERTION_ALWAYS_IGNORE;
+                break;
+            }
+        }
+#else
+        //SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "Assertion Failed", message, window);
+#endif // HAVE_STDIO_H
+    }
+
+    // Re-enter fullscreen mode
+    if (window) {
+        //SDL_RestoreWindow(window);
+    }
+
+    if (message != stack_buf) {
+        SDL_free(message);
+    }
+
+    return state;
+}
+
+SDL_AssertState SDL_ReportAssertion(SDL_AssertData *data, const char *func, const char *file, int line)
+{
+    SDL_AssertState state = SDL_ASSERTION_IGNORE;
+    static int assertion_running = 0;
+
+#ifndef SDL_THREADS_DISABLED
+    static SDL_SpinLock spinlock = 0;
+    SDL_LockSpinlock(&spinlock);
+    if (!assertion_mutex) { // never called SDL_Init()?
+        assertion_mutex = SDL_CreateMutex();
+        if (!assertion_mutex) {
+            SDL_UnlockSpinlock(&spinlock);
+            return SDL_ASSERTION_IGNORE; // oh well, I guess.
+        }
+    }
+    SDL_UnlockSpinlock(&spinlock);
+
+    SDL_LockMutex(assertion_mutex);
+#endif // !SDL_THREADS_DISABLED
+
+    // doing this because Visual C is upset over assigning in the macro.
+    if (data->trigger_count == 0) {
+        data->function = func;
+        data->filename = file;
+        data->linenum = line;
+    }
+
+    SDL_AddAssertionToReport(data);
+
+    assertion_running++;
+    if (assertion_running > 1) { // assert during assert! Abort.
+        if (assertion_running == 2) {
+            SDL_AbortAssertion();
+        } else if (assertion_running == 3) { // Abort asserted!
+            SDL_ExitProcess(42);
+        } else {
+            while (1) { // do nothing but spin; what else can you do?!
+            }
+        }
+    }
+
+    if (!data->always_ignore) {
+        state = assertion_handler(data, assertion_userdata);
+    }
+
+    switch (state) {
+    case SDL_ASSERTION_ALWAYS_IGNORE:
+        state = SDL_ASSERTION_IGNORE;
+        data->always_ignore = true;
+        break;
+
+    case SDL_ASSERTION_IGNORE:
+    case SDL_ASSERTION_RETRY:
+    case SDL_ASSERTION_BREAK:
+        break; // macro handles these.
+
+    case SDL_ASSERTION_ABORT:
+        SDL_AbortAssertion();
+        // break;  ...shouldn't return, but oh well.
+    }
+
+    assertion_running--;
+
+#ifndef SDL_THREADS_DISABLED
+    SDL_UnlockMutex(assertion_mutex);
+#endif
+
+    return state;
+}
+
+void SDL_AssertionsQuit(void)
+{
+#if SDL_ASSERT_LEVEL > 0
+    SDL_GenerateAssertionReport();
+#ifndef SDL_THREADS_DISABLED
+    if (assertion_mutex) {
+        SDL_DestroyMutex(assertion_mutex);
+        assertion_mutex = NULL;
+    }
+#endif
+#endif // SDL_ASSERT_LEVEL > 0
+}
+
+void SDL_SetAssertionHandler(SDL_AssertionHandler handler, void *userdata)
+{
+    if (handler != NULL) {
+        assertion_handler = handler;
+        assertion_userdata = userdata;
+    } else {
+        assertion_handler = SDL_PromptAssertion;
+        assertion_userdata = NULL;
+    }
+}
+
+const SDL_AssertData *SDL_GetAssertionReport(void)
+{
+    return triggered_assertions;
+}
+
+void SDL_ResetAssertionReport(void)
+{
+    SDL_AssertData *next = NULL;
+    SDL_AssertData *item;
+    for (item = triggered_assertions; item; item = next) {
+        next = (SDL_AssertData *)item->next;
+        item->always_ignore = false;
+        item->trigger_count = 0;
+        item->next = NULL;
+    }
+
+    triggered_assertions = NULL;
+}
+
+SDL_AssertionHandler SDL_GetDefaultAssertionHandler(void)
+{
+    return SDL_PromptAssertion;
+}
+
+SDL_AssertionHandler SDL_GetAssertionHandler(void **userdata)
+{
+    if (userdata) {
+        *userdata = assertion_userdata;
+    }
+    return assertion_handler;
+}

+ 28 - 0
thirdparty/sdl/SDL_assert_c.h

@@ -0,0 +1,28 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#ifndef SDL_assert_c_h_
+#define SDL_assert_c_h_
+
+extern void SDL_AssertionsQuit(void);
+
+#endif // SDL_assert_c_h_

+ 112 - 0
thirdparty/sdl/SDL_error.c

@@ -0,0 +1,112 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+// Simple error handling in SDL
+
+#include "SDL_error_c.h"
+
+bool SDL_SetError(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
+{
+    va_list ap;
+    bool result;
+
+    va_start(ap, fmt);
+    result = SDL_SetErrorV(fmt, ap);
+    va_end(ap);
+    return result;
+}
+
+bool SDL_SetErrorV(SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap)
+{
+    // Ignore call if invalid format pointer was passed
+    if (fmt) {
+        int result;
+        SDL_error *error = SDL_GetErrBuf(true);
+        va_list ap2;
+
+        error->error = SDL_ErrorCodeGeneric;
+
+        va_copy(ap2, ap);
+        result = SDL_vsnprintf(error->str, error->len, fmt, ap2);
+        va_end(ap2);
+
+        if (result >= 0 && (size_t)result >= error->len && error->realloc_func) {
+            size_t len = (size_t)result + 1;
+            char *str = (char *)error->realloc_func(error->str, len);
+            if (str) {
+                error->str = str;
+                error->len = len;
+                va_copy(ap2, ap);
+                (void)SDL_vsnprintf(error->str, error->len, fmt, ap2);
+                va_end(ap2);
+            }
+        }
+
+// Enable this if you want to see all errors printed as they occur.
+// Note that there are many recoverable errors that may happen internally and
+// can be safely ignored if the public API doesn't return an error code.
+#if 0
+        SDL_LogError(SDL_LOG_CATEGORY_ERROR, "%s", error->str);
+#endif
+    }
+
+    return false;
+}
+
+const char *SDL_GetError(void)
+{
+    const SDL_error *error = SDL_GetErrBuf(false);
+
+    if (!error) {
+        return "";
+    }
+
+    switch (error->error) {
+    case SDL_ErrorCodeGeneric:
+        return error->str;
+    case SDL_ErrorCodeOutOfMemory:
+        return "Out of memory";
+    default:
+        return "";
+    }
+}
+
+bool SDL_ClearError(void)
+{
+    SDL_error *error = SDL_GetErrBuf(false);
+
+    if (error) {
+        error->error = SDL_ErrorCodeNone;
+    }
+    return true;
+}
+
+bool SDL_OutOfMemory(void)
+{
+    SDL_error *error = SDL_GetErrBuf(true);
+
+    if (error) {
+        error->error = SDL_ErrorCodeOutOfMemory;
+    }
+    return false;
+}
+

+ 61 - 0
thirdparty/sdl/SDL_error_c.h

@@ -0,0 +1,61 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+/* This file defines a structure that carries language-independent
+   error messages
+*/
+
+#ifndef SDL_error_c_h_
+#define SDL_error_c_h_
+
+typedef enum
+{
+    SDL_ErrorCodeNone,
+    SDL_ErrorCodeGeneric,
+    SDL_ErrorCodeOutOfMemory,
+} SDL_ErrorCode;
+
+typedef struct SDL_error
+{
+    SDL_ErrorCode error;
+    char *str;
+    size_t len;
+    SDL_realloc_func realloc_func;
+    SDL_free_func free_func;
+} SDL_error;
+
+// Defined in SDL_thread.c
+extern SDL_error *SDL_GetErrBuf(bool create);
+
+// Macros to save and restore error values
+#define SDL_PushError() \
+    char *saved_error = SDL_strdup(SDL_GetError())
+
+#define SDL_PopError()                          \
+    do {                                        \
+        if (saved_error) {                      \
+            SDL_SetError("%s", saved_error);    \
+            SDL_free(saved_error);              \
+        }                                       \
+    } while (0)
+
+#endif // SDL_error_c_h_

+ 88 - 0
thirdparty/sdl/SDL_guid.c

@@ -0,0 +1,88 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+// convert the guid to a printable string
+void SDL_GUIDToString(SDL_GUID guid, char *pszGUID, int cbGUID)
+{
+    static const char k_rgchHexToASCII[] = "0123456789abcdef";
+    int i;
+
+    if ((!pszGUID) || (cbGUID <= 0)) {
+        return;
+    }
+
+    for (i = 0; i < sizeof(guid.data) && i < (cbGUID - 1) / 2; i++) {
+        // each input byte writes 2 ascii chars, and might write a null byte.
+        // If we don't have room for next input byte, stop
+        unsigned char c = guid.data[i];
+
+        *pszGUID++ = k_rgchHexToASCII[c >> 4];
+        *pszGUID++ = k_rgchHexToASCII[c & 0x0F];
+    }
+    *pszGUID = '\0';
+}
+
+/*-----------------------------------------------------------------------------
+ * Purpose: Returns the 4 bit nibble for a hex character
+ * Input  : c -
+ * Output : unsigned char
+ *-----------------------------------------------------------------------------*/
+static unsigned char nibble(unsigned char c)
+{
+    if ((c >= '0') && (c <= '9')) {
+        return c - '0';
+    }
+
+    if ((c >= 'A') && (c <= 'F')) {
+        return c - 'A' + 0x0a;
+    }
+
+    if ((c >= 'a') && (c <= 'f')) {
+        return c - 'a' + 0x0a;
+    }
+
+    // received an invalid character, and no real way to return an error
+    // AssertMsg1(false, "Q_nibble invalid hex character '%c' ", c);
+    return 0;
+}
+
+// convert the string version of a guid to the struct
+SDL_GUID SDL_StringToGUID(const char *pchGUID)
+{
+    SDL_GUID guid;
+    int maxoutputbytes = sizeof(guid);
+    size_t len = SDL_strlen(pchGUID);
+    Uint8 *p;
+    size_t i;
+
+    // Make sure it's even
+    len = (len) & ~0x1;
+
+    SDL_memset(&guid, 0x00, sizeof(guid));
+
+    p = (Uint8 *)&guid;
+    for (i = 0; (i < len) && ((p - (Uint8 *)&guid) < maxoutputbytes); i += 2, p++) {
+        *p = (nibble((unsigned char)pchGUID[i]) << 4) | nibble((unsigned char)pchGUID[i + 1]);
+    }
+
+    return guid;
+}

+ 543 - 0
thirdparty/sdl/SDL_hashtable.c

@@ -0,0 +1,543 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+typedef struct SDL_HashItem
+{
+    // TODO: Splitting off values into a separate array might be more cache-friendly
+    const void *key;
+    const void *value;
+    Uint32 hash;
+    Uint32 probe_len : 31;
+    Uint32 live : 1;
+} SDL_HashItem;
+
+// Must be a power of 2 >= sizeof(SDL_HashItem)
+#define MAX_HASHITEM_SIZEOF 32u
+SDL_COMPILE_TIME_ASSERT(sizeof_SDL_HashItem, sizeof(SDL_HashItem) <= MAX_HASHITEM_SIZEOF);
+
+// Anything larger than this will cause integer overflows
+#define MAX_HASHTABLE_SIZE (0x80000000u / (MAX_HASHITEM_SIZEOF))
+
+struct SDL_HashTable
+{
+    SDL_RWLock *lock;  // NULL if not created threadsafe
+    SDL_HashItem *table;
+    SDL_HashCallback hash;
+    SDL_HashKeyMatchCallback keymatch;
+    SDL_HashDestroyCallback destroy;
+    void *userdata;
+    Uint32 hash_mask;
+    Uint32 max_probe_len;
+    Uint32 num_occupied_slots;
+};
+
+
+static Uint32 CalculateHashBucketsFromEstimate(int estimated_capacity)
+{
+    if (estimated_capacity <= 0) {
+        return 4;  // start small, grow as necessary.
+    }
+
+    const Uint32 estimated32 = (Uint32) estimated_capacity;
+    Uint32 buckets = ((Uint32) 1) << SDL_MostSignificantBitIndex32(estimated32);
+    if (!SDL_HasExactlyOneBitSet32(estimated32)) {
+        buckets <<= 1;  // need next power of two up to fit overflow capacity bits.
+    }
+
+    return SDL_min(buckets, MAX_HASHTABLE_SIZE);
+}
+
+SDL_HashTable *SDL_CreateHashTable(int estimated_capacity, bool threadsafe, SDL_HashCallback hash,
+                                   SDL_HashKeyMatchCallback keymatch,
+                                   SDL_HashDestroyCallback destroy, void *userdata)
+{
+    const Uint32 num_buckets = CalculateHashBucketsFromEstimate(estimated_capacity);
+    SDL_HashTable *table = (SDL_HashTable *)SDL_calloc(1, sizeof(SDL_HashTable));
+    if (!table) {
+        return NULL;
+    }
+
+    if (threadsafe) {
+        table->lock = SDL_CreateRWLock();
+        if (!table->lock) {
+            SDL_DestroyHashTable(table);
+            return NULL;
+        }
+    }
+
+    table->table = (SDL_HashItem *)SDL_calloc(num_buckets, sizeof(SDL_HashItem));
+    if (!table->table) {
+        SDL_DestroyHashTable(table);
+        return NULL;
+    }
+
+    table->hash_mask = num_buckets - 1;
+    table->userdata = userdata;
+    table->hash = hash;
+    table->keymatch = keymatch;
+    table->destroy = destroy;
+    return table;
+}
+
+static SDL_INLINE Uint32 calc_hash(const SDL_HashTable *table, const void *key)
+{
+    const Uint32 BitMixer = 0x9E3779B1u;
+    return table->hash(table->userdata, key) * BitMixer;
+}
+
+static SDL_INLINE Uint32 get_probe_length(Uint32 zero_idx, Uint32 actual_idx, Uint32 num_buckets)
+{
+    // returns the probe sequence length from zero_idx to actual_idx
+    if (actual_idx < zero_idx) {
+        return num_buckets - zero_idx + actual_idx;
+    }
+
+    return actual_idx - zero_idx;
+}
+
+static SDL_HashItem *find_item(const SDL_HashTable *ht, const void *key, Uint32 hash, Uint32 *i, Uint32 *probe_len)
+{
+    Uint32 hash_mask = ht->hash_mask;
+    Uint32 max_probe_len = ht->max_probe_len;
+
+    SDL_HashItem *table = ht->table;
+
+    while (true) {
+        SDL_HashItem *item = table + *i;
+        Uint32 item_hash = item->hash;
+
+        if (!item->live) {
+            return NULL;
+        }
+
+        if (item_hash == hash && ht->keymatch(ht->userdata, item->key, key)) {
+            return item;
+        }
+
+        Uint32 item_probe_len = item->probe_len;
+        SDL_assert(item_probe_len == get_probe_length(item_hash & hash_mask, (Uint32)(item - table), hash_mask + 1));
+
+        if (*probe_len > item_probe_len) {
+            return NULL;
+        }
+
+        if (++*probe_len > max_probe_len) {
+            return NULL;
+        }
+
+        *i = (*i + 1) & hash_mask;
+    }
+}
+
+static SDL_HashItem *find_first_item(const SDL_HashTable *ht, const void *key, Uint32 hash)
+{
+    Uint32 i = hash & ht->hash_mask;
+    Uint32 probe_len = 0;
+    return find_item(ht, key, hash, &i, &probe_len);
+}
+
+static SDL_HashItem *insert_item(SDL_HashItem *item_to_insert, SDL_HashItem *table, Uint32 hash_mask, Uint32 *max_probe_len_ptr)
+{
+    const Uint32 num_buckets = hash_mask + 1;
+    Uint32 idx = item_to_insert->hash & hash_mask;
+    SDL_HashItem *target = NULL;
+    SDL_HashItem temp_item;
+
+    while (true) {
+        SDL_HashItem *candidate = table + idx;
+
+        if (!candidate->live) {
+            // Found an empty slot. Put it here and we're done.
+            *candidate = *item_to_insert;
+
+            if (target == NULL) {
+                target = candidate;
+            }
+
+            const Uint32 probe_len = get_probe_length(candidate->hash & hash_mask, idx, num_buckets);
+            candidate->probe_len = probe_len;
+
+            if (*max_probe_len_ptr < probe_len) {
+                *max_probe_len_ptr = probe_len;
+            }
+
+            break;
+        }
+
+        const Uint32 candidate_probe_len = candidate->probe_len;
+        SDL_assert(candidate_probe_len == get_probe_length(candidate->hash & hash_mask, idx, num_buckets));
+        const Uint32 new_probe_len = get_probe_length(item_to_insert->hash & hash_mask, idx, num_buckets);
+
+        if (candidate_probe_len < new_probe_len) {
+            // Robin Hood hashing: the item at idx has a better probe length than our item would at this position.
+            // Evict it and put our item in its place, then continue looking for a new spot for the displaced item.
+            // This algorithm significantly reduces clustering in the table, making lookups take very few probes.
+
+            temp_item = *candidate;
+            *candidate = *item_to_insert;
+
+            if (target == NULL) {
+                target = candidate;
+            }
+
+            *item_to_insert = temp_item;
+
+            SDL_assert(new_probe_len == get_probe_length(candidate->hash & hash_mask, idx, num_buckets));
+            candidate->probe_len = new_probe_len;
+
+            if (*max_probe_len_ptr < new_probe_len) {
+                *max_probe_len_ptr = new_probe_len;
+            }
+        }
+
+        idx = (idx + 1) & hash_mask;
+    }
+
+    return target;
+}
+
+static void delete_item(SDL_HashTable *ht, SDL_HashItem *item)
+{
+    const Uint32 hash_mask = ht->hash_mask;
+    SDL_HashItem *table = ht->table;
+
+    if (ht->destroy) {
+        ht->destroy(ht->userdata, item->key, item->value);
+    }
+
+    SDL_assert(ht->num_occupied_slots > 0);
+    ht->num_occupied_slots--;
+
+    Uint32 idx = (Uint32)(item - ht->table);
+
+    while (true) {
+        idx = (idx + 1) & hash_mask;
+        SDL_HashItem *next_item = table + idx;
+
+        if (next_item->probe_len < 1) {
+            SDL_zerop(item);
+            return;
+        }
+
+        *item = *next_item;
+        item->probe_len -= 1;
+        SDL_assert(item->probe_len < ht->max_probe_len);
+        item = next_item;
+    }
+}
+
+static bool resize(SDL_HashTable *ht, Uint32 new_size)
+{
+    const Uint32 new_hash_mask = new_size - 1;
+    SDL_HashItem *new_table = SDL_calloc(new_size, sizeof(*new_table));
+
+    if (!new_table) {
+        return false;
+    }
+
+    SDL_HashItem *old_table = ht->table;
+    const Uint32 old_size = ht->hash_mask + 1;
+
+    ht->max_probe_len = 0;
+    ht->hash_mask = new_hash_mask;
+    ht->table = new_table;
+
+    for (Uint32 i = 0; i < old_size; ++i) {
+        SDL_HashItem *item = old_table + i;
+        if (item->live) {
+            insert_item(item, new_table, new_hash_mask, &ht->max_probe_len);
+        }
+    }
+
+    SDL_free(old_table);
+    return true;
+}
+
+static bool maybe_resize(SDL_HashTable *ht)
+{
+    const Uint32 capacity = ht->hash_mask + 1;
+
+    if (capacity >= MAX_HASHTABLE_SIZE) {
+        return false;
+    }
+
+    const Uint32 max_load_factor = 217; // range: 0-255; 217 is roughly 85%
+    const Uint32 resize_threshold = (Uint32)((max_load_factor * (Uint64)capacity) >> 8);
+
+    if (ht->num_occupied_slots > resize_threshold) {
+        return resize(ht, capacity * 2);
+    }
+
+    return true;
+}
+
+bool SDL_InsertIntoHashTable(SDL_HashTable *table, const void *key, const void *value, bool replace)
+{
+    if (!table) {
+        return SDL_InvalidParamError("table");
+    }
+
+    bool result = false;
+
+    SDL_LockRWLockForWriting(table->lock);
+
+    const Uint32 hash = calc_hash(table, key);
+    SDL_HashItem *item = find_first_item(table, key, hash);
+    bool do_insert = true;
+
+    if (item) {
+        if (replace) {
+            delete_item(table, item);
+        } else {
+            SDL_SetError("key already exists and replace is disabled");
+            do_insert = false;
+        }
+    }
+
+    if (do_insert) {
+        SDL_HashItem new_item;
+        new_item.key = key;
+        new_item.value = value;
+        new_item.hash = hash;
+        new_item.live = true;
+        new_item.probe_len = 0;
+
+        table->num_occupied_slots++;
+
+        if (!maybe_resize(table)) {
+            table->num_occupied_slots--;
+        } else {
+            // This never returns NULL
+            insert_item(&new_item, table->table, table->hash_mask, &table->max_probe_len);
+            result = true;
+        }
+    }
+
+    SDL_UnlockRWLock(table->lock);
+    return result;
+}
+
+bool SDL_FindInHashTable(const SDL_HashTable *table, const void *key, const void **value)
+{
+    if (!table) {
+        if (value) {
+            *value = NULL;
+        }
+        return SDL_InvalidParamError("table");
+    }
+
+    SDL_LockRWLockForReading(table->lock);
+
+    bool result = false;
+    const Uint32 hash = calc_hash(table, key);
+    SDL_HashItem *i = find_first_item(table, key, hash);
+    if (i) {
+        if (value) {
+            *value = i->value;
+        }
+        result = true;
+    }
+
+    SDL_UnlockRWLock(table->lock);
+
+    return result;
+}
+
+bool SDL_RemoveFromHashTable(SDL_HashTable *table, const void *key)
+{
+    if (!table) {
+        return SDL_InvalidParamError("table");
+    }
+
+    SDL_LockRWLockForWriting(table->lock);
+
+    bool result = false;
+    const Uint32 hash = calc_hash(table, key);
+    SDL_HashItem *item = find_first_item(table, key, hash);
+    if (item) {
+        delete_item(table, item);
+        result = true;
+    }
+
+    SDL_UnlockRWLock(table->lock);
+    return result;
+}
+
+bool SDL_IterateHashTable(const SDL_HashTable *table, SDL_HashTableIterateCallback callback, void *userdata)
+{
+    if (!table) {
+        return SDL_InvalidParamError("table");
+    } else if (!callback) {
+        return SDL_InvalidParamError("callback");
+    }
+
+    SDL_LockRWLockForReading(table->lock);
+    SDL_HashItem *end = table->table + (table->hash_mask + 1);
+    Uint32 num_iterated = 0;
+
+    for (SDL_HashItem *item = table->table; item < end; item++) {
+        if (item->live) {
+            if (!callback(userdata, table, item->key, item->value)) {
+                break;  // callback requested iteration stop.
+            } else if (++num_iterated >= table->num_occupied_slots) {
+                break;  // we can drop out early because we've seen all the live items.
+            }
+        }
+    }
+
+    SDL_UnlockRWLock(table->lock);
+    return true;
+}
+
+bool SDL_HashTableEmpty(SDL_HashTable *table)
+{
+    if (!table) {
+        return SDL_InvalidParamError("table");
+    }
+
+    SDL_LockRWLockForReading(table->lock);
+    const bool retval = (table->num_occupied_slots == 0);
+    SDL_UnlockRWLock(table->lock);
+    return retval;
+}
+
+
+static void destroy_all(SDL_HashTable *table)
+{
+    SDL_HashDestroyCallback destroy = table->destroy;
+    if (destroy) {
+        void *userdata = table->userdata;
+        SDL_HashItem *end = table->table + (table->hash_mask + 1);
+        for (SDL_HashItem *i = table->table; i < end; ++i) {
+            if (i->live) {
+                i->live = false;
+                destroy(userdata, i->key, i->value);
+            }
+        }
+    }
+}
+
+void SDL_ClearHashTable(SDL_HashTable *table)
+{
+    if (table) {
+        SDL_LockRWLockForWriting(table->lock);
+        {
+            destroy_all(table);
+            SDL_memset(table->table, 0, sizeof(*table->table) * (table->hash_mask + 1));
+            table->num_occupied_slots = 0;
+        }
+        SDL_UnlockRWLock(table->lock);
+    }
+}
+
+void SDL_DestroyHashTable(SDL_HashTable *table)
+{
+    if (table) {
+        destroy_all(table);
+        if (table->lock) {
+            SDL_DestroyRWLock(table->lock);
+        }
+        SDL_free(table->table);
+        SDL_free(table);
+    }
+}
+
+// this is djb's xor hashing function.
+static SDL_INLINE Uint32 hash_string_djbxor(const char *str, size_t len)
+{
+    Uint32 hash = 5381;
+    while (len--) {
+        hash = ((hash << 5) + hash) ^ *(str++);
+    }
+    return hash;
+}
+
+Uint32 SDL_HashPointer(void *unused, const void *key)
+{
+    (void)unused;
+    return SDL_murmur3_32(&key, sizeof(key), 0);
+}
+
+bool SDL_KeyMatchPointer(void *unused, const void *a, const void *b)
+{
+    (void)unused;
+    return (a == b);
+}
+
+Uint32 SDL_HashString(void *unused, const void *key)
+{
+    (void)unused;
+    const char *str = (const char *)key;
+    return hash_string_djbxor(str, SDL_strlen(str));
+}
+
+bool SDL_KeyMatchString(void *unused, const void *a, const void *b)
+{
+    const char *a_string = (const char *)a;
+    const char *b_string = (const char *)b;
+
+    (void)unused;
+    if (a == b) {
+        return true; // same pointer, must match.
+    } else if (!a || !b) {
+        return false; // one pointer is NULL (and first test shows they aren't the same pointer), must not match.
+    } else if (a_string[0] != b_string[0]) {
+        return false; // we know they don't match
+    }
+    return (SDL_strcmp(a_string, b_string) == 0); // Check against actual string contents.
+}
+
+// We assume we can fit the ID in the key directly
+SDL_COMPILE_TIME_ASSERT(SDL_HashID_KeySize, sizeof(Uint32) <= sizeof(const void *));
+
+Uint32 SDL_HashID(void *unused, const void *key)
+{
+    (void)unused;
+    return (Uint32)(uintptr_t)key;
+}
+
+bool SDL_KeyMatchID(void *unused, const void *a, const void *b)
+{
+    (void)unused;
+    return (a == b);
+}
+
+void SDL_DestroyHashKeyAndValue(void *unused, const void *key, const void *value)
+{
+    (void)unused;
+    SDL_free((void *)key);
+    SDL_free((void *)value);
+}
+
+void SDL_DestroyHashKey(void *unused, const void *key, const void *value)
+{
+    (void)value;
+    (void)unused;
+    SDL_free((void *)key);
+}
+
+void SDL_DestroyHashValue(void *unused, const void *key, const void *value)
+{
+    (void)key;
+    (void)unused;
+    SDL_free((void *)value);
+}

+ 633 - 0
thirdparty/sdl/SDL_hashtable.h

@@ -0,0 +1,633 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+/* this is over-documented because it was almost a public API. Leaving the
+   full docs here in case it _does_ become public some day. */
+
+/* WIKI CATEGORY: HashTable */
+
+/**
+ * # CategoryHashTable
+ *
+ * SDL offers a hash table implementation, as a convenience for C code that
+ * needs efficient organization and access of arbitrary data.
+ *
+ * Hash tables are a popular data structure, designed to make it quick to
+ * store and look up arbitrary data. Data is stored with an associated "key."
+ * While one would look up an element of an array with an index, a hash table
+ * uses a unique key to find an element later.
+ *
+ * A key can be anything, as long as its unique and in a format that the table
+ * understands. For example, it's popular to use strings as keys: the key
+ * might be a username, and it is used to lookup account information for that
+ * user, etc.
+ *
+ * Hash tables are named because they "hash" their keys down into simple
+ * integers that can be used to efficiently organize and access the associated
+ * data.
+ *
+ * As this is a C API, there is one generic interface that is intended to work
+ * with different data types. This can be a little awkward to set up, but is
+ * easy to use after that.
+ *
+ * Hashtables are generated by a call to SDL_CreateHashTable(). This function
+ * requires several callbacks to be provided (for hashing keys, comparing
+ * entries, and cleaning up entries when removed). These are necessary to
+ * allow the hash to manage any arbitrary data type.
+ *
+ * Once a hash table is created, the common tasks are inserting data into the
+ * table, (SDL_InsertIntoHashTable), looking up previously inserted data
+ * (SDL_FindInHashTable), and removing data (SDL_RemoveFromHashTable and
+ * SDL_ClearHashTable). Less common but still useful is the ability to
+ * iterate through all the items in the table (SDL_IterateHashTable).
+ *
+ * The underlying hash table implementation is always subject to change, but
+ * at the time of writing, it uses open addressing and Robin Hood hashing.
+ * The technical details are explained [here](https://github.com/libsdl-org/SDL/pull/10897).
+ *
+ * Hashtables keep an SDL_RWLock internally, so multiple threads can perform
+ * hash lookups in parallel, while changes to the table will safely serialize
+ * access between threads.
+ *
+ * SDL provides a layer on top of this hash table implementation that might be
+ * more pleasant to use. SDL_PropertiesID maps a string to arbitrary data of
+ * various types in the same table, which could be both easier to use and more
+ * flexible. Refer to [CategoryProperties](CategoryProperties) for details.
+ */
+
+#ifndef SDL_hashtable_h_
+#define SDL_hashtable_h_
+
+#include <SDL3/SDL_stdinc.h>
+
+#include <SDL3/SDL_begin_code.h>
+/* Set up for C function definitions, even when using C++ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The opaque type that represents a hash table.
+ *
+ * This is hidden behind an opaque pointer because not only does the table
+ * need to store arbitrary data types, but the hash table implementation may
+ * change in the future.
+ *
+ * \since This struct is available since SDL 3.4.0.
+ *
+ * \sa SDL_CreateHashTable
+ */
+typedef struct SDL_HashTable SDL_HashTable;
+
+/**
+ * A function pointer representing a hash table hashing callback.
+ *
+ * This is called by SDL_HashTable when it needs to look up a key in
+ * its dataset. It generates a hash value from that key, and then uses that
+ * value as a basis for an index into an internal array.
+ *
+ * There are no rules on what hashing algorithm is used, so long as it
+ * can produce a reliable 32-bit value from `key`, and ideally distributes
+ * those values well across the 32-bit value space. The quality of a
+ * hashing algorithm is directly related to how well a hash table performs.
+ *
+ * Hashing can be a complicated subject, and often times what works best
+ * for one dataset will be suboptimal for another. There is a good discussion
+ * of the field [on Wikipedia](https://en.wikipedia.org/wiki/Hash_function).
+ *
+ * Also: do you _need_ to write a hashing function? SDL provides generic
+ * functions for strings (SDL_HashString), generic integer IDs (SDL_HashID),
+ * and generic pointers (SDL_HashPointer). Often you should use one of these
+ * before writing your own.
+ *
+ * \param userdata what was passed as `userdata` to SDL_CreateHashTable().
+ * \param key the key to be hashed.
+ * \returns a 32-bit value that represents a hash of `key`.
+ *
+ * \threadsafety This function must be thread safe if the hash table is used
+ *               from multiple threads at the same time.
+ *
+ * \since This datatype is available since SDL 3.4.0.
+ *
+ * \sa SDL_CreateHashTable
+ * \sa SDL_HashString
+ * \sa SDL_HashID
+ * \sa SDL_HashPointer
+ */
+typedef Uint32 (SDLCALL *SDL_HashCallback)(void *userdata, const void *key);
+
+
+/**
+ * A function pointer representing a hash table matching callback.
+ *
+ * This is called by SDL_HashTable when it needs to look up a key in its
+ * dataset. After hashing the key, it looks for items stored in relation to
+ * that hash value. Since there can be more than one item found through the
+ * same hash value, this function verifies a specific value is actually
+ * correct before choosing it.
+ *
+ * So this function needs to compare the keys at `a` and `b` and decide if
+ * they are actually the same.
+ *
+ * For example, if the keys are C strings, this function might just be:
+ *
+ * ```c
+ * return (SDL_strcmp((const char *) a, const char *b) == 0);`
+ * ```
+ *
+ * Also: do you _need_ to write a matching function? SDL provides generic
+ * functions for strings (SDL_KeyMatchString), generic integer IDs
+ * (SDL_KeyMatchID), and generic pointers (SDL_KeyMatchPointer). Often you
+ * should use one of these before writing your own.
+ *
+ * \param userdata what was passed as `userdata` to SDL_CreateHashTable().
+ * \param a the first key to be compared.
+ * \param b the second key to be compared.
+ * \returns true if two keys are identical, false otherwise.
+ *
+ * \threadsafety This function must be thread safe if the hash table is used
+ *               from multiple threads at the same time.
+ *
+ * \since This datatype is available since SDL 3.4.0.
+ *
+ * \sa SDL_CreateHashTable
+ */
+typedef bool (SDLCALL *SDL_HashKeyMatchCallback)(void *userdata, const void *a, const void *b);
+
+
+/**
+ * A function pointer representing a hash table cleanup callback.
+ *
+ * This is called by SDL_HashTable when removing items from the hash, or
+ * destroying the hash table. It is used to optionally deallocate the
+ * key/value pairs.
+ *
+ * This is not required to do anything, if all the data in the table is
+ * static or POD data, but it can also do more than a simple free: for
+ * example, if the hash table is storing open files, it can close them here.
+ * It can also free only the key or only the value; it depends on what the
+ * hash table contains.
+ *
+ * \param userdata what was passed as `userdata` to SDL_CreateHashTable().
+ * \param key the key to deallocate.
+ * \param value the value to deallocate.
+ *
+ * \threadsafety This function must be thread safe if the hash table is used
+ *               from multiple threads at the same time.
+ *
+ * \since This datatype is available since SDL 3.4.0.
+ *
+ * \sa SDL_CreateHashTable
+ */
+typedef void (SDLCALL *SDL_HashDestroyCallback)(void *userdata, const void *key, const void *value);
+
+
+/**
+ * A function pointer representing a hash table iterator callback.
+ *
+ * This function is called once for each key/value pair to be considered
+ * when iterating a hash table.
+ *
+ * Iteration continues as long as there are more items to examine and this
+ * callback continues to return true.
+ *
+ * Do not attempt to modify the hash table during this callback, as it will
+ * cause incorrect behavior and possibly crashes.
+ *
+ * \param userdata what was passed as `userdata` to an iterator function.
+ * \param table the hash table being iterated.
+ * \param key the current key being iterated.
+ * \param value the current value being iterated.
+ * \returns true to keep iterating, false to stop iteration.
+ *
+ * \threadsafety A read lock is held during iteration, so other threads can
+ *               still access the the hash table, but threads attempting to
+ *               make changes will be blocked until iteration completes. If
+ *               this is a concern, do as little in the callback as possible
+ *               and finish iteration quickly.
+ *
+ * \since This datatype is available since SDL 3.4.0.
+ *
+ * \sa SDL_IterateHashTable
+ */
+typedef bool (SDLCALL *SDL_HashTableIterateCallback)(void *userdata, const SDL_HashTable *table, const void *key, const void *value);
+
+
+/**
+ * Create a new hash table.
+ *
+ * To deal with different datatypes and needs of the caller, hash tables
+ * require several callbacks that deal with some specifics: how to hash a key,
+ * how to compare a key for equality, and how to clean up keys and values.
+ * SDL provides a few generic functions that can be used for these callbacks:
+ *
+ * - SDL_HashString and SDL_KeyMatchString for C strings.
+ * - SDL_HashPointer and SDL_KeyMatchPointer for generic pointers.
+ * - SDL_HashID and SDL_KeyMatchID for generic (possibly small) integers.
+ *
+ * Oftentimes, these are all you need for any hash table, but depending on
+ * your dataset, custom implementations might make more sense.
+ *
+ * You can specify an estimate of the number of items expected to be stored
+ * in the table, which can help make the table run more efficiently. The table
+ * will preallocate resources to accomodate this number of items, which is
+ * most useful if you intend to fill the table with a lot of data right after
+ * creating it. Otherwise, it might make more sense to specify the _minimum_
+ * you expect the table to hold and let it grow as necessary from there. This
+ * number is only a hint, and the table will be able to handle any amount of
+ * data--as long as the system doesn't run out of resources--so a perfect
+ * answer is not required. A value of 0 signifies no guess at all, and the
+ * table will start small and reallocate as necessary; often this is the
+ * correct thing to do.
+ *
+ * !!! FIXME: add note about `threadsafe` here. And update `threadsafety` tags.
+ * !!! FIXME: note that `threadsafe` tables can't be recursively locked, so
+ * !!! FIXME:  you can't use `destroy` callbacks that might end up relocking.
+ *
+ * Note that SDL provides a higher-level option built on its hash tables:
+ * SDL_PropertiesID lets you map strings to various datatypes, and this
+ * might be easier to use. It only allows strings for keys, however. Those are
+ * created with SDL_CreateProperties().
+ *
+ * The returned hash table should be destroyed with SDL_DestroyHashTable()
+ * when no longer needed.
+ *
+ * \param estimated_capacity the approximate maximum number of items to be held
+ *                           in the hash table, or 0 for no estimate.
+ * \param threadsafe true to create an internal rwlock for this table.
+ * \param hash the function to use to hash keys.
+ * \param keymatch the function to use to compare keys.
+ * \param destroy the function to use to clean up keys and values, may be NULL.
+ * \param userdata a pointer that is passed to the callbacks.
+ * \returns a newly-created hash table, or NULL if there was an error; call
+ *          SDL_GetError() for more information.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.4.0.
+ *
+ * \sa SDL_DestroyHashTable
+ */
+extern SDL_HashTable * SDL_CreateHashTable(int estimated_capacity,
+                                           bool threadsafe,
+                                           SDL_HashCallback hash,
+                                           SDL_HashKeyMatchCallback keymatch,
+                                           SDL_HashDestroyCallback destroy,
+                                           void *userdata);
+
+
+/**
+ * Destroy a hash table.
+ *
+ * This will call the hash table's SDL_HashDestroyCallback for each item in
+ * the table, removing all inserted items, before deallocating the table
+ * itself.
+ *
+ * The table becomes invalid once this function is called, and no other thread
+ * should be accessing this table once this function has started.
+ *
+ * \param table the hash table to destroy.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.4.0.
+ */
+extern void SDL_DestroyHashTable(SDL_HashTable *table);
+
+/**
+ * Add an item to a hash table.
+ *
+ * All keys in the table must be unique. If attempting to insert a key that
+ * already exists in the hash table, what will be done depends on the
+ * `replace` value:
+ *
+ * - If `replace` is false, this function will return false without modifying
+ *   the table.
+ * - If `replace` is true, SDL will remove the previous item first, so the new
+ *   value is the only one associated with that key. This will call the hash
+ *   table's SDL_HashDestroyCallback for the previous item.
+ *
+ * \param table the hash table to insert into.
+ * \param key the key of the new item to insert.
+ * \param value the value of the new item to insert.
+ * \param replace true if a duplicate key should replace the previous value.
+ * \returns true if the new item was inserted, false otherwise.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.4.0.
+ */
+extern bool SDL_InsertIntoHashTable(SDL_HashTable *table, const void *key, const void *value, bool replace);
+
+/**
+ * Look up an item in a hash table.
+ *
+ * On return, the value associated with `key` is stored to `*value`.
+ * If the key does not exist in the table, `*value` will be set to NULL.
+ *
+ * It is legal for `value` to be NULL, to not retrieve the key's value. In
+ * this case, the return value is still useful for reporting if the key exists
+ * in the table at all.
+ *
+ * \param table the hash table to search.
+ * \param key the key to search for in the table.
+ * \param value the found value will be stored here. Can be NULL.
+ * \returns true if key exists in the table, false otherwise.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.4.0.
+ *
+ * \sa SDL_InsertIntoHashTable
+ */
+extern bool SDL_FindInHashTable(const SDL_HashTable *table, const void *key, const void **value);
+
+/**
+ * Remove an item from a hash table.
+ *
+ * If there is an item that matches `key`, it is removed from the table. This
+ * will call the hash table's SDL_HashDestroyCallback for the item to be
+ * removed.
+ *
+ * \param table the hash table to remove from.
+ * \param key the key of the item to remove from the table.
+ * \returns true if a key was removed, false if the key was not found.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.4.0.
+ */
+extern bool SDL_RemoveFromHashTable(SDL_HashTable *table, const void *key);
+
+/**
+ * Remove all items in a hash table.
+ *
+ * This will call the hash table's SDL_HashDestroyCallback for each item in
+ * the table, removing all inserted items.
+ *
+ * When this function returns, the hash table will be empty.
+ *
+ * \param table the hash table to clear.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.4.0.
+ */
+extern void SDL_ClearHashTable(SDL_HashTable *table);
+
+/**
+ * Check if any items are currently stored in a hash table.
+ *
+ * If there are no items stored (the table is completely empty), this will
+ * return true.
+ *
+ * \param table the hash table to check.
+ * \returns true if the table is completely empty, false otherwise.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.4.0.
+ *
+ * \sa SDL_ClearHashTable
+ */
+extern bool SDL_HashTableEmpty(SDL_HashTable *table);
+
+/**
+ * Iterate all key/value pairs in a hash table.
+ *
+ * This function will call `callback` once for each key/value pair in the
+ * table, until either all pairs have been presented to the callback, or the
+ * callback has returned false to signal it is done.
+ *
+ * There is no guarantee what order results will be returned in.
+ *
+ * \param table the hash table to iterate.
+ * \param callback the function pointer to call for each value.
+ * \param userdata a pointer that is passed to `callback`.
+ * \returns true if iteration happened, false if not (bogus parameter, etc).
+ *
+ * \since This function is available since SDL 3.4.0.
+ */
+extern bool SDL_IterateHashTable(const SDL_HashTable *table, SDL_HashTableIterateCallback callback, void *userdata);
+
+
+/* Helper functions for SDL_CreateHashTable callbacks... */
+
+/**
+ * Generate a hash from a generic pointer.
+ *
+ * The key is intended to be a unique pointer to any datatype.
+ *
+ * This is intended to be used as one of the callbacks to SDL_CreateHashTable,
+ * if this is useful to the type of keys to be used with the hash table.
+ *
+ * Note that the implementation may change in the future; do not expect
+ * the results to be stable vs future SDL releases. Use this in a hash table
+ * in the current process and don't store them to disk for the future.
+ *
+ * \param unused this parameter is ignored.
+ * \param key the key to hash as a generic pointer.
+ * \returns a 32-bit hash of the key.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.4.0.
+ *
+ * \sa SDL_CreateHashTable
+ */
+extern Uint32 SDL_HashPointer(void *unused, const void *key);
+
+/**
+ * Compare two generic pointers as hash table keys.
+ *
+ * This is intended to be used as one of the callbacks to SDL_CreateHashTable,
+ * if this is useful to the type of keys to be used with the hash table.
+ *
+ * \param unused this parameter is ignored.
+ * \param a the first generic pointer to compare.
+ * \param b the second generic pointer to compare.
+ * \returns true if the pointers are the same, false otherwise.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.4.0.
+ *
+ * \sa SDL_CreateHashTable
+ */
+extern bool SDL_KeyMatchPointer(void *unused, const void *a, const void *b);
+
+/**
+ * Generate a hash from a C string.
+ *
+ * The key is intended to be a NULL-terminated string, in UTF-8 format.
+ *
+ * This is intended to be used as one of the callbacks to SDL_CreateHashTable,
+ * if this is useful to the type of keys to be used with the hash table.
+ *
+ * Note that the implementation may change in the future; do not expect
+ * the results to be stable vs future SDL releases. Use this in a hash table
+ * in the current process and don't store them to disk for the future.
+ *
+ * \param unused this parameter is ignored.
+ * \param key the key to hash as a generic pointer.
+ * \returns a 32-bit hash of the key.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.4.0.
+ *
+ * \sa SDL_CreateHashTable
+ */
+extern Uint32 SDL_HashString(void *unused, const void *key);
+
+/**
+ * Compare two C strings as hash table keys.
+ *
+ * Strings will be compared in a case-sensitive manner. More specifically,
+ * they'll be compared as NULL-terminated arrays of bytes.
+ *
+ * This is intended to be used as one of the callbacks to SDL_CreateHashTable,
+ * if this is useful to the type of keys to be used with the hash table.
+ *
+ * \param unused this parameter is ignored.
+ * \param a the first string to compare.
+ * \param b the second string to compare.
+ * \returns true if the strings are the same, false otherwise.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.4.0.
+ *
+ * \sa SDL_CreateHashTable
+ */
+extern bool SDL_KeyMatchString(void *unused, const void *a, const void *b);
+
+/**
+ * Generate a hash from an integer ID.
+ *
+ * The key is intended to a unique integer, possibly within a small range.
+ *
+ * This is intended to be used as one of the callbacks to SDL_CreateHashTable,
+ * if this is useful to the type of keys to be used with the hash table.
+ *
+ * Note that the implementation may change in the future; do not expect
+ * the results to be stable vs future SDL releases. Use this in a hash table
+ * in the current process and don't store them to disk for the future.
+ *
+ * \param unused this parameter is ignored.
+ * \param key the key to hash as a generic pointer.
+ * \returns a 32-bit hash of the key.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.4.0.
+ *
+ * \sa SDL_CreateHashTable
+ */
+extern Uint32 SDL_HashID(void *unused, const void *key);
+
+/**
+ * Compare two integer IDs as hash table keys.
+ *
+ * This is intended to be used as one of the callbacks to SDL_CreateHashTable,
+ * if this is useful to the type of keys to be used with the hash table.
+ *
+ * \param unused this parameter is ignored.
+ * \param a the first ID to compare.
+ * \param b the second ID to compare.
+ * \returns true if the IDs are the same, false otherwise.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.4.0.
+ *
+ * \sa SDL_CreateHashTable
+ */
+extern bool SDL_KeyMatchID(void *unused, const void *a, const void *b);
+
+/**
+ * Free both the key and value pointers of a hash table item.
+ *
+ * This is intended to be used as one of the callbacks to SDL_CreateHashTable,
+ * if this is useful to the type of data to be used with the hash table.
+ *
+ * This literally calls `SDL_free(key);` and `SDL_free(value);`.
+ *
+ * \param unused this parameter is ignored.
+ * \param key the key to be destroyed.
+ * \param value the value to be destroyed.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.4.0.
+ *
+ * \sa SDL_CreateHashTable
+ */
+extern void SDL_DestroyHashKeyAndValue(void *unused, const void *key, const void *value);
+
+/**
+ * Free just the value pointer of a hash table item.
+ *
+ * This is intended to be used as one of the callbacks to SDL_CreateHashTable,
+ * if this is useful to the type of data to be used with the hash table.
+ *
+ * This literally calls `SDL_free(key);` and leaves `value` alone.
+ *
+ * \param unused this parameter is ignored.
+ * \param key the key to be destroyed.
+ * \param value the value to be destroyed.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.4.0.
+ *
+ * \sa SDL_CreateHashTable
+ */
+extern void SDL_DestroyHashKey(void *unused, const void *key, const void *value);
+
+/**
+ * Free just the value pointer of a hash table item.
+ *
+ * This is intended to be used as one of the callbacks to SDL_CreateHashTable,
+ * if this is useful to the type of data to be used with the hash table.
+ *
+ * This literally calls `SDL_free(value);` and leaves `key` alone.
+ *
+ * \param unused this parameter is ignored.
+ * \param key the key to be destroyed.
+ * \param value the value to be destroyed.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.4.0.
+ *
+ * \sa SDL_CreateHashTable
+ */
+extern void SDL_DestroyHashValue(void *unused, const void *key, const void *value);
+
+
+/* Ends C function definitions when using C++ */
+#ifdef __cplusplus
+}
+#endif
+#include <SDL3/SDL_close_code.h>
+
+#endif /* SDL_hashtable_h_ */

+ 403 - 0
thirdparty/sdl/SDL_hints.c

@@ -0,0 +1,403 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#include "SDL_hints_c.h"
+
+#ifdef SDL_PLATFORM_ANDROID
+#include "core/android/SDL_android.h"
+#endif
+
+typedef struct SDL_HintWatch
+{
+    SDL_HintCallback callback;
+    void *userdata;
+    struct SDL_HintWatch *next;
+} SDL_HintWatch;
+
+typedef struct SDL_Hint
+{
+    char *value;
+    SDL_HintPriority priority;
+    SDL_HintWatch *callbacks;
+} SDL_Hint;
+
+static SDL_AtomicU32 SDL_hint_props;
+
+
+void SDL_InitHints(void)
+{
+}
+
+void SDL_QuitHints(void)
+{
+    SDL_PropertiesID props;
+    do {
+        props = SDL_GetAtomicU32(&SDL_hint_props);
+    } while (!SDL_CompareAndSwapAtomicU32(&SDL_hint_props, props, 0));
+
+    if (props) {
+        SDL_DestroyProperties(props);
+    }
+}
+
+static SDL_PropertiesID GetHintProperties(bool create)
+{
+    SDL_PropertiesID props = SDL_GetAtomicU32(&SDL_hint_props);
+    if (!props && create) {
+        props = SDL_CreateProperties();
+        if (!SDL_CompareAndSwapAtomicU32(&SDL_hint_props, 0, props)) {
+            // Somebody else created hint properties before us, just use those
+            SDL_DestroyProperties(props);
+            props = SDL_GetAtomicU32(&SDL_hint_props);
+        }
+    }
+    return props;
+}
+
+static void SDLCALL CleanupHintProperty(void *userdata, void *value)
+{
+    SDL_Hint *hint = (SDL_Hint *) value;
+    SDL_free(hint->value);
+
+    SDL_HintWatch *entry = hint->callbacks;
+    while (entry) {
+        SDL_HintWatch *freeable = entry;
+        entry = entry->next;
+        SDL_free(freeable);
+    }
+    SDL_free(hint);
+}
+
+static const char* GetHintEnvironmentVariable(const char *name)
+{
+    const char *result = SDL_getenv(name);
+    if (!result && name && *name) {
+        // fall back to old (SDL2) names of environment variables that
+        // are important to users (e.g. many use SDL_VIDEODRIVER=wayland)
+        if (SDL_strcmp(name, SDL_HINT_VIDEO_DRIVER) == 0) {
+            result = SDL_getenv("SDL_VIDEODRIVER");
+        } else if (SDL_strcmp(name, SDL_HINT_AUDIO_DRIVER) == 0) {
+            result = SDL_getenv("SDL_AUDIODRIVER");
+        }
+    }
+    return result;
+}
+
+bool SDL_SetHintWithPriority(const char *name, const char *value, SDL_HintPriority priority)
+{
+    if (!name || !*name) {
+        return SDL_InvalidParamError("name");
+    }
+
+    const char *env = GetHintEnvironmentVariable(name);
+    if (env && (priority < SDL_HINT_OVERRIDE)) {
+        return SDL_SetError("An environment variable is taking priority");
+    }
+
+    const SDL_PropertiesID hints = GetHintProperties(true);
+    if (!hints) {
+        return false;
+    }
+
+    bool result = false;
+
+    SDL_LockProperties(hints);
+
+    SDL_Hint *hint = (SDL_Hint *)SDL_GetPointerProperty(hints, name, NULL);
+    if (hint) {
+        if (priority >= hint->priority) {
+            if (hint->value != value && (!value || !hint->value || SDL_strcmp(hint->value, value) != 0)) {
+                char *old_value = hint->value;
+
+                hint->value = value ? SDL_strdup(value) : NULL;
+                SDL_HintWatch *entry = hint->callbacks;
+                while (entry) {
+                    // Save the next entry in case this one is deleted
+                    SDL_HintWatch *next = entry->next;
+                    entry->callback(entry->userdata, name, old_value, value);
+                    entry = next;
+                }
+                SDL_free(old_value);
+            }
+            hint->priority = priority;
+            result = true;
+        }
+    } else {  // Couldn't find the hint? Add a new one.
+        hint = (SDL_Hint *)SDL_malloc(sizeof(*hint));
+        if (hint) {
+            hint->value = value ? SDL_strdup(value) : NULL;
+            hint->priority = priority;
+            hint->callbacks = NULL;
+            result = SDL_SetPointerPropertyWithCleanup(hints, name, hint, CleanupHintProperty, NULL);
+        }
+    }
+
+#ifdef SDL_PLATFORM_ANDROID
+    if (SDL_strcmp(name, SDL_HINT_ANDROID_ALLOW_RECREATE_ACTIVITY) == 0) {
+        // Special handling for this hint, which needs to persist outside the normal application flow
+        Android_SetAllowRecreateActivity(SDL_GetStringBoolean(value, false));
+    }
+#endif // SDL_PLATFORM_ANDROID
+
+    SDL_UnlockProperties(hints);
+
+    return result;
+}
+
+bool SDL_ResetHint(const char *name)
+{
+    if (!name || !*name) {
+        return SDL_InvalidParamError("name");
+    }
+
+    const char *env = GetHintEnvironmentVariable(name);
+
+    const SDL_PropertiesID hints = GetHintProperties(false);
+    if (!hints) {
+        return false;
+    }
+
+    bool result = false;
+
+    SDL_LockProperties(hints);
+
+    SDL_Hint *hint = (SDL_Hint *)SDL_GetPointerProperty(hints, name, NULL);
+    if (hint) {
+        if ((!env && hint->value) || (env && !hint->value) || (env && SDL_strcmp(env, hint->value) != 0)) {
+            for (SDL_HintWatch *entry = hint->callbacks; entry;) {
+                // Save the next entry in case this one is deleted
+                SDL_HintWatch *next = entry->next;
+                entry->callback(entry->userdata, name, hint->value, env);
+                entry = next;
+            }
+        }
+        SDL_free(hint->value);
+        hint->value = NULL;
+        hint->priority = SDL_HINT_DEFAULT;
+        result = true;
+    }
+
+#ifdef SDL_PLATFORM_ANDROID
+    if (SDL_strcmp(name, SDL_HINT_ANDROID_ALLOW_RECREATE_ACTIVITY) == 0) {
+        // Special handling for this hint, which needs to persist outside the normal application flow
+        if (env) {
+            Android_SetAllowRecreateActivity(SDL_GetStringBoolean(env, false));
+        } else {
+            Android_SetAllowRecreateActivity(false);
+        }
+    }
+#endif // SDL_PLATFORM_ANDROID
+
+    SDL_UnlockProperties(hints);
+
+    return result;
+}
+
+static void SDLCALL ResetHintsCallback(void *userdata, SDL_PropertiesID hints, const char *name)
+{
+    SDL_Hint *hint = (SDL_Hint *)SDL_GetPointerProperty(hints, name, NULL);
+    if (!hint) {
+        return;  // uh...okay.
+    }
+
+    const char *env = GetHintEnvironmentVariable(name);
+    if ((!env && hint->value) || (env && !hint->value) || (env && SDL_strcmp(env, hint->value) != 0)) {
+        SDL_HintWatch *entry = hint->callbacks;
+        while (entry) {
+            // Save the next entry in case this one is deleted
+            SDL_HintWatch *next = entry->next;
+            entry->callback(entry->userdata, name, hint->value, env);
+            entry = next;
+        }
+    }
+    SDL_free(hint->value);
+    hint->value = NULL;
+    hint->priority = SDL_HINT_DEFAULT;
+
+#ifdef SDL_PLATFORM_ANDROID
+    if (SDL_strcmp(name, SDL_HINT_ANDROID_ALLOW_RECREATE_ACTIVITY) == 0) {
+        // Special handling for this hint, which needs to persist outside the normal application flow
+        if (env) {
+            Android_SetAllowRecreateActivity(SDL_GetStringBoolean(env, false));
+        } else {
+            Android_SetAllowRecreateActivity(false);
+        }
+    }
+#endif // SDL_PLATFORM_ANDROID
+}
+
+void SDL_ResetHints(void)
+{
+    SDL_EnumerateProperties(GetHintProperties(false), ResetHintsCallback, NULL);
+}
+
+bool SDL_SetHint(const char *name, const char *value)
+{
+    return SDL_SetHintWithPriority(name, value, SDL_HINT_NORMAL);
+}
+
+const char *SDL_GetHint(const char *name)
+{
+    if (!name) {
+        return NULL;
+    }
+
+    const char *result = GetHintEnvironmentVariable(name);
+
+    const SDL_PropertiesID hints = GetHintProperties(false);
+    if (hints) {
+        SDL_LockProperties(hints);
+
+        SDL_Hint *hint = (SDL_Hint *)SDL_GetPointerProperty(hints, name, NULL);
+        if (hint) {
+            if (!result || hint->priority == SDL_HINT_OVERRIDE) {
+                result = SDL_GetPersistentString(hint->value);
+            }
+        }
+
+        SDL_UnlockProperties(hints);
+    }
+
+    return result;
+}
+
+int SDL_GetStringInteger(const char *value, int default_value)
+{
+    if (!value || !*value) {
+        return default_value;
+    }
+    if (SDL_strcasecmp(value, "false") == 0) {
+        return 0;
+    }
+    if (SDL_strcasecmp(value, "true") == 0) {
+        return 1;
+    }
+    if (*value == '-' || SDL_isdigit(*value)) {
+        return SDL_atoi(value);
+    }
+    return default_value;
+}
+
+bool SDL_GetStringBoolean(const char *value, bool default_value)
+{
+    if (!value || !*value) {
+        return default_value;
+    }
+    if (*value == '0' || SDL_strcasecmp(value, "false") == 0) {
+        return false;
+    }
+    return true;
+}
+
+bool SDL_GetHintBoolean(const char *name, bool default_value)
+{
+    const char *hint = SDL_GetHint(name);
+    return SDL_GetStringBoolean(hint, default_value);
+}
+
+bool SDL_AddHintCallback(const char *name, SDL_HintCallback callback, void *userdata)
+{
+    if (!name || !*name) {
+        return SDL_InvalidParamError("name");
+    } else if (!callback) {
+        return SDL_InvalidParamError("callback");
+    }
+
+    const SDL_PropertiesID hints = GetHintProperties(true);
+    if (!hints) {
+        return false;
+    }
+
+    SDL_HintWatch *entry = (SDL_HintWatch *)SDL_malloc(sizeof(*entry));
+    if (!entry) {
+        return false;
+    }
+    entry->callback = callback;
+    entry->userdata = userdata;
+
+    bool result = false;
+
+    SDL_LockProperties(hints);
+
+    SDL_RemoveHintCallback(name, callback, userdata);
+
+    SDL_Hint *hint = (SDL_Hint *)SDL_GetPointerProperty(hints, name, NULL);
+    if (hint) {
+        result = true;
+    } else {  // Need to add a hint entry for this watcher
+        hint = (SDL_Hint *)SDL_malloc(sizeof(*hint));
+        if (!hint) {
+            SDL_free(entry);
+            SDL_UnlockProperties(hints);
+            return false;
+        } else {
+            hint->value = NULL;
+            hint->priority = SDL_HINT_DEFAULT;
+            hint->callbacks = NULL;
+            result = SDL_SetPointerPropertyWithCleanup(hints, name, hint, CleanupHintProperty, NULL);
+        }
+    }
+
+    // Add it to the callbacks for this hint
+    entry->next = hint->callbacks;
+    hint->callbacks = entry;
+
+    // Now call it with the current value
+    const char *value = SDL_GetHint(name);
+    callback(userdata, name, value, value);
+
+    SDL_UnlockProperties(hints);
+
+    return result;
+}
+
+void SDL_RemoveHintCallback(const char *name, SDL_HintCallback callback, void *userdata)
+{
+    if (!name || !*name) {
+        return;
+    }
+
+    const SDL_PropertiesID hints = GetHintProperties(false);
+    if (!hints) {
+        return;
+    }
+
+    SDL_LockProperties(hints);
+    SDL_Hint *hint = (SDL_Hint *)SDL_GetPointerProperty(hints, name, NULL);
+    if (hint) {
+        SDL_HintWatch *prev = NULL;
+        for (SDL_HintWatch *entry = hint->callbacks; entry; entry = entry->next) {
+            if ((callback == entry->callback) && (userdata == entry->userdata)) {
+                if (prev) {
+                    prev->next = entry->next;
+                } else {
+                    hint->callbacks = entry->next;
+                }
+                SDL_free(entry);
+                break;
+            }
+            prev = entry;
+        }
+    }
+    SDL_UnlockProperties(hints);
+}
+

+ 33 - 0
thirdparty/sdl/SDL_hints_c.h

@@ -0,0 +1,33 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+// This file defines useful function for working with SDL hints
+
+#ifndef SDL_hints_c_h_
+#define SDL_hints_c_h_
+
+extern void SDL_InitHints(void);
+extern bool SDL_GetStringBoolean(const char *value, bool default_value);
+extern int SDL_GetStringInteger(const char *value, int default_value);
+extern void SDL_QuitHints(void);
+
+#endif // SDL_hints_c_h_

+ 289 - 0
thirdparty/sdl/SDL_internal.h

@@ -0,0 +1,289 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#ifndef SDL_internal_h_
+#define SDL_internal_h_
+
+// Many of SDL's features require _GNU_SOURCE on various platforms
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+// Need this so Linux systems define fseek64o, ftell64o and off64_t
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE 1
+#endif
+
+/* This is for a variable-length array at the end of a struct:
+    struct x { int y; char z[SDL_VARIABLE_LENGTH_ARRAY]; };
+   Use this because GCC 2 needs different magic than other compilers. */
+#if (defined(__GNUC__) && (__GNUC__ <= 2)) || defined(__CC_ARM) || defined(__cplusplus)
+#define SDL_VARIABLE_LENGTH_ARRAY 1
+#else
+#define SDL_VARIABLE_LENGTH_ARRAY
+#endif
+
+#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) || defined(__clang__)
+#define HAVE_GCC_DIAGNOSTIC_PRAGMA 1
+#endif
+
+#ifdef _MSC_VER // We use constant comparison for generated code
+#pragma warning(disable : 6326)
+#endif
+
+#ifdef _MSC_VER // SDL_MAX_SMALL_ALLOC_STACKSIZE is smaller than _ALLOCA_S_THRESHOLD and should be generally safe
+#pragma warning(disable : 6255)
+#endif
+#define SDL_MAX_SMALL_ALLOC_STACKSIZE          128
+#define SDL_small_alloc(type, count, pisstack) ((*(pisstack) = ((sizeof(type) * (count)) < SDL_MAX_SMALL_ALLOC_STACKSIZE)), (*(pisstack) ? SDL_stack_alloc(type, count) : (type *)SDL_malloc(sizeof(type) * (count))))
+#define SDL_small_free(ptr, isstack) \
+    if ((isstack)) {                 \
+        SDL_stack_free(ptr);         \
+    } else {                         \
+        SDL_free(ptr);               \
+    }
+
+#include "SDL_build_config.h"
+
+//#include "dynapi/SDL_dynapi.h"
+
+#if SDL_DYNAMIC_API
+#include "dynapi/SDL_dynapi_overrides.h"
+/* force SDL_DECLSPEC off...it's all internal symbols now.
+   These will have actual #defines during SDL_dynapi.c only */
+#ifdef SDL_DECLSPEC
+#undef SDL_DECLSPEC
+#endif
+#define SDL_DECLSPEC
+#endif
+
+#ifdef SDL_PLATFORM_APPLE
+#ifndef _DARWIN_C_SOURCE
+#define _DARWIN_C_SOURCE 1 // for memset_pattern4()
+#endif
+#include <Availability.h>
+
+#ifndef __IPHONE_OS_VERSION_MAX_ALLOWED
+#define __IPHONE_OS_VERSION_MAX_ALLOWED 0
+#endif
+#ifndef __APPLETV_OS_VERSION_MAX_ALLOWED
+#define __APPLETV_OS_VERSION_MAX_ALLOWED 0
+#endif
+#ifndef __MAC_OS_X_VERSION_MAX_ALLOWED
+#define __MAC_OS_X_VERSION_MAX_ALLOWED 0
+#endif
+#endif // SDL_PLATFORM_APPLE
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#elif defined(HAVE_MALLOC_H)
+#include <malloc.h>
+#endif
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+#ifdef HAVE_STRING_H
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#elif defined(HAVE_STDINT_H)
+#include <stdint.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+
+// If you run into a warning that O_CLOEXEC is redefined, update the SDL configuration header for your platform to add HAVE_O_CLOEXEC
+#ifndef HAVE_O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+/* A few #defines to reduce SDL footprint.
+   Only effective when library is statically linked. */
+
+/* Optimized functions from 'SDL_blit_0.c'
+   - blit with source bits_per_pixel < 8, palette */
+#if !defined(SDL_HAVE_BLIT_0) && !defined(SDL_LEAN_AND_MEAN)
+#define SDL_HAVE_BLIT_0 1
+#endif
+
+/* Optimized functions from 'SDL_blit_1.c'
+   - blit with source bytes_per_pixel == 1, palette */
+#if !defined(SDL_HAVE_BLIT_1) && !defined(SDL_LEAN_AND_MEAN)
+#define SDL_HAVE_BLIT_1 1
+#endif
+
+/* Optimized functions from 'SDL_blit_A.c'
+   - blit with 'SDL_BLENDMODE_BLEND' blending mode */
+#if !defined(SDL_HAVE_BLIT_A) && !defined(SDL_LEAN_AND_MEAN)
+#define SDL_HAVE_BLIT_A 1
+#endif
+
+/* Optimized functions from 'SDL_blit_N.c'
+   - blit with COLORKEY mode, or nothing */
+#if !defined(SDL_HAVE_BLIT_N) && !defined(SDL_LEAN_AND_MEAN)
+#define SDL_HAVE_BLIT_N 1
+#endif
+
+/* Optimized functions from 'SDL_blit_N.c'
+   - RGB565 conversion with Lookup tables */
+#if !defined(SDL_HAVE_BLIT_N_RGB565) && !defined(SDL_LEAN_AND_MEAN)
+#define SDL_HAVE_BLIT_N_RGB565 1
+#endif
+
+/* Optimized functions from 'SDL_blit_AUTO.c'
+   - blit with modulate color, modulate alpha, any blending mode
+   - scaling or not */
+#if !defined(SDL_HAVE_BLIT_AUTO) && !defined(SDL_LEAN_AND_MEAN)
+#define SDL_HAVE_BLIT_AUTO 1
+#endif
+
+/* Run-Length-Encoding
+   - SDL_SetSurfaceColorKey() called with SDL_RLEACCEL flag */
+#if !defined(SDL_HAVE_RLE) && !defined(SDL_LEAN_AND_MEAN)
+#define SDL_HAVE_RLE 1
+#endif
+
+/* Software SDL_Renderer
+   - creation of software renderer
+   - *not* general blitting functions
+   - {blend,draw}{fillrect,line,point} internal functions */
+#if !defined(SDL_VIDEO_RENDER_SW) && !defined(SDL_LEAN_AND_MEAN)
+#define SDL_VIDEO_RENDER_SW 1
+#endif
+
+/* STB image conversion */
+#if !defined(SDL_HAVE_STB) && !defined(SDL_LEAN_AND_MEAN)
+#define SDL_HAVE_STB 1
+#endif
+
+/* YUV formats
+   - handling of YUV surfaces
+   - blitting and conversion functions */
+#if !defined(SDL_HAVE_YUV) && !defined(SDL_LEAN_AND_MEAN)
+#define SDL_HAVE_YUV 1
+#endif
+
+#ifdef SDL_CAMERA_DISABLED
+#undef SDL_CAMERA_DRIVER_ANDROID
+#undef SDL_CAMERA_DRIVER_COREMEDIA
+#undef SDL_CAMERA_DRIVER_DUMMY
+#undef SDL_CAMERA_DRIVER_EMSCRIPTEN
+#undef SDL_CAMERA_DRIVER_MEDIAFOUNDATION
+#undef SDL_CAMERA_DRIVER_PIPEWIRE
+#undef SDL_CAMERA_DRIVER_V4L2
+#undef SDL_CAMERA_DRIVER_VITA
+#endif
+
+#ifdef SDL_RENDER_DISABLED
+#undef SDL_VIDEO_RENDER_SW
+#undef SDL_VIDEO_RENDER_D3D
+#undef SDL_VIDEO_RENDER_D3D11
+#undef SDL_VIDEO_RENDER_D3D12
+#undef SDL_VIDEO_RENDER_GPU
+#undef SDL_VIDEO_RENDER_METAL
+#undef SDL_VIDEO_RENDER_OGL
+#undef SDL_VIDEO_RENDER_OGL_ES2
+#undef SDL_VIDEO_RENDER_PS2
+#undef SDL_VIDEO_RENDER_PSP
+#undef SDL_VIDEO_RENDER_VITA_GXM
+#undef SDL_VIDEO_RENDER_VULKAN
+#endif // SDL_RENDER_DISABLED
+
+#ifdef SDL_GPU_DISABLED
+#undef SDL_GPU_D3D12
+#undef SDL_GPU_METAL
+#undef SDL_GPU_VULKAN
+#undef SDL_VIDEO_RENDER_GPU
+#endif // SDL_GPU_DISABLED
+
+#if !defined(HAVE_LIBC)
+// If not using _any_ C runtime, these have to be defined before SDL_thread.h
+// gets included, so internal SDL_CreateThread calls will not try to reference
+// the (unavailable and unneeded) _beginthreadex/_endthreadex functions.
+#define SDL_BeginThreadFunction NULL
+#define SDL_EndThreadFunction NULL
+#endif
+
+#ifdef SDL_NOLONGLONG
+#error We cannot build a valid SDL3 library without long long support
+#endif
+
+/* Enable internal definitions in SDL API headers */
+#define SDL_INTERNAL
+
+#include <SDL3/SDL.h>
+#include <SDL3/SDL_intrin.h>
+
+#define SDL_MAIN_NOIMPL // don't drag in header-only implementation of SDL_main
+#include <SDL3/SDL_main.h>
+
+// Set up for C function definitions, even when using C++
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "SDL_utils_c.h"
+#include "SDL_hashtable.h"
+
+#define PUSH_SDL_ERROR() \
+    { char *_error = SDL_strdup(SDL_GetError());
+
+#define POP_SDL_ERROR() \
+    SDL_SetError("%s", _error); SDL_free(_error); }
+
+// Do any initialization that needs to happen before threads are started
+extern void SDL_InitMainThread(void);
+
+/* The internal implementations of these functions have up to nanosecond precision.
+   We can expose these functions as part of the API if we want to later.
+*/
+extern bool SDLCALL SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS);
+extern bool SDLCALL SDL_WaitConditionTimeoutNS(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS);
+extern bool SDLCALL SDL_WaitEventTimeoutNS(SDL_Event *event, Sint64 timeoutNS);
+
+// Ends C function definitions when using C++
+#ifdef __cplusplus
+}
+#endif
+
+#endif // SDL_internal_h_

+ 86 - 0
thirdparty/sdl/SDL_list.c

@@ -0,0 +1,86 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#include "./SDL_list.h"
+
+// Push
+bool SDL_ListAdd(SDL_ListNode **head, void *ent)
+{
+    SDL_ListNode *node = (SDL_ListNode *)SDL_malloc(sizeof(*node));
+
+    if (!node) {
+        return false;
+    }
+
+    node->entry = ent;
+    node->next = *head;
+    *head = node;
+    return true;
+}
+
+// Pop from end as a FIFO (if add with SDL_ListAdd)
+void SDL_ListPop(SDL_ListNode **head, void **ent)
+{
+    SDL_ListNode **ptr = head;
+
+    // Invalid or empty
+    if (!head || !*head) {
+        return;
+    }
+
+    while ((*ptr)->next) {
+        ptr = &(*ptr)->next;
+    }
+
+    if (ent) {
+        *ent = (*ptr)->entry;
+    }
+
+    SDL_free(*ptr);
+    *ptr = NULL;
+}
+
+void SDL_ListRemove(SDL_ListNode **head, void *ent)
+{
+    SDL_ListNode **ptr = head;
+
+    while (*ptr) {
+        if ((*ptr)->entry == ent) {
+            SDL_ListNode *tmp = *ptr;
+            *ptr = (*ptr)->next;
+            SDL_free(tmp);
+            return;
+        }
+        ptr = &(*ptr)->next;
+    }
+}
+
+void SDL_ListClear(SDL_ListNode **head)
+{
+    SDL_ListNode *l = *head;
+    *head = NULL;
+    while (l) {
+        SDL_ListNode *tmp = l;
+        l = l->next;
+        SDL_free(tmp);
+    }
+}

+ 36 - 0
thirdparty/sdl/SDL_list.h

@@ -0,0 +1,36 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#ifndef SDL_list_h_
+#define SDL_list_h_
+
+typedef struct SDL_ListNode
+{
+    void *entry;
+    struct SDL_ListNode *next;
+} SDL_ListNode;
+
+bool SDL_ListAdd(SDL_ListNode **head, void *ent);
+void SDL_ListPop(SDL_ListNode **head, void **ent);
+void SDL_ListRemove(SDL_ListNode **head, void *ent);
+void SDL_ListClear(SDL_ListNode **head);
+
+#endif // SDL_list_h_

+ 805 - 0
thirdparty/sdl/SDL_log.c

@@ -0,0 +1,805 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#if defined(SDL_PLATFORM_WINDOWS)
+#include "core/windows/SDL_windows.h"
+#endif
+
+// Simple log messages in SDL
+
+#include "SDL_log_c.h"
+
+#ifdef HAVE_STDIO_H
+#include <stdio.h>
+#endif
+
+#ifdef SDL_PLATFORM_ANDROID
+#include <android/log.h>
+#endif
+
+#include "stdlib/SDL_vacopy.h"
+
+// The size of the stack buffer to use for rendering log messages.
+#define SDL_MAX_LOG_MESSAGE_STACK 256
+
+#define DEFAULT_CATEGORY -1
+
+typedef struct SDL_LogLevel
+{
+    int category;
+    SDL_LogPriority priority;
+    struct SDL_LogLevel *next;
+} SDL_LogLevel;
+
+
+// The default log output function
+static void SDLCALL SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority, const char *message);
+
+static void CleanupLogPriorities(void);
+static void CleanupLogPrefixes(void);
+
+static SDL_InitState SDL_log_init;
+static SDL_Mutex *SDL_log_lock;
+static SDL_Mutex *SDL_log_function_lock;
+static SDL_LogLevel *SDL_loglevels SDL_GUARDED_BY(SDL_log_lock);
+static SDL_LogPriority SDL_log_priorities[SDL_LOG_CATEGORY_CUSTOM] SDL_GUARDED_BY(SDL_log_lock);
+static SDL_LogPriority SDL_log_default_priority SDL_GUARDED_BY(SDL_log_lock);
+static SDL_LogOutputFunction SDL_log_function SDL_GUARDED_BY(SDL_log_function_lock) = SDL_LogOutput;
+static void *SDL_log_userdata SDL_GUARDED_BY(SDL_log_function_lock) = NULL;
+
+#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#endif
+
+// If this list changes, update the documentation for SDL_HINT_LOGGING
+static const char * const SDL_priority_names[] = {
+    NULL,
+    "TRACE",
+    "VERBOSE",
+    "DEBUG",
+    "INFO",
+    "WARN",
+    "ERROR",
+    "CRITICAL"
+};
+SDL_COMPILE_TIME_ASSERT(priority_names, SDL_arraysize(SDL_priority_names) == SDL_LOG_PRIORITY_COUNT);
+
+// This is guarded by SDL_log_function_lock because it's the logging function that calls GetLogPriorityPrefix()
+static char *SDL_priority_prefixes[SDL_LOG_PRIORITY_COUNT] SDL_GUARDED_BY(SDL_log_function_lock);
+
+// If this list changes, update the documentation for SDL_HINT_LOGGING
+static const char * const SDL_category_names[] = {
+    "APP",
+    "ERROR",
+    "ASSERT",
+    "SYSTEM",
+    "AUDIO",
+    "VIDEO",
+    "RENDER",
+    "INPUT",
+    "TEST",
+    "GPU"
+};
+SDL_COMPILE_TIME_ASSERT(category_names, SDL_arraysize(SDL_category_names) == SDL_LOG_CATEGORY_RESERVED2);
+
+#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
+#pragma GCC diagnostic pop
+#endif
+
+#ifdef SDL_PLATFORM_ANDROID
+static int SDL_android_priority[] = {
+    ANDROID_LOG_UNKNOWN,
+    ANDROID_LOG_VERBOSE,
+    ANDROID_LOG_VERBOSE,
+    ANDROID_LOG_DEBUG,
+    ANDROID_LOG_INFO,
+    ANDROID_LOG_WARN,
+    ANDROID_LOG_ERROR,
+    ANDROID_LOG_FATAL
+};
+SDL_COMPILE_TIME_ASSERT(android_priority, SDL_arraysize(SDL_android_priority) == SDL_LOG_PRIORITY_COUNT);
+#endif // SDL_PLATFORM_ANDROID
+
+static void SDLCALL SDL_LoggingChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
+{
+    SDL_ResetLogPriorities();
+}
+
+void SDL_InitLog(void)
+{
+    if (!SDL_ShouldInit(&SDL_log_init)) {
+        return;
+    }
+
+    // If these fail we'll continue without them.
+    SDL_log_lock = SDL_CreateMutex();
+    SDL_log_function_lock = SDL_CreateMutex();
+
+    SDL_AddHintCallback(SDL_HINT_LOGGING, SDL_LoggingChanged, NULL);
+
+    SDL_SetInitialized(&SDL_log_init, true);
+}
+
+void SDL_QuitLog(void)
+{
+    if (!SDL_ShouldQuit(&SDL_log_init)) {
+        return;
+    }
+
+    SDL_RemoveHintCallback(SDL_HINT_LOGGING, SDL_LoggingChanged, NULL);
+
+    CleanupLogPriorities();
+    CleanupLogPrefixes();
+
+    if (SDL_log_lock) {
+        SDL_DestroyMutex(SDL_log_lock);
+        SDL_log_lock = NULL;
+    }
+    if (SDL_log_function_lock) {
+        SDL_DestroyMutex(SDL_log_function_lock);
+        SDL_log_function_lock = NULL;
+    }
+
+    SDL_SetInitialized(&SDL_log_init, false);
+}
+
+static void SDL_CheckInitLog(void)
+{
+    int status = SDL_GetAtomicInt(&SDL_log_init.status);
+    if (status == SDL_INIT_STATUS_INITIALIZED ||
+        (status == SDL_INIT_STATUS_INITIALIZING && SDL_log_init.thread == SDL_GetCurrentThreadID())) {
+        return;
+    }
+
+    SDL_InitLog();
+}
+
+static void CleanupLogPriorities(void)
+{
+    while (SDL_loglevels) {
+        SDL_LogLevel *entry = SDL_loglevels;
+        SDL_loglevels = entry->next;
+        SDL_free(entry);
+    }
+}
+
+void SDL_SetLogPriorities(SDL_LogPriority priority)
+{
+    SDL_CheckInitLog();
+
+    SDL_LockMutex(SDL_log_lock);
+    {
+        CleanupLogPriorities();
+
+        SDL_log_default_priority = priority;
+        for (int i = 0; i < SDL_arraysize(SDL_log_priorities); ++i) {
+            SDL_log_priorities[i] = priority;
+        }
+    }
+    SDL_UnlockMutex(SDL_log_lock);
+}
+
+void SDL_SetLogPriority(int category, SDL_LogPriority priority)
+{
+    SDL_LogLevel *entry;
+
+    SDL_CheckInitLog();
+
+    SDL_LockMutex(SDL_log_lock);
+    {
+        if (category >= 0 && category < SDL_arraysize(SDL_log_priorities)) {
+            SDL_log_priorities[category] = priority;
+        } else {
+            for (entry = SDL_loglevels; entry; entry = entry->next) {
+                if (entry->category == category) {
+                    entry->priority = priority;
+                    break;
+                }
+            }
+
+            if (!entry) {
+                entry = (SDL_LogLevel *)SDL_malloc(sizeof(*entry));
+                if (entry) {
+                    entry->category = category;
+                    entry->priority = priority;
+                    entry->next = SDL_loglevels;
+                    SDL_loglevels = entry;
+                }
+            }
+        }
+    }
+    SDL_UnlockMutex(SDL_log_lock);
+}
+
+SDL_LogPriority SDL_GetLogPriority(int category)
+{
+    SDL_LogLevel *entry;
+    SDL_LogPriority priority = SDL_LOG_PRIORITY_INVALID;
+
+    SDL_CheckInitLog();
+
+    // Bypass the lock for known categories
+    // Technically if the priority was set on a different CPU the value might not
+    // be visible on this CPU for a while, but in practice it's fast enough that
+    // this performance improvement is worthwhile.
+    if (category >= 0 && category < SDL_arraysize(SDL_log_priorities)) {
+        return SDL_log_priorities[category];
+    }
+
+    SDL_LockMutex(SDL_log_lock);
+    {
+        if (category >= 0 && category < SDL_arraysize(SDL_log_priorities)) {
+            priority = SDL_log_priorities[category];
+        } else {
+            for (entry = SDL_loglevels; entry; entry = entry->next) {
+                if (entry->category == category) {
+                    priority = entry->priority;
+                    break;
+                }
+            }
+            if (priority == SDL_LOG_PRIORITY_INVALID) {
+                priority = SDL_log_default_priority;
+            }
+        }
+    }
+    SDL_UnlockMutex(SDL_log_lock);
+
+    return priority;
+}
+
+static bool ParseLogCategory(const char *string, size_t length, int *category)
+{
+    int i;
+
+    if (SDL_isdigit(*string)) {
+        *category = SDL_atoi(string);
+        return true;
+    }
+
+    if (*string == '*') {
+        *category = DEFAULT_CATEGORY;
+        return true;
+    }
+
+    for (i = 0; i < SDL_arraysize(SDL_category_names); ++i) {
+        if (SDL_strncasecmp(string, SDL_category_names[i], length) == 0) {
+            *category = i;
+            return true;
+        }
+    }
+    return false;
+}
+
+static bool ParseLogPriority(const char *string, size_t length, SDL_LogPriority *priority)
+{
+    int i;
+
+    if (SDL_isdigit(*string)) {
+        i = SDL_atoi(string);
+        if (i == 0) {
+            // 0 has a special meaning of "disable this category"
+            *priority = SDL_LOG_PRIORITY_COUNT;
+            return true;
+        }
+        if (i > SDL_LOG_PRIORITY_INVALID && i < SDL_LOG_PRIORITY_COUNT) {
+            *priority = (SDL_LogPriority)i;
+            return true;
+        }
+        return false;
+    }
+
+    if (SDL_strncasecmp(string, "quiet", length) == 0) {
+        *priority = SDL_LOG_PRIORITY_COUNT;
+        return true;
+    }
+
+    for (i = SDL_LOG_PRIORITY_INVALID + 1; i < SDL_LOG_PRIORITY_COUNT; ++i) {
+        if (SDL_strncasecmp(string, SDL_priority_names[i], length) == 0) {
+            *priority = (SDL_LogPriority)i;
+            return true;
+        }
+    }
+    return false;
+}
+
+static void ParseLogPriorities(const char *hint)
+{
+    const char *name, *next;
+    int category = DEFAULT_CATEGORY;
+    SDL_LogPriority priority = SDL_LOG_PRIORITY_INVALID;
+
+    if (SDL_strchr(hint, '=') == NULL) {
+        if (ParseLogPriority(hint, SDL_strlen(hint), &priority)) {
+            SDL_SetLogPriorities(priority);
+        }
+        return;
+    }
+
+    for (name = hint; name; name = next) {
+        const char *sep = SDL_strchr(name, '=');
+        if (!sep) {
+            break;
+        }
+        next = SDL_strchr(sep, ',');
+        if (next) {
+            ++next;
+        }
+
+        if (ParseLogCategory(name, (sep - name), &category)) {
+            const char *value = sep + 1;
+            size_t len;
+            if (next) {
+                len = (next - value - 1);
+            } else {
+                len = SDL_strlen(value);
+            }
+            if (ParseLogPriority(value, len, &priority)) {
+                if (category == DEFAULT_CATEGORY) {
+                    for (int i = 0; i < SDL_arraysize(SDL_log_priorities); ++i) {
+                        if (SDL_log_priorities[i] == SDL_LOG_PRIORITY_INVALID) {
+                            SDL_log_priorities[i] = priority;
+                        }
+                    }
+                    SDL_log_default_priority = priority;
+                } else {
+                    SDL_SetLogPriority(category, priority);
+                }
+            }
+        }
+    }
+}
+
+void SDL_ResetLogPriorities(void)
+{
+    SDL_CheckInitLog();
+
+    SDL_LockMutex(SDL_log_lock);
+    {
+        CleanupLogPriorities();
+
+        SDL_log_default_priority = SDL_LOG_PRIORITY_INVALID;
+        for (int i = 0; i < SDL_arraysize(SDL_log_priorities); ++i) {
+            SDL_log_priorities[i] = SDL_LOG_PRIORITY_INVALID;
+        }
+
+        const char *hint = SDL_GetHint(SDL_HINT_LOGGING);
+        if (hint) {
+            ParseLogPriorities(hint);
+        }
+
+        if (SDL_log_default_priority == SDL_LOG_PRIORITY_INVALID) {
+            SDL_log_default_priority = SDL_LOG_PRIORITY_ERROR;
+        }
+        for (int i = 0; i < SDL_arraysize(SDL_log_priorities); ++i) {
+            if (SDL_log_priorities[i] != SDL_LOG_PRIORITY_INVALID) {
+                continue;
+            }
+
+            switch (i) {
+            case SDL_LOG_CATEGORY_APPLICATION:
+                SDL_log_priorities[i] = SDL_LOG_PRIORITY_INFO;
+                break;
+            case SDL_LOG_CATEGORY_ASSERT:
+                SDL_log_priorities[i] = SDL_LOG_PRIORITY_WARN;
+                break;
+            case SDL_LOG_CATEGORY_TEST:
+                SDL_log_priorities[i] = SDL_LOG_PRIORITY_VERBOSE;
+                break;
+            default:
+                SDL_log_priorities[i] = SDL_LOG_PRIORITY_ERROR;
+                break;
+            }
+        }
+    }
+    SDL_UnlockMutex(SDL_log_lock);
+}
+
+static void CleanupLogPrefixes(void)
+{
+    for (int i = 0; i < SDL_arraysize(SDL_priority_prefixes); ++i) {
+        if (SDL_priority_prefixes[i]) {
+            SDL_free(SDL_priority_prefixes[i]);
+            SDL_priority_prefixes[i] = NULL;
+        }
+    }
+}
+
+static const char *GetLogPriorityPrefix(SDL_LogPriority priority)
+{
+    if (priority <= SDL_LOG_PRIORITY_INVALID || priority >= SDL_LOG_PRIORITY_COUNT) {
+        return "";
+    }
+
+    if (SDL_priority_prefixes[priority]) {
+        return SDL_priority_prefixes[priority];
+    }
+
+    switch (priority) {
+    case SDL_LOG_PRIORITY_WARN:
+        return "WARNING: ";
+    case SDL_LOG_PRIORITY_ERROR:
+        return "ERROR: ";
+    case SDL_LOG_PRIORITY_CRITICAL:
+        return "ERROR: ";
+    default:
+        return "";
+    }
+}
+
+bool SDL_SetLogPriorityPrefix(SDL_LogPriority priority, const char *prefix)
+{
+    char *prefix_copy;
+
+    if (priority <= SDL_LOG_PRIORITY_INVALID || priority >= SDL_LOG_PRIORITY_COUNT) {
+        return SDL_InvalidParamError("priority");
+    }
+
+    if (!prefix || !*prefix) {
+        prefix_copy = SDL_strdup("");
+    } else {
+        prefix_copy = SDL_strdup(prefix);
+    }
+    if (!prefix_copy) {
+        return false;
+    }
+
+    SDL_LockMutex(SDL_log_function_lock);
+    {
+        if (SDL_priority_prefixes[priority]) {
+            SDL_free(SDL_priority_prefixes[priority]);
+        }
+        SDL_priority_prefixes[priority] = prefix_copy;
+    }
+    SDL_UnlockMutex(SDL_log_function_lock);
+
+    return true;
+}
+
+void SDL_Log(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, ap);
+    va_end(ap);
+}
+
+void SDL_LogTrace(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    SDL_LogMessageV(category, SDL_LOG_PRIORITY_TRACE, fmt, ap);
+    va_end(ap);
+}
+
+void SDL_LogVerbose(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    SDL_LogMessageV(category, SDL_LOG_PRIORITY_VERBOSE, fmt, ap);
+    va_end(ap);
+}
+
+void SDL_LogDebug(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    SDL_LogMessageV(category, SDL_LOG_PRIORITY_DEBUG, fmt, ap);
+    va_end(ap);
+}
+
+void SDL_LogInfo(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    SDL_LogMessageV(category, SDL_LOG_PRIORITY_INFO, fmt, ap);
+    va_end(ap);
+}
+
+void SDL_LogWarn(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    SDL_LogMessageV(category, SDL_LOG_PRIORITY_WARN, fmt, ap);
+    va_end(ap);
+}
+
+void SDL_LogError(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    SDL_LogMessageV(category, SDL_LOG_PRIORITY_ERROR, fmt, ap);
+    va_end(ap);
+}
+
+void SDL_LogCritical(int category, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    SDL_LogMessageV(category, SDL_LOG_PRIORITY_CRITICAL, fmt, ap);
+    va_end(ap);
+}
+
+void SDL_LogMessage(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    SDL_LogMessageV(category, priority, fmt, ap);
+    va_end(ap);
+}
+
+#ifdef SDL_PLATFORM_ANDROID
+static const char *GetCategoryPrefix(int category)
+{
+    if (category < SDL_LOG_CATEGORY_RESERVED2) {
+        return SDL_category_names[category];
+    }
+    if (category < SDL_LOG_CATEGORY_CUSTOM) {
+        return "RESERVED";
+    }
+    return "CUSTOM";
+}
+#endif // SDL_PLATFORM_ANDROID
+
+void SDL_LogMessageV(int category, SDL_LogPriority priority, SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap)
+{
+    char *message = NULL;
+    char stack_buf[SDL_MAX_LOG_MESSAGE_STACK];
+    size_t len_plus_term;
+    int len;
+    va_list aq;
+
+    // Nothing to do if we don't have an output function
+    if (!SDL_log_function) {
+        return;
+    }
+
+    // See if we want to do anything with this message
+    if (priority < SDL_GetLogPriority(category)) {
+        return;
+    }
+
+    // Render into stack buffer
+    va_copy(aq, ap);
+    len = SDL_vsnprintf(stack_buf, sizeof(stack_buf), fmt, aq);
+    va_end(aq);
+
+    if (len < 0) {
+        return;
+    }
+
+    // If message truncated, allocate and re-render
+    if (len >= sizeof(stack_buf) && SDL_size_add_check_overflow(len, 1, &len_plus_term)) {
+        // Allocate exactly what we need, including the zero-terminator
+        message = (char *)SDL_malloc(len_plus_term);
+        if (!message) {
+            return;
+        }
+        va_copy(aq, ap);
+        len = SDL_vsnprintf(message, len_plus_term, fmt, aq);
+        va_end(aq);
+    } else {
+        message = stack_buf;
+    }
+
+    // Chop off final endline.
+    if ((len > 0) && (message[len - 1] == '\n')) {
+        message[--len] = '\0';
+        if ((len > 0) && (message[len - 1] == '\r')) { // catch "\r\n", too.
+            message[--len] = '\0';
+        }
+    }
+
+    SDL_LockMutex(SDL_log_function_lock);
+    {
+        SDL_log_function(SDL_log_userdata, category, priority, message);
+    }
+    SDL_UnlockMutex(SDL_log_function_lock);
+
+    // Free only if dynamically allocated
+    if (message != stack_buf) {
+        SDL_free(message);
+    }
+}
+
+#if defined(SDL_PLATFORM_WIN32) && !defined(SDL_PLATFORM_GDK)
+enum {
+    CONSOLE_UNATTACHED = 0,
+    CONSOLE_ATTACHED_CONSOLE = 1,
+    CONSOLE_ATTACHED_FILE = 2,
+    CONSOLE_ATTACHED_ERROR = -1,
+} consoleAttached = CONSOLE_UNATTACHED;
+
+// Handle to stderr output of console.
+static HANDLE stderrHandle = NULL;
+#endif
+
+static void SDLCALL SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority,
+                                  const char *message)
+{
+#if defined(SDL_PLATFORM_WINDOWS)
+    // Way too many allocations here, urgh
+    // Note: One can't call SDL_SetError here, since that function itself logs.
+    {
+        char *output;
+        size_t length;
+        LPTSTR tstr;
+        bool isstack;
+
+#if !defined(SDL_PLATFORM_GDK)
+        BOOL attachResult;
+        DWORD attachError;
+        DWORD consoleMode;
+        DWORD charsWritten;
+
+        // Maybe attach console and get stderr handle
+        if (consoleAttached == CONSOLE_UNATTACHED) {
+            attachResult = AttachConsole(ATTACH_PARENT_PROCESS);
+            if (!attachResult) {
+                attachError = GetLastError();
+                if (attachError == ERROR_INVALID_HANDLE) {
+                    // This is expected when running from Visual Studio
+                    // OutputDebugString(TEXT("Parent process has no console\r\n"));
+                    consoleAttached = CONSOLE_ATTACHED_ERROR;
+                } else if (attachError == ERROR_GEN_FAILURE) {
+                    OutputDebugString(TEXT("Could not attach to console of parent process\r\n"));
+                    consoleAttached = CONSOLE_ATTACHED_ERROR;
+                } else if (attachError == ERROR_ACCESS_DENIED) {
+                    // Already attached
+                    consoleAttached = CONSOLE_ATTACHED_CONSOLE;
+                } else {
+                    OutputDebugString(TEXT("Error attaching console\r\n"));
+                    consoleAttached = CONSOLE_ATTACHED_ERROR;
+                }
+            } else {
+                // Newly attached
+                consoleAttached = CONSOLE_ATTACHED_CONSOLE;
+            }
+
+            if (consoleAttached == CONSOLE_ATTACHED_CONSOLE) {
+                stderrHandle = GetStdHandle(STD_ERROR_HANDLE);
+
+                if (GetConsoleMode(stderrHandle, &consoleMode) == 0) {
+                    // WriteConsole fails if the output is redirected to a file. Must use WriteFile instead.
+                    consoleAttached = CONSOLE_ATTACHED_FILE;
+                }
+            }
+        }
+#endif // !defined(SDL_PLATFORM_GDK)
+        length = SDL_strlen(GetLogPriorityPrefix(priority)) + SDL_strlen(message) + 1 + 1 + 1;
+        output = SDL_small_alloc(char, length, &isstack);
+        if (!output) {
+            return;
+        }
+        (void)SDL_snprintf(output, length, "%s%s\r\n", GetLogPriorityPrefix(priority), message);
+        tstr = WIN_UTF8ToString(output);
+
+        // Output to debugger
+        OutputDebugString(tstr);
+
+#if !defined(SDL_PLATFORM_GDK)
+        // Screen output to stderr, if console was attached.
+        if (consoleAttached == CONSOLE_ATTACHED_CONSOLE) {
+            if (!WriteConsole(stderrHandle, tstr, (DWORD)SDL_tcslen(tstr), &charsWritten, NULL)) {
+                OutputDebugString(TEXT("Error calling WriteConsole\r\n"));
+                if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {
+                    OutputDebugString(TEXT("Insufficient heap memory to write message\r\n"));
+                }
+            }
+
+        } else if (consoleAttached == CONSOLE_ATTACHED_FILE) {
+            if (!WriteFile(stderrHandle, output, (DWORD)SDL_strlen(output), &charsWritten, NULL)) {
+                OutputDebugString(TEXT("Error calling WriteFile\r\n"));
+            }
+        }
+#endif // !defined(SDL_PLATFORM_GDK)
+
+        SDL_free(tstr);
+        SDL_small_free(output, isstack);
+    }
+#elif defined(SDL_PLATFORM_ANDROID)
+    {
+        char tag[32];
+
+        SDL_snprintf(tag, SDL_arraysize(tag), "SDL/%s", GetCategoryPrefix(category));
+        __android_log_write(SDL_android_priority[priority], tag, message);
+    }
+#elif defined(SDL_PLATFORM_APPLE) && (defined(SDL_VIDEO_DRIVER_COCOA) || defined(SDL_VIDEO_DRIVER_UIKIT))
+    /* Technically we don't need Cocoa/UIKit, but that's where this function is defined for now.
+     */
+    extern void SDL_NSLog(const char *prefix, const char *text);
+    {
+        SDL_NSLog(GetLogPriorityPrefix(priority), message);
+        return;
+    }
+#elif defined(SDL_PLATFORM_PSP) || defined(SDL_PLATFORM_PS2)
+    {
+        FILE *pFile;
+        pFile = fopen("SDL_Log.txt", "a");
+        if (pFile) {
+            (void)fprintf(pFile, "%s%s\n", GetLogPriorityPrefix(priority), message);
+            (void)fclose(pFile);
+        }
+    }
+#elif defined(SDL_PLATFORM_VITA)
+    {
+        FILE *pFile;
+        pFile = fopen("ux0:/data/SDL_Log.txt", "a");
+        if (pFile) {
+            (void)fprintf(pFile, "%s%s\n", GetLogPriorityPrefix(priority), message);
+            (void)fclose(pFile);
+        }
+    }
+#elif defined(SDL_PLATFORM_3DS)
+    {
+        FILE *pFile;
+        pFile = fopen("sdmc:/3ds/SDL_Log.txt", "a");
+        if (pFile) {
+            (void)fprintf(pFile, "%s%s\n", GetLogPriorityPrefix(priority), message);
+            (void)fclose(pFile);
+        }
+    }
+#endif
+#if defined(HAVE_STDIO_H) && \
+    !(defined(SDL_PLATFORM_APPLE) && (defined(SDL_VIDEO_DRIVER_COCOA) || defined(SDL_VIDEO_DRIVER_UIKIT))) && \
+    !(defined(SDL_PLATFORM_WIN32))
+    (void)fprintf(stderr, "%s%s\n", GetLogPriorityPrefix(priority), message);
+#endif
+}
+
+SDL_LogOutputFunction SDL_GetDefaultLogOutputFunction(void)
+{
+    return SDL_LogOutput;
+}
+
+void SDL_GetLogOutputFunction(SDL_LogOutputFunction *callback, void **userdata)
+{
+    SDL_LockMutex(SDL_log_function_lock);
+    {
+        if (callback) {
+            *callback = SDL_log_function;
+        }
+        if (userdata) {
+            *userdata = SDL_log_userdata;
+        }
+    }
+    SDL_UnlockMutex(SDL_log_function_lock);
+}
+
+void SDL_SetLogOutputFunction(SDL_LogOutputFunction callback, void *userdata)
+{
+    SDL_LockMutex(SDL_log_function_lock);
+    {
+        SDL_log_function = callback;
+        SDL_log_userdata = userdata;
+    }
+    SDL_UnlockMutex(SDL_log_function_lock);
+}

+ 31 - 0
thirdparty/sdl/SDL_log_c.h

@@ -0,0 +1,31 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+// This file defines useful function for working with SDL logging
+
+#ifndef SDL_log_c_h_
+#define SDL_log_c_h_
+
+extern void SDL_InitLog(void);
+extern void SDL_QuitLog(void);
+
+#endif // SDL_log_c_h_

+ 824 - 0
thirdparty/sdl/SDL_properties.c

@@ -0,0 +1,824 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#include "SDL_hints_c.h"
+#include "SDL_properties_c.h"
+
+
+typedef struct
+{
+    SDL_PropertyType type;
+
+    union {
+        void *pointer_value;
+        char *string_value;
+        Sint64 number_value;
+        float float_value;
+        bool boolean_value;
+    } value;
+
+    char *string_storage;
+
+    SDL_CleanupPropertyCallback cleanup;
+    void *userdata;
+} SDL_Property;
+
+typedef struct
+{
+    SDL_HashTable *props;
+    SDL_Mutex *lock;
+} SDL_Properties;
+
+static SDL_InitState SDL_properties_init;
+static SDL_HashTable *SDL_properties;
+static SDL_AtomicU32 SDL_last_properties_id;
+static SDL_AtomicU32 SDL_global_properties;
+
+
+static void SDL_FreePropertyWithCleanup(const void *key, const void *value, void *data, bool cleanup)
+{
+    SDL_Property *property = (SDL_Property *)value;
+    if (property) {
+        switch (property->type) {
+        case SDL_PROPERTY_TYPE_POINTER:
+            if (property->cleanup && cleanup) {
+                property->cleanup(property->userdata, property->value.pointer_value);
+            }
+            break;
+        case SDL_PROPERTY_TYPE_STRING:
+            SDL_free(property->value.string_value);
+            break;
+        default:
+            break;
+        }
+        SDL_free(property->string_storage);
+    }
+    SDL_free((void *)key);
+    SDL_free((void *)value);
+}
+
+static void SDLCALL SDL_FreeProperty(void *data, const void *key, const void *value)
+{
+    SDL_FreePropertyWithCleanup(key, value, data, true);
+}
+
+static void SDL_FreeProperties(SDL_Properties *properties)
+{
+    if (properties) {
+        SDL_DestroyHashTable(properties->props);
+        SDL_DestroyMutex(properties->lock);
+        SDL_free(properties);
+    }
+}
+
+bool SDL_InitProperties(void)
+{
+    if (!SDL_ShouldInit(&SDL_properties_init)) {
+        return true;
+    }
+
+    SDL_properties = SDL_CreateHashTable(0, true, SDL_HashID, SDL_KeyMatchID, NULL, NULL);
+    const bool initialized = (SDL_properties != NULL);
+    SDL_SetInitialized(&SDL_properties_init, initialized);
+    return initialized;
+}
+
+static bool SDLCALL FreeOneProperties(void *userdata, const SDL_HashTable *table, const void *key, const void *value)
+{
+    SDL_FreeProperties((SDL_Properties *)value);
+    return true;  // keep iterating.
+}
+
+void SDL_QuitProperties(void)
+{
+    if (!SDL_ShouldQuit(&SDL_properties_init)) {
+        return;
+    }
+
+    SDL_PropertiesID props;
+    do {
+        props = SDL_GetAtomicU32(&SDL_global_properties);
+    } while (!SDL_CompareAndSwapAtomicU32(&SDL_global_properties, props, 0));
+
+    if (props) {
+        SDL_DestroyProperties(props);
+    }
+
+    // this can't just DestroyHashTable with SDL_FreeProperties as the destructor, because
+    //  other destructors under this might cause use to attempt a recursive lock on SDL_properties,
+    //  which isn't allowed with rwlocks. So manually iterate and free everything.
+    SDL_HashTable *properties = SDL_properties;
+    SDL_properties = NULL;
+    SDL_IterateHashTable(properties, FreeOneProperties, NULL);
+    SDL_DestroyHashTable(properties);
+
+    SDL_SetInitialized(&SDL_properties_init, false);
+}
+
+static bool SDL_CheckInitProperties(void)
+{
+    return SDL_InitProperties();
+}
+
+SDL_PropertiesID SDL_GetGlobalProperties(void)
+{
+    SDL_PropertiesID props = SDL_GetAtomicU32(&SDL_global_properties);
+    if (!props) {
+        props = SDL_CreateProperties();
+        if (!SDL_CompareAndSwapAtomicU32(&SDL_global_properties, 0, props)) {
+            // Somebody else created global properties before us, just use those
+            SDL_DestroyProperties(props);
+            props = SDL_GetAtomicU32(&SDL_global_properties);
+        }
+    }
+    return props;
+}
+
+SDL_PropertiesID SDL_CreateProperties(void)
+{
+    if (!SDL_CheckInitProperties()) {
+        return 0;
+    }
+
+    SDL_Properties *properties = (SDL_Properties *)SDL_calloc(1, sizeof(*properties));
+    if (!properties) {
+        return 0;
+    }
+
+    properties->lock = SDL_CreateMutex();
+    if (!properties->lock) {
+        SDL_free(properties);
+        return 0;
+    }
+
+    properties->props = SDL_CreateHashTable(0, false, SDL_HashString, SDL_KeyMatchString, SDL_FreeProperty, NULL);
+    if (!properties->props) {
+        SDL_DestroyMutex(properties->lock);
+        SDL_free(properties);
+        return 0;
+    }
+
+    SDL_PropertiesID props = 0;
+    while (true) {
+        props = (SDL_GetAtomicU32(&SDL_last_properties_id) + 1);
+        if (props == 0) {
+            continue;
+        } else if (SDL_CompareAndSwapAtomicU32(&SDL_last_properties_id, props - 1, props)) {
+            break;
+        }
+    }
+
+    SDL_assert(!SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, NULL));  // should NOT be in the hash table already.
+
+    if (!SDL_InsertIntoHashTable(SDL_properties, (const void *)(uintptr_t)props, properties, false)) {
+        SDL_FreeProperties(properties);
+        return 0;
+    }
+
+    return props;  // All done!
+}
+
+typedef struct CopyOnePropertyData
+{
+    SDL_Properties *dst_properties;
+    bool result;
+} CopyOnePropertyData;
+
+static bool SDLCALL CopyOneProperty(void *userdata, const SDL_HashTable *table, const void *key, const void *value)
+{
+    const SDL_Property *src_property = (const SDL_Property *)value;
+    if (src_property->cleanup) {
+        // Can't copy properties with cleanup functions, we don't know how to duplicate the data
+        return true;  // keep iterating.
+    }
+
+    CopyOnePropertyData *data = (CopyOnePropertyData *) userdata;
+    SDL_Properties *dst_properties = data->dst_properties;
+    const char *src_name = (const char *)key;
+    SDL_Property *dst_property;
+
+    char *dst_name = SDL_strdup(src_name);
+    if (!dst_name) {
+        data->result = false;
+        return true; // keep iterating (I guess...?)
+    }
+
+    dst_property = (SDL_Property *)SDL_malloc(sizeof(*dst_property));
+    if (!dst_property) {
+        SDL_free(dst_name);
+        data->result = false;
+        return true; // keep iterating (I guess...?)
+    }
+
+    SDL_copyp(dst_property, src_property);
+    if (src_property->type == SDL_PROPERTY_TYPE_STRING) {
+        dst_property->value.string_value = SDL_strdup(src_property->value.string_value);
+        if (!dst_property->value.string_value) {
+            SDL_free(dst_name);
+            SDL_free(dst_property);
+            data->result = false;
+            return true; // keep iterating (I guess...?)
+        }
+    }
+
+    if (!SDL_InsertIntoHashTable(dst_properties->props, dst_name, dst_property, true)) {
+        SDL_FreePropertyWithCleanup(dst_name, dst_property, NULL, false);
+        data->result = false;
+    }
+
+    return true;  // keep iterating.
+}
+
+bool SDL_CopyProperties(SDL_PropertiesID src, SDL_PropertiesID dst)
+{
+    if (!src) {
+        return SDL_InvalidParamError("src");
+    }
+    if (!dst) {
+        return SDL_InvalidParamError("dst");
+    }
+
+    SDL_Properties *src_properties = NULL;
+    SDL_Properties *dst_properties = NULL;
+
+    SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)src, (const void **)&src_properties);
+    if (!src_properties) {
+        return SDL_InvalidParamError("src");
+    }
+    SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)dst, (const void **)&dst_properties);
+    if (!dst_properties) {
+        return SDL_InvalidParamError("dst");
+    }
+
+    bool result = true;
+    SDL_LockMutex(src_properties->lock);
+    SDL_LockMutex(dst_properties->lock);
+    {
+        CopyOnePropertyData data = { dst_properties, true };
+        SDL_IterateHashTable(src_properties->props, CopyOneProperty, &data);
+        result = data.result;
+    }
+    SDL_UnlockMutex(dst_properties->lock);
+    SDL_UnlockMutex(src_properties->lock);
+
+    return result;
+}
+
+bool SDL_LockProperties(SDL_PropertiesID props)
+{
+    SDL_Properties *properties = NULL;
+
+    if (!props) {
+        return SDL_InvalidParamError("props");
+    }
+
+    SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
+    if (!properties) {
+        return SDL_InvalidParamError("props");
+    }
+
+    SDL_LockMutex(properties->lock);
+    return true;
+}
+
+void SDL_UnlockProperties(SDL_PropertiesID props)
+{
+    SDL_Properties *properties = NULL;
+
+    if (!props) {
+        return;
+    }
+
+    SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
+    if (!properties) {
+        return;
+    }
+
+    SDL_UnlockMutex(properties->lock);
+}
+
+static bool SDL_PrivateSetProperty(SDL_PropertiesID props, const char *name, SDL_Property *property)
+{
+    SDL_Properties *properties = NULL;
+    bool result = true;
+
+    if (!props) {
+        SDL_FreePropertyWithCleanup(NULL, property, NULL, true);
+        return SDL_InvalidParamError("props");
+    }
+    if (!name || !*name) {
+        SDL_FreePropertyWithCleanup(NULL, property, NULL, true);
+        return SDL_InvalidParamError("name");
+    }
+
+    SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
+    if (!properties) {
+        SDL_FreePropertyWithCleanup(NULL, property, NULL, true);
+        return SDL_InvalidParamError("props");
+    }
+
+    SDL_LockMutex(properties->lock);
+    {
+        SDL_RemoveFromHashTable(properties->props, name);
+        if (property) {
+            char *key = SDL_strdup(name);
+            if (!key || !SDL_InsertIntoHashTable(properties->props, key, property, false)) {
+                SDL_FreePropertyWithCleanup(key, property, NULL, true);
+                result = false;
+            }
+        }
+    }
+    SDL_UnlockMutex(properties->lock);
+
+    return result;
+}
+
+bool SDL_SetPointerPropertyWithCleanup(SDL_PropertiesID props, const char *name, void *value, SDL_CleanupPropertyCallback cleanup, void *userdata)
+{
+    SDL_Property *property;
+
+    if (!value) {
+        if (cleanup) {
+            cleanup(userdata, value);
+        }
+        return SDL_ClearProperty(props, name);
+    }
+
+    property = (SDL_Property *)SDL_calloc(1, sizeof(*property));
+    if (!property) {
+        if (cleanup) {
+            cleanup(userdata, value);
+        }
+        SDL_FreePropertyWithCleanup(NULL, property, NULL, false);
+        return false;
+    }
+    property->type = SDL_PROPERTY_TYPE_POINTER;
+    property->value.pointer_value = value;
+    property->cleanup = cleanup;
+    property->userdata = userdata;
+    return SDL_PrivateSetProperty(props, name, property);
+}
+
+bool SDL_SetPointerProperty(SDL_PropertiesID props, const char *name, void *value)
+{
+    SDL_Property *property;
+
+    if (!value) {
+        return SDL_ClearProperty(props, name);
+    }
+
+    property = (SDL_Property *)SDL_calloc(1, sizeof(*property));
+    if (!property) {
+        return false;
+    }
+    property->type = SDL_PROPERTY_TYPE_POINTER;
+    property->value.pointer_value = value;
+    return SDL_PrivateSetProperty(props, name, property);
+}
+
+static void SDLCALL CleanupFreeableProperty(void *userdata, void *value)
+{
+    SDL_free(value);
+}
+
+bool SDL_SetFreeableProperty(SDL_PropertiesID props, const char *name, void *value)
+{
+    return SDL_SetPointerPropertyWithCleanup(props, name, value, CleanupFreeableProperty, NULL);
+}
+
+static void SDLCALL CleanupSurface(void *userdata, void *value)
+{
+    SDL_Surface *surface = (SDL_Surface *)value;
+
+    //SDL_DestroySurface(surface);
+}
+
+bool SDL_SetSurfaceProperty(SDL_PropertiesID props, const char *name, SDL_Surface *surface)
+{
+    return SDL_SetPointerPropertyWithCleanup(props, name, surface, CleanupSurface, NULL);
+}
+
+bool SDL_SetStringProperty(SDL_PropertiesID props, const char *name, const char *value)
+{
+    SDL_Property *property;
+
+    if (!value) {
+        return SDL_ClearProperty(props, name);
+    }
+
+    property = (SDL_Property *)SDL_calloc(1, sizeof(*property));
+    if (!property) {
+        return false;
+    }
+    property->type = SDL_PROPERTY_TYPE_STRING;
+    property->value.string_value = SDL_strdup(value);
+    if (!property->value.string_value) {
+        SDL_free(property);
+        return false;
+    }
+    return SDL_PrivateSetProperty(props, name, property);
+}
+
+bool SDL_SetNumberProperty(SDL_PropertiesID props, const char *name, Sint64 value)
+{
+    SDL_Property *property = (SDL_Property *)SDL_calloc(1, sizeof(*property));
+    if (!property) {
+        return false;
+    }
+    property->type = SDL_PROPERTY_TYPE_NUMBER;
+    property->value.number_value = value;
+    return SDL_PrivateSetProperty(props, name, property);
+}
+
+bool SDL_SetFloatProperty(SDL_PropertiesID props, const char *name, float value)
+{
+    SDL_Property *property = (SDL_Property *)SDL_calloc(1, sizeof(*property));
+    if (!property) {
+        return false;
+    }
+    property->type = SDL_PROPERTY_TYPE_FLOAT;
+    property->value.float_value = value;
+    return SDL_PrivateSetProperty(props, name, property);
+}
+
+bool SDL_SetBooleanProperty(SDL_PropertiesID props, const char *name, bool value)
+{
+    SDL_Property *property = (SDL_Property *)SDL_calloc(1, sizeof(*property));
+    if (!property) {
+        return false;
+    }
+    property->type = SDL_PROPERTY_TYPE_BOOLEAN;
+    property->value.boolean_value = value ? true : false;
+    return SDL_PrivateSetProperty(props, name, property);
+}
+
+bool SDL_HasProperty(SDL_PropertiesID props, const char *name)
+{
+    return (SDL_GetPropertyType(props, name) != SDL_PROPERTY_TYPE_INVALID);
+}
+
+SDL_PropertyType SDL_GetPropertyType(SDL_PropertiesID props, const char *name)
+{
+    SDL_Properties *properties = NULL;
+    SDL_PropertyType type = SDL_PROPERTY_TYPE_INVALID;
+
+    if (!props) {
+        return SDL_PROPERTY_TYPE_INVALID;
+    }
+    if (!name || !*name) {
+        return SDL_PROPERTY_TYPE_INVALID;
+    }
+
+    SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
+    if (!properties) {
+        return SDL_PROPERTY_TYPE_INVALID;
+    }
+
+    SDL_LockMutex(properties->lock);
+    {
+        SDL_Property *property = NULL;
+        if (SDL_FindInHashTable(properties->props, name, (const void **)&property)) {
+            type = property->type;
+        }
+    }
+    SDL_UnlockMutex(properties->lock);
+
+    return type;
+}
+
+void *SDL_GetPointerProperty(SDL_PropertiesID props, const char *name, void *default_value)
+{
+    SDL_Properties *properties = NULL;
+    void *value = default_value;
+
+    if (!props) {
+        return value;
+    }
+    if (!name || !*name) {
+        return value;
+    }
+
+    SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
+    if (!properties) {
+        return value;
+    }
+
+    // Note that taking the lock here only guarantees that we won't read the
+    // hashtable while it's being modified. The value itself can easily be
+    // freed from another thread after it is returned here.
+    SDL_LockMutex(properties->lock);
+    {
+        SDL_Property *property = NULL;
+        if (SDL_FindInHashTable(properties->props, name, (const void **)&property)) {
+            if (property->type == SDL_PROPERTY_TYPE_POINTER) {
+                value = property->value.pointer_value;
+            }
+        }
+    }
+    SDL_UnlockMutex(properties->lock);
+
+    return value;
+}
+
+const char *SDL_GetStringProperty(SDL_PropertiesID props, const char *name, const char *default_value)
+{
+    SDL_Properties *properties = NULL;
+    const char *value = default_value;
+
+    if (!props) {
+        return value;
+    }
+    if (!name || !*name) {
+        return value;
+    }
+
+    SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
+    if (!properties) {
+        return value;
+    }
+
+    SDL_LockMutex(properties->lock);
+    {
+        SDL_Property *property = NULL;
+        if (SDL_FindInHashTable(properties->props, name, (const void **)&property)) {
+            switch (property->type) {
+            case SDL_PROPERTY_TYPE_STRING:
+                value = property->value.string_value;
+                break;
+            case SDL_PROPERTY_TYPE_NUMBER:
+                if (property->string_storage) {
+                    value = property->string_storage;
+                } else {
+                    SDL_asprintf(&property->string_storage, "%" SDL_PRIs64, property->value.number_value);
+                    if (property->string_storage) {
+                        value = property->string_storage;
+                    }
+                }
+                break;
+            case SDL_PROPERTY_TYPE_FLOAT:
+                if (property->string_storage) {
+                    value = property->string_storage;
+                } else {
+                    SDL_asprintf(&property->string_storage, "%f", property->value.float_value);
+                    if (property->string_storage) {
+                        value = property->string_storage;
+                    }
+                }
+                break;
+            case SDL_PROPERTY_TYPE_BOOLEAN:
+                value = property->value.boolean_value ? "true" : "false";
+                break;
+            default:
+                break;
+            }
+        }
+    }
+    SDL_UnlockMutex(properties->lock);
+
+    return value;
+}
+
+Sint64 SDL_GetNumberProperty(SDL_PropertiesID props, const char *name, Sint64 default_value)
+{
+    SDL_Properties *properties = NULL;
+    Sint64 value = default_value;
+
+    if (!props) {
+        return value;
+    }
+    if (!name || !*name) {
+        return value;
+    }
+
+    SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
+    if (!properties) {
+        return value;
+    }
+
+    SDL_LockMutex(properties->lock);
+    {
+        SDL_Property *property = NULL;
+        if (SDL_FindInHashTable(properties->props, name, (const void **)&property)) {
+            switch (property->type) {
+            case SDL_PROPERTY_TYPE_STRING:
+                value = (Sint64)SDL_strtoll(property->value.string_value, NULL, 0);
+                break;
+            case SDL_PROPERTY_TYPE_NUMBER:
+                value = property->value.number_value;
+                break;
+            case SDL_PROPERTY_TYPE_FLOAT:
+                value = (Sint64)SDL_round((double)property->value.float_value);
+                break;
+            case SDL_PROPERTY_TYPE_BOOLEAN:
+                value = property->value.boolean_value;
+                break;
+            default:
+                break;
+            }
+        }
+    }
+    SDL_UnlockMutex(properties->lock);
+
+    return value;
+}
+
+float SDL_GetFloatProperty(SDL_PropertiesID props, const char *name, float default_value)
+{
+    SDL_Properties *properties = NULL;
+    float value = default_value;
+
+    if (!props) {
+        return value;
+    }
+    if (!name || !*name) {
+        return value;
+    }
+
+    SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
+    if (!properties) {
+        return value;
+    }
+
+    SDL_LockMutex(properties->lock);
+    {
+        SDL_Property *property = NULL;
+        if (SDL_FindInHashTable(properties->props, name, (const void **)&property)) {
+            switch (property->type) {
+            case SDL_PROPERTY_TYPE_STRING:
+                value = (float)SDL_atof(property->value.string_value);
+                break;
+            case SDL_PROPERTY_TYPE_NUMBER:
+                value = (float)property->value.number_value;
+                break;
+            case SDL_PROPERTY_TYPE_FLOAT:
+                value = property->value.float_value;
+                break;
+            case SDL_PROPERTY_TYPE_BOOLEAN:
+                value = (float)property->value.boolean_value;
+                break;
+            default:
+                break;
+            }
+        }
+    }
+    SDL_UnlockMutex(properties->lock);
+
+    return value;
+}
+
+bool SDL_GetBooleanProperty(SDL_PropertiesID props, const char *name, bool default_value)
+{
+    SDL_Properties *properties = NULL;
+    bool value = default_value ? true : false;
+
+    if (!props) {
+        return value;
+    }
+    if (!name || !*name) {
+        return value;
+    }
+
+    SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
+    if (!properties) {
+        return value;
+    }
+
+    SDL_LockMutex(properties->lock);
+    {
+        SDL_Property *property = NULL;
+        if (SDL_FindInHashTable(properties->props, name, (const void **)&property)) {
+            switch (property->type) {
+            case SDL_PROPERTY_TYPE_STRING:
+                value = SDL_GetStringBoolean(property->value.string_value, default_value);
+                break;
+            case SDL_PROPERTY_TYPE_NUMBER:
+                value = (property->value.number_value != 0);
+                break;
+            case SDL_PROPERTY_TYPE_FLOAT:
+                value = (property->value.float_value != 0.0f);
+                break;
+            case SDL_PROPERTY_TYPE_BOOLEAN:
+                value = property->value.boolean_value;
+                break;
+            default:
+                break;
+            }
+        }
+    }
+    SDL_UnlockMutex(properties->lock);
+
+    return value;
+}
+
+bool SDL_ClearProperty(SDL_PropertiesID props, const char *name)
+{
+    return SDL_PrivateSetProperty(props, name, NULL);
+}
+
+typedef struct EnumerateOnePropertyData
+{
+    SDL_EnumeratePropertiesCallback callback;
+    void *userdata;
+    SDL_PropertiesID props;
+} EnumerateOnePropertyData;
+
+
+static bool SDLCALL EnumerateOneProperty(void *userdata, const SDL_HashTable *table, const void *key, const void *value)
+{
+    (void) table;
+    (void) value;
+    const EnumerateOnePropertyData *data = (const EnumerateOnePropertyData *) userdata;
+    data->callback(data->userdata, data->props, (const char *)key);
+    return true;  // keep iterating.
+}
+
+bool SDL_EnumerateProperties(SDL_PropertiesID props, SDL_EnumeratePropertiesCallback callback, void *userdata)
+{
+    SDL_Properties *properties = NULL;
+
+    if (!props) {
+        return SDL_InvalidParamError("props");
+    }
+    if (!callback) {
+        return SDL_InvalidParamError("callback");
+    }
+
+    SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties);
+    if (!properties) {
+        return SDL_InvalidParamError("props");
+    }
+
+    SDL_LockMutex(properties->lock);
+    {
+        EnumerateOnePropertyData data = { callback, userdata, props };
+        SDL_IterateHashTable(properties->props, EnumerateOneProperty, &data);
+    }
+    SDL_UnlockMutex(properties->lock);
+
+    return true;
+}
+
+static void SDLCALL SDL_DumpPropertiesCallback(void *userdata, SDL_PropertiesID props, const char *name)
+{
+    switch (SDL_GetPropertyType(props, name)) {
+    case SDL_PROPERTY_TYPE_POINTER:
+        SDL_Log("%s: %p", name, SDL_GetPointerProperty(props, name, NULL));
+        break;
+    case SDL_PROPERTY_TYPE_STRING:
+        SDL_Log("%s: \"%s\"", name, SDL_GetStringProperty(props, name, ""));
+        break;
+    case SDL_PROPERTY_TYPE_NUMBER:
+        {
+            Sint64 value = SDL_GetNumberProperty(props, name, 0);
+            SDL_Log("%s: %" SDL_PRIs64 " (%" SDL_PRIx64 ")", name, value, value);
+        }
+        break;
+    case SDL_PROPERTY_TYPE_FLOAT:
+        SDL_Log("%s: %g", name, SDL_GetFloatProperty(props, name, 0.0f));
+        break;
+    case SDL_PROPERTY_TYPE_BOOLEAN:
+        SDL_Log("%s: %s", name, SDL_GetBooleanProperty(props, name, false) ? "true" : "false");
+        break;
+    default:
+        SDL_Log("%s UNKNOWN TYPE", name);
+        break;
+    }
+}
+
+bool SDL_DumpProperties(SDL_PropertiesID props)
+{
+    return SDL_EnumerateProperties(props, SDL_DumpPropertiesCallback, NULL);
+}
+
+void SDL_DestroyProperties(SDL_PropertiesID props)
+{
+    if (props) {
+        // this can't just use RemoveFromHashTable with SDL_FreeProperties as the destructor, because
+        //  other destructors under this might cause use to attempt a recursive lock on SDL_properties,
+        //  which isn't allowed with rwlocks. So manually look it up and remove/free it.
+        SDL_Properties *properties = NULL;
+        if (SDL_FindInHashTable(SDL_properties, (const void *)(uintptr_t)props, (const void **)&properties)) {
+            SDL_FreeProperties(properties);
+            SDL_RemoveFromHashTable(SDL_properties, (const void *)(uintptr_t)props);
+        }
+    }
+}

+ 26 - 0
thirdparty/sdl/SDL_properties_c.h

@@ -0,0 +1,26 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+extern bool SDL_InitProperties(void);
+extern bool SDL_SetFreeableProperty(SDL_PropertiesID props, const char *name, void *value);
+extern bool SDL_SetSurfaceProperty(SDL_PropertiesID props, const char *name, SDL_Surface *surface);
+extern bool SDL_DumpProperties(SDL_PropertiesID props);
+extern void SDL_QuitProperties(void);

+ 554 - 0
thirdparty/sdl/SDL_utils.c

@@ -0,0 +1,554 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#if defined(HAVE_GETHOSTNAME) && !defined(SDL_PLATFORM_WINDOWS)
+#include <unistd.h>
+#endif
+
+#include "joystick/SDL_joystick_c.h" // For SDL_GetGamepadTypeFromVIDPID()
+
+
+// Common utility functions that aren't in the public API
+
+int SDL_powerof2(int x)
+{
+    int value;
+
+    if (x <= 0) {
+        // Return some sane value - we shouldn't hit this in our use cases
+        return 1;
+    }
+
+    // This trick works for 32-bit values
+    {
+        SDL_COMPILE_TIME_ASSERT(SDL_powerof2, sizeof(x) == sizeof(Uint32));
+    }
+    value = x;
+    value -= 1;
+    value |= value >> 1;
+    value |= value >> 2;
+    value |= value >> 4;
+    value |= value >> 8;
+    value |= value >> 16;
+    value += 1;
+
+    return value;
+}
+
+Uint32 SDL_CalculateGCD(Uint32 a, Uint32 b)
+{
+    if (b == 0) {
+        return a;
+    }
+    return SDL_CalculateGCD(b, (a % b));
+}
+
+// Algorithm adapted with thanks from John Cook's blog post:
+// http://www.johndcook.com/blog/2010/10/20/best-rational-approximation
+void SDL_CalculateFraction(float x, int *numerator, int *denominator)
+{
+    const int N = 1000;
+    int a = 0, b = 1;
+    int c = 1, d = 0;
+
+    while (b <= N && d <= N) {
+        float mediant = (float)(a + c) / (b + d);
+        if (x == mediant) {
+            if (b + d <= N) {
+                *numerator = a + c;
+                *denominator = b + d;
+            } else if (d > b) {
+                *numerator = c;
+                *denominator = d;
+            } else {
+                *numerator = a;
+                *denominator = b;
+            }
+            return;
+        } else if (x > mediant) {
+            a = a + c;
+            b = b + d;
+        } else {
+            c = a + c;
+            d = b + d;
+        }
+    }
+    if (b > N) {
+        *numerator = c;
+        *denominator = d;
+    } else {
+        *numerator = a;
+        *denominator = b;
+    }
+}
+
+bool SDL_startswith(const char *string, const char *prefix)
+{
+    if (SDL_strncmp(string, prefix, SDL_strlen(prefix)) == 0) {
+        return true;
+    }
+    return false;
+}
+
+bool SDL_endswith(const char *string, const char *suffix)
+{
+    size_t string_length = string ? SDL_strlen(string) : 0;
+    size_t suffix_length = suffix ? SDL_strlen(suffix) : 0;
+
+    if (suffix_length > 0 && suffix_length <= string_length) {
+        if (SDL_memcmp(string + string_length - suffix_length, suffix, suffix_length) == 0) {
+            return true;
+        }
+    }
+    return false;
+}
+
+SDL_COMPILE_TIME_ASSERT(sizeof_object_id, sizeof(int) == sizeof(Uint32));
+
+Uint32 SDL_GetNextObjectID(void)
+{
+    static SDL_AtomicInt last_id;
+
+    Uint32 id = (Uint32)SDL_AtomicIncRef(&last_id) + 1;
+    if (id == 0) {
+        id = (Uint32)SDL_AtomicIncRef(&last_id) + 1;
+    }
+    return id;
+}
+
+static SDL_InitState SDL_objects_init;
+static SDL_HashTable *SDL_objects;
+
+static Uint32 SDLCALL SDL_HashObject(void *unused, const void *key)
+{
+    return (Uint32)(uintptr_t)key;
+}
+
+static bool SDL_KeyMatchObject(void *unused, const void *a, const void *b)
+{
+    return (a == b);
+}
+
+void SDL_SetObjectValid(void *object, SDL_ObjectType type, bool valid)
+{
+    SDL_assert(object != NULL);
+
+    if (SDL_ShouldInit(&SDL_objects_init)) {
+        SDL_objects = SDL_CreateHashTable(0, true, SDL_HashObject, SDL_KeyMatchObject, NULL, NULL);
+        const bool initialized = (SDL_objects != NULL);
+        SDL_SetInitialized(&SDL_objects_init, initialized);
+        if (!initialized) {
+            return;
+        }
+    }
+
+    if (valid) {
+        SDL_InsertIntoHashTable(SDL_objects, object, (void *)(uintptr_t)type, true);
+    } else {
+        SDL_RemoveFromHashTable(SDL_objects, object);
+    }
+}
+
+bool SDL_ObjectValid(void *object, SDL_ObjectType type)
+{
+    if (!object) {
+        return false;
+    }
+
+    const void *object_type;
+    if (!SDL_FindInHashTable(SDL_objects, object, &object_type)) {
+        return false;
+    }
+
+    return (((SDL_ObjectType)(uintptr_t)object_type) == type);
+}
+
+typedef struct GetOneObjectData
+{
+    const SDL_ObjectType type;
+    void **objects;
+    const int count;
+    int num_objects;
+} GetOneObjectData;
+
+static bool SDLCALL GetOneObject(void *userdata, const SDL_HashTable *table, const void *object, const void *object_type)
+{
+    GetOneObjectData *data = (GetOneObjectData *) userdata;
+    if ((SDL_ObjectType)(uintptr_t)object_type == data->type) {
+        if (data->num_objects < data->count) {
+            data->objects[data->num_objects] = (void *)object;
+        }
+        ++data->num_objects;
+    }
+    return true;  // keep iterating.
+}
+
+
+int SDL_GetObjects(SDL_ObjectType type, void **objects, int count)
+{
+    GetOneObjectData data = { type, objects, count, 0 };
+    SDL_IterateHashTable(SDL_objects, GetOneObject, &data);
+    return data.num_objects;
+}
+
+static bool SDLCALL LogOneLeakedObject(void *userdata, const SDL_HashTable *table, const void *object, const void *object_type)
+{
+    const char *type = "unknown object";
+    switch ((SDL_ObjectType)(uintptr_t)object_type) {
+        #define SDLOBJTYPECASE(typ, name) case SDL_OBJECT_TYPE_##typ: type = name; break
+        SDLOBJTYPECASE(WINDOW, "SDL_Window");
+        SDLOBJTYPECASE(RENDERER, "SDL_Renderer");
+        SDLOBJTYPECASE(TEXTURE, "SDL_Texture");
+        SDLOBJTYPECASE(JOYSTICK, "SDL_Joystick");
+        SDLOBJTYPECASE(GAMEPAD, "SDL_Gamepad");
+        SDLOBJTYPECASE(HAPTIC, "SDL_Haptic");
+        SDLOBJTYPECASE(SENSOR, "SDL_Sensor");
+        SDLOBJTYPECASE(HIDAPI_DEVICE, "hidapi device");
+        SDLOBJTYPECASE(HIDAPI_JOYSTICK, "hidapi joystick");
+        SDLOBJTYPECASE(THREAD, "thread");
+        SDLOBJTYPECASE(TRAY, "SDL_Tray");
+        #undef SDLOBJTYPECASE
+        default: break;
+    }
+    SDL_Log("Leaked %s (%p)", type, object);
+    return true;  // keep iterating.
+}
+
+void SDL_SetObjectsInvalid(void)
+{
+    if (SDL_ShouldQuit(&SDL_objects_init)) {
+        // Log any leaked objects
+        SDL_IterateHashTable(SDL_objects, LogOneLeakedObject, NULL);
+        SDL_assert(SDL_HashTableEmpty(SDL_objects));
+        SDL_DestroyHashTable(SDL_objects);
+        SDL_objects = NULL;
+        SDL_SetInitialized(&SDL_objects_init, false);
+    }
+}
+
+static int SDL_URIDecode(const char *src, char *dst, int len)
+{
+    int ri, wi, di;
+    char decode = '\0';
+    if (!src || !dst || len < 0) {
+        return -1;
+    }
+    if (len == 0) {
+        len = (int)SDL_strlen(src);
+    }
+    for (ri = 0, wi = 0, di = 0; ri < len && wi < len; ri += 1) {
+        if (di == 0) {
+            // start decoding
+            if (src[ri] == '%') {
+                decode = '\0';
+                di += 1;
+                continue;
+            }
+            // normal write
+            dst[wi] = src[ri];
+            wi += 1;
+        } else if (di == 1 || di == 2) {
+            char off = '\0';
+            char isa = src[ri] >= 'a' && src[ri] <= 'f';
+            char isA = src[ri] >= 'A' && src[ri] <= 'F';
+            char isn = src[ri] >= '0' && src[ri] <= '9';
+            if (!(isa || isA || isn)) {
+                // not a hexadecimal
+                int sri;
+                for (sri = ri - di; sri <= ri; sri += 1) {
+                    dst[wi] = src[sri];
+                    wi += 1;
+                }
+                di = 0;
+                continue;
+            }
+            // itsy bitsy magicsy
+            if (isn) {
+                off = 0 - '0';
+            } else if (isa) {
+                off = 10 - 'a';
+            } else if (isA) {
+                off = 10 - 'A';
+            }
+            decode |= (src[ri] + off) << (2 - di) * 4;
+            if (di == 2) {
+                dst[wi] = decode;
+                wi += 1;
+                di = 0;
+            } else {
+                di += 1;
+            }
+        }
+    }
+    dst[wi] = '\0';
+    return wi;
+}
+
+int SDL_URIToLocal(const char *src, char *dst)
+{
+    if (SDL_memcmp(src, "file:/", 6) == 0) {
+        src += 6; // local file?
+    } else if (SDL_strstr(src, ":/") != NULL) {
+        return -1; // wrong scheme
+    }
+
+    bool local = src[0] != '/' || (src[0] != '\0' && src[1] == '/');
+
+    // Check the hostname, if present. RFC 3986 states that the hostname component of a URI is not case-sensitive.
+    if (!local && src[0] == '/' && src[2] != '/') {
+        char *hostname_end = SDL_strchr(src + 1, '/');
+        if (hostname_end) {
+            const size_t src_len = hostname_end - (src + 1);
+            size_t hostname_len;
+
+#if defined(HAVE_GETHOSTNAME) && !defined(SDL_PLATFORM_WINDOWS)
+            char hostname[257];
+            if (gethostname(hostname, 255) == 0) {
+                hostname[256] = '\0';
+                hostname_len = SDL_strlen(hostname);
+                if (hostname_len == src_len && SDL_strncasecmp(src + 1, hostname, src_len) == 0) {
+                    src = hostname_end + 1;
+                    local = true;
+                }
+            }
+#endif
+
+            if (!local) {
+                static const char *localhost = "localhost";
+                hostname_len = SDL_strlen(localhost);
+                if (hostname_len == src_len && SDL_strncasecmp(src + 1, localhost, src_len) == 0) {
+                    src = hostname_end + 1;
+                    local = true;
+                }
+            }
+        }
+    }
+
+    if (local) {
+        // Convert URI escape sequences to real characters
+        if (src[0] == '/') {
+            src++;
+        } else {
+            src--;
+        }
+        return SDL_URIDecode(src, dst, 0);
+    }
+    return -1;
+}
+
+// This is a set of per-thread persistent strings that we can return from the SDL API.
+// This is used for short strings that might persist past the lifetime of the object
+// they are related to.
+
+static SDL_TLSID SDL_string_storage;
+
+static void SDL_FreePersistentStrings( void *value )
+{
+    SDL_HashTable *strings = (SDL_HashTable *)value;
+    SDL_DestroyHashTable(strings);
+}
+
+const char *SDL_GetPersistentString(const char *string)
+{
+    if (!string) {
+        return NULL;
+    }
+    if (!*string) {
+        return "";
+    }
+
+    SDL_HashTable *strings = (SDL_HashTable *)SDL_GetTLS(&SDL_string_storage);
+    if (!strings) {
+        strings = SDL_CreateHashTable(0, false, SDL_HashString, SDL_KeyMatchString, SDL_DestroyHashValue, NULL);
+        if (!strings) {
+            return NULL;
+        }
+
+        SDL_SetTLS(&SDL_string_storage, strings, SDL_FreePersistentStrings);
+    }
+
+    const char *result;
+    if (!SDL_FindInHashTable(strings, string, (const void **)&result)) {
+        char *new_string = SDL_strdup(string);
+        if (!new_string) {
+            return NULL;
+        }
+
+        // If the hash table insert fails, at least we can return the string we allocated
+        SDL_InsertIntoHashTable(strings, new_string, new_string, false);
+        result = new_string;
+    }
+    return result;
+}
+
+static int PrefixMatch(const char *a, const char *b)
+{
+    int matchlen = 0;
+    // Fixes the "HORI HORl Taiko No Tatsujin Drum Controller"
+    if (SDL_strncmp(a, "HORI ", 5) == 0 && SDL_strncmp(b, "HORl ", 5) == 0) {
+        return 5;
+    }
+    while (*a && *b) {
+        if (SDL_tolower((unsigned char)*a++) == SDL_tolower((unsigned char)*b++)) {
+            ++matchlen;
+        } else {
+            break;
+        }
+    }
+    return matchlen;
+}
+
+char *SDL_CreateDeviceName(Uint16 vendor, Uint16 product, const char *vendor_name, const char *product_name, const char *default_name)
+{
+    static struct
+    {
+        const char *prefix;
+        const char *replacement;
+    } replacements[] = {
+        { "8BitDo Tech Ltd", "8BitDo" },
+        { "ASTRO Gaming", "ASTRO" },
+        { "Bensussen Deutsch & Associates,Inc.(BDA)", "BDA" },
+        { "Guangzhou Chicken Run Network Technology Co., Ltd.", "GameSir" },
+        { "HORI CO.,LTD.", "HORI" },
+        { "HORI CO.,LTD", "HORI" },
+        { "Mad Catz Inc.", "Mad Catz" },
+        { "Nintendo Co., Ltd.", "Nintendo" },
+        { "NVIDIA Corporation ", "" },
+        { "Performance Designed Products", "PDP" },
+        { "QANBA USA, LLC", "Qanba" },
+        { "QANBA USA,LLC", "Qanba" },
+        { "Unknown ", "" },
+    };
+    char *name = NULL;
+    size_t i, len;
+
+    if (!vendor_name) {
+        vendor_name = "";
+    }
+    if (!product_name) {
+        product_name = "";
+    }
+
+    while (*vendor_name == ' ') {
+        ++vendor_name;
+    }
+    while (*product_name == ' ') {
+        ++product_name;
+    }
+
+    if (*vendor_name && *product_name) {
+        len = (SDL_strlen(vendor_name) + 1 + SDL_strlen(product_name) + 1);
+        name = (char *)SDL_malloc(len);
+        if (name) {
+            (void)SDL_snprintf(name, len, "%s %s", vendor_name, product_name);
+        }
+    } else if (*product_name) {
+        name = SDL_strdup(product_name);
+    } else if (vendor || product) {
+        // Couldn't find a controller name, try to give it one based on device type
+        switch (SDL_GetGamepadTypeFromVIDPID(vendor, product, NULL, true)) {
+        case SDL_GAMEPAD_TYPE_XBOX360:
+            name = SDL_strdup("Xbox 360 Controller");
+            break;
+        case SDL_GAMEPAD_TYPE_XBOXONE:
+            name = SDL_strdup("Xbox One Controller");
+            break;
+        case SDL_GAMEPAD_TYPE_PS3:
+            name = SDL_strdup("PS3 Controller");
+            break;
+        case SDL_GAMEPAD_TYPE_PS4:
+            name = SDL_strdup("PS4 Controller");
+            break;
+        case SDL_GAMEPAD_TYPE_PS5:
+            name = SDL_strdup("DualSense Wireless Controller");
+            break;
+        case SDL_GAMEPAD_TYPE_NINTENDO_SWITCH_PRO:
+            name = SDL_strdup("Nintendo Switch Pro Controller");
+            break;
+        default:
+            len = (6 + 1 + 6 + 1);
+            name = (char *)SDL_malloc(len);
+            if (name) {
+                (void)SDL_snprintf(name, len, "0x%.4x/0x%.4x", vendor, product);
+            }
+            break;
+        }
+    } else if (default_name) {
+        name = SDL_strdup(default_name);
+    }
+
+    if (!name) {
+        return NULL;
+    }
+
+    // Trim trailing whitespace
+    for (len = SDL_strlen(name); (len > 0 && name[len - 1] == ' '); --len) {
+        // continue
+    }
+    name[len] = '\0';
+
+    // Compress duplicate spaces
+    for (i = 0; i < (len - 1);) {
+        if (name[i] == ' ' && name[i + 1] == ' ') {
+            SDL_memmove(&name[i], &name[i + 1], (len - i));
+            --len;
+        } else {
+            ++i;
+        }
+    }
+
+    // Perform any manufacturer replacements
+    for (i = 0; i < SDL_arraysize(replacements); ++i) {
+        size_t prefixlen = SDL_strlen(replacements[i].prefix);
+        if (SDL_strncasecmp(name, replacements[i].prefix, prefixlen) == 0) {
+            size_t replacementlen = SDL_strlen(replacements[i].replacement);
+            if (replacementlen <= prefixlen) {
+                SDL_memcpy(name, replacements[i].replacement, replacementlen);
+                SDL_memmove(name + replacementlen, name + prefixlen, (len - prefixlen) + 1);
+                len -= (prefixlen - replacementlen);
+            } else {
+                // FIXME: Need to handle the expand case by reallocating the string
+            }
+            break;
+        }
+    }
+
+    /* Remove duplicate manufacturer or product in the name
+     * e.g. Razer Razer Raiju Tournament Edition Wired
+     */
+    for (i = 1; i < (len - 1); ++i) {
+        int matchlen = PrefixMatch(name, &name[i]);
+        while (matchlen > 0) {
+            if (name[matchlen] == ' ' || name[matchlen] == '-') {
+                SDL_memmove(name, name + matchlen + 1, len - matchlen);
+                break;
+            }
+            --matchlen;
+        }
+        if (matchlen > 0) {
+            // We matched the manufacturer's name and removed it
+            break;
+        }
+    }
+
+    return name;
+}

+ 78 - 0
thirdparty/sdl/SDL_utils_c.h

@@ -0,0 +1,78 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+// This is included in SDL_internal.h
+//#include "SDL_internal.h"
+
+#ifndef SDL_utils_h_
+#define SDL_utils_h_
+
+// Common utility functions that aren't in the public API
+
+// Return the smallest power of 2 greater than or equal to 'x'
+extern int SDL_powerof2(int x);
+
+extern Uint32 SDL_CalculateGCD(Uint32 a, Uint32 b);
+extern void SDL_CalculateFraction(float x, int *numerator, int *denominator);
+
+extern bool SDL_startswith(const char *string, const char *prefix);
+extern bool SDL_endswith(const char *string, const char *suffix);
+
+/** Convert URI to a local filename, stripping the "file://"
+ *  preamble and hostname if present, and writes the result
+ *  to the dst buffer. Since URI-encoded characters take
+ *  three times the space of normal characters, src and dst
+ *  can safely point to the same buffer for in situ conversion.
+ *
+ *  Returns the number of decoded bytes that wound up in
+ *  the destination buffer, excluding the terminating NULL byte.
+ *
+ *  On error, -1 is returned.
+ */
+extern int SDL_URIToLocal(const char *src, char *dst);
+
+typedef enum
+{
+    SDL_OBJECT_TYPE_UNKNOWN,
+    SDL_OBJECT_TYPE_WINDOW,
+    SDL_OBJECT_TYPE_RENDERER,
+    SDL_OBJECT_TYPE_TEXTURE,
+    SDL_OBJECT_TYPE_JOYSTICK,
+    SDL_OBJECT_TYPE_GAMEPAD,
+    SDL_OBJECT_TYPE_HAPTIC,
+    SDL_OBJECT_TYPE_SENSOR,
+    SDL_OBJECT_TYPE_HIDAPI_DEVICE,
+    SDL_OBJECT_TYPE_HIDAPI_JOYSTICK,
+    SDL_OBJECT_TYPE_THREAD,
+    SDL_OBJECT_TYPE_TRAY,
+
+} SDL_ObjectType;
+
+extern Uint32 SDL_GetNextObjectID(void);
+extern void SDL_SetObjectValid(void *object, SDL_ObjectType type, bool valid);
+extern bool SDL_ObjectValid(void *object, SDL_ObjectType type);
+extern int SDL_GetObjects(SDL_ObjectType type, void **objects, int count);
+extern void SDL_SetObjectsInvalid(void);
+
+extern const char *SDL_GetPersistentString(const char *string);
+
+extern char *SDL_CreateDeviceName(Uint16 vendor, Uint16 product, const char *vendor_name, const char *product_name, const char *default_name);
+
+#endif // SDL_utils_h_

+ 382 - 0
thirdparty/sdl/atomic/SDL_atomic.c

@@ -0,0 +1,382 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1900)
+#include <intrin.h>
+#define HAVE_MSC_ATOMICS 1
+#endif
+
+#ifdef SDL_PLATFORM_MACOS // !!! FIXME: should we favor gcc atomics?
+#include <libkern/OSAtomic.h>
+#endif
+
+#if !defined(HAVE_GCC_ATOMICS) && defined(SDL_PLATFORM_SOLARIS)
+#include <atomic.h>
+#endif
+
+// The __atomic_load_n() intrinsic showed up in different times for different compilers.
+#ifdef __clang__
+#if __has_builtin(__atomic_load_n) || defined(HAVE_GCC_ATOMICS)
+/* !!! FIXME: this advertises as available in the NDK but uses an external symbol we don't have.
+   It might be in a later NDK or we might need an extra library? --ryan. */
+#ifndef SDL_PLATFORM_ANDROID
+#define HAVE_ATOMIC_LOAD_N 1
+#endif
+#endif
+#elif defined(__GNUC__)
+#if (__GNUC__ >= 5)
+#define HAVE_ATOMIC_LOAD_N 1
+#endif
+#endif
+
+/* *INDENT-OFF* */ // clang-format off
+#if defined(__WATCOMC__) && defined(__386__)
+SDL_COMPILE_TIME_ASSERT(intsize, 4==sizeof(int));
+#define HAVE_WATCOM_ATOMICS
+extern __inline int _SDL_xchg_watcom(volatile int *a, int v);
+#pragma aux _SDL_xchg_watcom = \
+  "lock xchg [ecx], eax" \
+  parm [ecx] [eax] \
+  value [eax] \
+  modify exact [eax];
+
+extern __inline unsigned char _SDL_cmpxchg_watcom(volatile int *a, int newval, int oldval);
+#pragma aux _SDL_cmpxchg_watcom = \
+  "lock cmpxchg [edx], ecx" \
+  "setz al" \
+  parm [edx] [ecx] [eax] \
+  value [al] \
+  modify exact [eax];
+
+extern __inline int _SDL_xadd_watcom(volatile int *a, int v);
+#pragma aux _SDL_xadd_watcom = \
+  "lock xadd [ecx], eax" \
+  parm [ecx] [eax] \
+  value [eax] \
+  modify exact [eax];
+
+#endif // __WATCOMC__ && __386__
+/* *INDENT-ON* */ // clang-format on
+
+/*
+  If any of the operations are not provided then we must emulate some
+  of them. That means we need a nice implementation of spin locks
+  that avoids the "one big lock" problem. We use a vector of spin
+  locks and pick which one to use based on the address of the operand
+  of the function.
+
+  To generate the index of the lock we first shift by 3 bits to get
+  rid on the zero bits that result from 32 and 64 bit alignment of
+  data. We then mask off all but 5 bits and use those 5 bits as an
+  index into the table.
+
+  Picking the lock this way insures that accesses to the same data at
+  the same time will go to the same lock. OTOH, accesses to different
+  data have only a 1/32 chance of hitting the same lock. That should
+  pretty much eliminate the chances of several atomic operations on
+  different data from waiting on the same "big lock". If it isn't
+  then the table of locks can be expanded to a new size so long as
+  the new size is a power of two.
+
+  Contributed by Bob Pendleton, [email protected]
+*/
+
+#if !defined(HAVE_MSC_ATOMICS) && !defined(HAVE_GCC_ATOMICS) && !defined(SDL_PLATFORM_MACOS) && !defined(SDL_PLATFORM_SOLARIS) && !defined(HAVE_WATCOM_ATOMICS)
+#define EMULATE_CAS 1
+#endif
+
+#ifdef EMULATE_CAS
+static SDL_SpinLock locks[32];
+
+static SDL_INLINE void enterLock(void *a)
+{
+    uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);
+
+    SDL_LockSpinlock(&locks[index]);
+}
+
+static SDL_INLINE void leaveLock(void *a)
+{
+    uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);
+
+    SDL_UnlockSpinlock(&locks[index]);
+}
+#endif
+
+bool SDL_CompareAndSwapAtomicInt(SDL_AtomicInt *a, int oldval, int newval)
+{
+#ifdef HAVE_MSC_ATOMICS
+    SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(long) == sizeof(a->value));
+    return _InterlockedCompareExchange((long *)&a->value, (long)newval, (long)oldval) == (long)oldval;
+#elif defined(HAVE_WATCOM_ATOMICS)
+    return _SDL_cmpxchg_watcom((volatile int *)&a->value, newval, oldval);
+#elif defined(HAVE_GCC_ATOMICS)
+    return __sync_bool_compare_and_swap(&a->value, oldval, newval);
+#elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics.
+    return OSAtomicCompareAndSwap32Barrier(oldval, newval, &a->value);
+#elif defined(SDL_PLATFORM_SOLARIS)
+    SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(uint_t) == sizeof(a->value));
+    return ((int)atomic_cas_uint((volatile uint_t *)&a->value, (uint_t)oldval, (uint_t)newval) == oldval);
+#elif defined(EMULATE_CAS)
+    bool result = false;
+
+    enterLock(a);
+    if (a->value == oldval) {
+        a->value = newval;
+        result = true;
+    }
+    leaveLock(a);
+
+    return result;
+#else
+#error Please define your platform.
+#endif
+}
+
+bool SDL_CompareAndSwapAtomicU32(SDL_AtomicU32 *a, Uint32 oldval, Uint32 newval)
+{
+#ifdef HAVE_MSC_ATOMICS
+    SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(long) == sizeof(a->value));
+    return _InterlockedCompareExchange((long *)&a->value, (long)newval, (long)oldval) == (long)oldval;
+#elif defined(HAVE_WATCOM_ATOMICS)
+    SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(int) == sizeof(a->value));
+    return _SDL_cmpxchg_watcom((volatile int *)&a->value, (int)newval, (int)oldval);
+#elif defined(HAVE_GCC_ATOMICS)
+    return __sync_bool_compare_and_swap(&a->value, oldval, newval);
+#elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics.
+    return OSAtomicCompareAndSwap32Barrier((int32_t)oldval, (int32_t)newval, (int32_t*)&a->value);
+#elif defined(SDL_PLATFORM_SOLARIS)
+    SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(uint_t) == sizeof(a->value));
+    return ((Uint32)atomic_cas_uint((volatile uint_t *)&a->value, (uint_t)oldval, (uint_t)newval) == oldval);
+#elif defined(EMULATE_CAS)
+    bool result = false;
+
+    enterLock(a);
+    if (a->value == oldval) {
+        a->value = newval;
+        result = true;
+    }
+    leaveLock(a);
+
+    return result;
+#else
+#error Please define your platform.
+#endif
+}
+
+bool SDL_CompareAndSwapAtomicPointer(void **a, void *oldval, void *newval)
+{
+#ifdef HAVE_MSC_ATOMICS
+    return _InterlockedCompareExchangePointer(a, newval, oldval) == oldval;
+#elif defined(HAVE_WATCOM_ATOMICS)
+    return _SDL_cmpxchg_watcom((int *)a, (long)newval, (long)oldval);
+#elif defined(HAVE_GCC_ATOMICS)
+    return __sync_bool_compare_and_swap(a, oldval, newval);
+#elif defined(SDL_PLATFORM_MACOS) && defined(__LP64__)  // this is deprecated in 10.12 sdk; favor gcc atomics.
+    return OSAtomicCompareAndSwap64Barrier((int64_t)oldval, (int64_t)newval, (int64_t *)a);
+#elif defined(SDL_PLATFORM_MACOS) && !defined(__LP64__) // this is deprecated in 10.12 sdk; favor gcc atomics.
+    return OSAtomicCompareAndSwap32Barrier((int32_t)oldval, (int32_t)newval, (int32_t *)a);
+#elif defined(SDL_PLATFORM_SOLARIS)
+    return (atomic_cas_ptr(a, oldval, newval) == oldval);
+#elif defined(EMULATE_CAS)
+    bool result = false;
+
+    enterLock(a);
+    if (*a == oldval) {
+        *a = newval;
+        result = true;
+    }
+    leaveLock(a);
+
+    return result;
+#else
+#error Please define your platform.
+#endif
+}
+
+int SDL_SetAtomicInt(SDL_AtomicInt *a, int v)
+{
+#ifdef HAVE_MSC_ATOMICS
+    SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(long) == sizeof(a->value));
+    return _InterlockedExchange((long *)&a->value, v);
+#elif defined(HAVE_WATCOM_ATOMICS)
+    return _SDL_xchg_watcom(&a->value, v);
+#elif defined(HAVE_GCC_ATOMICS)
+    return __sync_lock_test_and_set(&a->value, v);
+#elif defined(SDL_PLATFORM_SOLARIS)
+    SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(uint_t) == sizeof(a->value));
+    return (int)atomic_swap_uint((volatile uint_t *)&a->value, v);
+#else
+    int value;
+    do {
+        value = a->value;
+    } while (!SDL_CompareAndSwapAtomicInt(a, value, v));
+    return value;
+#endif
+}
+
+Uint32 SDL_SetAtomicU32(SDL_AtomicU32 *a, Uint32 v)
+{
+#ifdef HAVE_MSC_ATOMICS
+    SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(long) == sizeof(a->value));
+    return _InterlockedExchange((long *)&a->value, v);
+#elif defined(HAVE_WATCOM_ATOMICS)
+    return _SDL_xchg_watcom(&a->value, v);
+#elif defined(HAVE_GCC_ATOMICS)
+    return __sync_lock_test_and_set(&a->value, v);
+#elif defined(SDL_PLATFORM_SOLARIS)
+    SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(uint_t) == sizeof(a->value));
+    return (Uint32)atomic_swap_uint((volatile uint_t *)&a->value, v);
+#else
+    Uint32 value;
+    do {
+        value = a->value;
+    } while (!SDL_CompareAndSwapAtomicU32(a, value, v));
+    return value;
+#endif
+}
+
+void *SDL_SetAtomicPointer(void **a, void *v)
+{
+#ifdef HAVE_MSC_ATOMICS
+    return _InterlockedExchangePointer(a, v);
+#elif defined(HAVE_WATCOM_ATOMICS)
+    return (void *)_SDL_xchg_watcom((int *)a, (long)v);
+#elif defined(HAVE_GCC_ATOMICS)
+    return __sync_lock_test_and_set(a, v);
+#elif defined(SDL_PLATFORM_SOLARIS)
+    return atomic_swap_ptr(a, v);
+#else
+    void *value;
+    do {
+        value = *a;
+    } while (!SDL_CompareAndSwapAtomicPointer(a, value, v));
+    return value;
+#endif
+}
+
+int SDL_AddAtomicInt(SDL_AtomicInt *a, int v)
+{
+#ifdef HAVE_MSC_ATOMICS
+    SDL_COMPILE_TIME_ASSERT(atomic_add, sizeof(long) == sizeof(a->value));
+    return _InterlockedExchangeAdd((long *)&a->value, v);
+#elif defined(HAVE_WATCOM_ATOMICS)
+    SDL_COMPILE_TIME_ASSERT(atomic_add, sizeof(int) == sizeof(a->value));
+    return _SDL_xadd_watcom((volatile int *)&a->value, v);
+#elif defined(HAVE_GCC_ATOMICS)
+    return __sync_fetch_and_add(&a->value, v);
+#elif defined(SDL_PLATFORM_SOLARIS)
+    int pv = a->value;
+    membar_consumer();
+    atomic_add_int((volatile uint_t *)&a->value, v);
+    return pv;
+#else
+    int value;
+    do {
+        value = a->value;
+    } while (!SDL_CompareAndSwapAtomicInt(a, value, (value + v)));
+    return value;
+#endif
+}
+
+int SDL_GetAtomicInt(SDL_AtomicInt *a)
+{
+#ifdef HAVE_ATOMIC_LOAD_N
+    return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST);
+#elif defined(HAVE_MSC_ATOMICS)
+    SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(long) == sizeof(a->value));
+    return _InterlockedOr((long *)&a->value, 0);
+#elif defined(HAVE_WATCOM_ATOMICS)
+    return _SDL_xadd_watcom(&a->value, 0);
+#elif defined(HAVE_GCC_ATOMICS)
+    return __sync_or_and_fetch(&a->value, 0);
+#elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics.
+    return sizeof(a->value) == sizeof(uint32_t) ? OSAtomicOr32Barrier(0, (volatile uint32_t *)&a->value) : OSAtomicAdd64Barrier(0, (volatile int64_t *)&a->value);
+#elif defined(SDL_PLATFORM_SOLARIS)
+    return atomic_or_uint_nv((volatile uint_t *)&a->value, 0);
+#else
+    int value;
+    do {
+        value = a->value;
+    } while (!SDL_CompareAndSwapAtomicInt(a, value, value));
+    return value;
+#endif
+}
+
+Uint32 SDL_GetAtomicU32(SDL_AtomicU32 *a)
+{
+#ifdef HAVE_ATOMIC_LOAD_N
+    return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST);
+#elif defined(HAVE_MSC_ATOMICS)
+    SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(long) == sizeof(a->value));
+    return (Uint32)_InterlockedOr((long *)&a->value, 0);
+#elif defined(HAVE_WATCOM_ATOMICS)
+    SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(int) == sizeof(a->value));
+    return (Uint32)_SDL_xadd_watcom((volatile int *)&a->value, 0);
+#elif defined(HAVE_GCC_ATOMICS)
+    return __sync_or_and_fetch(&a->value, 0);
+#elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics.
+    return OSAtomicOr32Barrier(0, (volatile uint32_t *)&a->value);
+#elif defined(SDL_PLATFORM_SOLARIS)
+    SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(uint_t) == sizeof(a->value));
+    return (Uint32)atomic_or_uint_nv((volatile uint_t *)&a->value, 0);
+#else
+    Uint32 value;
+    do {
+        value = a->value;
+    } while (!SDL_CompareAndSwapAtomicU32(a, value, value));
+    return value;
+#endif
+}
+
+void *SDL_GetAtomicPointer(void **a)
+{
+#ifdef HAVE_ATOMIC_LOAD_N
+    return __atomic_load_n(a, __ATOMIC_SEQ_CST);
+#elif defined(HAVE_MSC_ATOMICS)
+    return _InterlockedCompareExchangePointer(a, NULL, NULL);
+#elif defined(HAVE_GCC_ATOMICS)
+    return __sync_val_compare_and_swap(a, (void *)0, (void *)0);
+#elif defined(SDL_PLATFORM_SOLARIS)
+    return atomic_cas_ptr(a, (void *)0, (void *)0);
+#else
+    void *value;
+    do {
+        value = *a;
+    } while (!SDL_CompareAndSwapAtomicPointer(a, value, value));
+    return value;
+#endif
+}
+
+#ifdef SDL_MEMORY_BARRIER_USES_FUNCTION
+#error This file should be built in arm mode so the mcr instruction is available for memory barriers
+#endif
+
+void SDL_MemoryBarrierReleaseFunction(void)
+{
+    SDL_MemoryBarrierRelease();
+}
+
+void SDL_MemoryBarrierAcquireFunction(void)
+{
+    SDL_MemoryBarrierAcquire();
+}

+ 203 - 0
thirdparty/sdl/atomic/SDL_spinlock.c

@@ -0,0 +1,203 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#if defined(SDL_PLATFORM_WINDOWS)
+#include "../core/windows/SDL_windows.h"
+#endif
+
+#if !defined(HAVE_GCC_ATOMICS) && defined(SDL_PLATFORM_SOLARIS)
+#include <atomic.h>
+#endif
+
+#if !defined(HAVE_GCC_ATOMICS) && defined(SDL_PLATFORM_RISCOS)
+#include <unixlib/local.h>
+#endif
+
+#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+#include <xmmintrin.h>
+#endif
+
+#ifdef PS2
+#include <kernel.h>
+#endif
+
+#if !defined(HAVE_GCC_ATOMICS) && defined(SDL_PLATFORM_MACOS)
+#include <libkern/OSAtomic.h>
+#endif
+
+/* *INDENT-OFF* */ // clang-format off
+#if defined(__WATCOMC__) && defined(__386__)
+SDL_COMPILE_TIME_ASSERT(locksize, 4==sizeof(SDL_SpinLock));
+extern __inline int _SDL_xchg_watcom(volatile int *a, int v);
+#pragma aux _SDL_xchg_watcom = \
+  "lock xchg [ecx], eax" \
+  parm [ecx] [eax] \
+  value [eax] \
+  modify exact [eax];
+#endif // __WATCOMC__ && __386__
+/* *INDENT-ON* */ // clang-format on
+
+// This function is where all the magic happens...
+bool SDL_TryLockSpinlock(SDL_SpinLock *lock)
+{
+#if defined(HAVE_GCC_ATOMICS) || defined(HAVE_GCC_SYNC_LOCK_TEST_AND_SET)
+    return __sync_lock_test_and_set(lock, 1) == 0;
+
+#elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
+    return _InterlockedExchange_acq(lock, 1) == 0;
+
+#elif defined(_MSC_VER)
+    SDL_COMPILE_TIME_ASSERT(locksize, sizeof(*lock) == sizeof(long));
+    return InterlockedExchange((long *)lock, 1) == 0;
+
+#elif defined(__WATCOMC__) && defined(__386__)
+    return _SDL_xchg_watcom(lock, 1) == 0;
+
+#elif defined(__GNUC__) && defined(__arm__) &&               \
+    (defined(__ARM_ARCH_3__) || defined(__ARM_ARCH_3M__) ||  \
+     defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__) ||  \
+     defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5TE__) || \
+     defined(__ARM_ARCH_5TEJ__))
+    int result;
+
+#ifdef SDL_PLATFORM_RISCOS
+    if (__cpucap_have_rex()) {
+        __asm__ __volatile__(
+            "ldrex %0, [%2]\nteq   %0, #0\nstrexeq %0, %1, [%2]"
+            : "=&r"(result)
+            : "r"(1), "r"(lock)
+            : "cc", "memory");
+        return result == 0;
+    }
+#endif
+
+    __asm__ __volatile__(
+        "swp %0, %1, [%2]\n"
+        : "=&r,&r"(result)
+        : "r,0"(1), "r,r"(lock)
+        : "memory");
+    return result == 0;
+
+#elif defined(__GNUC__) && defined(__arm__)
+    int result;
+    __asm__ __volatile__(
+        "ldrex %0, [%2]\nteq   %0, #0\nstrexeq %0, %1, [%2]"
+        : "=&r"(result)
+        : "r"(1), "r"(lock)
+        : "cc", "memory");
+    return result == 0;
+
+#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+    int result;
+    __asm__ __volatile__(
+        "lock ; xchgl %0, (%1)\n"
+        : "=r"(result)
+        : "r"(lock), "0"(1)
+        : "cc", "memory");
+    return result == 0;
+
+#elif defined(SDL_PLATFORM_MACOS) || defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS)
+    // Maybe used for PowerPC, but the Intel asm or gcc atomics are favored.
+    return OSAtomicCompareAndSwap32Barrier(0, 1, lock);
+
+#elif defined(SDL_PLATFORM_SOLARIS) && defined(_LP64)
+    // Used for Solaris with non-gcc compilers.
+    return ((int)atomic_cas_64((volatile uint64_t *)lock, 0, 1) == 0);
+
+#elif defined(SDL_PLATFORM_SOLARIS) && !defined(_LP64)
+    // Used for Solaris with non-gcc compilers.
+    return ((int)atomic_cas_32((volatile uint32_t *)lock, 0, 1) == 0);
+#elif defined(PS2)
+    uint32_t oldintr;
+    bool res = false;
+    // disable interruption
+    oldintr = DIntr();
+
+    if (*lock == 0) {
+        *lock = 1;
+        res = true;
+    }
+    // enable interruption
+    if (oldintr) {
+        EIntr();
+    }
+    return res;
+#else
+    // Terrible terrible damage
+    static SDL_Mutex *_spinlock_mutex;
+
+    if (!_spinlock_mutex) {
+        // Race condition on first lock...
+        _spinlock_mutex = SDL_CreateMutex();
+    }
+    SDL_LockMutex(_spinlock_mutex);
+    if (*lock == 0) {
+        *lock = 1;
+        SDL_UnlockMutex(_spinlock_mutex);
+        return true;
+    } else {
+        SDL_UnlockMutex(_spinlock_mutex);
+        return false;
+    }
+#endif
+}
+
+void SDL_LockSpinlock(SDL_SpinLock *lock)
+{
+    int iterations = 0;
+    // FIXME: Should we have an eventual timeout?
+    while (!SDL_TryLockSpinlock(lock)) {
+        if (iterations < 32) {
+            iterations++;
+            SDL_CPUPauseInstruction();
+        } else {
+            // !!! FIXME: this doesn't definitely give up the current timeslice, it does different things on various platforms.
+            SDL_Delay(0);
+        }
+    }
+}
+
+void SDL_UnlockSpinlock(SDL_SpinLock *lock)
+{
+#if defined(HAVE_GCC_ATOMICS) || defined(HAVE_GCC_SYNC_LOCK_TEST_AND_SET)
+    __sync_lock_release(lock);
+
+#elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
+    _InterlockedExchange_rel(lock, 0);
+
+#elif defined(_MSC_VER)
+    _ReadWriteBarrier();
+    *lock = 0;
+
+#elif defined(__WATCOMC__) && defined(__386__)
+    SDL_CompilerBarrier();
+    *lock = 0;
+
+#elif defined(SDL_PLATFORM_SOLARIS)
+    // Used for Solaris when not using gcc.
+    *lock = 0;
+    membar_producer();
+
+#else
+    *lock = 0;
+#endif
+}

+ 641 - 0
thirdparty/sdl/core/linux/SDL_dbus.c

@@ -0,0 +1,641 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+#include "SDL_dbus.h"
+#include "../../stdlib/SDL_vacopy.h"
+
+#ifdef SDL_USE_LIBDBUS
+// we never link directly to libdbus.
+static const char *dbus_library = "libdbus-1.so.3";
+static SDL_SharedObject *dbus_handle = NULL;
+static char *inhibit_handle = NULL;
+static unsigned int screensaver_cookie = 0;
+static SDL_DBusContext dbus;
+
+static bool LoadDBUSSyms(void)
+{
+#define SDL_DBUS_SYM2_OPTIONAL(TYPE, x, y)                   \
+    dbus.x = (TYPE)SDL_LoadFunction(dbus_handle, #y)
+
+#define SDL_DBUS_SYM2(TYPE, x, y)                            \
+    if (!(dbus.x = (TYPE)SDL_LoadFunction(dbus_handle, #y))) \
+        return false
+
+#define SDL_DBUS_SYM_OPTIONAL(TYPE, x) \
+    SDL_DBUS_SYM2_OPTIONAL(TYPE, x, dbus_##x)
+
+#define SDL_DBUS_SYM(TYPE, x) \
+    SDL_DBUS_SYM2(TYPE, x, dbus_##x)
+
+    SDL_DBUS_SYM(DBusConnection *(*)(DBusBusType, DBusError *), bus_get_private);
+    SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, DBusError *), bus_register);
+    SDL_DBUS_SYM(void (*)(DBusConnection *, const char *, DBusError *), bus_add_match);
+    SDL_DBUS_SYM(DBusConnection *(*)(const char *, DBusError *), connection_open_private);
+    SDL_DBUS_SYM(void (*)(DBusConnection *, dbus_bool_t), connection_set_exit_on_disconnect);
+    SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *), connection_get_is_connected);
+    SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, DBusHandleMessageFunction, void *, DBusFreeFunction), connection_add_filter);
+    SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, DBusHandleMessageFunction, void *), connection_remove_filter);
+    SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, const char *, const DBusObjectPathVTable *, void *, DBusError *), connection_try_register_object_path);
+    SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, DBusMessage *, dbus_uint32_t *), connection_send);
+    SDL_DBUS_SYM(DBusMessage *(*)(DBusConnection *, DBusMessage *, int, DBusError *), connection_send_with_reply_and_block);
+    SDL_DBUS_SYM(void (*)(DBusConnection *), connection_close);
+    SDL_DBUS_SYM(void (*)(DBusConnection *), connection_ref);
+    SDL_DBUS_SYM(void (*)(DBusConnection *), connection_unref);
+    SDL_DBUS_SYM(void (*)(DBusConnection *), connection_flush);
+    SDL_DBUS_SYM(dbus_bool_t (*)(DBusConnection *, int), connection_read_write);
+    SDL_DBUS_SYM(DBusDispatchStatus (*)(DBusConnection *), connection_dispatch);
+    SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, const char *, const char *), message_is_signal);
+    SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, const char *), message_has_path);
+    SDL_DBUS_SYM(DBusMessage *(*)(const char *, const char *, const char *, const char *), message_new_method_call);
+    SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, int, ...), message_append_args);
+    SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, int, va_list), message_append_args_valist);
+    SDL_DBUS_SYM(void (*)(DBusMessage *, DBusMessageIter *), message_iter_init_append);
+    SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessageIter *, int, const char *, DBusMessageIter *), message_iter_open_container);
+    SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessageIter *, int, const void *), message_iter_append_basic);
+    SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessageIter *, DBusMessageIter *), message_iter_close_container);
+    SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, DBusError *, int, ...), message_get_args);
+    SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, DBusError *, int, va_list), message_get_args_valist);
+    SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessage *, DBusMessageIter *), message_iter_init);
+    SDL_DBUS_SYM(dbus_bool_t (*)(DBusMessageIter *), message_iter_next);
+    SDL_DBUS_SYM(void (*)(DBusMessageIter *, void *), message_iter_get_basic);
+    SDL_DBUS_SYM(int (*)(DBusMessageIter *), message_iter_get_arg_type);
+    SDL_DBUS_SYM(void (*)(DBusMessageIter *, DBusMessageIter *), message_iter_recurse);
+    SDL_DBUS_SYM(void (*)(DBusMessage *), message_unref);
+    SDL_DBUS_SYM(dbus_bool_t (*)(void), threads_init_default);
+    SDL_DBUS_SYM(void (*)(DBusError *), error_init);
+    SDL_DBUS_SYM(dbus_bool_t (*)(const DBusError *), error_is_set);
+    SDL_DBUS_SYM(void (*)(DBusError *), error_free);
+    SDL_DBUS_SYM(char *(*)(void), get_local_machine_id);
+    SDL_DBUS_SYM_OPTIONAL(char *(*)(DBusError *), try_get_local_machine_id);
+    SDL_DBUS_SYM(void (*)(void *), free);
+    SDL_DBUS_SYM(void (*)(char **), free_string_array);
+    SDL_DBUS_SYM(void (*)(void), shutdown);
+
+#undef SDL_DBUS_SYM
+#undef SDL_DBUS_SYM2
+
+    return true;
+}
+
+static void UnloadDBUSLibrary(void)
+{
+    if (dbus_handle) {
+        SDL_UnloadObject(dbus_handle);
+        dbus_handle = NULL;
+    }
+}
+
+static bool LoadDBUSLibrary(void)
+{
+    bool result = true;
+    if (!dbus_handle) {
+        dbus_handle = SDL_LoadObject(dbus_library);
+        if (!dbus_handle) {
+            result = false;
+            // Don't call SDL_SetError(): SDL_LoadObject already did.
+        } else {
+            result = LoadDBUSSyms();
+            if (!result) {
+                UnloadDBUSLibrary();
+            }
+        }
+    }
+    return result;
+}
+
+static SDL_InitState dbus_init;
+
+void SDL_DBus_Init(void)
+{
+    static bool is_dbus_available = true;
+
+    if (!is_dbus_available) {
+        return; // don't keep trying if this fails.
+    }
+
+    if (!SDL_ShouldInit(&dbus_init)) {
+        return;
+    }
+
+    if (!LoadDBUSLibrary()) {
+        goto error;
+    }
+
+    if (!dbus.threads_init_default()) {
+        goto error;
+    }
+
+    DBusError err;
+    dbus.error_init(&err);
+    // session bus is required
+
+    dbus.session_conn = dbus.bus_get_private(DBUS_BUS_SESSION, &err);
+    if (dbus.error_is_set(&err)) {
+        dbus.error_free(&err);
+        goto error;
+    }
+    dbus.connection_set_exit_on_disconnect(dbus.session_conn, 0);
+
+    // system bus is optional
+    dbus.system_conn = dbus.bus_get_private(DBUS_BUS_SYSTEM, &err);
+    if (!dbus.error_is_set(&err)) {
+        dbus.connection_set_exit_on_disconnect(dbus.system_conn, 0);
+    }
+
+    dbus.error_free(&err);
+    SDL_SetInitialized(&dbus_init, true);
+    return;
+
+error:
+    is_dbus_available = false;
+    SDL_SetInitialized(&dbus_init, true);
+    SDL_DBus_Quit();
+}
+
+void SDL_DBus_Quit(void)
+{
+    if (!SDL_ShouldQuit(&dbus_init)) {
+        return;
+    }
+
+    if (dbus.system_conn) {
+        dbus.connection_close(dbus.system_conn);
+        dbus.connection_unref(dbus.system_conn);
+    }
+    if (dbus.session_conn) {
+        dbus.connection_close(dbus.session_conn);
+        dbus.connection_unref(dbus.session_conn);
+    }
+
+    if (SDL_GetHintBoolean(SDL_HINT_SHUTDOWN_DBUS_ON_QUIT, false)) {
+        if (dbus.shutdown) {
+            dbus.shutdown();
+        }
+
+        UnloadDBUSLibrary();
+    } else {
+        /* Leaving libdbus loaded when skipping dbus_shutdown() avoids
+         * spurious leak warnings from LeakSanitizer on internal D-Bus
+         * allocations that would be freed by dbus_shutdown(). */
+        dbus_handle = NULL;
+    }
+
+    SDL_zero(dbus);
+    if (inhibit_handle) {
+        SDL_free(inhibit_handle);
+        inhibit_handle = NULL;
+    }
+
+    SDL_SetInitialized(&dbus_init, false);
+}
+
+SDL_DBusContext *SDL_DBus_GetContext(void)
+{
+    if (!dbus_handle || !dbus.session_conn) {
+        SDL_DBus_Init();
+    }
+
+    return (dbus_handle && dbus.session_conn) ? &dbus : NULL;
+}
+
+static bool SDL_DBus_CallMethodInternal(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, va_list ap)
+{
+    bool result = false;
+
+    if (conn) {
+        DBusMessage *msg = dbus.message_new_method_call(node, path, interface, method);
+        if (msg) {
+            int firstarg;
+            va_list ap_reply;
+            va_copy(ap_reply, ap); // copy the arg list so we don't compete with D-Bus for it
+            firstarg = va_arg(ap, int);
+            if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_append_args_valist(msg, firstarg, ap)) {
+                DBusMessage *reply = dbus.connection_send_with_reply_and_block(conn, msg, 300, NULL);
+                if (reply) {
+                    // skip any input args, get to output args.
+                    while ((firstarg = va_arg(ap_reply, int)) != DBUS_TYPE_INVALID) {
+                        // we assume D-Bus already validated all this.
+                        {
+                            void *dumpptr = va_arg(ap_reply, void *);
+                            (void)dumpptr;
+                        }
+                        if (firstarg == DBUS_TYPE_ARRAY) {
+                            {
+                                const int dumpint = va_arg(ap_reply, int);
+                                (void)dumpint;
+                            }
+                        }
+                    }
+                    firstarg = va_arg(ap_reply, int);
+                    if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_get_args_valist(reply, NULL, firstarg, ap_reply)) {
+                        result = true;
+                    }
+                    dbus.message_unref(reply);
+                }
+            }
+            va_end(ap_reply);
+            dbus.message_unref(msg);
+        }
+    }
+
+    return result;
+}
+
+bool SDL_DBus_CallMethodOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...)
+{
+    bool result;
+    va_list ap;
+    va_start(ap, method);
+    result = SDL_DBus_CallMethodInternal(conn, node, path, interface, method, ap);
+    va_end(ap);
+    return result;
+}
+
+bool SDL_DBus_CallMethod(const char *node, const char *path, const char *interface, const char *method, ...)
+{
+    bool result;
+    va_list ap;
+    va_start(ap, method);
+    result = SDL_DBus_CallMethodInternal(dbus.session_conn, node, path, interface, method, ap);
+    va_end(ap);
+    return result;
+}
+
+static bool SDL_DBus_CallVoidMethodInternal(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, va_list ap)
+{
+    bool result = false;
+
+    if (conn) {
+        DBusMessage *msg = dbus.message_new_method_call(node, path, interface, method);
+        if (msg) {
+            int firstarg = va_arg(ap, int);
+            if ((firstarg == DBUS_TYPE_INVALID) || dbus.message_append_args_valist(msg, firstarg, ap)) {
+                if (dbus.connection_send(conn, msg, NULL)) {
+                    dbus.connection_flush(conn);
+                    result = true;
+                }
+            }
+
+            dbus.message_unref(msg);
+        }
+    }
+
+    return result;
+}
+
+static bool SDL_DBus_CallWithBasicReply(DBusConnection *conn, DBusMessage *msg, const int expectedtype, void *result)
+{
+    bool retval = false;
+
+    DBusMessage *reply = dbus.connection_send_with_reply_and_block(conn, msg, 300, NULL);
+    if (reply) {
+        DBusMessageIter iter, actual_iter;
+        dbus.message_iter_init(reply, &iter);
+        if (dbus.message_iter_get_arg_type(&iter) == DBUS_TYPE_VARIANT) {
+            dbus.message_iter_recurse(&iter, &actual_iter);
+        } else {
+            actual_iter = iter;
+        }
+
+        if (dbus.message_iter_get_arg_type(&actual_iter) == expectedtype) {
+            dbus.message_iter_get_basic(&actual_iter, result);
+            retval = true;
+        }
+
+        dbus.message_unref(reply);
+    }
+
+    return retval;
+}
+
+bool SDL_DBus_CallVoidMethodOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...)
+{
+    bool result;
+    va_list ap;
+    va_start(ap, method);
+    result = SDL_DBus_CallVoidMethodInternal(conn, node, path, interface, method, ap);
+    va_end(ap);
+    return result;
+}
+
+bool SDL_DBus_CallVoidMethod(const char *node, const char *path, const char *interface, const char *method, ...)
+{
+    bool result;
+    va_list ap;
+    va_start(ap, method);
+    result = SDL_DBus_CallVoidMethodInternal(dbus.session_conn, node, path, interface, method, ap);
+    va_end(ap);
+    return result;
+}
+
+bool SDL_DBus_QueryPropertyOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *property, int expectedtype, void *result)
+{
+    bool retval = false;
+
+    if (conn) {
+        DBusMessage *msg = dbus.message_new_method_call(node, path, "org.freedesktop.DBus.Properties", "Get");
+        if (msg) {
+            if (dbus.message_append_args(msg, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) {
+                retval = SDL_DBus_CallWithBasicReply(conn, msg, expectedtype, result);
+            }
+            dbus.message_unref(msg);
+        }
+    }
+
+    return retval;
+}
+
+bool SDL_DBus_QueryProperty(const char *node, const char *path, const char *interface, const char *property, int expectedtype, void *result)
+{
+    return SDL_DBus_QueryPropertyOnConnection(dbus.session_conn, node, path, interface, property, expectedtype, result);
+}
+
+void SDL_DBus_ScreensaverTickle(void)
+{
+    if (screensaver_cookie == 0 && !inhibit_handle) { // no need to tickle if we're inhibiting.
+        // org.gnome.ScreenSaver is the legacy interface, but it'll either do nothing or just be a second harmless tickle on newer systems, so we leave it for now.
+        SDL_DBus_CallVoidMethod("org.gnome.ScreenSaver", "/org/gnome/ScreenSaver", "org.gnome.ScreenSaver", "SimulateUserActivity", DBUS_TYPE_INVALID);
+        SDL_DBus_CallVoidMethod("org.freedesktop.ScreenSaver", "/org/freedesktop/ScreenSaver", "org.freedesktop.ScreenSaver", "SimulateUserActivity", DBUS_TYPE_INVALID);
+    }
+}
+
+static bool SDL_DBus_AppendDictWithKeysAndValues(DBusMessageIter *iterInit, const char **keys, const char **values, int count)
+{
+    DBusMessageIter iterDict;
+
+    if (!dbus.message_iter_open_container(iterInit, DBUS_TYPE_ARRAY, "{sv}", &iterDict)) {
+        goto failed;
+    }
+
+    for (int i = 0; i < count; i++) {
+        DBusMessageIter iterEntry, iterValue;
+        const char *key = keys[i];
+        const char *value = values[i];
+
+        if (!dbus.message_iter_open_container(&iterDict, DBUS_TYPE_DICT_ENTRY, NULL, &iterEntry)) {
+            goto failed;
+        }
+
+        if (!dbus.message_iter_append_basic(&iterEntry, DBUS_TYPE_STRING, &key)) {
+            goto failed;
+        }
+
+        if (!dbus.message_iter_open_container(&iterEntry, DBUS_TYPE_VARIANT, DBUS_TYPE_STRING_AS_STRING, &iterValue)) {
+            goto failed;
+        }
+
+        if (!dbus.message_iter_append_basic(&iterValue, DBUS_TYPE_STRING, &value)) {
+            goto failed;
+        }
+
+        if (!dbus.message_iter_close_container(&iterEntry, &iterValue) || !dbus.message_iter_close_container(&iterDict, &iterEntry)) {
+            goto failed;
+        }
+    }
+
+    if (!dbus.message_iter_close_container(iterInit, &iterDict)) {
+        goto failed;
+    }
+
+    return true;
+
+failed:
+    /* message_iter_abandon_container_if_open() and message_iter_abandon_container() might be
+     * missing if libdbus is too old. Instead, we just return without cleaning up any eventual
+     * open container */
+    return false;
+}
+
+static bool SDL_DBus_AppendDictWithKeyValue(DBusMessageIter *iterInit, const char *key, const char *value)
+{
+   const char *keys[1];
+   const char *values[1];
+
+   keys[0] = key;
+   values[0] = value;
+   return SDL_DBus_AppendDictWithKeysAndValues(iterInit, keys, values, 1);
+}
+
+bool SDL_DBus_ScreensaverInhibit(bool inhibit)
+{
+    const char *default_inhibit_reason = "Playing a game";
+
+    if ((inhibit && (screensaver_cookie != 0 || inhibit_handle)) || (!inhibit && (screensaver_cookie == 0 && !inhibit_handle))) {
+        return true;
+    }
+
+    if (!dbus.session_conn) {
+        /* We either lost connection to the session bus or were not able to
+         * load the D-Bus library at all. */
+        return false;
+    }
+
+    if (SDL_GetSandbox() != SDL_SANDBOX_NONE) {
+        const char *bus_name = "org.freedesktop.portal.Desktop";
+        const char *path = "/org/freedesktop/portal/desktop";
+        const char *interface = "org.freedesktop.portal.Inhibit";
+        const char *window = "";                    // As a future improvement we could gather the X11 XID or Wayland surface identifier
+        static const unsigned int INHIBIT_IDLE = 8; // Taken from the portal API reference
+        DBusMessageIter iterInit;
+
+        if (inhibit) {
+            DBusMessage *msg;
+            bool result = false;
+            const char *key = "reason";
+            const char *reply = NULL;
+            const char *reason = SDL_GetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME);
+            if (!reason || !reason[0]) {
+                reason = default_inhibit_reason;
+            }
+
+            msg = dbus.message_new_method_call(bus_name, path, interface, "Inhibit");
+            if (!msg) {
+                return false;
+            }
+
+            if (!dbus.message_append_args(msg, DBUS_TYPE_STRING, &window, DBUS_TYPE_UINT32, &INHIBIT_IDLE, DBUS_TYPE_INVALID)) {
+                dbus.message_unref(msg);
+                return false;
+            }
+
+            dbus.message_iter_init_append(msg, &iterInit);
+
+            // a{sv}
+            if (!SDL_DBus_AppendDictWithKeyValue(&iterInit, key, reason)) {
+                dbus.message_unref(msg);
+                return false;
+            }
+
+            if (SDL_DBus_CallWithBasicReply(dbus.session_conn, msg, DBUS_TYPE_OBJECT_PATH, &reply)) {
+                inhibit_handle = SDL_strdup(reply);
+                result = true;
+            }
+
+            dbus.message_unref(msg);
+            return result;
+        } else {
+            if (!SDL_DBus_CallVoidMethod(bus_name, inhibit_handle, "org.freedesktop.portal.Request", "Close", DBUS_TYPE_INVALID)) {
+                return false;
+            }
+            SDL_free(inhibit_handle);
+            inhibit_handle = NULL;
+        }
+    } else {
+        const char *bus_name = "org.freedesktop.ScreenSaver";
+        const char *path = "/org/freedesktop/ScreenSaver";
+        const char *interface = "org.freedesktop.ScreenSaver";
+
+        if (inhibit) {
+            const char *app = SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING);
+            const char *reason = SDL_GetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME);
+            if (!reason || !reason[0]) {
+                reason = default_inhibit_reason;
+            }
+
+            if (!SDL_DBus_CallMethod(bus_name, path, interface, "Inhibit",
+                                     DBUS_TYPE_STRING, &app, DBUS_TYPE_STRING, &reason, DBUS_TYPE_INVALID,
+                                     DBUS_TYPE_UINT32, &screensaver_cookie, DBUS_TYPE_INVALID)) {
+                return false;
+            }
+            return (screensaver_cookie != 0);
+        } else {
+            if (!SDL_DBus_CallVoidMethod(bus_name, path, interface, "UnInhibit", DBUS_TYPE_UINT32, &screensaver_cookie, DBUS_TYPE_INVALID)) {
+                return false;
+            }
+            screensaver_cookie = 0;
+        }
+    }
+
+    return true;
+}
+
+void SDL_DBus_PumpEvents(void)
+{
+    if (dbus.session_conn) {
+        dbus.connection_read_write(dbus.session_conn, 0);
+
+        while (dbus.connection_dispatch(dbus.session_conn) == DBUS_DISPATCH_DATA_REMAINS) {
+            // Do nothing, actual work happens in DBus_MessageFilter
+            SDL_DelayNS(SDL_US_TO_NS(10));
+        }
+    }
+}
+
+/*
+ * Get the machine ID if possible. Result must be freed with dbus->free().
+ */
+char *SDL_DBus_GetLocalMachineId(void)
+{
+    DBusError err;
+    char *result;
+
+    dbus.error_init(&err);
+
+    if (dbus.try_get_local_machine_id) {
+        // Available since dbus 1.12.0, has proper error-handling
+        result = dbus.try_get_local_machine_id(&err);
+    } else {
+        /* Available since time immemorial, but has no error-handling:
+         * if the machine ID can't be read, many versions of libdbus will
+         * treat that as a fatal mis-installation and abort() */
+        result = dbus.get_local_machine_id();
+    }
+
+    if (result) {
+        return result;
+    }
+
+    if (dbus.error_is_set(&err)) {
+        SDL_SetError("%s: %s", err.name, err.message);
+        dbus.error_free(&err);
+    } else {
+        SDL_SetError("Error getting D-Bus machine ID");
+    }
+
+    return NULL;
+}
+
+/*
+ * Convert file drops with mime type "application/vnd.portal.filetransfer" to file paths
+ * Result must be freed with dbus->free_string_array().
+ * https://flatpak.github.io/xdg-desktop-portal/#gdbus-method-org-freedesktop-portal-FileTransfer.RetrieveFiles
+ */
+char **SDL_DBus_DocumentsPortalRetrieveFiles(const char *key, int *path_count)
+{
+    DBusError err;
+    DBusMessageIter iter, iterDict;
+    char **paths = NULL;
+    DBusMessage *reply = NULL;
+    DBusMessage *msg = dbus.message_new_method_call("org.freedesktop.portal.Documents",    // Node
+                                                    "/org/freedesktop/portal/documents",   // Path
+                                                    "org.freedesktop.portal.FileTransfer", // Interface
+                                                    "RetrieveFiles");                      // Method
+
+    // Make sure we have a connection to the dbus session bus
+    if (!SDL_DBus_GetContext() || !dbus.session_conn) {
+        /* We either cannot connect to the session bus or were unable to
+         * load the D-Bus library at all. */
+        return NULL;
+    }
+
+    dbus.error_init(&err);
+
+    // First argument is a "application/vnd.portal.filetransfer" key from a DnD or clipboard event
+    if (!dbus.message_append_args(msg, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID)) {
+        SDL_OutOfMemory();
+        dbus.message_unref(msg);
+        goto failed;
+    }
+
+    /* Second argument is a variant dictionary for options.
+     * The spec doesn't define any entries yet so it's empty. */
+    dbus.message_iter_init_append(msg, &iter);
+    if (!dbus.message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &iterDict) ||
+        !dbus.message_iter_close_container(&iter,  &iterDict)) {
+        SDL_OutOfMemory();
+        dbus.message_unref(msg);
+        goto failed;
+    }
+
+    reply = dbus.connection_send_with_reply_and_block(dbus.session_conn, msg, DBUS_TIMEOUT_USE_DEFAULT, &err);
+    dbus.message_unref(msg);
+
+    if (reply) {
+        dbus.message_get_args(reply, &err, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &paths, path_count, DBUS_TYPE_INVALID);
+        dbus.message_unref(reply);
+    }
+
+    if (paths) {
+        return paths;
+    }
+
+failed:
+    if (dbus.error_is_set(&err)) {
+        SDL_SetError("%s: %s", err.name, err.message);
+        dbus.error_free(&err);
+    } else {
+        SDL_SetError("Error retrieving paths for documents portal \"%s\"", key);
+    }
+
+    return NULL;
+}
+
+#endif

+ 114 - 0
thirdparty/sdl/core/linux/SDL_dbus.h

@@ -0,0 +1,114 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "SDL_internal.h"
+
+#ifndef SDL_dbus_h_
+#define SDL_dbus_h_
+
+#ifdef HAVE_DBUS_DBUS_H
+#define SDL_USE_LIBDBUS 1
+#include <dbus/dbus.h>
+
+#ifndef DBUS_TIMEOUT_USE_DEFAULT
+#define DBUS_TIMEOUT_USE_DEFAULT -1
+#endif
+#ifndef DBUS_TIMEOUT_INFINITE
+#define DBUS_TIMEOUT_INFINITE ((int) 0x7fffffff)
+#endif
+
+typedef struct SDL_DBusContext
+{
+    DBusConnection *session_conn;
+    DBusConnection *system_conn;
+
+    DBusConnection *(*bus_get_private)(DBusBusType, DBusError *);
+    dbus_bool_t (*bus_register)(DBusConnection *, DBusError *);
+    void (*bus_add_match)(DBusConnection *, const char *, DBusError *);
+    DBusConnection *(*connection_open_private)(const char *, DBusError *);
+    void (*connection_set_exit_on_disconnect)(DBusConnection *, dbus_bool_t);
+    dbus_bool_t (*connection_get_is_connected)(DBusConnection *);
+    dbus_bool_t (*connection_add_filter)(DBusConnection *, DBusHandleMessageFunction, void *, DBusFreeFunction);
+    dbus_bool_t (*connection_remove_filter)(DBusConnection *, DBusHandleMessageFunction, void *);
+    dbus_bool_t (*connection_try_register_object_path)(DBusConnection *, const char *,
+                                                       const DBusObjectPathVTable *, void *, DBusError *);
+    dbus_bool_t (*connection_send)(DBusConnection *, DBusMessage *, dbus_uint32_t *);
+    DBusMessage *(*connection_send_with_reply_and_block)(DBusConnection *, DBusMessage *, int, DBusError *);
+    void (*connection_close)(DBusConnection *);
+    void (*connection_ref)(DBusConnection *);
+    void (*connection_unref)(DBusConnection *);
+    void (*connection_flush)(DBusConnection *);
+    dbus_bool_t (*connection_read_write)(DBusConnection *, int);
+    DBusDispatchStatus (*connection_dispatch)(DBusConnection *);
+    dbus_bool_t (*message_is_signal)(DBusMessage *, const char *, const char *);
+    dbus_bool_t (*message_has_path)(DBusMessage *, const char *);
+    DBusMessage *(*message_new_method_call)(const char *, const char *, const char *, const char *);
+    dbus_bool_t (*message_append_args)(DBusMessage *, int, ...);
+    dbus_bool_t (*message_append_args_valist)(DBusMessage *, int, va_list);
+    void (*message_iter_init_append)(DBusMessage *, DBusMessageIter *);
+    dbus_bool_t (*message_iter_open_container)(DBusMessageIter *, int, const char *, DBusMessageIter *);
+    dbus_bool_t (*message_iter_append_basic)(DBusMessageIter *, int, const void *);
+    dbus_bool_t (*message_iter_close_container)(DBusMessageIter *, DBusMessageIter *);
+    dbus_bool_t (*message_get_args)(DBusMessage *, DBusError *, int, ...);
+    dbus_bool_t (*message_get_args_valist)(DBusMessage *, DBusError *, int, va_list);
+    dbus_bool_t (*message_iter_init)(DBusMessage *, DBusMessageIter *);
+    dbus_bool_t (*message_iter_next)(DBusMessageIter *);
+    void (*message_iter_get_basic)(DBusMessageIter *, void *);
+    int (*message_iter_get_arg_type)(DBusMessageIter *);
+    void (*message_iter_recurse)(DBusMessageIter *, DBusMessageIter *);
+    void (*message_unref)(DBusMessage *);
+    dbus_bool_t (*threads_init_default)(void);
+    void (*error_init)(DBusError *);
+    dbus_bool_t (*error_is_set)(const DBusError *);
+    void (*error_free)(DBusError *);
+    char *(*get_local_machine_id)(void);
+    char *(*try_get_local_machine_id)(DBusError *);
+    void (*free)(void *);
+    void (*free_string_array)(char **);
+    void (*shutdown)(void);
+
+} SDL_DBusContext;
+
+extern void SDL_DBus_Init(void);
+extern void SDL_DBus_Quit(void);
+extern SDL_DBusContext *SDL_DBus_GetContext(void);
+
+// These use the built-in Session connection.
+extern bool SDL_DBus_CallMethod(const char *node, const char *path, const char *interface, const char *method, ...);
+extern bool SDL_DBus_CallVoidMethod(const char *node, const char *path, const char *interface, const char *method, ...);
+extern bool SDL_DBus_QueryProperty(const char *node, const char *path, const char *interface, const char *property, int expectedtype, void *result);
+
+// These use whatever connection you like.
+extern bool SDL_DBus_CallMethodOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...);
+extern bool SDL_DBus_CallVoidMethodOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...);
+extern bool SDL_DBus_QueryPropertyOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *property, int expectedtype, void *result);
+
+extern void SDL_DBus_ScreensaverTickle(void);
+extern bool SDL_DBus_ScreensaverInhibit(bool inhibit);
+
+extern void SDL_DBus_PumpEvents(void);
+extern char *SDL_DBus_GetLocalMachineId(void);
+
+extern char **SDL_DBus_DocumentsPortalRetrieveFiles(const char *key, int *files_count);
+
+#endif // HAVE_DBUS_DBUS_H
+
+#endif // SDL_dbus_h_

+ 1037 - 0
thirdparty/sdl/core/linux/SDL_evdev.c

@@ -0,0 +1,1037 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#ifdef SDL_INPUT_LINUXEV
+
+// This is based on the linux joystick driver
+/* References: https://www.kernel.org/doc/Documentation/input/input.txt
+ *             https://www.kernel.org/doc/Documentation/input/event-codes.txt
+ *             /usr/include/linux/input.h
+ *             The evtest application is also useful to debug the protocol
+ */
+
+#include "SDL_evdev.h"
+#include "SDL_evdev_kbd.h"
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/input.h>
+
+#include "../../events/SDL_events_c.h"
+#include "../../core/linux/SDL_evdev_capabilities.h"
+#include "../../core/linux/SDL_udev.h"
+
+// These are not defined in older Linux kernel headers
+#ifndef SYN_DROPPED
+#define SYN_DROPPED 3
+#endif
+#ifndef ABS_MT_SLOT
+#define ABS_MT_SLOT        0x2f
+#define ABS_MT_POSITION_X  0x35
+#define ABS_MT_POSITION_Y  0x36
+#define ABS_MT_TRACKING_ID 0x39
+#define ABS_MT_PRESSURE    0x3a
+#endif
+#ifndef REL_WHEEL_HI_RES
+#define REL_WHEEL_HI_RES  0x0b
+#define REL_HWHEEL_HI_RES 0x0c
+#endif
+
+// The field to look up in struct input_event for integer seconds
+#ifndef input_event_sec
+#define input_event_sec time.tv_sec
+#endif
+
+// The field to look up in struct input_event for fractional seconds
+#ifndef input_event_usec
+#define input_event_usec time.tv_usec
+#endif
+
+typedef struct SDL_evdevlist_item
+{
+    char *path;
+    int fd;
+    int udev_class;
+
+    // TODO: use this for every device, not just touchscreen
+    bool out_of_sync;
+
+    /* TODO: expand on this to have data for every possible class (mouse,
+       keyboard, touchpad, etc.). Also there's probably some things in here we
+       can pull out to the SDL_evdevlist_item i.e. name */
+    bool is_touchscreen;
+    struct
+    {
+        char *name;
+
+        int min_x, max_x, range_x;
+        int min_y, max_y, range_y;
+        int min_pressure, max_pressure, range_pressure;
+
+        int max_slots;
+        int current_slot;
+        struct
+        {
+            enum
+            {
+                EVDEV_TOUCH_SLOTDELTA_NONE = 0,
+                EVDEV_TOUCH_SLOTDELTA_DOWN,
+                EVDEV_TOUCH_SLOTDELTA_UP,
+                EVDEV_TOUCH_SLOTDELTA_MOVE
+            } delta;
+            int tracking_id;
+            int x, y, pressure;
+        } *slots;
+
+    } *touchscreen_data;
+
+    // Mouse state
+    bool high_res_wheel;
+    bool high_res_hwheel;
+    bool relative_mouse;
+    int mouse_x, mouse_y;
+    int mouse_wheel, mouse_hwheel;
+    int min_x, max_x, range_x;
+    int min_y, max_y, range_y;
+
+    struct SDL_evdevlist_item *next;
+} SDL_evdevlist_item;
+
+typedef struct SDL_EVDEV_PrivateData
+{
+    int ref_count;
+    int num_devices;
+    SDL_evdevlist_item *first;
+    SDL_evdevlist_item *last;
+    SDL_EVDEV_keyboard_state *kbd;
+} SDL_EVDEV_PrivateData;
+
+static SDL_EVDEV_PrivateData *_this = NULL;
+
+static SDL_Scancode SDL_EVDEV_translate_keycode(int keycode);
+static void SDL_EVDEV_sync_device(SDL_evdevlist_item *item);
+static bool SDL_EVDEV_device_removed(const char *dev_path);
+static bool SDL_EVDEV_device_added(const char *dev_path, int udev_class);
+#ifdef SDL_USE_LIBUDEV
+static void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_event, int udev_class, const char *dev_path);
+#endif // SDL_USE_LIBUDEV
+
+static Uint8 EVDEV_MouseButtons[] = {
+    SDL_BUTTON_LEFT,   // BTN_LEFT        0x110
+    SDL_BUTTON_RIGHT,  // BTN_RIGHT       0x111
+    SDL_BUTTON_MIDDLE, // BTN_MIDDLE      0x112
+    SDL_BUTTON_X1,     // BTN_SIDE        0x113
+    SDL_BUTTON_X2,     // BTN_EXTRA       0x114
+    SDL_BUTTON_X2 + 1, // BTN_FORWARD     0x115
+    SDL_BUTTON_X2 + 2, // BTN_BACK        0x116
+    SDL_BUTTON_X2 + 3  // BTN_TASK        0x117
+};
+
+static bool SDL_EVDEV_SetRelativeMouseMode(bool enabled)
+{
+    // Mice already send relative events through this interface
+    return true;
+}
+
+static void SDL_EVDEV_UpdateKeyboardMute(void)
+{
+    if (SDL_EVDEV_GetDeviceCount(SDL_UDEV_DEVICE_KEYBOARD) > 0) {
+        SDL_EVDEV_kbd_set_muted(_this->kbd, true);
+    } else {
+        SDL_EVDEV_kbd_set_muted(_this->kbd, false);
+    }
+}
+
+bool SDL_EVDEV_Init(void)
+{
+    if (!_this) {
+        _this = (SDL_EVDEV_PrivateData *)SDL_calloc(1, sizeof(*_this));
+        if (!_this) {
+            return false;
+        }
+
+#ifdef SDL_USE_LIBUDEV
+        if (!SDL_UDEV_Init()) {
+            SDL_free(_this);
+            _this = NULL;
+            return false;
+        }
+
+        // Set up the udev callback
+        if (!SDL_UDEV_AddCallback(SDL_EVDEV_udev_callback)) {
+            SDL_UDEV_Quit();
+            SDL_free(_this);
+            _this = NULL;
+            return false;
+        }
+
+        // Force a scan to build the initial device list
+        SDL_UDEV_Scan();
+#else
+        {
+            /* Allow the user to specify a list of devices explicitly of
+               the form:
+                  deviceclass:path[,deviceclass:path[,...]]
+               where device class is an integer representing the
+               SDL_UDEV_deviceclass and path is the full path to
+               the event device. */
+            const char *devices = SDL_GetHint(SDL_HINT_EVDEV_DEVICES);
+            if (devices) {
+                /* Assume this is the old use of the env var and it is not in
+                   ROM. */
+                char *rest = (char *)devices;
+                char *spec;
+                while ((spec = SDL_strtok_r(rest, ",", &rest))) {
+                    char *endofcls = 0;
+                    long cls = SDL_strtol(spec, &endofcls, 0);
+                    if (endofcls) {
+                        SDL_EVDEV_device_added(endofcls + 1, cls);
+                    }
+                }
+            } else {
+                // TODO: Scan the devices manually, like a caveman
+            }
+        }
+#endif // SDL_USE_LIBUDEV
+
+        _this->kbd = SDL_EVDEV_kbd_init();
+
+        SDL_EVDEV_UpdateKeyboardMute();
+    }
+
+    //SDL_GetMouse()->SetRelativeMouseMode = SDL_EVDEV_SetRelativeMouseMode;
+
+    _this->ref_count += 1;
+
+    return true;
+}
+
+void SDL_EVDEV_Quit(void)
+{
+    if (!_this) {
+        return;
+    }
+
+    _this->ref_count -= 1;
+
+    if (_this->ref_count < 1) {
+#ifdef SDL_USE_LIBUDEV
+        SDL_UDEV_DelCallback(SDL_EVDEV_udev_callback);
+        SDL_UDEV_Quit();
+#endif // SDL_USE_LIBUDEV
+
+        // Remove existing devices
+        while (_this->first) {
+            SDL_EVDEV_device_removed(_this->first->path);
+        }
+
+        SDL_EVDEV_kbd_quit(_this->kbd);
+
+        SDL_assert(_this->first == NULL);
+        SDL_assert(_this->last == NULL);
+        SDL_assert(_this->num_devices == 0);
+
+        SDL_free(_this);
+        _this = NULL;
+    }
+}
+
+#ifdef SDL_USE_LIBUDEV
+static void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_event, int udev_class,
+                                    const char *dev_path)
+{
+    if (!dev_path) {
+        return;
+    }
+
+    switch (udev_event) {
+    case SDL_UDEV_DEVICEADDED:
+        if (!(udev_class & (SDL_UDEV_DEVICE_MOUSE | SDL_UDEV_DEVICE_HAS_KEYS | SDL_UDEV_DEVICE_TOUCHSCREEN | SDL_UDEV_DEVICE_TOUCHPAD))) {
+            return;
+        }
+
+        if (udev_class & SDL_UDEV_DEVICE_JOYSTICK) {
+            return;
+        }
+
+        SDL_EVDEV_device_added(dev_path, udev_class);
+        break;
+    case SDL_UDEV_DEVICEREMOVED:
+        SDL_EVDEV_device_removed(dev_path);
+        break;
+    default:
+        break;
+    }
+}
+#endif // SDL_USE_LIBUDEV
+
+void SDL_EVDEV_SetVTSwitchCallbacks(void (*release_callback)(void*), void *release_callback_data,
+                                    void (*acquire_callback)(void*), void *acquire_callback_data)
+{
+    SDL_EVDEV_kbd_set_vt_switch_callbacks(_this->kbd,
+                                          release_callback, release_callback_data,
+                                          acquire_callback, acquire_callback_data);
+}
+
+int SDL_EVDEV_GetDeviceCount(int device_class)
+{
+    SDL_evdevlist_item *item;
+    int count = 0;
+
+    for (item = _this->first; item; item = item->next) {
+        if ((item->udev_class & device_class) == device_class) {
+            ++count;
+        }
+    }
+    return count;
+}
+
+void SDL_EVDEV_Poll(void)
+{
+    struct input_event events[32];
+    int i, j, len;
+    SDL_evdevlist_item *item;
+    SDL_Scancode scancode;
+    int mouse_button;
+    SDL_Mouse *mouse;
+    float norm_x, norm_y, norm_pressure;
+
+    if (!_this) {
+        return;
+    }
+
+#ifdef SDL_USE_LIBUDEV
+    SDL_UDEV_Poll();
+#endif
+
+    SDL_EVDEV_kbd_update(_this->kbd);
+
+    mouse = NULL; //SDL_GetMouse();
+
+    for (item = _this->first; item; item = item->next) {
+        while ((len = read(item->fd, events, sizeof(events))) > 0) {
+            len /= sizeof(events[0]);
+            for (i = 0; i < len; ++i) {
+                struct input_event *event = &events[i];
+
+                /* special handling for touchscreen, that should eventually be
+                   used for all devices */
+                if (item->out_of_sync && item->is_touchscreen &&
+                    event->type == EV_SYN && event->code != SYN_REPORT) {
+                    break;
+                }
+
+                switch (event->type) {
+                case EV_KEY:
+					break;
+                    if (event->code >= BTN_MOUSE && event->code < BTN_MOUSE + SDL_arraysize(EVDEV_MouseButtons)) {
+                        Uint64 timestamp = SDL_EVDEV_GetEventTimestamp(event);
+                        mouse_button = event->code - BTN_MOUSE;
+                        //SDL_SendMouseButton(timestamp, mouse->focus, (SDL_MouseID)item->fd, EVDEV_MouseButtons[mouse_button], (event->value != 0));
+                        break;
+                    }
+
+                    /* BTN_TOUCH event value 1 indicates there is contact with
+                       a touchscreen or trackpad (earliest finger's current
+                       position is sent in EV_ABS ABS_X/ABS_Y, switching to
+                       next finger after earliest is released) */
+                    if (item->is_touchscreen && event->code == BTN_TOUCH) {
+                        if (item->touchscreen_data->max_slots == 1) {
+                            if (event->value) {
+                                item->touchscreen_data->slots[0].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
+                            } else {
+                                item->touchscreen_data->slots[0].delta = EVDEV_TOUCH_SLOTDELTA_UP;
+                            }
+                        }
+                        break;
+                    }
+
+                    // Probably keyboard
+                    {
+                        Uint64 timestamp = SDL_EVDEV_GetEventTimestamp(event);
+                        scancode = SDL_EVDEV_translate_keycode(event->code);
+                        // if (event->value == 0) {
+                        //     SDL_SendKeyboardKey(timestamp, (SDL_KeyboardID)item->fd, event->code, scancode, false);
+                        // } else if (event->value == 1 || event->value == 2 /* key repeated */) {
+                        //     SDL_SendKeyboardKey(timestamp, (SDL_KeyboardID)item->fd, event->code, scancode, true);
+                        // }
+                        SDL_EVDEV_kbd_keycode(_this->kbd, event->code, event->value);
+                    }
+                    break;
+                case EV_ABS:
+                    switch (event->code) {
+                    case ABS_MT_SLOT:
+                        if (!item->is_touchscreen) { // FIXME: temp hack
+                            break;
+                        }
+                        item->touchscreen_data->current_slot = event->value;
+                        break;
+                    case ABS_MT_TRACKING_ID:
+                        if (!item->is_touchscreen) { // FIXME: temp hack
+                            break;
+                        }
+                        if (event->value >= 0) {
+                            item->touchscreen_data->slots[item->touchscreen_data->current_slot].tracking_id = event->value + 1;
+                            item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
+                        } else {
+                            item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_UP;
+                        }
+                        break;
+                    case ABS_MT_POSITION_X:
+                        if (!item->is_touchscreen) { // FIXME: temp hack
+                            break;
+                        }
+                        item->touchscreen_data->slots[item->touchscreen_data->current_slot].x = event->value;
+                        if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
+                            item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
+                        }
+                        break;
+                    case ABS_MT_POSITION_Y:
+                        if (!item->is_touchscreen) { // FIXME: temp hack
+                            break;
+                        }
+                        item->touchscreen_data->slots[item->touchscreen_data->current_slot].y = event->value;
+                        if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
+                            item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
+                        }
+                        break;
+                    case ABS_MT_PRESSURE:
+                        if (!item->is_touchscreen) { // FIXME: temp hack
+                            break;
+                        }
+                        item->touchscreen_data->slots[item->touchscreen_data->current_slot].pressure = event->value;
+                        if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
+                            item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
+                        }
+                        break;
+                    case ABS_X:
+                        if (item->is_touchscreen) {
+                            if (item->touchscreen_data->max_slots != 1) {
+                                break;
+                            }
+                            item->touchscreen_data->slots[0].x = event->value;
+                        } else if (!item->relative_mouse) {
+                            item->mouse_x = event->value;
+                        }
+                        break;
+                    case ABS_Y:
+                        if (item->is_touchscreen) {
+                            if (item->touchscreen_data->max_slots != 1) {
+                                break;
+                            }
+                            item->touchscreen_data->slots[0].y = event->value;
+                        } else if (!item->relative_mouse) {
+                            item->mouse_y = event->value;
+                        }
+                        break;
+                    default:
+                        break;
+                    }
+                    break;
+                case EV_REL:
+                    switch (event->code) {
+                    case REL_X:
+                        if (item->relative_mouse) {
+                            item->mouse_x += event->value;
+                        }
+                        break;
+                    case REL_Y:
+                        if (item->relative_mouse) {
+                            item->mouse_y += event->value;
+                        }
+                        break;
+                    case REL_WHEEL:
+                        if (!item->high_res_wheel) {
+                            item->mouse_wheel += event->value;
+                        }
+                        break;
+                    case REL_WHEEL_HI_RES:
+                        SDL_assert(item->high_res_wheel);
+                        item->mouse_wheel += event->value;
+                        break;
+                    case REL_HWHEEL:
+                        if (!item->high_res_hwheel) {
+                            item->mouse_hwheel += event->value;
+                        }
+                        break;
+                    case REL_HWHEEL_HI_RES:
+                        SDL_assert(item->high_res_hwheel);
+                        item->mouse_hwheel += event->value;
+                        break;
+                    default:
+                        break;
+                    }
+                    break;
+                case EV_SYN:
+                    switch (event->code) {
+                    case SYN_REPORT:
+                        // Send mouse axis changes together to ensure consistency and reduce event processing overhead
+                        if (item->relative_mouse) {
+                            if (item->mouse_x != 0 || item->mouse_y != 0) {
+                                Uint64 timestamp = SDL_EVDEV_GetEventTimestamp(event);
+                                //SDL_SendMouseMotion(timestamp, mouse->focus, (SDL_MouseID)item->fd, item->relative_mouse, (float)item->mouse_x, (float)item->mouse_y);
+                                item->mouse_x = item->mouse_y = 0;
+                            }
+                        } else if (item->range_x > 0 && item->range_y > 0) {
+                            int screen_w = 0, screen_h = 0;
+                            const SDL_DisplayMode *mode = NULL;
+
+                            if (mode) {
+                                screen_w = mode->w;
+                                screen_h = mode->h;
+                            }
+                            //SDL_SendMouseMotion(SDL_EVDEV_GetEventTimestamp(event), mouse->focus, (SDL_MouseID)item->fd, item->relative_mouse,
+                            //    (float)(item->mouse_x - item->min_x) * screen_w / item->range_x,
+                            //    (float)(item->mouse_y - item->min_y) * screen_h / item->range_y);
+                        }
+
+                        if (item->mouse_wheel != 0 || item->mouse_hwheel != 0) {
+                            Uint64 timestamp = SDL_EVDEV_GetEventTimestamp(event);
+                            const float denom = (item->high_res_hwheel ? 120.0f : 1.0f);
+                            //SDL_SendMouseWheel(timestamp,
+                            //                   mouse->focus, (SDL_MouseID)item->fd,
+                            //                   item->mouse_hwheel / denom,
+                            //                   item->mouse_wheel / denom,
+                            //                   SDL_MOUSEWHEEL_NORMAL);
+                            item->mouse_wheel = item->mouse_hwheel = 0;
+                        }
+
+                        if (!item->is_touchscreen) { // FIXME: temp hack
+                            break;
+                        }
+
+                        for (j = 0; j < item->touchscreen_data->max_slots; j++) {
+                            norm_x = (float)(item->touchscreen_data->slots[j].x - item->touchscreen_data->min_x) /
+                                     (float)item->touchscreen_data->range_x;
+                            norm_y = (float)(item->touchscreen_data->slots[j].y - item->touchscreen_data->min_y) /
+                                     (float)item->touchscreen_data->range_y;
+
+                            if (item->touchscreen_data->range_pressure > 0) {
+                                norm_pressure = (float)(item->touchscreen_data->slots[j].pressure - item->touchscreen_data->min_pressure) /
+                                                (float)item->touchscreen_data->range_pressure;
+                            } else {
+                                // This touchscreen does not support pressure
+                                norm_pressure = 1.0f;
+                            }
+
+                            /* FIXME: the touch's window shouldn't be null, but
+                             * the coordinate space of touch positions needs to
+                             * be window-relative in that case. */
+                            switch (item->touchscreen_data->slots[j].delta) {
+                            case EVDEV_TOUCH_SLOTDELTA_DOWN:
+                                //SDL_SendTouch(SDL_EVDEV_GetEventTimestamp(event), item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, SDL_EVENT_FINGER_DOWN, norm_x, norm_y, norm_pressure);
+                                item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
+                                break;
+                            case EVDEV_TOUCH_SLOTDELTA_UP:
+                                //SDL_SendTouch(SDL_EVDEV_GetEventTimestamp(event), item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, SDL_EVENT_FINGER_UP, norm_x, norm_y, norm_pressure);
+                                item->touchscreen_data->slots[j].tracking_id = 0;
+                                item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
+                                break;
+                            case EVDEV_TOUCH_SLOTDELTA_MOVE:
+                                //SDL_SendTouchMotion(SDL_EVDEV_GetEventTimestamp(event), item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, norm_x, norm_y, norm_pressure);
+                                item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
+                                break;
+                            default:
+                                break;
+                            }
+                        }
+
+                        if (item->out_of_sync) {
+                            item->out_of_sync = false;
+                        }
+                        break;
+                    case SYN_DROPPED:
+                        if (item->is_touchscreen) {
+                            item->out_of_sync = true;
+                        }
+                        SDL_EVDEV_sync_device(item);
+                        break;
+                    default:
+                        break;
+                    }
+                    break;
+                }
+            }
+        }
+    }
+}
+
+static SDL_Scancode SDL_EVDEV_translate_keycode(int keycode)
+{
+    //SDL_Scancode scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_LINUX, keycode);
+
+#ifdef DEBUG_SCANCODES
+    if (scancode == SDL_SCANCODE_UNKNOWN) {
+        /* BTN_TOUCH is handled elsewhere, but we might still end up here if
+           you get an unexpected BTN_TOUCH from something SDL believes is not
+           a touch device. In this case, we'd rather not get a misleading
+           SDL_Log message about an unknown key. */
+        if (keycode != BTN_TOUCH) {
+            SDL_Log("The key you just pressed is not recognized by SDL. To help "
+                    "get this fixed, please report this to the SDL forums/mailing list "
+                    "<https://discourse.libsdl.org/> EVDEV KeyCode %d",
+                    keycode);
+        }
+    }
+#endif // DEBUG_SCANCODES
+
+    return 0; //scancode;
+}
+
+static bool SDL_EVDEV_init_keyboard(SDL_evdevlist_item *item, int udev_class)
+{
+    char name[128];
+
+    name[0] = '\0';
+    ioctl(item->fd, EVIOCGNAME(sizeof(name)), name);
+
+    //SDL_AddKeyboard((SDL_KeyboardID)item->fd, name, true);
+
+    return true;
+}
+
+static void SDL_EVDEV_destroy_keyboard(SDL_evdevlist_item *item)
+{
+    //SDL_RemoveKeyboard((SDL_KeyboardID)item->fd, true);
+}
+
+static bool SDL_EVDEV_init_mouse(SDL_evdevlist_item *item, int udev_class)
+{
+    char name[128];
+    int ret;
+    struct input_absinfo abs_info;
+
+    name[0] = '\0';
+    ioctl(item->fd, EVIOCGNAME(sizeof(name)), name);
+
+    //SDL_AddMouse((SDL_MouseID)item->fd, name, true);
+
+    ret = ioctl(item->fd, EVIOCGABS(ABS_X), &abs_info);
+    if (ret < 0) {
+        // no absolute mode info, continue
+        return true;
+    }
+    item->min_x = abs_info.minimum;
+    item->max_x = abs_info.maximum;
+    item->range_x = abs_info.maximum - abs_info.minimum;
+
+    ret = ioctl(item->fd, EVIOCGABS(ABS_Y), &abs_info);
+    if (ret < 0) {
+        // no absolute mode info, continue
+        return true;
+    }
+    item->min_y = abs_info.minimum;
+    item->max_y = abs_info.maximum;
+    item->range_y = abs_info.maximum - abs_info.minimum;
+
+    return true;
+}
+
+static void SDL_EVDEV_destroy_mouse(SDL_evdevlist_item *item)
+{
+    //SDL_RemoveMouse((SDL_MouseID)item->fd, true);
+}
+
+static bool SDL_EVDEV_init_touchscreen(SDL_evdevlist_item *item, int udev_class)
+{
+    int ret;
+    unsigned long xreq, yreq;
+    char name[64];
+    struct input_absinfo abs_info;
+
+    if (!item->is_touchscreen) {
+        return true;
+    }
+
+    item->touchscreen_data = SDL_calloc(1, sizeof(*item->touchscreen_data));
+    if (!item->touchscreen_data) {
+        return false;
+    }
+
+    ret = ioctl(item->fd, EVIOCGNAME(sizeof(name)), name);
+    if (ret < 0) {
+        SDL_free(item->touchscreen_data);
+        return SDL_SetError("Failed to get evdev touchscreen name");
+    }
+
+    item->touchscreen_data->name = SDL_strdup(name);
+    if (!item->touchscreen_data->name) {
+        SDL_free(item->touchscreen_data);
+        return false;
+    }
+
+    ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
+    if (ret < 0) {
+        SDL_free(item->touchscreen_data->name);
+        SDL_free(item->touchscreen_data);
+        return SDL_SetError("Failed to get evdev touchscreen limits");
+    }
+
+    if (abs_info.maximum == 0) {
+        item->touchscreen_data->max_slots = 1;
+        xreq = EVIOCGABS(ABS_X);
+        yreq = EVIOCGABS(ABS_Y);
+    } else {
+        item->touchscreen_data->max_slots = abs_info.maximum + 1;
+        xreq = EVIOCGABS(ABS_MT_POSITION_X);
+        yreq = EVIOCGABS(ABS_MT_POSITION_Y);
+    }
+
+    ret = ioctl(item->fd, xreq, &abs_info);
+    if (ret < 0) {
+        SDL_free(item->touchscreen_data->name);
+        SDL_free(item->touchscreen_data);
+        return SDL_SetError("Failed to get evdev touchscreen limits");
+    }
+    item->touchscreen_data->min_x = abs_info.minimum;
+    item->touchscreen_data->max_x = abs_info.maximum;
+    item->touchscreen_data->range_x = abs_info.maximum - abs_info.minimum;
+
+    ret = ioctl(item->fd, yreq, &abs_info);
+    if (ret < 0) {
+        SDL_free(item->touchscreen_data->name);
+        SDL_free(item->touchscreen_data);
+        return SDL_SetError("Failed to get evdev touchscreen limits");
+    }
+    item->touchscreen_data->min_y = abs_info.minimum;
+    item->touchscreen_data->max_y = abs_info.maximum;
+    item->touchscreen_data->range_y = abs_info.maximum - abs_info.minimum;
+
+    ret = ioctl(item->fd, EVIOCGABS(ABS_MT_PRESSURE), &abs_info);
+    if (ret < 0) {
+        SDL_free(item->touchscreen_data->name);
+        SDL_free(item->touchscreen_data);
+        return SDL_SetError("Failed to get evdev touchscreen limits");
+    }
+    item->touchscreen_data->min_pressure = abs_info.minimum;
+    item->touchscreen_data->max_pressure = abs_info.maximum;
+    item->touchscreen_data->range_pressure = abs_info.maximum - abs_info.minimum;
+
+    item->touchscreen_data->slots = SDL_calloc(
+        item->touchscreen_data->max_slots,
+        sizeof(*item->touchscreen_data->slots));
+    if (!item->touchscreen_data->slots) {
+        SDL_free(item->touchscreen_data->name);
+        SDL_free(item->touchscreen_data);
+        return false;
+    }
+
+    //ret = SDL_AddTouch(item->fd, // I guess our fd is unique enough
+    //                   (udev_class & SDL_UDEV_DEVICE_TOUCHPAD) ? SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE : SDL_TOUCH_DEVICE_DIRECT,
+    //                   item->touchscreen_data->name);
+	ret = -1;
+    if (ret < 0) {
+        SDL_free(item->touchscreen_data->slots);
+        SDL_free(item->touchscreen_data->name);
+        SDL_free(item->touchscreen_data);
+        return false;
+    }
+
+    return true;
+}
+
+static void SDL_EVDEV_destroy_touchscreen(SDL_evdevlist_item *item)
+{
+    if (!item->is_touchscreen) {
+        return;
+    }
+
+    //SDL_DelTouch(item->fd);
+    SDL_free(item->touchscreen_data->slots);
+    SDL_free(item->touchscreen_data->name);
+    SDL_free(item->touchscreen_data);
+}
+
+static void SDL_EVDEV_sync_device(SDL_evdevlist_item *item)
+{
+#ifdef EVIOCGMTSLOTS
+    int i, ret;
+    struct input_absinfo abs_info;
+    /*
+     * struct input_mt_request_layout {
+     *     __u32 code;
+     *     __s32 values[num_slots];
+     * };
+     *
+     * this is the structure we're trying to emulate
+     */
+    Uint32 *mt_req_code;
+    Sint32 *mt_req_values;
+    size_t mt_req_size;
+
+    // TODO: sync devices other than touchscreen
+    if (!item->is_touchscreen) {
+        return;
+    }
+
+    mt_req_size = sizeof(*mt_req_code) +
+                  sizeof(*mt_req_values) * item->touchscreen_data->max_slots;
+
+    mt_req_code = SDL_calloc(1, mt_req_size);
+    if (!mt_req_code) {
+        return;
+    }
+
+    mt_req_values = (Sint32 *)mt_req_code + 1;
+
+    *mt_req_code = ABS_MT_TRACKING_ID;
+    ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
+    if (ret < 0) {
+        SDL_free(mt_req_code);
+        return;
+    }
+    for (i = 0; i < item->touchscreen_data->max_slots; i++) {
+        /*
+         * This doesn't account for the very edge case of the user removing their
+         * finger and replacing it on the screen during the time we're out of sync,
+         * which'll mean that we're not going from down -> up or up -> down, we're
+         * going from down -> down but with a different tracking id, meaning we'd
+         * have to tell SDL of the two events, but since we wait till SYN_REPORT in
+         * SDL_EVDEV_Poll to tell SDL, the current structure of this code doesn't
+         * allow it. Lets just pray to God it doesn't happen.
+         */
+        if (item->touchscreen_data->slots[i].tracking_id == 0 &&
+            mt_req_values[i] >= 0) {
+            item->touchscreen_data->slots[i].tracking_id = mt_req_values[i] + 1;
+            item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
+        } else if (item->touchscreen_data->slots[i].tracking_id != 0 &&
+                   mt_req_values[i] < 0) {
+            item->touchscreen_data->slots[i].tracking_id = 0;
+            item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_UP;
+        }
+    }
+
+    *mt_req_code = ABS_MT_POSITION_X;
+    ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
+    if (ret < 0) {
+        SDL_free(mt_req_code);
+        return;
+    }
+    for (i = 0; i < item->touchscreen_data->max_slots; i++) {
+        if (item->touchscreen_data->slots[i].tracking_id != 0 &&
+            item->touchscreen_data->slots[i].x != mt_req_values[i]) {
+            item->touchscreen_data->slots[i].x = mt_req_values[i];
+            if (item->touchscreen_data->slots[i].delta ==
+                EVDEV_TOUCH_SLOTDELTA_NONE) {
+                item->touchscreen_data->slots[i].delta =
+                    EVDEV_TOUCH_SLOTDELTA_MOVE;
+            }
+        }
+    }
+
+    *mt_req_code = ABS_MT_POSITION_Y;
+    ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
+    if (ret < 0) {
+        SDL_free(mt_req_code);
+        return;
+    }
+    for (i = 0; i < item->touchscreen_data->max_slots; i++) {
+        if (item->touchscreen_data->slots[i].tracking_id != 0 &&
+            item->touchscreen_data->slots[i].y != mt_req_values[i]) {
+            item->touchscreen_data->slots[i].y = mt_req_values[i];
+            if (item->touchscreen_data->slots[i].delta ==
+                EVDEV_TOUCH_SLOTDELTA_NONE) {
+                item->touchscreen_data->slots[i].delta =
+                    EVDEV_TOUCH_SLOTDELTA_MOVE;
+            }
+        }
+    }
+
+    *mt_req_code = ABS_MT_PRESSURE;
+    ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
+    if (ret < 0) {
+        SDL_free(mt_req_code);
+        return;
+    }
+    for (i = 0; i < item->touchscreen_data->max_slots; i++) {
+        if (item->touchscreen_data->slots[i].tracking_id != 0 &&
+            item->touchscreen_data->slots[i].pressure != mt_req_values[i]) {
+            item->touchscreen_data->slots[i].pressure = mt_req_values[i];
+            if (item->touchscreen_data->slots[i].delta ==
+                EVDEV_TOUCH_SLOTDELTA_NONE) {
+                item->touchscreen_data->slots[i].delta =
+                    EVDEV_TOUCH_SLOTDELTA_MOVE;
+            }
+        }
+    }
+
+    ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
+    if (ret < 0) {
+        SDL_free(mt_req_code);
+        return;
+    }
+    item->touchscreen_data->current_slot = abs_info.value;
+
+    SDL_free(mt_req_code);
+
+#endif // EVIOCGMTSLOTS
+}
+
+static bool SDL_EVDEV_device_added(const char *dev_path, int udev_class)
+{
+    SDL_evdevlist_item *item;
+    unsigned long relbit[NBITS(REL_MAX)] = { 0 };
+
+    // Check to make sure it's not already in list.
+    for (item = _this->first; item; item = item->next) {
+        if (SDL_strcmp(dev_path, item->path) == 0) {
+            return false; // already have this one
+        }
+    }
+
+    item = (SDL_evdevlist_item *)SDL_calloc(1, sizeof(SDL_evdevlist_item));
+    if (!item) {
+        return false;
+    }
+
+    item->fd = open(dev_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+    if (item->fd < 0) {
+        SDL_free(item);
+        return SDL_SetError("Unable to open %s", dev_path);
+    }
+
+    item->path = SDL_strdup(dev_path);
+    if (!item->path) {
+        close(item->fd);
+        SDL_free(item);
+        return false;
+    }
+
+    item->udev_class = udev_class;
+
+    if (ioctl(item->fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit) >= 0) {
+        item->relative_mouse = test_bit(REL_X, relbit) && test_bit(REL_Y, relbit);
+        item->high_res_wheel = test_bit(REL_WHEEL_HI_RES, relbit);
+        item->high_res_hwheel = test_bit(REL_HWHEEL_HI_RES, relbit);
+    }
+
+    // For now, we just treat a touchpad like a touchscreen
+    if (udev_class & (SDL_UDEV_DEVICE_TOUCHSCREEN | SDL_UDEV_DEVICE_TOUCHPAD)) {
+        item->is_touchscreen = true;
+        if (!SDL_EVDEV_init_touchscreen(item, udev_class)) {
+            close(item->fd);
+            SDL_free(item->path);
+            SDL_free(item);
+            return false;
+        }
+    }
+
+    if (udev_class & SDL_UDEV_DEVICE_MOUSE) {
+        if (!SDL_EVDEV_init_mouse(item, udev_class)) {
+            close(item->fd);
+            SDL_free(item->path);
+            SDL_free(item);
+            return false;
+        }
+    }
+
+    if (udev_class & SDL_UDEV_DEVICE_KEYBOARD) {
+        if (!SDL_EVDEV_init_keyboard(item, udev_class)) {
+            close(item->fd);
+            SDL_free(item->path);
+            SDL_free(item);
+            return false;
+        }
+    }
+
+    if (!_this->last) {
+        _this->first = _this->last = item;
+    } else {
+        _this->last->next = item;
+        _this->last = item;
+    }
+
+    SDL_EVDEV_sync_device(item);
+
+    SDL_EVDEV_UpdateKeyboardMute();
+
+    ++_this->num_devices;
+    return true;
+}
+
+static bool SDL_EVDEV_device_removed(const char *dev_path)
+{
+    SDL_evdevlist_item *item;
+    SDL_evdevlist_item *prev = NULL;
+
+    for (item = _this->first; item; item = item->next) {
+        // found it, remove it.
+        if (SDL_strcmp(dev_path, item->path) == 0) {
+            if (prev) {
+                prev->next = item->next;
+            } else {
+                SDL_assert(_this->first == item);
+                _this->first = item->next;
+            }
+            if (item == _this->last) {
+                _this->last = prev;
+            }
+
+            if (item->is_touchscreen) {
+                SDL_EVDEV_destroy_touchscreen(item);
+            }
+            if (item->udev_class & SDL_UDEV_DEVICE_MOUSE) {
+                SDL_EVDEV_destroy_mouse(item);
+            }
+            if (item->udev_class & SDL_UDEV_DEVICE_KEYBOARD) {
+                SDL_EVDEV_destroy_keyboard(item);
+            }
+            close(item->fd);
+            SDL_free(item->path);
+            SDL_free(item);
+            SDL_EVDEV_UpdateKeyboardMute();
+            _this->num_devices--;
+            return true;
+        }
+        prev = item;
+    }
+
+    return false;
+}
+
+Uint64 SDL_EVDEV_GetEventTimestamp(struct input_event *event)
+{
+    static Uint64 timestamp_offset;
+    Uint64 timestamp;
+    Uint64 now = SDL_GetTicksNS();
+
+    /* The kernel internally has nanosecond timestamps, but converts it
+       to microseconds when delivering the events */
+    timestamp = event->input_event_sec;
+    timestamp *= SDL_NS_PER_SECOND;
+    timestamp += SDL_US_TO_NS(event->input_event_usec);
+
+    if (!timestamp_offset) {
+        timestamp_offset = (now - timestamp);
+    }
+    timestamp += timestamp_offset;
+
+    if (timestamp > now) {
+        timestamp_offset -= (timestamp - now);
+        timestamp = now;
+    }
+    return timestamp;
+}
+
+#endif // SDL_INPUT_LINUXEV

+ 41 - 0
thirdparty/sdl/core/linux/SDL_evdev.h

@@ -0,0 +1,41 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "SDL_internal.h"
+
+#ifndef SDL_evdev_h_
+#define SDL_evdev_h_
+
+#ifdef SDL_INPUT_LINUXEV
+
+struct input_event;
+
+extern bool SDL_EVDEV_Init(void);
+extern void SDL_EVDEV_Quit(void);
+extern void SDL_EVDEV_SetVTSwitchCallbacks(void (*release_callback)(void*), void *release_callback_data,
+                                           void (*acquire_callback)(void*), void *acquire_callback_data);
+extern int SDL_EVDEV_GetDeviceCount(int device_class);
+extern void SDL_EVDEV_Poll(void);
+extern Uint64 SDL_EVDEV_GetEventTimestamp(struct input_event *event);
+
+#endif // SDL_INPUT_LINUXEV
+
+#endif // SDL_evdev_h_

+ 168 - 0
thirdparty/sdl/core/linux/SDL_evdev_capabilities.c

@@ -0,0 +1,168 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+  Copyright (C) 2020 Collabora Ltd.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#include "SDL_evdev_capabilities.h"
+
+#ifdef HAVE_LINUX_INPUT_H
+
+// missing defines in older Linux kernel headers
+#ifndef BTN_TRIGGER_HAPPY
+#define BTN_TRIGGER_HAPPY 0x2c0
+#endif
+#ifndef BTN_DPAD_UP
+#define BTN_DPAD_UP 0x220
+#endif
+#ifndef KEY_ALS_TOGGLE
+#define KEY_ALS_TOGGLE 0x230
+#endif
+
+extern int
+SDL_EVDEV_GuessDeviceClass(const unsigned long bitmask_props[NBITS(INPUT_PROP_MAX)],
+                           const unsigned long bitmask_ev[NBITS(EV_MAX)],
+                           const unsigned long bitmask_abs[NBITS(ABS_MAX)],
+                           const unsigned long bitmask_key[NBITS(KEY_MAX)],
+                           const unsigned long bitmask_rel[NBITS(REL_MAX)])
+{
+    struct range
+    {
+        unsigned start;
+        unsigned end;
+    };
+
+    // key code ranges above BTN_MISC (start is inclusive, stop is exclusive)
+    static const struct range high_key_blocks[] = {
+        { KEY_OK, BTN_DPAD_UP },
+        { KEY_ALS_TOGGLE, BTN_TRIGGER_HAPPY }
+    };
+
+    int devclass = 0;
+    unsigned long keyboard_mask;
+
+    // If the kernel specifically says it's an accelerometer, believe it
+    if (test_bit(INPUT_PROP_ACCELEROMETER, bitmask_props)) {
+        return SDL_UDEV_DEVICE_ACCELEROMETER;
+    }
+
+    // We treat pointing sticks as indistinguishable from mice
+    if (test_bit(INPUT_PROP_POINTING_STICK, bitmask_props)) {
+        return SDL_UDEV_DEVICE_MOUSE;
+    }
+
+    // We treat buttonpads as equivalent to touchpads
+    if (test_bit(INPUT_PROP_TOPBUTTONPAD, bitmask_props) ||
+        test_bit(INPUT_PROP_BUTTONPAD, bitmask_props) ||
+        test_bit(INPUT_PROP_SEMI_MT, bitmask_props)) {
+        return SDL_UDEV_DEVICE_TOUCHPAD;
+    }
+
+    // X, Y, Z axes but no buttons probably means an accelerometer
+    if (test_bit(EV_ABS, bitmask_ev) &&
+        test_bit(ABS_X, bitmask_abs) &&
+        test_bit(ABS_Y, bitmask_abs) &&
+        test_bit(ABS_Z, bitmask_abs) &&
+        !test_bit(EV_KEY, bitmask_ev)) {
+        return SDL_UDEV_DEVICE_ACCELEROMETER;
+    }
+
+    /* RX, RY, RZ axes but no buttons probably means a gyro or
+     * accelerometer (we don't distinguish) */
+    if (test_bit(EV_ABS, bitmask_ev) &&
+        test_bit(ABS_RX, bitmask_abs) &&
+        test_bit(ABS_RY, bitmask_abs) &&
+        test_bit(ABS_RZ, bitmask_abs) &&
+        !test_bit(EV_KEY, bitmask_ev)) {
+        return SDL_UDEV_DEVICE_ACCELEROMETER;
+    }
+
+    if (test_bit(EV_ABS, bitmask_ev) &&
+        test_bit(ABS_X, bitmask_abs) && test_bit(ABS_Y, bitmask_abs)) {
+        if (test_bit(BTN_STYLUS, bitmask_key) || test_bit(BTN_TOOL_PEN, bitmask_key)) {
+            ; // ID_INPUT_TABLET
+        } else if (test_bit(BTN_TOOL_FINGER, bitmask_key) && !test_bit(BTN_TOOL_PEN, bitmask_key)) {
+            devclass |= SDL_UDEV_DEVICE_TOUCHPAD; // ID_INPUT_TOUCHPAD
+        } else if (test_bit(BTN_MOUSE, bitmask_key)) {
+            devclass |= SDL_UDEV_DEVICE_MOUSE; // ID_INPUT_MOUSE
+        } else if (test_bit(BTN_TOUCH, bitmask_key)) {
+            /* TODO: better determining between touchscreen and multitouch touchpad,
+               see https://github.com/systemd/systemd/blob/master/src/udev/udev-builtin-input_id.c */
+            devclass |= SDL_UDEV_DEVICE_TOUCHSCREEN; // ID_INPUT_TOUCHSCREEN
+        }
+
+        if (test_bit(BTN_TRIGGER, bitmask_key) ||
+            test_bit(BTN_A, bitmask_key) ||
+            test_bit(BTN_1, bitmask_key) ||
+            test_bit(ABS_RX, bitmask_abs) ||
+            test_bit(ABS_RY, bitmask_abs) ||
+            test_bit(ABS_RZ, bitmask_abs) ||
+            test_bit(ABS_THROTTLE, bitmask_abs) ||
+            test_bit(ABS_RUDDER, bitmask_abs) ||
+            test_bit(ABS_WHEEL, bitmask_abs) ||
+            test_bit(ABS_GAS, bitmask_abs) ||
+            test_bit(ABS_BRAKE, bitmask_abs)) {
+            devclass |= SDL_UDEV_DEVICE_JOYSTICK; // ID_INPUT_JOYSTICK
+        }
+    }
+
+    if (test_bit(EV_REL, bitmask_ev) &&
+        test_bit(REL_X, bitmask_rel) && test_bit(REL_Y, bitmask_rel) &&
+        test_bit(BTN_MOUSE, bitmask_key)) {
+        devclass |= SDL_UDEV_DEVICE_MOUSE; // ID_INPUT_MOUSE
+    }
+
+    if (test_bit(EV_KEY, bitmask_ev)) {
+        unsigned i;
+        unsigned long found = 0;
+
+        for (i = 0; i < BTN_MISC / BITS_PER_LONG; ++i) {
+            found |= bitmask_key[i];
+        }
+        // If there are no keys in the lower block, check the higher blocks
+        if (!found) {
+            unsigned block;
+            for (block = 0; block < (sizeof(high_key_blocks) / sizeof(struct range)); ++block) {
+                for (i = high_key_blocks[block].start; i < high_key_blocks[block].end; ++i) {
+                    if (test_bit(i, bitmask_key)) {
+                        found = 1;
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (found > 0) {
+            devclass |= SDL_UDEV_DEVICE_HAS_KEYS; // ID_INPUT_KEY
+        }
+    }
+
+    /* the first 32 bits are ESC, numbers, and Q to D, so if we have all of
+     * those, consider it to be a fully-featured keyboard;
+     * do not test KEY_RESERVED, though */
+    keyboard_mask = 0xFFFFFFFE;
+    if ((bitmask_key[0] & keyboard_mask) == keyboard_mask) {
+        devclass |= SDL_UDEV_DEVICE_KEYBOARD; // ID_INPUT_KEYBOARD
+    }
+
+    return devclass;
+}
+
+#endif // HAVE_LINUX_INPUT_H

+ 76 - 0
thirdparty/sdl/core/linux/SDL_evdev_capabilities.h

@@ -0,0 +1,76 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+  Copyright (C) 2020 Collabora Ltd.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#ifndef SDL_evdev_capabilities_h_
+#define SDL_evdev_capabilities_h_
+
+#ifdef HAVE_LINUX_INPUT_H
+
+#include <linux/input.h>
+
+#ifndef INPUT_PROP_SEMI_MT
+#define INPUT_PROP_SEMI_MT          0x03
+#endif
+#ifndef INPUT_PROP_TOPBUTTONPAD
+#define INPUT_PROP_TOPBUTTONPAD     0x04
+#endif
+#ifndef INPUT_PROP_POINTING_STICK
+#define INPUT_PROP_POINTING_STICK   0x05
+#endif
+#ifndef INPUT_PROP_ACCELEROMETER
+#define INPUT_PROP_ACCELEROMETER    0x06
+#endif
+#ifndef INPUT_PROP_MAX
+#define INPUT_PROP_MAX 0x1f
+#endif
+
+// A device can be any combination of these classes
+typedef enum
+{
+    SDL_UDEV_DEVICE_UNKNOWN = 0x0000,
+    SDL_UDEV_DEVICE_MOUSE = 0x0001,
+    SDL_UDEV_DEVICE_KEYBOARD = 0x0002,
+    SDL_UDEV_DEVICE_JOYSTICK = 0x0004,
+    SDL_UDEV_DEVICE_SOUND = 0x0008,
+    SDL_UDEV_DEVICE_TOUCHSCREEN = 0x0010,
+    SDL_UDEV_DEVICE_ACCELEROMETER = 0x0020,
+    SDL_UDEV_DEVICE_TOUCHPAD = 0x0040,
+    SDL_UDEV_DEVICE_HAS_KEYS = 0x0080,
+    SDL_UDEV_DEVICE_VIDEO_CAPTURE = 0x0100,
+} SDL_UDEV_deviceclass;
+
+#define BITS_PER_LONG        (sizeof(unsigned long) * 8)
+#define NBITS(x)             ((((x)-1) / BITS_PER_LONG) + 1)
+#define EVDEV_OFF(x)         ((x) % BITS_PER_LONG)
+#define EVDEV_LONG(x)        ((x) / BITS_PER_LONG)
+#define test_bit(bit, array) ((array[EVDEV_LONG(bit)] >> EVDEV_OFF(bit)) & 1)
+
+extern int SDL_EVDEV_GuessDeviceClass(const unsigned long bitmask_props[NBITS(INPUT_PROP_MAX)],
+                                      const unsigned long bitmask_ev[NBITS(EV_MAX)],
+                                      const unsigned long bitmask_abs[NBITS(ABS_MAX)],
+                                      const unsigned long bitmask_key[NBITS(KEY_MAX)],
+                                      const unsigned long bitmask_rel[NBITS(REL_MAX)]);
+
+#endif // HAVE_LINUX_INPUT_H
+
+#endif // SDL_evdev_capabilities_h_

+ 997 - 0
thirdparty/sdl/core/linux/SDL_evdev_kbd.c

@@ -0,0 +1,997 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#include "SDL_evdev_kbd.h"
+
+#ifdef SDL_INPUT_LINUXKD
+
+// This logic is adapted from drivers/tty/vt/keyboard.c in the Linux kernel source
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/kd.h>
+#include <linux/keyboard.h>
+#include <linux/vt.h>
+#include <linux/tiocl.h> // for TIOCL_GETSHIFTSTATE
+
+#include <signal.h>
+
+#include "../../events/SDL_events_c.h"
+#include "SDL_evdev_kbd_default_accents.h"
+#include "SDL_evdev_kbd_default_keymap.h"
+
+// These are not defined in older Linux kernel headers
+#ifndef K_UNICODE
+#define K_UNICODE 0x03
+#endif
+#ifndef K_OFF
+#define K_OFF 0x04
+#endif
+
+/*
+ * Handler Tables.
+ */
+
+#define K_HANDLERS                            \
+    k_self, k_fn, k_spec, k_pad,              \
+        k_dead, k_cons, k_cur, k_shift,       \
+        k_meta, k_ascii, k_lock, k_lowercase, \
+        k_slock, k_dead2, k_brl, k_ignore
+
+typedef void(k_handler_fn)(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag);
+static k_handler_fn K_HANDLERS;
+static k_handler_fn *k_handler[16] = { K_HANDLERS };
+
+typedef void(fn_handler_fn)(SDL_EVDEV_keyboard_state *kbd);
+static void fn_enter(SDL_EVDEV_keyboard_state *kbd);
+static void fn_caps_toggle(SDL_EVDEV_keyboard_state *kbd);
+static void fn_caps_on(SDL_EVDEV_keyboard_state *kbd);
+static void fn_num(SDL_EVDEV_keyboard_state *kbd);
+static void fn_compose(SDL_EVDEV_keyboard_state *kbd);
+
+static fn_handler_fn *fn_handler[] = {
+    NULL, fn_enter, NULL, NULL,
+    NULL, NULL, NULL, fn_caps_toggle,
+    fn_num, NULL, NULL, NULL,
+    NULL, fn_caps_on, fn_compose, NULL,
+    NULL, NULL, NULL, fn_num
+};
+
+/*
+ * Keyboard State
+ */
+
+struct SDL_EVDEV_keyboard_state
+{
+    int console_fd;
+    bool muted;
+    int old_kbd_mode;
+    unsigned short **key_maps;
+    unsigned char shift_down[NR_SHIFT]; // shift state counters..
+    bool dead_key_next;
+    int npadch; // -1 or number assembled on pad
+    struct kbdiacrs *accents;
+    unsigned int diacr;
+    bool rep; // flag telling character repeat
+    unsigned char lockstate;
+    unsigned char slockstate;
+    unsigned char ledflagstate;
+    char shift_state;
+    char text[128];
+    unsigned int text_len;
+    void (*vt_release_callback)(void *);
+    void *vt_release_callback_data;
+    void (*vt_acquire_callback)(void *);
+    void *vt_acquire_callback_data;
+};
+
+#ifdef DUMP_ACCENTS
+static void SDL_EVDEV_dump_accents(SDL_EVDEV_keyboard_state *kbd)
+{
+    unsigned int i;
+
+    printf("static struct kbdiacrs default_accents = {\n");
+    printf("    %d,\n", kbd->accents->kb_cnt);
+    printf("    {\n");
+    for (i = 0; i < kbd->accents->kb_cnt; ++i) {
+        struct kbdiacr *diacr = &kbd->accents->kbdiacr[i];
+        printf("        { 0x%.2x, 0x%.2x, 0x%.2x },\n",
+               diacr->diacr, diacr->base, diacr->result);
+    }
+    while (i < 256) {
+        printf("        { 0x00, 0x00, 0x00 },\n");
+        ++i;
+    }
+    printf("    }\n");
+    printf("};\n");
+}
+#endif // DUMP_ACCENTS
+
+#ifdef DUMP_KEYMAP
+static void SDL_EVDEV_dump_keymap(SDL_EVDEV_keyboard_state *kbd)
+{
+    int i, j;
+
+    for (i = 0; i < MAX_NR_KEYMAPS; ++i) {
+        if (kbd->key_maps[i]) {
+            printf("static unsigned short default_key_map_%d[NR_KEYS] = {", i);
+            for (j = 0; j < NR_KEYS; ++j) {
+                if ((j % 8) == 0) {
+                    printf("\n    ");
+                }
+                printf("0x%.4x, ", kbd->key_maps[i][j]);
+            }
+            printf("\n};\n");
+        }
+    }
+    printf("\n");
+    printf("static unsigned short *default_key_maps[MAX_NR_KEYMAPS] = {\n");
+    for (i = 0; i < MAX_NR_KEYMAPS; ++i) {
+        if (kbd->key_maps[i]) {
+            printf("    default_key_map_%d,\n", i);
+        } else {
+            printf("    NULL,\n");
+        }
+    }
+    printf("};\n");
+}
+#endif // DUMP_KEYMAP
+
+static SDL_EVDEV_keyboard_state *kbd_cleanup_state = NULL;
+static int kbd_cleanup_sigactions_installed = 0;
+static int kbd_cleanup_atexit_installed = 0;
+
+static struct sigaction old_sigaction[NSIG];
+
+static int fatal_signals[] = {
+    // Handlers for SIGTERM and SIGINT are installed in SDL_InitQuit.
+    SIGHUP, SIGQUIT, SIGILL, SIGABRT,
+    SIGFPE, SIGSEGV, SIGPIPE, SIGBUS,
+    SIGSYS
+};
+
+static void kbd_cleanup(void)
+{
+    SDL_EVDEV_keyboard_state *kbd = kbd_cleanup_state;
+    if (!kbd) {
+        return;
+    }
+    kbd_cleanup_state = NULL;
+
+    ioctl(kbd->console_fd, KDSKBMODE, kbd->old_kbd_mode);
+}
+
+static void SDL_EVDEV_kbd_reraise_signal(int sig)
+{
+    (void)raise(sig);
+}
+
+static siginfo_t *SDL_EVDEV_kdb_cleanup_siginfo = NULL;
+static void *SDL_EVDEV_kdb_cleanup_ucontext = NULL;
+
+static void kbd_cleanup_signal_action(int signum, siginfo_t *info, void *ucontext)
+{
+    struct sigaction *old_action_p = &(old_sigaction[signum]);
+    sigset_t sigset;
+
+    // Restore original signal handler before going any further.
+    sigaction(signum, old_action_p, NULL);
+
+    // Unmask current signal.
+    sigemptyset(&sigset);
+    sigaddset(&sigset, signum);
+    sigprocmask(SIG_UNBLOCK, &sigset, NULL);
+
+    // Save original signal info and context for archeologists.
+    SDL_EVDEV_kdb_cleanup_siginfo = info;
+    SDL_EVDEV_kdb_cleanup_ucontext = ucontext;
+
+    // Restore keyboard.
+    kbd_cleanup();
+
+    // Reraise signal.
+    SDL_EVDEV_kbd_reraise_signal(signum);
+}
+
+static void kbd_unregister_emerg_cleanup(void)
+{
+    int tabidx;
+
+    kbd_cleanup_state = NULL;
+
+    if (!kbd_cleanup_sigactions_installed) {
+        return;
+    }
+    kbd_cleanup_sigactions_installed = 0;
+
+    for (tabidx = 0; tabidx < sizeof(fatal_signals) / sizeof(fatal_signals[0]); ++tabidx) {
+        struct sigaction *old_action_p;
+        struct sigaction cur_action;
+        int signum = fatal_signals[tabidx];
+        old_action_p = &(old_sigaction[signum]);
+
+        // Examine current signal action
+        if (sigaction(signum, NULL, &cur_action)) {
+            continue;
+        }
+
+        // Check if action installed and not modified
+        if (!(cur_action.sa_flags & SA_SIGINFO) || cur_action.sa_sigaction != &kbd_cleanup_signal_action) {
+            continue;
+        }
+
+        // Restore original action
+        sigaction(signum, old_action_p, NULL);
+    }
+}
+
+static void kbd_cleanup_atexit(void)
+{
+    // Restore keyboard.
+    kbd_cleanup();
+
+    // Try to restore signal handlers in case shared library is being unloaded
+    kbd_unregister_emerg_cleanup();
+}
+
+static void kbd_register_emerg_cleanup(SDL_EVDEV_keyboard_state *kbd)
+{
+    int tabidx;
+
+    if (kbd_cleanup_state) {
+        return;
+    }
+    kbd_cleanup_state = kbd;
+
+    if (!kbd_cleanup_atexit_installed) {
+        /* Since glibc 2.2.3, atexit() (and on_exit(3)) can be used within a shared library to establish
+         * functions that are called when the shared library is unloaded.
+         * -- man atexit(3)
+         */
+        (void)atexit(kbd_cleanup_atexit);
+        kbd_cleanup_atexit_installed = 1;
+    }
+
+    if (kbd_cleanup_sigactions_installed) {
+        return;
+    }
+    kbd_cleanup_sigactions_installed = 1;
+
+    for (tabidx = 0; tabidx < sizeof(fatal_signals) / sizeof(fatal_signals[0]); ++tabidx) {
+        struct sigaction *old_action_p;
+        struct sigaction new_action;
+        int signum = fatal_signals[tabidx];
+        old_action_p = &(old_sigaction[signum]);
+        if (sigaction(signum, NULL, old_action_p)) {
+            continue;
+        }
+
+        /* Skip SIGHUP and SIGPIPE if handler is already installed
+         * - assume the handler will do the cleanup
+         */
+        if ((signum == SIGHUP || signum == SIGPIPE) && (old_action_p->sa_handler != SIG_DFL || (void (*)(int))old_action_p->sa_sigaction != SIG_DFL)) {
+            continue;
+        }
+
+        new_action = *old_action_p;
+        new_action.sa_flags |= SA_SIGINFO;
+        new_action.sa_sigaction = &kbd_cleanup_signal_action;
+        sigaction(signum, &new_action, NULL);
+    }
+}
+
+enum {
+    VT_SIGNAL_NONE,
+    VT_SIGNAL_RELEASE,
+    VT_SIGNAL_ACQUIRE,
+};
+static int vt_release_signal;
+static int vt_acquire_signal;
+static SDL_AtomicInt vt_signal_pending;
+
+typedef void (*signal_handler)(int signum);
+
+static void kbd_vt_release_signal_action(int signum)
+{
+    SDL_SetAtomicInt(&vt_signal_pending, VT_SIGNAL_RELEASE);
+}
+
+static void kbd_vt_acquire_signal_action(int signum)
+{
+    SDL_SetAtomicInt(&vt_signal_pending, VT_SIGNAL_ACQUIRE);
+}
+
+static bool setup_vt_signal(int signum, signal_handler handler)
+{
+    struct sigaction *old_action_p;
+    struct sigaction new_action;
+    old_action_p = &(old_sigaction[signum]);
+    SDL_zero(new_action);
+    new_action.sa_handler = handler;
+    new_action.sa_flags = SA_RESTART;
+    if (sigaction(signum, &new_action, old_action_p) < 0) {
+        return false;
+    }
+    if (old_action_p->sa_handler != SIG_DFL) {
+        // This signal is already in use
+        sigaction(signum, old_action_p, NULL);
+        return false;
+    }
+    return true;
+}
+
+static int find_free_signal(signal_handler handler)
+{
+#ifdef SIGRTMIN
+    int i;
+
+    for (i = SIGRTMIN + 2; i <= SIGRTMAX; ++i) {
+        if (setup_vt_signal(i, handler)) {
+            return i;
+        }
+    }
+#endif
+    if (setup_vt_signal(SIGUSR1, handler)) {
+        return SIGUSR1;
+    }
+    if (setup_vt_signal(SIGUSR2, handler)) {
+        return SIGUSR2;
+    }
+    return 0;
+}
+
+static void kbd_vt_quit(int console_fd)
+{
+    struct vt_mode mode;
+
+    if (vt_release_signal) {
+        sigaction(vt_release_signal, &old_sigaction[vt_release_signal], NULL);
+        vt_release_signal = 0;
+    }
+    if (vt_acquire_signal) {
+        sigaction(vt_acquire_signal, &old_sigaction[vt_acquire_signal], NULL);
+        vt_acquire_signal = 0;
+    }
+
+    SDL_zero(mode);
+    mode.mode = VT_AUTO;
+    ioctl(console_fd, VT_SETMODE, &mode);
+}
+
+static bool kbd_vt_init(int console_fd)
+{
+    struct vt_mode mode;
+
+    vt_release_signal = find_free_signal(kbd_vt_release_signal_action);
+    vt_acquire_signal = find_free_signal(kbd_vt_acquire_signal_action);
+    if (!vt_release_signal || !vt_acquire_signal ) {
+        kbd_vt_quit(console_fd);
+        return false;
+    }
+
+    SDL_zero(mode);
+    mode.mode = VT_PROCESS;
+    mode.relsig = vt_release_signal;
+    mode.acqsig = vt_acquire_signal;
+    mode.frsig = SIGIO;
+    if (ioctl(console_fd, VT_SETMODE, &mode) < 0) {
+        kbd_vt_quit(console_fd);
+        return false;
+    }
+    return true;
+}
+
+static void kbd_vt_update(SDL_EVDEV_keyboard_state *state)
+{
+    int signal_pending = SDL_GetAtomicInt(&vt_signal_pending);
+    if (signal_pending != VT_SIGNAL_NONE) {
+        if (signal_pending == VT_SIGNAL_RELEASE) {
+            if (state->vt_release_callback) {
+                state->vt_release_callback(state->vt_release_callback_data);
+            }
+            ioctl(state->console_fd, VT_RELDISP, 1);
+        } else {
+            if (state->vt_acquire_callback) {
+                state->vt_acquire_callback(state->vt_acquire_callback_data);
+            }
+            ioctl(state->console_fd, VT_RELDISP, VT_ACKACQ);
+        }
+        SDL_CompareAndSwapAtomicInt(&vt_signal_pending, signal_pending, VT_SIGNAL_NONE);
+    }
+}
+
+SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void)
+{
+    SDL_EVDEV_keyboard_state *kbd;
+    char flag_state;
+    char kbtype;
+    char shift_state[sizeof(long)] = { TIOCL_GETSHIFTSTATE, 0 };
+
+    kbd = (SDL_EVDEV_keyboard_state *)SDL_calloc(1, sizeof(*kbd));
+    if (!kbd) {
+        return NULL;
+    }
+
+    // This might fail if we're not connected to a tty (e.g. on the Steam Link)
+    kbd->console_fd = open("/dev/tty", O_RDONLY | O_CLOEXEC);
+    if (!((ioctl(kbd->console_fd, KDGKBTYPE, &kbtype) == 0) && ((kbtype == KB_101) || (kbtype == KB_84)))) {
+        close(kbd->console_fd);
+        kbd->console_fd = -1;
+    }
+
+    kbd->npadch = -1;
+
+    if (ioctl(kbd->console_fd, TIOCLINUX, shift_state) == 0) {
+        kbd->shift_state = *shift_state;
+    }
+
+    if (ioctl(kbd->console_fd, KDGKBLED, &flag_state) == 0) {
+        kbd->ledflagstate = flag_state;
+    }
+
+    kbd->accents = &default_accents;
+    kbd->key_maps = default_key_maps;
+
+    if (ioctl(kbd->console_fd, KDGKBMODE, &kbd->old_kbd_mode) == 0) {
+        // Set the keyboard in UNICODE mode and load the keymaps
+        ioctl(kbd->console_fd, KDSKBMODE, K_UNICODE);
+    }
+
+    kbd_vt_init(kbd->console_fd);
+
+    return kbd;
+}
+
+void SDL_EVDEV_kbd_set_muted(SDL_EVDEV_keyboard_state *state, bool muted)
+{
+    if (!state) {
+        return;
+    }
+
+    if (muted == state->muted) {
+        return;
+    }
+
+    if (muted) {
+        if (SDL_GetHintBoolean(SDL_HINT_MUTE_CONSOLE_KEYBOARD, true)) {
+            /* Mute the keyboard so keystrokes only generate evdev events
+             * and do not leak through to the console
+             */
+            ioctl(state->console_fd, KDSKBMODE, K_OFF);
+
+            /* Make sure to restore keyboard if application fails to call
+             * SDL_Quit before exit or fatal signal is raised.
+             */
+            if (!SDL_GetHintBoolean(SDL_HINT_NO_SIGNAL_HANDLERS, false)) {
+                kbd_register_emerg_cleanup(state);
+            }
+        }
+    } else {
+        kbd_unregister_emerg_cleanup();
+
+        // Restore the original keyboard mode
+        ioctl(state->console_fd, KDSKBMODE, state->old_kbd_mode);
+    }
+    state->muted = muted;
+}
+
+void SDL_EVDEV_kbd_set_vt_switch_callbacks(SDL_EVDEV_keyboard_state *state, void (*release_callback)(void*), void *release_callback_data, void (*acquire_callback)(void*), void *acquire_callback_data)
+{
+    if (state == NULL) {
+        return;
+    }
+
+    state->vt_release_callback = release_callback;
+    state->vt_release_callback_data = release_callback_data;
+    state->vt_acquire_callback = acquire_callback;
+    state->vt_acquire_callback_data = acquire_callback_data;
+}
+
+void SDL_EVDEV_kbd_update(SDL_EVDEV_keyboard_state *state)
+{
+    if (!state) {
+        return;
+    }
+
+    kbd_vt_update(state);
+}
+
+void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *state)
+{
+    if (state == NULL) {
+        return;
+    }
+
+    SDL_EVDEV_kbd_set_muted(state, false);
+
+    kbd_vt_quit(state->console_fd);
+
+    if (state->console_fd >= 0) {
+        close(state->console_fd);
+        state->console_fd = -1;
+    }
+
+    if (state->key_maps && state->key_maps != default_key_maps) {
+        int i;
+        for (i = 0; i < MAX_NR_KEYMAPS; ++i) {
+            if (state->key_maps[i]) {
+                SDL_free(state->key_maps[i]);
+            }
+        }
+        SDL_free(state->key_maps);
+    }
+
+    SDL_free(state);
+}
+
+/*
+ * Helper Functions.
+ */
+static void put_queue(SDL_EVDEV_keyboard_state *kbd, uint c)
+{
+    // c is already part of a UTF-8 sequence and safe to add as a character
+    if (kbd->text_len < (sizeof(kbd->text) - 1)) {
+        kbd->text[kbd->text_len++] = (char)c;
+    }
+}
+
+static void put_utf8(SDL_EVDEV_keyboard_state *kbd, uint c)
+{
+    if (c < 0x80) {
+        put_queue(kbd, c); /*  0******* */
+    } else if (c < 0x800) {
+        /* 110***** 10****** */
+        put_queue(kbd, 0xc0 | (c >> 6));
+        put_queue(kbd, 0x80 | (c & 0x3f));
+    } else if (c < 0x10000) {
+        if (c >= 0xD800 && c < 0xE000) {
+            return;
+        }
+        if (c == 0xFFFF) {
+            return;
+        }
+        /* 1110**** 10****** 10****** */
+        put_queue(kbd, 0xe0 | (c >> 12));
+        put_queue(kbd, 0x80 | ((c >> 6) & 0x3f));
+        put_queue(kbd, 0x80 | (c & 0x3f));
+    } else if (c < 0x110000) {
+        /* 11110*** 10****** 10****** 10****** */
+        put_queue(kbd, 0xf0 | (c >> 18));
+        put_queue(kbd, 0x80 | ((c >> 12) & 0x3f));
+        put_queue(kbd, 0x80 | ((c >> 6) & 0x3f));
+        put_queue(kbd, 0x80 | (c & 0x3f));
+    }
+}
+
+/*
+ * We have a combining character DIACR here, followed by the character CH.
+ * If the combination occurs in the table, return the corresponding value.
+ * Otherwise, if CH is a space or equals DIACR, return DIACR.
+ * Otherwise, conclude that DIACR was not combining after all,
+ * queue it and return CH.
+ */
+static unsigned int handle_diacr(SDL_EVDEV_keyboard_state *kbd, unsigned int ch)
+{
+    unsigned int d = kbd->diacr;
+    unsigned int i;
+
+    kbd->diacr = 0;
+
+    if (kbd->console_fd >= 0)
+        if (ioctl(kbd->console_fd, KDGKBDIACR, kbd->accents) < 0) {
+            // No worries, we'll use the default accent table
+        }
+
+    for (i = 0; i < kbd->accents->kb_cnt; i++) {
+        if (kbd->accents->kbdiacr[i].diacr == d &&
+            kbd->accents->kbdiacr[i].base == ch) {
+            return kbd->accents->kbdiacr[i].result;
+        }
+    }
+
+    if (ch == ' ' || ch == d) {
+        return d;
+    }
+
+    put_utf8(kbd, d);
+
+    return ch;
+}
+
+static bool vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
+{
+    return (kbd->ledflagstate & flag) != 0;
+}
+
+static void set_vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
+{
+    kbd->ledflagstate |= flag;
+    ioctl(kbd->console_fd, KDSETLED, (unsigned long)(kbd->ledflagstate));
+}
+
+static void clr_vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
+{
+    kbd->ledflagstate &= ~flag;
+    ioctl(kbd->console_fd, KDSETLED, (unsigned long)(kbd->ledflagstate));
+}
+
+static void chg_vc_kbd_lock(SDL_EVDEV_keyboard_state *kbd, int flag)
+{
+    kbd->lockstate ^= 1 << flag;
+}
+
+static void chg_vc_kbd_slock(SDL_EVDEV_keyboard_state *kbd, int flag)
+{
+    kbd->slockstate ^= 1 << flag;
+}
+
+static void chg_vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
+{
+    kbd->ledflagstate ^= flag;
+    ioctl(kbd->console_fd, KDSETLED, (unsigned long)(kbd->ledflagstate));
+}
+
+/*
+ * Special function handlers
+ */
+
+static void fn_enter(SDL_EVDEV_keyboard_state *kbd)
+{
+    if (kbd->diacr) {
+        put_utf8(kbd, kbd->diacr);
+        kbd->diacr = 0;
+    }
+}
+
+static void fn_caps_toggle(SDL_EVDEV_keyboard_state *kbd)
+{
+    if (kbd->rep) {
+        return;
+    }
+
+    chg_vc_kbd_led(kbd, K_CAPSLOCK);
+}
+
+static void fn_caps_on(SDL_EVDEV_keyboard_state *kbd)
+{
+    if (kbd->rep) {
+        return;
+    }
+
+    set_vc_kbd_led(kbd, K_CAPSLOCK);
+}
+
+static void fn_num(SDL_EVDEV_keyboard_state *kbd)
+{
+    if (!kbd->rep) {
+        chg_vc_kbd_led(kbd, K_NUMLOCK);
+    }
+}
+
+static void fn_compose(SDL_EVDEV_keyboard_state *kbd)
+{
+    kbd->dead_key_next = true;
+}
+
+/*
+ * Special key handlers
+ */
+
+static void k_ignore(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
+{
+}
+
+static void k_spec(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
+{
+    if (up_flag) {
+        return;
+    }
+    if (value >= SDL_arraysize(fn_handler)) {
+        return;
+    }
+    if (fn_handler[value]) {
+        fn_handler[value](kbd);
+    }
+}
+
+static void k_lowercase(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
+{
+}
+
+static void k_self(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
+{
+    if (up_flag) {
+        return; // no action, if this is a key release
+    }
+
+    if (kbd->diacr) {
+        value = handle_diacr(kbd, value);
+    }
+
+    if (kbd->dead_key_next) {
+        kbd->dead_key_next = false;
+        kbd->diacr = value;
+        return;
+    }
+    put_utf8(kbd, value);
+}
+
+static void k_deadunicode(SDL_EVDEV_keyboard_state *kbd, unsigned int value, char up_flag)
+{
+    if (up_flag) {
+        return;
+    }
+
+    kbd->diacr = (kbd->diacr ? handle_diacr(kbd, value) : value);
+}
+
+static void k_dead(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
+{
+    const unsigned char ret_diacr[NR_DEAD] = { '`', '\'', '^', '~', '"', ',' };
+
+    k_deadunicode(kbd, ret_diacr[value], up_flag);
+}
+
+static void k_dead2(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
+{
+    k_deadunicode(kbd, value, up_flag);
+}
+
+static void k_cons(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
+{
+}
+
+static void k_fn(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
+{
+}
+
+static void k_cur(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
+{
+}
+
+static void k_pad(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
+{
+    static const char pad_chars[] = "0123456789+-*/\015,.?()#";
+
+    if (up_flag) {
+        return; // no action, if this is a key release
+    }
+
+    if (!vc_kbd_led(kbd, K_NUMLOCK)) {
+        // unprintable action
+        return;
+    }
+
+    put_queue(kbd, pad_chars[value]);
+}
+
+static void k_shift(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
+{
+    int old_state = kbd->shift_state;
+
+    if (kbd->rep) {
+        return;
+    }
+    /*
+     * Mimic typewriter:
+     * a CapsShift key acts like Shift but undoes CapsLock
+     */
+    if (value == KVAL(K_CAPSSHIFT)) {
+        value = KVAL(K_SHIFT);
+        if (!up_flag) {
+            clr_vc_kbd_led(kbd, K_CAPSLOCK);
+        }
+    }
+
+    if (up_flag) {
+        /*
+         * handle the case that two shift or control
+         * keys are depressed simultaneously
+         */
+        if (kbd->shift_down[value]) {
+            kbd->shift_down[value]--;
+        }
+    } else {
+        kbd->shift_down[value]++;
+    }
+
+    if (kbd->shift_down[value]) {
+        kbd->shift_state |= (1 << value);
+    } else {
+        kbd->shift_state &= ~(1 << value);
+    }
+
+    // kludge
+    if (up_flag && kbd->shift_state != old_state && kbd->npadch != -1) {
+        put_utf8(kbd, kbd->npadch);
+        kbd->npadch = -1;
+    }
+}
+
+static void k_meta(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
+{
+}
+
+static void k_ascii(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
+{
+    int base;
+
+    if (up_flag) {
+        return;
+    }
+
+    if (value < 10) {
+        // decimal input of code, while Alt depressed
+        base = 10;
+    } else {
+        // hexadecimal input of code, while AltGr depressed
+        value -= 10;
+        base = 16;
+    }
+
+    if (kbd->npadch == -1) {
+        kbd->npadch = value;
+    } else {
+        kbd->npadch = kbd->npadch * base + value;
+    }
+}
+
+static void k_lock(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
+{
+    if (up_flag || kbd->rep) {
+        return;
+    }
+
+    chg_vc_kbd_lock(kbd, value);
+}
+
+static void k_slock(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
+{
+    k_shift(kbd, value, up_flag);
+    if (up_flag || kbd->rep) {
+        return;
+    }
+
+    chg_vc_kbd_slock(kbd, value);
+    // try to make Alt, oops, AltGr and such work
+    if (!kbd->key_maps[kbd->lockstate ^ kbd->slockstate]) {
+        kbd->slockstate = 0;
+        chg_vc_kbd_slock(kbd, value);
+    }
+}
+
+static void k_brl(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
+{
+}
+
+void SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *state, unsigned int keycode, int down)
+{
+    unsigned char shift_final;
+    unsigned char type;
+    unsigned short *key_map;
+    unsigned short keysym;
+
+    if (!state) {
+        return;
+    }
+
+    state->rep = (down == 2);
+
+    shift_final = (state->shift_state | state->slockstate) ^ state->lockstate;
+    key_map = state->key_maps[shift_final];
+    if (!key_map) {
+        // Unsupported shift state (e.g. ctrl = 4, alt = 8), just reset to the default state
+        state->shift_state = 0;
+        state->slockstate = 0;
+        state->lockstate = 0;
+        return;
+    }
+
+    if (keycode < NR_KEYS) {
+        if (state->console_fd < 0) {
+            keysym = key_map[keycode];
+        } else {
+            struct kbentry kbe;
+            kbe.kb_table = shift_final;
+            kbe.kb_index = keycode;
+            if (ioctl(state->console_fd, KDGKBENT, &kbe) == 0)
+                keysym = (kbe.kb_value ^ 0xf000);
+            else
+                return;
+        }
+    } else {
+        return;
+    }
+
+    type = KTYP(keysym);
+
+    if (type < 0xf0) {
+        if (down) {
+            put_utf8(state, keysym);
+        }
+    } else {
+        type -= 0xf0;
+
+        // if type is KT_LETTER then it can be affected by Caps Lock
+        if (type == KT_LETTER) {
+            type = KT_LATIN;
+
+            if (vc_kbd_led(state, K_CAPSLOCK)) {
+                shift_final = shift_final ^ (1 << KG_SHIFT);
+                key_map = state->key_maps[shift_final];
+                if (key_map) {
+                    if (state->console_fd < 0) {
+                        keysym = key_map[keycode];
+                    } else {
+                        struct kbentry kbe;
+                        kbe.kb_table = shift_final;
+                        kbe.kb_index = keycode;
+                        if (ioctl(state->console_fd, KDGKBENT, &kbe) == 0)
+                            keysym = (kbe.kb_value ^ 0xf000);
+                    }
+                }
+            }
+        }
+
+        (*k_handler[type])(state, keysym & 0xff, !down);
+
+        if (type != KT_SLOCK) {
+            state->slockstate = 0;
+        }
+    }
+
+    if (state->text_len > 0) {
+        state->text[state->text_len] = '\0';
+        SDL_SendKeyboardText(state->text);
+        state->text_len = 0;
+    }
+}
+
+#elif !defined(SDL_INPUT_FBSDKBIO) // !SDL_INPUT_LINUXKD
+
+SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void)
+{
+    return NULL;
+}
+
+void SDL_EVDEV_kbd_set_muted(SDL_EVDEV_keyboard_state *state, bool muted)
+{
+}
+
+void SDL_EVDEV_kbd_set_vt_switch_callbacks(SDL_EVDEV_keyboard_state *state, void (*release_callback)(void*), void *release_callback_data, void (*acquire_callback)(void*), void *acquire_callback_data)
+{
+}
+
+void SDL_EVDEV_kbd_update(SDL_EVDEV_keyboard_state *state)
+{
+}
+
+void SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *state, unsigned int keycode, int down)
+{
+}
+
+void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *state)
+{
+}
+
+#endif // SDL_INPUT_LINUXKD

+ 35 - 0
thirdparty/sdl/core/linux/SDL_evdev_kbd.h

@@ -0,0 +1,35 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#ifndef SDL_evdev_kbd_h_
+#define SDL_evdev_kbd_h_
+
+struct SDL_EVDEV_keyboard_state;
+typedef struct SDL_EVDEV_keyboard_state SDL_EVDEV_keyboard_state;
+
+extern SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void);
+extern void SDL_EVDEV_kbd_set_muted(SDL_EVDEV_keyboard_state *state, bool muted);
+extern void SDL_EVDEV_kbd_set_vt_switch_callbacks(SDL_EVDEV_keyboard_state *state, void (*release_callback)(void*), void *release_callback_data, void (*acquire_callback)(void*), void *acquire_callback_data);
+extern void SDL_EVDEV_kbd_update(SDL_EVDEV_keyboard_state *state);
+extern void SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *state, unsigned int keycode, int down);
+extern void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *state);
+
+#endif // SDL_evdev_kbd_h_

+ 282 - 0
thirdparty/sdl/core/linux/SDL_evdev_kbd_default_accents.h

@@ -0,0 +1,282 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+static struct kbdiacrs default_accents = {
+    68,
+    {
+        { 0x60, 0x41, 0xc0 },
+        { 0x60, 0x61, 0xe0 },
+        { 0x27, 0x41, 0xc1 },
+        { 0x27, 0x61, 0xe1 },
+        { 0x5e, 0x41, 0xc2 },
+        { 0x5e, 0x61, 0xe2 },
+        { 0x7e, 0x41, 0xc3 },
+        { 0x7e, 0x61, 0xe3 },
+        { 0x22, 0x41, 0xc4 },
+        { 0x22, 0x61, 0xe4 },
+        { 0x4f, 0x41, 0xc5 },
+        { 0x6f, 0x61, 0xe5 },
+        { 0x30, 0x41, 0xc5 },
+        { 0x30, 0x61, 0xe5 },
+        { 0x41, 0x41, 0xc5 },
+        { 0x61, 0x61, 0xe5 },
+        { 0x41, 0x45, 0xc6 },
+        { 0x61, 0x65, 0xe6 },
+        { 0x2c, 0x43, 0xc7 },
+        { 0x2c, 0x63, 0xe7 },
+        { 0x60, 0x45, 0xc8 },
+        { 0x60, 0x65, 0xe8 },
+        { 0x27, 0x45, 0xc9 },
+        { 0x27, 0x65, 0xe9 },
+        { 0x5e, 0x45, 0xca },
+        { 0x5e, 0x65, 0xea },
+        { 0x22, 0x45, 0xcb },
+        { 0x22, 0x65, 0xeb },
+        { 0x60, 0x49, 0xcc },
+        { 0x60, 0x69, 0xec },
+        { 0x27, 0x49, 0xcd },
+        { 0x27, 0x69, 0xed },
+        { 0x5e, 0x49, 0xce },
+        { 0x5e, 0x69, 0xee },
+        { 0x22, 0x49, 0xcf },
+        { 0x22, 0x69, 0xef },
+        { 0x2d, 0x44, 0xd0 },
+        { 0x2d, 0x64, 0xf0 },
+        { 0x7e, 0x4e, 0xd1 },
+        { 0x7e, 0x6e, 0xf1 },
+        { 0x60, 0x4f, 0xd2 },
+        { 0x60, 0x6f, 0xf2 },
+        { 0x27, 0x4f, 0xd3 },
+        { 0x27, 0x6f, 0xf3 },
+        { 0x5e, 0x4f, 0xd4 },
+        { 0x5e, 0x6f, 0xf4 },
+        { 0x7e, 0x4f, 0xd5 },
+        { 0x7e, 0x6f, 0xf5 },
+        { 0x22, 0x4f, 0xd6 },
+        { 0x22, 0x6f, 0xf6 },
+        { 0x2f, 0x4f, 0xd8 },
+        { 0x2f, 0x6f, 0xf8 },
+        { 0x60, 0x55, 0xd9 },
+        { 0x60, 0x75, 0xf9 },
+        { 0x27, 0x55, 0xda },
+        { 0x27, 0x75, 0xfa },
+        { 0x5e, 0x55, 0xdb },
+        { 0x5e, 0x75, 0xfb },
+        { 0x22, 0x55, 0xdc },
+        { 0x22, 0x75, 0xfc },
+        { 0x27, 0x59, 0xdd },
+        { 0x27, 0x79, 0xfd },
+        { 0x54, 0x48, 0xde },
+        { 0x74, 0x68, 0xfe },
+        { 0x73, 0x73, 0xdf },
+        { 0x22, 0x79, 0xff },
+        { 0x73, 0x7a, 0xdf },
+        { 0x69, 0x6a, 0xff },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+        { 0x00, 0x00, 0x00 },
+    }
+};

+ 4765 - 0
thirdparty/sdl/core/linux/SDL_evdev_kbd_default_keymap.h

@@ -0,0 +1,4765 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+/* *INDENT-OFF* */ /* clang-format off */
+
+static unsigned short default_key_map_0[NR_KEYS] = {
+    0xf200, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 
+    0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, 
+    0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 
+    0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb61, 0xfb73, 
+    0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b, 
+    0xf027, 0xf060, 0xf700, 0xf05c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 
+    0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf200, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+};
+static unsigned short default_key_map_1[NR_KEYS] = {
+    0xf200, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 
+    0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, 
+    0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, 
+    0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb41, 0xfb53, 
+    0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a, 
+    0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 
+    0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf10c, 0xf10d, 0xf10e, 0xf10f, 0xf110, 
+    0xf111, 0xf112, 0xf113, 0xf11e, 0xf11f, 0xf208, 0xf203, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf120, 
+    0xf121, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf200, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf20b, 0xf601, 0xf602, 0xf117, 0xf600, 0xf20a, 0xf115, 0xf116, 
+    0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+};
+static unsigned short default_key_map_2[NR_KEYS] = {
+    0xf200, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 
+    0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, 
+    0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 
+    0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb61, 0xfb73, 
+    0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b, 
+    0xf027, 0xf060, 0xf700, 0xf05c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 
+    0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf916, 
+    0xf703, 0xf020, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf914, 0xf202, 0xf911, 
+    0xf912, 0xf913, 0xf917, 0xf90e, 0xf90f, 0xf910, 0xf918, 0xf90b, 
+    0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf07c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf919, 0xf702, 0xf915, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+};
+static unsigned short default_key_map_3[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 
+    0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, 
+    0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, 
+    0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb41, 0xfb53, 
+    0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a, 
+    0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 
+    0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf518, 0xf519, 0xf51a, 0xf51b, 0xf51c, 
+    0xf51d, 0xf51e, 0xf51f, 0xf520, 0xf521, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0x00a6, 0xf522, 
+    0xf523, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+#ifdef INCLUDE_EXTENDED_KEYMAP
+static unsigned short default_key_map_4[NR_KEYS] = {
+    0xf200, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf122, 0xf123, 0xf124, 0xf125, 0xf126, 
+    0xf127, 0xf128, 0xf129, 0xf12a, 0xf12b, 0xf208, 0xf204, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf12c, 
+    0xf12d, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+};
+static unsigned short default_key_map_5[NR_KEYS] = {
+    0xf200, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf01e, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf12e, 0xf12f, 0xf130, 0xf131, 0xf132, 
+    0xf133, 0xf134, 0xf135, 0xf136, 0xf137, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf138, 
+    0xf139, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+};
+static unsigned short default_key_map_6[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf01c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_7[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf01e, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf518, 0xf519, 0xf51a, 0xf51b, 0xf51c, 
+    0xf51d, 0xf51e, 0xf51f, 0xf520, 0xf521, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf522, 
+    0xf523, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_8[NR_KEYS] = {
+    0xf200, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 
+    0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, 
+    0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, 
+    0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, 
+    0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, 
+    0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, 
+    0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 
+    0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf202, 0xf907, 
+    0xf908, 0xf909, 0xf30b, 0xf904, 0xf905, 0xf906, 0xf30a, 0xf901, 
+    0xf902, 0xf903, 0xf900, 0xf310, 0xf206, 0xf200, 0xf83c, 0xf50a, 
+    0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf212, 
+    0xf118, 0xf210, 0xf211, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+};
+static unsigned short default_key_map_9[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf821, 0xf840, 0xf823, 0xf824, 0xf825, 0xf85e, 
+    0xf826, 0xf82a, 0xf828, 0xf829, 0xf85f, 0xf82b, 0xf87f, 0xf809, 
+    0xf851, 0xf857, 0xf845, 0xf852, 0xf854, 0xf859, 0xf855, 0xf849, 
+    0xf84f, 0xf850, 0xf87b, 0xf87d, 0xf80d, 0xf702, 0xf841, 0xf853, 
+    0xf844, 0xf846, 0xf847, 0xf848, 0xf84a, 0xf84b, 0xf84c, 0xf83a, 
+    0xf822, 0xf87e, 0xf700, 0xf87c, 0xf85a, 0xf858, 0xf843, 0xf856, 
+    0xf842, 0xf84e, 0xf84d, 0xf83c, 0xf83e, 0xf83f, 0xf700, 0xf916, 
+    0xf703, 0xf820, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf914, 0xf209, 0xf911, 
+    0xf912, 0xf913, 0xf917, 0xf90e, 0xf90f, 0xf910, 0xf918, 0xf90b, 
+    0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf83e, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf919, 0xf702, 0xf915, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_10[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 
+    0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, 
+    0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, 
+    0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, 
+    0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, 
+    0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, 
+    0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf87c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_11[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf821, 0xf840, 0xf823, 0xf824, 0xf825, 0xf85e, 
+    0xf826, 0xf82a, 0xf828, 0xf829, 0xf85f, 0xf82b, 0xf87f, 0xf809, 
+    0xf851, 0xf857, 0xf845, 0xf852, 0xf854, 0xf859, 0xf855, 0xf849, 
+    0xf84f, 0xf850, 0xf87b, 0xf87d, 0xf80d, 0xf702, 0xf841, 0xf853, 
+    0xf844, 0xf846, 0xf847, 0xf848, 0xf84a, 0xf84b, 0xf84c, 0xf83a, 
+    0xf822, 0xf87e, 0xf700, 0xf87c, 0xf85a, 0xf858, 0xf843, 0xf856, 
+    0xf842, 0xf84e, 0xf84d, 0xf83c, 0xf83e, 0xf83f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf87c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_12[NR_KEYS] = {
+    0xf200, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf800, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 
+    0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf81c, 0xf50a, 
+    0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
+};
+static unsigned short default_key_map_13[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf81e, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf81c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_14[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf800, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf81c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_15[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf81e, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf81c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_16[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 
+    0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, 
+    0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 
+    0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb61, 0xfb73, 
+    0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b, 
+    0xf027, 0xf060, 0xf700, 0xf05c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 
+    0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf200, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_17[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 
+    0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, 
+    0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, 
+    0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb41, 0xfb53, 
+    0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a, 
+    0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 
+    0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf10c, 0xf10d, 0xf10e, 0xf10f, 0xf110, 
+    0xf111, 0xf112, 0xf113, 0xf11e, 0xf11f, 0xf208, 0xf203, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf120, 
+    0xf121, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf200, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf20b, 0xf601, 0xf602, 0xf117, 0xf600, 0xf20a, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_18[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 
+    0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, 
+    0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 
+    0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb61, 0xfb73, 
+    0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b, 
+    0xf027, 0xf060, 0xf700, 0xf05c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 
+    0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf916, 
+    0xf703, 0xf020, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf914, 0xf202, 0xf911, 
+    0xf912, 0xf913, 0xf917, 0xf90e, 0xf90f, 0xf910, 0xf918, 0xf90b, 
+    0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf07c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf919, 0xf702, 0xf915, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_19[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 
+    0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, 
+    0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, 
+    0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb41, 0xfb53, 
+    0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a, 
+    0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 
+    0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf518, 0xf519, 0xf51a, 0xf51b, 0xf51c, 
+    0xf51d, 0xf51e, 0xf51f, 0xf520, 0xf521, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0x00a6, 0xf522, 
+    0xf523, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_20[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf122, 0xf123, 0xf124, 0xf125, 0xf126, 
+    0xf127, 0xf128, 0xf129, 0xf12a, 0xf12b, 0xf208, 0xf204, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf12c, 
+    0xf12d, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_21[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf01e, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf12e, 0xf12f, 0xf130, 0xf131, 0xf132, 
+    0xf133, 0xf134, 0xf135, 0xf136, 0xf137, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf138, 
+    0xf139, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_22[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf01c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_23[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf01e, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf518, 0xf519, 0xf51a, 0xf51b, 0xf51c, 
+    0xf51d, 0xf51e, 0xf51f, 0xf520, 0xf521, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf522, 
+    0xf523, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_24[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 
+    0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, 
+    0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, 
+    0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, 
+    0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, 
+    0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, 
+    0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 
+    0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf202, 0xf907, 
+    0xf908, 0xf909, 0xf30b, 0xf904, 0xf905, 0xf906, 0xf30a, 0xf901, 
+    0xf902, 0xf903, 0xf900, 0xf310, 0xf206, 0xf200, 0xf83c, 0xf50a, 
+    0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf212, 
+    0xf118, 0xf210, 0xf211, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_25[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf821, 0xf840, 0xf823, 0xf824, 0xf825, 0xf85e, 
+    0xf826, 0xf82a, 0xf828, 0xf829, 0xf85f, 0xf82b, 0xf87f, 0xf809, 
+    0xf851, 0xf857, 0xf845, 0xf852, 0xf854, 0xf859, 0xf855, 0xf849, 
+    0xf84f, 0xf850, 0xf87b, 0xf87d, 0xf80d, 0xf702, 0xf841, 0xf853, 
+    0xf844, 0xf846, 0xf847, 0xf848, 0xf84a, 0xf84b, 0xf84c, 0xf83a, 
+    0xf822, 0xf87e, 0xf700, 0xf87c, 0xf85a, 0xf858, 0xf843, 0xf856, 
+    0xf842, 0xf84e, 0xf84d, 0xf83c, 0xf83e, 0xf83f, 0xf700, 0xf916, 
+    0xf703, 0xf820, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf914, 0xf209, 0xf911, 
+    0xf912, 0xf913, 0xf917, 0xf90e, 0xf90f, 0xf910, 0xf918, 0xf90b, 
+    0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf83e, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf919, 0xf702, 0xf915, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_26[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 
+    0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, 
+    0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, 
+    0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, 
+    0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, 
+    0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, 
+    0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf87c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_27[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf821, 0xf840, 0xf823, 0xf824, 0xf825, 0xf85e, 
+    0xf826, 0xf82a, 0xf828, 0xf829, 0xf85f, 0xf82b, 0xf87f, 0xf809, 
+    0xf851, 0xf857, 0xf845, 0xf852, 0xf854, 0xf859, 0xf855, 0xf849, 
+    0xf84f, 0xf850, 0xf87b, 0xf87d, 0xf80d, 0xf702, 0xf841, 0xf853, 
+    0xf844, 0xf846, 0xf847, 0xf848, 0xf84a, 0xf84b, 0xf84c, 0xf83a, 
+    0xf822, 0xf87e, 0xf700, 0xf87c, 0xf85a, 0xf858, 0xf843, 0xf856, 
+    0xf842, 0xf84e, 0xf84d, 0xf83c, 0xf83e, 0xf83f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf87c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_28[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf800, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 
+    0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf81c, 0xf50a, 
+    0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_29[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf81e, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf81c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_30[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf800, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf81c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_31[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf81e, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf81c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_32[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 
+    0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, 
+    0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 
+    0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb61, 0xfb73, 
+    0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b, 
+    0xf027, 0xf060, 0xf700, 0xf05c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 
+    0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf200, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_33[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 
+    0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, 
+    0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, 
+    0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb41, 0xfb53, 
+    0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a, 
+    0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 
+    0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf10c, 0xf10d, 0xf10e, 0xf10f, 0xf110, 
+    0xf111, 0xf112, 0xf113, 0xf11e, 0xf11f, 0xf208, 0xf203, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf120, 
+    0xf121, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf200, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf20b, 0xf601, 0xf602, 0xf117, 0xf600, 0xf20a, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_34[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 
+    0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, 
+    0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 
+    0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb61, 0xfb73, 
+    0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b, 
+    0xf027, 0xf060, 0xf700, 0xf05c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 
+    0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf916, 
+    0xf703, 0xf020, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf914, 0xf202, 0xf911, 
+    0xf912, 0xf913, 0xf917, 0xf90e, 0xf90f, 0xf910, 0xf918, 0xf90b, 
+    0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf07c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf919, 0xf702, 0xf915, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_35[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 
+    0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, 
+    0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, 
+    0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb41, 0xfb53, 
+    0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a, 
+    0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 
+    0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf518, 0xf519, 0xf51a, 0xf51b, 0xf51c, 
+    0xf51d, 0xf51e, 0xf51f, 0xf520, 0xf521, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0x00a6, 0xf522, 
+    0xf523, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_36[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf122, 0xf123, 0xf124, 0xf125, 0xf126, 
+    0xf127, 0xf128, 0xf129, 0xf12a, 0xf12b, 0xf208, 0xf204, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf12c, 
+    0xf12d, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_37[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf01e, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf12e, 0xf12f, 0xf130, 0xf131, 0xf132, 
+    0xf133, 0xf134, 0xf135, 0xf136, 0xf137, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf138, 
+    0xf139, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_38[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf01c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_39[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf01e, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf518, 0xf519, 0xf51a, 0xf51b, 0xf51c, 
+    0xf51d, 0xf51e, 0xf51f, 0xf520, 0xf521, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf522, 
+    0xf523, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_40[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 
+    0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, 
+    0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, 
+    0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, 
+    0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, 
+    0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, 
+    0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 
+    0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf202, 0xf907, 
+    0xf908, 0xf909, 0xf30b, 0xf904, 0xf905, 0xf906, 0xf30a, 0xf901, 
+    0xf902, 0xf903, 0xf900, 0xf310, 0xf206, 0xf200, 0xf83c, 0xf50a, 
+    0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf212, 
+    0xf118, 0xf210, 0xf211, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_41[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf821, 0xf840, 0xf823, 0xf824, 0xf825, 0xf85e, 
+    0xf826, 0xf82a, 0xf828, 0xf829, 0xf85f, 0xf82b, 0xf87f, 0xf809, 
+    0xf851, 0xf857, 0xf845, 0xf852, 0xf854, 0xf859, 0xf855, 0xf849, 
+    0xf84f, 0xf850, 0xf87b, 0xf87d, 0xf80d, 0xf702, 0xf841, 0xf853, 
+    0xf844, 0xf846, 0xf847, 0xf848, 0xf84a, 0xf84b, 0xf84c, 0xf83a, 
+    0xf822, 0xf87e, 0xf700, 0xf87c, 0xf85a, 0xf858, 0xf843, 0xf856, 
+    0xf842, 0xf84e, 0xf84d, 0xf83c, 0xf83e, 0xf83f, 0xf700, 0xf916, 
+    0xf703, 0xf820, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf914, 0xf209, 0xf911, 
+    0xf912, 0xf913, 0xf917, 0xf90e, 0xf90f, 0xf910, 0xf918, 0xf90b, 
+    0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf83e, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf919, 0xf702, 0xf915, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_42[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 
+    0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, 
+    0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, 
+    0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, 
+    0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, 
+    0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, 
+    0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf87c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_43[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf821, 0xf840, 0xf823, 0xf824, 0xf825, 0xf85e, 
+    0xf826, 0xf82a, 0xf828, 0xf829, 0xf85f, 0xf82b, 0xf87f, 0xf809, 
+    0xf851, 0xf857, 0xf845, 0xf852, 0xf854, 0xf859, 0xf855, 0xf849, 
+    0xf84f, 0xf850, 0xf87b, 0xf87d, 0xf80d, 0xf702, 0xf841, 0xf853, 
+    0xf844, 0xf846, 0xf847, 0xf848, 0xf84a, 0xf84b, 0xf84c, 0xf83a, 
+    0xf822, 0xf87e, 0xf700, 0xf87c, 0xf85a, 0xf858, 0xf843, 0xf856, 
+    0xf842, 0xf84e, 0xf84d, 0xf83c, 0xf83e, 0xf83f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf87c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_44[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf800, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 
+    0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf81c, 0xf50a, 
+    0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_45[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf81e, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf81c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_46[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf800, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf81c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_47[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf81e, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf81c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_48[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 
+    0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, 
+    0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 
+    0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb61, 0xfb73, 
+    0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b, 
+    0xf027, 0xf060, 0xf700, 0xf05c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 
+    0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf200, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_49[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 
+    0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, 
+    0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, 
+    0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb41, 0xfb53, 
+    0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a, 
+    0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 
+    0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf10c, 0xf10d, 0xf10e, 0xf10f, 0xf110, 
+    0xf111, 0xf112, 0xf113, 0xf11e, 0xf11f, 0xf208, 0xf203, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf120, 
+    0xf121, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf200, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf20b, 0xf601, 0xf602, 0xf117, 0xf600, 0xf20a, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_50[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 
+    0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, 
+    0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 
+    0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb61, 0xfb73, 
+    0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b, 
+    0xf027, 0xf060, 0xf700, 0xf05c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 
+    0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf916, 
+    0xf703, 0xf020, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf914, 0xf202, 0xf911, 
+    0xf912, 0xf913, 0xf917, 0xf90e, 0xf90f, 0xf910, 0xf918, 0xf90b, 
+    0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf07c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf919, 0xf702, 0xf915, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_51[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 
+    0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, 
+    0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, 
+    0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb41, 0xfb53, 
+    0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a, 
+    0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 
+    0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf518, 0xf519, 0xf51a, 0xf51b, 0xf51c, 
+    0xf51d, 0xf51e, 0xf51f, 0xf520, 0xf521, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0x00a6, 0xf522, 
+    0xf523, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_52[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf122, 0xf123, 0xf124, 0xf125, 0xf126, 
+    0xf127, 0xf128, 0xf129, 0xf12a, 0xf12b, 0xf208, 0xf204, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf12c, 
+    0xf12d, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_53[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf01e, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf12e, 0xf12f, 0xf130, 0xf131, 0xf132, 
+    0xf133, 0xf134, 0xf135, 0xf136, 0xf137, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf138, 
+    0xf139, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_54[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf01c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_55[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf01e, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf518, 0xf519, 0xf51a, 0xf51b, 0xf51c, 
+    0xf51d, 0xf51e, 0xf51f, 0xf520, 0xf521, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf522, 
+    0xf523, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_56[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 
+    0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, 
+    0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, 
+    0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, 
+    0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, 
+    0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, 
+    0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 
+    0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf202, 0xf907, 
+    0xf908, 0xf909, 0xf30b, 0xf904, 0xf905, 0xf906, 0xf30a, 0xf901, 
+    0xf902, 0xf903, 0xf900, 0xf310, 0xf206, 0xf200, 0xf83c, 0xf50a, 
+    0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf212, 
+    0xf118, 0xf210, 0xf211, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_57[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf821, 0xf840, 0xf823, 0xf824, 0xf825, 0xf85e, 
+    0xf826, 0xf82a, 0xf828, 0xf829, 0xf85f, 0xf82b, 0xf87f, 0xf809, 
+    0xf851, 0xf857, 0xf845, 0xf852, 0xf854, 0xf859, 0xf855, 0xf849, 
+    0xf84f, 0xf850, 0xf87b, 0xf87d, 0xf80d, 0xf702, 0xf841, 0xf853, 
+    0xf844, 0xf846, 0xf847, 0xf848, 0xf84a, 0xf84b, 0xf84c, 0xf83a, 
+    0xf822, 0xf87e, 0xf700, 0xf87c, 0xf85a, 0xf858, 0xf843, 0xf856, 
+    0xf842, 0xf84e, 0xf84d, 0xf83c, 0xf83e, 0xf83f, 0xf700, 0xf916, 
+    0xf703, 0xf820, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf914, 0xf209, 0xf911, 
+    0xf912, 0xf913, 0xf917, 0xf90e, 0xf90f, 0xf910, 0xf918, 0xf90b, 
+    0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf83e, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf919, 0xf702, 0xf915, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_58[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 
+    0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, 
+    0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, 
+    0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, 
+    0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, 
+    0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, 
+    0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf87c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_59[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf821, 0xf840, 0xf823, 0xf824, 0xf825, 0xf85e, 
+    0xf826, 0xf82a, 0xf828, 0xf829, 0xf85f, 0xf82b, 0xf87f, 0xf809, 
+    0xf851, 0xf857, 0xf845, 0xf852, 0xf854, 0xf859, 0xf855, 0xf849, 
+    0xf84f, 0xf850, 0xf87b, 0xf87d, 0xf80d, 0xf702, 0xf841, 0xf853, 
+    0xf844, 0xf846, 0xf847, 0xf848, 0xf84a, 0xf84b, 0xf84c, 0xf83a, 
+    0xf822, 0xf87e, 0xf700, 0xf87c, 0xf85a, 0xf858, 0xf843, 0xf856, 
+    0xf842, 0xf84e, 0xf84d, 0xf83c, 0xf83e, 0xf83f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf87c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_60[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf800, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 
+    0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf81c, 0xf50a, 
+    0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_61[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf81e, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf81c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_62[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf800, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf81c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_63[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf81e, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf81c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_64[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 
+    0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, 
+    0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, 
+    0xfb4f, 0xfb50, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb41, 0xfb53, 
+    0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03b, 
+    0xf027, 0xf060, 0xf700, 0xf05c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 
+    0xfb42, 0xfb4e, 0xfb4d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf200, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_65[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 
+    0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, 
+    0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 
+    0xfb6f, 0xfb70, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb61, 0xfb73, 
+    0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03a, 
+    0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 
+    0xfb62, 0xfb6e, 0xfb6d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf10c, 0xf10d, 0xf10e, 0xf10f, 0xf110, 
+    0xf111, 0xf112, 0xf113, 0xf11e, 0xf11f, 0xf208, 0xf203, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf120, 
+    0xf121, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf200, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf20b, 0xf601, 0xf602, 0xf117, 0xf600, 0xf20a, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_66[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 
+    0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, 
+    0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, 
+    0xfb4f, 0xfb50, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb41, 0xfb53, 
+    0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03b, 
+    0xf027, 0xf060, 0xf700, 0xf05c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 
+    0xfb42, 0xfb4e, 0xfb4d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf916, 
+    0xf703, 0xf020, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf914, 0xf202, 0xf911, 
+    0xf912, 0xf913, 0xf917, 0xf90e, 0xf90f, 0xf910, 0xf918, 0xf90b, 
+    0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf07c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf919, 0xf702, 0xf915, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_67[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 
+    0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, 
+    0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 
+    0xfb6f, 0xfb70, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb61, 0xfb73, 
+    0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03a, 
+    0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 
+    0xfb62, 0xfb6e, 0xfb6d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf518, 0xf519, 0xf51a, 0xf51b, 0xf51c, 
+    0xf51d, 0xf51e, 0xf51f, 0xf520, 0xf521, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0x00a6, 0xf522, 
+    0xf523, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_68[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf122, 0xf123, 0xf124, 0xf125, 0xf126, 
+    0xf127, 0xf128, 0xf129, 0xf12a, 0xf12b, 0xf208, 0xf204, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf12c, 
+    0xf12d, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_69[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf01e, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf12e, 0xf12f, 0xf130, 0xf131, 0xf132, 
+    0xf133, 0xf134, 0xf135, 0xf136, 0xf137, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf138, 
+    0xf139, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_70[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf01c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_71[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf01e, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf518, 0xf519, 0xf51a, 0xf51b, 0xf51c, 
+    0xf51d, 0xf51e, 0xf51f, 0xf520, 0xf521, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf522, 
+    0xf523, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_72[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 
+    0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, 
+    0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, 
+    0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, 
+    0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, 
+    0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, 
+    0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 
+    0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf202, 0xf907, 
+    0xf908, 0xf909, 0xf30b, 0xf904, 0xf905, 0xf906, 0xf30a, 0xf901, 
+    0xf902, 0xf903, 0xf900, 0xf310, 0xf206, 0xf200, 0xf83c, 0xf50a, 
+    0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf212, 
+    0xf118, 0xf210, 0xf211, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_73[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf821, 0xf840, 0xf823, 0xf824, 0xf825, 0xf85e, 
+    0xf826, 0xf82a, 0xf828, 0xf829, 0xf85f, 0xf82b, 0xf87f, 0xf809, 
+    0xf851, 0xf857, 0xf845, 0xf852, 0xf854, 0xf859, 0xf855, 0xf849, 
+    0xf84f, 0xf850, 0xf87b, 0xf87d, 0xf80d, 0xf702, 0xf841, 0xf853, 
+    0xf844, 0xf846, 0xf847, 0xf848, 0xf84a, 0xf84b, 0xf84c, 0xf83a, 
+    0xf822, 0xf87e, 0xf700, 0xf87c, 0xf85a, 0xf858, 0xf843, 0xf856, 
+    0xf842, 0xf84e, 0xf84d, 0xf83c, 0xf83e, 0xf83f, 0xf700, 0xf916, 
+    0xf703, 0xf820, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf914, 0xf209, 0xf911, 
+    0xf912, 0xf913, 0xf917, 0xf90e, 0xf90f, 0xf910, 0xf918, 0xf90b, 
+    0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf83e, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf919, 0xf702, 0xf915, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_74[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 
+    0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, 
+    0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, 
+    0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, 
+    0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, 
+    0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, 
+    0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf87c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_75[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf821, 0xf840, 0xf823, 0xf824, 0xf825, 0xf85e, 
+    0xf826, 0xf82a, 0xf828, 0xf829, 0xf85f, 0xf82b, 0xf87f, 0xf809, 
+    0xf851, 0xf857, 0xf845, 0xf852, 0xf854, 0xf859, 0xf855, 0xf849, 
+    0xf84f, 0xf850, 0xf87b, 0xf87d, 0xf80d, 0xf702, 0xf841, 0xf853, 
+    0xf844, 0xf846, 0xf847, 0xf848, 0xf84a, 0xf84b, 0xf84c, 0xf83a, 
+    0xf822, 0xf87e, 0xf700, 0xf87c, 0xf85a, 0xf858, 0xf843, 0xf856, 
+    0xf842, 0xf84e, 0xf84d, 0xf83c, 0xf83e, 0xf83f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf87c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_76[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf800, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 
+    0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf81c, 0xf50a, 
+    0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_77[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf81e, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf81c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_78[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf800, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf81c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_79[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf81e, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf81c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_80[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 
+    0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, 
+    0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, 
+    0xfb4f, 0xfb50, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb41, 0xfb53, 
+    0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03b, 
+    0xf027, 0xf060, 0xf700, 0xf05c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 
+    0xfb42, 0xfb4e, 0xfb4d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf200, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_81[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 
+    0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, 
+    0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 
+    0xfb6f, 0xfb70, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb61, 0xfb73, 
+    0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03a, 
+    0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 
+    0xfb62, 0xfb6e, 0xfb6d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf10c, 0xf10d, 0xf10e, 0xf10f, 0xf110, 
+    0xf111, 0xf112, 0xf113, 0xf11e, 0xf11f, 0xf208, 0xf203, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf120, 
+    0xf121, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf200, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf20b, 0xf601, 0xf602, 0xf117, 0xf600, 0xf20a, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_82[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 
+    0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, 
+    0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, 
+    0xfb4f, 0xfb50, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb41, 0xfb53, 
+    0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03b, 
+    0xf027, 0xf060, 0xf700, 0xf05c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 
+    0xfb42, 0xfb4e, 0xfb4d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf916, 
+    0xf703, 0xf020, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf914, 0xf202, 0xf911, 
+    0xf912, 0xf913, 0xf917, 0xf90e, 0xf90f, 0xf910, 0xf918, 0xf90b, 
+    0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf07c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf919, 0xf702, 0xf915, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_83[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 
+    0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, 
+    0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 
+    0xfb6f, 0xfb70, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb61, 0xfb73, 
+    0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03a, 
+    0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 
+    0xfb62, 0xfb6e, 0xfb6d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf518, 0xf519, 0xf51a, 0xf51b, 0xf51c, 
+    0xf51d, 0xf51e, 0xf51f, 0xf520, 0xf521, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0x00a6, 0xf522, 
+    0xf523, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_84[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf122, 0xf123, 0xf124, 0xf125, 0xf126, 
+    0xf127, 0xf128, 0xf129, 0xf12a, 0xf12b, 0xf208, 0xf204, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf12c, 
+    0xf12d, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_85[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf01e, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf12e, 0xf12f, 0xf130, 0xf131, 0xf132, 
+    0xf133, 0xf134, 0xf135, 0xf136, 0xf137, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf138, 
+    0xf139, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_86[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf01c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_87[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf01e, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf518, 0xf519, 0xf51a, 0xf51b, 0xf51c, 
+    0xf51d, 0xf51e, 0xf51f, 0xf520, 0xf521, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf522, 
+    0xf523, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_88[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 
+    0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, 
+    0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, 
+    0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, 
+    0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, 
+    0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, 
+    0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 
+    0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf202, 0xf907, 
+    0xf908, 0xf909, 0xf30b, 0xf904, 0xf905, 0xf906, 0xf30a, 0xf901, 
+    0xf902, 0xf903, 0xf900, 0xf310, 0xf206, 0xf200, 0xf83c, 0xf50a, 
+    0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf212, 
+    0xf118, 0xf210, 0xf211, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_89[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf821, 0xf840, 0xf823, 0xf824, 0xf825, 0xf85e, 
+    0xf826, 0xf82a, 0xf828, 0xf829, 0xf85f, 0xf82b, 0xf87f, 0xf809, 
+    0xf851, 0xf857, 0xf845, 0xf852, 0xf854, 0xf859, 0xf855, 0xf849, 
+    0xf84f, 0xf850, 0xf87b, 0xf87d, 0xf80d, 0xf702, 0xf841, 0xf853, 
+    0xf844, 0xf846, 0xf847, 0xf848, 0xf84a, 0xf84b, 0xf84c, 0xf83a, 
+    0xf822, 0xf87e, 0xf700, 0xf87c, 0xf85a, 0xf858, 0xf843, 0xf856, 
+    0xf842, 0xf84e, 0xf84d, 0xf83c, 0xf83e, 0xf83f, 0xf700, 0xf916, 
+    0xf703, 0xf820, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf914, 0xf209, 0xf911, 
+    0xf912, 0xf913, 0xf917, 0xf90e, 0xf90f, 0xf910, 0xf918, 0xf90b, 
+    0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf83e, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf919, 0xf702, 0xf915, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_90[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 
+    0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, 
+    0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, 
+    0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, 
+    0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, 
+    0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, 
+    0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf87c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_91[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf821, 0xf840, 0xf823, 0xf824, 0xf825, 0xf85e, 
+    0xf826, 0xf82a, 0xf828, 0xf829, 0xf85f, 0xf82b, 0xf87f, 0xf809, 
+    0xf851, 0xf857, 0xf845, 0xf852, 0xf854, 0xf859, 0xf855, 0xf849, 
+    0xf84f, 0xf850, 0xf87b, 0xf87d, 0xf80d, 0xf702, 0xf841, 0xf853, 
+    0xf844, 0xf846, 0xf847, 0xf848, 0xf84a, 0xf84b, 0xf84c, 0xf83a, 
+    0xf822, 0xf87e, 0xf700, 0xf87c, 0xf85a, 0xf858, 0xf843, 0xf856, 
+    0xf842, 0xf84e, 0xf84d, 0xf83c, 0xf83e, 0xf83f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf87c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_92[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf800, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 
+    0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf81c, 0xf50a, 
+    0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_93[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf81e, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf81c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_94[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf800, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf81c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_95[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf81e, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf81c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_96[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 
+    0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, 
+    0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, 
+    0xfb4f, 0xfb50, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb41, 0xfb53, 
+    0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03b, 
+    0xf027, 0xf060, 0xf700, 0xf05c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 
+    0xfb42, 0xfb4e, 0xfb4d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf200, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_97[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 
+    0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, 
+    0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 
+    0xfb6f, 0xfb70, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb61, 0xfb73, 
+    0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03a, 
+    0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 
+    0xfb62, 0xfb6e, 0xfb6d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf10c, 0xf10d, 0xf10e, 0xf10f, 0xf110, 
+    0xf111, 0xf112, 0xf113, 0xf11e, 0xf11f, 0xf208, 0xf203, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf120, 
+    0xf121, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf200, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf20b, 0xf601, 0xf602, 0xf117, 0xf600, 0xf20a, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_98[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 
+    0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, 
+    0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, 
+    0xfb4f, 0xfb50, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb41, 0xfb53, 
+    0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03b, 
+    0xf027, 0xf060, 0xf700, 0xf05c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 
+    0xfb42, 0xfb4e, 0xfb4d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf916, 
+    0xf703, 0xf020, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf914, 0xf202, 0xf911, 
+    0xf912, 0xf913, 0xf917, 0xf90e, 0xf90f, 0xf910, 0xf918, 0xf90b, 
+    0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf07c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf919, 0xf702, 0xf915, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_99[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 
+    0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, 
+    0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 
+    0xfb6f, 0xfb70, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb61, 0xfb73, 
+    0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03a, 
+    0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 
+    0xfb62, 0xfb6e, 0xfb6d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf518, 0xf519, 0xf51a, 0xf51b, 0xf51c, 
+    0xf51d, 0xf51e, 0xf51f, 0xf520, 0xf521, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0x00a6, 0xf522, 
+    0xf523, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_100[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf122, 0xf123, 0xf124, 0xf125, 0xf126, 
+    0xf127, 0xf128, 0xf129, 0xf12a, 0xf12b, 0xf208, 0xf204, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf12c, 
+    0xf12d, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_101[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf01e, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf12e, 0xf12f, 0xf130, 0xf131, 0xf132, 
+    0xf133, 0xf134, 0xf135, 0xf136, 0xf137, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf138, 
+    0xf139, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_102[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf01c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_103[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf01e, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf518, 0xf519, 0xf51a, 0xf51b, 0xf51c, 
+    0xf51d, 0xf51e, 0xf51f, 0xf520, 0xf521, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf522, 
+    0xf523, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_104[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 
+    0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, 
+    0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, 
+    0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, 
+    0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, 
+    0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, 
+    0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 
+    0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf202, 0xf907, 
+    0xf908, 0xf909, 0xf30b, 0xf904, 0xf905, 0xf906, 0xf30a, 0xf901, 
+    0xf902, 0xf903, 0xf900, 0xf310, 0xf206, 0xf200, 0xf83c, 0xf50a, 
+    0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf212, 
+    0xf118, 0xf210, 0xf211, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_105[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf821, 0xf840, 0xf823, 0xf824, 0xf825, 0xf85e, 
+    0xf826, 0xf82a, 0xf828, 0xf829, 0xf85f, 0xf82b, 0xf87f, 0xf809, 
+    0xf851, 0xf857, 0xf845, 0xf852, 0xf854, 0xf859, 0xf855, 0xf849, 
+    0xf84f, 0xf850, 0xf87b, 0xf87d, 0xf80d, 0xf702, 0xf841, 0xf853, 
+    0xf844, 0xf846, 0xf847, 0xf848, 0xf84a, 0xf84b, 0xf84c, 0xf83a, 
+    0xf822, 0xf87e, 0xf700, 0xf87c, 0xf85a, 0xf858, 0xf843, 0xf856, 
+    0xf842, 0xf84e, 0xf84d, 0xf83c, 0xf83e, 0xf83f, 0xf700, 0xf916, 
+    0xf703, 0xf820, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf914, 0xf209, 0xf911, 
+    0xf912, 0xf913, 0xf917, 0xf90e, 0xf90f, 0xf910, 0xf918, 0xf90b, 
+    0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf83e, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf919, 0xf702, 0xf915, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_106[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 
+    0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, 
+    0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, 
+    0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, 
+    0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, 
+    0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, 
+    0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf87c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_107[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf821, 0xf840, 0xf823, 0xf824, 0xf825, 0xf85e, 
+    0xf826, 0xf82a, 0xf828, 0xf829, 0xf85f, 0xf82b, 0xf87f, 0xf809, 
+    0xf851, 0xf857, 0xf845, 0xf852, 0xf854, 0xf859, 0xf855, 0xf849, 
+    0xf84f, 0xf850, 0xf87b, 0xf87d, 0xf80d, 0xf702, 0xf841, 0xf853, 
+    0xf844, 0xf846, 0xf847, 0xf848, 0xf84a, 0xf84b, 0xf84c, 0xf83a, 
+    0xf822, 0xf87e, 0xf700, 0xf87c, 0xf85a, 0xf858, 0xf843, 0xf856, 
+    0xf842, 0xf84e, 0xf84d, 0xf83c, 0xf83e, 0xf83f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf87c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_108[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf800, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 
+    0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf81c, 0xf50a, 
+    0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_109[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf81e, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf81c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_110[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf800, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf81c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_111[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf81e, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf81c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_112[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 
+    0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, 
+    0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, 
+    0xfb4f, 0xfb50, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb41, 0xfb53, 
+    0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03b, 
+    0xf027, 0xf060, 0xf700, 0xf05c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 
+    0xfb42, 0xfb4e, 0xfb4d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf200, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_113[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 
+    0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, 
+    0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 
+    0xfb6f, 0xfb70, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb61, 0xfb73, 
+    0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03a, 
+    0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 
+    0xfb62, 0xfb6e, 0xfb6d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf10c, 0xf10d, 0xf10e, 0xf10f, 0xf110, 
+    0xf111, 0xf112, 0xf113, 0xf11e, 0xf11f, 0xf208, 0xf203, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf120, 
+    0xf121, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf200, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf20b, 0xf601, 0xf602, 0xf117, 0xf600, 0xf20a, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_114[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 
+    0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, 
+    0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, 
+    0xfb4f, 0xfb50, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb41, 0xfb53, 
+    0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03b, 
+    0xf027, 0xf060, 0xf700, 0xf05c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, 
+    0xfb42, 0xfb4e, 0xfb4d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf916, 
+    0xf703, 0xf020, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf914, 0xf202, 0xf911, 
+    0xf912, 0xf913, 0xf917, 0xf90e, 0xf90f, 0xf910, 0xf918, 0xf90b, 
+    0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf07c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf919, 0xf702, 0xf915, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_115[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 
+    0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, 
+    0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 
+    0xfb6f, 0xfb70, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb61, 0xfb73, 
+    0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03a, 
+    0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, 
+    0xfb62, 0xfb6e, 0xfb6d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, 
+    0xf703, 0xf020, 0xfa06, 0xf518, 0xf519, 0xf51a, 0xf51b, 0xf51c, 
+    0xf51d, 0xf51e, 0xf51f, 0xf520, 0xf521, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0x00a6, 0xf522, 
+    0xf523, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_116[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf122, 0xf123, 0xf124, 0xf125, 0xf126, 
+    0xf127, 0xf128, 0xf129, 0xf12a, 0xf12b, 0xf208, 0xf204, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf12c, 
+    0xf12d, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_117[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf01e, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf12e, 0xf12f, 0xf130, 0xf131, 0xf132, 
+    0xf133, 0xf134, 0xf135, 0xf136, 0xf137, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf138, 
+    0xf139, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_118[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf01c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_119[NR_KEYS] = {
+    0xf27e, 0xf01b, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf01e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf009, 
+    0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 
+    0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf00d, 0xf702, 0xf001, 0xf013, 
+    0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 
+    0xf200, 0xf01e, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, 
+    0xf002, 0xf00e, 0xf201, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, 
+    0xf703, 0xf000, 0xfa06, 0xf518, 0xf519, 0xf51a, 0xf51b, 0xf51c, 
+    0xf51d, 0xf51e, 0xf51f, 0xf520, 0xf521, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf01c, 0xf522, 
+    0xf523, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf205, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_120[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 
+    0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, 
+    0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, 
+    0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, 
+    0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, 
+    0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, 
+    0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 
+    0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf202, 0xf907, 
+    0xf908, 0xf909, 0xf30b, 0xf904, 0xf905, 0xf906, 0xf30a, 0xf901, 
+    0xf902, 0xf903, 0xf900, 0xf310, 0xf206, 0xf200, 0xf83c, 0xf50a, 
+    0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf212, 
+    0xf118, 0xf210, 0xf211, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_121[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf821, 0xf840, 0xf823, 0xf824, 0xf825, 0xf85e, 
+    0xf826, 0xf82a, 0xf828, 0xf829, 0xf85f, 0xf82b, 0xf87f, 0xf809, 
+    0xf851, 0xf857, 0xf845, 0xf852, 0xf854, 0xf859, 0xf855, 0xf849, 
+    0xf84f, 0xf850, 0xf87b, 0xf87d, 0xf80d, 0xf702, 0xf841, 0xf853, 
+    0xf844, 0xf846, 0xf847, 0xf848, 0xf84a, 0xf84b, 0xf84c, 0xf83a, 
+    0xf822, 0xf87e, 0xf700, 0xf87c, 0xf85a, 0xf858, 0xf843, 0xf856, 
+    0xf842, 0xf84e, 0xf84d, 0xf83c, 0xf83e, 0xf83f, 0xf700, 0xf916, 
+    0xf703, 0xf820, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf914, 0xf209, 0xf911, 
+    0xf912, 0xf913, 0xf917, 0xf90e, 0xf90f, 0xf910, 0xf918, 0xf90b, 
+    0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf83e, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf919, 0xf702, 0xf915, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_122[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 
+    0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, 
+    0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, 
+    0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, 
+    0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, 
+    0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, 
+    0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf87c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_123[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf821, 0xf840, 0xf823, 0xf824, 0xf825, 0xf85e, 
+    0xf826, 0xf82a, 0xf828, 0xf829, 0xf85f, 0xf82b, 0xf87f, 0xf809, 
+    0xf851, 0xf857, 0xf845, 0xf852, 0xf854, 0xf859, 0xf855, 0xf849, 
+    0xf84f, 0xf850, 0xf87b, 0xf87d, 0xf80d, 0xf702, 0xf841, 0xf853, 
+    0xf844, 0xf846, 0xf847, 0xf848, 0xf84a, 0xf84b, 0xf84c, 0xf83a, 
+    0xf822, 0xf87e, 0xf700, 0xf87c, 0xf85a, 0xf858, 0xf843, 0xf856, 
+    0xf842, 0xf84e, 0xf84d, 0xf83c, 0xf83e, 0xf83f, 0xf700, 0xf30c, 
+    0xf703, 0xf820, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf87c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf206, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_124[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf800, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 
+    0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf81c, 0xf50a, 
+    0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_125[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf81e, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 
+    0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf81c, 0xf516, 
+    0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_126[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf800, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf81c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+static unsigned short default_key_map_127[NR_KEYS] = {
+    0xf27e, 0xf81b, 0xf200, 0xf800, 0xf200, 0xf200, 0xf200, 0xf81e, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf81f, 0xf200, 0xf808, 0xf809, 
+    0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 
+    0xf80f, 0xf810, 0xf81b, 0xf81d, 0xf80d, 0xf702, 0xf801, 0xf813, 
+    0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 
+    0xf200, 0xf81e, 0xf700, 0xf81c, 0xf81a, 0xf818, 0xf803, 0xf816, 
+    0xf802, 0xf80e, 0xf80d, 0xf200, 0xf20e, 0xf87f, 0xf700, 0xf30c, 
+    0xf703, 0xf800, 0xfa06, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 
+    0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, 
+    0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, 
+    0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf81c, 0xf10a, 
+    0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf30e, 0xf702, 0xf30d, 0xf81c, 0xf703, 0xf205, 0xf114, 0xf603, 
+    0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf11d, 
+    0xf200, 0xf310, 0xf200, 0xf200, 0xf200, 0xf703, 0xf703, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+    0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 
+};
+#endif /* INCLUDE_EXTENDED_KEYMAP */
+
+/* *INDENT-ON* */ /* clang-format on */
+
+static unsigned short *default_key_maps[MAX_NR_KEYMAPS] = {
+    default_key_map_0,
+    default_key_map_1,
+    default_key_map_2,
+    default_key_map_3,
+#ifdef INCLUDE_EXTENDED_KEYMAP
+    default_key_map_4,
+    default_key_map_5,
+    default_key_map_6,
+    default_key_map_7,
+    default_key_map_8,
+    default_key_map_9,
+    default_key_map_10,
+    default_key_map_11,
+    default_key_map_12,
+    default_key_map_13,
+    default_key_map_14,
+    default_key_map_15,
+    default_key_map_16,
+    default_key_map_17,
+    default_key_map_18,
+    default_key_map_19,
+    default_key_map_20,
+    default_key_map_21,
+    default_key_map_22,
+    default_key_map_23,
+    default_key_map_24,
+    default_key_map_25,
+    default_key_map_26,
+    default_key_map_27,
+    default_key_map_28,
+    default_key_map_29,
+    default_key_map_30,
+    default_key_map_31,
+    default_key_map_32,
+    default_key_map_33,
+    default_key_map_34,
+    default_key_map_35,
+    default_key_map_36,
+    default_key_map_37,
+    default_key_map_38,
+    default_key_map_39,
+    default_key_map_40,
+    default_key_map_41,
+    default_key_map_42,
+    default_key_map_43,
+    default_key_map_44,
+    default_key_map_45,
+    default_key_map_46,
+    default_key_map_47,
+    default_key_map_48,
+    default_key_map_49,
+    default_key_map_50,
+    default_key_map_51,
+    default_key_map_52,
+    default_key_map_53,
+    default_key_map_54,
+    default_key_map_55,
+    default_key_map_56,
+    default_key_map_57,
+    default_key_map_58,
+    default_key_map_59,
+    default_key_map_60,
+    default_key_map_61,
+    default_key_map_62,
+    default_key_map_63,
+    default_key_map_64,
+    default_key_map_65,
+    default_key_map_66,
+    default_key_map_67,
+    default_key_map_68,
+    default_key_map_69,
+    default_key_map_70,
+    default_key_map_71,
+    default_key_map_72,
+    default_key_map_73,
+    default_key_map_74,
+    default_key_map_75,
+    default_key_map_76,
+    default_key_map_77,
+    default_key_map_78,
+    default_key_map_79,
+    default_key_map_80,
+    default_key_map_81,
+    default_key_map_82,
+    default_key_map_83,
+    default_key_map_84,
+    default_key_map_85,
+    default_key_map_86,
+    default_key_map_87,
+    default_key_map_88,
+    default_key_map_89,
+    default_key_map_90,
+    default_key_map_91,
+    default_key_map_92,
+    default_key_map_93,
+    default_key_map_94,
+    default_key_map_95,
+    default_key_map_96,
+    default_key_map_97,
+    default_key_map_98,
+    default_key_map_99,
+    default_key_map_100,
+    default_key_map_101,
+    default_key_map_102,
+    default_key_map_103,
+    default_key_map_104,
+    default_key_map_105,
+    default_key_map_106,
+    default_key_map_107,
+    default_key_map_108,
+    default_key_map_109,
+    default_key_map_110,
+    default_key_map_111,
+    default_key_map_112,
+    default_key_map_113,
+    default_key_map_114,
+    default_key_map_115,
+    default_key_map_116,
+    default_key_map_117,
+    default_key_map_118,
+    default_key_map_119,
+    default_key_map_120,
+    default_key_map_121,
+    default_key_map_122,
+    default_key_map_123,
+    default_key_map_124,
+    default_key_map_125,
+    default_key_map_126,
+    default_key_map_127,
+#else  /* !INCLUDE_EXTENDED_KEYMAP */
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+#endif /* INCLUDE_EXTENDED_KEYMAP */
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+};

+ 414 - 0
thirdparty/sdl/core/linux/SDL_fcitx.c

@@ -0,0 +1,414 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#include <unistd.h>
+
+#include "SDL_fcitx.h"
+//#include "../../video/SDL_sysvideo.h"
+#include "SDL_dbus.h"
+
+#ifdef SDL_VIDEO_DRIVER_X11
+#include "../../video/x11/SDL_x11video.h"
+#endif
+
+#define FCITX_DBUS_SERVICE "org.freedesktop.portal.Fcitx"
+
+#define FCITX_IM_DBUS_PATH "/org/freedesktop/portal/inputmethod"
+
+#define FCITX_IM_DBUS_INTERFACE "org.fcitx.Fcitx.InputMethod1"
+#define FCITX_IC_DBUS_INTERFACE "org.fcitx.Fcitx.InputContext1"
+
+#define DBUS_TIMEOUT 500
+
+typedef struct FcitxClient
+{
+    SDL_DBusContext *dbus;
+
+    char *ic_path;
+
+    int id;
+
+    SDL_Rect cursor_rect;
+} FcitxClient;
+
+static FcitxClient fcitx_client;
+
+static char *GetAppName(void)
+{
+#if defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_FREEBSD)
+    char *spot;
+    char procfile[1024];
+    char linkfile[1024];
+    int linksize;
+
+#ifdef SDL_PLATFORM_LINUX
+    (void)SDL_snprintf(procfile, sizeof(procfile), "/proc/%d/exe", getpid());
+#elif defined(SDL_PLATFORM_FREEBSD)
+    (void)SDL_snprintf(procfile, sizeof(procfile), "/proc/%d/file", getpid());
+#endif
+    linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1);
+    if (linksize > 0) {
+        linkfile[linksize] = '\0';
+        spot = SDL_strrchr(linkfile, '/');
+        if (spot) {
+            return SDL_strdup(spot + 1);
+        } else {
+            return SDL_strdup(linkfile);
+        }
+    }
+#endif // SDL_PLATFORM_LINUX || SDL_PLATFORM_FREEBSD
+
+    return SDL_strdup("SDL_App");
+}
+
+static size_t Fcitx_GetPreeditString(SDL_DBusContext *dbus,
+                       DBusMessage *msg,
+                       char **ret,
+                       Sint32 *start_pos,
+                       Sint32 *end_pos)
+{
+    char *text = NULL, *subtext;
+    size_t text_bytes = 0;
+    DBusMessageIter iter, array, sub;
+    Sint32 p_start_pos = -1;
+    Sint32 p_end_pos = -1;
+
+    dbus->message_iter_init(msg, &iter);
+    // Message type is a(si)i, we only need string part
+    if (dbus->message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) {
+        size_t pos = 0;
+        // First pass: calculate string length
+        dbus->message_iter_recurse(&iter, &array);
+        while (dbus->message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
+            dbus->message_iter_recurse(&array, &sub);
+            subtext = NULL;
+            if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
+                dbus->message_iter_get_basic(&sub, &subtext);
+                if (subtext && *subtext) {
+                    text_bytes += SDL_strlen(subtext);
+                }
+            }
+            dbus->message_iter_next(&sub);
+            if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_INT32 && p_end_pos == -1) {
+                // Type is a bit field defined as follows:
+                // bit 3: Underline, bit 4: HighLight, bit 5: DontCommit,
+                // bit 6: Bold,      bit 7: Strike,    bit 8: Italic
+                Sint32 type;
+                dbus->message_iter_get_basic(&sub, &type);
+                // We only consider highlight
+                if (type & (1 << 4)) {
+                    if (p_start_pos == -1) {
+                        p_start_pos = pos;
+                    }
+                } else if (p_start_pos != -1 && p_end_pos == -1) {
+                    p_end_pos = pos;
+                }
+            }
+            dbus->message_iter_next(&array);
+            if (subtext && *subtext) {
+                pos += SDL_utf8strlen(subtext);
+            }
+        }
+        if (p_start_pos != -1 && p_end_pos == -1) {
+            p_end_pos = pos;
+        }
+        if (text_bytes) {
+            text = SDL_malloc(text_bytes + 1);
+        }
+
+        if (text) {
+            char *pivot = text;
+            // Second pass: join all the sub string
+            dbus->message_iter_recurse(&iter, &array);
+            while (dbus->message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
+                dbus->message_iter_recurse(&array, &sub);
+                if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
+                    dbus->message_iter_get_basic(&sub, &subtext);
+                    if (subtext && *subtext) {
+                        size_t length = SDL_strlen(subtext);
+                        SDL_strlcpy(pivot, subtext, length + 1);
+                        pivot += length;
+                    }
+                }
+                dbus->message_iter_next(&array);
+            }
+        } else {
+            text_bytes = 0;
+        }
+    }
+
+    *ret = text;
+    *start_pos = p_start_pos;
+    *end_pos = p_end_pos;
+    return text_bytes;
+}
+
+static Sint32 Fcitx_GetPreeditCursorByte(SDL_DBusContext *dbus, DBusMessage *msg)
+{
+    Sint32 byte = -1;
+    DBusMessageIter iter;
+
+    dbus->message_iter_init(msg, &iter);
+
+    dbus->message_iter_next(&iter);
+
+    if (dbus->message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
+        return -1;
+    }
+
+    dbus->message_iter_get_basic(&iter, &byte);
+
+    return byte;
+}
+
+static DBusHandlerResult DBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+    SDL_DBusContext *dbus = (SDL_DBusContext *)data;
+
+    if (dbus->message_is_signal(msg, FCITX_IC_DBUS_INTERFACE, "CommitString")) {
+        DBusMessageIter iter;
+        const char *text = NULL;
+
+        dbus->message_iter_init(msg, &iter);
+        dbus->message_iter_get_basic(&iter, &text);
+
+        //SDL_SendKeyboardText(text);
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+    }
+
+    if (dbus->message_is_signal(msg, FCITX_IC_DBUS_INTERFACE, "UpdateFormattedPreedit")) {
+        char *text = NULL;
+        Sint32 start_pos, end_pos;
+        size_t text_bytes = Fcitx_GetPreeditString(dbus, msg, &text, &start_pos, &end_pos);
+        if (text_bytes) {
+            if (start_pos == -1) {
+                Sint32 byte_pos = Fcitx_GetPreeditCursorByte(dbus, msg);
+                start_pos = byte_pos >= 0 ? SDL_utf8strnlen(text, byte_pos) : -1;
+            }
+            //SDL_SendEditingText(text, start_pos, end_pos >= 0 ? end_pos - start_pos : -1);
+            SDL_free(text);
+        } else {
+            //SDL_SendEditingText("", 0, 0);
+        }
+
+        //SDL_Fcitx_UpdateTextInputArea(SDL_GetKeyboardFocus());
+        return DBUS_HANDLER_RESULT_HANDLED;
+    }
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void FcitxClientICCallMethod(FcitxClient *client, const char *method)
+{
+    if (!client->ic_path) {
+        return;
+    }
+    SDL_DBus_CallVoidMethod(FCITX_DBUS_SERVICE, client->ic_path, FCITX_IC_DBUS_INTERFACE, method, DBUS_TYPE_INVALID);
+}
+
+static void SDLCALL Fcitx_SetCapabilities(void *data,
+                                          const char *name,
+                                          const char *old_val,
+                                          const char *hint)
+{
+    FcitxClient *client = (FcitxClient *)data;
+    Uint64 caps = 0;
+    if (!client->ic_path) {
+        return;
+    }
+
+    if (hint && SDL_strstr(hint, "composition")) {
+        caps |= (1 << 1); // Preedit Flag
+        caps |= (1 << 4); // Formatted Preedit Flag
+    }
+    if (hint && SDL_strstr(hint, "candidates")) {
+        // FIXME, turn off native candidate rendering
+    }
+
+    SDL_DBus_CallVoidMethod(FCITX_DBUS_SERVICE, client->ic_path, FCITX_IC_DBUS_INTERFACE, "SetCapability", DBUS_TYPE_UINT64, &caps, DBUS_TYPE_INVALID);
+}
+
+static bool FcitxCreateInputContext(SDL_DBusContext *dbus, const char *appname, char **ic_path)
+{
+    const char *program = "program";
+    bool result = false;
+
+    if (dbus && dbus->session_conn) {
+        DBusMessage *msg = dbus->message_new_method_call(FCITX_DBUS_SERVICE, FCITX_IM_DBUS_PATH, FCITX_IM_DBUS_INTERFACE, "CreateInputContext");
+        if (msg) {
+            DBusMessage *reply = NULL;
+            DBusMessageIter args, array, sub;
+            dbus->message_iter_init_append(msg, &args);
+            dbus->message_iter_open_container(&args, DBUS_TYPE_ARRAY, "(ss)", &array);
+            dbus->message_iter_open_container(&array, DBUS_TYPE_STRUCT, 0, &sub);
+            dbus->message_iter_append_basic(&sub, DBUS_TYPE_STRING, &program);
+            dbus->message_iter_append_basic(&sub, DBUS_TYPE_STRING, &appname);
+            dbus->message_iter_close_container(&array, &sub);
+            dbus->message_iter_close_container(&args, &array);
+            reply = dbus->connection_send_with_reply_and_block(dbus->session_conn, msg, 300, NULL);
+            if (reply) {
+                if (dbus->message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, ic_path, DBUS_TYPE_INVALID)) {
+                    result = true;
+                }
+                dbus->message_unref(reply);
+            }
+            dbus->message_unref(msg);
+        }
+    }
+    return result;
+}
+
+static bool FcitxClientCreateIC(FcitxClient *client)
+{
+    char *appname = GetAppName();
+    char *ic_path = NULL;
+    SDL_DBusContext *dbus = client->dbus;
+
+    // SDL_DBus_CallMethod cannot handle a(ss) type, call dbus function directly
+    if (!FcitxCreateInputContext(dbus, appname, &ic_path)) {
+        ic_path = NULL; // just in case.
+    }
+
+    SDL_free(appname);
+
+    if (ic_path) {
+        SDL_free(client->ic_path);
+        client->ic_path = SDL_strdup(ic_path);
+
+        dbus->bus_add_match(dbus->session_conn,
+                            "type='signal', interface='org.fcitx.Fcitx.InputContext1'",
+                            NULL);
+        dbus->connection_add_filter(dbus->session_conn,
+                                    &DBus_MessageFilter, dbus,
+                                    NULL);
+        dbus->connection_flush(dbus->session_conn);
+
+        SDL_AddHintCallback(SDL_HINT_IME_IMPLEMENTED_UI, Fcitx_SetCapabilities, client);
+        return true;
+    }
+
+    return false;
+}
+
+static Uint32 Fcitx_ModState(void)
+{
+    Uint32 fcitx_mods = 0;
+    SDL_Keymod sdl_mods = SDL_GetModState();
+
+    if (sdl_mods & SDL_KMOD_SHIFT) {
+        fcitx_mods |= (1 << 0);
+    }
+    if (sdl_mods & SDL_KMOD_CAPS) {
+        fcitx_mods |= (1 << 1);
+    }
+    if (sdl_mods & SDL_KMOD_CTRL) {
+        fcitx_mods |= (1 << 2);
+    }
+    if (sdl_mods & SDL_KMOD_ALT) {
+        fcitx_mods |= (1 << 3);
+    }
+    if (sdl_mods & SDL_KMOD_NUM) {
+        fcitx_mods |= (1 << 4);
+    }
+    if (sdl_mods & SDL_KMOD_MODE) {
+        fcitx_mods |= (1 << 7);
+    }
+    if (sdl_mods & SDL_KMOD_LGUI) {
+        fcitx_mods |= (1 << 6);
+    }
+    if (sdl_mods & SDL_KMOD_RGUI) {
+        fcitx_mods |= (1 << 28);
+    }
+
+    return fcitx_mods;
+}
+
+bool SDL_Fcitx_Init(void)
+{
+    fcitx_client.dbus = SDL_DBus_GetContext();
+
+    fcitx_client.cursor_rect.x = -1;
+    fcitx_client.cursor_rect.y = -1;
+    fcitx_client.cursor_rect.w = 0;
+    fcitx_client.cursor_rect.h = 0;
+
+    return FcitxClientCreateIC(&fcitx_client);
+}
+
+void SDL_Fcitx_Quit(void)
+{
+    FcitxClientICCallMethod(&fcitx_client, "DestroyIC");
+    if (fcitx_client.ic_path) {
+        SDL_free(fcitx_client.ic_path);
+        fcitx_client.ic_path = NULL;
+    }
+}
+
+void SDL_Fcitx_SetFocus(bool focused)
+{
+    if (focused) {
+        FcitxClientICCallMethod(&fcitx_client, "FocusIn");
+    } else {
+        FcitxClientICCallMethod(&fcitx_client, "FocusOut");
+    }
+}
+
+void SDL_Fcitx_Reset(void)
+{
+    FcitxClientICCallMethod(&fcitx_client, "Reset");
+}
+
+bool SDL_Fcitx_ProcessKeyEvent(Uint32 keysym, Uint32 keycode, bool down)
+{
+    Uint32 mod_state = Fcitx_ModState();
+    Uint32 handled = false;
+    Uint32 is_release = !down;
+    Uint32 event_time = 0;
+
+    if (!fcitx_client.ic_path) {
+        return false;
+    }
+
+    if (SDL_DBus_CallMethod(FCITX_DBUS_SERVICE, fcitx_client.ic_path, FCITX_IC_DBUS_INTERFACE, "ProcessKeyEvent",
+                            DBUS_TYPE_UINT32, &keysym, DBUS_TYPE_UINT32, &keycode, DBUS_TYPE_UINT32, &mod_state, DBUS_TYPE_BOOLEAN, &is_release, DBUS_TYPE_UINT32, &event_time, DBUS_TYPE_INVALID,
+                            DBUS_TYPE_BOOLEAN, &handled, DBUS_TYPE_INVALID)) {
+        if (handled) {
+            //SDL_Fcitx_UpdateTextInputArea(SDL_GetKeyboardFocus());
+            return true;
+        }
+    }
+
+    return false;
+}
+
+void SDL_Fcitx_PumpEvents(void)
+{
+    SDL_DBusContext *dbus = fcitx_client.dbus;
+    DBusConnection *conn = dbus->session_conn;
+
+    dbus->connection_read_write(conn, 0);
+
+    while (dbus->connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS) {
+        // Do nothing, actual work happens in DBus_MessageFilter
+    }
+}

+ 35 - 0
thirdparty/sdl/core/linux/SDL_fcitx.h

@@ -0,0 +1,35 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#ifndef SDL_fcitx_h_
+#define SDL_fcitx_h_
+
+#include "SDL_internal.h"
+
+extern bool SDL_Fcitx_Init(void);
+extern void SDL_Fcitx_Quit(void);
+extern void SDL_Fcitx_SetFocus(bool focused);
+extern void SDL_Fcitx_Reset(void);
+extern bool SDL_Fcitx_ProcessKeyEvent(Uint32 keysym, Uint32 keycode, bool down);
+extern void SDL_Fcitx_UpdateTextInputArea(SDL_Window *window);
+extern void SDL_Fcitx_PumpEvents(void);
+
+#endif // SDL_fcitx_h_

+ 694 - 0
thirdparty/sdl/core/linux/SDL_ibus.c

@@ -0,0 +1,694 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#ifdef HAVE_IBUS_IBUS_H
+#include "SDL_ibus.h"
+#include "SDL_dbus.h"
+
+#ifdef SDL_USE_LIBDBUS
+
+//#include "../../video/SDL_sysvideo.h"
+#include "../../events/SDL_keyboard_c.h"
+
+#ifdef SDL_VIDEO_DRIVER_X11
+#include "../../video/x11/SDL_x11video.h"
+#endif
+
+#include <sys/inotify.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+static const char IBUS_PATH[] = "/org/freedesktop/IBus";
+
+static const char IBUS_SERVICE[] = "org.freedesktop.IBus";
+static const char IBUS_INTERFACE[] = "org.freedesktop.IBus";
+static const char IBUS_INPUT_INTERFACE[] = "org.freedesktop.IBus.InputContext";
+
+static const char IBUS_PORTAL_SERVICE[] = "org.freedesktop.portal.IBus";
+static const char IBUS_PORTAL_INTERFACE[] = "org.freedesktop.IBus.Portal";
+static const char IBUS_PORTAL_INPUT_INTERFACE[] = "org.freedesktop.IBus.InputContext";
+
+static const char *ibus_service = NULL;
+static const char *ibus_interface = NULL;
+static const char *ibus_input_interface = NULL;
+static char *input_ctx_path = NULL;
+static SDL_Rect ibus_cursor_rect = { 0, 0, 0, 0 };
+static DBusConnection *ibus_conn = NULL;
+static bool ibus_is_portal_interface = false;
+static char *ibus_addr_file = NULL;
+static int inotify_fd = -1, inotify_wd = -1;
+
+static Uint32 IBus_ModState(void)
+{
+    Uint32 ibus_mods = 0;
+    SDL_Keymod sdl_mods = SDL_GetModState();
+
+    // Not sure about MOD3, MOD4 and HYPER mappings
+    if (sdl_mods & SDL_KMOD_LSHIFT) {
+        ibus_mods |= IBUS_SHIFT_MASK;
+    }
+    if (sdl_mods & SDL_KMOD_CAPS) {
+        ibus_mods |= IBUS_LOCK_MASK;
+    }
+    if (sdl_mods & SDL_KMOD_LCTRL) {
+        ibus_mods |= IBUS_CONTROL_MASK;
+    }
+    if (sdl_mods & SDL_KMOD_LALT) {
+        ibus_mods |= IBUS_MOD1_MASK;
+    }
+    if (sdl_mods & SDL_KMOD_NUM) {
+        ibus_mods |= IBUS_MOD2_MASK;
+    }
+    if (sdl_mods & SDL_KMOD_MODE) {
+        ibus_mods |= IBUS_MOD5_MASK;
+    }
+    if (sdl_mods & SDL_KMOD_LGUI) {
+        ibus_mods |= IBUS_SUPER_MASK;
+    }
+    if (sdl_mods & SDL_KMOD_RGUI) {
+        ibus_mods |= IBUS_META_MASK;
+    }
+
+    return ibus_mods;
+}
+
+static bool IBus_EnterVariant(DBusConnection *conn, DBusMessageIter *iter, SDL_DBusContext *dbus,
+                                  DBusMessageIter *inside, const char *struct_id, size_t id_size)
+{
+    DBusMessageIter sub;
+    if (dbus->message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) {
+        return false;
+    }
+
+    dbus->message_iter_recurse(iter, &sub);
+
+    if (dbus->message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+        return false;
+    }
+
+    dbus->message_iter_recurse(&sub, inside);
+
+    if (dbus->message_iter_get_arg_type(inside) != DBUS_TYPE_STRING) {
+        return false;
+    }
+
+    dbus->message_iter_get_basic(inside, &struct_id);
+    if (!struct_id || SDL_strncmp(struct_id, struct_id, id_size) != 0) {
+        return false;
+    }
+    return true;
+}
+
+static bool IBus_GetDecorationPosition(DBusConnection *conn, DBusMessageIter *iter, SDL_DBusContext *dbus,
+                                           Uint32 *start_pos, Uint32 *end_pos)
+{
+    DBusMessageIter sub1, sub2, array;
+
+    if (!IBus_EnterVariant(conn, iter, dbus, &sub1, "IBusText", sizeof("IBusText"))) {
+        return false;
+    }
+
+    dbus->message_iter_next(&sub1);
+    dbus->message_iter_next(&sub1);
+    dbus->message_iter_next(&sub1);
+
+    if (!IBus_EnterVariant(conn, &sub1, dbus, &sub2, "IBusAttrList", sizeof("IBusAttrList"))) {
+        return false;
+    }
+
+    dbus->message_iter_next(&sub2);
+    dbus->message_iter_next(&sub2);
+
+    if (dbus->message_iter_get_arg_type(&sub2) != DBUS_TYPE_ARRAY) {
+        return false;
+    }
+
+    dbus->message_iter_recurse(&sub2, &array);
+
+    while (dbus->message_iter_get_arg_type(&array) == DBUS_TYPE_VARIANT) {
+        DBusMessageIter sub;
+        if (IBus_EnterVariant(conn, &array, dbus, &sub, "IBusAttribute", sizeof("IBusAttribute"))) {
+            Uint32 type;
+
+            dbus->message_iter_next(&sub);
+            dbus->message_iter_next(&sub);
+
+            // From here on, the structure looks like this:
+            // Uint32 type: 1=underline, 2=foreground, 3=background
+            // Uint32 value: for underline it's 0=NONE, 1=SINGLE, 2=DOUBLE,
+            // 3=LOW,  4=ERROR
+            // for foreground and background it's a color
+            // Uint32 start_index: starting position for the style (utf8-char)
+            // Uint32 end_index: end position for the style (utf8-char)
+
+            dbus->message_iter_get_basic(&sub, &type);
+            // We only use the background type to determine the selection
+            if (type == 3) {
+                Uint32 start = -1;
+                dbus->message_iter_next(&sub);
+                dbus->message_iter_next(&sub);
+                if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32) {
+                    dbus->message_iter_get_basic(&sub, &start);
+                    dbus->message_iter_next(&sub);
+                    if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32) {
+                        dbus->message_iter_get_basic(&sub, end_pos);
+                        *start_pos = start;
+                        return true;
+                    }
+                }
+            }
+        }
+        dbus->message_iter_next(&array);
+    }
+    return false;
+}
+
+static const char *IBus_GetVariantText(DBusConnection *conn, DBusMessageIter *iter, SDL_DBusContext *dbus)
+{
+    // The text we need is nested weirdly, use dbus-monitor to see the structure better
+    const char *text = NULL;
+    DBusMessageIter sub;
+
+    if (!IBus_EnterVariant(conn, iter, dbus, &sub, "IBusText", sizeof("IBusText"))) {
+        return NULL;
+    }
+
+    dbus->message_iter_next(&sub);
+    dbus->message_iter_next(&sub);
+
+    if (dbus->message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
+        return NULL;
+    }
+    dbus->message_iter_get_basic(&sub, &text);
+
+    return text;
+}
+
+static bool IBus_GetVariantCursorPos(DBusConnection *conn, DBusMessageIter *iter, SDL_DBusContext *dbus,
+                                         Uint32 *pos)
+{
+    dbus->message_iter_next(iter);
+
+    if (dbus->message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32) {
+        return false;
+    }
+
+    dbus->message_iter_get_basic(iter, pos);
+
+    return true;
+}
+
+static DBusHandlerResult IBus_MessageHandler(DBusConnection *conn, DBusMessage *msg, void *user_data)
+{
+    SDL_DBusContext *dbus = (SDL_DBusContext *)user_data;
+
+    if (dbus->message_is_signal(msg, ibus_input_interface, "CommitText")) {
+        DBusMessageIter iter;
+        const char *text;
+
+        dbus->message_iter_init(msg, &iter);
+        text = IBus_GetVariantText(conn, &iter, dbus);
+
+        SDL_SendKeyboardText(text);
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+    }
+
+    if (dbus->message_is_signal(msg, ibus_input_interface, "UpdatePreeditText")) {
+        DBusMessageIter iter;
+        const char *text;
+
+        dbus->message_iter_init(msg, &iter);
+        text = IBus_GetVariantText(conn, &iter, dbus);
+
+        if (text) {
+            Uint32 pos, start_pos, end_pos;
+            bool has_pos = false;
+            bool has_dec_pos = false;
+
+            dbus->message_iter_init(msg, &iter);
+            has_dec_pos = IBus_GetDecorationPosition(conn, &iter, dbus, &start_pos, &end_pos);
+            if (!has_dec_pos) {
+                dbus->message_iter_init(msg, &iter);
+                has_pos = IBus_GetVariantCursorPos(conn, &iter, dbus, &pos);
+            }
+
+            if (has_dec_pos) {
+                SDL_SendEditingText(text, start_pos, end_pos - start_pos);
+            } else if (has_pos) {
+                SDL_SendEditingText(text, pos, -1);
+            } else {
+                SDL_SendEditingText(text, -1, -1);
+            }
+        }
+
+        //SDL_IBus_UpdateTextInputArea(SDL_GetKeyboardFocus());
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+    }
+
+    if (dbus->message_is_signal(msg, ibus_input_interface, "HidePreeditText")) {
+        SDL_SendEditingText("", 0, 0);
+        return DBUS_HANDLER_RESULT_HANDLED;
+    }
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static char *IBus_ReadAddressFromFile(const char *file_path)
+{
+    char addr_buf[1024];
+    bool success = false;
+    FILE *addr_file;
+
+    addr_file = fopen(file_path, "r");
+    if (!addr_file) {
+        return NULL;
+    }
+
+    while (fgets(addr_buf, sizeof(addr_buf), addr_file)) {
+        if (SDL_strncmp(addr_buf, "IBUS_ADDRESS=", sizeof("IBUS_ADDRESS=") - 1) == 0) {
+            size_t sz = SDL_strlen(addr_buf);
+            if (addr_buf[sz - 1] == '\n') {
+                addr_buf[sz - 1] = 0;
+            }
+            if (addr_buf[sz - 2] == '\r') {
+                addr_buf[sz - 2] = 0;
+            }
+            success = true;
+            break;
+        }
+    }
+
+    (void)fclose(addr_file);
+
+    if (success) {
+        return SDL_strdup(addr_buf + (sizeof("IBUS_ADDRESS=") - 1));
+    } else {
+        return NULL;
+    }
+}
+
+static char *IBus_GetDBusAddressFilename(void)
+{
+    SDL_DBusContext *dbus;
+    const char *disp_env;
+    char config_dir[PATH_MAX];
+    char *display = NULL;
+    const char *addr;
+    const char *conf_env;
+    char *key;
+    char file_path[PATH_MAX];
+    const char *host;
+    char *disp_num, *screen_num;
+
+    if (ibus_addr_file) {
+        return SDL_strdup(ibus_addr_file);
+    }
+
+    dbus = SDL_DBus_GetContext();
+    if (!dbus) {
+        return NULL;
+    }
+
+    // Use this environment variable if it exists.
+    addr = SDL_getenv("IBUS_ADDRESS");
+    if (addr && *addr) {
+        return SDL_strdup(addr);
+    }
+
+    /* Otherwise, we have to get the hostname, display, machine id, config dir
+       and look up the address from a filepath using all those bits, eek. */
+    disp_env = SDL_getenv("DISPLAY");
+
+    if (!disp_env || !*disp_env) {
+        display = SDL_strdup(":0.0");
+    } else {
+        display = SDL_strdup(disp_env);
+    }
+
+    host = display;
+    disp_num = SDL_strrchr(display, ':');
+    screen_num = SDL_strrchr(display, '.');
+
+    if (!disp_num) {
+        SDL_free(display);
+        return NULL;
+    }
+
+    *disp_num = 0;
+    disp_num++;
+
+    if (screen_num) {
+        *screen_num = 0;
+    }
+
+    if (!*host) {
+        const char *session = SDL_getenv("XDG_SESSION_TYPE");
+        if (session && SDL_strcmp(session, "wayland") == 0) {
+            host = "unix-wayland";
+        } else {
+            host = "unix";
+        }
+    }
+
+    SDL_memset(config_dir, 0, sizeof(config_dir));
+
+    conf_env = SDL_getenv("XDG_CONFIG_HOME");
+    if (conf_env && *conf_env) {
+        SDL_strlcpy(config_dir, conf_env, sizeof(config_dir));
+    } else {
+        const char *home_env = SDL_getenv("HOME");
+        if (!home_env || !*home_env) {
+            SDL_free(display);
+            return NULL;
+        }
+        (void)SDL_snprintf(config_dir, sizeof(config_dir), "%s/.config", home_env);
+    }
+
+    key = SDL_DBus_GetLocalMachineId();
+
+    if (!key) {
+        SDL_free(display);
+        return NULL;
+    }
+
+    SDL_memset(file_path, 0, sizeof(file_path));
+    (void)SDL_snprintf(file_path, sizeof(file_path), "%s/ibus/bus/%s-%s-%s",
+                       config_dir, key, host, disp_num);
+    dbus->free(key);
+    SDL_free(display);
+
+    return SDL_strdup(file_path);
+}
+
+static bool IBus_CheckConnection(SDL_DBusContext *dbus);
+
+static void SDLCALL IBus_SetCapabilities(void *data, const char *name, const char *old_val,
+                                         const char *hint)
+{
+    SDL_DBusContext *dbus = SDL_DBus_GetContext();
+
+    if (IBus_CheckConnection(dbus)) {
+        Uint32 caps = IBUS_CAP_FOCUS;
+
+        if (hint && SDL_strstr(hint, "composition")) {
+            caps |= IBUS_CAP_PREEDIT_TEXT;
+        }
+        if (hint && SDL_strstr(hint, "candidates")) {
+            // FIXME, turn off native candidate rendering
+        }
+
+        SDL_DBus_CallVoidMethodOnConnection(ibus_conn, ibus_service, input_ctx_path, ibus_input_interface, "SetCapabilities",
+                                            DBUS_TYPE_UINT32, &caps, DBUS_TYPE_INVALID);
+    }
+}
+
+static bool IBus_SetupConnection(SDL_DBusContext *dbus, const char *addr)
+{
+    const char *client_name = "SDL3_Application";
+    const char *path = NULL;
+    bool result = false;
+    DBusObjectPathVTable ibus_vtable;
+
+    SDL_zero(ibus_vtable);
+    ibus_vtable.message_function = &IBus_MessageHandler;
+
+    /* try the portal interface first. Modern systems have this in general,
+       and sandbox things like FlakPak and Snaps, etc, require it. */
+
+    ibus_is_portal_interface = true;
+    ibus_service = IBUS_PORTAL_SERVICE;
+    ibus_interface = IBUS_PORTAL_INTERFACE;
+    ibus_input_interface = IBUS_PORTAL_INPUT_INTERFACE;
+    ibus_conn = dbus->session_conn;
+
+    result = SDL_DBus_CallMethodOnConnection(ibus_conn, ibus_service, IBUS_PATH, ibus_interface, "CreateInputContext",
+                                             DBUS_TYPE_STRING, &client_name, DBUS_TYPE_INVALID,
+                                             DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
+    if (!result) {
+        ibus_is_portal_interface = false;
+        ibus_service = IBUS_SERVICE;
+        ibus_interface = IBUS_INTERFACE;
+        ibus_input_interface = IBUS_INPUT_INTERFACE;
+        ibus_conn = dbus->connection_open_private(addr, NULL);
+
+        if (!ibus_conn) {
+            return false; // oh well.
+        }
+
+        dbus->connection_flush(ibus_conn);
+
+        if (!dbus->bus_register(ibus_conn, NULL)) {
+            ibus_conn = NULL;
+            return false;
+        }
+
+        dbus->connection_flush(ibus_conn);
+
+        result = SDL_DBus_CallMethodOnConnection(ibus_conn, ibus_service, IBUS_PATH, ibus_interface, "CreateInputContext",
+                                                 DBUS_TYPE_STRING, &client_name, DBUS_TYPE_INVALID,
+                                                 DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
+    } else {
+        // re-using dbus->session_conn
+        dbus->connection_ref(ibus_conn);
+    }
+
+    if (result) {
+        char matchstr[128];
+        (void)SDL_snprintf(matchstr, sizeof(matchstr), "type='signal',interface='%s'", ibus_input_interface);
+        SDL_free(input_ctx_path);
+        input_ctx_path = SDL_strdup(path);
+        SDL_AddHintCallback(SDL_HINT_IME_IMPLEMENTED_UI, IBus_SetCapabilities, NULL);
+        dbus->bus_add_match(ibus_conn, matchstr, NULL);
+        dbus->connection_try_register_object_path(ibus_conn, input_ctx_path, &ibus_vtable, dbus, NULL);
+        dbus->connection_flush(ibus_conn);
+    }
+
+    return result;
+}
+
+static bool IBus_CheckConnection(SDL_DBusContext *dbus)
+{
+    if (!dbus) {
+        return false;
+    }
+
+    if (ibus_conn && dbus->connection_get_is_connected(ibus_conn)) {
+        return true;
+    }
+
+    if (inotify_fd > 0 && inotify_wd > 0) {
+        char buf[1024];
+        ssize_t readsize = read(inotify_fd, buf, sizeof(buf));
+        if (readsize > 0) {
+
+            char *p;
+            bool file_updated = false;
+
+            for (p = buf; p < buf + readsize; /**/) {
+                struct inotify_event *event = (struct inotify_event *)p;
+                if (event->len > 0) {
+                    char *addr_file_no_path = SDL_strrchr(ibus_addr_file, '/');
+                    if (!addr_file_no_path) {
+                        return false;
+                    }
+
+                    if (SDL_strcmp(addr_file_no_path + 1, event->name) == 0) {
+                        file_updated = true;
+                        break;
+                    }
+                }
+
+                p += sizeof(struct inotify_event) + event->len;
+            }
+
+            if (file_updated) {
+                char *addr = IBus_ReadAddressFromFile(ibus_addr_file);
+                if (addr) {
+                    bool result = IBus_SetupConnection(dbus, addr);
+                    SDL_free(addr);
+                    return result;
+                }
+            }
+        }
+    }
+
+    return false;
+}
+
+bool SDL_IBus_Init(void)
+{
+    bool result = false;
+    SDL_DBusContext *dbus = SDL_DBus_GetContext();
+
+    if (dbus) {
+        char *addr_file = IBus_GetDBusAddressFilename();
+        char *addr;
+        char *addr_file_dir;
+
+        if (!addr_file) {
+            return false;
+        }
+
+        addr = IBus_ReadAddressFromFile(addr_file);
+        if (!addr) {
+            SDL_free(addr_file);
+            return false;
+        }
+
+        if (ibus_addr_file) {
+            SDL_free(ibus_addr_file);
+        }
+        ibus_addr_file = SDL_strdup(addr_file);
+
+        if (inotify_fd < 0) {
+            inotify_fd = inotify_init();
+            fcntl(inotify_fd, F_SETFL, O_NONBLOCK);
+        }
+
+        addr_file_dir = SDL_strrchr(addr_file, '/');
+        if (addr_file_dir) {
+            *addr_file_dir = 0;
+        }
+
+        inotify_wd = inotify_add_watch(inotify_fd, addr_file, IN_CREATE | IN_MODIFY);
+        SDL_free(addr_file);
+
+        result = IBus_SetupConnection(dbus, addr);
+        SDL_free(addr);
+
+        // don't use the addr_file if using the portal interface.
+        if (result && ibus_is_portal_interface) {
+            if (inotify_fd > 0) {
+                if (inotify_wd > 0) {
+                    inotify_rm_watch(inotify_fd, inotify_wd);
+                    inotify_wd = -1;
+                }
+                close(inotify_fd);
+                inotify_fd = -1;
+            }
+        }
+    }
+
+    return result;
+}
+
+void SDL_IBus_Quit(void)
+{
+    SDL_DBusContext *dbus;
+
+    if (input_ctx_path) {
+        SDL_free(input_ctx_path);
+        input_ctx_path = NULL;
+    }
+
+    if (ibus_addr_file) {
+        SDL_free(ibus_addr_file);
+        ibus_addr_file = NULL;
+    }
+
+    dbus = SDL_DBus_GetContext();
+
+    // if using portal, ibus_conn == session_conn; don't release it here.
+    if (dbus && ibus_conn && !ibus_is_portal_interface) {
+        dbus->connection_close(ibus_conn);
+        dbus->connection_unref(ibus_conn);
+    }
+
+    ibus_conn = NULL;
+    ibus_service = NULL;
+    ibus_interface = NULL;
+    ibus_input_interface = NULL;
+    ibus_is_portal_interface = false;
+
+    if (inotify_fd > 0 && inotify_wd > 0) {
+        inotify_rm_watch(inotify_fd, inotify_wd);
+        inotify_wd = -1;
+    }
+
+    // !!! FIXME: should we close(inotify_fd) here?
+
+    SDL_RemoveHintCallback(SDL_HINT_IME_IMPLEMENTED_UI, IBus_SetCapabilities, NULL);
+
+    SDL_memset(&ibus_cursor_rect, 0, sizeof(ibus_cursor_rect));
+}
+
+static void IBus_SimpleMessage(const char *method)
+{
+    SDL_DBusContext *dbus = SDL_DBus_GetContext();
+
+    if ((input_ctx_path) && (IBus_CheckConnection(dbus))) {
+        SDL_DBus_CallVoidMethodOnConnection(ibus_conn, ibus_service, input_ctx_path, ibus_input_interface, method, DBUS_TYPE_INVALID);
+    }
+}
+
+void SDL_IBus_SetFocus(bool focused)
+{
+    const char *method = focused ? "FocusIn" : "FocusOut";
+    IBus_SimpleMessage(method);
+}
+
+void SDL_IBus_Reset(void)
+{
+    IBus_SimpleMessage("Reset");
+}
+
+bool SDL_IBus_ProcessKeyEvent(Uint32 keysym, Uint32 keycode, bool down)
+{
+    Uint32 result = 0;
+    SDL_DBusContext *dbus = SDL_DBus_GetContext();
+
+    if (IBus_CheckConnection(dbus)) {
+        Uint32 mods = IBus_ModState();
+        Uint32 ibus_keycode = keycode - 8;
+        if (!down) {
+            mods |= (1 << 30); // IBUS_RELEASE_MASK
+        }
+        if (!SDL_DBus_CallMethodOnConnection(ibus_conn, ibus_service, input_ctx_path, ibus_input_interface, "ProcessKeyEvent",
+                                             DBUS_TYPE_UINT32, &keysym, DBUS_TYPE_UINT32, &ibus_keycode, DBUS_TYPE_UINT32, &mods, DBUS_TYPE_INVALID,
+                                             DBUS_TYPE_BOOLEAN, &result, DBUS_TYPE_INVALID)) {
+            result = 0;
+        }
+    }
+
+    //SDL_IBus_UpdateTextInputArea(SDL_GetKeyboardFocus());
+
+    return (result != 0);
+}
+
+void SDL_IBus_PumpEvents(void)
+{
+    SDL_DBusContext *dbus = SDL_DBus_GetContext();
+
+    if (IBus_CheckConnection(dbus)) {
+        dbus->connection_read_write(ibus_conn, 0);
+
+        while (dbus->connection_dispatch(ibus_conn) == DBUS_DISPATCH_DATA_REMAINS) {
+            // Do nothing, actual work happens in IBus_MessageHandler
+        }
+    }
+}
+
+#endif // SDL_USE_LIBDBUS
+
+#endif

+ 55 - 0
thirdparty/sdl/core/linux/SDL_ibus.h

@@ -0,0 +1,55 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "SDL_internal.h"
+
+#ifndef SDL_ibus_h_
+#define SDL_ibus_h_
+
+#ifdef HAVE_IBUS_IBUS_H
+#define SDL_USE_IBUS 1
+#include <ibus.h>
+
+extern bool SDL_IBus_Init(void);
+extern void SDL_IBus_Quit(void);
+
+// Lets the IBus server know about changes in window focus
+extern void SDL_IBus_SetFocus(bool focused);
+
+// Closes the candidate list and resets any text currently being edited
+extern void SDL_IBus_Reset(void);
+
+/* Sends a keypress event to IBus, returns true if IBus used this event to
+   update its candidate list or change input methods. PumpEvents should be
+   called some time after this, to receive the TextInput / TextEditing event back. */
+extern bool SDL_IBus_ProcessKeyEvent(Uint32 keysym, Uint32 keycode, bool down);
+
+/* Update the position of IBus' candidate list. If rect is NULL then this will
+   just reposition it relative to the focused window's new position. */
+extern void SDL_IBus_UpdateTextInputArea(SDL_Window *window);
+
+/* Checks DBus for new IBus events, and calls SDL_SendKeyboardText /
+   SDL_SendEditingText for each event it finds */
+extern void SDL_IBus_PumpEvents(void);
+
+#endif // HAVE_IBUS_IBUS_H
+
+#endif // SDL_ibus_h_

+ 150 - 0
thirdparty/sdl/core/linux/SDL_ime.c

@@ -0,0 +1,150 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#include "SDL_ime.h"
+#include "SDL_ibus.h"
+#include "SDL_fcitx.h"
+
+typedef bool (*SDL_IME_Init_t)(void);
+typedef void (*SDL_IME_Quit_t)(void);
+typedef void (*SDL_IME_SetFocus_t)(bool);
+typedef void (*SDL_IME_Reset_t)(void);
+typedef bool (*SDL_IME_ProcessKeyEvent_t)(Uint32, Uint32, bool down);
+typedef void (*SDL_IME_UpdateTextInputArea_t)(SDL_Window *window);
+typedef void (*SDL_IME_PumpEvents_t)(void);
+
+static SDL_IME_Init_t SDL_IME_Init_Real = NULL;
+static SDL_IME_Quit_t SDL_IME_Quit_Real = NULL;
+static SDL_IME_SetFocus_t SDL_IME_SetFocus_Real = NULL;
+static SDL_IME_Reset_t SDL_IME_Reset_Real = NULL;
+static SDL_IME_ProcessKeyEvent_t SDL_IME_ProcessKeyEvent_Real = NULL;
+static SDL_IME_UpdateTextInputArea_t SDL_IME_UpdateTextInputArea_Real = NULL;
+static SDL_IME_PumpEvents_t SDL_IME_PumpEvents_Real = NULL;
+
+static void InitIME(void)
+{
+    static bool inited = false;
+#ifdef HAVE_FCITX
+    const char *im_module = SDL_getenv("SDL_IM_MODULE");
+    const char *xmodifiers = SDL_getenv("XMODIFIERS");
+#endif
+
+    if (inited == true) {
+        return;
+    }
+
+    inited = true;
+
+    // See if fcitx IME support is being requested
+#ifdef HAVE_FCITX
+    if (!SDL_IME_Init_Real &&
+        ((im_module && SDL_strcmp(im_module, "fcitx") == 0) ||
+         (!im_module && xmodifiers && SDL_strstr(xmodifiers, "@im=fcitx") != NULL))) {
+        SDL_IME_Init_Real = SDL_Fcitx_Init;
+        SDL_IME_Quit_Real = SDL_Fcitx_Quit;
+        SDL_IME_SetFocus_Real = SDL_Fcitx_SetFocus;
+        SDL_IME_Reset_Real = SDL_Fcitx_Reset;
+        SDL_IME_ProcessKeyEvent_Real = SDL_Fcitx_ProcessKeyEvent;
+        SDL_IME_UpdateTextInputArea_Real = SDL_Fcitx_UpdateTextInputArea;
+        SDL_IME_PumpEvents_Real = SDL_Fcitx_PumpEvents;
+    }
+#endif // HAVE_FCITX
+
+    // default to IBus
+#ifdef HAVE_IBUS_IBUS_H
+    if (!SDL_IME_Init_Real) {
+        SDL_IME_Init_Real = SDL_IBus_Init;
+        SDL_IME_Quit_Real = SDL_IBus_Quit;
+        SDL_IME_SetFocus_Real = SDL_IBus_SetFocus;
+        SDL_IME_Reset_Real = SDL_IBus_Reset;
+        SDL_IME_ProcessKeyEvent_Real = SDL_IBus_ProcessKeyEvent;
+        SDL_IME_UpdateTextInputArea_Real = SDL_IBus_UpdateTextInputArea;
+        SDL_IME_PumpEvents_Real = SDL_IBus_PumpEvents;
+    }
+#endif // HAVE_IBUS_IBUS_H
+}
+
+bool SDL_IME_Init(void)
+{
+    InitIME();
+
+    if (SDL_IME_Init_Real) {
+        if (SDL_IME_Init_Real()) {
+            return true;
+        }
+
+        // uhoh, the IME implementation's init failed! Disable IME support.
+        SDL_IME_Init_Real = NULL;
+        SDL_IME_Quit_Real = NULL;
+        SDL_IME_SetFocus_Real = NULL;
+        SDL_IME_Reset_Real = NULL;
+        SDL_IME_ProcessKeyEvent_Real = NULL;
+        SDL_IME_UpdateTextInputArea_Real = NULL;
+        SDL_IME_PumpEvents_Real = NULL;
+    }
+
+    return false;
+}
+
+void SDL_IME_Quit(void)
+{
+    if (SDL_IME_Quit_Real) {
+        SDL_IME_Quit_Real();
+    }
+}
+
+void SDL_IME_SetFocus(bool focused)
+{
+    if (SDL_IME_SetFocus_Real) {
+        SDL_IME_SetFocus_Real(focused);
+    }
+}
+
+void SDL_IME_Reset(void)
+{
+    if (SDL_IME_Reset_Real) {
+        SDL_IME_Reset_Real();
+    }
+}
+
+bool SDL_IME_ProcessKeyEvent(Uint32 keysym, Uint32 keycode, bool down)
+{
+    if (SDL_IME_ProcessKeyEvent_Real) {
+        return SDL_IME_ProcessKeyEvent_Real(keysym, keycode, down);
+    }
+
+    return false;
+}
+
+void SDL_IME_UpdateTextInputArea(SDL_Window *window)
+{
+    if (SDL_IME_UpdateTextInputArea_Real) {
+        SDL_IME_UpdateTextInputArea_Real(window);
+    }
+}
+
+void SDL_IME_PumpEvents(void)
+{
+    if (SDL_IME_PumpEvents_Real) {
+        SDL_IME_PumpEvents_Real();
+    }
+}

+ 35 - 0
thirdparty/sdl/core/linux/SDL_ime.h

@@ -0,0 +1,35 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#ifndef SDL_ime_h_
+#define SDL_ime_h_
+
+#include "SDL_internal.h"
+
+extern bool SDL_IME_Init(void);
+extern void SDL_IME_Quit(void);
+extern void SDL_IME_SetFocus(bool focused);
+extern void SDL_IME_Reset(void);
+extern bool SDL_IME_ProcessKeyEvent(Uint32 keysym, Uint32 keycode, bool down);
+extern void SDL_IME_UpdateTextInputArea(SDL_Window *window);
+extern void SDL_IME_PumpEvents(void);
+
+#endif // SDL_ime_h_

+ 156 - 0
thirdparty/sdl/core/linux/SDL_system_theme.c

@@ -0,0 +1,156 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#include "SDL_dbus.h"
+#include "SDL_system_theme.h"
+//#include "../../video/SDL_sysvideo.h"
+
+#include <unistd.h>
+
+#define PORTAL_DESTINATION "org.freedesktop.portal.Desktop"
+#define PORTAL_PATH "/org/freedesktop/portal/desktop"
+#define PORTAL_INTERFACE "org.freedesktop.portal.Settings"
+#define PORTAL_METHOD "Read"
+
+#define SIGNAL_INTERFACE "org.freedesktop.portal.Settings"
+#define SIGNAL_NAMESPACE "org.freedesktop.appearance"
+#define SIGNAL_NAME "SettingChanged"
+#define SIGNAL_KEY "color-scheme"
+
+typedef struct SystemThemeData
+{
+    SDL_DBusContext *dbus;
+    SDL_SystemTheme theme;
+} SystemThemeData;
+
+static SystemThemeData system_theme_data;
+
+static bool DBus_ExtractThemeVariant(DBusMessageIter *iter, SDL_SystemTheme *theme) {
+    SDL_DBusContext *dbus = system_theme_data.dbus;
+    Uint32 color_scheme;
+    DBusMessageIter variant_iter;
+
+    if (dbus->message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT)
+        return false;
+    dbus->message_iter_recurse(iter, &variant_iter);
+    if (dbus->message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_UINT32)
+        return false;
+    dbus->message_iter_get_basic(&variant_iter, &color_scheme);
+    switch (color_scheme) {
+        case 0:
+            *theme = SDL_SYSTEM_THEME_UNKNOWN;
+            break;
+        case 1:
+            *theme = SDL_SYSTEM_THEME_DARK;
+            break;
+        case 2:
+            *theme = SDL_SYSTEM_THEME_LIGHT;
+            break;
+    }
+    return true;
+}
+
+static DBusHandlerResult DBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *data) {
+    SDL_DBusContext *dbus = (SDL_DBusContext *)data;
+
+    if (dbus->message_is_signal(msg, SIGNAL_INTERFACE, SIGNAL_NAME)) {
+        DBusMessageIter signal_iter;
+        const char *namespace, *key;
+
+        dbus->message_iter_init(msg, &signal_iter);
+        // Check if the parameters are what we expect
+        if (dbus->message_iter_get_arg_type(&signal_iter) != DBUS_TYPE_STRING)
+            goto not_our_signal;
+        dbus->message_iter_get_basic(&signal_iter, &namespace);
+        if (SDL_strcmp(SIGNAL_NAMESPACE, namespace) != 0)
+            goto not_our_signal;
+
+        if (!dbus->message_iter_next(&signal_iter))
+            goto not_our_signal;
+
+        if (dbus->message_iter_get_arg_type(&signal_iter) != DBUS_TYPE_STRING)
+            goto not_our_signal;
+        dbus->message_iter_get_basic(&signal_iter, &key);
+        if (SDL_strcmp(SIGNAL_KEY, key) != 0)
+            goto not_our_signal;
+
+        if (!dbus->message_iter_next(&signal_iter))
+            goto not_our_signal;
+
+        if (!DBus_ExtractThemeVariant(&signal_iter, &system_theme_data.theme))
+            goto not_our_signal;
+
+        //SDL_SetSystemTheme(system_theme_data.theme);
+        return DBUS_HANDLER_RESULT_HANDLED;
+    }
+not_our_signal:
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+bool SDL_SystemTheme_Init(void)
+{
+    SDL_DBusContext *dbus = SDL_DBus_GetContext();
+    DBusMessage *msg;
+    static const char *namespace = SIGNAL_NAMESPACE;
+    static const char *key = SIGNAL_KEY;
+
+    system_theme_data.theme = SDL_SYSTEM_THEME_UNKNOWN;
+    system_theme_data.dbus = dbus;
+    if (!dbus) {
+        return false;
+    }
+
+    msg = dbus->message_new_method_call(PORTAL_DESTINATION, PORTAL_PATH, PORTAL_INTERFACE, PORTAL_METHOD);
+    if (msg) {
+        if (dbus->message_append_args(msg, DBUS_TYPE_STRING, &namespace, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID)) {
+            DBusMessage *reply = dbus->connection_send_with_reply_and_block(dbus->session_conn, msg, 300, NULL);
+            if (reply) {
+                DBusMessageIter reply_iter, variant_outer_iter;
+
+                dbus->message_iter_init(reply, &reply_iter);
+                // The response has signature <<u>>
+                if (dbus->message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_VARIANT)
+                    goto incorrect_type;
+                dbus->message_iter_recurse(&reply_iter, &variant_outer_iter);
+                if (!DBus_ExtractThemeVariant(&variant_outer_iter, &system_theme_data.theme))
+                    goto incorrect_type;
+incorrect_type:
+                dbus->message_unref(reply);
+            }
+        }
+        dbus->message_unref(msg);
+    }
+
+    dbus->bus_add_match(dbus->session_conn,
+                        "type='signal', interface='"SIGNAL_INTERFACE"',"
+                        "member='"SIGNAL_NAME"', arg0='"SIGNAL_NAMESPACE"',"
+                        "arg1='"SIGNAL_KEY"'", NULL);
+    dbus->connection_add_filter(dbus->session_conn,
+                                &DBus_MessageFilter, dbus, NULL);
+    dbus->connection_flush(dbus->session_conn);
+    return true;
+}
+
+SDL_SystemTheme SDL_SystemTheme_Get(void)
+{
+    return system_theme_data.theme;
+}

+ 30 - 0
thirdparty/sdl/core/linux/SDL_system_theme.h

@@ -0,0 +1,30 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#ifndef SDL_system_theme_h_
+#define SDL_system_theme_h_
+
+#include "SDL_internal.h"
+
+extern bool SDL_SystemTheme_Init(void);
+extern SDL_SystemTheme SDL_SystemTheme_Get(void);
+
+#endif // SDL_system_theme_h_

+ 345 - 0
thirdparty/sdl/core/linux/SDL_threadprio.c

@@ -0,0 +1,345 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#ifdef SDL_PLATFORM_LINUX
+
+#ifndef SDL_THREADS_DISABLED
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <pthread.h>
+#include <sched.h>
+#include <unistd.h>
+
+// RLIMIT_RTTIME requires kernel >= 2.6.25 and is in glibc >= 2.14
+#ifndef RLIMIT_RTTIME
+#define RLIMIT_RTTIME 15
+#endif
+// SCHED_RESET_ON_FORK is in kernel >= 2.6.32.
+#ifndef SCHED_RESET_ON_FORK
+#define SCHED_RESET_ON_FORK 0x40000000
+#endif
+
+#include "SDL_dbus.h"
+
+#ifdef SDL_USE_LIBDBUS
+
+// d-bus queries to org.freedesktop.RealtimeKit1.
+#define RTKIT_DBUS_NODE      "org.freedesktop.RealtimeKit1"
+#define RTKIT_DBUS_PATH      "/org/freedesktop/RealtimeKit1"
+#define RTKIT_DBUS_INTERFACE "org.freedesktop.RealtimeKit1"
+
+// d-bus queries to the XDG portal interface to RealtimeKit1
+#define XDG_PORTAL_DBUS_NODE      "org.freedesktop.portal.Desktop"
+#define XDG_PORTAL_DBUS_PATH      "/org/freedesktop/portal/desktop"
+#define XDG_PORTAL_DBUS_INTERFACE "org.freedesktop.portal.Realtime"
+
+static bool rtkit_use_session_conn;
+static const char *rtkit_dbus_node;
+static const char *rtkit_dbus_path;
+static const char *rtkit_dbus_interface;
+
+static pthread_once_t rtkit_initialize_once = PTHREAD_ONCE_INIT;
+static Sint32 rtkit_min_nice_level = -20;
+static Sint32 rtkit_max_realtime_priority = 99;
+static Sint64 rtkit_max_rttime_usec = 200000;
+
+/*
+ * Checking that the RTTimeUSecMax property exists and is an int64 confirms that:
+ *  - The desktop portal exists and supports the realtime interface.
+ *  - The realtime interface is new enough to have the required bug fixes applied.
+ */
+static bool realtime_portal_supported(DBusConnection *conn)
+{
+    Sint64 res;
+    return SDL_DBus_QueryPropertyOnConnection(conn, XDG_PORTAL_DBUS_NODE, XDG_PORTAL_DBUS_PATH, XDG_PORTAL_DBUS_INTERFACE,
+                                              "RTTimeUSecMax", DBUS_TYPE_INT64, &res);
+}
+
+static void set_rtkit_interface(void)
+{
+    SDL_DBusContext *dbus = SDL_DBus_GetContext();
+
+    // xdg-desktop-portal works in all instances, so check for it first.
+    if (dbus && realtime_portal_supported(dbus->session_conn)) {
+        rtkit_use_session_conn = true;
+        rtkit_dbus_node = XDG_PORTAL_DBUS_NODE;
+        rtkit_dbus_path = XDG_PORTAL_DBUS_PATH;
+        rtkit_dbus_interface = XDG_PORTAL_DBUS_INTERFACE;
+    } else { // Fall back to the standard rtkit interface in all other cases.
+        rtkit_use_session_conn = false;
+        rtkit_dbus_node = RTKIT_DBUS_NODE;
+        rtkit_dbus_path = RTKIT_DBUS_PATH;
+        rtkit_dbus_interface = RTKIT_DBUS_INTERFACE;
+    }
+}
+
+static DBusConnection *get_rtkit_dbus_connection(void)
+{
+    SDL_DBusContext *dbus = SDL_DBus_GetContext();
+
+    if (dbus) {
+        return rtkit_use_session_conn ? dbus->session_conn : dbus->system_conn;
+    }
+
+    return NULL;
+}
+
+static void rtkit_initialize(void)
+{
+    DBusConnection *dbus_conn;
+
+    set_rtkit_interface();
+    dbus_conn = get_rtkit_dbus_connection();
+
+    // Try getting minimum nice level: this is often greater than PRIO_MIN (-20).
+    if (!dbus_conn || !SDL_DBus_QueryPropertyOnConnection(dbus_conn, rtkit_dbus_node, rtkit_dbus_path, rtkit_dbus_interface, "MinNiceLevel",
+                                                                 DBUS_TYPE_INT32, &rtkit_min_nice_level)) {
+        rtkit_min_nice_level = -20;
+    }
+
+    // Try getting maximum realtime priority: this can be less than the POSIX default (99).
+    if (!dbus_conn || !SDL_DBus_QueryPropertyOnConnection(dbus_conn, rtkit_dbus_node, rtkit_dbus_path, rtkit_dbus_interface, "MaxRealtimePriority",
+                                                                 DBUS_TYPE_INT32, &rtkit_max_realtime_priority)) {
+        rtkit_max_realtime_priority = 99;
+    }
+
+    // Try getting maximum rttime allowed by rtkit: exceeding this value will result in SIGKILL
+    if (!dbus_conn || !SDL_DBus_QueryPropertyOnConnection(dbus_conn, rtkit_dbus_node, rtkit_dbus_path, rtkit_dbus_interface, "RTTimeUSecMax",
+                                                                 DBUS_TYPE_INT64, &rtkit_max_rttime_usec)) {
+        rtkit_max_rttime_usec = 200000;
+    }
+}
+
+static bool rtkit_initialize_realtime_thread(void)
+{
+    // Following is an excerpt from rtkit README that outlines the requirements
+    // a thread must meet before making rtkit requests:
+    //
+    //   * Only clients with RLIMIT_RTTIME set will get RT scheduling
+    //
+    //   * RT scheduling will only be handed out to processes with
+    //     SCHED_RESET_ON_FORK set to guarantee that the scheduling
+    //     settings cannot 'leak' to child processes, thus making sure
+    //     that 'RT fork bombs' cannot be used to bypass RLIMIT_RTTIME
+    //     and take the system down.
+    //
+    //   * Limits are enforced on all user controllable resources, only
+    //     a maximum number of users, processes, threads can request RT
+    //     scheduling at the same time.
+    //
+    //   * Only a limited number of threads may be made RT in a
+    //     specific time frame.
+    //
+    //   * Client authorization is verified with PolicyKit
+
+    int err;
+    struct rlimit rlimit;
+    int nLimit = RLIMIT_RTTIME;
+    pid_t nPid = 0; // self
+    int nSchedPolicy = sched_getscheduler(nPid) | SCHED_RESET_ON_FORK;
+    struct sched_param schedParam;
+
+    SDL_zero(schedParam);
+
+    // Requirement #1: Set RLIMIT_RTTIME
+    err = getrlimit(nLimit, &rlimit);
+    if (err) {
+        return false;
+    }
+
+    // Current rtkit allows a max of 200ms right now
+    rlimit.rlim_max = rtkit_max_rttime_usec;
+    rlimit.rlim_cur = rlimit.rlim_max / 2;
+    err = setrlimit(nLimit, &rlimit);
+    if (err) {
+        return false;
+    }
+
+    // Requirement #2: Add SCHED_RESET_ON_FORK to the scheduler policy
+    err = sched_getparam(nPid, &schedParam);
+    if (err) {
+        return false;
+    }
+
+    err = sched_setscheduler(nPid, nSchedPolicy, &schedParam);
+    if (err) {
+        return false;
+    }
+
+    return true;
+}
+
+static bool rtkit_setpriority_nice(pid_t thread, int nice_level)
+{
+    DBusConnection *dbus_conn;
+    Uint64 pid = (Uint64)getpid();
+    Uint64 tid = (Uint64)thread;
+    Sint32 nice = (Sint32)nice_level;
+
+    pthread_once(&rtkit_initialize_once, rtkit_initialize);
+    dbus_conn = get_rtkit_dbus_connection();
+
+    if (nice < rtkit_min_nice_level) {
+        nice = rtkit_min_nice_level;
+    }
+
+    if (!dbus_conn || !SDL_DBus_CallMethodOnConnection(dbus_conn,
+                                                              rtkit_dbus_node, rtkit_dbus_path, rtkit_dbus_interface, "MakeThreadHighPriorityWithPID",
+                                                              DBUS_TYPE_UINT64, &pid, DBUS_TYPE_UINT64, &tid, DBUS_TYPE_INT32, &nice, DBUS_TYPE_INVALID,
+                                                              DBUS_TYPE_INVALID)) {
+        return false;
+    }
+    return true;
+}
+
+static bool rtkit_setpriority_realtime(pid_t thread, int rt_priority)
+{
+    DBusConnection *dbus_conn;
+    Uint64 pid = (Uint64)getpid();
+    Uint64 tid = (Uint64)thread;
+    Uint32 priority = (Uint32)rt_priority;
+
+    pthread_once(&rtkit_initialize_once, rtkit_initialize);
+    dbus_conn = get_rtkit_dbus_connection();
+
+    if (priority > rtkit_max_realtime_priority) {
+        priority = rtkit_max_realtime_priority;
+    }
+
+    // We always perform the thread state changes necessary for rtkit.
+    // This wastes some system calls if the state is already set but
+    // typically code sets a thread priority and leaves it so it's
+    // not expected that this wasted effort will be an issue.
+    // We also do not quit if this fails, we let the rtkit request
+    // go through to determine whether it really needs to fail or not.
+    rtkit_initialize_realtime_thread();
+
+    if (!dbus_conn || !SDL_DBus_CallMethodOnConnection(dbus_conn,
+                                                              rtkit_dbus_node, rtkit_dbus_path, rtkit_dbus_interface, "MakeThreadRealtimeWithPID",
+                                                              DBUS_TYPE_UINT64, &pid, DBUS_TYPE_UINT64, &tid, DBUS_TYPE_UINT32, &priority, DBUS_TYPE_INVALID,
+                                                              DBUS_TYPE_INVALID)) {
+        return false;
+    }
+    return true;
+}
+#else
+
+#define rtkit_max_realtime_priority 99
+
+#endif // dbus
+#endif // threads
+
+// this is a public symbol, so it has to exist even if threads are disabled.
+bool SDL_SetLinuxThreadPriority(Sint64 threadID, int priority)
+{
+#ifdef SDL_THREADS_DISABLED
+    return SDL_Unsupported();
+#else
+    if (setpriority(PRIO_PROCESS, (id_t)threadID, priority) == 0) {
+        return true;
+    }
+
+#ifdef SDL_USE_LIBDBUS
+    /* Note that this fails you most likely:
+         * Have your process's scheduler incorrectly configured.
+           See the requirements at:
+           http://git.0pointer.net/rtkit.git/tree/README#n16
+         * Encountered dbus/polkit security restrictions. Note
+           that the RealtimeKit1 dbus endpoint is inaccessible
+           over ssh connections for most common distro configs.
+           You might want to check your local config for details:
+           /usr/share/polkit-1/actions/org.freedesktop.RealtimeKit1.policy
+
+       README and sample code at: http://git.0pointer.net/rtkit.git
+    */
+    if (rtkit_setpriority_nice((pid_t)threadID, priority)) {
+        return true;
+    }
+#endif
+
+    return SDL_SetError("setpriority() failed");
+#endif
+}
+
+// this is a public symbol, so it has to exist even if threads are disabled.
+bool SDL_SetLinuxThreadPriorityAndPolicy(Sint64 threadID, int sdlPriority, int schedPolicy)
+{
+#ifdef SDL_THREADS_DISABLED
+    return SDL_Unsupported();
+#else
+    int osPriority;
+
+    if (schedPolicy == SCHED_RR || schedPolicy == SCHED_FIFO) {
+        if (sdlPriority == SDL_THREAD_PRIORITY_LOW) {
+            osPriority = 1;
+        } else if (sdlPriority == SDL_THREAD_PRIORITY_HIGH) {
+            osPriority = rtkit_max_realtime_priority * 3 / 4;
+        } else if (sdlPriority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
+            osPriority = rtkit_max_realtime_priority;
+        } else {
+            osPriority = rtkit_max_realtime_priority / 2;
+        }
+    } else {
+        if (sdlPriority == SDL_THREAD_PRIORITY_LOW) {
+            osPriority = 19;
+        } else if (sdlPriority == SDL_THREAD_PRIORITY_HIGH) {
+            osPriority = -10;
+        } else if (sdlPriority == SDL_THREAD_PRIORITY_TIME_CRITICAL) {
+            osPriority = -20;
+        } else {
+            osPriority = 0;
+        }
+
+        if (setpriority(PRIO_PROCESS, (id_t)threadID, osPriority) == 0) {
+            return true;
+        }
+    }
+
+#ifdef SDL_USE_LIBDBUS
+    /* Note that this fails you most likely:
+     * Have your process's scheduler incorrectly configured.
+       See the requirements at:
+       http://git.0pointer.net/rtkit.git/tree/README#n16
+     * Encountered dbus/polkit security restrictions. Note
+       that the RealtimeKit1 dbus endpoint is inaccessible
+       over ssh connections for most common distro configs.
+       You might want to check your local config for details:
+       /usr/share/polkit-1/actions/org.freedesktop.RealtimeKit1.policy
+
+       README and sample code at: http://git.0pointer.net/rtkit.git
+    */
+    if (schedPolicy == SCHED_RR || schedPolicy == SCHED_FIFO) {
+        if (rtkit_setpriority_realtime((pid_t)threadID, osPriority)) {
+            return true;
+        }
+    } else {
+        if (rtkit_setpriority_nice((pid_t)threadID, osPriority)) {
+            return true;
+        }
+    }
+#endif
+
+    return SDL_SetError("setpriority() failed");
+#endif
+}
+
+#endif // SDL_PLATFORM_LINUX

+ 596 - 0
thirdparty/sdl/core/linux/SDL_udev.c

@@ -0,0 +1,596 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+/*
+ * To list the properties of a device, try something like:
+ * udevadm info -a -n snd/hwC0D0 (for a sound card)
+ * udevadm info --query=all -n input/event3 (for a keyboard, mouse, etc)
+ * udevadm info --query=property -n input/event2
+ */
+#include "SDL_udev.h"
+
+#ifdef SDL_USE_LIBUDEV
+
+#include <linux/input.h>
+#include <sys/stat.h>
+
+#include "SDL_evdev_capabilities.h"
+#include "../unix/SDL_poll.h"
+
+static const char *SDL_UDEV_LIBS[] = { "libudev.so.1", "libudev.so.0" };
+
+static SDL_UDEV_PrivateData *_this = NULL;
+
+static bool SDL_UDEV_load_sym(const char *fn, void **addr);
+static bool SDL_UDEV_load_syms(void);
+static bool SDL_UDEV_hotplug_update_available(void);
+static void get_caps(struct udev_device *dev, struct udev_device *pdev, const char *attr, unsigned long *bitmask, size_t bitmask_len);
+static int guess_device_class(struct udev_device *dev);
+static int device_class(struct udev_device *dev);
+static void device_event(SDL_UDEV_deviceevent type, struct udev_device *dev);
+
+static bool SDL_UDEV_load_sym(const char *fn, void **addr)
+{
+    *addr = SDL_LoadFunction(_this->udev_handle, fn);
+    if (!*addr) {
+        // Don't call SDL_SetError(): SDL_LoadFunction already did.
+        return false;
+    }
+
+    return true;
+}
+
+static bool SDL_UDEV_load_syms(void)
+{
+/* cast funcs to char* first, to please GCC's strict aliasing rules. */
+#define SDL_UDEV_SYM(x)                                          \
+    if (!SDL_UDEV_load_sym(#x, (void **)(char *)&_this->syms.x)) \
+        return false
+
+    SDL_UDEV_SYM(udev_device_get_action);
+    SDL_UDEV_SYM(udev_device_get_devnode);
+    SDL_UDEV_SYM(udev_device_get_syspath);
+    SDL_UDEV_SYM(udev_device_get_subsystem);
+    SDL_UDEV_SYM(udev_device_get_parent_with_subsystem_devtype);
+    SDL_UDEV_SYM(udev_device_get_property_value);
+    SDL_UDEV_SYM(udev_device_get_sysattr_value);
+    SDL_UDEV_SYM(udev_device_new_from_syspath);
+    SDL_UDEV_SYM(udev_device_unref);
+    SDL_UDEV_SYM(udev_enumerate_add_match_property);
+    SDL_UDEV_SYM(udev_enumerate_add_match_subsystem);
+    SDL_UDEV_SYM(udev_enumerate_get_list_entry);
+    SDL_UDEV_SYM(udev_enumerate_new);
+    SDL_UDEV_SYM(udev_enumerate_scan_devices);
+    SDL_UDEV_SYM(udev_enumerate_unref);
+    SDL_UDEV_SYM(udev_list_entry_get_name);
+    SDL_UDEV_SYM(udev_list_entry_get_next);
+    SDL_UDEV_SYM(udev_monitor_enable_receiving);
+    SDL_UDEV_SYM(udev_monitor_filter_add_match_subsystem_devtype);
+    SDL_UDEV_SYM(udev_monitor_get_fd);
+    SDL_UDEV_SYM(udev_monitor_new_from_netlink);
+    SDL_UDEV_SYM(udev_monitor_receive_device);
+    SDL_UDEV_SYM(udev_monitor_unref);
+    SDL_UDEV_SYM(udev_new);
+    SDL_UDEV_SYM(udev_unref);
+    SDL_UDEV_SYM(udev_device_new_from_devnum);
+    SDL_UDEV_SYM(udev_device_get_devnum);
+#undef SDL_UDEV_SYM
+
+    return true;
+}
+
+static bool SDL_UDEV_hotplug_update_available(void)
+{
+    if (_this->udev_mon) {
+        const int fd = _this->syms.udev_monitor_get_fd(_this->udev_mon);
+        if (SDL_IOReady(fd, SDL_IOR_READ, 0)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool SDL_UDEV_Init(void)
+{
+    if (!_this) {
+        _this = (SDL_UDEV_PrivateData *)SDL_calloc(1, sizeof(*_this));
+        if (!_this) {
+            return false;
+        }
+
+        if (!SDL_UDEV_LoadLibrary()) {
+            SDL_UDEV_Quit();
+            return false;
+        }
+
+        /* Set up udev monitoring
+         * Listen for input devices (mouse, keyboard, joystick, etc) and sound devices
+         */
+
+        _this->udev = _this->syms.udev_new();
+        if (!_this->udev) {
+            SDL_UDEV_Quit();
+            return SDL_SetError("udev_new() failed");
+        }
+
+        _this->udev_mon = _this->syms.udev_monitor_new_from_netlink(_this->udev, "udev");
+        if (!_this->udev_mon) {
+            SDL_UDEV_Quit();
+            return SDL_SetError("udev_monitor_new_from_netlink() failed");
+        }
+
+        _this->syms.udev_monitor_filter_add_match_subsystem_devtype(_this->udev_mon, "input", NULL);
+        _this->syms.udev_monitor_filter_add_match_subsystem_devtype(_this->udev_mon, "sound", NULL);
+        _this->syms.udev_monitor_filter_add_match_subsystem_devtype(_this->udev_mon, "video4linux", NULL);
+        _this->syms.udev_monitor_enable_receiving(_this->udev_mon);
+
+        // Do an initial scan of existing devices
+        SDL_UDEV_Scan();
+    }
+
+    _this->ref_count += 1;
+
+    return true;
+}
+
+void SDL_UDEV_Quit(void)
+{
+    if (!_this) {
+        return;
+    }
+
+    _this->ref_count -= 1;
+
+    if (_this->ref_count < 1) {
+
+        if (_this->udev_mon) {
+            _this->syms.udev_monitor_unref(_this->udev_mon);
+            _this->udev_mon = NULL;
+        }
+        if (_this->udev) {
+            _this->syms.udev_unref(_this->udev);
+            _this->udev = NULL;
+        }
+
+        // Remove existing devices
+        while (_this->first) {
+            SDL_UDEV_CallbackList *item = _this->first;
+            _this->first = _this->first->next;
+            SDL_free(item);
+        }
+
+        SDL_UDEV_UnloadLibrary();
+        SDL_free(_this);
+        _this = NULL;
+    }
+}
+
+bool SDL_UDEV_Scan(void)
+{
+    struct udev_enumerate *enumerate = NULL;
+    struct udev_list_entry *devs = NULL;
+    struct udev_list_entry *item = NULL;
+
+    if (!_this) {
+        return true;
+    }
+
+    enumerate = _this->syms.udev_enumerate_new(_this->udev);
+    if (!enumerate) {
+        SDL_UDEV_Quit();
+        return SDL_SetError("udev_enumerate_new() failed");
+    }
+
+    _this->syms.udev_enumerate_add_match_subsystem(enumerate, "input");
+    _this->syms.udev_enumerate_add_match_subsystem(enumerate, "sound");
+    _this->syms.udev_enumerate_add_match_subsystem(enumerate, "video4linux");
+
+    _this->syms.udev_enumerate_scan_devices(enumerate);
+    devs = _this->syms.udev_enumerate_get_list_entry(enumerate);
+    for (item = devs; item; item = _this->syms.udev_list_entry_get_next(item)) {
+        const char *path = _this->syms.udev_list_entry_get_name(item);
+        struct udev_device *dev = _this->syms.udev_device_new_from_syspath(_this->udev, path);
+        if (dev) {
+            device_event(SDL_UDEV_DEVICEADDED, dev);
+            _this->syms.udev_device_unref(dev);
+        }
+    }
+
+    _this->syms.udev_enumerate_unref(enumerate);
+    return true;
+}
+
+bool SDL_UDEV_GetProductInfo(const char *device_path, Uint16 *vendor, Uint16 *product, Uint16 *version, int *class)
+{
+    struct stat statbuf;
+    char type;
+    struct udev_device *dev;
+    const char* val;
+    int class_temp;
+
+    if (!_this) {
+        return false;
+    }
+
+    if (stat(device_path, &statbuf) == -1) {
+        return false;
+    }
+
+    if (S_ISBLK(statbuf.st_mode)) {
+        type = 'b';
+    }
+    else if (S_ISCHR(statbuf.st_mode)) {
+        type = 'c';
+    }
+    else {
+        return false;
+    }
+
+    dev = _this->syms.udev_device_new_from_devnum(_this->udev, type, statbuf.st_rdev);
+
+    if (!dev) {
+        return false;
+    }
+
+    val = _this->syms.udev_device_get_property_value(dev, "ID_VENDOR_ID");
+    if (val) {
+        *vendor = (Uint16)SDL_strtol(val, NULL, 16);
+    }
+
+    val = _this->syms.udev_device_get_property_value(dev, "ID_MODEL_ID");
+    if (val) {
+        *product = (Uint16)SDL_strtol(val, NULL, 16);
+    }
+
+    val = _this->syms.udev_device_get_property_value(dev, "ID_REVISION");
+    if (val) {
+        *version = (Uint16)SDL_strtol(val, NULL, 16);
+    }
+
+    class_temp = device_class(dev);
+    if (class_temp) {
+        *class = class_temp;
+    }
+
+    _this->syms.udev_device_unref(dev);
+
+    return true;
+}
+
+void SDL_UDEV_UnloadLibrary(void)
+{
+    if (!_this) {
+        return;
+    }
+
+    if (_this->udev_handle) {
+        SDL_UnloadObject(_this->udev_handle);
+        _this->udev_handle = NULL;
+    }
+}
+
+bool SDL_UDEV_LoadLibrary(void)
+{
+    bool result = true;
+
+    if (!_this) {
+        return SDL_SetError("UDEV not initialized");
+    }
+
+    // See if there is a udev library already loaded
+    if (SDL_UDEV_load_syms()) {
+        return true;
+    }
+
+#ifdef SDL_UDEV_DYNAMIC
+    // Check for the build environment's libudev first
+    if (!_this->udev_handle) {
+        _this->udev_handle = SDL_LoadObject(SDL_UDEV_DYNAMIC);
+        if (_this->udev_handle) {
+            result = SDL_UDEV_load_syms();
+            if (!result) {
+                SDL_UDEV_UnloadLibrary();
+            }
+        }
+    }
+#endif
+
+    if (!_this->udev_handle) {
+        for (int i = 0; i < SDL_arraysize(SDL_UDEV_LIBS); i++) {
+            _this->udev_handle = SDL_LoadObject(SDL_UDEV_LIBS[i]);
+            if (_this->udev_handle) {
+                result = SDL_UDEV_load_syms();
+                if (!result) {
+                    SDL_UDEV_UnloadLibrary();
+                } else {
+                    break;
+                }
+            }
+        }
+
+        if (!_this->udev_handle) {
+            result = false;
+            // Don't call SDL_SetError(): SDL_LoadObject already did.
+        }
+    }
+
+    return result;
+}
+
+static void get_caps(struct udev_device *dev, struct udev_device *pdev, const char *attr, unsigned long *bitmask, size_t bitmask_len)
+{
+    const char *value;
+    char text[4096];
+    char *word;
+    int i;
+    unsigned long v;
+
+    SDL_memset(bitmask, 0, bitmask_len * sizeof(*bitmask));
+    value = _this->syms.udev_device_get_sysattr_value(pdev, attr);
+    if (!value) {
+        return;
+    }
+
+    SDL_strlcpy(text, value, sizeof(text));
+    i = 0;
+    while ((word = SDL_strrchr(text, ' ')) != NULL) {
+        v = SDL_strtoul(word + 1, NULL, 16);
+        if (i < bitmask_len) {
+            bitmask[i] = v;
+        }
+        ++i;
+        *word = '\0';
+    }
+    v = SDL_strtoul(text, NULL, 16);
+    if (i < bitmask_len) {
+        bitmask[i] = v;
+    }
+}
+
+static int guess_device_class(struct udev_device *dev)
+{
+    struct udev_device *pdev;
+    unsigned long bitmask_props[NBITS(INPUT_PROP_MAX)];
+    unsigned long bitmask_ev[NBITS(EV_MAX)];
+    unsigned long bitmask_abs[NBITS(ABS_MAX)];
+    unsigned long bitmask_key[NBITS(KEY_MAX)];
+    unsigned long bitmask_rel[NBITS(REL_MAX)];
+
+    /* walk up the parental chain until we find the real input device; the
+     * argument is very likely a subdevice of this, like eventN */
+    pdev = dev;
+    while (pdev && !_this->syms.udev_device_get_sysattr_value(pdev, "capabilities/ev")) {
+        pdev = _this->syms.udev_device_get_parent_with_subsystem_devtype(pdev, "input", NULL);
+    }
+    if (!pdev) {
+        return 0;
+    }
+
+    get_caps(dev, pdev, "properties", bitmask_props, SDL_arraysize(bitmask_props));
+    get_caps(dev, pdev, "capabilities/ev", bitmask_ev, SDL_arraysize(bitmask_ev));
+    get_caps(dev, pdev, "capabilities/abs", bitmask_abs, SDL_arraysize(bitmask_abs));
+    get_caps(dev, pdev, "capabilities/rel", bitmask_rel, SDL_arraysize(bitmask_rel));
+    get_caps(dev, pdev, "capabilities/key", bitmask_key, SDL_arraysize(bitmask_key));
+
+    return SDL_EVDEV_GuessDeviceClass(&bitmask_props[0],
+                                      &bitmask_ev[0],
+                                      &bitmask_abs[0],
+                                      &bitmask_key[0],
+                                      &bitmask_rel[0]);
+}
+
+static int device_class(struct udev_device *dev)
+{
+    const char *subsystem;
+    const char *val = NULL;
+    int devclass = 0;
+
+    subsystem = _this->syms.udev_device_get_subsystem(dev);
+    if (!subsystem) {
+        return 0;
+    }
+
+    if (SDL_strcmp(subsystem, "sound") == 0) {
+        devclass = SDL_UDEV_DEVICE_SOUND;
+    } else if (SDL_strcmp(subsystem, "video4linux") == 0) {
+        val = _this->syms.udev_device_get_property_value(dev, "ID_V4L_CAPABILITIES");
+        if (val && SDL_strcasestr(val, "capture")) {
+            devclass = SDL_UDEV_DEVICE_VIDEO_CAPTURE;
+        }
+    } else if (SDL_strcmp(subsystem, "input") == 0) {
+        // udev rules reference: http://cgit.freedesktop.org/systemd/systemd/tree/src/udev/udev-builtin-input_id.c
+
+        val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK");
+        if (val && SDL_strcmp(val, "1") == 0) {
+            devclass |= SDL_UDEV_DEVICE_JOYSTICK;
+        }
+
+        val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_ACCELEROMETER");
+        if (val && SDL_strcmp(val, "1") == 0) {
+            devclass |= SDL_UDEV_DEVICE_ACCELEROMETER;
+        }
+
+        val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_MOUSE");
+        if (val && SDL_strcmp(val, "1") == 0) {
+            devclass |= SDL_UDEV_DEVICE_MOUSE;
+        }
+
+        val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_TOUCHSCREEN");
+        if (val && SDL_strcmp(val, "1") == 0) {
+            devclass |= SDL_UDEV_DEVICE_TOUCHSCREEN;
+        }
+
+        /* The undocumented rule is:
+           - All devices with keys get ID_INPUT_KEY
+           - From this subset, if they have ESC, numbers, and Q to D, it also gets ID_INPUT_KEYBOARD
+
+           Ref: http://cgit.freedesktop.org/systemd/systemd/tree/src/udev/udev-builtin-input_id.c#n183
+        */
+        val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_KEY");
+        if (val && SDL_strcmp(val, "1") == 0) {
+            devclass |= SDL_UDEV_DEVICE_HAS_KEYS;
+        }
+
+        val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD");
+        if (val && SDL_strcmp(val, "1") == 0) {
+            devclass |= SDL_UDEV_DEVICE_KEYBOARD;
+        }
+
+        if (devclass == 0) {
+            // Fall back to old style input classes
+            val = _this->syms.udev_device_get_property_value(dev, "ID_CLASS");
+            if (val) {
+                if (SDL_strcmp(val, "joystick") == 0) {
+                    devclass = SDL_UDEV_DEVICE_JOYSTICK;
+                } else if (SDL_strcmp(val, "mouse") == 0) {
+                    devclass = SDL_UDEV_DEVICE_MOUSE;
+                } else if (SDL_strcmp(val, "kbd") == 0) {
+                    devclass = SDL_UDEV_DEVICE_HAS_KEYS | SDL_UDEV_DEVICE_KEYBOARD;
+                }
+            } else {
+                // We could be linked with libudev on a system that doesn't have udev running
+                devclass = guess_device_class(dev);
+            }
+        }
+    }
+
+    return devclass;
+}
+
+static void device_event(SDL_UDEV_deviceevent type, struct udev_device *dev)
+{
+    int devclass = 0;
+    const char *path;
+    SDL_UDEV_CallbackList *item;
+
+    path = _this->syms.udev_device_get_devnode(dev);
+    if (!path) {
+        return;
+    }
+
+    if (type == SDL_UDEV_DEVICEADDED) {
+        devclass = device_class(dev);
+        if (!devclass) {
+            return;
+        }
+    } else {
+        // The device has been removed, the class isn't available
+    }
+
+    // Process callbacks
+    for (item = _this->first; item; item = item->next) {
+        item->callback(type, devclass, path);
+    }
+}
+
+void SDL_UDEV_Poll(void)
+{
+    struct udev_device *dev = NULL;
+    const char *action = NULL;
+
+    if (!_this) {
+        return;
+    }
+
+    while (SDL_UDEV_hotplug_update_available()) {
+        dev = _this->syms.udev_monitor_receive_device(_this->udev_mon);
+        if (!dev) {
+            break;
+        }
+        action = _this->syms.udev_device_get_action(dev);
+
+        if (action) {
+            if (SDL_strcmp(action, "add") == 0) {
+                device_event(SDL_UDEV_DEVICEADDED, dev);
+            } else if (SDL_strcmp(action, "remove") == 0) {
+                device_event(SDL_UDEV_DEVICEREMOVED, dev);
+            }
+        }
+
+        _this->syms.udev_device_unref(dev);
+    }
+}
+
+bool SDL_UDEV_AddCallback(SDL_UDEV_Callback cb)
+{
+    SDL_UDEV_CallbackList *item;
+    item = (SDL_UDEV_CallbackList *)SDL_calloc(1, sizeof(SDL_UDEV_CallbackList));
+    if (!item) {
+        return false;
+    }
+
+    item->callback = cb;
+
+    if (!_this->last) {
+        _this->first = _this->last = item;
+    } else {
+        _this->last->next = item;
+        _this->last = item;
+    }
+
+    return true;
+}
+
+void SDL_UDEV_DelCallback(SDL_UDEV_Callback cb)
+{
+    SDL_UDEV_CallbackList *item;
+    SDL_UDEV_CallbackList *prev = NULL;
+
+    if (!_this) {
+        return;
+    }
+
+    for (item = _this->first; item; item = item->next) {
+        // found it, remove it.
+        if (item->callback == cb) {
+            if (prev) {
+                prev->next = item->next;
+            } else {
+                SDL_assert(_this->first == item);
+                _this->first = item->next;
+            }
+            if (item == _this->last) {
+                _this->last = prev;
+            }
+            SDL_free(item);
+            return;
+        }
+        prev = item;
+    }
+}
+
+const SDL_UDEV_Symbols *SDL_UDEV_GetUdevSyms(void)
+{
+    if (!SDL_UDEV_Init()) {
+        SDL_SetError("Could not initialize UDEV");
+        return NULL;
+    }
+
+    return &_this->syms;
+}
+
+void SDL_UDEV_ReleaseUdevSyms(void)
+{
+    SDL_UDEV_Quit();
+}
+
+#endif // SDL_USE_LIBUDEV

+ 113 - 0
thirdparty/sdl/core/linux/SDL_udev.h

@@ -0,0 +1,113 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "SDL_internal.h"
+
+#ifndef SDL_udev_h_
+#define SDL_udev_h_
+
+#if defined(HAVE_LIBUDEV_H) && defined(HAVE_LINUX_INPUT_H)
+
+#ifndef SDL_USE_LIBUDEV
+#define SDL_USE_LIBUDEV 1
+#endif
+
+#include <libudev.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+/**
+ *  Device type
+ */
+
+typedef enum
+{
+    SDL_UDEV_DEVICEADDED = 1,
+    SDL_UDEV_DEVICEREMOVED
+} SDL_UDEV_deviceevent;
+
+typedef void (*SDL_UDEV_Callback)(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath);
+
+typedef struct SDL_UDEV_CallbackList
+{
+    SDL_UDEV_Callback callback;
+    struct SDL_UDEV_CallbackList *next;
+} SDL_UDEV_CallbackList;
+
+typedef struct SDL_UDEV_Symbols
+{
+    const char *(*udev_device_get_action)(struct udev_device *);
+    const char *(*udev_device_get_devnode)(struct udev_device *);
+    const char *(*udev_device_get_syspath)(struct udev_device *);
+    const char *(*udev_device_get_subsystem)(struct udev_device *);
+    struct udev_device *(*udev_device_get_parent_with_subsystem_devtype)(struct udev_device *udev_device, const char *subsystem, const char *devtype);
+    const char *(*udev_device_get_property_value)(struct udev_device *, const char *);
+    const char *(*udev_device_get_sysattr_value)(struct udev_device *udev_device, const char *sysattr);
+    struct udev_device *(*udev_device_new_from_syspath)(struct udev *, const char *);
+    void (*udev_device_unref)(struct udev_device *);
+    int (*udev_enumerate_add_match_property)(struct udev_enumerate *, const char *, const char *);
+    int (*udev_enumerate_add_match_subsystem)(struct udev_enumerate *, const char *);
+    struct udev_list_entry *(*udev_enumerate_get_list_entry)(struct udev_enumerate *);
+    struct udev_enumerate *(*udev_enumerate_new)(struct udev *);
+    int (*udev_enumerate_scan_devices)(struct udev_enumerate *);
+    void (*udev_enumerate_unref)(struct udev_enumerate *);
+    const char *(*udev_list_entry_get_name)(struct udev_list_entry *);
+    struct udev_list_entry *(*udev_list_entry_get_next)(struct udev_list_entry *);
+    int (*udev_monitor_enable_receiving)(struct udev_monitor *);
+    int (*udev_monitor_filter_add_match_subsystem_devtype)(struct udev_monitor *, const char *, const char *);
+    int (*udev_monitor_get_fd)(struct udev_monitor *);
+    struct udev_monitor *(*udev_monitor_new_from_netlink)(struct udev *, const char *);
+    struct udev_device *(*udev_monitor_receive_device)(struct udev_monitor *);
+    void (*udev_monitor_unref)(struct udev_monitor *);
+    struct udev *(*udev_new)(void);
+    void (*udev_unref)(struct udev *);
+    struct udev_device *(*udev_device_new_from_devnum)(struct udev *udev, char type, dev_t devnum);
+    dev_t (*udev_device_get_devnum)(struct udev_device *udev_device);
+} SDL_UDEV_Symbols;
+
+typedef struct SDL_UDEV_PrivateData
+{
+    const char *udev_library;
+    SDL_SharedObject *udev_handle;
+    struct udev *udev;
+    struct udev_monitor *udev_mon;
+    int ref_count;
+    SDL_UDEV_CallbackList *first, *last;
+
+    // Function pointers
+    SDL_UDEV_Symbols syms;
+} SDL_UDEV_PrivateData;
+
+extern bool SDL_UDEV_Init(void);
+extern void SDL_UDEV_Quit(void);
+extern void SDL_UDEV_UnloadLibrary(void);
+extern bool SDL_UDEV_LoadLibrary(void);
+extern void SDL_UDEV_Poll(void);
+extern bool SDL_UDEV_Scan(void);
+extern bool SDL_UDEV_GetProductInfo(const char *device_path, Uint16 *vendor, Uint16 *product, Uint16 *version, int *class);
+extern bool SDL_UDEV_AddCallback(SDL_UDEV_Callback cb);
+extern void SDL_UDEV_DelCallback(SDL_UDEV_Callback cb);
+extern const SDL_UDEV_Symbols *SDL_UDEV_GetUdevSyms(void);
+extern void SDL_UDEV_ReleaseUdevSyms(void);
+
+#endif // HAVE_LIBUDEV_H && HAVE_LINUX_INPUT_H
+
+#endif // SDL_udev_h_

+ 75 - 0
thirdparty/sdl/core/unix/SDL_appid.c

@@ -0,0 +1,75 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "SDL_internal.h"
+
+#include "SDL_appid.h"
+#include <unistd.h>
+
+const char *SDL_GetExeName(void)
+{
+    static const char *proc_name = NULL;
+
+    // TODO: Use a fallback if BSD has no mounted procfs (OpenBSD has no procfs at all)
+    if (!proc_name) {
+#if defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_FREEBSD) || defined (SDL_PLATFORM_NETBSD)
+        static char linkfile[1024];
+        int linksize;
+
+#if defined(SDL_PLATFORM_LINUX)
+        const char *proc_path = "/proc/self/exe";
+#elif defined(SDL_PLATFORM_FREEBSD)
+        const char *proc_path = "/proc/curproc/file";
+#elif defined(SDL_PLATFORM_NETBSD)
+        const char *proc_path = "/proc/curproc/exe";
+#endif
+        linksize = readlink(proc_path, linkfile, sizeof(linkfile) - 1);
+        if (linksize > 0) {
+            linkfile[linksize] = '\0';
+            proc_name = SDL_strrchr(linkfile, '/');
+            if (proc_name) {
+                ++proc_name;
+            } else {
+                proc_name = linkfile;
+            }
+        }
+#endif
+    }
+
+    return proc_name;
+}
+
+const char *SDL_GetAppID(void)
+{
+    const char *id_str = SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_IDENTIFIER_STRING);
+
+    if (!id_str) {
+        // If the hint isn't set, try to use the application's executable name
+        id_str = SDL_GetExeName();
+    }
+
+    if (!id_str) {
+        // Finally, use the default we've used forever
+        id_str = "SDL_App";
+    }
+
+    return id_str;
+}

+ 30 - 0
thirdparty/sdl/core/unix/SDL_appid.h

@@ -0,0 +1,30 @@
+/*
+Simple DirectMedia Layer
+Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+This software is provided 'as-is', without any express or implied
+warranty.  In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+   claim that you wrote the original software. If you use this software
+   in a product, an acknowledgment in the product documentation would be
+   appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+   misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "SDL_internal.h"
+
+#ifndef SDL_appid_h_
+#define SDL_appid_h_
+
+extern const char *SDL_GetExeName(void);
+extern const char *SDL_GetAppID(void);
+
+#endif // SDL_appid_h_

+ 95 - 0
thirdparty/sdl/core/unix/SDL_poll.c

@@ -0,0 +1,95 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "SDL_internal.h"
+
+#include "SDL_poll.h"
+
+#ifdef HAVE_POLL
+#include <poll.h>
+#else
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+#include <errno.h>
+
+int SDL_IOReady(int fd, int flags, Sint64 timeoutNS)
+{
+    int result;
+
+    SDL_assert(flags & (SDL_IOR_READ | SDL_IOR_WRITE));
+
+    // Note: We don't bother to account for elapsed time if we get EINTR
+    do {
+#ifdef HAVE_POLL
+        struct pollfd info;
+        int timeoutMS;
+
+        info.fd = fd;
+        info.events = 0;
+        if (flags & SDL_IOR_READ) {
+            info.events |= POLLIN | POLLPRI;
+        }
+        if (flags & SDL_IOR_WRITE) {
+            info.events |= POLLOUT;
+        }
+        // FIXME: Add support for ppoll() for nanosecond precision
+        if (timeoutNS > 0) {
+            timeoutMS = (int)SDL_NS_TO_MS(timeoutNS + (SDL_NS_PER_MS - 1));
+        } else if (timeoutNS == 0) {
+            timeoutMS = 0;
+        } else {
+            timeoutMS = -1;
+        }
+        result = poll(&info, 1, timeoutMS);
+#else
+        fd_set rfdset, *rfdp = NULL;
+        fd_set wfdset, *wfdp = NULL;
+        struct timeval tv, *tvp = NULL;
+
+        // If this assert triggers we'll corrupt memory here
+        SDL_assert(fd >= 0 && fd < FD_SETSIZE);
+
+        if (flags & SDL_IOR_READ) {
+            FD_ZERO(&rfdset);
+            FD_SET(fd, &rfdset);
+            rfdp = &rfdset;
+        }
+        if (flags & SDL_IOR_WRITE) {
+            FD_ZERO(&wfdset);
+            FD_SET(fd, &wfdset);
+            wfdp = &wfdset;
+        }
+
+        if (timeoutNS >= 0) {
+            tv.tv_sec = (timeoutNS / SDL_NS_PER_SECOND);
+            tv.tv_usec = SDL_NS_TO_US((timeoutNS % SDL_NS_PER_SECOND) + (SDL_NS_PER_US - 1));
+            tvp = &tv;
+        }
+
+        result = select(fd + 1, rfdp, wfdp, NULL, tvp);
+#endif // HAVE_POLL
+
+    } while (result < 0 && errno == EINTR && !(flags & SDL_IOR_NO_RETRY));
+
+    return result;
+}

+ 33 - 0
thirdparty/sdl/core/unix/SDL_poll.h

@@ -0,0 +1,33 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "SDL_internal.h"
+
+#ifndef SDL_poll_h_
+#define SDL_poll_h_
+
+#define SDL_IOR_READ     0x1
+#define SDL_IOR_WRITE    0x2
+#define SDL_IOR_NO_RETRY 0x4
+
+extern int SDL_IOReady(int fd, int flags, Sint64 timeoutNS);
+
+#endif // SDL_poll_h_

+ 112 - 0
thirdparty/sdl/core/windows/SDL_directx.h

@@ -0,0 +1,112 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#ifndef SDL_directx_h_
+#define SDL_directx_h_
+
+// Include all of the DirectX 8.0 headers and adds any necessary tweaks
+
+#include "SDL_windows.h"
+#include <mmsystem.h>
+#ifndef WIN32
+#define WIN32
+#endif
+#undef WINNT
+
+// Far pointers don't exist in 32-bit code
+#ifndef FAR
+#define FAR
+#endif
+
+// Error codes not yet included in Win32 API header files
+#ifndef MAKE_HRESULT
+#define MAKE_HRESULT(sev, fac, code) \
+    ((HRESULT)(((unsigned long)(sev) << 31) | ((unsigned long)(fac) << 16) | ((unsigned long)(code))))
+#endif
+
+#ifndef S_OK
+#define S_OK (HRESULT)0x00000000L
+#endif
+
+#ifndef SUCCEEDED
+#define SUCCEEDED(x) ((HRESULT)(x) >= 0)
+#endif
+#ifndef FAILED
+#define FAILED(x) ((HRESULT)(x) < 0)
+#endif
+
+#ifndef E_FAIL
+#define E_FAIL (HRESULT)0x80000008L
+#endif
+#ifndef E_NOINTERFACE
+#define E_NOINTERFACE (HRESULT)0x80004002L
+#endif
+#ifndef E_OUTOFMEMORY
+#define E_OUTOFMEMORY (HRESULT)0x8007000EL
+#endif
+#ifndef E_INVALIDARG
+#define E_INVALIDARG (HRESULT)0x80070057L
+#endif
+#ifndef E_NOTIMPL
+#define E_NOTIMPL (HRESULT)0x80004001L
+#endif
+#ifndef REGDB_E_CLASSNOTREG
+#define REGDB_E_CLASSNOTREG (HRESULT)0x80040154L
+#endif
+
+// Severity codes
+#ifndef SEVERITY_ERROR
+#define SEVERITY_ERROR 1
+#endif
+
+// Error facility codes
+#ifndef FACILITY_WIN32
+#define FACILITY_WIN32 7
+#endif
+
+#ifndef FIELD_OFFSET
+#define FIELD_OFFSET(type, field) ((LONG) & (((type *)0)->field))
+#endif
+
+/* DirectX headers (if it isn't included, I haven't tested it yet)
+ */
+// We need these defines to mark what version of DirectX API we use
+#define DIRECTDRAW_VERSION  0x0700
+#define DIRECTSOUND_VERSION 0x0800
+#define DIRECTINPUT_VERSION 0x0800 // Need version 7 for force feedback. Need version 8 so IDirectInput8_EnumDevices doesn't leak like a sieve...
+
+#ifdef HAVE_DDRAW_H
+#include <ddraw.h>
+#endif
+#ifdef HAVE_DSOUND_H
+#include <dsound.h>
+#endif
+#ifdef HAVE_DINPUT_H
+#include <dinput.h>
+#else
+typedef struct
+{
+    int unused;
+} DIDEVICEINSTANCE;
+#endif
+
+#endif // SDL_directx_h_

+ 98 - 0
thirdparty/sdl/core/windows/SDL_gameinput.c

@@ -0,0 +1,98 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#ifdef HAVE_GAMEINPUT_H
+
+#include "SDL_windows.h"
+#include "SDL_gameinput.h"
+
+#ifdef SDL_PLATFORM_WIN32
+#include <initguid.h>
+// {11BE2A7E-4254-445A-9C09-FFC40F006918}
+DEFINE_GUID(SDL_IID_GameInput, 0x11BE2A7E, 0x4254, 0x445A, 0x9C, 0x09, 0xFF, 0xC4, 0x0F, 0x00, 0x69, 0x18);
+#endif
+
+static SDL_SharedObject *g_hGameInputDLL;
+static IGameInput *g_pGameInput;
+static int g_nGameInputRefCount;
+
+bool SDL_InitGameInput(IGameInput **ppGameInput)
+{
+    if (g_nGameInputRefCount == 0) {
+        g_hGameInputDLL = SDL_LoadObject("gameinput.dll");
+        if (!g_hGameInputDLL) {
+            return false;
+        }
+
+        typedef HRESULT (WINAPI *GameInputCreate_t)(IGameInput * *gameInput);
+        GameInputCreate_t GameInputCreateFunc = (GameInputCreate_t)SDL_LoadFunction(g_hGameInputDLL, "GameInputCreate");
+        if (!GameInputCreateFunc) {
+            SDL_UnloadObject(g_hGameInputDLL);
+            return false;
+        }
+
+        IGameInput *pGameInput = NULL;
+        HRESULT hr = GameInputCreateFunc(&pGameInput);
+        if (FAILED(hr)) {
+            SDL_UnloadObject(g_hGameInputDLL);
+            return WIN_SetErrorFromHRESULT("GameInputCreate failed", hr);
+        }
+
+#ifdef SDL_PLATFORM_WIN32
+        hr = IGameInput_QueryInterface(pGameInput, &SDL_IID_GameInput, (void **)&g_pGameInput);
+        IGameInput_Release(pGameInput);
+        if (FAILED(hr)) {
+            SDL_UnloadObject(g_hGameInputDLL);
+            return WIN_SetErrorFromHRESULT("GameInput QueryInterface failed", hr);
+        }
+#else
+        // Assume that the version we get is compatible with the current SDK
+        // If that isn't the case, define the correct GUID for SDL_IID_GameInput above
+        g_pGameInput = pGameInput;
+#endif
+    }
+    ++g_nGameInputRefCount;
+
+    if (ppGameInput) {
+        *ppGameInput = g_pGameInput;
+    }
+    return true;
+}
+
+void SDL_QuitGameInput(void)
+{
+    SDL_assert(g_nGameInputRefCount > 0);
+
+    --g_nGameInputRefCount;
+    if (g_nGameInputRefCount == 0) {
+        if (g_pGameInput) {
+            IGameInput_Release(g_pGameInput);
+            g_pGameInput = NULL;
+        }
+        if (g_hGameInputDLL) {
+            SDL_UnloadObject(g_hGameInputDLL);
+            g_hGameInputDLL = NULL;
+        }
+    }
+}
+
+#endif // HAVE_GAMEINPUT_H

+ 36 - 0
thirdparty/sdl/core/windows/SDL_gameinput.h

@@ -0,0 +1,36 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#ifndef SDL_gameinput_h_
+#define SDL_gameinput_h_
+
+#ifdef HAVE_GAMEINPUT_H
+
+#define COBJMACROS
+#include <gameinput.h>
+
+extern bool SDL_InitGameInput(IGameInput **ppGameInput);
+extern void SDL_QuitGameInput(void);
+
+#endif // HAVE_GAMEINPUT_H
+
+#endif // SDL_gameinput_h_

+ 254 - 0
thirdparty/sdl/core/windows/SDL_hid.c

@@ -0,0 +1,254 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#include "SDL_hid.h"
+
+HidD_GetAttributes_t SDL_HidD_GetAttributes;
+HidD_GetString_t SDL_HidD_GetManufacturerString;
+HidD_GetString_t SDL_HidD_GetProductString;
+HidP_GetCaps_t SDL_HidP_GetCaps;
+HidP_GetButtonCaps_t SDL_HidP_GetButtonCaps;
+HidP_GetValueCaps_t SDL_HidP_GetValueCaps;
+HidP_MaxDataListLength_t SDL_HidP_MaxDataListLength;
+HidP_GetData_t SDL_HidP_GetData;
+
+static HMODULE s_pHIDDLL = 0;
+static int s_HIDDLLRefCount = 0;
+
+
+bool WIN_LoadHIDDLL(void)
+{
+    if (s_pHIDDLL) {
+        SDL_assert(s_HIDDLLRefCount > 0);
+        s_HIDDLLRefCount++;
+        return true; // already loaded
+    }
+
+    s_pHIDDLL = LoadLibrary(TEXT("hid.dll"));
+    if (!s_pHIDDLL) {
+        return false;
+    }
+
+    SDL_assert(s_HIDDLLRefCount == 0);
+    s_HIDDLLRefCount = 1;
+
+    SDL_HidD_GetAttributes = (HidD_GetAttributes_t)GetProcAddress(s_pHIDDLL, "HidD_GetAttributes");
+    SDL_HidD_GetManufacturerString = (HidD_GetString_t)GetProcAddress(s_pHIDDLL, "HidD_GetManufacturerString");
+    SDL_HidD_GetProductString = (HidD_GetString_t)GetProcAddress(s_pHIDDLL, "HidD_GetProductString");
+    SDL_HidP_GetCaps = (HidP_GetCaps_t)GetProcAddress(s_pHIDDLL, "HidP_GetCaps");
+    SDL_HidP_GetButtonCaps = (HidP_GetButtonCaps_t)GetProcAddress(s_pHIDDLL, "HidP_GetButtonCaps");
+    SDL_HidP_GetValueCaps = (HidP_GetValueCaps_t)GetProcAddress(s_pHIDDLL, "HidP_GetValueCaps");
+    SDL_HidP_MaxDataListLength = (HidP_MaxDataListLength_t)GetProcAddress(s_pHIDDLL, "HidP_MaxDataListLength");
+    SDL_HidP_GetData = (HidP_GetData_t)GetProcAddress(s_pHIDDLL, "HidP_GetData");
+    if (!SDL_HidD_GetManufacturerString || !SDL_HidD_GetProductString ||
+        !SDL_HidP_GetCaps || !SDL_HidP_GetButtonCaps ||
+        !SDL_HidP_GetValueCaps || !SDL_HidP_MaxDataListLength || !SDL_HidP_GetData) {
+        WIN_UnloadHIDDLL();
+        return false;
+    }
+
+    return true;
+}
+
+void WIN_UnloadHIDDLL(void)
+{
+    if (s_pHIDDLL) {
+        SDL_assert(s_HIDDLLRefCount > 0);
+        if (--s_HIDDLLRefCount == 0) {
+            FreeLibrary(s_pHIDDLL);
+            s_pHIDDLL = NULL;
+        }
+    } else {
+        SDL_assert(s_HIDDLLRefCount == 0);
+    }
+}
+
+#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
+
+// CM_Register_Notification definitions
+
+#define CR_SUCCESS 0
+
+DECLARE_HANDLE(HCMNOTIFICATION);
+typedef HCMNOTIFICATION *PHCMNOTIFICATION;
+
+typedef enum _CM_NOTIFY_FILTER_TYPE
+{
+    CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE = 0,
+    CM_NOTIFY_FILTER_TYPE_DEVICEHANDLE,
+    CM_NOTIFY_FILTER_TYPE_DEVICEINSTANCE,
+    CM_NOTIFY_FILTER_TYPE_MAX
+} CM_NOTIFY_FILTER_TYPE, *PCM_NOTIFY_FILTER_TYPE;
+
+typedef struct _CM_NOTIFY_FILTER
+{
+    DWORD cbSize;
+    DWORD Flags;
+    CM_NOTIFY_FILTER_TYPE FilterType;
+    DWORD Reserved;
+    union
+    {
+        struct
+        {
+            GUID ClassGuid;
+        } DeviceInterface;
+        struct
+        {
+            HANDLE hTarget;
+        } DeviceHandle;
+        struct
+        {
+            WCHAR InstanceId[200];
+        } DeviceInstance;
+    } u;
+} CM_NOTIFY_FILTER, *PCM_NOTIFY_FILTER;
+
+typedef enum _CM_NOTIFY_ACTION
+{
+    CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL = 0,
+    CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL,
+    CM_NOTIFY_ACTION_DEVICEQUERYREMOVE,
+    CM_NOTIFY_ACTION_DEVICEQUERYREMOVEFAILED,
+    CM_NOTIFY_ACTION_DEVICEREMOVEPENDING,
+    CM_NOTIFY_ACTION_DEVICEREMOVECOMPLETE,
+    CM_NOTIFY_ACTION_DEVICECUSTOMEVENT,
+    CM_NOTIFY_ACTION_DEVICEINSTANCEENUMERATED,
+    CM_NOTIFY_ACTION_DEVICEINSTANCESTARTED,
+    CM_NOTIFY_ACTION_DEVICEINSTANCEREMOVED,
+    CM_NOTIFY_ACTION_MAX
+} CM_NOTIFY_ACTION, *PCM_NOTIFY_ACTION;
+
+typedef struct _CM_NOTIFY_EVENT_DATA
+{
+    CM_NOTIFY_FILTER_TYPE FilterType;
+    DWORD Reserved;
+    union
+    {
+        struct
+        {
+            GUID ClassGuid;
+            WCHAR SymbolicLink[ANYSIZE_ARRAY];
+        } DeviceInterface;
+        struct
+        {
+            GUID EventGuid;
+            LONG NameOffset;
+            DWORD DataSize;
+            BYTE Data[ANYSIZE_ARRAY];
+        } DeviceHandle;
+        struct
+        {
+            WCHAR InstanceId[ANYSIZE_ARRAY];
+        } DeviceInstance;
+    } u;
+} CM_NOTIFY_EVENT_DATA, *PCM_NOTIFY_EVENT_DATA;
+
+typedef DWORD (CALLBACK *PCM_NOTIFY_CALLBACK)(HCMNOTIFICATION hNotify, PVOID Context, CM_NOTIFY_ACTION Action, PCM_NOTIFY_EVENT_DATA EventData, DWORD EventDataSize);
+
+typedef DWORD (WINAPI *CM_Register_NotificationFunc)(PCM_NOTIFY_FILTER pFilter, PVOID pContext, PCM_NOTIFY_CALLBACK pCallback, PHCMNOTIFICATION pNotifyContext);
+typedef DWORD (WINAPI *CM_Unregister_NotificationFunc)(HCMNOTIFICATION NotifyContext);
+
+static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2L, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
+
+static int s_DeviceNotificationsRequested;
+static HMODULE cfgmgr32_lib_handle;
+static CM_Register_NotificationFunc CM_Register_Notification;
+static CM_Unregister_NotificationFunc CM_Unregister_Notification;
+static HCMNOTIFICATION s_DeviceNotificationFuncHandle;
+static Uint64 s_LastDeviceNotification = 1;
+
+static DWORD CALLBACK SDL_DeviceNotificationFunc(HCMNOTIFICATION hNotify, PVOID context, CM_NOTIFY_ACTION action, PCM_NOTIFY_EVENT_DATA eventData, DWORD event_data_size)
+{
+    if (action == CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL ||
+        action == CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL) {
+        s_LastDeviceNotification = SDL_GetTicksNS();
+    }
+    return ERROR_SUCCESS;
+}
+
+void WIN_InitDeviceNotification(void)
+{
+    ++s_DeviceNotificationsRequested;
+    if (s_DeviceNotificationsRequested > 1) {
+        return;
+    }
+
+    cfgmgr32_lib_handle = LoadLibraryA("cfgmgr32.dll");
+    if (cfgmgr32_lib_handle) {
+        CM_Register_Notification = (CM_Register_NotificationFunc)GetProcAddress(cfgmgr32_lib_handle, "CM_Register_Notification");
+        CM_Unregister_Notification = (CM_Unregister_NotificationFunc)GetProcAddress(cfgmgr32_lib_handle, "CM_Unregister_Notification");
+        if (CM_Register_Notification && CM_Unregister_Notification) {
+            CM_NOTIFY_FILTER notify_filter;
+
+            SDL_zero(notify_filter);
+            notify_filter.cbSize = sizeof(notify_filter);
+            notify_filter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE;
+            notify_filter.u.DeviceInterface.ClassGuid = GUID_DEVINTERFACE_HID;
+            if (CM_Register_Notification(&notify_filter, NULL, SDL_DeviceNotificationFunc, &s_DeviceNotificationFuncHandle) == CR_SUCCESS) {
+                return;
+            }
+        }
+    }
+
+    // FIXME: Should we log errors?
+}
+
+Uint64 WIN_GetLastDeviceNotification(void)
+{
+    return s_LastDeviceNotification;
+}
+
+void WIN_QuitDeviceNotification(void)
+{
+    if (--s_DeviceNotificationsRequested > 0) {
+        return;
+    }
+    // Make sure we have balanced calls to init/quit
+    SDL_assert(s_DeviceNotificationsRequested == 0);
+
+    if (cfgmgr32_lib_handle) {
+        if (s_DeviceNotificationFuncHandle && CM_Unregister_Notification) {
+            CM_Unregister_Notification(s_DeviceNotificationFuncHandle);
+            s_DeviceNotificationFuncHandle = NULL;
+        }
+
+        FreeLibrary(cfgmgr32_lib_handle);
+        cfgmgr32_lib_handle = NULL;
+    }
+}
+
+#else
+
+void WIN_InitDeviceNotification(void)
+{
+}
+
+Uint64 WIN_GetLastDeviceNotification( void )
+{
+    return 0;
+}
+
+void WIN_QuitDeviceNotification(void)
+{
+}
+
+#endif // !SDL_PLATFORM_XBOXONE && !SDL_PLATFORM_XBOXSERIES

+ 215 - 0
thirdparty/sdl/core/windows/SDL_hid.h

@@ -0,0 +1,215 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#ifndef SDL_hid_h_
+#define SDL_hid_h_
+
+#include "SDL_windows.h"
+
+typedef LONG NTSTATUS;
+typedef USHORT USAGE;
+typedef struct _HIDP_PREPARSED_DATA *PHIDP_PREPARSED_DATA;
+
+typedef struct _HIDD_ATTRIBUTES
+{
+    ULONG Size;
+    USHORT VendorID;
+    USHORT ProductID;
+    USHORT VersionNumber;
+} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
+
+typedef enum
+{
+    HidP_Input = 0,
+    HidP_Output = 1,
+    HidP_Feature = 2
+} HIDP_REPORT_TYPE;
+
+typedef struct
+{
+    USAGE UsagePage;
+    UCHAR ReportID;
+    BOOLEAN IsAlias;
+    USHORT BitField;
+    USHORT LinkCollection;
+    USAGE LinkUsage;
+    USAGE LinkUsagePage;
+    BOOLEAN IsRange;
+    BOOLEAN IsStringRange;
+    BOOLEAN IsDesignatorRange;
+    BOOLEAN IsAbsolute;
+    ULONG Reserved[10];
+    union
+    {
+        struct
+        {
+            USAGE UsageMin;
+            USAGE UsageMax;
+            USHORT StringMin;
+            USHORT StringMax;
+            USHORT DesignatorMin;
+            USHORT DesignatorMax;
+            USHORT DataIndexMin;
+            USHORT DataIndexMax;
+        } Range;
+        struct
+        {
+            USAGE Usage;
+            USAGE Reserved1;
+            USHORT StringIndex;
+            USHORT Reserved2;
+            USHORT DesignatorIndex;
+            USHORT Reserved3;
+            USHORT DataIndex;
+            USHORT Reserved4;
+        } NotRange;
+    };
+} HIDP_BUTTON_CAPS, *PHIDP_BUTTON_CAPS;
+
+typedef struct
+{
+    USAGE UsagePage;
+    UCHAR ReportID;
+    BOOLEAN IsAlias;
+    USHORT BitField;
+    USHORT LinkCollection;
+    USAGE LinkUsage;
+    USAGE LinkUsagePage;
+    BOOLEAN IsRange;
+    BOOLEAN IsStringRange;
+    BOOLEAN IsDesignatorRange;
+    BOOLEAN IsAbsolute;
+    BOOLEAN HasNull;
+    UCHAR Reserved;
+    USHORT BitSize;
+    USHORT ReportCount;
+    USHORT Reserved2[5];
+    ULONG UnitsExp;
+    ULONG Units;
+    LONG LogicalMin;
+    LONG LogicalMax;
+    LONG PhysicalMin;
+    LONG PhysicalMax;
+    union
+    {
+        struct
+        {
+            USAGE UsageMin;
+            USAGE UsageMax;
+            USHORT StringMin;
+            USHORT StringMax;
+            USHORT DesignatorMin;
+            USHORT DesignatorMax;
+            USHORT DataIndexMin;
+            USHORT DataIndexMax;
+        } Range;
+        struct
+        {
+            USAGE Usage;
+            USAGE Reserved1;
+            USHORT StringIndex;
+            USHORT Reserved2;
+            USHORT DesignatorIndex;
+            USHORT Reserved3;
+            USHORT DataIndex;
+            USHORT Reserved4;
+        } NotRange;
+    };
+} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS;
+
+typedef struct
+{
+    USAGE Usage;
+    USAGE UsagePage;
+    USHORT InputReportByteLength;
+    USHORT OutputReportByteLength;
+    USHORT FeatureReportByteLength;
+    USHORT Reserved[17];
+    USHORT NumberLinkCollectionNodes;
+    USHORT NumberInputButtonCaps;
+    USHORT NumberInputValueCaps;
+    USHORT NumberInputDataIndices;
+    USHORT NumberOutputButtonCaps;
+    USHORT NumberOutputValueCaps;
+    USHORT NumberOutputDataIndices;
+    USHORT NumberFeatureButtonCaps;
+    USHORT NumberFeatureValueCaps;
+    USHORT NumberFeatureDataIndices;
+} HIDP_CAPS, *PHIDP_CAPS;
+
+typedef struct
+{
+    USHORT DataIndex;
+    USHORT Reserved;
+    union
+    {
+        ULONG RawValue;
+        BOOLEAN On;
+    };
+} HIDP_DATA, *PHIDP_DATA;
+
+#define HIDP_ERROR_CODES(p1, p2)            ((NTSTATUS)(((p1) << 28) | (0x11 << 16) | (p2)))
+#define HIDP_STATUS_SUCCESS                 HIDP_ERROR_CODES(0x0, 0x0000)
+#define HIDP_STATUS_NULL                    HIDP_ERROR_CODES(0x8, 0x0001)
+#define HIDP_STATUS_INVALID_PREPARSED_DATA  HIDP_ERROR_CODES(0xC, 0x0001)
+#define HIDP_STATUS_INVALID_REPORT_TYPE     HIDP_ERROR_CODES(0xC, 0x0002)
+#define HIDP_STATUS_INVALID_REPORT_LENGTH   HIDP_ERROR_CODES(0xC, 0x0003)
+#define HIDP_STATUS_USAGE_NOT_FOUND         HIDP_ERROR_CODES(0xC, 0x0004)
+#define HIDP_STATUS_VALUE_OUT_OF_RANGE      HIDP_ERROR_CODES(0xC, 0x0005)
+#define HIDP_STATUS_BAD_LOG_PHY_VALUES      HIDP_ERROR_CODES(0xC, 0x0006)
+#define HIDP_STATUS_BUFFER_TOO_SMALL        HIDP_ERROR_CODES(0xC, 0x0007)
+#define HIDP_STATUS_INTERNAL_ERROR          HIDP_ERROR_CODES(0xC, 0x0008)
+#define HIDP_STATUS_I8042_TRANS_UNKNOWN     HIDP_ERROR_CODES(0xC, 0x0009)
+#define HIDP_STATUS_INCOMPATIBLE_REPORT_ID  HIDP_ERROR_CODES(0xC, 0x000A)
+#define HIDP_STATUS_NOT_VALUE_ARRAY         HIDP_ERROR_CODES(0xC, 0x000B)
+#define HIDP_STATUS_IS_VALUE_ARRAY          HIDP_ERROR_CODES(0xC, 0x000C)
+#define HIDP_STATUS_DATA_INDEX_NOT_FOUND    HIDP_ERROR_CODES(0xC, 0x000D)
+#define HIDP_STATUS_DATA_INDEX_OUT_OF_RANGE HIDP_ERROR_CODES(0xC, 0x000E)
+#define HIDP_STATUS_BUTTON_NOT_PRESSED      HIDP_ERROR_CODES(0xC, 0x000F)
+#define HIDP_STATUS_REPORT_DOES_NOT_EXIST   HIDP_ERROR_CODES(0xC, 0x0010)
+#define HIDP_STATUS_NOT_IMPLEMENTED         HIDP_ERROR_CODES(0xC, 0x0020)
+
+extern bool WIN_LoadHIDDLL(void);
+extern void WIN_UnloadHIDDLL(void);
+
+typedef BOOLEAN (WINAPI *HidD_GetAttributes_t)(HANDLE HidDeviceObject, PHIDD_ATTRIBUTES Attributes);
+typedef BOOLEAN (WINAPI *HidD_GetString_t)(HANDLE HidDeviceObject, PVOID Buffer, ULONG BufferLength);
+typedef NTSTATUS (WINAPI *HidP_GetCaps_t)(PHIDP_PREPARSED_DATA PreparsedData, PHIDP_CAPS Capabilities);
+typedef NTSTATUS (WINAPI *HidP_GetButtonCaps_t)(HIDP_REPORT_TYPE ReportType, PHIDP_BUTTON_CAPS ButtonCaps, PUSHORT ButtonCapsLength, PHIDP_PREPARSED_DATA PreparsedData);
+typedef NTSTATUS (WINAPI *HidP_GetValueCaps_t)(HIDP_REPORT_TYPE ReportType, PHIDP_VALUE_CAPS ValueCaps, PUSHORT ValueCapsLength, PHIDP_PREPARSED_DATA PreparsedData);
+typedef ULONG (WINAPI *HidP_MaxDataListLength_t)(HIDP_REPORT_TYPE ReportType, PHIDP_PREPARSED_DATA PreparsedData);
+typedef NTSTATUS (WINAPI *HidP_GetData_t)(HIDP_REPORT_TYPE ReportType, PHIDP_DATA DataList, PULONG DataLength, PHIDP_PREPARSED_DATA PreparsedData, PCHAR Report, ULONG ReportLength);
+
+extern HidD_GetAttributes_t SDL_HidD_GetAttributes;
+extern HidD_GetString_t SDL_HidD_GetManufacturerString;
+extern HidD_GetString_t SDL_HidD_GetProductString;
+extern HidP_GetCaps_t SDL_HidP_GetCaps;
+extern HidP_GetButtonCaps_t SDL_HidP_GetButtonCaps;
+extern HidP_GetValueCaps_t SDL_HidP_GetValueCaps;
+extern HidP_MaxDataListLength_t SDL_HidP_MaxDataListLength;
+extern HidP_GetData_t SDL_HidP_GetData;
+
+void WIN_InitDeviceNotification(void);
+Uint64 WIN_GetLastDeviceNotification(void);
+void WIN_QuitDeviceNotification(void);
+
+#endif // SDL_hid_h_

+ 434 - 0
thirdparty/sdl/core/windows/SDL_immdevice.c

@@ -0,0 +1,434 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#if defined(SDL_PLATFORM_WINDOWS) && defined(HAVE_MMDEVICEAPI_H)
+
+#include "SDL_windows.h"
+#include "SDL_immdevice.h"
+#include "../../audio/SDL_sysaudio.h"
+#include <objbase.h> // For CLSIDFromString
+
+typedef struct SDL_IMMDevice_HandleData
+{
+    LPWSTR immdevice_id;
+    GUID directsound_guid;
+} SDL_IMMDevice_HandleData;
+
+static const ERole SDL_IMMDevice_role = eConsole; // !!! FIXME: should this be eMultimedia? Should be a hint?
+
+// This is global to the WASAPI target, to handle hotplug and default device lookup.
+static IMMDeviceEnumerator *enumerator = NULL;
+static SDL_IMMDevice_callbacks immcallbacks;
+
+// PropVariantInit() is an inline function/macro in PropIdl.h that calls the C runtime's memset() directly. Use ours instead, to avoid dependency.
+#ifdef PropVariantInit
+#undef PropVariantInit
+#endif
+#define PropVariantInit(p) SDL_zerop(p)
+
+// Some GUIDs we need to know without linking to libraries that aren't available before Vista.
+/* *INDENT-OFF* */ // clang-format off
+static const CLSID SDL_CLSID_MMDeviceEnumerator = { 0xbcde0395, 0xe52f, 0x467c,{ 0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e } };
+static const IID SDL_IID_IMMDeviceEnumerator = { 0xa95664d2, 0x9614, 0x4f35,{ 0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6 } };
+static const IID SDL_IID_IMMNotificationClient = { 0x7991eec9, 0x7e89, 0x4d85,{ 0x83, 0x90, 0x6c, 0x70, 0x3c, 0xec, 0x60, 0xc0 } };
+static const IID SDL_IID_IMMEndpoint = { 0x1be09788, 0x6894, 0x4089,{ 0x85, 0x86, 0x9a, 0x2a, 0x6c, 0x26, 0x5a, 0xc5 } };
+static const PROPERTYKEY SDL_PKEY_Device_FriendlyName = { { 0xa45c254e, 0xdf1c, 0x4efd,{ 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, } }, 14 };
+static const PROPERTYKEY SDL_PKEY_AudioEngine_DeviceFormat = { { 0xf19f064d, 0x82c, 0x4e27,{ 0xbc, 0x73, 0x68, 0x82, 0xa1, 0xbb, 0x8e, 0x4c, } }, 0 };
+static const PROPERTYKEY SDL_PKEY_AudioEndpoint_GUID = { { 0x1da5d803, 0xd492, 0x4edd,{ 0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e, } }, 4 };
+/* *INDENT-ON* */ // clang-format on
+
+static bool FindByDevIDCallback(SDL_AudioDevice *device, void *userdata)
+{
+    LPCWSTR devid = (LPCWSTR)userdata;
+    if (devid && device && device->handle) {
+        const SDL_IMMDevice_HandleData *handle = (const SDL_IMMDevice_HandleData *)device->handle;
+        if (handle->immdevice_id && SDL_wcscmp(handle->immdevice_id, devid) == 0) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static SDL_AudioDevice *SDL_IMMDevice_FindByDevID(LPCWSTR devid)
+{
+    return SDL_FindPhysicalAudioDeviceByCallback(FindByDevIDCallback, (void *) devid);
+}
+
+LPGUID SDL_IMMDevice_GetDirectSoundGUID(SDL_AudioDevice *device)
+{
+    return (device && device->handle) ? &(((SDL_IMMDevice_HandleData *) device->handle)->directsound_guid) : NULL;
+}
+
+LPCWSTR SDL_IMMDevice_GetDevID(SDL_AudioDevice *device)
+{
+    return (device && device->handle) ? ((const SDL_IMMDevice_HandleData *) device->handle)->immdevice_id : NULL;
+}
+
+static void GetMMDeviceInfo(IMMDevice *device, char **utf8dev, WAVEFORMATEXTENSIBLE *fmt, GUID *guid)
+{
+    /* PKEY_Device_FriendlyName gives you "Speakers (SoundBlaster Pro)" which drives me nuts. I'd rather it be
+       "SoundBlaster Pro (Speakers)" but I guess that's developers vs users. Windows uses the FriendlyName in
+       its own UIs, like Volume Control, etc. */
+    IPropertyStore *props = NULL;
+    *utf8dev = NULL;
+    SDL_zerop(fmt);
+    if (SUCCEEDED(IMMDevice_OpenPropertyStore(device, STGM_READ, &props))) {
+        PROPVARIANT var;
+        PropVariantInit(&var);
+        if (SUCCEEDED(IPropertyStore_GetValue(props, &SDL_PKEY_Device_FriendlyName, &var))) {
+            *utf8dev = WIN_StringToUTF8W(var.pwszVal);
+        }
+        PropVariantClear(&var);
+        if (SUCCEEDED(IPropertyStore_GetValue(props, &SDL_PKEY_AudioEngine_DeviceFormat, &var))) {
+            SDL_memcpy(fmt, var.blob.pBlobData, SDL_min(var.blob.cbSize, sizeof(WAVEFORMATEXTENSIBLE)));
+        }
+        PropVariantClear(&var);
+        if (SUCCEEDED(IPropertyStore_GetValue(props, &SDL_PKEY_AudioEndpoint_GUID, &var))) {
+            (void)CLSIDFromString(var.pwszVal, guid);
+        }
+        PropVariantClear(&var);
+        IPropertyStore_Release(props);
+    }
+}
+
+void SDL_IMMDevice_FreeDeviceHandle(SDL_AudioDevice *device)
+{
+    if (device && device->handle) {
+        SDL_IMMDevice_HandleData *handle = (SDL_IMMDevice_HandleData *) device->handle;
+        SDL_free(handle->immdevice_id);
+        SDL_free(handle);
+        device->handle = NULL;
+    }
+}
+
+static SDL_AudioDevice *SDL_IMMDevice_Add(const bool recording, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid, GUID *dsoundguid)
+{
+    /* You can have multiple endpoints on a device that are mutually exclusive ("Speakers" vs "Line Out" or whatever).
+       In a perfect world, things that are unplugged won't be in this collection. The only gotcha is probably for
+       phones and tablets, where you might have an internal speaker and a headphone jack and expect both to be
+       available and switch automatically. (!!! FIXME...?) */
+
+    if (!devname) {
+        return NULL;
+    }
+
+    // see if we already have this one first.
+    SDL_AudioDevice *device = SDL_IMMDevice_FindByDevID(devid);
+    if (device) {
+        if (SDL_GetAtomicInt(&device->zombie)) {
+            // whoa, it came back! This can happen if you unplug and replug USB headphones while we're still keeping the SDL object alive.
+            // Kill this device's IMMDevice id; the device will go away when the app closes it, or maybe a new default device is chosen
+            // (possibly this reconnected device), so we just want to make sure IMMDevice doesn't try to find the old device by the existing ID string.
+            SDL_IMMDevice_HandleData *handle = (SDL_IMMDevice_HandleData *) device->handle;
+            SDL_free(handle->immdevice_id);
+            handle->immdevice_id = NULL;
+            device = NULL;  // add a new device, below.
+        }
+    }
+
+    if (!device) {
+        // handle is freed by SDL_IMMDevice_FreeDeviceHandle!
+        SDL_IMMDevice_HandleData *handle = (SDL_IMMDevice_HandleData *)SDL_malloc(sizeof(SDL_IMMDevice_HandleData));
+        if (!handle) {
+            return NULL;
+        }
+        handle->immdevice_id = SDL_wcsdup(devid);
+        if (!handle->immdevice_id) {
+            SDL_free(handle);
+            return NULL;
+        }
+        SDL_memcpy(&handle->directsound_guid, dsoundguid, sizeof(GUID));
+
+        SDL_AudioSpec spec;
+        SDL_zero(spec);
+        spec.channels = (Uint8)fmt->Format.nChannels;
+        spec.freq = fmt->Format.nSamplesPerSec;
+        spec.format = SDL_WaveFormatExToSDLFormat((WAVEFORMATEX *)fmt);
+
+        device = SDL_AddAudioDevice(recording, devname, &spec, handle);
+        if (!device) {
+            SDL_free(handle->immdevice_id);
+            SDL_free(handle);
+        }
+    }
+
+    return device;
+}
+
+/* We need a COM subclass of IMMNotificationClient for hotplug support, which is
+   easy in C++, but we have to tapdance more to make work in C.
+   Thanks to this page for coaching on how to make this work:
+     https://www.codeproject.com/Articles/13601/COM-in-plain-C */
+
+typedef struct SDLMMNotificationClient
+{
+    const IMMNotificationClientVtbl *lpVtbl;
+    SDL_AtomicInt refcount;
+} SDLMMNotificationClient;
+
+static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_QueryInterface(IMMNotificationClient *client, REFIID iid, void **ppv)
+{
+    if ((WIN_IsEqualIID(iid, &IID_IUnknown)) || (WIN_IsEqualIID(iid, &SDL_IID_IMMNotificationClient))) {
+        *ppv = client;
+        client->lpVtbl->AddRef(client);
+        return S_OK;
+    }
+
+    *ppv = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_AddRef(IMMNotificationClient *iclient)
+{
+    SDLMMNotificationClient *client = (SDLMMNotificationClient *)iclient;
+    return (ULONG)(SDL_AtomicIncRef(&client->refcount) + 1);
+}
+
+static ULONG STDMETHODCALLTYPE SDLMMNotificationClient_Release(IMMNotificationClient *iclient)
+{
+    // client is a static object; we don't ever free it.
+    SDLMMNotificationClient *client = (SDLMMNotificationClient *)iclient;
+    const ULONG rc = SDL_AtomicDecRef(&client->refcount);
+    if (rc == 0) {
+        SDL_SetAtomicInt(&client->refcount, 0); // uhh...
+        return 0;
+    }
+    return rc - 1;
+}
+
+// These are the entry points called when WASAPI device endpoints change.
+static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDefaultDeviceChanged(IMMNotificationClient *iclient, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId)
+{
+    if (role == SDL_IMMDevice_role) {
+        immcallbacks.default_audio_device_changed(SDL_IMMDevice_FindByDevID(pwstrDeviceId));
+    }
+    return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceAdded(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId)
+{
+    /* we ignore this; devices added here then progress to ACTIVE, if appropriate, in
+       OnDeviceStateChange, making that a better place to deal with device adds. More
+       importantly: the first time you plug in a USB audio device, this callback will
+       fire, but when you unplug it, it isn't removed (it's state changes to NOTPRESENT).
+       Plugging it back in won't fire this callback again. */
+    return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceRemoved(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId)
+{
+    return S_OK;  // See notes in OnDeviceAdded handler about why we ignore this.
+}
+
+static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnDeviceStateChanged(IMMNotificationClient *iclient, LPCWSTR pwstrDeviceId, DWORD dwNewState)
+{
+    IMMDevice *device = NULL;
+
+    if (SUCCEEDED(IMMDeviceEnumerator_GetDevice(enumerator, pwstrDeviceId, &device))) {
+        IMMEndpoint *endpoint = NULL;
+        if (SUCCEEDED(IMMDevice_QueryInterface(device, &SDL_IID_IMMEndpoint, (void **)&endpoint))) {
+            EDataFlow flow;
+            if (SUCCEEDED(IMMEndpoint_GetDataFlow(endpoint, &flow))) {
+                const bool recording = (flow == eCapture);
+                if (dwNewState == DEVICE_STATE_ACTIVE) {
+                    char *utf8dev;
+                    WAVEFORMATEXTENSIBLE fmt;
+                    GUID dsoundguid;
+                    GetMMDeviceInfo(device, &utf8dev, &fmt, &dsoundguid);
+                    if (utf8dev) {
+                        SDL_IMMDevice_Add(recording, utf8dev, &fmt, pwstrDeviceId, &dsoundguid);
+                        SDL_free(utf8dev);
+                    }
+                } else {
+                    immcallbacks.audio_device_disconnected(SDL_IMMDevice_FindByDevID(pwstrDeviceId));
+                }
+            }
+            IMMEndpoint_Release(endpoint);
+        }
+        IMMDevice_Release(device);
+    }
+
+    return S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE SDLMMNotificationClient_OnPropertyValueChanged(IMMNotificationClient *client, LPCWSTR pwstrDeviceId, const PROPERTYKEY key)
+{
+    return S_OK; // we don't care about these.
+}
+
+static const IMMNotificationClientVtbl notification_client_vtbl = {
+    SDLMMNotificationClient_QueryInterface,
+    SDLMMNotificationClient_AddRef,
+    SDLMMNotificationClient_Release,
+    SDLMMNotificationClient_OnDeviceStateChanged,
+    SDLMMNotificationClient_OnDeviceAdded,
+    SDLMMNotificationClient_OnDeviceRemoved,
+    SDLMMNotificationClient_OnDefaultDeviceChanged,
+    SDLMMNotificationClient_OnPropertyValueChanged
+};
+
+static SDLMMNotificationClient notification_client = { &notification_client_vtbl, { 1 } };
+
+bool SDL_IMMDevice_Init(const SDL_IMMDevice_callbacks *callbacks)
+{
+    HRESULT ret;
+
+    // just skip the discussion with COM here.
+    if (!WIN_IsWindowsVistaOrGreater()) {
+        return SDL_SetError("IMMDevice support requires Windows Vista or later");
+    }
+
+    if (FAILED(WIN_CoInitialize())) {
+        return SDL_SetError("IMMDevice: CoInitialize() failed");
+    }
+
+    ret = CoCreateInstance(&SDL_CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &SDL_IID_IMMDeviceEnumerator, (LPVOID *)&enumerator);
+    if (FAILED(ret)) {
+        WIN_CoUninitialize();
+        return WIN_SetErrorFromHRESULT("IMMDevice CoCreateInstance(MMDeviceEnumerator)", ret);
+    }
+
+    if (callbacks) {
+        SDL_copyp(&immcallbacks, callbacks);
+    } else {
+        SDL_zero(immcallbacks);
+    }
+
+    if (!immcallbacks.audio_device_disconnected) {
+        immcallbacks.audio_device_disconnected = SDL_AudioDeviceDisconnected;
+    }
+    if (!immcallbacks.default_audio_device_changed) {
+        immcallbacks.default_audio_device_changed = SDL_DefaultAudioDeviceChanged;
+    }
+
+    return true;
+}
+
+void SDL_IMMDevice_Quit(void)
+{
+    if (enumerator) {
+        IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)&notification_client);
+        IMMDeviceEnumerator_Release(enumerator);
+        enumerator = NULL;
+    }
+
+    SDL_zero(immcallbacks);
+
+    WIN_CoUninitialize();
+}
+
+bool SDL_IMMDevice_Get(SDL_AudioDevice *device, IMMDevice **immdevice, bool recording)
+{
+    const Uint64 timeout = SDL_GetTicks() + 8000;  // intel's audio drivers can fail for up to EIGHT SECONDS after a device is connected or we wake from sleep.
+
+    SDL_assert(device != NULL);
+    SDL_assert(immdevice != NULL);
+
+    LPCWSTR devid = SDL_IMMDevice_GetDevID(device);
+    SDL_assert(devid != NULL);
+
+    HRESULT ret;
+    while ((ret = IMMDeviceEnumerator_GetDevice(enumerator, devid, immdevice)) == E_NOTFOUND) {
+        const Uint64 now = SDL_GetTicks();
+        if (timeout > now) {
+            const Uint64 ticksleft = timeout - now;
+            SDL_Delay((Uint32)SDL_min(ticksleft, 300));   // wait awhile and try again.
+            continue;
+        }
+        break;
+    }
+
+    if (!SUCCEEDED(ret)) {
+        return WIN_SetErrorFromHRESULT("WASAPI can't find requested audio endpoint", ret);
+    }
+    return true;
+}
+
+static void EnumerateEndpointsForFlow(const bool recording, SDL_AudioDevice **default_device)
+{
+    /* Note that WASAPI separates "adapter devices" from "audio endpoint devices"
+       ...one adapter device ("SoundBlaster Pro") might have multiple endpoint devices ("Speakers", "Line-Out"). */
+
+    IMMDeviceCollection *collection = NULL;
+    if (FAILED(IMMDeviceEnumerator_EnumAudioEndpoints(enumerator, recording ? eCapture : eRender, DEVICE_STATE_ACTIVE, &collection))) {
+        return;
+    }
+
+    UINT total = 0;
+    if (FAILED(IMMDeviceCollection_GetCount(collection, &total))) {
+        IMMDeviceCollection_Release(collection);
+        return;
+    }
+
+    LPWSTR default_devid = NULL;
+    if (default_device) {
+        IMMDevice *default_immdevice = NULL;
+        const EDataFlow dataflow = recording ? eCapture : eRender;
+        if (SUCCEEDED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(enumerator, dataflow, SDL_IMMDevice_role, &default_immdevice))) {
+            LPWSTR devid = NULL;
+            if (SUCCEEDED(IMMDevice_GetId(default_immdevice, &devid))) {
+                default_devid = SDL_wcsdup(devid);  // if this fails, oh well.
+                CoTaskMemFree(devid);
+            }
+            IMMDevice_Release(default_immdevice);
+        }
+    }
+
+    for (UINT i = 0; i < total; i++) {
+        IMMDevice *immdevice = NULL;
+        if (SUCCEEDED(IMMDeviceCollection_Item(collection, i, &immdevice))) {
+            LPWSTR devid = NULL;
+            if (SUCCEEDED(IMMDevice_GetId(immdevice, &devid))) {
+                char *devname = NULL;
+                WAVEFORMATEXTENSIBLE fmt;
+                GUID dsoundguid;
+                SDL_zero(fmt);
+                SDL_zero(dsoundguid);
+                GetMMDeviceInfo(immdevice, &devname, &fmt, &dsoundguid);
+                if (devname) {
+                    SDL_AudioDevice *sdldevice = SDL_IMMDevice_Add(recording, devname, &fmt, devid, &dsoundguid);
+                    if (default_device && default_devid && SDL_wcscmp(default_devid, devid) == 0) {
+                        *default_device = sdldevice;
+                    }
+                    SDL_free(devname);
+                }
+                CoTaskMemFree(devid);
+            }
+            IMMDevice_Release(immdevice);
+        }
+    }
+
+    SDL_free(default_devid);
+
+    IMMDeviceCollection_Release(collection);
+}
+
+void SDL_IMMDevice_EnumerateEndpoints(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording)
+{
+    EnumerateEndpointsForFlow(false, default_playback);
+    EnumerateEndpointsForFlow(true, default_recording);
+
+    // if this fails, we just won't get hotplug events. Carry on anyhow.
+    IMMDeviceEnumerator_RegisterEndpointNotificationCallback(enumerator, (IMMNotificationClient *)&notification_client);
+}
+
+#endif // defined(SDL_PLATFORM_WINDOWS) && defined(HAVE_MMDEVICEAPI_H)

+ 45 - 0
thirdparty/sdl/core/windows/SDL_immdevice.h

@@ -0,0 +1,45 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#ifndef SDL_IMMDEVICE_H
+#define SDL_IMMDEVICE_H
+
+#define COBJMACROS
+#include <mmdeviceapi.h>
+#include <mmreg.h>
+
+struct SDL_AudioDevice; // defined in src/audio/SDL_sysaudio.h
+
+typedef struct SDL_IMMDevice_callbacks
+{
+    void (*audio_device_disconnected)(struct SDL_AudioDevice *device);
+    void (*default_audio_device_changed)(struct SDL_AudioDevice *new_default_device);
+} SDL_IMMDevice_callbacks;
+
+bool SDL_IMMDevice_Init(const SDL_IMMDevice_callbacks *callbacks);
+void SDL_IMMDevice_Quit(void);
+bool SDL_IMMDevice_Get(struct SDL_AudioDevice *device, IMMDevice **immdevice, bool recording);
+void SDL_IMMDevice_EnumerateEndpoints(struct SDL_AudioDevice **default_playback, struct SDL_AudioDevice **default_recording);
+LPGUID SDL_IMMDevice_GetDirectSoundGUID(struct SDL_AudioDevice *device);
+LPCWSTR SDL_IMMDevice_GetDevID(struct SDL_AudioDevice *device);
+void SDL_IMMDevice_FreeDeviceHandle(struct SDL_AudioDevice *device);
+
+#endif // SDL_IMMDEVICE_H

+ 375 - 0
thirdparty/sdl/core/windows/SDL_windows.c

@@ -0,0 +1,375 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#if defined(SDL_PLATFORM_WINDOWS)
+
+#include "SDL_windows.h"
+
+#include <objbase.h> // for CoInitialize/CoUninitialize (Win32 only)
+#ifdef HAVE_ROAPI_H
+#include <roapi.h> // For RoInitialize/RoUninitialize (Win32 only)
+#else
+typedef enum RO_INIT_TYPE
+{
+    RO_INIT_SINGLETHREADED = 0,
+    RO_INIT_MULTITHREADED = 1
+} RO_INIT_TYPE;
+#endif
+
+#ifndef _WIN32_WINNT_VISTA
+#define _WIN32_WINNT_VISTA 0x0600
+#endif
+#ifndef _WIN32_WINNT_WIN7
+#define _WIN32_WINNT_WIN7 0x0601
+#endif
+#ifndef _WIN32_WINNT_WIN8
+#define _WIN32_WINNT_WIN8 0x0602
+#endif
+
+#ifndef LOAD_LIBRARY_SEARCH_SYSTEM32
+#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800
+#endif
+
+#ifndef WC_ERR_INVALID_CHARS
+#define WC_ERR_INVALID_CHARS 0x00000080
+#endif
+
+// Sets an error message based on an HRESULT
+bool WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr)
+{
+    TCHAR buffer[1024];
+    char *message;
+    TCHAR *p = buffer;
+    DWORD c = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, 0,
+                            buffer, SDL_arraysize(buffer), NULL);
+    buffer[c] = 0;
+    // kill CR/LF that FormatMessage() sticks at the end
+    while (*p) {
+        if (*p == '\r') {
+            *p = 0;
+            break;
+        }
+        ++p;
+    }
+    message = WIN_StringToUTF8(buffer);
+    SDL_SetError("%s%s%s", prefix ? prefix : "", prefix ? ": " : "", message);
+    SDL_free(message);
+    return false;
+}
+
+// Sets an error message based on GetLastError()
+bool WIN_SetError(const char *prefix)
+{
+    return WIN_SetErrorFromHRESULT(prefix, GetLastError());
+}
+
+HRESULT
+WIN_CoInitialize(void)
+{
+    /* SDL handles any threading model, so initialize with the default, which
+       is compatible with OLE and if that doesn't work, try multi-threaded mode.
+
+       If you need multi-threaded mode, call CoInitializeEx() before SDL_Init()
+    */
+#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
+    // On Xbox, there's no need to call CoInitializeEx (and it's not implemented)
+    return S_OK;
+#else
+    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+    if (hr == RPC_E_CHANGED_MODE) {
+        hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+    }
+
+    // S_FALSE means success, but someone else already initialized.
+    // You still need to call CoUninitialize in this case!
+    if (hr == S_FALSE) {
+        return S_OK;
+    }
+
+    return hr;
+#endif
+}
+
+void WIN_CoUninitialize(void)
+{
+    CoUninitialize();
+}
+
+FARPROC WIN_LoadComBaseFunction(const char *name)
+{
+    static bool s_bLoaded;
+    static HMODULE s_hComBase;
+
+    if (!s_bLoaded) {
+        s_hComBase = LoadLibraryEx(TEXT("combase.dll"), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+        s_bLoaded = true;
+    }
+    if (s_hComBase) {
+        return GetProcAddress(s_hComBase, name);
+    } else {
+        return NULL;
+    }
+}
+
+HRESULT
+WIN_RoInitialize(void)
+{
+    typedef HRESULT(WINAPI * RoInitialize_t)(RO_INIT_TYPE initType);
+    RoInitialize_t RoInitializeFunc = (RoInitialize_t)WIN_LoadComBaseFunction("RoInitialize");
+    if (RoInitializeFunc) {
+        // RO_INIT_SINGLETHREADED is equivalent to COINIT_APARTMENTTHREADED
+        HRESULT hr = RoInitializeFunc(RO_INIT_SINGLETHREADED);
+        if (hr == RPC_E_CHANGED_MODE) {
+            hr = RoInitializeFunc(RO_INIT_MULTITHREADED);
+        }
+
+        // S_FALSE means success, but someone else already initialized.
+        // You still need to call RoUninitialize in this case!
+        if (hr == S_FALSE) {
+            return S_OK;
+        }
+
+        return hr;
+    } else {
+        return E_NOINTERFACE;
+    }
+}
+
+void WIN_RoUninitialize(void)
+{
+    typedef void(WINAPI * RoUninitialize_t)(void);
+    RoUninitialize_t RoUninitializeFunc = (RoUninitialize_t)WIN_LoadComBaseFunction("RoUninitialize");
+    if (RoUninitializeFunc) {
+        RoUninitializeFunc();
+    }
+}
+
+#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
+static BOOL IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor)
+{
+    OSVERSIONINFOEXW osvi;
+    DWORDLONG const dwlConditionMask = VerSetConditionMask(
+        VerSetConditionMask(
+            VerSetConditionMask(
+                0, VER_MAJORVERSION, VER_GREATER_EQUAL),
+            VER_MINORVERSION, VER_GREATER_EQUAL),
+        VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
+
+    SDL_zero(osvi);
+    osvi.dwOSVersionInfoSize = sizeof(osvi);
+    osvi.dwMajorVersion = wMajorVersion;
+    osvi.dwMinorVersion = wMinorVersion;
+    osvi.wServicePackMajor = wServicePackMajor;
+
+    return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
+}
+#endif
+
+// apply some static variables so we only call into the Win32 API once per process for each check.
+#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
+    #define CHECKWINVER(notdesktop_platform_result, test) return (notdesktop_platform_result);
+#else
+    #define CHECKWINVER(notdesktop_platform_result, test) \
+        static bool checked = false; \
+        static BOOL result = FALSE; \
+        if (!checked) { \
+            result = (test); \
+            checked = true; \
+        } \
+        return result;
+#endif
+
+// this is the oldest thing we run on (and we may lose support for this in SDL3 at any time!),
+//  so there's no "OrGreater" as that would always be TRUE. The other functions are here to
+//  ask "can we support a specific feature?" but this function is here to ask "do we need to do
+//  something different for an OS version we probably should abandon?"  :)
+BOOL WIN_IsWindowsXP(void)
+{
+    CHECKWINVER(FALSE, !WIN_IsWindowsVistaOrGreater() && IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 0));
+}
+
+BOOL WIN_IsWindowsVistaOrGreater(void)
+{
+    CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 0));
+}
+
+BOOL WIN_IsWindows7OrGreater(void)
+{
+    CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 0));
+}
+
+BOOL WIN_IsWindows8OrGreater(void)
+{
+    CHECKWINVER(TRUE, IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0));
+}
+
+#undef CHECKWINVER
+
+
+/*
+WAVExxxCAPS gives you 31 bytes for the device name, and just truncates if it's
+longer. However, since WinXP, you can use the WAVExxxCAPS2 structure, which
+will give you a name GUID. The full name is in the Windows Registry under
+that GUID, located here: HKLM\System\CurrentControlSet\Control\MediaCategories
+
+Note that drivers can report GUID_NULL for the name GUID, in which case,
+Windows makes a best effort to fill in those 31 bytes in the usual place.
+This info summarized from MSDN:
+
+http://web.archive.org/web/20131027093034/http://msdn.microsoft.com/en-us/library/windows/hardware/ff536382(v=vs.85).aspx
+
+Always look this up in the registry if possible, because the strings are
+different! At least on Win10, I see "Yeti Stereo Microphone" in the
+Registry, and a unhelpful "Microphone(Yeti Stereo Microph" in winmm. Sigh.
+
+(Also, DirectSound shouldn't be limited to 32 chars, but its device enum
+has the same problem.)
+
+WASAPI doesn't need this. This is just for DirectSound/WinMM.
+*/
+char *WIN_LookupAudioDeviceName(const WCHAR *name, const GUID *guid)
+{
+#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
+    return WIN_StringToUTF8W(name); // No registry access on Xbox, go with what we've got.
+#else
+    static const GUID nullguid = { 0 };
+    const unsigned char *ptr;
+    char keystr[128];
+    WCHAR *strw = NULL;
+    bool rc;
+    HKEY hkey;
+    DWORD len = 0;
+    char *result = NULL;
+
+    if (WIN_IsEqualGUID(guid, &nullguid)) {
+        return WIN_StringToUTF8(name); // No GUID, go with what we've got.
+    }
+
+    ptr = (const unsigned char *)guid;
+    (void)SDL_snprintf(keystr, sizeof(keystr),
+                       "System\\CurrentControlSet\\Control\\MediaCategories\\{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
+                       ptr[3], ptr[2], ptr[1], ptr[0], ptr[5], ptr[4], ptr[7], ptr[6],
+                       ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15]);
+
+    strw = WIN_UTF8ToString(keystr);
+    rc = (RegOpenKeyExW(HKEY_LOCAL_MACHINE, strw, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS);
+    SDL_free(strw);
+    if (!rc) {
+        return WIN_StringToUTF8(name); // oh well.
+    }
+
+    rc = (RegQueryValueExW(hkey, L"Name", NULL, NULL, NULL, &len) == ERROR_SUCCESS);
+    if (!rc) {
+        RegCloseKey(hkey);
+        return WIN_StringToUTF8(name); // oh well.
+    }
+
+    strw = (WCHAR *)SDL_malloc(len + sizeof(WCHAR));
+    if (!strw) {
+        RegCloseKey(hkey);
+        return WIN_StringToUTF8(name); // oh well.
+    }
+
+    rc = (RegQueryValueExW(hkey, L"Name", NULL, NULL, (LPBYTE)strw, &len) == ERROR_SUCCESS);
+    RegCloseKey(hkey);
+    if (!rc) {
+        SDL_free(strw);
+        return WIN_StringToUTF8(name); // oh well.
+    }
+
+    strw[len / 2] = 0; // make sure it's null-terminated.
+
+    result = WIN_StringToUTF8(strw);
+    SDL_free(strw);
+    return result ? result : WIN_StringToUTF8(name);
+#endif
+}
+
+BOOL WIN_IsEqualGUID(const GUID *a, const GUID *b)
+{
+    return (SDL_memcmp(a, b, sizeof(*a)) == 0);
+}
+
+BOOL WIN_IsEqualIID(REFIID a, REFIID b)
+{
+    return (SDL_memcmp(a, b, sizeof(*a)) == 0);
+}
+
+void WIN_RECTToRect(const RECT *winrect, SDL_Rect *sdlrect)
+{
+    sdlrect->x = winrect->left;
+    sdlrect->w = (winrect->right - winrect->left) + 1;
+    sdlrect->y = winrect->top;
+    sdlrect->h = (winrect->bottom - winrect->top) + 1;
+}
+
+void WIN_RectToRECT(const SDL_Rect *sdlrect, RECT *winrect)
+{
+    winrect->left = sdlrect->x;
+    winrect->right = sdlrect->x + sdlrect->w - 1;
+    winrect->top = sdlrect->y;
+    winrect->bottom = sdlrect->y + sdlrect->h - 1;
+}
+
+bool WIN_WindowRectValid(const RECT *rect)
+{
+    // A window can be resized to zero height, but not zero width
+    return (rect->right > 0);
+}
+
+// Some GUIDs we need to know without linking to libraries that aren't available before Vista.
+/* *INDENT-OFF* */ // clang-format off
+static const GUID SDL_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
+static const GUID SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010,{ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
+/* *INDENT-ON* */ // clang-format on
+
+SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat)
+{
+    if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
+        return SDL_AUDIO_F32;
+    } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
+        return SDL_AUDIO_S16;
+    } else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
+        return SDL_AUDIO_S32;
+    } else if (waveformat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
+        const WAVEFORMATEXTENSIBLE *ext = (const WAVEFORMATEXTENSIBLE *)waveformat;
+        if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
+            return SDL_AUDIO_F32;
+        } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
+            return SDL_AUDIO_S16;
+        } else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
+            return SDL_AUDIO_S32;
+        }
+    }
+    return SDL_AUDIO_UNKNOWN;
+}
+
+
+int WIN_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar)
+{
+    if (WIN_IsWindowsXP()) {
+        dwFlags &= ~WC_ERR_INVALID_CHARS;  // not supported before Vista. Without this flag, it will just replace bogus chars with U+FFFD. You're on your own, WinXP.
+    }
+    return WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar);
+}
+
+#endif // defined(SDL_PLATFORM_WINDOWS)

+ 172 - 0
thirdparty/sdl/core/windows/SDL_windows.h

@@ -0,0 +1,172 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+// This is an include file for windows.h with the SDL build settings
+
+#ifndef _INCLUDED_WINDOWS_H
+#define _INCLUDED_WINDOWS_H
+
+#ifdef SDL_PLATFORM_WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif
+#ifndef STRICT
+#define STRICT 1
+#endif
+#ifndef UNICODE
+#define UNICODE 1
+#endif
+#undef WINVER
+#undef _WIN32_WINNT
+#if defined(SDL_VIDEO_RENDER_D3D12) || defined(HAVE_DXGI1_6_H)
+#define _WIN32_WINNT 0xA00 // For D3D12, 0xA00 is required
+#elif defined(HAVE_SHELLSCALINGAPI_H)
+#define _WIN32_WINNT 0x603 // For DPI support
+#else
+#define _WIN32_WINNT 0x501 // Need 0x410 for AlphaBlend() and 0x500 for EnumDisplayDevices(), 0x501 for raw input
+#endif
+#define WINVER _WIN32_WINNT
+
+#elif defined(SDL_PLATFORM_WINGDK)
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif
+#ifndef STRICT
+#define STRICT 1
+#endif
+#ifndef UNICODE
+#define UNICODE 1
+#endif
+#undef WINVER
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0xA00
+#define WINVER       _WIN32_WINNT
+
+#elif defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif
+#ifndef STRICT
+#define STRICT 1
+#endif
+#ifndef UNICODE
+#define UNICODE 1
+#endif
+#undef WINVER
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0xA00
+#define WINVER       _WIN32_WINNT
+#endif
+
+// See https://github.com/libsdl-org/SDL/pull/7607
+// force_align_arg_pointer attribute requires gcc >= 4.2.x.
+#if defined(__clang__)
+#define HAVE_FORCE_ALIGN_ARG_POINTER
+#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))
+#define HAVE_FORCE_ALIGN_ARG_POINTER
+#endif
+#if defined(__GNUC__) && defined(__i386__) && defined(HAVE_FORCE_ALIGN_ARG_POINTER)
+#define MINGW32_FORCEALIGN __attribute__((force_align_arg_pointer))
+#else
+#define MINGW32_FORCEALIGN
+#endif
+
+#include <windows.h>
+#include <basetyps.h> // for REFIID with broken mingw.org headers
+#include <mmreg.h>
+
+// Routines to convert from UTF8 to native Windows text
+#define WIN_StringToUTF8W(S) SDL_iconv_string("UTF-8", "UTF-16LE", (const char *)(S), (SDL_wcslen(S) + 1) * sizeof(WCHAR))
+#define WIN_UTF8ToStringW(S) (WCHAR *)SDL_iconv_string("UTF-16LE", "UTF-8", (const char *)(S), SDL_strlen(S) + 1)
+// !!! FIXME: UTF8ToString() can just be a SDL_strdup() here.
+#define WIN_StringToUTF8A(S) SDL_iconv_string("UTF-8", "ASCII", (const char *)(S), (SDL_strlen(S) + 1))
+#define WIN_UTF8ToStringA(S) SDL_iconv_string("ASCII", "UTF-8", (const char *)(S), SDL_strlen(S) + 1)
+#if UNICODE
+#define WIN_StringToUTF8 WIN_StringToUTF8W
+#define WIN_UTF8ToString WIN_UTF8ToStringW
+#define SDL_tcslen       SDL_wcslen
+#define SDL_tcsstr       SDL_wcsstr
+#else
+#define WIN_StringToUTF8 WIN_StringToUTF8A
+#define WIN_UTF8ToString WIN_UTF8ToStringA
+#define SDL_tcslen       SDL_strlen
+#define SDL_tcsstr       SDL_strstr
+#endif
+
+// Set up for C function definitions, even when using C++
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Sets an error message based on a given HRESULT
+extern bool WIN_SetErrorFromHRESULT(const char *prefix, HRESULT hr);
+
+// Sets an error message based on GetLastError(). Always returns false.
+extern bool WIN_SetError(const char *prefix);
+
+// Load a function from combase.dll
+FARPROC WIN_LoadComBaseFunction(const char *name);
+
+// Wrap up the oddities of CoInitialize() into a common function.
+extern HRESULT WIN_CoInitialize(void);
+extern void WIN_CoUninitialize(void);
+
+// Wrap up the oddities of RoInitialize() into a common function.
+extern HRESULT WIN_RoInitialize(void);
+extern void WIN_RoUninitialize(void);
+
+// Returns true if we're running on Windows XP (any service pack). DOES NOT CHECK XP "OR GREATER"!
+extern BOOL WIN_IsWindowsXP(void);
+
+// Returns true if we're running on Windows Vista and newer
+extern BOOL WIN_IsWindowsVistaOrGreater(void);
+
+// Returns true if we're running on Windows 7 and newer
+extern BOOL WIN_IsWindows7OrGreater(void);
+
+// Returns true if we're running on Windows 8 and newer
+extern BOOL WIN_IsWindows8OrGreater(void);
+
+// You need to SDL_free() the result of this call.
+extern char *WIN_LookupAudioDeviceName(const WCHAR *name, const GUID *guid);
+
+// Checks to see if two GUID are the same.
+extern BOOL WIN_IsEqualGUID(const GUID *a, const GUID *b);
+extern BOOL WIN_IsEqualIID(REFIID a, REFIID b);
+
+// Convert between SDL_rect and RECT
+extern void WIN_RECTToRect(const RECT *winrect, SDL_Rect *sdlrect);
+extern void WIN_RectToRECT(const SDL_Rect *sdlrect, RECT *winrect);
+
+// Returns false if a window client rect is not valid
+bool WIN_WindowRectValid(const RECT *rect);
+
+extern SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat);
+
+// WideCharToMultiByte, but with some WinXP management.
+extern int WIN_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar);
+
+// Ends C function definitions when using C++
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _INCLUDED_WINDOWS_H

+ 140 - 0
thirdparty/sdl/core/windows/SDL_xinput.c

@@ -0,0 +1,140 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#include "SDL_xinput.h"
+
+// Set up for C function definitions, even when using C++
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+XInputGetState_t SDL_XInputGetState = NULL;
+XInputSetState_t SDL_XInputSetState = NULL;
+XInputGetCapabilities_t SDL_XInputGetCapabilities = NULL;
+XInputGetCapabilitiesEx_t SDL_XInputGetCapabilitiesEx = NULL;
+XInputGetBatteryInformation_t SDL_XInputGetBatteryInformation = NULL;
+DWORD SDL_XInputVersion = 0;
+
+static HMODULE s_pXInputDLL = NULL;
+static int s_XInputDLLRefCount = 0;
+
+#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
+
+bool WIN_LoadXInputDLL(void)
+{
+    /* Getting handles to system dlls (via LoadLibrary and its variants) is not
+     * supported on Xbox, thus, pointers to XInput's functions can't be
+     * retrieved via GetProcAddress.
+     *
+     * When on Xbox, assume that XInput is already loaded, and directly map
+     * its XInput.h-declared functions to the SDL_XInput* set of function
+     * pointers.
+     */
+    SDL_XInputGetState = (XInputGetState_t)XInputGetState;
+    SDL_XInputSetState = (XInputSetState_t)XInputSetState;
+    SDL_XInputGetCapabilities = (XInputGetCapabilities_t)XInputGetCapabilities;
+    SDL_XInputGetBatteryInformation = (XInputGetBatteryInformation_t)XInputGetBatteryInformation;
+
+    // XInput 1.4 ships with Windows 8 and 8.1:
+    SDL_XInputVersion = (1 << 16) | 4;
+
+    return true;
+}
+
+void WIN_UnloadXInputDLL(void)
+{
+}
+
+#else // !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
+
+bool WIN_LoadXInputDLL(void)
+{
+    DWORD version = 0;
+
+    if (s_pXInputDLL) {
+        SDL_assert(s_XInputDLLRefCount > 0);
+        s_XInputDLLRefCount++;
+        return true; // already loaded
+    }
+
+    /* NOTE: Don't load XinputUap.dll
+     * This is XInput emulation over Windows.Gaming.Input, and has all the
+     * limitations of that API (no devices at startup, no background input, etc.)
+     */
+    version = (1 << 16) | 4;
+    s_pXInputDLL = LoadLibrary(TEXT("XInput1_4.dll")); // 1.4 Ships with Windows 8.
+    if (!s_pXInputDLL) {
+        version = (1 << 16) | 3;
+        s_pXInputDLL = LoadLibrary(TEXT("XInput1_3.dll")); // 1.3 can be installed as a redistributable component.
+    }
+    if (!s_pXInputDLL) {
+        s_pXInputDLL = LoadLibrary(TEXT("bin\\XInput1_3.dll"));
+    }
+    if (!s_pXInputDLL) {
+        // "9.1.0" Ships with Vista and Win7, and is more limited than 1.3+ (e.g. XInputGetStateEx is not available.)
+        s_pXInputDLL = LoadLibrary(TEXT("XInput9_1_0.dll"));
+    }
+    if (!s_pXInputDLL) {
+        return false;
+    }
+
+    SDL_assert(s_XInputDLLRefCount == 0);
+    SDL_XInputVersion = version;
+    s_XInputDLLRefCount = 1;
+
+    // 100 is the ordinal for _XInputGetStateEx, which returns the same struct as XinputGetState, but with extra data in wButtons for the guide button, we think...
+    SDL_XInputGetState = (XInputGetState_t)GetProcAddress(s_pXInputDLL, (LPCSTR)100);
+    if (!SDL_XInputGetState) {
+        SDL_XInputGetState = (XInputGetState_t)GetProcAddress(s_pXInputDLL, "XInputGetState");
+    }
+    SDL_XInputSetState = (XInputSetState_t)GetProcAddress(s_pXInputDLL, "XInputSetState");
+    SDL_XInputGetCapabilities = (XInputGetCapabilities_t)GetProcAddress(s_pXInputDLL, "XInputGetCapabilities");
+    // 108 is the ordinal for _XInputGetCapabilitiesEx, which additionally returns VID/PID of the controller.
+    SDL_XInputGetCapabilitiesEx = (XInputGetCapabilitiesEx_t)GetProcAddress(s_pXInputDLL, (LPCSTR)108);
+    SDL_XInputGetBatteryInformation = (XInputGetBatteryInformation_t)GetProcAddress(s_pXInputDLL, "XInputGetBatteryInformation");
+    if (!SDL_XInputGetState || !SDL_XInputSetState || !SDL_XInputGetCapabilities) {
+        WIN_UnloadXInputDLL();
+        return false;
+    }
+
+    return true;
+}
+
+void WIN_UnloadXInputDLL(void)
+{
+    if (s_pXInputDLL) {
+        SDL_assert(s_XInputDLLRefCount > 0);
+        if (--s_XInputDLLRefCount == 0) {
+            FreeLibrary(s_pXInputDLL);
+            s_pXInputDLL = NULL;
+        }
+    } else {
+        SDL_assert(s_XInputDLLRefCount == 0);
+    }
+}
+
+#endif
+
+// Ends C function definitions when using C++
+#ifdef __cplusplus
+}
+#endif

+ 276 - 0
thirdparty/sdl/core/windows/SDL_xinput.h

@@ -0,0 +1,276 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#ifndef SDL_xinput_h_
+#define SDL_xinput_h_
+
+#include "SDL_windows.h"
+
+#ifdef HAVE_XINPUT_H
+#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES)
+// Xbox supports an XInput wrapper which is a C++-only header...
+#include <math.h> // Required to compile with recent MSVC...
+#include <XInputOnGameInput.h>
+using namespace XInputOnGameInput;
+#else
+#include <xinput.h>
+#endif
+#endif // HAVE_XINPUT_H
+
+#ifndef XUSER_MAX_COUNT
+#define XUSER_MAX_COUNT 4
+#endif
+#ifndef XUSER_INDEX_ANY
+#define XUSER_INDEX_ANY 0x000000FF
+#endif
+#ifndef XINPUT_CAPS_FFB_SUPPORTED
+#define XINPUT_CAPS_FFB_SUPPORTED 0x0001
+#endif
+#ifndef XINPUT_CAPS_WIRELESS
+#define XINPUT_CAPS_WIRELESS 0x0002
+#endif
+
+#ifndef XINPUT_DEVSUBTYPE_UNKNOWN
+#define XINPUT_DEVSUBTYPE_UNKNOWN 0x00
+#endif
+#ifndef XINPUT_DEVSUBTYPE_GAMEPAD
+#define XINPUT_DEVSUBTYPE_GAMEPAD 0x01
+#endif
+#ifndef XINPUT_DEVSUBTYPE_WHEEL
+#define XINPUT_DEVSUBTYPE_WHEEL 0x02
+#endif
+#ifndef XINPUT_DEVSUBTYPE_ARCADE_STICK
+#define XINPUT_DEVSUBTYPE_ARCADE_STICK 0x03
+#endif
+#ifndef XINPUT_DEVSUBTYPE_FLIGHT_STICK
+#define XINPUT_DEVSUBTYPE_FLIGHT_STICK 0x04
+#endif
+#ifndef XINPUT_DEVSUBTYPE_DANCE_PAD
+#define XINPUT_DEVSUBTYPE_DANCE_PAD 0x05
+#endif
+#ifndef XINPUT_DEVSUBTYPE_GUITAR
+#define XINPUT_DEVSUBTYPE_GUITAR 0x06
+#endif
+#ifndef XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE
+#define XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE 0x07
+#endif
+#ifndef XINPUT_DEVSUBTYPE_DRUM_KIT
+#define XINPUT_DEVSUBTYPE_DRUM_KIT 0x08
+#endif
+#ifndef XINPUT_DEVSUBTYPE_GUITAR_BASS
+#define XINPUT_DEVSUBTYPE_GUITAR_BASS 0x0B
+#endif
+#ifndef XINPUT_DEVSUBTYPE_ARCADE_PAD
+#define XINPUT_DEVSUBTYPE_ARCADE_PAD 0x13
+#endif
+
+#ifndef XINPUT_FLAG_GAMEPAD
+#define XINPUT_FLAG_GAMEPAD 0x01
+#endif
+
+#ifndef XINPUT_GAMEPAD_DPAD_UP
+#define XINPUT_GAMEPAD_DPAD_UP 0x0001
+#endif
+#ifndef XINPUT_GAMEPAD_DPAD_DOWN
+#define XINPUT_GAMEPAD_DPAD_DOWN 0x0002
+#endif
+#ifndef XINPUT_GAMEPAD_DPAD_LEFT
+#define XINPUT_GAMEPAD_DPAD_LEFT 0x0004
+#endif
+#ifndef XINPUT_GAMEPAD_DPAD_RIGHT
+#define XINPUT_GAMEPAD_DPAD_RIGHT 0x0008
+#endif
+#ifndef XINPUT_GAMEPAD_START
+#define XINPUT_GAMEPAD_START 0x0010
+#endif
+#ifndef XINPUT_GAMEPAD_BACK
+#define XINPUT_GAMEPAD_BACK 0x0020
+#endif
+#ifndef XINPUT_GAMEPAD_LEFT_THUMB
+#define XINPUT_GAMEPAD_LEFT_THUMB 0x0040
+#endif
+#ifndef XINPUT_GAMEPAD_RIGHT_THUMB
+#define XINPUT_GAMEPAD_RIGHT_THUMB 0x0080
+#endif
+#ifndef XINPUT_GAMEPAD_LEFT_SHOULDER
+#define XINPUT_GAMEPAD_LEFT_SHOULDER 0x0100
+#endif
+#ifndef XINPUT_GAMEPAD_RIGHT_SHOULDER
+#define XINPUT_GAMEPAD_RIGHT_SHOULDER 0x0200
+#endif
+#ifndef XINPUT_GAMEPAD_A
+#define XINPUT_GAMEPAD_A 0x1000
+#endif
+#ifndef XINPUT_GAMEPAD_B
+#define XINPUT_GAMEPAD_B 0x2000
+#endif
+#ifndef XINPUT_GAMEPAD_X
+#define XINPUT_GAMEPAD_X 0x4000
+#endif
+#ifndef XINPUT_GAMEPAD_Y
+#define XINPUT_GAMEPAD_Y 0x8000
+#endif
+
+#ifndef XINPUT_GAMEPAD_GUIDE
+#define XINPUT_GAMEPAD_GUIDE 0x0400
+#endif
+
+#ifndef BATTERY_DEVTYPE_GAMEPAD
+#define BATTERY_DEVTYPE_GAMEPAD 0x00
+#endif
+
+#ifndef BATTERY_TYPE_DISCONNECTED
+#define BATTERY_TYPE_DISCONNECTED 0x00
+#endif
+#ifndef BATTERY_TYPE_WIRED
+#define BATTERY_TYPE_WIRED 0x01
+#endif
+#ifndef BATTERY_TYPE_UNKNOWN
+#define BATTERY_TYPE_UNKNOWN 0xFF
+#endif
+#ifndef BATTERY_LEVEL_EMPTY
+#define BATTERY_LEVEL_EMPTY 0x00
+#endif
+#ifndef BATTERY_LEVEL_LOW
+#define BATTERY_LEVEL_LOW 0x01
+#endif
+#ifndef BATTERY_LEVEL_MEDIUM
+#define BATTERY_LEVEL_MEDIUM 0x02
+#endif
+#ifndef BATTERY_LEVEL_FULL
+#define BATTERY_LEVEL_FULL 0x03
+#endif
+
+// Set up for C function definitions, even when using C++
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// typedef's for XInput structs we use
+
+
+// This is the same as XINPUT_BATTERY_INFORMATION, but always defined instead of just if WIN32_WINNT >= _WIN32_WINNT_WIN8
+typedef struct
+{
+    BYTE BatteryType;
+    BYTE BatteryLevel;
+} XINPUT_BATTERY_INFORMATION_EX;
+
+#ifndef HAVE_XINPUT_H
+
+typedef struct
+{
+    WORD wButtons;
+    BYTE bLeftTrigger;
+    BYTE bRightTrigger;
+    SHORT sThumbLX;
+    SHORT sThumbLY;
+    SHORT sThumbRX;
+    SHORT sThumbRY;
+} XINPUT_GAMEPAD;
+
+typedef struct
+{
+    DWORD dwPacketNumber;
+    XINPUT_GAMEPAD Gamepad;
+} XINPUT_STATE;
+
+typedef struct
+{
+    WORD wLeftMotorSpeed;
+    WORD wRightMotorSpeed;
+} XINPUT_VIBRATION;
+
+typedef struct
+{
+    BYTE Type;
+    BYTE SubType;
+    WORD Flags;
+    XINPUT_GAMEPAD Gamepad;
+    XINPUT_VIBRATION Vibration;
+} XINPUT_CAPABILITIES;
+
+#endif // HAVE_XINPUT_H
+
+// This struct is not defined in XInput headers.
+typedef struct
+{
+    XINPUT_CAPABILITIES Capabilities;
+    WORD VendorId;
+    WORD ProductId;
+    WORD ProductVersion;
+    WORD unk1;
+    DWORD unk2;
+} SDL_XINPUT_CAPABILITIES_EX;
+
+// Forward decl's for XInput API's we load dynamically and use if available
+typedef DWORD(WINAPI *XInputGetState_t)(
+    DWORD dwUserIndex,      // [in] Index of the gamer associated with the device
+    XINPUT_STATE *pState    // [out] Receives the current state
+);
+
+typedef DWORD(WINAPI *XInputSetState_t)(
+    DWORD dwUserIndex,           // [in] Index of the gamer associated with the device
+    XINPUT_VIBRATION *pVibration // [in, out] The vibration information to send to the controller
+);
+
+typedef DWORD(WINAPI *XInputGetCapabilities_t)(
+    DWORD dwUserIndex,                 // [in] Index of the gamer associated with the device
+    DWORD dwFlags,                     // [in] Input flags that identify the device type
+    XINPUT_CAPABILITIES *pCapabilities // [out] Receives the capabilities
+);
+
+// Only available in XInput 1.4 that is shipped with Windows 8 and newer.
+typedef DWORD(WINAPI *XInputGetCapabilitiesEx_t)(
+    DWORD dwReserved,                       // [in] Must be 1
+    DWORD dwUserIndex,                      // [in] Index of the gamer associated with the device
+    DWORD dwFlags,                          // [in] Input flags that identify the device type
+    SDL_XINPUT_CAPABILITIES_EX *pCapabilitiesEx // [out] Receives the capabilities
+);
+
+typedef DWORD(WINAPI *XInputGetBatteryInformation_t)(
+    DWORD dwUserIndex,
+    BYTE devType,
+    XINPUT_BATTERY_INFORMATION_EX *pBatteryInformation);
+
+extern bool WIN_LoadXInputDLL(void);
+extern void WIN_UnloadXInputDLL(void);
+
+extern XInputGetState_t SDL_XInputGetState;
+extern XInputSetState_t SDL_XInputSetState;
+extern XInputGetCapabilities_t SDL_XInputGetCapabilities;
+extern XInputGetCapabilitiesEx_t SDL_XInputGetCapabilitiesEx;
+extern XInputGetBatteryInformation_t SDL_XInputGetBatteryInformation;
+extern DWORD SDL_XInputVersion; // ((major << 16) & 0xFF00) | (minor & 0xFF)
+
+// Ends C function definitions when using C++
+#ifdef __cplusplus
+}
+#endif
+
+#define XINPUTGETSTATE              SDL_XInputGetState
+#define XINPUTSETSTATE              SDL_XInputSetState
+#define XINPUTGETCAPABILITIES       SDL_XInputGetCapabilities
+#define XINPUTGETCAPABILITIESEX     SDL_XInputGetCapabilitiesEx
+#define XINPUTGETBATTERYINFORMATION SDL_XInputGetBatteryInformation
+
+#endif // SDL_xinput_h_

+ 21 - 0
thirdparty/sdl/core/windows/pch.c

@@ -0,0 +1,21 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"

+ 21 - 0
thirdparty/sdl/core/windows/pch_cpp.cpp

@@ -0,0 +1,21 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"

+ 1833 - 0
thirdparty/sdl/events/SDL_events.c

@@ -0,0 +1,1833 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+// General event handling code for SDL
+
+#include "SDL_events_c.h"
+#include "SDL_eventwatch_c.h"
+#include "../SDL_hints_c.h"
+#include "../timer/SDL_timer_c.h"
+#ifndef SDL_JOYSTICK_DISABLED
+#include "../joystick/SDL_joystick_c.h"
+#endif
+#ifndef SDL_SENSOR_DISABLED
+#include "../sensor/SDL_sensor_c.h"
+#endif
+//#include "../video/SDL_sysvideo.h"
+
+#ifdef SDL_PLATFORM_ANDROID
+#include "../core/android/SDL_android.h"
+#include "../video/android/SDL_androidevents.h"
+#endif
+
+// An arbitrary limit so we don't have unbounded growth
+#define SDL_MAX_QUEUED_EVENTS 65535
+
+// Determines how often we pump events if joystick or sensor subsystems are active
+#define ENUMERATION_POLL_INTERVAL_NS (3 * SDL_NS_PER_SECOND)
+
+// Determines how often to pump events if joysticks or sensors are actively being read
+#define EVENT_POLL_INTERVAL_NS SDL_MS_TO_NS(1)
+
+// Make sure the type in the SDL_Event aligns properly across the union
+SDL_COMPILE_TIME_ASSERT(SDL_Event_type, sizeof(Uint32) == sizeof(SDL_EventType));
+
+#define SDL2_SYSWMEVENT 0x201
+
+#ifdef SDL_VIDEO_DRIVER_WINDOWS
+#include "../core/windows/SDL_windows.h"
+#endif
+
+#ifdef SDL_VIDEO_DRIVER_X11
+#include <X11/Xlib.h>
+#endif
+
+typedef struct SDL2_version
+{
+    Uint8 major;
+    Uint8 minor;
+    Uint8 patch;
+} SDL2_version;
+
+typedef enum
+{
+  SDL2_SYSWM_UNKNOWN
+} SDL2_SYSWM_TYPE;
+
+typedef struct SDL2_SysWMmsg
+{
+    SDL2_version version;
+    SDL2_SYSWM_TYPE subsystem;
+    union
+    {
+#ifdef SDL_VIDEO_DRIVER_WINDOWS
+        struct {
+            HWND hwnd;                  /**< The window for the message */
+            UINT msg;                   /**< The type of message */
+            WPARAM wParam;              /**< WORD message parameter */
+            LPARAM lParam;              /**< LONG message parameter */
+        } win;
+#endif
+#ifdef SDL_VIDEO_DRIVER_X11
+        struct {
+            XEvent event;
+        } x11;
+#endif
+        /* Can't have an empty union */
+        int dummy;
+    } msg;
+} SDL2_SysWMmsg;
+
+static SDL_EventWatchList SDL_event_watchers;
+static SDL_AtomicInt SDL_sentinel_pending;
+static Uint32 SDL_last_event_id = 0;
+
+typedef struct
+{
+    Uint32 bits[8];
+} SDL_DisabledEventBlock;
+
+static SDL_DisabledEventBlock *SDL_disabled_events[256];
+static SDL_AtomicInt SDL_userevents;
+
+typedef struct SDL_TemporaryMemory
+{
+    void *memory;
+    struct SDL_TemporaryMemory *prev;
+    struct SDL_TemporaryMemory *next;
+} SDL_TemporaryMemory;
+
+typedef struct SDL_TemporaryMemoryState
+{
+    SDL_TemporaryMemory *head;
+    SDL_TemporaryMemory *tail;
+} SDL_TemporaryMemoryState;
+
+static SDL_TLSID SDL_temporary_memory;
+
+typedef struct SDL_EventEntry
+{
+    SDL_Event event;
+    SDL_TemporaryMemory *memory;
+    struct SDL_EventEntry *prev;
+    struct SDL_EventEntry *next;
+} SDL_EventEntry;
+
+static struct
+{
+    SDL_Mutex *lock;
+    bool active;
+    SDL_AtomicInt count;
+    int max_events_seen;
+    SDL_EventEntry *head;
+    SDL_EventEntry *tail;
+    SDL_EventEntry *free;
+} SDL_EventQ = { NULL, false, { 0 }, 0, NULL, NULL, NULL };
+
+
+static void SDL_CleanupTemporaryMemory(void *data)
+{
+    SDL_TemporaryMemoryState *state = (SDL_TemporaryMemoryState *)data;
+
+    SDL_FreeTemporaryMemory();
+    SDL_free(state);
+}
+
+static SDL_TemporaryMemoryState *SDL_GetTemporaryMemoryState(bool create)
+{
+    SDL_TemporaryMemoryState *state;
+
+    state = (SDL_TemporaryMemoryState *)SDL_GetTLS(&SDL_temporary_memory);
+    if (!state) {
+        if (!create) {
+            return NULL;
+        }
+
+        state = (SDL_TemporaryMemoryState *)SDL_calloc(1, sizeof(*state));
+        if (!state) {
+            return NULL;
+        }
+
+        if (!SDL_SetTLS(&SDL_temporary_memory, state, SDL_CleanupTemporaryMemory)) {
+            SDL_free(state);
+            return NULL;
+        }
+    }
+    return state;
+}
+
+static SDL_TemporaryMemory *SDL_GetTemporaryMemoryEntry(SDL_TemporaryMemoryState *state, const void *mem)
+{
+    SDL_TemporaryMemory *entry;
+
+    // Start from the end, it's likely to have been recently allocated
+    for (entry = state->tail; entry; entry = entry->prev) {
+        if (mem == entry->memory) {
+            return entry;
+        }
+    }
+    return NULL;
+}
+
+static void SDL_LinkTemporaryMemoryEntry(SDL_TemporaryMemoryState *state, SDL_TemporaryMemory *entry)
+{
+    entry->prev = state->tail;
+    entry->next = NULL;
+
+    if (state->tail) {
+        state->tail->next = entry;
+    } else {
+        state->head = entry;
+    }
+    state->tail = entry;
+}
+
+static void SDL_UnlinkTemporaryMemoryEntry(SDL_TemporaryMemoryState *state, SDL_TemporaryMemory *entry)
+{
+    if (state->head == entry) {
+        state->head = entry->next;
+    }
+    if (state->tail == entry) {
+        state->tail = entry->prev;
+    }
+
+    if (entry->prev) {
+        entry->prev->next = entry->next;
+    }
+    if (entry->next) {
+        entry->next->prev = entry->prev;
+    }
+
+    entry->prev = NULL;
+    entry->next = NULL;
+}
+
+static void SDL_FreeTemporaryMemoryEntry(SDL_TemporaryMemoryState *state, SDL_TemporaryMemory *entry, bool free_data)
+{
+    if (free_data) {
+        SDL_free(entry->memory);
+    }
+    SDL_free(entry);
+}
+
+static void SDL_LinkTemporaryMemoryToEvent(SDL_EventEntry *event, const void *mem)
+{
+    SDL_TemporaryMemoryState *state;
+    SDL_TemporaryMemory *entry;
+
+    state = SDL_GetTemporaryMemoryState(false);
+    if (!state) {
+        return;
+    }
+
+    entry = SDL_GetTemporaryMemoryEntry(state, mem);
+    if (entry) {
+        SDL_UnlinkTemporaryMemoryEntry(state, entry);
+        entry->next = event->memory;
+        event->memory = entry;
+    }
+}
+
+static void SDL_TransferSysWMMemoryToEvent(SDL_EventEntry *event)
+{
+    SDL2_SysWMmsg **wmmsg = (SDL2_SysWMmsg **)((&event->event.common)+1);
+    SDL2_SysWMmsg *mem = SDL_AllocateTemporaryMemory(sizeof(*mem));
+    if (mem) {
+        SDL_copyp(mem, *wmmsg);
+        *wmmsg = mem;
+        SDL_LinkTemporaryMemoryToEvent(event, mem);
+    }
+}
+
+// Transfer the event memory from the thread-local event memory list to the event
+static void SDL_TransferTemporaryMemoryToEvent(SDL_EventEntry *event)
+{
+    switch (event->event.type) {
+    case SDL_EVENT_TEXT_EDITING:
+        SDL_LinkTemporaryMemoryToEvent(event, event->event.edit.text);
+        break;
+    case SDL_EVENT_TEXT_EDITING_CANDIDATES:
+        SDL_LinkTemporaryMemoryToEvent(event, event->event.edit_candidates.candidates);
+        break;
+    case SDL_EVENT_TEXT_INPUT:
+        SDL_LinkTemporaryMemoryToEvent(event, event->event.text.text);
+        break;
+    case SDL_EVENT_DROP_BEGIN:
+    case SDL_EVENT_DROP_FILE:
+    case SDL_EVENT_DROP_TEXT:
+    case SDL_EVENT_DROP_COMPLETE:
+    case SDL_EVENT_DROP_POSITION:
+        SDL_LinkTemporaryMemoryToEvent(event, event->event.drop.source);
+        SDL_LinkTemporaryMemoryToEvent(event, event->event.drop.data);
+        break;
+    case SDL_EVENT_CLIPBOARD_UPDATE:
+        SDL_LinkTemporaryMemoryToEvent(event, event->event.clipboard.mime_types);
+        break;
+    case SDL2_SYSWMEVENT:
+        // We need to copy the stack pointer into temporary memory
+        SDL_TransferSysWMMemoryToEvent(event);
+        break;
+    default:
+        break;
+    }
+}
+
+// Transfer the event memory from the event to the thread-local event memory list
+static void SDL_TransferTemporaryMemoryFromEvent(SDL_EventEntry *event)
+{
+    SDL_TemporaryMemoryState *state;
+    SDL_TemporaryMemory *entry, *next;
+
+    if (!event->memory) {
+        return;
+    }
+
+    state = SDL_GetTemporaryMemoryState(true);
+    if (!state) {
+        return;  // this is now a leak, but you probably have bigger problems if malloc failed.
+    }
+
+    for (entry = event->memory; entry; entry = next) {
+        next = entry->next;
+        SDL_LinkTemporaryMemoryEntry(state, entry);
+    }
+    event->memory = NULL;
+}
+
+static void *SDL_FreeLater(void *memory)
+{
+    SDL_TemporaryMemoryState *state;
+
+    if (memory == NULL) {
+        return NULL;
+    }
+
+    // Make sure we're not adding this to the list twice
+    //SDL_assert(!SDL_ClaimTemporaryMemory(memory));
+
+    state = SDL_GetTemporaryMemoryState(true);
+    if (!state) {
+        return memory;  // this is now a leak, but you probably have bigger problems if malloc failed.
+    }
+
+    SDL_TemporaryMemory *entry = (SDL_TemporaryMemory *)SDL_malloc(sizeof(*entry));
+    if (!entry) {
+        return memory;  // this is now a leak, but you probably have bigger problems if malloc failed. We could probably pool up and reuse entries, though.
+    }
+
+    entry->memory = memory;
+
+    SDL_LinkTemporaryMemoryEntry(state, entry);
+
+    return memory;
+}
+
+void *SDL_AllocateTemporaryMemory(size_t size)
+{
+    return SDL_FreeLater(SDL_malloc(size));
+}
+
+const char *SDL_CreateTemporaryString(const char *string)
+{
+    if (string) {
+        return (const char *)SDL_FreeLater(SDL_strdup(string));
+    }
+    return NULL;
+}
+
+void *SDL_ClaimTemporaryMemory(const void *mem)
+{
+    SDL_TemporaryMemoryState *state;
+
+    state = SDL_GetTemporaryMemoryState(false);
+    if (state && mem) {
+        SDL_TemporaryMemory *entry = SDL_GetTemporaryMemoryEntry(state, mem);
+        if (entry) {
+            SDL_UnlinkTemporaryMemoryEntry(state, entry);
+            SDL_FreeTemporaryMemoryEntry(state, entry, false);
+            return (void *)mem;
+        }
+    }
+    return NULL;
+}
+
+void SDL_FreeTemporaryMemory(void)
+{
+    SDL_TemporaryMemoryState *state;
+
+    state = SDL_GetTemporaryMemoryState(false);
+    if (!state) {
+        return;
+    }
+
+    while (state->head) {
+        SDL_TemporaryMemory *entry = state->head;
+
+        SDL_UnlinkTemporaryMemoryEntry(state, entry);
+        SDL_FreeTemporaryMemoryEntry(state, entry, true);
+    }
+}
+
+#ifndef SDL_JOYSTICK_DISABLED
+
+static bool SDL_update_joysticks = true;
+
+static void SDLCALL SDL_AutoUpdateJoysticksChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
+{
+    SDL_update_joysticks = SDL_GetStringBoolean(hint, true);
+}
+
+#endif // !SDL_JOYSTICK_DISABLED
+
+#ifndef SDL_SENSOR_DISABLED
+
+static bool SDL_update_sensors = true;
+
+static void SDLCALL SDL_AutoUpdateSensorsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
+{
+    SDL_update_sensors = SDL_GetStringBoolean(hint, true);
+}
+
+#endif // !SDL_SENSOR_DISABLED
+
+static void SDLCALL SDL_PollSentinelChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
+{
+    SDL_SetEventEnabled(SDL_EVENT_POLL_SENTINEL, SDL_GetStringBoolean(hint, true));
+}
+
+/**
+ * Verbosity of logged events as defined in SDL_HINT_EVENT_LOGGING:
+ *  - 0: (default) no logging
+ *  - 1: logging of most events
+ *  - 2: as above, plus mouse, pen, and finger motion
+ */
+static int SDL_EventLoggingVerbosity = 0;
+
+static void SDLCALL SDL_EventLoggingChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
+{
+    SDL_EventLoggingVerbosity = (hint && *hint) ? SDL_clamp(SDL_atoi(hint), 0, 3) : 0;
+}
+
+static void SDL_LogEvent(const SDL_Event *event)
+{
+    static const char *pen_axisnames[] = { "PRESSURE", "XTILT", "YTILT", "DISTANCE", "ROTATION", "SLIDER", "TANGENTIAL_PRESSURE" };
+    SDL_COMPILE_TIME_ASSERT(pen_axisnames_array_matches, SDL_arraysize(pen_axisnames) == SDL_PEN_AXIS_COUNT);
+
+    char name[64];
+    char details[128];
+
+    // sensor/mouse/pen/finger motion are spammy, ignore these if they aren't demanded.
+    if ((SDL_EventLoggingVerbosity < 2) &&
+        ((event->type == SDL_EVENT_MOUSE_MOTION) ||
+         (event->type == SDL_EVENT_FINGER_MOTION) ||
+         (event->type == SDL_EVENT_PEN_AXIS) ||
+         (event->type == SDL_EVENT_PEN_MOTION) ||
+         (event->type == SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION) ||
+         (event->type == SDL_EVENT_GAMEPAD_SENSOR_UPDATE) ||
+         (event->type == SDL_EVENT_SENSOR_UPDATE))) {
+        return;
+    }
+
+// this is to make (void)SDL_snprintf() calls cleaner.
+#define uint unsigned int
+
+    name[0] = '\0';
+    details[0] = '\0';
+
+    // !!! FIXME: This code is kinda ugly, sorry.
+
+    if ((event->type >= SDL_EVENT_USER) && (event->type <= SDL_EVENT_LAST)) {
+        char plusstr[16];
+        SDL_strlcpy(name, "SDL_EVENT_USER", sizeof(name));
+        if (event->type > SDL_EVENT_USER) {
+            (void)SDL_snprintf(plusstr, sizeof(plusstr), "+%u", ((uint)event->type) - SDL_EVENT_USER);
+        } else {
+            plusstr[0] = '\0';
+        }
+        (void)SDL_snprintf(details, sizeof(details), "%s (timestamp=%u windowid=%u code=%d data1=%p data2=%p)",
+                           plusstr, (uint)event->user.timestamp, (uint)event->user.windowID,
+                           (int)event->user.code, event->user.data1, event->user.data2);
+    }
+
+    switch (event->type) {
+#define SDL_EVENT_CASE(x) \
+    case x:               \
+        SDL_strlcpy(name, #x, sizeof(name));
+        SDL_EVENT_CASE(SDL_EVENT_FIRST)
+        SDL_strlcpy(details, " (THIS IS PROBABLY A BUG!)", sizeof(details));
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_QUIT)
+        (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u)", (uint)event->quit.timestamp);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_TERMINATING)
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_LOW_MEMORY)
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_WILL_ENTER_BACKGROUND)
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_DID_ENTER_BACKGROUND)
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_WILL_ENTER_FOREGROUND)
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_DID_ENTER_FOREGROUND)
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_LOCALE_CHANGED)
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_SYSTEM_THEME_CHANGED)
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_KEYMAP_CHANGED)
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_CLIPBOARD_UPDATE)
+        break;
+
+#define SDL_RENDEREVENT_CASE(x)                \
+    case x:                                    \
+        SDL_strlcpy(name, #x, sizeof(name));   \
+        (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u event=%s windowid=%u)", \
+                           (uint)event->display.timestamp, name, (uint)event->render.windowID); \
+        break
+        SDL_RENDEREVENT_CASE(SDL_EVENT_RENDER_TARGETS_RESET);
+        SDL_RENDEREVENT_CASE(SDL_EVENT_RENDER_DEVICE_RESET);
+        SDL_RENDEREVENT_CASE(SDL_EVENT_RENDER_DEVICE_LOST);
+
+#define SDL_DISPLAYEVENT_CASE(x)               \
+    case x:                                    \
+        SDL_strlcpy(name, #x, sizeof(name));   \
+        (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u display=%u event=%s data1=%d, data2=%d)", \
+                           (uint)event->display.timestamp, (uint)event->display.displayID, name, (int)event->display.data1, (int)event->display.data2); \
+        break
+        SDL_DISPLAYEVENT_CASE(SDL_EVENT_DISPLAY_ORIENTATION);
+        SDL_DISPLAYEVENT_CASE(SDL_EVENT_DISPLAY_ADDED);
+        SDL_DISPLAYEVENT_CASE(SDL_EVENT_DISPLAY_REMOVED);
+        SDL_DISPLAYEVENT_CASE(SDL_EVENT_DISPLAY_MOVED);
+        SDL_DISPLAYEVENT_CASE(SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED);
+        SDL_DISPLAYEVENT_CASE(SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED);
+        SDL_DISPLAYEVENT_CASE(SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED);
+#undef SDL_DISPLAYEVENT_CASE
+
+#define SDL_WINDOWEVENT_CASE(x)                \
+    case x:                                    \
+        SDL_strlcpy(name, #x, sizeof(name)); \
+        (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u event=%s data1=%d data2=%d)", \
+                           (uint)event->window.timestamp, (uint)event->window.windowID, name, (int)event->window.data1, (int)event->window.data2); \
+        break
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_SHOWN);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_HIDDEN);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_EXPOSED);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_MOVED);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_RESIZED);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_METAL_VIEW_RESIZED);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_SAFE_AREA_CHANGED);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_MINIMIZED);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_MAXIMIZED);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_RESTORED);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_MOUSE_ENTER);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_MOUSE_LEAVE);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_FOCUS_GAINED);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_FOCUS_LOST);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_CLOSE_REQUESTED);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_HIT_TEST);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_ICCPROF_CHANGED);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_DISPLAY_CHANGED);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_OCCLUDED);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_ENTER_FULLSCREEN);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_LEAVE_FULLSCREEN);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_DESTROYED);
+        SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_HDR_STATE_CHANGED);
+#undef SDL_WINDOWEVENT_CASE
+
+#define PRINT_KEYDEV_EVENT(event) (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%u)", (uint)event->kdevice.timestamp, (uint)event->kdevice.which)
+        SDL_EVENT_CASE(SDL_EVENT_KEYBOARD_ADDED)
+        PRINT_KEYDEV_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_KEYBOARD_REMOVED)
+        PRINT_KEYDEV_EVENT(event);
+        break;
+#undef PRINT_KEYDEV_EVENT
+
+#define PRINT_KEY_EVENT(event)                                                                                                              \
+    (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u state=%s repeat=%s scancode=%u keycode=%u mod=0x%x)", \
+                       (uint)event->key.timestamp, (uint)event->key.windowID, (uint)event->key.which,                                       \
+                       event->key.down ? "pressed" : "released",                                                            \
+                       event->key.repeat ? "true" : "false",                                                                                \
+                       (uint)event->key.scancode,                                                                                           \
+                       (uint)event->key.key,                                                                                                \
+                       (uint)event->key.mod)
+        SDL_EVENT_CASE(SDL_EVENT_KEY_DOWN)
+        PRINT_KEY_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_KEY_UP)
+        PRINT_KEY_EVENT(event);
+        break;
+#undef PRINT_KEY_EVENT
+
+        SDL_EVENT_CASE(SDL_EVENT_TEXT_EDITING)
+        (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u text='%s' start=%d length=%d)",
+                           (uint)event->edit.timestamp, (uint)event->edit.windowID,
+                           event->edit.text, (int)event->edit.start, (int)event->edit.length);
+        break;
+
+        SDL_EVENT_CASE(SDL_EVENT_TEXT_EDITING_CANDIDATES)
+        (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u num_candidates=%d selected_candidate=%d)",
+                           (uint)event->edit_candidates.timestamp, (uint)event->edit_candidates.windowID,
+                           (int)event->edit_candidates.num_candidates, (int)event->edit_candidates.selected_candidate);
+        break;
+
+        SDL_EVENT_CASE(SDL_EVENT_TEXT_INPUT)
+        (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u text='%s')", (uint)event->text.timestamp, (uint)event->text.windowID, event->text.text);
+        break;
+
+#define PRINT_MOUSEDEV_EVENT(event) (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%u)", (uint)event->mdevice.timestamp, (uint)event->mdevice.which)
+        SDL_EVENT_CASE(SDL_EVENT_MOUSE_ADDED)
+        PRINT_MOUSEDEV_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_MOUSE_REMOVED)
+        PRINT_MOUSEDEV_EVENT(event);
+        break;
+#undef PRINT_MOUSEDEV_EVENT
+
+        SDL_EVENT_CASE(SDL_EVENT_MOUSE_MOTION)
+        (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u state=%u x=%g y=%g xrel=%g yrel=%g)",
+                           (uint)event->motion.timestamp, (uint)event->motion.windowID,
+                           (uint)event->motion.which, (uint)event->motion.state,
+                           event->motion.x, event->motion.y,
+                           event->motion.xrel, event->motion.yrel);
+        break;
+
+#define PRINT_MBUTTON_EVENT(event)                                                                                              \
+    (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u button=%u state=%s clicks=%u x=%g y=%g)", \
+                       (uint)event->button.timestamp, (uint)event->button.windowID,                                             \
+                       (uint)event->button.which, (uint)event->button.button,                                                   \
+                       event->button.down ? "pressed" : "released",                                                             \
+                       (uint)event->button.clicks, event->button.x, event->button.y)
+        SDL_EVENT_CASE(SDL_EVENT_MOUSE_BUTTON_DOWN)
+        PRINT_MBUTTON_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_MOUSE_BUTTON_UP)
+        PRINT_MBUTTON_EVENT(event);
+        break;
+#undef PRINT_MBUTTON_EVENT
+
+        SDL_EVENT_CASE(SDL_EVENT_MOUSE_WHEEL)
+        (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u x=%g y=%g integer_x=%d integer_y=%d direction=%s)",
+                           (uint)event->wheel.timestamp, (uint)event->wheel.windowID,
+                           (uint)event->wheel.which, event->wheel.x, event->wheel.y,
+                           (int)event->wheel.integer_x, (int)event->wheel.integer_y,
+                           event->wheel.direction == SDL_MOUSEWHEEL_NORMAL ? "normal" : "flipped");
+        break;
+
+        SDL_EVENT_CASE(SDL_EVENT_JOYSTICK_AXIS_MOTION)
+        (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%d axis=%u value=%d)",
+                           (uint)event->jaxis.timestamp, (int)event->jaxis.which,
+                           (uint)event->jaxis.axis, (int)event->jaxis.value);
+        break;
+
+        SDL_EVENT_CASE(SDL_EVENT_JOYSTICK_BALL_MOTION)
+        (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%d ball=%u xrel=%d yrel=%d)",
+                           (uint)event->jball.timestamp, (int)event->jball.which,
+                           (uint)event->jball.ball, (int)event->jball.xrel, (int)event->jball.yrel);
+        break;
+
+        SDL_EVENT_CASE(SDL_EVENT_JOYSTICK_HAT_MOTION)
+        (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%d hat=%u value=%u)",
+                           (uint)event->jhat.timestamp, (int)event->jhat.which,
+                           (uint)event->jhat.hat, (uint)event->jhat.value);
+        break;
+
+#define PRINT_JBUTTON_EVENT(event)                                                              \
+    (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%d button=%u state=%s)", \
+                       (uint)event->jbutton.timestamp, (int)event->jbutton.which,               \
+                       (uint)event->jbutton.button, event->jbutton.down ? "pressed" : "released")
+        SDL_EVENT_CASE(SDL_EVENT_JOYSTICK_BUTTON_DOWN)
+        PRINT_JBUTTON_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_JOYSTICK_BUTTON_UP)
+        PRINT_JBUTTON_EVENT(event);
+        break;
+#undef PRINT_JBUTTON_EVENT
+
+        SDL_EVENT_CASE(SDL_EVENT_JOYSTICK_BATTERY_UPDATED)
+        (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%d state=%u percent=%d)",
+                           (uint)event->jbattery.timestamp, (int)event->jbattery.which,
+                           event->jbattery.state, event->jbattery.percent);
+        break;
+
+#define PRINT_JOYDEV_EVENT(event) (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%d)", (uint)event->jdevice.timestamp, (int)event->jdevice.which)
+        SDL_EVENT_CASE(SDL_EVENT_JOYSTICK_ADDED)
+        PRINT_JOYDEV_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_JOYSTICK_REMOVED)
+        PRINT_JOYDEV_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_JOYSTICK_UPDATE_COMPLETE)
+        PRINT_JOYDEV_EVENT(event);
+        break;
+#undef PRINT_JOYDEV_EVENT
+
+        SDL_EVENT_CASE(SDL_EVENT_GAMEPAD_AXIS_MOTION)
+        (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%d axis=%u value=%d)",
+                           (uint)event->gaxis.timestamp, (int)event->gaxis.which,
+                           (uint)event->gaxis.axis, (int)event->gaxis.value);
+        break;
+
+#define PRINT_CBUTTON_EVENT(event)                                                              \
+    (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%d button=%u state=%s)", \
+                       (uint)event->gbutton.timestamp, (int)event->gbutton.which,               \
+                       (uint)event->gbutton.button, event->gbutton.down ? "pressed" : "released")
+        SDL_EVENT_CASE(SDL_EVENT_GAMEPAD_BUTTON_DOWN)
+        PRINT_CBUTTON_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_GAMEPAD_BUTTON_UP)
+        PRINT_CBUTTON_EVENT(event);
+        break;
+#undef PRINT_CBUTTON_EVENT
+
+#define PRINT_GAMEPADDEV_EVENT(event) (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%d)", (uint)event->gdevice.timestamp, (int)event->gdevice.which)
+        SDL_EVENT_CASE(SDL_EVENT_GAMEPAD_ADDED)
+        PRINT_GAMEPADDEV_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_GAMEPAD_REMOVED)
+        PRINT_GAMEPADDEV_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_GAMEPAD_REMAPPED)
+        PRINT_GAMEPADDEV_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_GAMEPAD_UPDATE_COMPLETE)
+        PRINT_GAMEPADDEV_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED)
+        PRINT_GAMEPADDEV_EVENT(event);
+        break;
+#undef PRINT_GAMEPADDEV_EVENT
+
+#define PRINT_CTOUCHPAD_EVENT(event)                                                                                     \
+    (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%d touchpad=%d finger=%d x=%f y=%f pressure=%f)", \
+                       (uint)event->gtouchpad.timestamp, (int)event->gtouchpad.which,                                    \
+                       (int)event->gtouchpad.touchpad, (int)event->gtouchpad.finger,                                     \
+                       event->gtouchpad.x, event->gtouchpad.y, event->gtouchpad.pressure)
+        SDL_EVENT_CASE(SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN)
+        PRINT_CTOUCHPAD_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_GAMEPAD_TOUCHPAD_UP)
+        PRINT_CTOUCHPAD_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION)
+        PRINT_CTOUCHPAD_EVENT(event);
+        break;
+#undef PRINT_CTOUCHPAD_EVENT
+
+        SDL_EVENT_CASE(SDL_EVENT_GAMEPAD_SENSOR_UPDATE)
+        (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%d sensor=%d data[0]=%f data[1]=%f data[2]=%f)",
+                           (uint)event->gsensor.timestamp, (int)event->gsensor.which, (int)event->gsensor.sensor,
+                           event->gsensor.data[0], event->gsensor.data[1], event->gsensor.data[2]);
+        break;
+
+#define PRINT_FINGER_EVENT(event)                                                                                                                      \
+    (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u touchid=%" SDL_PRIu64 " fingerid=%" SDL_PRIu64 " x=%f y=%f dx=%f dy=%f pressure=%f)", \
+                       (uint)event->tfinger.timestamp, event->tfinger.touchID,                                                              \
+                       event->tfinger.fingerID, event->tfinger.x, event->tfinger.y,                                                         \
+                       event->tfinger.dx, event->tfinger.dy, event->tfinger.pressure)
+        SDL_EVENT_CASE(SDL_EVENT_FINGER_DOWN)
+        PRINT_FINGER_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_FINGER_UP)
+        PRINT_FINGER_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_FINGER_CANCELED)
+        PRINT_FINGER_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_FINGER_MOTION)
+        PRINT_FINGER_EVENT(event);
+        break;
+#undef PRINT_FINGER_EVENT
+
+#define PRINT_PTOUCH_EVENT(event)                                                                             \
+    (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u pen_state=%u x=%g y=%g eraser=%s state=%s)", \
+                       (uint)event->ptouch.timestamp, (uint)event->ptouch.windowID, (uint)event->ptouch.which, (uint)event->ptouch.pen_state, event->ptouch.x, event->ptouch.y, \
+                       event->ptouch.eraser ? "yes" : "no", event->ptouch.down ? "down" : "up");
+        SDL_EVENT_CASE(SDL_EVENT_PEN_DOWN)
+        PRINT_PTOUCH_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_PEN_UP)
+        PRINT_PTOUCH_EVENT(event);
+        break;
+#undef PRINT_PTOUCH_EVENT
+
+#define PRINT_PPROXIMITY_EVENT(event)                                                                             \
+    (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u)", \
+                       (uint)event->pproximity.timestamp, (uint)event->pproximity.windowID, (uint)event->pproximity.which);
+        SDL_EVENT_CASE(SDL_EVENT_PEN_PROXIMITY_IN)
+        PRINT_PPROXIMITY_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_PEN_PROXIMITY_OUT)
+        PRINT_PPROXIMITY_EVENT(event);
+        break;
+#undef PRINT_PPROXIMITY_EVENT
+
+        SDL_EVENT_CASE(SDL_EVENT_PEN_AXIS)
+        (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u pen_state=%u x=%g y=%g axis=%s value=%g)",
+                           (uint)event->paxis.timestamp, (uint)event->paxis.windowID, (uint)event->paxis.which, (uint)event->paxis.pen_state, event->paxis.x, event->paxis.y,
+                           ((((int) event->paxis.axis) >= 0) && (event->paxis.axis < SDL_arraysize(pen_axisnames))) ? pen_axisnames[event->paxis.axis] : "[UNKNOWN]", event->paxis.value);
+        break;
+
+        SDL_EVENT_CASE(SDL_EVENT_PEN_MOTION)
+        (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u pen_state=%u x=%g y=%g)",
+                           (uint)event->pmotion.timestamp, (uint)event->pmotion.windowID, (uint)event->pmotion.which, (uint)event->pmotion.pen_state, event->pmotion.x, event->pmotion.y);
+        break;
+
+#define PRINT_PBUTTON_EVENT(event)                                                                                                               \
+    (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u windowid=%u which=%u pen_state=%u x=%g y=%g button=%u state=%s)", \
+                       (uint)event->pbutton.timestamp, (uint)event->pbutton.windowID, (uint)event->pbutton.which, (uint)event->pbutton.pen_state, event->pbutton.x, event->pbutton.y, \
+                       (uint)event->pbutton.button, event->pbutton.down ? "down" : "up");
+        SDL_EVENT_CASE(SDL_EVENT_PEN_BUTTON_DOWN)
+        PRINT_PBUTTON_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_PEN_BUTTON_UP)
+        PRINT_PBUTTON_EVENT(event);
+        break;
+#undef PRINT_PBUTTON_EVENT
+
+#define PRINT_DROP_EVENT(event) (void)SDL_snprintf(details, sizeof(details), " (data='%s' timestamp=%u windowid=%u x=%f y=%f)", event->drop.data, (uint)event->drop.timestamp, (uint)event->drop.windowID, event->drop.x, event->drop.y)
+        SDL_EVENT_CASE(SDL_EVENT_DROP_FILE)
+        PRINT_DROP_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_DROP_TEXT)
+        PRINT_DROP_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_DROP_BEGIN)
+        PRINT_DROP_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_DROP_COMPLETE)
+        PRINT_DROP_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_DROP_POSITION)
+        PRINT_DROP_EVENT(event);
+        break;
+#undef PRINT_DROP_EVENT
+
+#define PRINT_AUDIODEV_EVENT(event) (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%u recording=%s)", (uint)event->adevice.timestamp, (uint)event->adevice.which, event->adevice.recording ? "true" : "false")
+        SDL_EVENT_CASE(SDL_EVENT_AUDIO_DEVICE_ADDED)
+        PRINT_AUDIODEV_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_AUDIO_DEVICE_REMOVED)
+        PRINT_AUDIODEV_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED)
+        PRINT_AUDIODEV_EVENT(event);
+        break;
+#undef PRINT_AUDIODEV_EVENT
+
+#define PRINT_CAMERADEV_EVENT(event) (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%u)", (uint)event->cdevice.timestamp, (uint)event->cdevice.which)
+        SDL_EVENT_CASE(SDL_EVENT_CAMERA_DEVICE_ADDED)
+        PRINT_CAMERADEV_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_CAMERA_DEVICE_REMOVED)
+        PRINT_CAMERADEV_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_CAMERA_DEVICE_APPROVED)
+        PRINT_CAMERADEV_EVENT(event);
+        break;
+        SDL_EVENT_CASE(SDL_EVENT_CAMERA_DEVICE_DENIED)
+        PRINT_CAMERADEV_EVENT(event);
+        break;
+#undef PRINT_CAMERADEV_EVENT
+
+        SDL_EVENT_CASE(SDL_EVENT_SENSOR_UPDATE)
+        (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%d data[0]=%f data[1]=%f data[2]=%f data[3]=%f data[4]=%f data[5]=%f)",
+                           (uint)event->sensor.timestamp, (int)event->sensor.which,
+                           event->sensor.data[0], event->sensor.data[1], event->sensor.data[2],
+                           event->sensor.data[3], event->sensor.data[4], event->sensor.data[5]);
+        break;
+
+#undef SDL_EVENT_CASE
+
+    case SDL_EVENT_POLL_SENTINEL:
+        // No logging necessary for this one
+        break;
+
+    default:
+        if (!name[0]) {
+            if (event->type >= SDL_EVENT_USER) {
+                SDL_strlcpy(name, "USER", sizeof(name));
+            } else {
+                SDL_strlcpy(name, "UNKNOWN", sizeof(name));
+            }
+            (void)SDL_snprintf(details, sizeof(details), " 0x%x", (uint)event->type);
+        }
+        break;
+    }
+
+    if (name[0]) {
+        SDL_Log("SDL EVENT: %s%s", name, details);
+    }
+
+#undef uint
+}
+
+void SDL_StopEventLoop(void)
+{
+    const char *report = SDL_GetHint("SDL_EVENT_QUEUE_STATISTICS");
+    int i;
+    SDL_EventEntry *entry;
+
+    SDL_LockMutex(SDL_EventQ.lock);
+
+    SDL_EventQ.active = false;
+
+    if (report && SDL_atoi(report)) {
+        SDL_Log("SDL EVENT QUEUE: Maximum events in-flight: %d",
+                SDL_EventQ.max_events_seen);
+    }
+
+    // Clean out EventQ
+    for (entry = SDL_EventQ.head; entry;) {
+        SDL_EventEntry *next = entry->next;
+        SDL_TransferTemporaryMemoryFromEvent(entry);
+        SDL_free(entry);
+        entry = next;
+    }
+    for (entry = SDL_EventQ.free; entry;) {
+        SDL_EventEntry *next = entry->next;
+        SDL_free(entry);
+        entry = next;
+    }
+
+    SDL_SetAtomicInt(&SDL_EventQ.count, 0);
+    SDL_EventQ.max_events_seen = 0;
+    SDL_EventQ.head = NULL;
+    SDL_EventQ.tail = NULL;
+    SDL_EventQ.free = NULL;
+    SDL_SetAtomicInt(&SDL_sentinel_pending, 0);
+
+    // Clear disabled event state
+    for (i = 0; i < SDL_arraysize(SDL_disabled_events); ++i) {
+        SDL_free(SDL_disabled_events[i]);
+        SDL_disabled_events[i] = NULL;
+    }
+
+    SDL_QuitEventWatchList(&SDL_event_watchers);
+    //SDL_QuitWindowEventWatch();
+
+    SDL_Mutex *lock = NULL;
+    if (SDL_EventQ.lock) {
+        lock = SDL_EventQ.lock;
+        SDL_EventQ.lock = NULL;
+    }
+
+    SDL_UnlockMutex(lock);
+
+    if (lock) {
+        SDL_DestroyMutex(lock);
+    }
+}
+
+// This function (and associated calls) may be called more than once
+bool SDL_StartEventLoop(void)
+{
+    /* We'll leave the event queue alone, since we might have gotten
+       some important events at launch (like SDL_EVENT_DROP_FILE)
+
+       FIXME: Does this introduce any other bugs with events at startup?
+     */
+
+    // Create the lock and set ourselves active
+#ifndef SDL_THREADS_DISABLED
+    if (!SDL_EventQ.lock) {
+        SDL_EventQ.lock = SDL_CreateMutex();
+        if (SDL_EventQ.lock == NULL) {
+            return false;
+        }
+    }
+    SDL_LockMutex(SDL_EventQ.lock);
+
+    if (!SDL_InitEventWatchList(&SDL_event_watchers)) {
+        SDL_UnlockMutex(SDL_EventQ.lock);
+        return false;
+    }
+#endif // !SDL_THREADS_DISABLED
+
+    //SDL_InitWindowEventWatch();
+
+    SDL_EventQ.active = true;
+
+#ifndef SDL_THREADS_DISABLED
+    SDL_UnlockMutex(SDL_EventQ.lock);
+#endif
+    return true;
+}
+
+// Add an event to the event queue -- called with the queue locked
+static int SDL_AddEvent(SDL_Event *event)
+{
+    SDL_EventEntry *entry;
+    const int initial_count = SDL_GetAtomicInt(&SDL_EventQ.count);
+    int final_count;
+
+    if (initial_count >= SDL_MAX_QUEUED_EVENTS) {
+        SDL_SetError("Event queue is full (%d events)", initial_count);
+        return 0;
+    }
+
+    if (SDL_EventQ.free == NULL) {
+        entry = (SDL_EventEntry *)SDL_malloc(sizeof(*entry));
+        if (entry == NULL) {
+            return 0;
+        }
+    } else {
+        entry = SDL_EventQ.free;
+        SDL_EventQ.free = entry->next;
+    }
+
+    if (SDL_EventLoggingVerbosity > 0) {
+        SDL_LogEvent(event);
+    }
+
+    SDL_copyp(&entry->event, event);
+    if (event->type == SDL_EVENT_POLL_SENTINEL) {
+        SDL_AddAtomicInt(&SDL_sentinel_pending, 1);
+    }
+    entry->memory = NULL;
+    SDL_TransferTemporaryMemoryToEvent(entry);
+
+    if (SDL_EventQ.tail) {
+        SDL_EventQ.tail->next = entry;
+        entry->prev = SDL_EventQ.tail;
+        SDL_EventQ.tail = entry;
+        entry->next = NULL;
+    } else {
+        SDL_assert(!SDL_EventQ.head);
+        SDL_EventQ.head = entry;
+        SDL_EventQ.tail = entry;
+        entry->prev = NULL;
+        entry->next = NULL;
+    }
+
+    final_count = SDL_AddAtomicInt(&SDL_EventQ.count, 1) + 1;
+    if (final_count > SDL_EventQ.max_events_seen) {
+        SDL_EventQ.max_events_seen = final_count;
+    }
+
+    ++SDL_last_event_id;
+
+    return 1;
+}
+
+// Remove an event from the queue -- called with the queue locked
+static void SDL_CutEvent(SDL_EventEntry *entry)
+{
+    SDL_TransferTemporaryMemoryFromEvent(entry);
+
+    if (entry->prev) {
+        entry->prev->next = entry->next;
+    }
+    if (entry->next) {
+        entry->next->prev = entry->prev;
+    }
+
+    if (entry == SDL_EventQ.head) {
+        SDL_assert(entry->prev == NULL);
+        SDL_EventQ.head = entry->next;
+    }
+    if (entry == SDL_EventQ.tail) {
+        SDL_assert(entry->next == NULL);
+        SDL_EventQ.tail = entry->prev;
+    }
+
+    if (entry->event.type == SDL_EVENT_POLL_SENTINEL) {
+        SDL_AddAtomicInt(&SDL_sentinel_pending, -1);
+    }
+
+    entry->next = SDL_EventQ.free;
+    SDL_EventQ.free = entry;
+    SDL_assert(SDL_GetAtomicInt(&SDL_EventQ.count) > 0);
+    SDL_AddAtomicInt(&SDL_EventQ.count, -1);
+}
+
+static void SDL_SendWakeupEvent(void)
+{
+#ifdef SDL_PLATFORM_ANDROID
+    Android_SendLifecycleEvent(SDL_ANDROID_LIFECYCLE_WAKE);
+#endif
+}
+
+// Lock the event queue, take a peep at it, and unlock it
+static int SDL_PeepEventsInternal(SDL_Event *events, int numevents, SDL_EventAction action,
+                                  Uint32 minType, Uint32 maxType, bool include_sentinel)
+{
+    int i, used, sentinels_expected = 0;
+
+    // Lock the event queue
+    used = 0;
+
+    SDL_LockMutex(SDL_EventQ.lock);
+    {
+        // Don't look after we've quit
+        if (!SDL_EventQ.active) {
+            // We get a few spurious events at shutdown, so don't warn then
+            if (action == SDL_GETEVENT) {
+                SDL_SetError("The event system has been shut down");
+            }
+            SDL_UnlockMutex(SDL_EventQ.lock);
+            return -1;
+        }
+        if (action == SDL_ADDEVENT) {
+            if (!events) {
+                SDL_UnlockMutex(SDL_EventQ.lock);
+                return SDL_InvalidParamError("events");
+            }
+            for (i = 0; i < numevents; ++i) {
+                used += SDL_AddEvent(&events[i]);
+            }
+        } else {
+            SDL_EventEntry *entry, *next;
+            Uint32 type;
+
+            for (entry = SDL_EventQ.head; entry && (events == NULL || used < numevents); entry = next) {
+                next = entry->next;
+                type = entry->event.type;
+                if (minType <= type && type <= maxType) {
+                    if (events) {
+                        SDL_copyp(&events[used], &entry->event);
+
+                        if (action == SDL_GETEVENT) {
+                            SDL_CutEvent(entry);
+                        }
+                    }
+                    if (type == SDL_EVENT_POLL_SENTINEL) {
+                        // Special handling for the sentinel event
+                        if (!include_sentinel) {
+                            // Skip it, we don't want to include it
+                            continue;
+                        }
+                        if (events == NULL || action != SDL_GETEVENT) {
+                            ++sentinels_expected;
+                        }
+                        if (SDL_GetAtomicInt(&SDL_sentinel_pending) > sentinels_expected) {
+                            // Skip it, there's another one pending
+                            continue;
+                        }
+                    }
+                    ++used;
+                }
+            }
+        }
+    }
+    SDL_UnlockMutex(SDL_EventQ.lock);
+
+    if (used > 0 && action == SDL_ADDEVENT) {
+        SDL_SendWakeupEvent();
+    }
+
+    return used;
+}
+int SDL_PeepEvents(SDL_Event *events, int numevents, SDL_EventAction action,
+                   Uint32 minType, Uint32 maxType)
+{
+    return SDL_PeepEventsInternal(events, numevents, action, minType, maxType, false);
+}
+
+bool SDL_HasEvent(Uint32 type)
+{
+    return SDL_HasEvents(type, type);
+}
+
+bool SDL_HasEvents(Uint32 minType, Uint32 maxType)
+{
+    bool found = false;
+
+    SDL_LockMutex(SDL_EventQ.lock);
+    {
+        if (SDL_EventQ.active) {
+            for (SDL_EventEntry *entry = SDL_EventQ.head; entry; entry = entry->next) {
+                const Uint32 type = entry->event.type;
+                if (minType <= type && type <= maxType) {
+                    found = true;
+                    break;
+                }
+            }
+        }
+    }
+    SDL_UnlockMutex(SDL_EventQ.lock);
+
+    return found;
+}
+
+void SDL_FlushEvent(Uint32 type)
+{
+    SDL_FlushEvents(type, type);
+}
+
+void SDL_FlushEvents(Uint32 minType, Uint32 maxType)
+{
+    SDL_EventEntry *entry, *next;
+    Uint32 type;
+
+    // Make sure the events are current
+#if 0
+    /* Actually, we can't do this since we might be flushing while processing
+       a resize event, and calling this might trigger further resize events.
+    */
+    SDL_PumpEvents();
+#endif
+
+    // Lock the event queue
+    SDL_LockMutex(SDL_EventQ.lock);
+    {
+        // Don't look after we've quit
+        if (!SDL_EventQ.active) {
+            SDL_UnlockMutex(SDL_EventQ.lock);
+            return;
+        }
+        for (entry = SDL_EventQ.head; entry; entry = next) {
+            next = entry->next;
+            type = entry->event.type;
+            if (minType <= type && type <= maxType) {
+                SDL_CutEvent(entry);
+            }
+        }
+    }
+    SDL_UnlockMutex(SDL_EventQ.lock);
+}
+
+typedef enum
+{
+    SDL_MAIN_CALLBACK_WAITING,
+    SDL_MAIN_CALLBACK_COMPLETE,
+    SDL_MAIN_CALLBACK_CANCELED,
+} SDL_MainThreadCallbackState;
+
+typedef struct SDL_MainThreadCallbackEntry
+{
+    SDL_MainThreadCallback callback;
+    void *userdata;
+    SDL_AtomicInt state;
+    SDL_Semaphore *semaphore;
+    struct SDL_MainThreadCallbackEntry *next;
+} SDL_MainThreadCallbackEntry;
+
+static SDL_Mutex *SDL_main_callbacks_lock;
+static SDL_MainThreadCallbackEntry *SDL_main_callbacks_head;
+static SDL_MainThreadCallbackEntry *SDL_main_callbacks_tail;
+
+static SDL_MainThreadCallbackEntry *SDL_CreateMainThreadCallback(SDL_MainThreadCallback callback, void *userdata, bool wait_complete)
+{
+    SDL_MainThreadCallbackEntry *entry = (SDL_MainThreadCallbackEntry *)SDL_malloc(sizeof(*entry));
+    if (!entry) {
+        return NULL;
+    }
+
+    entry->callback = callback;
+    entry->userdata = userdata;
+    SDL_SetAtomicInt(&entry->state, SDL_MAIN_CALLBACK_WAITING);
+    if (wait_complete) {
+        entry->semaphore = SDL_CreateSemaphore(0);
+        if (!entry->semaphore) {
+            SDL_free(entry);
+            return NULL;
+        }
+    } else {
+        entry->semaphore = NULL;
+    }
+    entry->next = NULL;
+
+    return entry;
+}
+
+static void SDL_DestroyMainThreadCallback(SDL_MainThreadCallbackEntry *entry)
+{
+    if (entry->semaphore) {
+        SDL_DestroySemaphore(entry->semaphore);
+    }
+    SDL_free(entry);
+}
+
+static void SDL_InitMainThreadCallbacks(void)
+{
+    SDL_main_callbacks_lock = SDL_CreateMutex();
+    SDL_assert(SDL_main_callbacks_head == NULL &&
+               SDL_main_callbacks_tail == NULL);
+}
+
+static void SDL_QuitMainThreadCallbacks(void)
+{
+    SDL_MainThreadCallbackEntry *entry;
+
+    SDL_LockMutex(SDL_main_callbacks_lock);
+    {
+        entry = SDL_main_callbacks_head;
+        SDL_main_callbacks_head = NULL;
+        SDL_main_callbacks_tail = NULL;
+    }
+    SDL_UnlockMutex(SDL_main_callbacks_lock);
+
+    while (entry) {
+        SDL_MainThreadCallbackEntry *next = entry->next;
+
+        if (entry->semaphore) {
+            // Let the waiting thread know this is canceled
+            SDL_SetAtomicInt(&entry->state, SDL_MAIN_CALLBACK_CANCELED);
+            SDL_SignalSemaphore(entry->semaphore);
+        } else {
+            // Nobody's waiting for this, clean it up
+            SDL_DestroyMainThreadCallback(entry);
+        }
+        entry = next;
+    }
+
+    SDL_DestroyMutex(SDL_main_callbacks_lock);
+    SDL_main_callbacks_lock = NULL;
+}
+
+static void SDL_RunMainThreadCallbacks(void)
+{
+    SDL_MainThreadCallbackEntry *entry;
+
+    SDL_LockMutex(SDL_main_callbacks_lock);
+    {
+        entry = SDL_main_callbacks_head;
+        SDL_main_callbacks_head = NULL;
+        SDL_main_callbacks_tail = NULL;
+    }
+    SDL_UnlockMutex(SDL_main_callbacks_lock);
+
+    while (entry) {
+        SDL_MainThreadCallbackEntry *next = entry->next;
+
+        entry->callback(entry->userdata);
+
+        if (entry->semaphore) {
+            // Let the waiting thread know this is done
+            SDL_SetAtomicInt(&entry->state, SDL_MAIN_CALLBACK_COMPLETE);
+            SDL_SignalSemaphore(entry->semaphore);
+        } else {
+            // Nobody's waiting for this, clean it up
+            SDL_DestroyMainThreadCallback(entry);
+        }
+        entry = next;
+    }
+}
+
+bool SDL_RunOnMainThread(SDL_MainThreadCallback callback, void *userdata, bool wait_complete)
+{
+    if (SDL_IsMainThread() || !SDL_WasInit(SDL_INIT_EVENTS)) {
+        // No need to queue the callback
+        callback(userdata);
+        return true;
+    }
+
+    SDL_MainThreadCallbackEntry *entry = SDL_CreateMainThreadCallback(callback, userdata, wait_complete);
+    if (!entry) {
+        return false;
+    }
+
+    SDL_LockMutex(SDL_main_callbacks_lock);
+    {
+        if (SDL_main_callbacks_tail) {
+            SDL_main_callbacks_tail->next = entry;
+            SDL_main_callbacks_tail = entry;
+        } else {
+            SDL_main_callbacks_head = entry;
+            SDL_main_callbacks_tail = entry;
+        }
+    }
+    SDL_UnlockMutex(SDL_main_callbacks_lock);
+
+    // If the main thread is waiting for events, wake it up
+    SDL_SendWakeupEvent();
+
+    if (!wait_complete) {
+        // Queued for execution, wait not requested
+        return true;
+    }
+
+    SDL_WaitSemaphore(entry->semaphore);
+
+    switch (SDL_GetAtomicInt(&entry->state)) {
+    case SDL_MAIN_CALLBACK_COMPLETE:
+        // Execution complete!
+        SDL_DestroyMainThreadCallback(entry);
+        return true;
+
+    case SDL_MAIN_CALLBACK_CANCELED:
+        // The callback was canceled on the main thread
+        SDL_DestroyMainThreadCallback(entry);
+        return SDL_SetError("Callback canceled");
+
+    default:
+        // Probably hit a deadlock in the callback
+        // We can't destroy the entry as the semaphore will be signaled
+        // if it ever comes back, just leak it here.
+        return SDL_SetError("Callback timed out");
+    }
+}
+
+void SDL_PumpEventMaintenance(void)
+{
+#ifndef SDL_AUDIO_DISABLED
+    SDL_UpdateAudio();
+#endif
+
+#ifndef SDL_CAMERA_DISABLED
+    SDL_UpdateCamera();
+#endif
+
+#ifndef SDL_SENSOR_DISABLED
+    // Check for sensor state change
+    if (SDL_update_sensors) {
+        SDL_UpdateSensors();
+    }
+#endif
+
+#ifndef SDL_JOYSTICK_DISABLED
+    // Check for joystick state change
+    if (SDL_update_joysticks) {
+        SDL_UpdateJoysticks();
+    }
+#endif
+
+    //SDL_UpdateTrays();
+
+    //SDL_SendPendingSignalEvents(); // in case we had a signal handler fire, etc.
+}
+
+// Run the system dependent event loops
+static void SDL_PumpEventsInternal(bool push_sentinel)
+{
+    // Free any temporary memory from old events
+    SDL_FreeTemporaryMemory();
+
+    // Release any keys held down from last frame
+    //SDL_ReleaseAutoReleaseKeys();
+
+    // Run any pending main thread callbacks
+    SDL_RunMainThreadCallbacks();
+
+#ifdef SDL_PLATFORM_ANDROID
+    // Android event processing is independent of the video subsystem
+    Android_PumpEvents(0);
+#endif
+
+    SDL_PumpEventMaintenance();
+
+    if (push_sentinel && SDL_EventEnabled(SDL_EVENT_POLL_SENTINEL)) {
+        SDL_Event sentinel;
+
+        // Make sure we don't already have a sentinel in the queue, and add one to the end
+        if (SDL_GetAtomicInt(&SDL_sentinel_pending) > 0) {
+            SDL_PeepEventsInternal(&sentinel, 1, SDL_GETEVENT, SDL_EVENT_POLL_SENTINEL, SDL_EVENT_POLL_SENTINEL, true);
+        }
+
+        sentinel.type = SDL_EVENT_POLL_SENTINEL;
+        sentinel.common.timestamp = 0;
+        SDL_PushEvent(&sentinel);
+    }
+}
+
+void SDL_PumpEvents(void)
+{
+    SDL_PumpEventsInternal(false);
+}
+
+// Public functions
+
+bool SDL_PollEvent(SDL_Event *event)
+{
+    return SDL_WaitEventTimeoutNS(event, 0);
+}
+
+bool SDL_WaitEvent(SDL_Event *event)
+{
+    return SDL_WaitEventTimeoutNS(event, -1);
+}
+
+bool SDL_WaitEventTimeout(SDL_Event *event, Sint32 timeoutMS)
+{
+    Sint64 timeoutNS;
+
+    if (timeoutMS > 0) {
+        timeoutNS = SDL_MS_TO_NS(timeoutMS);
+    } else {
+        timeoutNS = timeoutMS;
+    }
+    return SDL_WaitEventTimeoutNS(event, timeoutNS);
+}
+
+bool SDL_WaitEventTimeoutNS(SDL_Event *event, Sint64 timeoutNS)
+{
+    Uint64 start, expiration;
+    bool include_sentinel = (timeoutNS == 0);
+    int result;
+
+    if (timeoutNS > 0) {
+        start = SDL_GetTicksNS();
+        expiration = start + timeoutNS;
+    } else {
+        start = 0;
+        expiration = 0;
+    }
+
+    // If there isn't a poll sentinel event pending, pump events and add one
+    if (SDL_GetAtomicInt(&SDL_sentinel_pending) == 0) {
+        SDL_PumpEventsInternal(true);
+    }
+
+    // First check for existing events
+    result = SDL_PeepEventsInternal(event, 1, SDL_GETEVENT, SDL_EVENT_FIRST, SDL_EVENT_LAST, include_sentinel);
+    if (result < 0) {
+        return false;
+    }
+    if (include_sentinel) {
+        if (event) {
+            if (event->type == SDL_EVENT_POLL_SENTINEL) {
+                // Reached the end of a poll cycle, and not willing to wait
+                return false;
+            }
+        } else {
+            // Need to peek the next event to check for sentinel
+            SDL_Event dummy;
+
+            if (SDL_PeepEventsInternal(&dummy, 1, SDL_PEEKEVENT, SDL_EVENT_FIRST, SDL_EVENT_LAST, true) &&
+                dummy.type == SDL_EVENT_POLL_SENTINEL) {
+                SDL_PeepEventsInternal(&dummy, 1, SDL_GETEVENT, SDL_EVENT_POLL_SENTINEL, SDL_EVENT_POLL_SENTINEL, true);
+                // Reached the end of a poll cycle, and not willing to wait
+                return false;
+            }
+        }
+    }
+    if (result == 0) {
+        if (timeoutNS == 0) {
+            // No events available, and not willing to wait
+            return false;
+        }
+    } else {
+        // Has existing events
+        return true;
+    }
+    // We should have completely handled timeoutNS == 0 above
+    SDL_assert(timeoutNS != 0);
+
+#ifdef SDL_PLATFORM_ANDROID
+    for (;;) {
+        if (SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_EVENT_FIRST, SDL_EVENT_LAST) > 0) {
+            return true;
+        }
+
+        Uint64 delay = -1;
+        if (timeoutNS > 0) {
+            Uint64 now = SDL_GetTicksNS();
+            if (now >= expiration) {
+                // Timeout expired and no events
+                return false;
+            }
+            delay = (expiration - now);
+        }
+        Android_PumpEvents(delay);
+    }
+#else
+    for (;;) {
+        SDL_PumpEventsInternal(true);
+
+        if (SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_EVENT_FIRST, SDL_EVENT_LAST) > 0) {
+            return true;
+        }
+
+        Uint64 delay = EVENT_POLL_INTERVAL_NS;
+        if (timeoutNS > 0) {
+            Uint64 now = SDL_GetTicksNS();
+            if (now >= expiration) {
+                // Timeout expired and no events
+                return false;
+            }
+            delay = SDL_min((expiration - now), delay);
+        }
+        SDL_DelayNS(delay);
+    }
+#endif // SDL_PLATFORM_ANDROID
+}
+
+static bool SDL_CallEventWatchers(SDL_Event *event)
+{
+    if (event->common.type == SDL_EVENT_POLL_SENTINEL) {
+        return true;
+    }
+
+    return SDL_DispatchEventWatchList(&SDL_event_watchers, event);
+}
+
+bool SDL_PushEvent(SDL_Event *event)
+{
+    if (!event->common.timestamp) {
+        event->common.timestamp = SDL_GetTicksNS();
+    }
+
+    if (!SDL_CallEventWatchers(event)) {
+        SDL_ClearError();
+        return false;
+    }
+
+    if (SDL_PeepEvents(event, 1, SDL_ADDEVENT, 0, 0) <= 0) {
+        return false;
+    }
+
+    return true;
+}
+
+void SDL_SetEventFilter(SDL_EventFilter filter, void *userdata)
+{
+    SDL_EventEntry *event, *next;
+    SDL_LockMutex(SDL_event_watchers.lock);
+    {
+        // Set filter and discard pending events
+        SDL_event_watchers.filter.callback = filter;
+        SDL_event_watchers.filter.userdata = userdata;
+        if (filter) {
+            // Cut all events not accepted by the filter
+            SDL_LockMutex(SDL_EventQ.lock);
+            {
+                for (event = SDL_EventQ.head; event; event = next) {
+                    next = event->next;
+                    if (!filter(userdata, &event->event)) {
+                        SDL_CutEvent(event);
+                    }
+                }
+            }
+            SDL_UnlockMutex(SDL_EventQ.lock);
+        }
+    }
+    SDL_UnlockMutex(SDL_event_watchers.lock);
+}
+
+bool SDL_GetEventFilter(SDL_EventFilter *filter, void **userdata)
+{
+    SDL_EventWatcher event_ok;
+
+    SDL_LockMutex(SDL_event_watchers.lock);
+    {
+        event_ok = SDL_event_watchers.filter;
+    }
+    SDL_UnlockMutex(SDL_event_watchers.lock);
+
+    if (filter) {
+        *filter = event_ok.callback;
+    }
+    if (userdata) {
+        *userdata = event_ok.userdata;
+    }
+    return event_ok.callback ? true : false;
+}
+
+bool SDL_AddEventWatch(SDL_EventFilter filter, void *userdata)
+{
+    return SDL_AddEventWatchList(&SDL_event_watchers, filter, userdata);
+}
+
+void SDL_RemoveEventWatch(SDL_EventFilter filter, void *userdata)
+{
+    SDL_RemoveEventWatchList(&SDL_event_watchers, filter, userdata);
+}
+
+void SDL_FilterEvents(SDL_EventFilter filter, void *userdata)
+{
+    SDL_LockMutex(SDL_EventQ.lock);
+    {
+        SDL_EventEntry *entry, *next;
+        for (entry = SDL_EventQ.head; entry; entry = next) {
+            next = entry->next;
+            if (!filter(userdata, &entry->event)) {
+                SDL_CutEvent(entry);
+            }
+        }
+    }
+    SDL_UnlockMutex(SDL_EventQ.lock);
+}
+
+void SDL_SetEventEnabled(Uint32 type, bool enabled)
+{
+    bool current_state;
+    Uint8 hi = ((type >> 8) & 0xff);
+    Uint8 lo = (type & 0xff);
+
+    if (SDL_disabled_events[hi] &&
+        (SDL_disabled_events[hi]->bits[lo / 32] & (1U << (lo & 31)))) {
+        current_state = false;
+    } else {
+        current_state = true;
+    }
+
+    if ((enabled != false) != current_state) {
+        if (enabled) {
+            SDL_assert(SDL_disabled_events[hi] != NULL);
+            SDL_disabled_events[hi]->bits[lo / 32] &= ~(1U << (lo & 31));
+
+            // Gamepad events depend on joystick events
+            switch (type) {
+            case SDL_EVENT_GAMEPAD_ADDED:
+                SDL_SetEventEnabled(SDL_EVENT_JOYSTICK_ADDED, true);
+                break;
+            case SDL_EVENT_GAMEPAD_REMOVED:
+                SDL_SetEventEnabled(SDL_EVENT_JOYSTICK_REMOVED, true);
+                break;
+            case SDL_EVENT_GAMEPAD_AXIS_MOTION:
+            case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
+            case SDL_EVENT_GAMEPAD_BUTTON_UP:
+                SDL_SetEventEnabled(SDL_EVENT_JOYSTICK_AXIS_MOTION, true);
+                SDL_SetEventEnabled(SDL_EVENT_JOYSTICK_HAT_MOTION, true);
+                SDL_SetEventEnabled(SDL_EVENT_JOYSTICK_BUTTON_DOWN, true);
+                SDL_SetEventEnabled(SDL_EVENT_JOYSTICK_BUTTON_UP, true);
+                break;
+            case SDL_EVENT_GAMEPAD_UPDATE_COMPLETE:
+                SDL_SetEventEnabled(SDL_EVENT_JOYSTICK_UPDATE_COMPLETE, true);
+                break;
+            default:
+                break;
+            }
+        } else {
+            // Disable this event type and discard pending events
+            if (!SDL_disabled_events[hi]) {
+                SDL_disabled_events[hi] = (SDL_DisabledEventBlock *)SDL_calloc(1, sizeof(SDL_DisabledEventBlock));
+            }
+            // Out of memory, nothing we can do...
+            if (SDL_disabled_events[hi]) {
+                SDL_disabled_events[hi]->bits[lo / 32] |= (1U << (lo & 31));
+                SDL_FlushEvent(type);
+            }
+        }
+
+        /* turn off drag'n'drop support if we've disabled the events.
+           This might change some UI details at the OS level. */
+        if (type == SDL_EVENT_DROP_FILE || type == SDL_EVENT_DROP_TEXT) {
+            //SDL_ToggleDragAndDropSupport();
+        }
+    }
+}
+
+bool SDL_EventEnabled(Uint32 type)
+{
+    Uint8 hi = ((type >> 8) & 0xff);
+    Uint8 lo = (type & 0xff);
+
+    if (SDL_disabled_events[hi] &&
+        (SDL_disabled_events[hi]->bits[lo / 32] & (1U << (lo & 31)))) {
+        return false;
+    } else {
+        return true;
+    }
+}
+
+Uint32 SDL_RegisterEvents(int numevents)
+{
+    Uint32 event_base = 0;
+
+    if (numevents > 0) {
+        int value = SDL_AddAtomicInt(&SDL_userevents, numevents);
+        if (value >= 0 && value <= (SDL_EVENT_LAST - SDL_EVENT_USER)) {
+            event_base = (Uint32)(SDL_EVENT_USER + value);
+        }
+    }
+    return event_base;
+}
+
+void SDL_SendAppEvent(SDL_EventType eventType)
+{
+    if (SDL_EventEnabled(eventType)) {
+        SDL_Event event;
+        event.type = eventType;
+        event.common.timestamp = 0;
+
+        switch (eventType) {
+        case SDL_EVENT_TERMINATING:
+        case SDL_EVENT_LOW_MEMORY:
+        case SDL_EVENT_WILL_ENTER_BACKGROUND:
+        case SDL_EVENT_DID_ENTER_BACKGROUND:
+        case SDL_EVENT_WILL_ENTER_FOREGROUND:
+        case SDL_EVENT_DID_ENTER_FOREGROUND:
+            // We won't actually queue this event, it needs to be handled in this call stack by an event watcher
+            if (SDL_EventLoggingVerbosity > 0) {
+                SDL_LogEvent(&event);
+            }
+            SDL_CallEventWatchers(&event);
+            break;
+        default:
+            SDL_PushEvent(&event);
+            break;
+        }
+    }
+}
+
+void SDL_SendKeymapChangedEvent(void)
+{
+    SDL_SendAppEvent(SDL_EVENT_KEYMAP_CHANGED);
+}
+
+void SDL_SendLocaleChangedEvent(void)
+{
+    SDL_SendAppEvent(SDL_EVENT_LOCALE_CHANGED);
+}
+
+void SDL_SendSystemThemeChangedEvent(void)
+{
+    SDL_SendAppEvent(SDL_EVENT_SYSTEM_THEME_CHANGED);
+}
+
+bool SDL_InitEvents(void)
+{
+#ifdef SDL_PLATFORM_ANDROID
+    Android_InitEvents();
+#endif
+#ifndef SDL_JOYSTICK_DISABLED
+    SDL_AddHintCallback(SDL_HINT_AUTO_UPDATE_JOYSTICKS, SDL_AutoUpdateJoysticksChanged, NULL);
+#endif
+#ifndef SDL_SENSOR_DISABLED
+    SDL_AddHintCallback(SDL_HINT_AUTO_UPDATE_SENSORS, SDL_AutoUpdateSensorsChanged, NULL);
+#endif
+    SDL_AddHintCallback(SDL_HINT_EVENT_LOGGING, SDL_EventLoggingChanged, NULL);
+    SDL_AddHintCallback(SDL_HINT_POLL_SENTINEL, SDL_PollSentinelChanged, NULL);
+    SDL_InitMainThreadCallbacks();
+    if (!SDL_StartEventLoop()) {
+        SDL_RemoveHintCallback(SDL_HINT_EVENT_LOGGING, SDL_EventLoggingChanged, NULL);
+        return false;
+    }
+
+    //SDL_InitQuit();
+
+    return true;
+}
+
+void SDL_QuitEvents(void)
+{
+    //SDL_QuitQuit();
+    SDL_StopEventLoop();
+    SDL_QuitMainThreadCallbacks();
+    SDL_RemoveHintCallback(SDL_HINT_POLL_SENTINEL, SDL_PollSentinelChanged, NULL);
+    SDL_RemoveHintCallback(SDL_HINT_EVENT_LOGGING, SDL_EventLoggingChanged, NULL);
+#ifndef SDL_JOYSTICK_DISABLED
+    SDL_RemoveHintCallback(SDL_HINT_AUTO_UPDATE_JOYSTICKS, SDL_AutoUpdateJoysticksChanged, NULL);
+#endif
+#ifndef SDL_SENSOR_DISABLED
+    SDL_RemoveHintCallback(SDL_HINT_AUTO_UPDATE_SENSORS, SDL_AutoUpdateSensorsChanged, NULL);
+#endif
+#ifdef SDL_PLATFORM_ANDROID
+    Android_QuitEvents();
+#endif
+}

+ 59 - 0
thirdparty/sdl/events/SDL_events_c.h

@@ -0,0 +1,59 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#ifndef SDL_events_c_h_
+#define SDL_events_c_h_
+
+#include "SDL_internal.h"
+
+// Useful functions and variables from SDL_events.c
+//#include "../video/SDL_sysvideo.h"
+
+#include "SDL_mouse_c.h"
+
+// Start and stop the event processing loop
+extern bool SDL_StartEventLoop(void);
+extern void SDL_StopEventLoop(void);
+extern void SDL_QuitInterrupt(void);
+
+extern void SDL_SendAppEvent(SDL_EventType eventType);
+extern void SDL_SendKeymapChangedEvent(void);
+extern void SDL_SendLocaleChangedEvent(void);
+extern void SDL_SendSystemThemeChangedEvent(void);
+
+extern void *SDL_AllocateTemporaryMemory(size_t size);
+extern const char *SDL_CreateTemporaryString(const char *string);
+extern void *SDL_ClaimTemporaryMemory(const void *mem);
+extern void SDL_FreeTemporaryMemory(void);
+
+extern void SDL_PumpEventMaintenance(void);
+
+extern void SDL_SendQuit(void);
+
+extern bool SDL_InitEvents(void);
+extern void SDL_QuitEvents(void);
+
+extern void SDL_SendPendingSignalEvents(void);
+
+extern bool SDL_InitQuit(void);
+extern void SDL_QuitQuit(void);
+
+#endif // SDL_events_c_h_

+ 143 - 0
thirdparty/sdl/events/SDL_eventwatch.c

@@ -0,0 +1,143 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#include "SDL_eventwatch_c.h"
+
+
+bool SDL_InitEventWatchList(SDL_EventWatchList *list)
+{
+    if (list->lock == NULL) {
+        list->lock = SDL_CreateMutex();
+        if (list->lock == NULL) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void SDL_QuitEventWatchList(SDL_EventWatchList *list)
+{
+    if (list->lock) {
+        SDL_DestroyMutex(list->lock);
+        list->lock = NULL;
+    }
+    if (list->watchers) {
+        SDL_free(list->watchers);
+        list->watchers = NULL;
+        list->count = 0;
+    }
+    SDL_zero(list->filter);
+}
+
+bool SDL_DispatchEventWatchList(SDL_EventWatchList *list, SDL_Event *event)
+{
+    SDL_EventWatcher *filter = &list->filter;
+
+    if (!filter->callback && list->count == 0) {
+        return true;
+    }
+
+    SDL_LockMutex(list->lock);
+    {
+        // Make sure we only dispatch the current watcher list
+        int i, count = list->count;
+
+        if (filter->callback && !filter->callback(filter->userdata, event)) {
+            SDL_UnlockMutex(list->lock);
+            return false;
+        }
+
+        list->dispatching = true;
+        for (i = 0; i < count; ++i) {
+            if (!list->watchers[i].removed) {
+                list->watchers[i].callback(list->watchers[i].userdata, event);
+            }
+        }
+        list->dispatching = false;
+
+        if (list->removed) {
+            for (i = list->count; i--;) {
+                if (list->watchers[i].removed) {
+                    --list->count;
+                    if (i < list->count) {
+                        SDL_memmove(&list->watchers[i], &list->watchers[i + 1], (list->count - i) * sizeof(list->watchers[i]));
+                    }
+                }
+            }
+            list->removed = false;
+        }
+    }
+    SDL_UnlockMutex(list->lock);
+
+    return true;
+}
+
+bool SDL_AddEventWatchList(SDL_EventWatchList *list, SDL_EventFilter filter, void *userdata)
+{
+    bool result = true;
+
+    SDL_LockMutex(list->lock);
+    {
+        SDL_EventWatcher *watchers;
+
+        watchers = (SDL_EventWatcher *)SDL_realloc(list->watchers, (list->count + 1) * sizeof(*watchers));
+        if (watchers) {
+            SDL_EventWatcher *watcher;
+
+            list->watchers = watchers;
+            watcher = &list->watchers[list->count];
+            watcher->callback = filter;
+            watcher->userdata = userdata;
+            watcher->removed = false;
+            ++list->count;
+        } else {
+            result = false;
+        }
+    }
+    SDL_UnlockMutex(list->lock);
+
+    return result;
+}
+
+void SDL_RemoveEventWatchList(SDL_EventWatchList *list, SDL_EventFilter filter, void *userdata)
+{
+    SDL_LockMutex(list->lock);
+    {
+        int i;
+
+        for (i = 0; i < list->count; ++i) {
+            if (list->watchers[i].callback == filter && list->watchers[i].userdata == userdata) {
+                if (list->dispatching) {
+                    list->watchers[i].removed = true;
+                    list->removed = true;
+                } else {
+                    --list->count;
+                    if (i < list->count) {
+                        SDL_memmove(&list->watchers[i], &list->watchers[i + 1], (list->count - i) * sizeof(list->watchers[i]));
+                    }
+                }
+                break;
+            }
+        }
+    }
+    SDL_UnlockMutex(list->lock);
+}

+ 45 - 0
thirdparty/sdl/events/SDL_eventwatch_c.h

@@ -0,0 +1,45 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+typedef struct SDL_EventWatcher
+{
+    SDL_EventFilter callback;
+    void *userdata;
+    bool removed;
+} SDL_EventWatcher;
+
+typedef struct SDL_EventWatchList
+{
+    SDL_Mutex *lock;
+    SDL_EventWatcher filter;
+    SDL_EventWatcher *watchers;
+    int count;
+    bool dispatching;
+    bool removed;
+} SDL_EventWatchList;
+
+
+extern bool SDL_InitEventWatchList(SDL_EventWatchList *list);
+extern void SDL_QuitEventWatchList(SDL_EventWatchList *list);
+extern bool SDL_DispatchEventWatchList(SDL_EventWatchList *list, SDL_Event *event);
+extern bool SDL_AddEventWatchList(SDL_EventWatchList *list, SDL_EventFilter filter, void *userdata);
+extern void SDL_RemoveEventWatchList(SDL_EventWatchList *list, SDL_EventFilter filter, void *userdata);

+ 215 - 0
thirdparty/sdl/events/SDL_mouse_c.h

@@ -0,0 +1,215 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#ifndef SDL_mouse_c_h_
+#define SDL_mouse_c_h_
+
+// Mouse events not associated with a specific input device
+#define SDL_GLOBAL_MOUSE_ID     0
+
+// The default mouse input device, for platforms that don't have multiple mice
+#define SDL_DEFAULT_MOUSE_ID    1
+
+typedef struct SDL_CursorData SDL_CursorData;
+
+struct SDL_Cursor
+{
+    struct SDL_Cursor *next;
+    SDL_CursorData *internal;
+};
+
+typedef struct
+{
+    Uint64 last_timestamp;
+    double click_motion_x;
+    double click_motion_y;
+    Uint8 click_count;
+} SDL_MouseClickState;
+
+typedef struct
+{
+    SDL_MouseID mouseID;
+    Uint32 buttonstate;
+
+    // Data for double-click tracking
+    int num_clickstates;
+    SDL_MouseClickState *clickstate;
+} SDL_MouseInputSource;
+
+typedef struct
+{
+    // Create a cursor from a surface
+    SDL_Cursor *(*CreateCursor)(SDL_Surface *surface, int hot_x, int hot_y);
+
+    // Create a system cursor
+    SDL_Cursor *(*CreateSystemCursor)(SDL_SystemCursor id);
+
+    // Show the specified cursor, or hide if cursor is NULL
+    bool (*ShowCursor)(SDL_Cursor *cursor);
+
+    // This is called when a mouse motion event occurs
+    bool (*MoveCursor)(SDL_Cursor *cursor);
+
+    // Free a window manager cursor
+    void (*FreeCursor)(SDL_Cursor *cursor);
+
+    // Warp the mouse to (x,y) within a window
+    bool (*WarpMouse)(SDL_Window *window, float x, float y);
+
+    // Warp the mouse to (x,y) in screen space
+    bool (*WarpMouseGlobal)(float x, float y);
+
+    // Set relative mode
+    bool (*SetRelativeMouseMode)(bool enabled);
+
+    // Set mouse capture
+    bool (*CaptureMouse)(SDL_Window *window);
+
+    // Get absolute mouse coordinates. (x) and (y) are never NULL and set to zero before call.
+    SDL_MouseButtonFlags (*GetGlobalMouseState)(float *x, float *y);
+
+    // Platform-specific system mouse transform
+    void (*ApplySystemScale)(void *internal, Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, float *x, float *y);
+    void *system_scale_data;
+
+    // integer mode data
+    Uint8 integer_mode_flags; // 1 to enable mouse quantization, 2 to enable wheel quantization
+    float integer_mode_residual_motion_x;
+    float integer_mode_residual_motion_y;
+
+    // Data common to all mice
+    SDL_Window *focus;
+    float x;
+    float y;
+    float x_accu;
+    float y_accu;
+    float last_x, last_y; // the last reported x and y coordinates
+    float residual_scroll_x;
+    float residual_scroll_y;
+    double click_motion_x;
+    double click_motion_y;
+    bool has_position;
+    bool relative_mode;
+    bool relative_mode_warp_motion;
+    bool relative_mode_cursor_visible;
+    bool relative_mode_center;
+    bool warp_emulation_hint;
+    bool warp_emulation_active;
+    bool warp_emulation_prohibited;
+    Uint64 last_center_warp_time_ns;
+    bool enable_normal_speed_scale;
+    float normal_speed_scale;
+    bool enable_relative_speed_scale;
+    float relative_speed_scale;
+    bool enable_relative_system_scale;
+    Uint32 double_click_time;
+    int double_click_radius;
+    bool touch_mouse_events;
+    bool mouse_touch_events;
+    bool pen_mouse_events;
+    bool pen_touch_events;
+    bool was_touch_mouse_events; // Was a touch-mouse event pending?
+    bool added_mouse_touch_device;  // did we SDL_AddTouch() a virtual touch device for the mouse?
+    bool added_pen_touch_device;  // did we SDL_AddTouch() a virtual touch device for pens?
+#ifdef SDL_PLATFORM_VITA
+    Uint8 vita_touch_mouse_device;
+#endif
+    bool auto_capture;
+    bool capture_desired;
+    SDL_Window *capture_window;
+
+    // Data for input source state
+    int num_sources;
+    SDL_MouseInputSource *sources;
+
+    SDL_Cursor *cursors;
+    SDL_Cursor *def_cursor;
+    SDL_Cursor *cur_cursor;
+    bool cursor_shown;
+
+    // Driver-dependent data.
+    void *internal;
+} SDL_Mouse;
+
+// Initialize the mouse subsystem, called before the main video driver is initialized
+extern bool SDL_PreInitMouse(void);
+
+// Finish initializing the mouse subsystem, called after the main video driver was initialized
+extern void SDL_PostInitMouse(void);
+
+// Return whether a device is actually a mouse
+extern bool SDL_IsMouse(Uint16 vendor, Uint16 product);
+
+// A mouse has been added to the system
+extern void SDL_AddMouse(SDL_MouseID mouseID, const char *name, bool send_event);
+
+// A mouse has been removed from the system
+extern void SDL_RemoveMouse(SDL_MouseID mouseID, bool send_event);
+
+// Get the mouse state structure
+extern SDL_Mouse *SDL_GetMouse(void);
+
+// Set the default mouse cursor
+extern void SDL_SetDefaultCursor(SDL_Cursor *cursor);
+
+// Get the preferred default system cursor
+extern SDL_SystemCursor SDL_GetDefaultSystemCursor(void);
+
+// Set the mouse focus window
+extern void SDL_SetMouseFocus(SDL_Window *window);
+
+// Update the mouse capture window
+extern bool SDL_UpdateMouseCapture(bool force_release);
+
+// Send a mouse motion event
+extern void SDL_SendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, bool relative, float x, float y);
+
+// Send a mouse button event
+extern void SDL_SendMouseButton(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, Uint8 button, bool down);
+
+// Send a mouse button event with a click count
+extern void SDL_SendMouseButtonClicks(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, Uint8 button, bool down, int clicks);
+
+// Send a mouse wheel event
+extern void SDL_SendMouseWheel(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseID, float x, float y, SDL_MouseWheelDirection direction);
+
+// Warp the mouse within the window, potentially overriding relative mode
+extern void SDL_PerformWarpMouseInWindow(SDL_Window *window, float x, float y, bool ignore_relative_mode);
+
+// Relative mouse mode
+extern bool SDL_SetRelativeMouseMode(bool enabled);
+extern bool SDL_GetRelativeMouseMode(void);
+extern void SDL_UpdateRelativeMouseMode(void);
+extern void SDL_DisableMouseWarpEmulation(void);
+
+// TODO RECONNECT: Set mouse state to "zero"
+#if 0
+extern void SDL_ResetMouse(void);
+#endif // 0
+
+// Check if mouse position is within window or captured by window
+extern bool SDL_MousePositionInWindow(SDL_Window *window, float x, float y);
+
+// Shutdown the mouse subsystem
+extern void SDL_QuitMouse(void);
+
+#endif // SDL_mouse_c_h_

+ 788 - 0
thirdparty/sdl/haptic/SDL_haptic.c

@@ -0,0 +1,788 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#include "SDL_syshaptic.h"
+#include "SDL_haptic_c.h"
+#include "../joystick/SDL_joystick_c.h" // For SDL_IsJoystickValid
+#include "../SDL_hints_c.h"
+
+typedef struct SDL_Haptic_VIDPID_Naxes {
+    Uint16 vid;
+    Uint16 pid;
+    Uint16 naxes;
+} SDL_Haptic_VIDPID_Naxes;
+
+static void SDL_Haptic_Load_Axes_List(SDL_Haptic_VIDPID_Naxes **entries, int *num_entries)
+{
+    SDL_Haptic_VIDPID_Naxes entry;
+    const char *spot;
+    int length = 0;
+
+    spot = SDL_GetHint(SDL_HINT_JOYSTICK_HAPTIC_AXES);
+    if (!spot)
+        return;
+
+    while (SDL_sscanf(spot, "0x%hx/0x%hx/%hu%n", &entry.vid, &entry.pid, &entry.naxes, &length) == 3) {
+        SDL_assert(length > 0);
+        spot += length;
+        length = 0;
+
+        if ((*num_entries % 8) == 0) {
+            int new_max = *num_entries + 8;
+            SDL_Haptic_VIDPID_Naxes *new_entries =
+                (SDL_Haptic_VIDPID_Naxes *)SDL_realloc(*entries, new_max * sizeof(**entries));
+
+            // Out of memory, go with what we have already
+            if (!new_entries)
+                break;
+
+            *entries = new_entries;
+        }
+        (*entries)[(*num_entries)++] = entry;
+
+        if (spot[0] == ',')
+            spot++;
+    }
+}
+
+// /* Return -1 if not found */
+static int SDL_Haptic_Naxes_List_Index(struct SDL_Haptic_VIDPID_Naxes *entries, int num_entries, Uint16 vid, Uint16 pid)
+{
+    if (!entries)
+        return -1;
+
+    int i;
+    for (i = 0; i < num_entries; ++i) {
+        if (entries[i].vid == vid && entries[i].pid == pid)
+            return i;
+    }
+
+    return -1;
+}
+
+// Check if device needs a custom number of naxes
+static int SDL_Haptic_Get_Naxes(Uint16 vid, Uint16 pid)
+{
+    int num_entries = 0, index = 0, naxes = -1;
+    SDL_Haptic_VIDPID_Naxes *naxes_list = NULL;
+
+    SDL_Haptic_Load_Axes_List(&naxes_list, &num_entries);
+    if (!num_entries || !naxes_list)
+        return -1;
+
+    // Perform "wildcard" pass
+    index = SDL_Haptic_Naxes_List_Index(naxes_list, num_entries, 0xffff, 0xffff);
+    if (index >= 0)
+        naxes = naxes_list[index].naxes;
+
+    index = SDL_Haptic_Naxes_List_Index(naxes_list, num_entries, vid, pid);
+    if (index >= 0)
+        naxes = naxes_list[index].naxes;
+
+    SDL_free(naxes_list);
+    return naxes;
+}
+
+static SDL_Haptic *SDL_haptics = NULL;
+
+#define CHECK_HAPTIC_MAGIC(haptic, result)                  \
+    if (!SDL_ObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC)) { \
+        SDL_InvalidParamError("haptic");                    \
+        return result;                                      \
+    }
+
+bool SDL_InitHaptics(void)
+{
+    return SDL_SYS_HapticInit();
+}
+
+static bool SDL_GetHapticIndex(SDL_HapticID instance_id, int *driver_index)
+{
+    int num_haptics, device_index;
+
+    if (instance_id > 0) {
+        num_haptics = SDL_SYS_NumHaptics();
+        for (device_index = 0; device_index < num_haptics; ++device_index) {
+            SDL_HapticID haptic_id = SDL_SYS_HapticInstanceID(device_index);
+            if (haptic_id == instance_id) {
+                *driver_index = device_index;
+                return true;
+            }
+        }
+    }
+
+    SDL_SetError("Haptic device %" SDL_PRIu32 " not found", instance_id);
+    return false;
+}
+
+SDL_HapticID *SDL_GetHaptics(int *count)
+{
+    int device_index;
+    int haptic_index = 0, num_haptics = 0;
+    SDL_HapticID *haptics;
+
+    num_haptics = SDL_SYS_NumHaptics();
+
+    haptics = (SDL_HapticID *)SDL_malloc((num_haptics + 1) * sizeof(*haptics));
+    if (haptics) {
+        if (count) {
+            *count = num_haptics;
+        }
+
+        for (device_index = 0; device_index < num_haptics; ++device_index) {
+            haptics[haptic_index] = SDL_SYS_HapticInstanceID(device_index);
+            SDL_assert(haptics[haptic_index] > 0);
+            ++haptic_index;
+        }
+        haptics[haptic_index] = 0;
+    } else {
+        if (count) {
+            *count = 0;
+        }
+    }
+
+    return haptics;
+}
+
+const char *SDL_GetHapticNameForID(SDL_HapticID instance_id)
+{
+    int device_index;
+    const char *name = NULL;
+
+    if (SDL_GetHapticIndex(instance_id, &device_index)) {
+        name = SDL_GetPersistentString(SDL_SYS_HapticName(device_index));
+    }
+    return name;
+}
+
+SDL_Haptic *SDL_OpenHaptic(SDL_HapticID instance_id)
+{
+    SDL_Haptic *haptic;
+    SDL_Haptic *hapticlist;
+    const char *name;
+    int device_index = 0;
+
+    if (!SDL_GetHapticIndex(instance_id, &device_index)) {
+        return NULL;
+    }
+
+    hapticlist = SDL_haptics;
+    /* If the haptic device is already open, return it
+     * it is important that we have a single haptic device for each instance id
+     */
+    while (hapticlist) {
+        if (instance_id == hapticlist->instance_id) {
+            haptic = hapticlist;
+            ++haptic->ref_count;
+            return haptic;
+        }
+        hapticlist = hapticlist->next;
+    }
+
+    // Create the haptic device
+    haptic = (SDL_Haptic *)SDL_calloc(1, sizeof(*haptic));
+    if (!haptic) {
+        return NULL;
+    }
+
+    // Initialize the haptic device
+    SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, true);
+    haptic->instance_id = instance_id;
+    haptic->rumble_id = -1;
+    if (!SDL_SYS_HapticOpen(haptic)) {
+        SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, false);
+        SDL_free(haptic);
+        return NULL;
+    }
+
+    if (!haptic->name) {
+        name = SDL_SYS_HapticName(device_index);
+        if (name) {
+            haptic->name = SDL_strdup(name);
+        }
+    }
+
+    // Add haptic to list
+    ++haptic->ref_count;
+    // Link the haptic in the list
+    haptic->next = SDL_haptics;
+    SDL_haptics = haptic;
+
+    // Disable autocenter and set gain to max.
+    if (haptic->supported & SDL_HAPTIC_GAIN) {
+        SDL_SetHapticGain(haptic, 100);
+    }
+    if (haptic->supported & SDL_HAPTIC_AUTOCENTER) {
+        SDL_SetHapticAutocenter(haptic, 0);
+    }
+
+    return haptic;
+}
+
+SDL_Haptic *SDL_GetHapticFromID(SDL_HapticID instance_id)
+{
+    SDL_Haptic *haptic;
+
+    for (haptic = SDL_haptics; haptic; haptic = haptic->next) {
+        if (instance_id == haptic->instance_id) {
+            break;
+        }
+    }
+    return haptic;
+}
+
+SDL_HapticID SDL_GetHapticID(SDL_Haptic *haptic)
+{
+    CHECK_HAPTIC_MAGIC(haptic, 0);
+
+    return haptic->instance_id;
+}
+
+const char *SDL_GetHapticName(SDL_Haptic *haptic)
+{
+    CHECK_HAPTIC_MAGIC(haptic, NULL);
+
+    return SDL_GetPersistentString(haptic->name);
+}
+
+bool SDL_IsMouseHaptic(void)
+{
+    if (SDL_SYS_HapticMouse() < 0) {
+        return false;
+    }
+    return true;
+}
+
+SDL_Haptic *SDL_OpenHapticFromMouse(void)
+{
+    int device_index;
+
+    device_index = SDL_SYS_HapticMouse();
+
+    if (device_index < 0) {
+        SDL_SetError("Haptic: Mouse isn't a haptic device.");
+        return NULL;
+    }
+
+    return SDL_OpenHaptic(device_index);
+}
+
+bool SDL_IsJoystickHaptic(SDL_Joystick *joystick)
+{
+    bool result = false;
+
+    SDL_LockJoysticks();
+    {
+        // Must be a valid joystick
+        if (SDL_IsJoystickValid(joystick) &&
+            !SDL_IsGamepad(SDL_GetJoystickID(joystick))) {
+            result = SDL_SYS_JoystickIsHaptic(joystick);
+        }
+    }
+    SDL_UnlockJoysticks();
+
+    return result;
+}
+
+SDL_Haptic *SDL_OpenHapticFromJoystick(SDL_Joystick *joystick)
+{
+    SDL_Haptic *haptic;
+    SDL_Haptic *hapticlist;
+
+    SDL_LockJoysticks();
+    {
+        // Must be a valid joystick
+        if (!SDL_IsJoystickValid(joystick)) {
+            SDL_SetError("Haptic: Joystick isn't valid.");
+            SDL_UnlockJoysticks();
+            return NULL;
+        }
+
+        // Joystick must be haptic
+        if (SDL_IsGamepad(SDL_GetJoystickID(joystick)) ||
+            !SDL_SYS_JoystickIsHaptic(joystick)) {
+            SDL_SetError("Haptic: Joystick isn't a haptic device.");
+            SDL_UnlockJoysticks();
+            return NULL;
+        }
+
+        hapticlist = SDL_haptics;
+        // Check to see if joystick's haptic is already open
+        while (hapticlist) {
+            if (SDL_SYS_JoystickSameHaptic(hapticlist, joystick)) {
+                haptic = hapticlist;
+                ++haptic->ref_count;
+                SDL_UnlockJoysticks();
+                return haptic;
+            }
+            hapticlist = hapticlist->next;
+        }
+
+        // Create the haptic device
+        haptic = (SDL_Haptic *)SDL_calloc(1, sizeof(*haptic));
+        if (!haptic) {
+            SDL_UnlockJoysticks();
+            return NULL;
+        }
+
+        /* Initialize the haptic device
+         * This function should fill in the instance ID and name.
+         */
+        SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, true);
+        haptic->rumble_id = -1;
+        if (!SDL_SYS_HapticOpenFromJoystick(haptic, joystick)) {
+            SDL_SetError("Haptic: SDL_SYS_HapticOpenFromJoystick failed.");
+            SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, false);
+            SDL_free(haptic);
+            SDL_UnlockJoysticks();
+            return NULL;
+        }
+        SDL_assert(haptic->instance_id != 0);
+    }
+    SDL_UnlockJoysticks();
+
+    // Check if custom number of haptic axes was defined
+    Uint16 vid = SDL_GetJoystickVendor(joystick);
+    Uint16 pid = SDL_GetJoystickProduct(joystick);
+    int general_axes = SDL_GetNumJoystickAxes(joystick);
+
+    int naxes = SDL_Haptic_Get_Naxes(vid, pid);
+    if (naxes > 0)
+        haptic->naxes = naxes;
+
+    // Limit to the actual number of axes found on the device
+    if (general_axes >= 0 && naxes > general_axes)
+        haptic->naxes = general_axes;
+
+    // Add haptic to list
+    ++haptic->ref_count;
+    // Link the haptic in the list
+    haptic->next = SDL_haptics;
+    SDL_haptics = haptic;
+
+    return haptic;
+}
+
+void SDL_CloseHaptic(SDL_Haptic *haptic)
+{
+    int i;
+    SDL_Haptic *hapticlist;
+    SDL_Haptic *hapticlistprev;
+
+    CHECK_HAPTIC_MAGIC(haptic,);
+
+    // Check if it's still in use
+    if (--haptic->ref_count > 0) {
+        return;
+    }
+
+    // Close it, properly removing effects if needed
+    for (i = 0; i < haptic->neffects; i++) {
+        if (haptic->effects[i].hweffect != NULL) {
+            SDL_DestroyHapticEffect(haptic, i);
+        }
+    }
+    SDL_SYS_HapticClose(haptic);
+    SDL_SetObjectValid(haptic, SDL_OBJECT_TYPE_HAPTIC, false);
+
+    // Remove from the list
+    hapticlist = SDL_haptics;
+    hapticlistprev = NULL;
+    while (hapticlist) {
+        if (haptic == hapticlist) {
+            if (hapticlistprev) {
+                // unlink this entry
+                hapticlistprev->next = hapticlist->next;
+            } else {
+                SDL_haptics = haptic->next;
+            }
+
+            break;
+        }
+        hapticlistprev = hapticlist;
+        hapticlist = hapticlist->next;
+    }
+
+    // Free the data associated with this device
+    SDL_free(haptic->name);
+    SDL_free(haptic);
+}
+
+void SDL_QuitHaptics(void)
+{
+    while (SDL_haptics) {
+        SDL_CloseHaptic(SDL_haptics);
+    }
+
+    SDL_SYS_HapticQuit();
+}
+
+int SDL_GetMaxHapticEffects(SDL_Haptic *haptic)
+{
+    CHECK_HAPTIC_MAGIC(haptic, -1);
+
+    return haptic->neffects;
+}
+
+int SDL_GetMaxHapticEffectsPlaying(SDL_Haptic *haptic)
+{
+    CHECK_HAPTIC_MAGIC(haptic, -1);
+
+    return haptic->nplaying;
+}
+
+Uint32 SDL_GetHapticFeatures(SDL_Haptic *haptic)
+{
+    CHECK_HAPTIC_MAGIC(haptic, 0);
+
+    return haptic->supported;
+}
+
+int SDL_GetNumHapticAxes(SDL_Haptic *haptic)
+{
+    CHECK_HAPTIC_MAGIC(haptic, -1);
+
+    return haptic->naxes;
+}
+
+bool SDL_HapticEffectSupported(SDL_Haptic *haptic, const SDL_HapticEffect *effect)
+{
+    CHECK_HAPTIC_MAGIC(haptic, false);
+
+    if (!effect) {
+        return false;
+    }
+
+    if ((haptic->supported & effect->type) != 0) {
+        return true;
+    }
+    return false;
+}
+
+int SDL_CreateHapticEffect(SDL_Haptic *haptic, const SDL_HapticEffect *effect)
+{
+    int i;
+
+    CHECK_HAPTIC_MAGIC(haptic, -1);
+
+    if (!effect) {
+        SDL_InvalidParamError("effect");
+        return -1;
+    }
+
+    // Check to see if effect is supported
+    if (SDL_HapticEffectSupported(haptic, effect) == false) {
+        SDL_SetError("Haptic: Effect not supported by haptic device.");
+        return -1;
+    }
+
+    // See if there's a free slot
+    for (i = 0; i < haptic->neffects; i++) {
+        if (haptic->effects[i].hweffect == NULL) {
+
+            // Now let the backend create the real effect
+            if (!SDL_SYS_HapticNewEffect(haptic, &haptic->effects[i], effect)) {
+                return -1; // Backend failed to create effect
+            }
+
+            SDL_memcpy(&haptic->effects[i].effect, effect,
+                       sizeof(SDL_HapticEffect));
+            return i;
+        }
+    }
+
+    SDL_SetError("Haptic: Device has no free space left.");
+    return -1;
+}
+
+static bool ValidEffect(SDL_Haptic *haptic, int effect)
+{
+    if ((effect < 0) || (effect >= haptic->neffects)) {
+        SDL_SetError("Haptic: Invalid effect identifier.");
+        return false;
+    }
+    return true;
+}
+
+bool SDL_UpdateHapticEffect(SDL_Haptic *haptic, int effect, const SDL_HapticEffect *data)
+{
+    CHECK_HAPTIC_MAGIC(haptic, false);
+
+    if (!ValidEffect(haptic, effect)) {
+        return false;
+    }
+
+    if (!data) {
+        return SDL_InvalidParamError("data");
+    }
+
+    // Can't change type dynamically.
+    if (data->type != haptic->effects[effect].effect.type) {
+        return SDL_SetError("Haptic: Updating effect type is illegal.");
+    }
+
+    // Updates the effect
+    if (!SDL_SYS_HapticUpdateEffect(haptic, &haptic->effects[effect], data)) {
+        return false;
+    }
+
+    SDL_memcpy(&haptic->effects[effect].effect, data,
+               sizeof(SDL_HapticEffect));
+    return true;
+}
+
+bool SDL_RunHapticEffect(SDL_Haptic *haptic, int effect, Uint32 iterations)
+{
+    CHECK_HAPTIC_MAGIC(haptic, false);
+
+    if (!ValidEffect(haptic, effect)) {
+        return false;
+    }
+
+    // Run the effect
+    if (!SDL_SYS_HapticRunEffect(haptic, &haptic->effects[effect], iterations)) {
+        return false;
+    }
+
+    return true;
+}
+
+bool SDL_StopHapticEffect(SDL_Haptic *haptic, int effect)
+{
+    CHECK_HAPTIC_MAGIC(haptic, false);
+
+    if (!ValidEffect(haptic, effect)) {
+        return false;
+    }
+
+    // Stop the effect
+    if (!SDL_SYS_HapticStopEffect(haptic, &haptic->effects[effect])) {
+        return false;
+    }
+
+    return true;
+}
+
+void SDL_DestroyHapticEffect(SDL_Haptic *haptic, int effect)
+{
+    CHECK_HAPTIC_MAGIC(haptic,);
+
+    if (!ValidEffect(haptic, effect)) {
+        return;
+    }
+
+    // Not allocated
+    if (haptic->effects[effect].hweffect == NULL) {
+        return;
+    }
+
+    SDL_SYS_HapticDestroyEffect(haptic, &haptic->effects[effect]);
+}
+
+bool SDL_GetHapticEffectStatus(SDL_Haptic *haptic, int effect)
+{
+    CHECK_HAPTIC_MAGIC(haptic, false);
+
+    if (!ValidEffect(haptic, effect)) {
+        return false;
+    }
+
+    if (!(haptic->supported & SDL_HAPTIC_STATUS)) {
+        return SDL_SetError("Haptic: Device does not support status queries.");
+    }
+
+    SDL_ClearError();
+
+    return (SDL_SYS_HapticGetEffectStatus(haptic, &haptic->effects[effect]) > 0);
+}
+
+bool SDL_SetHapticGain(SDL_Haptic *haptic, int gain)
+{
+    const char *env;
+    int real_gain, max_gain;
+
+    CHECK_HAPTIC_MAGIC(haptic, false);
+
+    if (!(haptic->supported & SDL_HAPTIC_GAIN)) {
+        return SDL_SetError("Haptic: Device does not support setting gain.");
+    }
+
+    if ((gain < 0) || (gain > 100)) {
+        return SDL_SetError("Haptic: Gain must be between 0 and 100.");
+    }
+
+    // The user can use an environment variable to override the max gain.
+    env = SDL_getenv("SDL_HAPTIC_GAIN_MAX");
+    if (env) {
+        max_gain = SDL_atoi(env);
+
+        // Check for sanity.
+        if (max_gain < 0) {
+            max_gain = 0;
+        } else if (max_gain > 100) {
+            max_gain = 100;
+        }
+
+        // We'll scale it linearly with SDL_HAPTIC_GAIN_MAX
+        real_gain = (gain * max_gain) / 100;
+    } else {
+        real_gain = gain;
+    }
+
+    return SDL_SYS_HapticSetGain(haptic, real_gain);
+}
+
+bool SDL_SetHapticAutocenter(SDL_Haptic *haptic, int autocenter)
+{
+    CHECK_HAPTIC_MAGIC(haptic, false);
+
+    if (!(haptic->supported & SDL_HAPTIC_AUTOCENTER)) {
+        return SDL_SetError("Haptic: Device does not support setting autocenter.");
+    }
+
+    if ((autocenter < 0) || (autocenter > 100)) {
+        return SDL_SetError("Haptic: Autocenter must be between 0 and 100.");
+    }
+
+    return SDL_SYS_HapticSetAutocenter(haptic, autocenter);
+}
+
+bool SDL_PauseHaptic(SDL_Haptic *haptic)
+{
+    CHECK_HAPTIC_MAGIC(haptic, false);
+
+    if (!(haptic->supported & SDL_HAPTIC_PAUSE)) {
+        return SDL_SetError("Haptic: Device does not support setting pausing.");
+    }
+
+    return SDL_SYS_HapticPause(haptic);
+}
+
+bool SDL_ResumeHaptic(SDL_Haptic *haptic)
+{
+    CHECK_HAPTIC_MAGIC(haptic, false);
+
+    if (!(haptic->supported & SDL_HAPTIC_PAUSE)) {
+        return true; // Not going to be paused, so we pretend it's unpaused.
+    }
+
+    return SDL_SYS_HapticResume(haptic);
+}
+
+bool SDL_StopHapticEffects(SDL_Haptic *haptic)
+{
+    CHECK_HAPTIC_MAGIC(haptic, false);
+
+    return SDL_SYS_HapticStopAll(haptic);
+}
+
+bool SDL_HapticRumbleSupported(SDL_Haptic *haptic)
+{
+    CHECK_HAPTIC_MAGIC(haptic, false);
+
+    // Most things can use SINE, but XInput only has LEFTRIGHT.
+    return (haptic->supported & (SDL_HAPTIC_SINE | SDL_HAPTIC_LEFTRIGHT)) != 0;
+}
+
+bool SDL_InitHapticRumble(SDL_Haptic *haptic)
+{
+    SDL_HapticEffect *efx = &haptic->rumble_effect;
+
+    CHECK_HAPTIC_MAGIC(haptic, false);
+
+    // Already allocated.
+    if (haptic->rumble_id >= 0) {
+        return true;
+    }
+
+    SDL_zerop(efx);
+    if (haptic->supported & SDL_HAPTIC_SINE) {
+        efx->type = SDL_HAPTIC_SINE;
+        efx->periodic.direction.type = SDL_HAPTIC_CARTESIAN;
+        efx->periodic.period = 1000;
+        efx->periodic.magnitude = 0x4000;
+        efx->periodic.length = 5000;
+        efx->periodic.attack_length = 0;
+        efx->periodic.fade_length = 0;
+    } else if (haptic->supported & SDL_HAPTIC_LEFTRIGHT) { // XInput?
+        efx->type = SDL_HAPTIC_LEFTRIGHT;
+        efx->leftright.length = 5000;
+        efx->leftright.large_magnitude = 0x4000;
+        efx->leftright.small_magnitude = 0x4000;
+    } else {
+        return SDL_SetError("Device doesn't support rumble");
+    }
+
+    haptic->rumble_id = SDL_CreateHapticEffect(haptic, &haptic->rumble_effect);
+    if (haptic->rumble_id >= 0) {
+        return true;
+    }
+    return false;
+}
+
+bool SDL_PlayHapticRumble(SDL_Haptic *haptic, float strength, Uint32 length)
+{
+    SDL_HapticEffect *efx;
+    Sint16 magnitude;
+
+    CHECK_HAPTIC_MAGIC(haptic, false);
+
+    if (haptic->rumble_id < 0) {
+        return SDL_SetError("Haptic: Rumble effect not initialized on haptic device");
+    }
+
+    // Clamp strength.
+    if (strength > 1.0f) {
+        strength = 1.0f;
+    } else if (strength < 0.0f) {
+        strength = 0.0f;
+    }
+    magnitude = (Sint16)(32767.0f * strength);
+
+    efx = &haptic->rumble_effect;
+    if (efx->type == SDL_HAPTIC_SINE) {
+        efx->periodic.magnitude = magnitude;
+        efx->periodic.length = length;
+    } else if (efx->type == SDL_HAPTIC_LEFTRIGHT) {
+        efx->leftright.small_magnitude = efx->leftright.large_magnitude = magnitude;
+        efx->leftright.length = length;
+    } else {
+        SDL_assert(!"This should have been caught elsewhere");
+    }
+
+    if (!SDL_UpdateHapticEffect(haptic, haptic->rumble_id, &haptic->rumble_effect)) {
+        return false;
+    }
+
+    return SDL_RunHapticEffect(haptic, haptic->rumble_id, 1);
+}
+
+bool SDL_StopHapticRumble(SDL_Haptic *haptic)
+{
+    CHECK_HAPTIC_MAGIC(haptic, false);
+
+    if (haptic->rumble_id < 0) {
+        return SDL_SetError("Haptic: Rumble effect not initialized on haptic device");
+    }
+
+    return SDL_StopHapticEffect(haptic, haptic->rumble_id);
+}

+ 28 - 0
thirdparty/sdl/haptic/SDL_haptic_c.h

@@ -0,0 +1,28 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#ifndef SDL_haptic_c_h_
+#define SDL_haptic_c_h_
+
+extern bool SDL_InitHaptics(void);
+extern void SDL_QuitHaptics(void);
+
+#endif // SDL_haptic_c_h_

+ 194 - 0
thirdparty/sdl/haptic/SDL_syshaptic.h

@@ -0,0 +1,194 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "SDL_internal.h"
+
+#ifndef SDL_syshaptic_h_
+#define SDL_syshaptic_h_
+
+// Set up for C function definitions, even when using C++
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct haptic_effect
+{
+    SDL_HapticEffect effect;          // The current event
+    struct haptic_hweffect *hweffect; // The hardware behind the event
+};
+
+/*
+ * The real SDL_Haptic struct.
+ */
+struct SDL_Haptic
+{
+    SDL_HapticID instance_id;       // Device instance, monotonically increasing from 0
+    char *name;                     // Device name - system dependent
+
+    struct haptic_effect *effects;  // Allocated effects
+    int neffects;                   // Maximum amount of effects
+    int nplaying;                   // Maximum amount of effects to play at the same time
+    Uint32 supported;               // Supported effects and features
+    int naxes;                      // Number of axes on the device.
+
+    struct haptic_hwdata *hwdata;   // Driver dependent
+    int ref_count;                  // Count for multiple opens
+
+    int rumble_id;                  // ID of rumble effect for simple rumble API.
+    SDL_HapticEffect rumble_effect; // Rumble effect.
+    struct SDL_Haptic *next;        // pointer to next haptic we have allocated
+};
+
+/*
+ * Scans the system for haptic devices.
+ *
+ * Returns number of devices on success, -1 on error.
+ */
+extern bool SDL_SYS_HapticInit(void);
+
+// Function to return the number of haptic devices plugged in right now
+extern int SDL_SYS_NumHaptics(void);
+
+/*
+ * Gets the instance ID of the haptic device
+ */
+extern SDL_HapticID SDL_SYS_HapticInstanceID(int index);
+
+/*
+ * Gets the device dependent name of the haptic device
+ */
+extern const char *SDL_SYS_HapticName(int index);
+
+/*
+ * Opens the haptic device for usage.  The haptic device should have
+ * the index value set previously.
+ */
+extern bool SDL_SYS_HapticOpen(SDL_Haptic *haptic);
+
+/*
+ * Returns the index of the haptic core pointer or -1 if none is found.
+ */
+extern int SDL_SYS_HapticMouse(void);
+
+/*
+ * Checks to see if the joystick has haptic capabilities.
+ */
+extern bool SDL_SYS_JoystickIsHaptic(SDL_Joystick *joystick);
+
+/*
+ * Opens the haptic device for usage using the same device as
+ * the joystick.
+ */
+extern bool SDL_SYS_HapticOpenFromJoystick(SDL_Haptic *haptic,
+                                          SDL_Joystick *joystick);
+/*
+ * Checks to see if haptic device and joystick device are the same.
+ *
+ * Returns true if they are the same, false if they aren't.
+ */
+extern bool SDL_SYS_JoystickSameHaptic(SDL_Haptic *haptic,
+                                      SDL_Joystick *joystick);
+
+/*
+ * Closes a haptic device after usage.
+ */
+extern void SDL_SYS_HapticClose(SDL_Haptic *haptic);
+
+/*
+ * Performs a cleanup on the haptic subsystem.
+ */
+extern void SDL_SYS_HapticQuit(void);
+
+/*
+ * Creates a new haptic effect on the haptic device using base
+ * as a template for the effect.
+ */
+extern bool SDL_SYS_HapticNewEffect(SDL_Haptic *haptic,
+                                    struct haptic_effect *effect,
+                                    const SDL_HapticEffect *base);
+
+/*
+ * Updates the haptic effect on the haptic device using data
+ * as a template.
+ */
+extern bool SDL_SYS_HapticUpdateEffect(SDL_Haptic *haptic,
+                                       struct haptic_effect *effect,
+                                       const SDL_HapticEffect *data);
+
+/*
+ * Runs the effect on the haptic device.
+ */
+extern bool SDL_SYS_HapticRunEffect(SDL_Haptic *haptic,
+                                    struct haptic_effect *effect,
+                                    Uint32 iterations);
+
+/*
+ * Stops the effect on the haptic device.
+ */
+extern bool SDL_SYS_HapticStopEffect(SDL_Haptic *haptic,
+                                     struct haptic_effect *effect);
+
+/*
+ * Cleanups up the effect on the haptic device.
+ */
+extern void SDL_SYS_HapticDestroyEffect(SDL_Haptic *haptic,
+                                        struct haptic_effect *effect);
+
+/*
+ * Queries the device for the status of effect.
+ *
+ * Returns 0 if device is stopped, >0 if device is playing and
+ * -1 on error.
+ */
+extern int SDL_SYS_HapticGetEffectStatus(SDL_Haptic *haptic,
+                                         struct haptic_effect *effect);
+
+/*
+ * Sets the global gain of the haptic device.
+ */
+extern bool SDL_SYS_HapticSetGain(SDL_Haptic *haptic, int gain);
+
+/*
+ * Sets the autocenter feature of the haptic device.
+ */
+extern bool SDL_SYS_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter);
+
+/*
+ * Pauses the haptic device.
+ */
+extern bool SDL_SYS_HapticPause(SDL_Haptic *haptic);
+
+/*
+ * Unpauses the haptic device.
+ */
+extern bool SDL_SYS_HapticResume(SDL_Haptic *haptic);
+
+/*
+ * Stops all the currently playing haptic effects on the device.
+ */
+extern bool SDL_SYS_HapticStopAll(SDL_Haptic *haptic);
+
+// Ends C function definitions when using C++
+#ifdef __cplusplus
+}
+#endif
+
+#endif // SDL_syshaptic_h_

+ 1373 - 0
thirdparty/sdl/haptic/darwin/SDL_syshaptic.c

@@ -0,0 +1,1373 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#ifdef SDL_HAPTIC_IOKIT
+
+#include "../SDL_syshaptic.h"
+#include "../../joystick/SDL_sysjoystick.h"            // For the real SDL_Joystick
+#include "../../joystick/darwin/SDL_iokitjoystick_c.h" // For joystick hwdata
+#include "SDL_syshaptic_c.h"
+
+#include <IOKit/IOKitLib.h>
+#include <IOKit/hid/IOHIDKeys.h>
+#include <IOKit/hid/IOHIDUsageTables.h>
+#include <ForceFeedback/ForceFeedback.h>
+#include <ForceFeedback/ForceFeedbackConstants.h>
+
+#ifndef IO_OBJECT_NULL
+#define IO_OBJECT_NULL ((io_service_t)0)
+#endif
+
+/*
+ * List of available haptic devices.
+ */
+typedef struct SDL_hapticlist_item
+{
+    SDL_HapticID instance_id;
+    char name[256]; // Name of the device.
+
+    io_service_t dev;   // Node we use to create the device.
+    SDL_Haptic *haptic; // Haptic currently associated with it.
+
+    // Usage pages for determining if it's a mouse or not.
+    long usage;
+    long usagePage;
+
+    struct SDL_hapticlist_item *next;
+} SDL_hapticlist_item;
+
+/*
+ * Haptic system hardware data.
+ */
+struct haptic_hwdata
+{
+    FFDeviceObjectReference device; // Hardware device.
+    UInt8 axes[3];
+};
+
+/*
+ * Haptic system effect data.
+ */
+struct haptic_hweffect
+{
+    FFEffectObjectReference ref; // Reference.
+    struct FFEFFECT effect;      // Hardware effect.
+};
+
+/*
+ * Prototypes.
+ */
+static void SDL_SYS_HapticFreeFFEFFECT(FFEFFECT *effect, int type);
+static bool HIDGetDeviceProduct(io_service_t dev, char *name);
+
+static SDL_hapticlist_item *SDL_hapticlist = NULL;
+static SDL_hapticlist_item *SDL_hapticlist_tail = NULL;
+static int numhaptics = -1;
+
+/*
+ * Like strerror but for force feedback errors.
+ */
+static const char *FFStrError(unsigned int err)
+{
+    switch (err) {
+    case FFERR_DEVICEFULL:
+        return "device full";
+    // This should be valid, but for some reason isn't defined...
+    /* case FFERR_DEVICENOTREG:
+        return "device not registered"; */
+    case FFERR_DEVICEPAUSED:
+        return "device paused";
+    case FFERR_DEVICERELEASED:
+        return "device released";
+    case FFERR_EFFECTPLAYING:
+        return "effect playing";
+    case FFERR_EFFECTTYPEMISMATCH:
+        return "effect type mismatch";
+    case FFERR_EFFECTTYPENOTSUPPORTED:
+        return "effect type not supported";
+    case FFERR_GENERIC:
+        return "undetermined error";
+    case FFERR_HASEFFECTS:
+        return "device has effects";
+    case FFERR_INCOMPLETEEFFECT:
+        return "incomplete effect";
+    case FFERR_INTERNAL:
+        return "internal fault";
+    case FFERR_INVALIDDOWNLOADID:
+        return "invalid download id";
+    case FFERR_INVALIDPARAM:
+        return "invalid parameter";
+    case FFERR_MOREDATA:
+        return "more data";
+    case FFERR_NOINTERFACE:
+        return "interface not supported";
+    case FFERR_NOTDOWNLOADED:
+        return "effect is not downloaded";
+    case FFERR_NOTINITIALIZED:
+        return "object has not been initialized";
+    case FFERR_OUTOFMEMORY:
+        return "out of memory";
+    case FFERR_UNPLUGGED:
+        return "device is unplugged";
+    case FFERR_UNSUPPORTED:
+        return "function call unsupported";
+    case FFERR_UNSUPPORTEDAXIS:
+        return "axis unsupported";
+
+    default:
+        return "unknown error";
+    }
+}
+
+/*
+ * Initializes the haptic subsystem.
+ */
+bool SDL_SYS_HapticInit(void)
+{
+    IOReturn result;
+    io_iterator_t iter;
+    CFDictionaryRef match;
+    io_service_t device;
+
+    if (numhaptics != -1) {
+        return SDL_SetError("Haptic subsystem already initialized!");
+    }
+    numhaptics = 0;
+
+    // Get HID devices.
+    match = IOServiceMatching(kIOHIDDeviceKey);
+    if (!match) {
+        return SDL_SetError("Haptic: Failed to get IOServiceMatching.");
+    }
+
+    // Now search I/O Registry for matching devices.
+    result = IOServiceGetMatchingServices(kIOMainPortDefault, match, &iter);
+    if (result != kIOReturnSuccess) {
+        return SDL_SetError("Haptic: Couldn't create a HID object iterator.");
+    }
+    // IOServiceGetMatchingServices consumes dictionary.
+
+    if (!IOIteratorIsValid(iter)) { // No iterator.
+        return true;
+    }
+
+    while ((device = IOIteratorNext(iter)) != IO_OBJECT_NULL) {
+        MacHaptic_MaybeAddDevice(device);
+        // always release as the AddDevice will retain IF it's a forcefeedback device
+        IOObjectRelease(device);
+    }
+    IOObjectRelease(iter);
+
+    return true;
+}
+
+int SDL_SYS_NumHaptics(void)
+{
+    return numhaptics;
+}
+
+static SDL_hapticlist_item *HapticByDevIndex(int device_index)
+{
+    SDL_hapticlist_item *item = SDL_hapticlist;
+
+    if ((device_index < 0) || (device_index >= numhaptics)) {
+        return NULL;
+    }
+
+    while (device_index > 0) {
+        SDL_assert(item != NULL);
+        --device_index;
+        item = item->next;
+    }
+
+    return item;
+}
+
+static SDL_hapticlist_item *HapticByInstanceID(SDL_HapticID instance_id)
+{
+    SDL_hapticlist_item *item;
+    for (item = SDL_hapticlist; item; item = item->next) {
+        if (instance_id == item->instance_id) {
+            return item;
+        }
+    }
+    return NULL;
+}
+
+bool MacHaptic_MaybeAddDevice(io_object_t device)
+{
+    IOReturn result;
+    CFMutableDictionaryRef hidProperties;
+    CFTypeRef refCF;
+    SDL_hapticlist_item *item;
+
+    if (numhaptics == -1) {
+        return false; // not initialized. We'll pick these up on enumeration if we init later.
+    }
+
+    // Check for force feedback.
+    if (FFIsForceFeedback(device) != FF_OK) {
+        return false;
+    }
+
+    // Make sure we don't already have it
+    for (item = SDL_hapticlist; item; item = item->next) {
+        if (IOObjectIsEqualTo((io_object_t)item->dev, device)) {
+            // Already added
+            return false;
+        }
+    }
+
+    item = (SDL_hapticlist_item *)SDL_calloc(1, sizeof(SDL_hapticlist_item));
+    if (!item) {
+        return SDL_SetError("Could not allocate haptic storage");
+    }
+    item->instance_id = SDL_GetNextObjectID();
+
+    // retain it as we are going to keep it around a while
+    IOObjectRetain(device);
+
+    // Set basic device data.
+    HIDGetDeviceProduct(device, item->name);
+    item->dev = device;
+
+    // Set usage pages.
+    hidProperties = 0;
+    refCF = 0;
+    result = IORegistryEntryCreateCFProperties(device,
+                                               &hidProperties,
+                                               kCFAllocatorDefault,
+                                               kNilOptions);
+    if ((result == KERN_SUCCESS) && hidProperties) {
+        refCF = CFDictionaryGetValue(hidProperties,
+                                     CFSTR(kIOHIDPrimaryUsagePageKey));
+        if (refCF) {
+            if (!CFNumberGetValue(refCF, kCFNumberLongType, &item->usagePage)) {
+                SDL_SetError("Haptic: Receiving device's usage page.");
+            }
+            refCF = CFDictionaryGetValue(hidProperties,
+                                         CFSTR(kIOHIDPrimaryUsageKey));
+            if (refCF) {
+                if (!CFNumberGetValue(refCF, kCFNumberLongType, &item->usage)) {
+                    SDL_SetError("Haptic: Receiving device's usage.");
+                }
+            }
+        }
+        CFRelease(hidProperties);
+    }
+
+    if (!SDL_hapticlist_tail) {
+        SDL_hapticlist = SDL_hapticlist_tail = item;
+    } else {
+        SDL_hapticlist_tail->next = item;
+        SDL_hapticlist_tail = item;
+    }
+
+    // Device has been added.
+    ++numhaptics;
+
+    return true;
+}
+
+bool MacHaptic_MaybeRemoveDevice(io_object_t device)
+{
+    SDL_hapticlist_item *item;
+    SDL_hapticlist_item *prev = NULL;
+
+    if (numhaptics == -1) {
+        return false; // not initialized. ignore this.
+    }
+
+    for (item = SDL_hapticlist; item; item = item->next) {
+        // found it, remove it.
+        if (IOObjectIsEqualTo((io_object_t)item->dev, device)) {
+            bool result = item->haptic ? true : false;
+
+            if (prev) {
+                prev->next = item->next;
+            } else {
+                SDL_assert(SDL_hapticlist == item);
+                SDL_hapticlist = item->next;
+            }
+            if (item == SDL_hapticlist_tail) {
+                SDL_hapticlist_tail = prev;
+            }
+
+            // Need to decrement the haptic count
+            --numhaptics;
+            // !!! TODO: Send a haptic remove event?
+
+            IOObjectRelease(item->dev);
+            SDL_free(item);
+            return result;
+        }
+        prev = item;
+    }
+
+    return false;
+}
+
+SDL_HapticID SDL_SYS_HapticInstanceID(int index)
+{
+    SDL_hapticlist_item *item;
+    item = HapticByDevIndex(index);
+    if (item) {
+        return item->instance_id;
+    }
+    return 0;
+}
+
+/*
+ * Return the name of a haptic device, does not need to be opened.
+ */
+const char *SDL_SYS_HapticName(int index)
+{
+    SDL_hapticlist_item *item;
+    item = HapticByDevIndex(index);
+    if (item) {
+        return item->name;
+    }
+    return NULL;
+}
+
+/*
+ * Gets the device's product name.
+ */
+static bool HIDGetDeviceProduct(io_service_t dev, char *name)
+{
+    CFMutableDictionaryRef hidProperties, usbProperties;
+    io_registry_entry_t parent1, parent2;
+    kern_return_t ret;
+
+    hidProperties = usbProperties = 0;
+
+    ret = IORegistryEntryCreateCFProperties(dev, &hidProperties,
+                                            kCFAllocatorDefault, kNilOptions);
+    if ((ret != KERN_SUCCESS) || !hidProperties) {
+        return SDL_SetError("Haptic: Unable to create CFProperties.");
+    }
+
+    /* macOS currently is not mirroring all USB properties to HID page so need to look at USB device page also
+     * get dictionary for USB properties: step up two levels and get CF dictionary for USB properties
+     */
+    if ((KERN_SUCCESS ==
+         IORegistryEntryGetParentEntry(dev, kIOServicePlane, &parent1)) &&
+        (KERN_SUCCESS ==
+         IORegistryEntryGetParentEntry(parent1, kIOServicePlane, &parent2)) &&
+        (KERN_SUCCESS ==
+         IORegistryEntryCreateCFProperties(parent2, &usbProperties,
+                                           kCFAllocatorDefault,
+                                           kNilOptions))) {
+        if (usbProperties) {
+            CFTypeRef refCF = 0;
+            /* get device info
+             * try hid dictionary first, if fail then go to USB dictionary
+             */
+
+            // Get product name
+            refCF = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDProductKey));
+            if (!refCF) {
+                refCF = CFDictionaryGetValue(usbProperties,
+                                             CFSTR("USB Product Name"));
+            }
+            if (refCF) {
+                if (!CFStringGetCString(refCF, name, 256,
+                                        CFStringGetSystemEncoding())) {
+                    return SDL_SetError("Haptic: CFStringGetCString error retrieving pDevice->product.");
+                }
+            }
+
+            CFRelease(usbProperties);
+        } else {
+            return SDL_SetError("Haptic: IORegistryEntryCreateCFProperties failed to create usbProperties.");
+        }
+
+        // Release stuff.
+        if (kIOReturnSuccess != IOObjectRelease(parent2)) {
+            SDL_SetError("Haptic: IOObjectRelease error with parent2.");
+        }
+        if (kIOReturnSuccess != IOObjectRelease(parent1)) {
+            SDL_SetError("Haptic: IOObjectRelease error with parent1.");
+        }
+    } else {
+        return SDL_SetError("Haptic: Error getting registry entries.");
+    }
+
+    return true;
+}
+
+#define FF_TEST(ff, s)                    \
+    if (features.supportedEffects & (ff)) \
+    supported |= (s)
+/*
+ * Gets supported features.
+ */
+static bool GetSupportedFeatures(SDL_Haptic *haptic)
+{
+    HRESULT ret;
+    FFDeviceObjectReference device;
+    FFCAPABILITIES features;
+    unsigned int supported;
+    Uint32 val;
+
+    device = haptic->hwdata->device;
+
+    ret = FFDeviceGetForceFeedbackCapabilities(device, &features);
+    if (ret != FF_OK) {
+        return SDL_SetError("Haptic: Unable to get device's supported features.");
+    }
+
+    supported = 0;
+
+    // Get maximum effects.
+    haptic->neffects = features.storageCapacity;
+    haptic->nplaying = features.playbackCapacity;
+
+    // Test for effects.
+    FF_TEST(FFCAP_ET_CONSTANTFORCE, SDL_HAPTIC_CONSTANT);
+    FF_TEST(FFCAP_ET_RAMPFORCE, SDL_HAPTIC_RAMP);
+    FF_TEST(FFCAP_ET_SQUARE, SDL_HAPTIC_SQUARE);
+    FF_TEST(FFCAP_ET_SINE, SDL_HAPTIC_SINE);
+    FF_TEST(FFCAP_ET_TRIANGLE, SDL_HAPTIC_TRIANGLE);
+    FF_TEST(FFCAP_ET_SAWTOOTHUP, SDL_HAPTIC_SAWTOOTHUP);
+    FF_TEST(FFCAP_ET_SAWTOOTHDOWN, SDL_HAPTIC_SAWTOOTHDOWN);
+    FF_TEST(FFCAP_ET_SPRING, SDL_HAPTIC_SPRING);
+    FF_TEST(FFCAP_ET_DAMPER, SDL_HAPTIC_DAMPER);
+    FF_TEST(FFCAP_ET_INERTIA, SDL_HAPTIC_INERTIA);
+    FF_TEST(FFCAP_ET_FRICTION, SDL_HAPTIC_FRICTION);
+    FF_TEST(FFCAP_ET_CUSTOMFORCE, SDL_HAPTIC_CUSTOM);
+
+    // Check if supports gain.
+    ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_FFGAIN,
+                                           &val, sizeof(val));
+    if (ret == FF_OK) {
+        supported |= SDL_HAPTIC_GAIN;
+    } else if (ret != FFERR_UNSUPPORTED) {
+        return SDL_SetError("Haptic: Unable to get if device supports gain: %s.",
+                            FFStrError(ret));
+    }
+
+    // Checks if supports autocenter.
+    ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_AUTOCENTER,
+                                           &val, sizeof(val));
+    if (ret == FF_OK) {
+        supported |= SDL_HAPTIC_AUTOCENTER;
+    } else if (ret != FFERR_UNSUPPORTED) {
+        return SDL_SetError("Haptic: Unable to get if device supports autocenter: %s.",
+                            FFStrError(ret));
+    }
+
+    // Check for axes, we have an artificial limit on axes
+    haptic->naxes = ((features.numFfAxes) > 3) ? 3 : features.numFfAxes;
+    // Actually store the axes we want to use
+    SDL_memcpy(haptic->hwdata->axes, features.ffAxes,
+               haptic->naxes * sizeof(Uint8));
+
+    // Always supported features.
+    supported |= SDL_HAPTIC_STATUS | SDL_HAPTIC_PAUSE;
+
+    haptic->supported = supported;
+    return true;
+}
+
+/*
+ * Opens the haptic device from the file descriptor.
+ */
+static bool SDL_SYS_HapticOpenFromService(SDL_Haptic *haptic, io_service_t service)
+{
+    HRESULT ret;
+
+    // Allocate the hwdata
+    haptic->hwdata = (struct haptic_hwdata *) SDL_calloc(1, sizeof(*haptic->hwdata));
+    if (!haptic->hwdata) {
+        goto creat_err;
+    }
+
+    // Open the device
+    ret = FFCreateDevice(service, &haptic->hwdata->device);
+    if (ret != FF_OK) {
+        SDL_SetError("Haptic: Unable to create device from service: %s.", FFStrError(ret));
+        goto creat_err;
+    }
+
+    // Get supported features.
+    if (!GetSupportedFeatures(haptic)) {
+        goto open_err;
+    }
+
+    // Reset and then enable actuators.
+    ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
+                                           FFSFFC_RESET);
+    if (ret != FF_OK) {
+        SDL_SetError("Haptic: Unable to reset device: %s.", FFStrError(ret));
+        goto open_err;
+    }
+    ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
+                                           FFSFFC_SETACTUATORSON);
+    if (ret != FF_OK) {
+        SDL_SetError("Haptic: Unable to enable actuators: %s.",
+                     FFStrError(ret));
+        goto open_err;
+    }
+
+    // Allocate effects memory.
+    haptic->effects = (struct haptic_effect *)
+        SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
+    if (!haptic->effects) {
+        goto open_err;
+    }
+    // Clear the memory
+    SDL_memset(haptic->effects, 0,
+               sizeof(struct haptic_effect) * haptic->neffects);
+
+    return true;
+
+    // Error handling
+open_err:
+    FFReleaseDevice(haptic->hwdata->device);
+creat_err:
+    if (haptic->hwdata) {
+        SDL_free(haptic->hwdata);
+        haptic->hwdata = NULL;
+    }
+    return false;
+}
+
+/*
+ * Opens a haptic device for usage.
+ */
+bool SDL_SYS_HapticOpen(SDL_Haptic *haptic)
+{
+    SDL_hapticlist_item *item;
+    item = HapticByInstanceID(haptic->instance_id);
+
+    return SDL_SYS_HapticOpenFromService(haptic, item->dev);
+}
+
+/*
+ * Opens a haptic device from first mouse it finds for usage.
+ */
+int SDL_SYS_HapticMouse(void)
+{
+    int device_index = 0;
+    SDL_hapticlist_item *item;
+
+    for (item = SDL_hapticlist; item; item = item->next) {
+        if ((item->usagePage == kHIDPage_GenericDesktop) &&
+            (item->usage == kHIDUsage_GD_Mouse)) {
+            return device_index;
+        }
+        ++device_index;
+    }
+
+    return 0;
+}
+
+/*
+ * Checks to see if a joystick has haptic features.
+ */
+bool SDL_SYS_JoystickIsHaptic(SDL_Joystick *joystick)
+{
+#ifdef SDL_JOYSTICK_IOKIT
+    if (joystick->driver != &SDL_DARWIN_JoystickDriver) {
+        return false;
+    }
+    if (joystick->hwdata->ffservice != 0) {
+        return true;
+    }
+#endif
+    return false;
+}
+
+/*
+ * Checks to see if the haptic device and joystick are in reality the same.
+ */
+bool SDL_SYS_JoystickSameHaptic(SDL_Haptic *haptic, SDL_Joystick *joystick)
+{
+#ifdef SDL_JOYSTICK_IOKIT
+    if (joystick->driver != &SDL_DARWIN_JoystickDriver) {
+        return false;
+    }
+    if (IOObjectIsEqualTo((io_object_t)((size_t)haptic->hwdata->device),
+                          joystick->hwdata->ffservice)) {
+        return true;
+    }
+#endif
+    return false;
+}
+
+/*
+ * Opens a SDL_Haptic from a SDL_Joystick.
+ */
+bool SDL_SYS_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick)
+{
+#ifdef SDL_JOYSTICK_IOKIT
+    SDL_hapticlist_item *item;
+
+    if (joystick->driver != &SDL_DARWIN_JoystickDriver) {
+        return false;
+    }
+    for (item = SDL_hapticlist; item; item = item->next) {
+        if (IOObjectIsEqualTo((io_object_t)item->dev,
+                              joystick->hwdata->ffservice)) {
+            haptic->instance_id = item->instance_id;
+            break;
+        }
+    }
+
+    if (joystick->name) {
+        haptic->name = SDL_strdup(joystick->name);
+    }
+
+    return SDL_SYS_HapticOpenFromService(haptic, joystick->hwdata->ffservice);
+#else
+    return false;
+#endif
+}
+
+/*
+ * Closes the haptic device.
+ */
+void SDL_SYS_HapticClose(SDL_Haptic *haptic)
+{
+    if (haptic->hwdata) {
+
+        // Free Effects.
+        SDL_free(haptic->effects);
+        haptic->effects = NULL;
+        haptic->neffects = 0;
+
+        // Clean up
+        FFReleaseDevice(haptic->hwdata->device);
+
+        // Free
+        SDL_free(haptic->hwdata);
+        haptic->hwdata = NULL;
+    }
+}
+
+/*
+ * Clean up after system specific haptic stuff
+ */
+void SDL_SYS_HapticQuit(void)
+{
+    SDL_hapticlist_item *item;
+    SDL_hapticlist_item *next = NULL;
+
+    for (item = SDL_hapticlist; item; item = next) {
+        next = item->next;
+        /* Opened and not closed haptics are leaked, this is on purpose.
+         * Close your haptic devices after usage. */
+
+        // Free the io_service_t
+        IOObjectRelease(item->dev);
+        SDL_free(item);
+    }
+
+    numhaptics = -1;
+    SDL_hapticlist = NULL;
+    SDL_hapticlist_tail = NULL;
+}
+
+/*
+ * Converts an SDL trigger button to an FFEFFECT trigger button.
+ */
+static DWORD FFGetTriggerButton(Uint16 button)
+{
+    DWORD dwTriggerButton;
+
+    dwTriggerButton = FFEB_NOTRIGGER;
+
+    if (button != 0) {
+        dwTriggerButton = FFJOFS_BUTTON(button - 1);
+    }
+
+    return dwTriggerButton;
+}
+
+/*
+ * Sets the direction.
+ */
+static bool SDL_SYS_SetDirection(FFEFFECT *effect, const SDL_HapticDirection *dir, int naxes)
+{
+    LONG *rglDir;
+
+    // Handle no axes a part.
+    if (naxes == 0) {
+        effect->dwFlags |= FFEFF_SPHERICAL; // Set as default.
+        effect->rglDirection = NULL;
+        return true;
+    }
+
+    // Has axes.
+    rglDir = SDL_malloc(sizeof(LONG) * naxes);
+    if (!rglDir) {
+        return false;
+    }
+    SDL_memset(rglDir, 0, sizeof(LONG) * naxes);
+    effect->rglDirection = rglDir;
+
+    switch (dir->type) {
+    case SDL_HAPTIC_POLAR:
+        effect->dwFlags |= FFEFF_POLAR;
+        rglDir[0] = dir->dir[0];
+        return true;
+    case SDL_HAPTIC_CARTESIAN:
+        effect->dwFlags |= FFEFF_CARTESIAN;
+        rglDir[0] = dir->dir[0];
+        if (naxes > 1) {
+            rglDir[1] = dir->dir[1];
+        }
+        if (naxes > 2) {
+            rglDir[2] = dir->dir[2];
+        }
+        return true;
+    case SDL_HAPTIC_SPHERICAL:
+        effect->dwFlags |= FFEFF_SPHERICAL;
+        rglDir[0] = dir->dir[0];
+        if (naxes > 1) {
+            rglDir[1] = dir->dir[1];
+        }
+        if (naxes > 2) {
+            rglDir[2] = dir->dir[2];
+        }
+        return true;
+    case SDL_HAPTIC_STEERING_AXIS:
+        effect->dwFlags |= FFEFF_CARTESIAN;
+        rglDir[0] = 0;
+        return true;
+
+    default:
+        return SDL_SetError("Haptic: Unknown direction type.");
+    }
+}
+
+// Clamps and converts.
+#define CCONVERT(x) (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF)
+// Just converts.
+#define CONVERT(x) (((x)*10000) / 0x7FFF)
+/*
+ * Creates the FFEFFECT from a SDL_HapticEffect.
+ */
+static bool SDL_SYS_ToFFEFFECT(SDL_Haptic *haptic, FFEFFECT *dest, const SDL_HapticEffect *src)
+{
+    int i;
+    FFCONSTANTFORCE *constant = NULL;
+    FFPERIODIC *periodic = NULL;
+    FFCONDITION *condition = NULL; // Actually an array of conditions - one per axis.
+    FFRAMPFORCE *ramp = NULL;
+    FFCUSTOMFORCE *custom = NULL;
+    FFENVELOPE *envelope = NULL;
+    const SDL_HapticConstant *hap_constant = NULL;
+    const SDL_HapticPeriodic *hap_periodic = NULL;
+    const SDL_HapticCondition *hap_condition = NULL;
+    const SDL_HapticRamp *hap_ramp = NULL;
+    const SDL_HapticCustom *hap_custom = NULL;
+    DWORD *axes = NULL;
+
+    // Set global stuff.
+    SDL_memset(dest, 0, sizeof(FFEFFECT));
+    dest->dwSize = sizeof(FFEFFECT);     // Set the structure size.
+    dest->dwSamplePeriod = 0;            // Not used by us.
+    dest->dwGain = 10000;                // Gain is set globally, not locally.
+    dest->dwFlags = FFEFF_OBJECTOFFSETS; // Seems obligatory.
+
+    // Envelope.
+    envelope = SDL_calloc(1, sizeof(FFENVELOPE));
+    if (!envelope) {
+        return false;
+    }
+    dest->lpEnvelope = envelope;
+    envelope->dwSize = sizeof(FFENVELOPE); // Always should be this.
+
+    // Axes.
+    if (src->constant.direction.type == SDL_HAPTIC_STEERING_AXIS) {
+        dest->cAxes = 1;
+    } else {
+        dest->cAxes = haptic->naxes;
+    }
+    if (dest->cAxes > 0) {
+        axes = SDL_malloc(sizeof(DWORD) * dest->cAxes);
+        if (!axes) {
+            return false;
+        }
+        axes[0] = haptic->hwdata->axes[0]; // Always at least one axis.
+        if (dest->cAxes > 1) {
+            axes[1] = haptic->hwdata->axes[1];
+        }
+        if (dest->cAxes > 2) {
+            axes[2] = haptic->hwdata->axes[2];
+        }
+        dest->rgdwAxes = axes;
+    }
+
+    // The big type handling switch, even bigger then Linux's version.
+    switch (src->type) {
+    case SDL_HAPTIC_CONSTANT:
+        hap_constant = &src->constant;
+        constant = SDL_calloc(1, sizeof(FFCONSTANTFORCE));
+        if (!constant) {
+            return false;
+        }
+
+        // Specifics
+        constant->lMagnitude = CONVERT(hap_constant->level);
+        dest->cbTypeSpecificParams = sizeof(FFCONSTANTFORCE);
+        dest->lpvTypeSpecificParams = constant;
+
+        // Generics
+        dest->dwDuration = hap_constant->length * 1000; // In microseconds.
+        dest->dwTriggerButton = FFGetTriggerButton(hap_constant->button);
+        dest->dwTriggerRepeatInterval = hap_constant->interval;
+        dest->dwStartDelay = hap_constant->delay * 1000; // In microseconds.
+
+        // Direction.
+        if (!SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes)) {
+            return false;
+        }
+
+        // Envelope
+        if ((hap_constant->attack_length == 0) && (hap_constant->fade_length == 0)) {
+            SDL_free(envelope);
+            dest->lpEnvelope = NULL;
+        } else {
+            envelope->dwAttackLevel = CCONVERT(hap_constant->attack_level);
+            envelope->dwAttackTime = hap_constant->attack_length * 1000;
+            envelope->dwFadeLevel = CCONVERT(hap_constant->fade_level);
+            envelope->dwFadeTime = hap_constant->fade_length * 1000;
+        }
+
+        break;
+
+    case SDL_HAPTIC_SINE:
+    case SDL_HAPTIC_SQUARE:
+    case SDL_HAPTIC_TRIANGLE:
+    case SDL_HAPTIC_SAWTOOTHUP:
+    case SDL_HAPTIC_SAWTOOTHDOWN:
+        hap_periodic = &src->periodic;
+        periodic = SDL_calloc(1, sizeof(FFPERIODIC));
+        if (!periodic) {
+            return false;
+        }
+
+        // Specifics
+        periodic->dwMagnitude = CONVERT(SDL_abs(hap_periodic->magnitude));
+        periodic->lOffset = CONVERT(hap_periodic->offset);
+        periodic->dwPhase =
+            (hap_periodic->phase + (hap_periodic->magnitude < 0 ? 18000 : 0)) % 36000;
+        periodic->dwPeriod = hap_periodic->period * 1000;
+        dest->cbTypeSpecificParams = sizeof(FFPERIODIC);
+        dest->lpvTypeSpecificParams = periodic;
+
+        // Generics
+        dest->dwDuration = hap_periodic->length * 1000; // In microseconds.
+        dest->dwTriggerButton = FFGetTriggerButton(hap_periodic->button);
+        dest->dwTriggerRepeatInterval = hap_periodic->interval;
+        dest->dwStartDelay = hap_periodic->delay * 1000; // In microseconds.
+
+        // Direction.
+        if (!SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes)) {
+            return false;
+        }
+
+        // Envelope
+        if ((hap_periodic->attack_length == 0) && (hap_periodic->fade_length == 0)) {
+            SDL_free(envelope);
+            dest->lpEnvelope = NULL;
+        } else {
+            envelope->dwAttackLevel = CCONVERT(hap_periodic->attack_level);
+            envelope->dwAttackTime = hap_periodic->attack_length * 1000;
+            envelope->dwFadeLevel = CCONVERT(hap_periodic->fade_level);
+            envelope->dwFadeTime = hap_periodic->fade_length * 1000;
+        }
+
+        break;
+
+    case SDL_HAPTIC_SPRING:
+    case SDL_HAPTIC_DAMPER:
+    case SDL_HAPTIC_INERTIA:
+    case SDL_HAPTIC_FRICTION:
+        hap_condition = &src->condition;
+        if (dest->cAxes > 0) {
+            condition = SDL_calloc(dest->cAxes, sizeof(FFCONDITION));
+            if (!condition) {
+                return false;
+            }
+
+            // Specifics
+            for (i = 0; i < dest->cAxes; i++) {
+                condition[i].lOffset = CONVERT(hap_condition->center[i]);
+                condition[i].lPositiveCoefficient =
+                    CONVERT(hap_condition->right_coeff[i]);
+                condition[i].lNegativeCoefficient =
+                    CONVERT(hap_condition->left_coeff[i]);
+                condition[i].dwPositiveSaturation =
+                    CCONVERT(hap_condition->right_sat[i] / 2);
+                condition[i].dwNegativeSaturation =
+                    CCONVERT(hap_condition->left_sat[i] / 2);
+                condition[i].lDeadBand = CCONVERT(hap_condition->deadband[i] / 2);
+            }
+        }
+
+        dest->cbTypeSpecificParams = sizeof(FFCONDITION) * dest->cAxes;
+        dest->lpvTypeSpecificParams = condition;
+
+        // Generics
+        dest->dwDuration = hap_condition->length * 1000; // In microseconds.
+        dest->dwTriggerButton = FFGetTriggerButton(hap_condition->button);
+        dest->dwTriggerRepeatInterval = hap_condition->interval;
+        dest->dwStartDelay = hap_condition->delay * 1000; // In microseconds.
+
+        // Direction.
+        if (!SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes)) {
+            return false;
+        }
+
+        // Envelope - Not actually supported by most CONDITION implementations.
+        SDL_free(dest->lpEnvelope);
+        dest->lpEnvelope = NULL;
+
+        break;
+
+    case SDL_HAPTIC_RAMP:
+        hap_ramp = &src->ramp;
+        ramp = SDL_calloc(1, sizeof(FFRAMPFORCE));
+        if (!ramp) {
+            return false;
+        }
+
+        // Specifics
+        ramp->lStart = CONVERT(hap_ramp->start);
+        ramp->lEnd = CONVERT(hap_ramp->end);
+        dest->cbTypeSpecificParams = sizeof(FFRAMPFORCE);
+        dest->lpvTypeSpecificParams = ramp;
+
+        // Generics
+        dest->dwDuration = hap_ramp->length * 1000; // In microseconds.
+        dest->dwTriggerButton = FFGetTriggerButton(hap_ramp->button);
+        dest->dwTriggerRepeatInterval = hap_ramp->interval;
+        dest->dwStartDelay = hap_ramp->delay * 1000; // In microseconds.
+
+        // Direction.
+        if (!SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes)) {
+            return false;
+        }
+
+        // Envelope
+        if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) {
+            SDL_free(envelope);
+            dest->lpEnvelope = NULL;
+        } else {
+            envelope->dwAttackLevel = CCONVERT(hap_ramp->attack_level);
+            envelope->dwAttackTime = hap_ramp->attack_length * 1000;
+            envelope->dwFadeLevel = CCONVERT(hap_ramp->fade_level);
+            envelope->dwFadeTime = hap_ramp->fade_length * 1000;
+        }
+
+        break;
+
+    case SDL_HAPTIC_CUSTOM:
+        hap_custom = &src->custom;
+        custom = SDL_calloc(1, sizeof(FFCUSTOMFORCE));
+        if (!custom) {
+            return false;
+        }
+
+        // Specifics
+        custom->cChannels = hap_custom->channels;
+        custom->dwSamplePeriod = hap_custom->period * 1000;
+        custom->cSamples = hap_custom->samples;
+        custom->rglForceData =
+            SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels);
+        for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) { // Copy data.
+            custom->rglForceData[i] = CCONVERT(hap_custom->data[i]);
+        }
+        dest->cbTypeSpecificParams = sizeof(FFCUSTOMFORCE);
+        dest->lpvTypeSpecificParams = custom;
+
+        // Generics
+        dest->dwDuration = hap_custom->length * 1000; // In microseconds.
+        dest->dwTriggerButton = FFGetTriggerButton(hap_custom->button);
+        dest->dwTriggerRepeatInterval = hap_custom->interval;
+        dest->dwStartDelay = hap_custom->delay * 1000; // In microseconds.
+
+        // Direction.
+        if (!SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes)) {
+            return false;
+        }
+
+        // Envelope
+        if ((hap_custom->attack_length == 0) && (hap_custom->fade_length == 0)) {
+            SDL_free(envelope);
+            dest->lpEnvelope = NULL;
+        } else {
+            envelope->dwAttackLevel = CCONVERT(hap_custom->attack_level);
+            envelope->dwAttackTime = hap_custom->attack_length * 1000;
+            envelope->dwFadeLevel = CCONVERT(hap_custom->fade_level);
+            envelope->dwFadeTime = hap_custom->fade_length * 1000;
+        }
+
+        break;
+
+    default:
+        return SDL_SetError("Haptic: Unknown effect type.");
+    }
+
+    return true;
+}
+
+/*
+ * Frees an FFEFFECT allocated by SDL_SYS_ToFFEFFECT.
+ */
+static void SDL_SYS_HapticFreeFFEFFECT(FFEFFECT *effect, int type)
+{
+    FFCUSTOMFORCE *custom;
+
+    SDL_free(effect->lpEnvelope);
+    effect->lpEnvelope = NULL;
+    SDL_free(effect->rgdwAxes);
+    effect->rgdwAxes = NULL;
+    if (effect->lpvTypeSpecificParams) {
+        if (type == SDL_HAPTIC_CUSTOM) { // Must free the custom data.
+            custom = (FFCUSTOMFORCE *)effect->lpvTypeSpecificParams;
+            SDL_free(custom->rglForceData);
+            custom->rglForceData = NULL;
+        }
+        SDL_free(effect->lpvTypeSpecificParams);
+        effect->lpvTypeSpecificParams = NULL;
+    }
+    SDL_free(effect->rglDirection);
+    effect->rglDirection = NULL;
+}
+
+/*
+ * Gets the effect type from the generic SDL haptic effect wrapper.
+ */
+CFUUIDRef
+SDL_SYS_HapticEffectType(Uint16 type)
+{
+    switch (type) {
+    case SDL_HAPTIC_CONSTANT:
+        return kFFEffectType_ConstantForce_ID;
+
+    case SDL_HAPTIC_RAMP:
+        return kFFEffectType_RampForce_ID;
+
+    case SDL_HAPTIC_SQUARE:
+        return kFFEffectType_Square_ID;
+
+    case SDL_HAPTIC_SINE:
+        return kFFEffectType_Sine_ID;
+
+    case SDL_HAPTIC_TRIANGLE:
+        return kFFEffectType_Triangle_ID;
+
+    case SDL_HAPTIC_SAWTOOTHUP:
+        return kFFEffectType_SawtoothUp_ID;
+
+    case SDL_HAPTIC_SAWTOOTHDOWN:
+        return kFFEffectType_SawtoothDown_ID;
+
+    case SDL_HAPTIC_SPRING:
+        return kFFEffectType_Spring_ID;
+
+    case SDL_HAPTIC_DAMPER:
+        return kFFEffectType_Damper_ID;
+
+    case SDL_HAPTIC_INERTIA:
+        return kFFEffectType_Inertia_ID;
+
+    case SDL_HAPTIC_FRICTION:
+        return kFFEffectType_Friction_ID;
+
+    case SDL_HAPTIC_CUSTOM:
+        return kFFEffectType_CustomForce_ID;
+
+    default:
+        SDL_SetError("Haptic: Unknown effect type.");
+        return NULL;
+    }
+}
+
+/*
+ * Creates a new haptic effect.
+ */
+bool SDL_SYS_HapticNewEffect(SDL_Haptic *haptic, struct haptic_effect *effect,
+                            const SDL_HapticEffect *base)
+{
+    HRESULT ret;
+    CFUUIDRef type;
+
+    // Alloc the effect.
+    effect->hweffect = (struct haptic_hweffect *)
+        SDL_calloc(1, sizeof(struct haptic_hweffect));
+    if (!effect->hweffect) {
+        goto err_hweffect;
+    }
+
+    // Get the type.
+    type = SDL_SYS_HapticEffectType(base->type);
+    if (!type) {
+        goto err_hweffect;
+    }
+
+    // Get the effect.
+    if (!SDL_SYS_ToFFEFFECT(haptic, &effect->hweffect->effect, base)) {
+        goto err_effectdone;
+    }
+
+    // Create the actual effect.
+    ret = FFDeviceCreateEffect(haptic->hwdata->device, type,
+                               &effect->hweffect->effect,
+                               &effect->hweffect->ref);
+    if (ret != FF_OK) {
+        SDL_SetError("Haptic: Unable to create effect: %s.", FFStrError(ret));
+        goto err_effectdone;
+    }
+
+    return true;
+
+err_effectdone:
+    SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, base->type);
+err_hweffect:
+    SDL_free(effect->hweffect);
+    effect->hweffect = NULL;
+    return false;
+}
+
+/*
+ * Updates an effect.
+ */
+bool SDL_SYS_HapticUpdateEffect(SDL_Haptic *haptic,
+                               struct haptic_effect *effect,
+                               const SDL_HapticEffect *data)
+{
+    HRESULT ret;
+    FFEffectParameterFlag flags;
+    FFEFFECT temp;
+
+    // Get the effect.
+    SDL_memset(&temp, 0, sizeof(FFEFFECT));
+    if (!SDL_SYS_ToFFEFFECT(haptic, &temp, data)) {
+        goto err_update;
+    }
+
+    /* Set the flags.  Might be worthwhile to diff temp with loaded effect and
+     *  only change those parameters. */
+    flags = FFEP_DIRECTION |
+            FFEP_DURATION |
+            FFEP_ENVELOPE |
+            FFEP_STARTDELAY |
+            FFEP_TRIGGERBUTTON |
+            FFEP_TRIGGERREPEATINTERVAL | FFEP_TYPESPECIFICPARAMS;
+
+    // Create the actual effect.
+    ret = FFEffectSetParameters(effect->hweffect->ref, &temp, flags);
+    if (ret != FF_OK) {
+        SDL_SetError("Haptic: Unable to update effect: %s.", FFStrError(ret));
+        goto err_update;
+    }
+
+    // Copy it over.
+    SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, data->type);
+    SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(FFEFFECT));
+
+    return true;
+
+err_update:
+    SDL_SYS_HapticFreeFFEFFECT(&temp, data->type);
+    return false;
+}
+
+/*
+ * Runs an effect.
+ */
+bool SDL_SYS_HapticRunEffect(SDL_Haptic *haptic, struct haptic_effect *effect,
+                            Uint32 iterations)
+{
+    HRESULT ret;
+    Uint32 iter;
+
+    // Check if it's infinite.
+    if (iterations == SDL_HAPTIC_INFINITY) {
+        iter = FF_INFINITE;
+    } else {
+        iter = iterations;
+    }
+
+    // Run the effect.
+    ret = FFEffectStart(effect->hweffect->ref, iter, 0);
+    if (ret != FF_OK) {
+        return SDL_SetError("Haptic: Unable to run the effect: %s.",
+                            FFStrError(ret));
+    }
+
+    return true;
+}
+
+/*
+ * Stops an effect.
+ */
+bool SDL_SYS_HapticStopEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
+{
+    HRESULT ret;
+
+    ret = FFEffectStop(effect->hweffect->ref);
+    if (ret != FF_OK) {
+        return SDL_SetError("Haptic: Unable to stop the effect: %s.",
+                            FFStrError(ret));
+    }
+
+    return true;
+}
+
+/*
+ * Frees the effect.
+ */
+void SDL_SYS_HapticDestroyEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
+{
+    HRESULT ret;
+
+    ret = FFDeviceReleaseEffect(haptic->hwdata->device, effect->hweffect->ref);
+    if (ret != FF_OK) {
+        SDL_SetError("Haptic: Error removing the effect from the device: %s.",
+                     FFStrError(ret));
+    }
+    SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect,
+                               effect->effect.type);
+    SDL_free(effect->hweffect);
+    effect->hweffect = NULL;
+}
+
+/*
+ * Gets the status of a haptic effect.
+ */
+int SDL_SYS_HapticGetEffectStatus(SDL_Haptic *haptic,
+                                  struct haptic_effect *effect)
+{
+    HRESULT ret;
+    FFEffectStatusFlag status;
+
+    ret = FFEffectGetEffectStatus(effect->hweffect->ref, &status);
+    if (ret != FF_OK) {
+        SDL_SetError("Haptic: Unable to get effect status: %s.", FFStrError(ret));
+        return -1;
+    }
+
+    if (status == 0) {
+        return 0;
+    }
+    return 1; // Assume it's playing or emulated.
+}
+
+/*
+ * Sets the gain.
+ */
+bool SDL_SYS_HapticSetGain(SDL_Haptic *haptic, int gain)
+{
+    HRESULT ret;
+    Uint32 val;
+
+    val = gain * 100; // macOS uses 0 to 10,000
+    ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device,
+                                           FFPROP_FFGAIN, &val);
+    if (ret != FF_OK) {
+        return SDL_SetError("Haptic: Error setting gain: %s.", FFStrError(ret));
+    }
+
+    return true;
+}
+
+/*
+ * Sets the autocentering.
+ */
+bool SDL_SYS_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter)
+{
+    HRESULT ret;
+    Uint32 val;
+
+    // macOS only has 0 (off) and 1 (on)
+    if (autocenter == 0) {
+        val = 0;
+    } else {
+        val = 1;
+    }
+
+    ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device,
+                                           FFPROP_AUTOCENTER, &val);
+    if (ret != FF_OK) {
+        return SDL_SetError("Haptic: Error setting autocenter: %s.",
+                            FFStrError(ret));
+    }
+
+    return true;
+}
+
+/*
+ * Pauses the device.
+ */
+bool SDL_SYS_HapticPause(SDL_Haptic *haptic)
+{
+    HRESULT ret;
+
+    ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
+                                           FFSFFC_PAUSE);
+    if (ret != FF_OK) {
+        return SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret));
+    }
+
+    return true;
+}
+
+/*
+ * Unpauses the device.
+ */
+bool SDL_SYS_HapticResume(SDL_Haptic *haptic)
+{
+    HRESULT ret;
+
+    ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
+                                           FFSFFC_CONTINUE);
+    if (ret != FF_OK) {
+        return SDL_SetError("Haptic: Error resuming device: %s.", FFStrError(ret));
+    }
+
+    return true;
+}
+
+/*
+ * Stops all currently playing effects.
+ */
+bool SDL_SYS_HapticStopAll(SDL_Haptic *haptic)
+{
+    HRESULT ret;
+
+    ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
+                                           FFSFFC_STOPALL);
+    if (ret != FF_OK) {
+        return SDL_SetError("Haptic: Error stopping device: %s.", FFStrError(ret));
+    }
+
+    return true;
+}
+
+#endif // SDL_HAPTIC_IOKIT

+ 29 - 0
thirdparty/sdl/haptic/darwin/SDL_syshaptic_c.h

@@ -0,0 +1,29 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+// Things named "Master" were renamed to "Main" in macOS 12.0's SDK.
+#include <AvailabilityMacros.h>
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 120000
+#define kIOMainPortDefault kIOMasterPortDefault
+#endif
+
+extern bool MacHaptic_MaybeAddDevice(io_object_t device);
+extern bool MacHaptic_MaybeRemoveDevice(io_object_t device);

+ 1134 - 0
thirdparty/sdl/haptic/linux/SDL_syshaptic.c

@@ -0,0 +1,1134 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+#include "SDL_internal.h"
+
+#ifdef SDL_HAPTIC_LINUX
+
+#include "../SDL_syshaptic.h"
+#include "../../joystick/SDL_sysjoystick.h"         // For the real SDL_Joystick
+#include "../../joystick/linux/SDL_sysjoystick_c.h" // For joystick hwdata
+#include "../../core/linux/SDL_evdev_capabilities.h"
+#include "../../core/linux/SDL_udev.h"
+
+#include <unistd.h>      // close
+#include <linux/input.h> // Force feedback linux stuff.
+#include <fcntl.h>       // O_RDWR
+#include <limits.h>      // INT_MAX
+#include <errno.h>       // errno
+#include <string.h>      // strerror
+#include <sys/stat.h>    // stat
+
+#define MAX_HAPTICS 32 // It's doubtful someone has more then 32 evdev
+
+static bool MaybeAddDevice(const char *path);
+#ifdef SDL_USE_LIBUDEV
+static bool MaybeRemoveDevice(const char *path);
+static void haptic_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath);
+#endif // SDL_USE_LIBUDEV
+
+/*
+ * List of available haptic devices.
+ */
+typedef struct SDL_hapticlist_item
+{
+    SDL_HapticID instance_id;
+    char *fname;        // Dev path name (like /dev/input/event1)
+    SDL_Haptic *haptic; // Associated haptic.
+    dev_t dev_num;
+    struct SDL_hapticlist_item *next;
+} SDL_hapticlist_item;
+
+/*
+ * Haptic system hardware data.
+ */
+struct haptic_hwdata
+{
+    int fd;      // File descriptor of the device.
+    char *fname; // Points to the name in SDL_hapticlist.
+};
+
+/*
+ * Haptic system effect data.
+ */
+struct haptic_hweffect
+{
+    struct ff_effect effect; // The linux kernel effect structure.
+};
+
+static SDL_hapticlist_item *SDL_hapticlist = NULL;
+static SDL_hapticlist_item *SDL_hapticlist_tail = NULL;
+static int numhaptics = 0;
+
+#define EV_TEST(ev, f)              \
+    if (test_bit((ev), features)) { \
+        ret |= (f);                 \
+    }
+/*
+ * Test whether a device has haptic properties.
+ * Returns available properties or 0 if there are none.
+ */
+static Uint32 EV_IsHaptic(int fd)
+{
+    unsigned long features[1 + FF_MAX / sizeof(unsigned long)];
+    Uint32 ret = 0;
+
+    // Ask device for what it has.
+    if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(features)), features) < 0) {
+        SDL_SetError("Haptic: Unable to get device's features: %s", strerror(errno));
+        return 0;
+    }
+
+    // Convert supported features to SDL_HAPTIC platform-neutral features.
+    EV_TEST(FF_CONSTANT, SDL_HAPTIC_CONSTANT);
+    EV_TEST(FF_SINE, SDL_HAPTIC_SINE);
+    EV_TEST(FF_SQUARE, SDL_HAPTIC_SQUARE);
+    EV_TEST(FF_TRIANGLE, SDL_HAPTIC_TRIANGLE);
+    EV_TEST(FF_SAW_UP, SDL_HAPTIC_SAWTOOTHUP);
+    EV_TEST(FF_SAW_DOWN, SDL_HAPTIC_SAWTOOTHDOWN);
+    EV_TEST(FF_RAMP, SDL_HAPTIC_RAMP);
+    EV_TEST(FF_SPRING, SDL_HAPTIC_SPRING);
+    EV_TEST(FF_FRICTION, SDL_HAPTIC_FRICTION);
+    EV_TEST(FF_DAMPER, SDL_HAPTIC_DAMPER);
+    EV_TEST(FF_INERTIA, SDL_HAPTIC_INERTIA);
+    EV_TEST(FF_CUSTOM, SDL_HAPTIC_CUSTOM);
+    EV_TEST(FF_GAIN, SDL_HAPTIC_GAIN);
+    EV_TEST(FF_AUTOCENTER, SDL_HAPTIC_AUTOCENTER);
+    EV_TEST(FF_RUMBLE, SDL_HAPTIC_LEFTRIGHT);
+
+    // Return what it supports.
+    return ret;
+}
+
+/*
+ * Tests whether a device is a mouse or not.
+ */
+static bool EV_IsMouse(int fd)
+{
+    unsigned long argp[40];
+
+    // Ask for supported features.
+    if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(argp)), argp) < 0) {
+        return false;
+    }
+
+    // Currently we only test for BTN_MOUSE which can give fake positives.
+    if (test_bit(BTN_MOUSE, argp) != 0) {
+        return true;
+    }
+
+    return true;
+}
+
+/*
+ * Initializes the haptic subsystem by finding available devices.
+ */
+bool SDL_SYS_HapticInit(void)
+{
+    const char joydev_pattern[] = "/dev/input/event%d";
+    char path[PATH_MAX];
+    int i, j;
+
+    /*
+     * Limit amount of checks to MAX_HAPTICS since we may or may not have
+     * permission to some or all devices.
+     */
+    i = 0;
+    for (j = 0; j < MAX_HAPTICS; ++j) {
+        (void)SDL_snprintf(path, PATH_MAX, joydev_pattern, i++);
+        MaybeAddDevice(path);
+    }
+
+#ifdef SDL_USE_LIBUDEV
+    if (!SDL_UDEV_Init()) {
+        return SDL_SetError("Could not initialize UDEV");
+    }
+
+    if (!SDL_UDEV_AddCallback(haptic_udev_callback)) {
+        SDL_UDEV_Quit();
+        return SDL_SetError("Could not setup haptic <-> udev callback");
+    }
+
+    // Force a scan to build the initial device list
+    SDL_UDEV_Scan();
+#endif // SDL_USE_LIBUDEV
+
+    return true;
+}
+
+int SDL_SYS_NumHaptics(void)
+{
+    return numhaptics;
+}
+
+static SDL_hapticlist_item *HapticByDevIndex(int device_index)
+{
+    SDL_hapticlist_item *item = SDL_hapticlist;
+
+    if ((device_index < 0) || (device_index >= numhaptics)) {
+        return NULL;
+    }
+
+    while (device_index > 0) {
+        SDL_assert(item != NULL);
+        --device_index;
+        item = item->next;
+    }
+
+    return item;
+}
+
+static SDL_hapticlist_item *HapticByInstanceID(SDL_HapticID instance_id)
+{
+    SDL_hapticlist_item *item;
+    for (item = SDL_hapticlist; item; item = item->next) {
+        if (instance_id == item->instance_id) {
+            return item;
+        }
+    }
+    return NULL;
+}
+
+#ifdef SDL_USE_LIBUDEV
+static void haptic_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath)
+{
+    if (!devpath || !(udev_class & SDL_UDEV_DEVICE_JOYSTICK)) {
+        return;
+    }
+
+    switch (udev_type) {
+    case SDL_UDEV_DEVICEADDED:
+        MaybeAddDevice(devpath);
+        break;
+
+    case SDL_UDEV_DEVICEREMOVED:
+        MaybeRemoveDevice(devpath);
+        break;
+
+    default:
+        break;
+    }
+}
+#endif // SDL_USE_LIBUDEV
+
+static bool MaybeAddDevice(const char *path)
+{
+    struct stat sb;
+    int fd;
+    Uint32 supported;
+    SDL_hapticlist_item *item;
+
+    if (!path) {
+        return false;
+    }
+
+    // try to open
+    fd = open(path, O_RDWR | O_CLOEXEC, 0);
+    if (fd < 0) {
+        return false;
+    }
+
+    // get file status
+    if (fstat(fd, &sb) != 0) {
+        close(fd);
+        return false;
+    }
+
+    // check for duplicates
+    for (item = SDL_hapticlist; item; item = item->next) {
+        if (item->dev_num == sb.st_rdev) {
+            close(fd);
+            return false; // duplicate.
+        }
+    }
+
+#ifdef DEBUG_INPUT_EVENTS
+    printf("Checking %s\n", path);
+#endif
+
+    // see if it works
+    supported = EV_IsHaptic(fd);
+    close(fd);
+    if (!supported) {
+        return false;
+    }
+
+    item = (SDL_hapticlist_item *)SDL_calloc(1, sizeof(SDL_hapticlist_item));
+    if (!item) {
+        return false;
+    }
+
+    item->instance_id = SDL_GetNextObjectID();
+    item->fname = SDL_strdup(path);
+    if (!item->fname) {
+        SDL_free(item);
+        return false;
+    }
+
+    item->dev_num = sb.st_rdev;
+
+    // TODO: should we add instance IDs?
+    if (!SDL_hapticlist_tail) {
+        SDL_hapticlist = SDL_hapticlist_tail = item;
+    } else {
+        SDL_hapticlist_tail->next = item;
+        SDL_hapticlist_tail = item;
+    }
+
+    ++numhaptics;
+
+    // !!! TODO: Send a haptic add event?
+
+    return true;
+}
+
+#ifdef SDL_USE_LIBUDEV
+static bool MaybeRemoveDevice(const char *path)
+{
+    SDL_hapticlist_item *item;
+    SDL_hapticlist_item *prev = NULL;
+
+    if (!path) {
+        return false;
+    }
+
+    for (item = SDL_hapticlist; item; item = item->next) {
+        // found it, remove it.
+        if (SDL_strcmp(path, item->fname) == 0) {
+            const bool result = item->haptic ? true : false;
+
+            if (prev) {
+                prev->next = item->next;
+            } else {
+                SDL_assert(SDL_hapticlist == item);
+                SDL_hapticlist = item->next;
+            }
+            if (item == SDL_hapticlist_tail) {
+                SDL_hapticlist_tail = prev;
+            }
+
+            // Need to decrement the haptic count
+            --numhaptics;
+            // !!! TODO: Send a haptic remove event?
+
+            SDL_free(item->fname);
+            SDL_free(item);
+            return result;
+        }
+        prev = item;
+    }
+
+    return false;
+}
+#endif // SDL_USE_LIBUDEV
+
+/*
+ * Return the instance ID of a haptic device, does not need to be opened.
+ */
+SDL_HapticID SDL_SYS_HapticInstanceID(int index)
+{
+    SDL_hapticlist_item *item;
+
+    item = HapticByDevIndex(index);
+    if (item) {
+        return item->instance_id;
+    }
+    return 0;
+}
+
+/*
+ * Gets the name from a file descriptor.
+ */
+static const char *SDL_SYS_HapticNameFromFD(int fd)
+{
+    static char namebuf[128];
+
+    // We use the evdev name ioctl.
+    if (ioctl(fd, EVIOCGNAME(sizeof(namebuf)), namebuf) <= 0) {
+        return NULL;
+    }
+
+    return namebuf;
+}
+
+/*
+ * Return the name of a haptic device, does not need to be opened.
+ */
+const char *SDL_SYS_HapticName(int index)
+{
+    SDL_hapticlist_item *item;
+    int fd;
+    const char *name = NULL;
+
+    item = HapticByDevIndex(index);
+    if (item) {
+        // Open the haptic device.
+        fd = open(item->fname, O_RDONLY | O_CLOEXEC, 0);
+
+        if (fd >= 0) {
+
+            name = SDL_SYS_HapticNameFromFD(fd);
+            if (!name) {
+                // No name found, return device character device
+                name = item->fname;
+            }
+            close(fd);
+        }
+    }
+    return name;
+}
+
+/*
+ * Opens the haptic device from the file descriptor.
+ */
+static bool SDL_SYS_HapticOpenFromFD(SDL_Haptic *haptic, int fd)
+{
+    // Allocate the hwdata
+    haptic->hwdata = (struct haptic_hwdata *)
+        SDL_calloc(1, sizeof(*haptic->hwdata));
+    if (!haptic->hwdata) {
+        goto open_err;
+    }
+
+    // Set the data.
+    haptic->hwdata->fd = fd;
+    haptic->supported = EV_IsHaptic(fd);
+    haptic->naxes = 2; // Hardcoded for now, not sure if it's possible to find out.
+
+    // Set the effects
+    if (ioctl(fd, EVIOCGEFFECTS, &haptic->neffects) < 0) {
+        SDL_SetError("Haptic: Unable to query device memory: %s",
+                     strerror(errno));
+        goto open_err;
+    }
+    haptic->nplaying = haptic->neffects; // Linux makes no distinction.
+    haptic->effects = (struct haptic_effect *)
+        SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
+    if (!haptic->effects) {
+        goto open_err;
+    }
+    // Clear the memory
+    SDL_memset(haptic->effects, 0,
+               sizeof(struct haptic_effect) * haptic->neffects);
+
+    return true;
+
+    // Error handling
+open_err:
+    close(fd);
+    if (haptic->hwdata) {
+        SDL_free(haptic->hwdata);
+        haptic->hwdata = NULL;
+    }
+    return false;
+}
+
+/*
+ * Opens a haptic device for usage.
+ */
+bool SDL_SYS_HapticOpen(SDL_Haptic *haptic)
+{
+    int fd;
+    SDL_hapticlist_item *item;
+
+    item = HapticByInstanceID(haptic->instance_id);
+    // Open the character device
+    fd = open(item->fname, O_RDWR | O_CLOEXEC, 0);
+    if (fd < 0) {
+        return SDL_SetError("Haptic: Unable to open %s: %s",
+                            item->fname, strerror(errno));
+    }
+
+    // Try to create the haptic.
+    if (!SDL_SYS_HapticOpenFromFD(haptic, fd)) {
+        // Already closes on error.
+        return false;
+    }
+
+    // Set the fname.
+    haptic->hwdata->fname = SDL_strdup(item->fname);
+    return true;
+}
+
+/*
+ * Opens a haptic device from first mouse it finds for usage.
+ */
+int SDL_SYS_HapticMouse(void)
+{
+    int fd;
+    int device_index = 0;
+    SDL_hapticlist_item *item;
+
+    for (item = SDL_hapticlist; item; item = item->next) {
+        // Open the device.
+        fd = open(item->fname, O_RDWR | O_CLOEXEC, 0);
+        if (fd < 0) {
+            return SDL_SetError("Haptic: Unable to open %s: %s",
+                                item->fname, strerror(errno));
+        }
+
+        // Is it a mouse?
+        if (EV_IsMouse(fd)) {
+            close(fd);
+            return device_index;
+        }
+
+        close(fd);
+
+        ++device_index;
+    }
+
+    return -1;
+}
+
+/*
+ * Checks to see if a joystick has haptic features.
+ */
+bool SDL_SYS_JoystickIsHaptic(SDL_Joystick *joystick)
+{
+#ifdef SDL_JOYSTICK_LINUX
+    SDL_AssertJoysticksLocked();
+
+    if (joystick->driver != &SDL_LINUX_JoystickDriver) {
+        return false;
+    }
+    if (EV_IsHaptic(joystick->hwdata->fd)) {
+        return true;
+    }
+#endif
+    return false;
+}
+
+/*
+ * Checks to see if the haptic device and joystick are in reality the same.
+ */
+bool SDL_SYS_JoystickSameHaptic(SDL_Haptic *haptic, SDL_Joystick *joystick)
+{
+#ifdef SDL_JOYSTICK_LINUX
+    SDL_AssertJoysticksLocked();
+
+    if (joystick->driver != &SDL_LINUX_JoystickDriver) {
+        return false;
+    }
+    /* We are assuming Linux is using evdev which should trump the old
+     * joystick methods. */
+    if (SDL_strcmp(joystick->hwdata->fname, haptic->hwdata->fname) == 0) {
+        return true;
+    }
+#endif
+    return false;
+}
+
+/*
+ * Opens a SDL_Haptic from a SDL_Joystick.
+ */
+bool SDL_SYS_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick)
+{
+#ifdef SDL_JOYSTICK_LINUX
+    int fd;
+    SDL_hapticlist_item *item;
+    const char *name;
+
+    SDL_AssertJoysticksLocked();
+
+    if (joystick->driver != &SDL_LINUX_JoystickDriver) {
+        return false;
+    }
+    // Find the joystick in the haptic list.
+    for (item = SDL_hapticlist; item; item = item->next) {
+        if (SDL_strcmp(item->fname, joystick->hwdata->fname) == 0) {
+            haptic->instance_id = item->instance_id;
+            break;
+        }
+    }
+
+    fd = open(joystick->hwdata->fname, O_RDWR | O_CLOEXEC, 0);
+    if (fd < 0) {
+        return SDL_SetError("Haptic: Unable to open %s: %s",
+                            joystick->hwdata->fname, strerror(errno));
+    }
+    if (!SDL_SYS_HapticOpenFromFD(haptic, fd)) {
+        // Already closes on error.
+        return false;
+    }
+
+    haptic->hwdata->fname = SDL_strdup(joystick->hwdata->fname);
+
+    name = SDL_SYS_HapticNameFromFD(fd);
+    if (name) {
+        haptic->name = SDL_strdup(name);
+    }
+    return true;
+#else
+    return false;
+#endif
+}
+
+/*
+ * Closes the haptic device.
+ */
+void SDL_SYS_HapticClose(SDL_Haptic *haptic)
+{
+    if (haptic->hwdata) {
+
+        // Free effects.
+        SDL_free(haptic->effects);
+        haptic->effects = NULL;
+        haptic->neffects = 0;
+
+        // Clean up
+        close(haptic->hwdata->fd);
+
+        // Free
+        SDL_free(haptic->hwdata->fname);
+        SDL_free(haptic->hwdata);
+        haptic->hwdata = NULL;
+    }
+
+    // Clear the rest.
+    SDL_memset(haptic, 0, sizeof(SDL_Haptic));
+}
+
+/*
+ * Clean up after system specific haptic stuff
+ */
+void SDL_SYS_HapticQuit(void)
+{
+    SDL_hapticlist_item *item = NULL;
+    SDL_hapticlist_item *next = NULL;
+
+    for (item = SDL_hapticlist; item; item = next) {
+        next = item->next;
+        /* Opened and not closed haptics are leaked, this is on purpose.
+         * Close your haptic devices after usage. */
+        SDL_free(item->fname);
+        SDL_free(item);
+    }
+
+#ifdef SDL_USE_LIBUDEV
+    SDL_UDEV_DelCallback(haptic_udev_callback);
+    SDL_UDEV_Quit();
+#endif // SDL_USE_LIBUDEV
+
+    numhaptics = 0;
+    SDL_hapticlist = NULL;
+    SDL_hapticlist_tail = NULL;
+}
+
+/*
+ * Converts an SDL button to a ff_trigger button.
+ */
+static Uint16 SDL_SYS_ToButton(Uint16 button)
+{
+    Uint16 ff_button;
+
+    ff_button = 0;
+
+    /*
+     * Not sure what the proper syntax is because this actually isn't implemented
+     * in the current kernel from what I've seen (2.6.26).
+     */
+    if (button != 0) {
+        ff_button = BTN_GAMEPAD + button - 1;
+    }
+
+    return ff_button;
+}
+
+/*
+ * Initializes the ff_effect usable direction from a SDL_HapticDirection.
+ */
+static bool SDL_SYS_ToDirection(Uint16 *dest, const SDL_HapticDirection *src)
+{
+    Uint32 tmp;
+
+    switch (src->type) {
+    case SDL_HAPTIC_POLAR:
+        tmp = ((src->dir[0] % 36000) * 0x8000) / 18000; // convert to range [0,0xFFFF]
+        *dest = (Uint16)tmp;
+        break;
+
+    case SDL_HAPTIC_SPHERICAL:
+        /*
+            We convert to polar, because that's the only supported direction on Linux.
+            The first value of a spherical direction is practically the same as a
+            Polar direction, except that we have to add 90 degrees. It is the angle
+            from EAST {1,0} towards SOUTH {0,1}.
+            --> add 9000
+            --> finally convert to [0,0xFFFF] as in case SDL_HAPTIC_POLAR.
+        */
+        tmp = ((src->dir[0]) + 9000) % 36000; // Convert to polars
+        tmp = (tmp * 0x8000) / 18000;         // convert to range [0,0xFFFF]
+        *dest = (Uint16)tmp;
+        break;
+
+    case SDL_HAPTIC_CARTESIAN:
+        if (!src->dir[1]) {
+            *dest = (src->dir[0] >= 0 ? 0x4000 : 0xC000);
+        } else if (!src->dir[0]) {
+            *dest = (src->dir[1] >= 0 ? 0x8000 : 0);
+        } else {
+            float f = SDL_atan2f(src->dir[1], src->dir[0]); // Ideally we'd use fixed point math instead of floats...
+            /*
+              SDL_atan2 takes the parameters: Y-axis-value and X-axis-value (in that order)
+               - Y-axis-value is the second coordinate (from center to SOUTH)
+               - X-axis-value is the first coordinate (from center to EAST)
+                We add 36000, because SDL_atan2 also returns negative values. Then we practically
+                have the first spherical value. Therefore we proceed as in case
+                SDL_HAPTIC_SPHERICAL and add another 9000 to get the polar value.
+              --> add 45000 in total
+              --> finally convert to [0,0xFFFF] as in case SDL_HAPTIC_POLAR.
+            */
+            tmp = (((Sint32)(f * 18000.0 / SDL_PI_D)) + 45000) % 36000;
+            tmp = (tmp * 0x8000) / 18000; // convert to range [0,0xFFFF]
+            *dest = (Uint16)tmp;
+        }
+        break;
+    case SDL_HAPTIC_STEERING_AXIS:
+        *dest = 0x4000;
+        break;
+    default:
+        return SDL_SetError("Haptic: Unsupported direction type.");
+    }
+
+    return true;
+}
+
+#define CLAMP(x) (((x) > 32767) ? 32767 : x)
+/*
+ * Initializes the Linux effect struct from a haptic_effect.
+ * Values above 32767 (for unsigned) are unspecified so we must clamp.
+ */
+static bool SDL_SYS_ToFFEffect(struct ff_effect *dest, const SDL_HapticEffect *src)
+{
+    const SDL_HapticConstant *constant;
+    const SDL_HapticPeriodic *periodic;
+    const SDL_HapticCondition *condition;
+    const SDL_HapticRamp *ramp;
+    const SDL_HapticLeftRight *leftright;
+
+    // Clear up
+    SDL_memset(dest, 0, sizeof(struct ff_effect));
+
+    switch (src->type) {
+    case SDL_HAPTIC_CONSTANT:
+        constant = &src->constant;
+
+        // Header
+        dest->type = FF_CONSTANT;
+        if (!SDL_SYS_ToDirection(&dest->direction, &constant->direction)) {
+            return false;
+        }
+
+        // Replay
+        dest->replay.length = (constant->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(constant->length);
+        dest->replay.delay = CLAMP(constant->delay);
+
+        // Trigger
+        dest->trigger.button = SDL_SYS_ToButton(constant->button);
+        dest->trigger.interval = CLAMP(constant->interval);
+
+        // Constant
+        dest->u.constant.level = constant->level;
+
+        // Envelope
+        dest->u.constant.envelope.attack_length =
+            CLAMP(constant->attack_length);
+        dest->u.constant.envelope.attack_level =
+            CLAMP(constant->attack_level);
+        dest->u.constant.envelope.fade_length = CLAMP(constant->fade_length);
+        dest->u.constant.envelope.fade_level = CLAMP(constant->fade_level);
+
+        break;
+
+    case SDL_HAPTIC_SINE:
+    case SDL_HAPTIC_SQUARE:
+    case SDL_HAPTIC_TRIANGLE:
+    case SDL_HAPTIC_SAWTOOTHUP:
+    case SDL_HAPTIC_SAWTOOTHDOWN:
+        periodic = &src->periodic;
+
+        // Header
+        dest->type = FF_PERIODIC;
+        if (!SDL_SYS_ToDirection(&dest->direction, &periodic->direction)) {
+            return false;
+        }
+
+        // Replay
+        dest->replay.length = (periodic->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(periodic->length);
+        dest->replay.delay = CLAMP(periodic->delay);
+
+        // Trigger
+        dest->trigger.button = SDL_SYS_ToButton(periodic->button);
+        dest->trigger.interval = CLAMP(periodic->interval);
+
+        // Periodic
+        if (periodic->type == SDL_HAPTIC_SINE) {
+            dest->u.periodic.waveform = FF_SINE;
+        } else if (periodic->type == SDL_HAPTIC_SQUARE) {
+            dest->u.periodic.waveform = FF_SQUARE;
+        } else if (periodic->type == SDL_HAPTIC_TRIANGLE) {
+            dest->u.periodic.waveform = FF_TRIANGLE;
+        } else if (periodic->type == SDL_HAPTIC_SAWTOOTHUP) {
+            dest->u.periodic.waveform = FF_SAW_UP;
+        } else if (periodic->type == SDL_HAPTIC_SAWTOOTHDOWN) {
+            dest->u.periodic.waveform = FF_SAW_DOWN;
+        }
+        dest->u.periodic.period = CLAMP(periodic->period);
+        dest->u.periodic.magnitude = periodic->magnitude;
+        dest->u.periodic.offset = periodic->offset;
+        // Linux phase is defined in interval "[0x0000, 0x10000[", corresponds with "[0deg, 360deg[" phase shift.
+        dest->u.periodic.phase = ((Uint32)periodic->phase * 0x10000U) / 36000;
+
+        // Envelope
+        dest->u.periodic.envelope.attack_length =
+            CLAMP(periodic->attack_length);
+        dest->u.periodic.envelope.attack_level =
+            CLAMP(periodic->attack_level);
+        dest->u.periodic.envelope.fade_length = CLAMP(periodic->fade_length);
+        dest->u.periodic.envelope.fade_level = CLAMP(periodic->fade_level);
+
+        break;
+
+    case SDL_HAPTIC_SPRING:
+    case SDL_HAPTIC_DAMPER:
+    case SDL_HAPTIC_INERTIA:
+    case SDL_HAPTIC_FRICTION:
+        condition = &src->condition;
+
+        // Header
+        if (condition->type == SDL_HAPTIC_SPRING) {
+            dest->type = FF_SPRING;
+        } else if (condition->type == SDL_HAPTIC_DAMPER) {
+            dest->type = FF_DAMPER;
+        } else if (condition->type == SDL_HAPTIC_INERTIA) {
+            dest->type = FF_INERTIA;
+        } else if (condition->type == SDL_HAPTIC_FRICTION) {
+            dest->type = FF_FRICTION;
+        }
+
+        if (!SDL_SYS_ToDirection(&dest->direction, &condition->direction)) {
+            return false;
+        }
+
+        // Replay
+        dest->replay.length = (condition->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(condition->length);
+        dest->replay.delay = CLAMP(condition->delay);
+
+        // Trigger
+        dest->trigger.button = SDL_SYS_ToButton(condition->button);
+        dest->trigger.interval = CLAMP(condition->interval);
+
+        // Condition
+        // X axis
+        dest->u.condition[0].right_saturation = condition->right_sat[0];
+        dest->u.condition[0].left_saturation = condition->left_sat[0];
+        dest->u.condition[0].right_coeff = condition->right_coeff[0];
+        dest->u.condition[0].left_coeff = condition->left_coeff[0];
+        dest->u.condition[0].deadband = condition->deadband[0];
+        dest->u.condition[0].center = condition->center[0];
+        // Y axis
+        dest->u.condition[1].right_saturation = condition->right_sat[1];
+        dest->u.condition[1].left_saturation = condition->left_sat[1];
+        dest->u.condition[1].right_coeff = condition->right_coeff[1];
+        dest->u.condition[1].left_coeff = condition->left_coeff[1];
+        dest->u.condition[1].deadband = condition->deadband[1];
+        dest->u.condition[1].center = condition->center[1];
+
+        /*
+         * There is no envelope in the linux force feedback api for conditions.
+         */
+
+        break;
+
+    case SDL_HAPTIC_RAMP:
+        ramp = &src->ramp;
+
+        // Header
+        dest->type = FF_RAMP;
+        if (!SDL_SYS_ToDirection(&dest->direction, &ramp->direction)) {
+            return false;
+        }
+
+        // Replay
+        dest->replay.length = (ramp->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(ramp->length);
+        dest->replay.delay = CLAMP(ramp->delay);
+
+        // Trigger
+        dest->trigger.button = SDL_SYS_ToButton(ramp->button);
+        dest->trigger.interval = CLAMP(ramp->interval);
+
+        // Ramp
+        dest->u.ramp.start_level = ramp->start;
+        dest->u.ramp.end_level = ramp->end;
+
+        // Envelope
+        dest->u.ramp.envelope.attack_length = CLAMP(ramp->attack_length);
+        dest->u.ramp.envelope.attack_level = CLAMP(ramp->attack_level);
+        dest->u.ramp.envelope.fade_length = CLAMP(ramp->fade_length);
+        dest->u.ramp.envelope.fade_level = CLAMP(ramp->fade_level);
+
+        break;
+
+    case SDL_HAPTIC_LEFTRIGHT:
+        leftright = &src->leftright;
+
+        // Header
+        dest->type = FF_RUMBLE;
+        dest->direction = 0x4000;
+
+        // Replay
+        dest->replay.length = (leftright->length == SDL_HAPTIC_INFINITY) ? 0 : CLAMP(leftright->length);
+
+        // Trigger
+        dest->trigger.button = 0;
+        dest->trigger.interval = 0;
+
+        // Rumble (Linux expects 0-65535, so multiply by 2)
+        dest->u.rumble.strong_magnitude = CLAMP(leftright->large_magnitude) * 2;
+        dest->u.rumble.weak_magnitude = CLAMP(leftright->small_magnitude) * 2;
+
+        break;
+
+    default:
+        return SDL_SetError("Haptic: Unknown effect type.");
+    }
+
+    return true;
+}
+
+/*
+ * Creates a new haptic effect.
+ */
+bool SDL_SYS_HapticNewEffect(SDL_Haptic *haptic, struct haptic_effect *effect,
+                            const SDL_HapticEffect *base)
+{
+    struct ff_effect *linux_effect;
+
+    // Allocate the hardware effect
+    effect->hweffect = (struct haptic_hweffect *)
+        SDL_calloc(1, sizeof(struct haptic_hweffect));
+    if (!effect->hweffect) {
+        return false;
+    }
+
+    // Prepare the ff_effect
+    linux_effect = &effect->hweffect->effect;
+    if (!SDL_SYS_ToFFEffect(linux_effect, base)) {
+        goto new_effect_err;
+    }
+    linux_effect->id = -1; // Have the kernel give it an id
+
+    // Upload the effect
+    if (ioctl(haptic->hwdata->fd, EVIOCSFF, linux_effect) < 0) {
+        SDL_SetError("Haptic: Error uploading effect to the device: %s",
+                     strerror(errno));
+        goto new_effect_err;
+    }
+
+    return true;
+
+new_effect_err:
+    SDL_free(effect->hweffect);
+    effect->hweffect = NULL;
+    return false;
+}
+
+/*
+ * Updates an effect.
+ *
+ * Note: Dynamically updating the direction can in some cases force
+ * the effect to restart and run once.
+ */
+bool SDL_SYS_HapticUpdateEffect(SDL_Haptic *haptic,
+                               struct haptic_effect *effect,
+                               const SDL_HapticEffect *data)
+{
+    struct ff_effect linux_effect;
+
+    // Create the new effect
+    if (!SDL_SYS_ToFFEffect(&linux_effect, data)) {
+        return false;
+    }
+    linux_effect.id = effect->hweffect->effect.id;
+
+    // See if it can be uploaded.
+    if (ioctl(haptic->hwdata->fd, EVIOCSFF, &linux_effect) < 0) {
+        return SDL_SetError("Haptic: Error updating the effect: %s",
+                            strerror(errno));
+    }
+
+    // Copy the new effect into memory.
+    SDL_memcpy(&effect->hweffect->effect, &linux_effect,
+               sizeof(struct ff_effect));
+
+    return true;
+}
+
+/*
+ * Runs an effect.
+ */
+bool SDL_SYS_HapticRunEffect(SDL_Haptic *haptic, struct haptic_effect *effect,
+                            Uint32 iterations)
+{
+    struct input_event run;
+
+    // Prepare to run the effect
+    run.type = EV_FF;
+    run.code = effect->hweffect->effect.id;
+    // We don't actually have infinity here, so we just do INT_MAX which is pretty damn close.
+    run.value = (iterations > INT_MAX) ? INT_MAX : iterations;
+
+    if (write(haptic->hwdata->fd, (const void *)&run, sizeof(run)) < 0) {
+        return SDL_SetError("Haptic: Unable to run the effect: %s", strerror(errno));
+    }
+
+    return true;
+}
+
+/*
+ * Stops an effect.
+ */
+bool SDL_SYS_HapticStopEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
+{
+    struct input_event stop;
+
+    stop.type = EV_FF;
+    stop.code = effect->hweffect->effect.id;
+    stop.value = 0;
+
+    if (write(haptic->hwdata->fd, (const void *)&stop, sizeof(stop)) < 0) {
+        return SDL_SetError("Haptic: Unable to stop the effect: %s",
+                            strerror(errno));
+    }
+
+    return true;
+}
+
+/*
+ * Frees the effect.
+ */
+void SDL_SYS_HapticDestroyEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
+{
+    if (ioctl(haptic->hwdata->fd, EVIOCRMFF, effect->hweffect->effect.id) < 0) {
+        SDL_SetError("Haptic: Error removing the effect from the device: %s",
+                     strerror(errno));
+    }
+    SDL_free(effect->hweffect);
+    effect->hweffect = NULL;
+}
+
+/*
+ * Gets the status of a haptic effect.
+ */
+int SDL_SYS_HapticGetEffectStatus(SDL_Haptic *haptic,
+                                  struct haptic_effect *effect)
+{
+#if 0 // Not supported atm.
+    struct input_event ie;
+
+    ie.type = EV_FF;
+    ie.type = EV_FF_STATUS;
+    ie.code = effect->hweffect->effect.id;
+
+    if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
+        SDL_SetError("Haptic: Error getting device status.");
+        return -1;
+    }
+
+    return 1;
+#endif
+
+    SDL_Unsupported();
+    return -1;
+}
+
+/*
+ * Sets the gain.
+ */
+bool SDL_SYS_HapticSetGain(SDL_Haptic *haptic, int gain)
+{
+    struct input_event ie;
+
+    ie.type = EV_FF;
+    ie.code = FF_GAIN;
+    ie.value = (0xFFFFUL * gain) / 100;
+
+    if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
+        return SDL_SetError("Haptic: Error setting gain: %s", strerror(errno));
+    }
+
+    return true;
+}
+
+/*
+ * Sets the autocentering.
+ */
+bool SDL_SYS_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter)
+{
+    struct input_event ie;
+
+    ie.type = EV_FF;
+    ie.code = FF_AUTOCENTER;
+    ie.value = (0xFFFFUL * autocenter) / 100;
+
+    if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
+        return SDL_SetError("Haptic: Error setting autocenter: %s", strerror(errno));
+    }
+
+    return true;
+}
+
+/*
+ * Pausing is not supported atm by linux.
+ */
+bool SDL_SYS_HapticPause(SDL_Haptic *haptic)
+{
+    return SDL_Unsupported();
+}
+
+/*
+ * Unpausing is not supported atm by linux.
+ */
+bool SDL_SYS_HapticResume(SDL_Haptic *haptic)
+{
+    return SDL_Unsupported();
+}
+
+/*
+ * Stops all the currently playing effects.
+ */
+bool SDL_SYS_HapticStopAll(SDL_Haptic *haptic)
+{
+    int i, ret;
+
+    // Linux does not support this natively so we have to loop.
+    for (i = 0; i < haptic->neffects; i++) {
+        if (haptic->effects[i].hweffect != NULL) {
+            ret = SDL_SYS_HapticStopEffect(haptic, &haptic->effects[i]);
+            if (ret < 0) {
+                return SDL_SetError("Haptic: Error while trying to stop all playing effects.");
+            }
+        }
+    }
+    return true;
+}
+
+#endif // SDL_HAPTIC_LINUX

Some files were not shown because too many files changed in this diff