Procházet zdrojové kódy

Merge pull request #106218 from Nintorch/master

Add support for SDL3 joystick input driver for Windows, Linux and macOS
Thaddeus Crews před 1 měsícem
rodič
revize
ab134b386a
100 změnil soubory, kde provedl 26456 přidání a 1561 odebrání
  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

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů