initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
This commit is contained in:
38
thirdparty/sdl/CREDITS.md
vendored
Normal file
38
thirdparty/sdl/CREDITS.md
vendored
Normal file
@@ -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 <slouken@libsdl.org>
|
||||
|
||||
18
thirdparty/sdl/LICENSE.txt
vendored
Normal file
18
thirdparty/sdl/LICENSE.txt
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/SDL.c
vendored
Normal file
841
thirdparty/sdl/SDL.c
vendored
Normal file
@@ -0,0 +1,841 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/SDL_assert.c
vendored
Normal file
443
thirdparty/sdl/SDL_assert.c
vendored
Normal file
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/SDL_assert_c.h
vendored
Normal file
28
thirdparty/sdl/SDL_assert_c.h
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/SDL_error.c
vendored
Normal file
112
thirdparty/sdl/SDL_error.c
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/SDL_error_c.h
vendored
Normal file
61
thirdparty/sdl/SDL_error_c.h
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/SDL_guid.c
vendored
Normal file
88
thirdparty/sdl/SDL_guid.c
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/SDL_hashtable.c
vendored
Normal file
543
thirdparty/sdl/SDL_hashtable.c
vendored
Normal file
@@ -0,0 +1,543 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/SDL_hashtable.h
vendored
Normal file
633
thirdparty/sdl/SDL_hashtable.h
vendored
Normal file
@@ -0,0 +1,633 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/SDL_hints.c
vendored
Normal file
403
thirdparty/sdl/SDL_hints.c
vendored
Normal file
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/SDL_hints_c.h
vendored
Normal file
33
thirdparty/sdl/SDL_hints_c.h
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/SDL_internal.h
vendored
Normal file
289
thirdparty/sdl/SDL_internal.h
vendored
Normal file
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/SDL_list.c
vendored
Normal file
86
thirdparty/sdl/SDL_list.c
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/SDL_list.h
vendored
Normal file
36
thirdparty/sdl/SDL_list.h
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/SDL_log.c
vendored
Normal file
805
thirdparty/sdl/SDL_log.c
vendored
Normal file
@@ -0,0 +1,805 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/SDL_log_c.h
vendored
Normal file
31
thirdparty/sdl/SDL_log_c.h
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/SDL_properties.c
vendored
Normal file
824
thirdparty/sdl/SDL_properties.c
vendored
Normal file
@@ -0,0 +1,824 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/SDL_properties_c.h
vendored
Normal file
26
thirdparty/sdl/SDL_properties_c.h
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/SDL_utils.c
vendored
Normal file
554
thirdparty/sdl/SDL_utils.c
vendored
Normal file
@@ -0,0 +1,554 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/SDL_utils_c.h
vendored
Normal file
78
thirdparty/sdl/SDL_utils_c.h
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/atomic/SDL_atomic.c
vendored
Normal file
382
thirdparty/sdl/atomic/SDL_atomic.c
vendored
Normal file
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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, bob@pendleton.com
|
||||
*/
|
||||
|
||||
#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
thirdparty/sdl/atomic/SDL_spinlock.c
vendored
Normal file
203
thirdparty/sdl/atomic/SDL_spinlock.c
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
|
||||
}
|
||||
647
thirdparty/sdl/core/linux/SDL_dbus.c
vendored
Normal file
647
thirdparty/sdl/core/linux/SDL_dbus.c
vendored
Normal file
@@ -0,0 +1,647 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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)
|
||||
{
|
||||
#ifdef SOWRAP_ENABLED // Godot build system constant
|
||||
if (dbus_handle) {
|
||||
SDL_UnloadObject(dbus_handle);
|
||||
dbus_handle = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool LoadDBUSLibrary(void)
|
||||
{
|
||||
bool result = true;
|
||||
#ifdef SOWRAP_ENABLED // Godot build system constant
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
result = LoadDBUSSyms();
|
||||
#endif
|
||||
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
thirdparty/sdl/core/linux/SDL_dbus.h
vendored
Normal file
114
thirdparty/sdl/core/linux/SDL_dbus.h
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/linux/SDL_evdev.c
vendored
Normal file
1037
thirdparty/sdl/core/linux/SDL_evdev.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
41
thirdparty/sdl/core/linux/SDL_evdev.h
vendored
Normal file
41
thirdparty/sdl/core/linux/SDL_evdev.h
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/linux/SDL_evdev_capabilities.c
vendored
Normal file
168
thirdparty/sdl/core/linux/SDL_evdev_capabilities.c
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
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
thirdparty/sdl/core/linux/SDL_evdev_capabilities.h
vendored
Normal file
76
thirdparty/sdl/core/linux/SDL_evdev_capabilities.h
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
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
thirdparty/sdl/core/linux/SDL_evdev_kbd.c
vendored
Normal file
997
thirdparty/sdl/core/linux/SDL_evdev_kbd.c
vendored
Normal file
@@ -0,0 +1,997 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/linux/SDL_evdev_kbd.h
vendored
Normal file
35
thirdparty/sdl/core/linux/SDL_evdev_kbd.h
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/linux/SDL_evdev_kbd_default_accents.h
vendored
Normal file
282
thirdparty/sdl/core/linux/SDL_evdev_kbd_default_accents.h
vendored
Normal file
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/linux/SDL_evdev_kbd_default_keymap.h
vendored
Normal file
4765
thirdparty/sdl/core/linux/SDL_evdev_kbd_default_keymap.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
414
thirdparty/sdl/core/linux/SDL_fcitx.c
vendored
Normal file
414
thirdparty/sdl/core/linux/SDL_fcitx.c
vendored
Normal file
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/linux/SDL_fcitx.h
vendored
Normal file
35
thirdparty/sdl/core/linux/SDL_fcitx.h
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/linux/SDL_ibus.c
vendored
Normal file
694
thirdparty/sdl/core/linux/SDL_ibus.c
vendored
Normal file
@@ -0,0 +1,694 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/linux/SDL_ibus.h
vendored
Normal file
55
thirdparty/sdl/core/linux/SDL_ibus.h
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/linux/SDL_ime.c
vendored
Normal file
150
thirdparty/sdl/core/linux/SDL_ime.c
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/linux/SDL_ime.h
vendored
Normal file
35
thirdparty/sdl/core/linux/SDL_ime.h
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/linux/SDL_system_theme.c
vendored
Normal file
156
thirdparty/sdl/core/linux/SDL_system_theme.c
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/linux/SDL_system_theme.h
vendored
Normal file
30
thirdparty/sdl/core/linux/SDL_system_theme.h
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/linux/SDL_threadprio.c
vendored
Normal file
345
thirdparty/sdl/core/linux/SDL_threadprio.c
vendored
Normal file
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
|
||||
597
thirdparty/sdl/core/linux/SDL_udev.c
vendored
Normal file
597
thirdparty/sdl/core/linux/SDL_udev.c
vendored
Normal file
@@ -0,0 +1,597 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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" };
|
||||
|
||||
SDL_UDEV_PrivateData *SDL_UDEV_PrivateData_this = NULL;
|
||||
#define _this SDL_UDEV_PrivateData_this
|
||||
|
||||
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
|
||||
114
thirdparty/sdl/core/linux/SDL_udev.h
vendored
Normal file
114
thirdparty/sdl/core/linux/SDL_udev.h
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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 "thirdparty/linuxbsd_headers/udev/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
thirdparty/sdl/core/unix/SDL_appid.c
vendored
Normal file
75
thirdparty/sdl/core/unix/SDL_appid.c
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/unix/SDL_appid.h
vendored
Normal file
30
thirdparty/sdl/core/unix/SDL_appid.h
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/unix/SDL_poll.c
vendored
Normal file
95
thirdparty/sdl/core/unix/SDL_poll.c
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/unix/SDL_poll.h
vendored
Normal file
33
thirdparty/sdl/core/unix/SDL_poll.h
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/windows/SDL_directx.h
vendored
Normal file
112
thirdparty/sdl/core/windows/SDL_directx.h
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/windows/SDL_gameinput.c
vendored
Normal file
98
thirdparty/sdl/core/windows/SDL_gameinput.c
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/windows/SDL_gameinput.h
vendored
Normal file
36
thirdparty/sdl/core/windows/SDL_gameinput.h
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/windows/SDL_hid.c
vendored
Normal file
254
thirdparty/sdl/core/windows/SDL_hid.c
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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(¬ify_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
thirdparty/sdl/core/windows/SDL_hid.h
vendored
Normal file
215
thirdparty/sdl/core/windows/SDL_hid.h
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/windows/SDL_immdevice.c
vendored
Normal file
434
thirdparty/sdl/core/windows/SDL_immdevice.c
vendored
Normal file
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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 = { ¬ification_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 *)¬ification_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 *)¬ification_client);
|
||||
}
|
||||
|
||||
#endif // defined(SDL_PLATFORM_WINDOWS) && defined(HAVE_MMDEVICEAPI_H)
|
||||
45
thirdparty/sdl/core/windows/SDL_immdevice.h
vendored
Normal file
45
thirdparty/sdl/core/windows/SDL_immdevice.h
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/windows/SDL_windows.c
vendored
Normal file
375
thirdparty/sdl/core/windows/SDL_windows.c
vendored
Normal file
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/windows/SDL_windows.h
vendored
Normal file
172
thirdparty/sdl/core/windows/SDL_windows.h
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/windows/SDL_xinput.c
vendored
Normal file
140
thirdparty/sdl/core/windows/SDL_xinput.c
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/windows/SDL_xinput.h
vendored
Normal file
276
thirdparty/sdl/core/windows/SDL_xinput.h
vendored
Normal file
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/windows/pch.c
vendored
Normal file
21
thirdparty/sdl/core/windows/pch.c
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/core/windows/pch_cpp.cpp
vendored
Normal file
21
thirdparty/sdl/core/windows/pch_cpp.cpp
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/events/SDL_events.c
vendored
Normal file
1833
thirdparty/sdl/events/SDL_events.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
59
thirdparty/sdl/events/SDL_events_c.h
vendored
Normal file
59
thirdparty/sdl/events/SDL_events_c.h
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/events/SDL_eventwatch.c
vendored
Normal file
143
thirdparty/sdl/events/SDL_eventwatch.c
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/events/SDL_eventwatch_c.h
vendored
Normal file
45
thirdparty/sdl/events/SDL_eventwatch_c.h
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/events/SDL_mouse_c.h
vendored
Normal file
215
thirdparty/sdl/events/SDL_mouse_c.h
vendored
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/haptic/SDL_haptic.c
vendored
Normal file
788
thirdparty/sdl/haptic/SDL_haptic.c
vendored
Normal file
@@ -0,0 +1,788 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/haptic/SDL_haptic_c.h
vendored
Normal file
28
thirdparty/sdl/haptic/SDL_haptic_c.h
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/haptic/SDL_syshaptic.h
vendored
Normal file
194
thirdparty/sdl/haptic/SDL_syshaptic.h
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/haptic/darwin/SDL_syshaptic.c
vendored
Normal file
1373
thirdparty/sdl/haptic/darwin/SDL_syshaptic.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
29
thirdparty/sdl/haptic/darwin/SDL_syshaptic_c.h
vendored
Normal file
29
thirdparty/sdl/haptic/darwin/SDL_syshaptic_c.h
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
thirdparty/sdl/haptic/linux/SDL_syshaptic.c
vendored
Normal file
1134
thirdparty/sdl/haptic/linux/SDL_syshaptic.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1244
thirdparty/sdl/haptic/windows/SDL_dinputhaptic.c
vendored
Normal file
1244
thirdparty/sdl/haptic/windows/SDL_dinputhaptic.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
53
thirdparty/sdl/haptic/windows/SDL_dinputhaptic_c.h
vendored
Normal file
53
thirdparty/sdl/haptic/windows/SDL_dinputhaptic_c.h
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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_windowshaptic_c.h"
|
||||
|
||||
// Set up for C function definitions, even when using C++
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern bool SDL_DINPUT_HapticInit(void);
|
||||
extern bool SDL_DINPUT_HapticMaybeAddDevice(const DIDEVICEINSTANCE *pdidInstance);
|
||||
extern bool SDL_DINPUT_HapticMaybeRemoveDevice(const DIDEVICEINSTANCE *pdidInstance);
|
||||
extern bool SDL_DINPUT_HapticOpen(SDL_Haptic *haptic, SDL_hapticlist_item *item);
|
||||
extern bool SDL_DINPUT_JoystickSameHaptic(SDL_Haptic *haptic, SDL_Joystick *joystick);
|
||||
extern bool SDL_DINPUT_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick);
|
||||
extern void SDL_DINPUT_HapticClose(SDL_Haptic *haptic);
|
||||
extern void SDL_DINPUT_HapticQuit(void);
|
||||
extern bool SDL_DINPUT_HapticNewEffect(SDL_Haptic *haptic, struct haptic_effect *effect, const SDL_HapticEffect *base);
|
||||
extern bool SDL_DINPUT_HapticUpdateEffect(SDL_Haptic *haptic, struct haptic_effect *effect, const SDL_HapticEffect *data);
|
||||
extern bool SDL_DINPUT_HapticRunEffect(SDL_Haptic *haptic, struct haptic_effect *effect, Uint32 iterations);
|
||||
extern bool SDL_DINPUT_HapticStopEffect(SDL_Haptic *haptic, struct haptic_effect *effect);
|
||||
extern void SDL_DINPUT_HapticDestroyEffect(SDL_Haptic *haptic, struct haptic_effect *effect);
|
||||
extern int SDL_DINPUT_HapticGetEffectStatus(SDL_Haptic *haptic, struct haptic_effect *effect);
|
||||
extern bool SDL_DINPUT_HapticSetGain(SDL_Haptic *haptic, int gain);
|
||||
extern bool SDL_DINPUT_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter);
|
||||
extern bool SDL_DINPUT_HapticPause(SDL_Haptic *haptic);
|
||||
extern bool SDL_DINPUT_HapticResume(SDL_Haptic *haptic);
|
||||
extern bool SDL_DINPUT_HapticStopAll(SDL_Haptic *haptic);
|
||||
|
||||
// Ends C function definitions when using C++
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
369
thirdparty/sdl/haptic/windows/SDL_windowshaptic.c
vendored
Normal file
369
thirdparty/sdl/haptic/windows/SDL_windowshaptic.c
vendored
Normal file
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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_DINPUT
|
||||
|
||||
#include "../SDL_syshaptic.h"
|
||||
#include "../../joystick/SDL_sysjoystick.h" // For the real SDL_Joystick
|
||||
#include "../../joystick/windows/SDL_windowsjoystick_c.h" // For joystick hwdata
|
||||
#include "../../joystick/windows/SDL_xinputjoystick_c.h" // For xinput rumble
|
||||
|
||||
#include "SDL_windowshaptic_c.h"
|
||||
#include "SDL_dinputhaptic_c.h"
|
||||
|
||||
// Set up for C function definitions, even when using C++
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Internal stuff.
|
||||
*/
|
||||
SDL_hapticlist_item *SDL_hapticlist = NULL;
|
||||
static SDL_hapticlist_item *SDL_hapticlist_tail = NULL;
|
||||
static int numhaptics = 0;
|
||||
|
||||
/*
|
||||
* Initializes the haptic subsystem.
|
||||
*/
|
||||
bool SDL_SYS_HapticInit(void)
|
||||
{
|
||||
JoyStick_DeviceData *device;
|
||||
|
||||
if (!SDL_DINPUT_HapticInit()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The joystick subsystem will usually be initialized before haptics,
|
||||
* so the initial HapticMaybeAddDevice() calls from the joystick
|
||||
* subsystem will arrive too early to create haptic devices. We will
|
||||
* invoke those callbacks again here to pick up any joysticks that
|
||||
* were added prior to haptics initialization. */
|
||||
for (device = SYS_Joystick; device; device = device->pNext) {
|
||||
SDL_DINPUT_HapticMaybeAddDevice(&device->dxdevice);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDL_SYS_AddHapticDevice(SDL_hapticlist_item *item)
|
||||
{
|
||||
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 SDL_SYS_RemoveHapticDevice(SDL_hapticlist_item *prev, SDL_hapticlist_item *item)
|
||||
{
|
||||
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;
|
||||
}
|
||||
--numhaptics;
|
||||
// !!! TODO: Send a haptic remove event?
|
||||
SDL_free(item);
|
||||
return result;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
SDL_HapticID SDL_SYS_HapticInstanceID(int index)
|
||||
{
|
||||
SDL_hapticlist_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 = HapticByDevIndex(index);
|
||||
return item->name;
|
||||
}
|
||||
|
||||
/*
|
||||
* Opens a haptic device for usage.
|
||||
*/
|
||||
bool SDL_SYS_HapticOpen(SDL_Haptic *haptic)
|
||||
{
|
||||
SDL_hapticlist_item *item = HapticByInstanceID(haptic->instance_id);
|
||||
return SDL_DINPUT_HapticOpen(haptic, item);
|
||||
}
|
||||
|
||||
/*
|
||||
* Opens a haptic device from first mouse it finds for usage.
|
||||
*/
|
||||
int SDL_SYS_HapticMouse(void)
|
||||
{
|
||||
#ifdef SDL_HAPTIC_DINPUT
|
||||
SDL_hapticlist_item *item;
|
||||
int index = 0;
|
||||
|
||||
// Grab the first mouse haptic device we find.
|
||||
for (item = SDL_hapticlist; item; item = item->next) {
|
||||
if (item->capabilities.dwDevType == DI8DEVCLASS_POINTER) {
|
||||
return index;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
#endif // SDL_HAPTIC_DINPUT
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks to see if a joystick has haptic features.
|
||||
*/
|
||||
bool SDL_SYS_JoystickIsHaptic(SDL_Joystick *joystick)
|
||||
{
|
||||
if (joystick->driver != &SDL_WINDOWS_JoystickDriver) {
|
||||
return false;
|
||||
}
|
||||
if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
|
||||
return true;
|
||||
}
|
||||
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)
|
||||
{
|
||||
if (joystick->driver != &SDL_WINDOWS_JoystickDriver) {
|
||||
return false;
|
||||
}
|
||||
return SDL_DINPUT_JoystickSameHaptic(haptic, joystick);
|
||||
}
|
||||
|
||||
/*
|
||||
* Opens a SDL_Haptic from a SDL_Joystick.
|
||||
*/
|
||||
bool SDL_SYS_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick)
|
||||
{
|
||||
SDL_assert(joystick->driver == &SDL_WINDOWS_JoystickDriver);
|
||||
|
||||
return SDL_DINPUT_HapticOpenFromJoystick(haptic, joystick);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
SDL_DINPUT_HapticClose(haptic);
|
||||
|
||||
// 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) {
|
||||
/* Opened and not closed haptics are leaked, this is on purpose.
|
||||
* Close your haptic devices after usage. */
|
||||
// !!! FIXME: (...is leaking on purpose a good idea?) - No, of course not.
|
||||
next = item->next;
|
||||
SDL_free(item->name);
|
||||
SDL_free(item);
|
||||
}
|
||||
|
||||
SDL_DINPUT_HapticQuit();
|
||||
|
||||
numhaptics = 0;
|
||||
SDL_hapticlist = NULL;
|
||||
SDL_hapticlist_tail = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a new haptic effect.
|
||||
*/
|
||||
bool SDL_SYS_HapticNewEffect(SDL_Haptic *haptic, struct haptic_effect *effect,
|
||||
const SDL_HapticEffect *base)
|
||||
{
|
||||
bool result;
|
||||
|
||||
// Alloc the effect.
|
||||
effect->hweffect = (struct haptic_hweffect *) SDL_calloc(1, sizeof(struct haptic_hweffect));
|
||||
if (!effect->hweffect) {
|
||||
return false;
|
||||
}
|
||||
|
||||
result = SDL_DINPUT_HapticNewEffect(haptic, effect, base);
|
||||
if (!result) {
|
||||
SDL_free(effect->hweffect);
|
||||
effect->hweffect = NULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates an effect.
|
||||
*/
|
||||
bool SDL_SYS_HapticUpdateEffect(SDL_Haptic *haptic, struct haptic_effect *effect, const SDL_HapticEffect *data)
|
||||
{
|
||||
return SDL_DINPUT_HapticUpdateEffect(haptic, effect, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Runs an effect.
|
||||
*/
|
||||
bool SDL_SYS_HapticRunEffect(SDL_Haptic *haptic, struct haptic_effect *effect, Uint32 iterations)
|
||||
{
|
||||
return SDL_DINPUT_HapticRunEffect(haptic, effect, iterations);
|
||||
}
|
||||
|
||||
/*
|
||||
* Stops an effect.
|
||||
*/
|
||||
bool SDL_SYS_HapticStopEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
|
||||
{
|
||||
return SDL_DINPUT_HapticStopEffect(haptic, effect);
|
||||
}
|
||||
|
||||
/*
|
||||
* Frees the effect.
|
||||
*/
|
||||
void SDL_SYS_HapticDestroyEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
|
||||
{
|
||||
SDL_DINPUT_HapticDestroyEffect(haptic, effect);
|
||||
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)
|
||||
{
|
||||
return SDL_DINPUT_HapticGetEffectStatus(haptic, effect);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the gain.
|
||||
*/
|
||||
bool SDL_SYS_HapticSetGain(SDL_Haptic *haptic, int gain)
|
||||
{
|
||||
return SDL_DINPUT_HapticSetGain(haptic, gain);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the autocentering.
|
||||
*/
|
||||
bool SDL_SYS_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter)
|
||||
{
|
||||
return SDL_DINPUT_HapticSetAutocenter(haptic, autocenter);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pauses the device.
|
||||
*/
|
||||
bool SDL_SYS_HapticPause(SDL_Haptic *haptic)
|
||||
{
|
||||
return SDL_DINPUT_HapticPause(haptic);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pauses the device.
|
||||
*/
|
||||
bool SDL_SYS_HapticResume(SDL_Haptic *haptic)
|
||||
{
|
||||
return SDL_DINPUT_HapticResume(haptic);
|
||||
}
|
||||
|
||||
/*
|
||||
* Stops all the playing effects on the device.
|
||||
*/
|
||||
bool SDL_SYS_HapticStopAll(SDL_Haptic *haptic)
|
||||
{
|
||||
return SDL_DINPUT_HapticStopAll(haptic);
|
||||
}
|
||||
|
||||
// Ends C function definitions when using C++
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // SDL_HAPTIC_DINPUT
|
||||
87
thirdparty/sdl/haptic/windows/SDL_windowshaptic_c.h
vendored
Normal file
87
thirdparty/sdl/haptic/windows/SDL_windowshaptic_c.h
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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_windowshaptic_c_h_
|
||||
#define SDL_windowshaptic_c_h_
|
||||
|
||||
#include "../SDL_syshaptic.h"
|
||||
#include "../../core/windows/SDL_directx.h"
|
||||
#include "../../core/windows/SDL_xinput.h"
|
||||
|
||||
// Set up for C function definitions, even when using C++
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Haptic system hardware data.
|
||||
*/
|
||||
struct haptic_hwdata
|
||||
{
|
||||
#ifdef SDL_HAPTIC_DINPUT
|
||||
LPDIRECTINPUTDEVICE8 device;
|
||||
#endif
|
||||
DWORD axes[3]; // Axes to use.
|
||||
bool is_joystick; // Device is loaded as joystick.
|
||||
SDL_Thread *thread;
|
||||
SDL_Mutex *mutex;
|
||||
Uint64 stopTicks;
|
||||
SDL_AtomicInt stopThread;
|
||||
};
|
||||
|
||||
/*
|
||||
* Haptic system effect data.
|
||||
*/
|
||||
#ifdef SDL_HAPTIC_DINPUT
|
||||
struct haptic_hweffect
|
||||
{
|
||||
DIEFFECT effect;
|
||||
LPDIRECTINPUTEFFECT ref;
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* List of available haptic devices.
|
||||
*/
|
||||
typedef struct SDL_hapticlist_item
|
||||
{
|
||||
SDL_HapticID instance_id;
|
||||
char *name;
|
||||
SDL_Haptic *haptic;
|
||||
#ifdef SDL_HAPTIC_DINPUT
|
||||
DIDEVICEINSTANCE instance;
|
||||
DIDEVCAPS capabilities;
|
||||
#endif
|
||||
struct SDL_hapticlist_item *next;
|
||||
} SDL_hapticlist_item;
|
||||
|
||||
extern SDL_hapticlist_item *SDL_hapticlist;
|
||||
|
||||
extern bool SDL_SYS_AddHapticDevice(SDL_hapticlist_item *item);
|
||||
extern bool SDL_SYS_RemoveHapticDevice(SDL_hapticlist_item *prev, SDL_hapticlist_item *item);
|
||||
|
||||
// Ends C function definitions when using C++
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // SDL_windowshaptic_c_h_
|
||||
18
thirdparty/sdl/hidapi/AUTHORS.txt
vendored
Normal file
18
thirdparty/sdl/hidapi/AUTHORS.txt
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
HIDAPI Authors:
|
||||
|
||||
Alan Ott <alan@signal11.us>:
|
||||
Original Author and Maintainer
|
||||
Linux, Windows, and Mac implementations
|
||||
|
||||
Ludovic Rousseau <rousseau@debian.org>:
|
||||
Formatting for Doxygen documentation
|
||||
Bug fixes
|
||||
Correctness fixes
|
||||
|
||||
libusb/hidapi Team:
|
||||
Development/maintenance since June 4th 2019
|
||||
|
||||
For a comprehensive list of contributions, see the commit list at github:
|
||||
https://github.com/libusb/hidapi/graphs/contributors
|
||||
|
||||
26
thirdparty/sdl/hidapi/LICENSE-bsd.txt
vendored
Normal file
26
thirdparty/sdl/hidapi/LICENSE-bsd.txt
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
Copyright (c) 2010, Alan Ott, Signal 11 Software
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Signal 11 Software nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
13
thirdparty/sdl/hidapi/LICENSE.txt
vendored
Normal file
13
thirdparty/sdl/hidapi/LICENSE.txt
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
HIDAPI can be used under one of three licenses.
|
||||
|
||||
1. The GNU General Public License, version 3.0, in LICENSE-gpl3.txt
|
||||
2. A BSD-Style License, in LICENSE-bsd.txt.
|
||||
3. The more liberal original HIDAPI license. LICENSE-orig.txt
|
||||
|
||||
The license chosen is at the discretion of the user of HIDAPI. For example:
|
||||
1. An author of GPL software would likely use HIDAPI under the terms of the
|
||||
GPL.
|
||||
|
||||
2. An author of commercial closed-source software would likely use HIDAPI
|
||||
under the terms of the BSD-style license or the original HIDAPI license.
|
||||
|
||||
1752
thirdparty/sdl/hidapi/SDL_hidapi.c
vendored
Normal file
1752
thirdparty/sdl/hidapi/SDL_hidapi.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
26
thirdparty/sdl/hidapi/SDL_hidapi_android.h
vendored
Normal file
26
thirdparty/sdl/hidapi/SDL_hidapi_android.h
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/* The implementation for Android is in a separate .cpp file */
|
||||
#undef HIDAPI_H__
|
||||
#include "hidapi/hidapi.h"
|
||||
#define HAVE_PLATFORM_BACKEND 1
|
||||
#define udev_ctx 1
|
||||
35
thirdparty/sdl/hidapi/SDL_hidapi_c.h
vendored
Normal file
35
thirdparty/sdl/hidapi/SDL_hidapi_c.h
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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"
|
||||
|
||||
|
||||
/* Return true if the HIDAPI should ignore a device during enumeration */
|
||||
extern bool SDL_HIDAPI_ShouldIgnoreDevice(int bus_type, Uint16 vendor_id, Uint16 product_id, Uint16 usage_page, Uint16 usage);
|
||||
|
||||
#ifdef SDL_JOYSTICK_HIDAPI
|
||||
#ifdef HAVE_LIBUSB
|
||||
#define HAVE_ENABLE_GAMECUBE_ADAPTORS
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ENABLE_GAMECUBE_ADAPTORS
|
||||
extern void SDL_EnableGameCubeAdaptors(void);
|
||||
#endif
|
||||
#endif /* SDL_JOYSTICK_HIDAPI */
|
||||
26
thirdparty/sdl/hidapi/SDL_hidapi_ios.h
vendored
Normal file
26
thirdparty/sdl/hidapi/SDL_hidapi_ios.h
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/* The implementation for iOS and tvOS is in a separate .m file */
|
||||
#undef HIDAPI_H__
|
||||
#include "hidapi/hidapi.h"
|
||||
#define HAVE_PLATFORM_BACKEND 1
|
||||
#define udev_ctx 1
|
||||
134
thirdparty/sdl/hidapi/SDL_hidapi_libusb.h
vendored
Normal file
134
thirdparty/sdl/hidapi/SDL_hidapi_libusb.h
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/* Define standard library functions in terms of SDL */
|
||||
|
||||
/* #pragma push_macro/pop_macro works correctly only as of gcc >= 4.4.3
|
||||
clang-3.0 _seems_ to be OK. */
|
||||
#pragma push_macro("calloc")
|
||||
#pragma push_macro("malloc")
|
||||
#pragma push_macro("realloc")
|
||||
#pragma push_macro("free")
|
||||
#pragma push_macro("iconv_t")
|
||||
#pragma push_macro("iconv")
|
||||
#pragma push_macro("iconv_open")
|
||||
#pragma push_macro("iconv_close")
|
||||
#pragma push_macro("setlocale")
|
||||
#pragma push_macro("snprintf")
|
||||
#pragma push_macro("strcmp")
|
||||
#pragma push_macro("strdup")
|
||||
#pragma push_macro("strncpy")
|
||||
#pragma push_macro("tolower")
|
||||
#pragma push_macro("wcscmp")
|
||||
#pragma push_macro("wcsdup")
|
||||
#pragma push_macro("wcsncpy")
|
||||
|
||||
#undef calloc
|
||||
#undef malloc
|
||||
#undef realloc
|
||||
#undef free
|
||||
#undef iconv_t
|
||||
#undef iconv
|
||||
#undef iconv_open
|
||||
#undef iconv_close
|
||||
#undef setlocale
|
||||
#undef snprintf
|
||||
#undef strcmp
|
||||
#undef strdup
|
||||
#undef strncpy
|
||||
#undef tolower
|
||||
#undef wcscmp
|
||||
#undef wcsdup
|
||||
#undef wcsncpy
|
||||
|
||||
#define calloc SDL_calloc
|
||||
#define malloc SDL_malloc
|
||||
#define realloc SDL_realloc
|
||||
#define free SDL_free
|
||||
#define iconv_t SDL_iconv_t
|
||||
#ifndef ICONV_CONST
|
||||
#define ICONV_CONST
|
||||
#define UNDEF_ICONV_CONST
|
||||
#endif
|
||||
#define iconv(a,b,c,d,e) SDL_iconv(a, (const char **)b, c, d, e)
|
||||
#define iconv_open SDL_iconv_open
|
||||
#define iconv_close SDL_iconv_close
|
||||
#define setlocale(X, Y) NULL
|
||||
#define snprintf SDL_snprintf
|
||||
#define strcmp SDL_strcmp
|
||||
#define strdup SDL_strdup
|
||||
#define strncpy SDL_strlcpy
|
||||
#define tolower SDL_tolower
|
||||
#define wcscmp SDL_wcscmp
|
||||
#define wcsdup SDL_wcsdup
|
||||
#define wcsncpy SDL_wcslcpy
|
||||
|
||||
|
||||
#ifndef SDL_PLATFORM_FREEBSD
|
||||
/* this is awkwardly inlined, so we need to re-implement it here
|
||||
* so we can override the libusb_control_transfer call */
|
||||
static int SDL_libusb_get_string_descriptor(libusb_device_handle *dev,
|
||||
uint8_t descriptor_index, uint16_t lang_id,
|
||||
unsigned char *data, int length)
|
||||
{
|
||||
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN | 0x0, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8) | descriptor_index, lang_id,
|
||||
data, (uint16_t)length, 1000); /* Endpoint 0 IN */
|
||||
}
|
||||
#define libusb_get_string_descriptor SDL_libusb_get_string_descriptor
|
||||
#endif /* SDL_PLATFORM_FREEBSD */
|
||||
|
||||
#define HIDAPI_THREAD_MODEL_INCLUDE "hidapi_thread_sdl.h"
|
||||
#ifndef LIBUSB_API_VERSION
|
||||
#ifdef LIBUSBX_API_VERSION
|
||||
#define LIBUSB_API_VERSION LIBUSBX_API_VERSION
|
||||
#else
|
||||
#define LIBUSB_API_VERSION 0x0
|
||||
#endif
|
||||
#endif
|
||||
/* we need libusb >= 1.0.16 because of libusb_get_port_numbers */
|
||||
/* we don't need libusb_wrap_sys_device: */
|
||||
#define HIDAPI_TARGET_LIBUSB_API_VERSION 0x01000102
|
||||
|
||||
#undef HIDAPI_H__
|
||||
#include "libusb/hid.c"
|
||||
|
||||
/* restore libc function macros */
|
||||
#ifdef UNDEF_ICONV_CONST
|
||||
#undef ICONV_CONST
|
||||
#undef UNDEF_ICONV_CONST
|
||||
#endif
|
||||
#pragma pop_macro("calloc")
|
||||
#pragma pop_macro("malloc")
|
||||
#pragma pop_macro("realloc")
|
||||
#pragma pop_macro("free")
|
||||
#pragma pop_macro("iconv_t")
|
||||
#pragma pop_macro("iconv")
|
||||
#pragma pop_macro("iconv_open")
|
||||
#pragma pop_macro("iconv_close")
|
||||
#pragma pop_macro("setlocale")
|
||||
#pragma pop_macro("snprintf")
|
||||
#pragma pop_macro("strcmp")
|
||||
#pragma pop_macro("strdup")
|
||||
#pragma pop_macro("strncpy")
|
||||
#pragma pop_macro("tolower")
|
||||
#pragma pop_macro("wcscmp")
|
||||
#pragma pop_macro("wcsdup")
|
||||
#pragma pop_macro("wcsncpy")
|
||||
47
thirdparty/sdl/hidapi/SDL_hidapi_linux.h
vendored
Normal file
47
thirdparty/sdl/hidapi/SDL_hidapi_linux.h
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifdef SDL_USE_LIBUDEV
|
||||
static const SDL_UDEV_Symbols *udev_ctx = NULL;
|
||||
|
||||
#define udev_device_get_devnode udev_ctx->udev_device_get_devnode
|
||||
#define udev_device_get_parent_with_subsystem_devtype udev_ctx->udev_device_get_parent_with_subsystem_devtype
|
||||
#define udev_device_get_sysattr_value udev_ctx->udev_device_get_sysattr_value
|
||||
#define udev_device_get_syspath udev_ctx->udev_device_get_syspath
|
||||
#define udev_device_new_from_devnum udev_ctx->udev_device_new_from_devnum
|
||||
#define udev_device_new_from_syspath udev_ctx->udev_device_new_from_syspath
|
||||
#define udev_device_unref udev_ctx->udev_device_unref
|
||||
#define udev_enumerate_add_match_subsystem udev_ctx->udev_enumerate_add_match_subsystem
|
||||
#define udev_enumerate_get_list_entry udev_ctx->udev_enumerate_get_list_entry
|
||||
#define udev_enumerate_new udev_ctx->udev_enumerate_new
|
||||
#define udev_enumerate_scan_devices udev_ctx->udev_enumerate_scan_devices
|
||||
#define udev_enumerate_unref udev_ctx->udev_enumerate_unref
|
||||
#define udev_list_entry_get_name udev_ctx->udev_list_entry_get_name
|
||||
#define udev_list_entry_get_next udev_ctx->udev_list_entry_get_next
|
||||
#define udev_new udev_ctx->udev_new
|
||||
#define udev_unref udev_ctx->udev_unref
|
||||
|
||||
#undef HIDAPI_H__
|
||||
#define HIDAPI_ALLOW_BUILD_WORKAROUND_KERNEL_2_6_39
|
||||
#include "linux/hid.c"
|
||||
#define HAVE_PLATFORM_BACKEND 1
|
||||
|
||||
#endif /* SDL_USE_LIBUDEV */
|
||||
25
thirdparty/sdl/hidapi/SDL_hidapi_mac.h
vendored
Normal file
25
thirdparty/sdl/hidapi/SDL_hidapi_mac.h
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#undef HIDAPI_H__
|
||||
#include "mac/hid.c"
|
||||
#define HAVE_PLATFORM_BACKEND 1
|
||||
#define udev_ctx 1
|
||||
25
thirdparty/sdl/hidapi/SDL_hidapi_netbsd.h
vendored
Normal file
25
thirdparty/sdl/hidapi/SDL_hidapi_netbsd.h
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#undef HIDAPI_H__
|
||||
#include "netbsd/hid.c"
|
||||
#define HAVE_PLATFORM_BACKEND 1
|
||||
#define udev_ctx 1
|
||||
23
thirdparty/sdl/hidapi/SDL_hidapi_steamxbox.h
vendored
Normal file
23
thirdparty/sdl/hidapi/SDL_hidapi_steamxbox.h
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#undef HIDAPI_H__
|
||||
#include "steamxbox/hid.c"
|
||||
86
thirdparty/sdl/hidapi/SDL_hidapi_windows.h
vendored
Normal file
86
thirdparty/sdl/hidapi/SDL_hidapi_windows.h
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/* Define standard library functions in terms of SDL */
|
||||
|
||||
/* #pragma push_macro/pop_macro works correctly only as of gcc >= 4.4.3
|
||||
clang-3.0 _seems_ to be OK. */
|
||||
#pragma push_macro("calloc")
|
||||
#pragma push_macro("free")
|
||||
#pragma push_macro("malloc")
|
||||
#pragma push_macro("memcmp")
|
||||
#pragma push_macro("swprintf")
|
||||
#pragma push_macro("towupper")
|
||||
#pragma push_macro("wcscmp")
|
||||
#pragma push_macro("_wcsdup")
|
||||
#pragma push_macro("wcslen")
|
||||
#pragma push_macro("wcsncpy")
|
||||
#pragma push_macro("wcsstr")
|
||||
#pragma push_macro("wcstol")
|
||||
|
||||
#undef calloc
|
||||
#undef free
|
||||
#undef malloc
|
||||
#undef memcmp
|
||||
#undef swprintf
|
||||
#undef towupper
|
||||
#undef wcscmp
|
||||
#undef _wcsdup
|
||||
#undef wcslen
|
||||
#undef wcsncpy
|
||||
#undef wcsstr
|
||||
#undef wcstol
|
||||
|
||||
#define calloc SDL_calloc
|
||||
#define free SDL_free
|
||||
#define malloc SDL_malloc
|
||||
#define memcmp SDL_memcmp
|
||||
#define swprintf SDL_swprintf
|
||||
#define towupper (wchar_t)SDL_toupper
|
||||
#define wcscmp SDL_wcscmp
|
||||
#define _wcsdup SDL_wcsdup
|
||||
#define wcslen SDL_wcslen
|
||||
#define wcsncpy SDL_wcslcpy
|
||||
#define wcsstr SDL_wcsstr
|
||||
#define wcstol SDL_wcstol
|
||||
|
||||
// These functions conflict when linking both SDL and hidapi statically
|
||||
#define hid_winapi_descriptor_reconstruct_pp_data SDL_hid_winapi_descriptor_reconstruct_pp_data
|
||||
#define hid_winapi_get_container_id SDL_hid_winapi_get_container_id
|
||||
|
||||
#undef HIDAPI_H__
|
||||
#include "windows/hid.c"
|
||||
#define HAVE_PLATFORM_BACKEND 1
|
||||
#define udev_ctx 1
|
||||
|
||||
/* restore libc function macros */
|
||||
#pragma pop_macro("calloc")
|
||||
#pragma pop_macro("free")
|
||||
#pragma pop_macro("malloc")
|
||||
#pragma pop_macro("memcmp")
|
||||
#pragma pop_macro("swprintf")
|
||||
#pragma pop_macro("towupper")
|
||||
#pragma pop_macro("wcscmp")
|
||||
#pragma pop_macro("_wcsdup")
|
||||
#pragma pop_macro("wcslen")
|
||||
#pragma pop_macro("wcsncpy")
|
||||
#pragma pop_macro("wcsstr")
|
||||
#pragma pop_macro("wcstol")
|
||||
1
thirdparty/sdl/hidapi/VERSION
vendored
Normal file
1
thirdparty/sdl/hidapi/VERSION
vendored
Normal file
@@ -0,0 +1 @@
|
||||
0.14.0
|
||||
634
thirdparty/sdl/hidapi/hidapi/hidapi.h
vendored
Normal file
634
thirdparty/sdl/hidapi/hidapi/hidapi.h
vendored
Normal file
@@ -0,0 +1,634 @@
|
||||
/*******************************************************
|
||||
HIDAPI - Multi-Platform library for
|
||||
communication with HID devices.
|
||||
|
||||
Alan Ott
|
||||
Signal 11 Software
|
||||
|
||||
libusb/hidapi Team
|
||||
|
||||
Copyright 2023, All Rights Reserved.
|
||||
|
||||
At the discretion of the user of this library,
|
||||
this software may be licensed under the terms of the
|
||||
GNU General Public License v3, a BSD-Style license, or the
|
||||
original HIDAPI license as outlined in the LICENSE.txt,
|
||||
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
|
||||
files located at the root of the source distribution.
|
||||
These files may also be found in the public source
|
||||
code repository located at:
|
||||
https://github.com/libusb/hidapi .
|
||||
********************************************************/
|
||||
|
||||
/** @file
|
||||
* @defgroup API hidapi API
|
||||
*/
|
||||
|
||||
#ifndef HIDAPI_H__
|
||||
#define HIDAPI_H__
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
/* #480: this is to be refactored properly for v1.0 */
|
||||
#ifdef _WIN32
|
||||
#ifndef HID_API_NO_EXPORT_DEFINE
|
||||
#define HID_API_EXPORT __declspec(dllexport)
|
||||
#endif
|
||||
#endif
|
||||
#ifndef HID_API_EXPORT
|
||||
#define HID_API_EXPORT /**< API export macro */
|
||||
#endif
|
||||
/* To be removed in v1.0 */
|
||||
#define HID_API_CALL /**< API call macro */
|
||||
|
||||
#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/
|
||||
|
||||
/** @brief Static/compile-time major version of the library.
|
||||
|
||||
@ingroup API
|
||||
*/
|
||||
#define HID_API_VERSION_MAJOR 0
|
||||
/** @brief Static/compile-time minor version of the library.
|
||||
|
||||
@ingroup API
|
||||
*/
|
||||
#define HID_API_VERSION_MINOR 14
|
||||
/** @brief Static/compile-time patch version of the library.
|
||||
|
||||
@ingroup API
|
||||
*/
|
||||
#define HID_API_VERSION_PATCH 0
|
||||
|
||||
/* Helper macros */
|
||||
#define HID_API_AS_STR_IMPL(x) #x
|
||||
#define HID_API_AS_STR(x) HID_API_AS_STR_IMPL(x)
|
||||
#define HID_API_TO_VERSION_STR(v1, v2, v3) HID_API_AS_STR(v1.v2.v3)
|
||||
|
||||
/** @brief Coverts a version as Major/Minor/Patch into a number:
|
||||
<8 bit major><16 bit minor><8 bit patch>.
|
||||
|
||||
This macro was added in version 0.12.0.
|
||||
|
||||
Convenient function to be used for compile-time checks, like:
|
||||
@code{.c}
|
||||
#if HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
|
||||
@endcode
|
||||
|
||||
@ingroup API
|
||||
*/
|
||||
#define HID_API_MAKE_VERSION(mj, mn, p) (((mj) << 24) | ((mn) << 8) | (p))
|
||||
|
||||
/** @brief Static/compile-time version of the library.
|
||||
|
||||
This macro was added in version 0.12.0.
|
||||
|
||||
@see @ref HID_API_MAKE_VERSION.
|
||||
|
||||
@ingroup API
|
||||
*/
|
||||
#define HID_API_VERSION HID_API_MAKE_VERSION(HID_API_VERSION_MAJOR, HID_API_VERSION_MINOR, HID_API_VERSION_PATCH)
|
||||
|
||||
/** @brief Static/compile-time string version of the library.
|
||||
|
||||
@ingroup API
|
||||
*/
|
||||
#define HID_API_VERSION_STR HID_API_TO_VERSION_STR(HID_API_VERSION_MAJOR, HID_API_VERSION_MINOR, HID_API_VERSION_PATCH)
|
||||
|
||||
/** @brief Maximum expected HID Report descriptor size in bytes.
|
||||
|
||||
Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0)
|
||||
|
||||
@ingroup API
|
||||
*/
|
||||
#define HID_API_MAX_REPORT_DESCRIPTOR_SIZE 4096
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#ifndef DEFINED_HID_TYPES
|
||||
#define DEFINED_HID_TYPES
|
||||
/** A structure to hold the version numbers. */
|
||||
struct hid_api_version {
|
||||
int major; /**< major version number */
|
||||
int minor; /**< minor version number */
|
||||
int patch; /**< patch version number */
|
||||
};
|
||||
|
||||
struct hid_device_;
|
||||
typedef struct hid_device_ hid_device; /**< opaque hidapi structure */
|
||||
|
||||
/** @brief HID underlying bus types.
|
||||
|
||||
@ingroup API
|
||||
*/
|
||||
typedef enum {
|
||||
/** Unknown bus type */
|
||||
HID_API_BUS_UNKNOWN = 0x00,
|
||||
|
||||
/** USB bus
|
||||
Specifications:
|
||||
https://usb.org/hid */
|
||||
HID_API_BUS_USB = 0x01,
|
||||
|
||||
/** Bluetooth or Bluetooth LE bus
|
||||
Specifications:
|
||||
https://www.bluetooth.com/specifications/specs/human-interface-device-profile-1-1-1/
|
||||
https://www.bluetooth.com/specifications/specs/hid-service-1-0/
|
||||
https://www.bluetooth.com/specifications/specs/hid-over-gatt-profile-1-0/ */
|
||||
HID_API_BUS_BLUETOOTH = 0x02,
|
||||
|
||||
/** I2C bus
|
||||
Specifications:
|
||||
https://docs.microsoft.com/previous-versions/windows/hardware/design/dn642101(v=vs.85) */
|
||||
HID_API_BUS_I2C = 0x03,
|
||||
|
||||
/** SPI bus
|
||||
Specifications:
|
||||
https://www.microsoft.com/download/details.aspx?id=103325 */
|
||||
HID_API_BUS_SPI = 0x04,
|
||||
} hid_bus_type;
|
||||
|
||||
/** hidapi info structure */
|
||||
struct hid_device_info {
|
||||
/** Platform-specific device path */
|
||||
char *path;
|
||||
/** Device Vendor ID */
|
||||
unsigned short vendor_id;
|
||||
/** Device Product ID */
|
||||
unsigned short product_id;
|
||||
/** Serial Number */
|
||||
wchar_t *serial_number;
|
||||
/** Device Release Number in binary-coded decimal,
|
||||
also known as Device Version Number */
|
||||
unsigned short release_number;
|
||||
/** Manufacturer String */
|
||||
wchar_t *manufacturer_string;
|
||||
/** Product string */
|
||||
wchar_t *product_string;
|
||||
/** Usage Page for this Device/Interface
|
||||
(Windows/Mac/hidraw only) */
|
||||
unsigned short usage_page;
|
||||
/** Usage for this Device/Interface
|
||||
(Windows/Mac/hidraw only) */
|
||||
unsigned short usage;
|
||||
/** The USB interface which this logical device
|
||||
represents.
|
||||
|
||||
Valid only if the device is a USB HID device.
|
||||
Set to -1 in all other cases.
|
||||
*/
|
||||
int interface_number;
|
||||
|
||||
/** Pointer to the next device */
|
||||
struct hid_device_info *next;
|
||||
|
||||
/** Underlying bus type
|
||||
Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0)
|
||||
*/
|
||||
hid_bus_type bus_type;
|
||||
|
||||
/** Additional information about the USB interface.
|
||||
(libusb only) */
|
||||
int interface_class;
|
||||
int interface_subclass;
|
||||
int interface_protocol;
|
||||
};
|
||||
|
||||
#endif /* DEFINED_HID_TYPES */
|
||||
|
||||
|
||||
/** @brief Initialize the HIDAPI library.
|
||||
|
||||
This function initializes the HIDAPI library. Calling it is not
|
||||
strictly necessary, as it will be called automatically by
|
||||
hid_enumerate() and any of the hid_open_*() functions if it is
|
||||
needed. This function should be called at the beginning of
|
||||
execution however, if there is a chance of HIDAPI handles
|
||||
being opened by different threads simultaneously.
|
||||
|
||||
@ingroup API
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
Call hid_error(NULL) to get the failure reason.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_init(void);
|
||||
|
||||
/** @brief Finalize the HIDAPI library.
|
||||
|
||||
This function frees all of the static data associated with
|
||||
HIDAPI. It should be called at the end of execution to avoid
|
||||
memory leaks.
|
||||
|
||||
@ingroup API
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_exit(void);
|
||||
|
||||
/** @brief Enumerate the HID Devices.
|
||||
|
||||
This function returns a linked list of all the HID devices
|
||||
attached to the system which match vendor_id and product_id.
|
||||
If @p vendor_id is set to 0 then any vendor matches.
|
||||
If @p product_id is set to 0 then any product matches.
|
||||
If @p vendor_id and @p product_id are both set to 0, then
|
||||
all HID devices will be returned.
|
||||
|
||||
@ingroup API
|
||||
@param vendor_id The Vendor ID (VID) of the types of device
|
||||
to open.
|
||||
@param product_id The Product ID (PID) of the types of
|
||||
device to open.
|
||||
|
||||
@returns
|
||||
This function returns a pointer to a linked list of type
|
||||
struct #hid_device_info, containing information about the HID devices
|
||||
attached to the system,
|
||||
or NULL in the case of failure or if no HID devices present in the system.
|
||||
Call hid_error(NULL) to get the failure reason.
|
||||
|
||||
@note The returned value by this function must to be freed by calling hid_free_enumeration(),
|
||||
when not needed anymore.
|
||||
*/
|
||||
struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id);
|
||||
|
||||
/** @brief Free an enumeration Linked List
|
||||
|
||||
This function frees a linked list created by hid_enumerate().
|
||||
|
||||
@ingroup API
|
||||
@param devs Pointer to a list of struct_device returned from
|
||||
hid_enumerate().
|
||||
*/
|
||||
void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs);
|
||||
|
||||
/** @brief Open a HID device using a Vendor ID (VID), Product ID
|
||||
(PID) and optionally a serial number.
|
||||
|
||||
If @p serial_number is NULL, the first device with the
|
||||
specified VID and PID is opened.
|
||||
|
||||
@ingroup API
|
||||
@param vendor_id The Vendor ID (VID) of the device to open.
|
||||
@param product_id The Product ID (PID) of the device to open.
|
||||
@param serial_number The Serial Number of the device to open
|
||||
(Optionally NULL).
|
||||
|
||||
@returns
|
||||
This function returns a pointer to a #hid_device object on
|
||||
success or NULL on failure.
|
||||
Call hid_error(NULL) to get the failure reason.
|
||||
|
||||
@note The returned object must be freed by calling hid_close(),
|
||||
when not needed anymore.
|
||||
*/
|
||||
HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number);
|
||||
|
||||
/** @brief Open a HID device by its path name.
|
||||
|
||||
The path name be determined by calling hid_enumerate(), or a
|
||||
platform-specific path name can be used (eg: /dev/hidraw0 on
|
||||
Linux).
|
||||
|
||||
@ingroup API
|
||||
@param path The path name of the device to open
|
||||
|
||||
@returns
|
||||
This function returns a pointer to a #hid_device object on
|
||||
success or NULL on failure.
|
||||
Call hid_error(NULL) to get the failure reason.
|
||||
|
||||
@note The returned object must be freed by calling hid_close(),
|
||||
when not needed anymore.
|
||||
*/
|
||||
HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path);
|
||||
|
||||
/** @brief Write an Output report to a HID device.
|
||||
|
||||
The first byte of @p data[] must contain the Report ID. For
|
||||
devices which only support a single report, this must be set
|
||||
to 0x0. The remaining bytes contain the report data. Since
|
||||
the Report ID is mandatory, calls to hid_write() will always
|
||||
contain one more byte than the report contains. For example,
|
||||
if a hid report is 16 bytes long, 17 bytes must be passed to
|
||||
hid_write(), the Report ID (or 0x0, for devices with a
|
||||
single report), followed by the report data (16 bytes). In
|
||||
this example, the length passed in would be 17.
|
||||
|
||||
hid_write() will send the data on the first OUT endpoint, if
|
||||
one exists. If it does not, it will send the data through
|
||||
the Control Endpoint (Endpoint 0).
|
||||
|
||||
@ingroup API
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param data The data to send, including the report number as
|
||||
the first byte.
|
||||
@param length The length in bytes of the data to send.
|
||||
|
||||
@returns
|
||||
This function returns the actual number of bytes written and
|
||||
-1 on error.
|
||||
Call hid_error(dev) to get the failure reason.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length);
|
||||
|
||||
/** @brief Read an Input report from a HID device with timeout.
|
||||
|
||||
Input reports are returned
|
||||
to the host through the INTERRUPT IN endpoint. The first byte will
|
||||
contain the Report number if the device uses numbered reports.
|
||||
|
||||
@ingroup API
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param data A buffer to put the read data into.
|
||||
@param length The number of bytes to read. For devices with
|
||||
multiple reports, make sure to read an extra byte for
|
||||
the report number.
|
||||
@param milliseconds timeout in milliseconds or -1 for blocking wait.
|
||||
|
||||
@returns
|
||||
This function returns the actual number of bytes read and
|
||||
-1 on error.
|
||||
Call hid_error(dev) to get the failure reason.
|
||||
If no packet was available to be read within
|
||||
the timeout period, this function returns 0.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds);
|
||||
|
||||
/** @brief Read an Input report from a HID device.
|
||||
|
||||
Input reports are returned
|
||||
to the host through the INTERRUPT IN endpoint. The first byte will
|
||||
contain the Report number if the device uses numbered reports.
|
||||
|
||||
@ingroup API
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param data A buffer to put the read data into.
|
||||
@param length The number of bytes to read. For devices with
|
||||
multiple reports, make sure to read an extra byte for
|
||||
the report number.
|
||||
|
||||
@returns
|
||||
This function returns the actual number of bytes read and
|
||||
-1 on error.
|
||||
Call hid_error(dev) to get the failure reason.
|
||||
If no packet was available to be read and
|
||||
the handle is in non-blocking mode, this function returns 0.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length);
|
||||
|
||||
/** @brief Set the device handle to be non-blocking.
|
||||
|
||||
In non-blocking mode calls to hid_read() will return
|
||||
immediately with a value of 0 if there is no data to be
|
||||
read. In blocking mode, hid_read() will wait (block) until
|
||||
there is data to read before returning.
|
||||
|
||||
Nonblocking can be turned on and off at any time.
|
||||
|
||||
@ingroup API
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param nonblock enable or not the nonblocking reads
|
||||
- 1 to enable nonblocking
|
||||
- 0 to disable nonblocking.
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
Call hid_error(dev) to get the failure reason.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock);
|
||||
|
||||
/** @brief Send a Feature report to the device.
|
||||
|
||||
Feature reports are sent over the Control endpoint as a
|
||||
Set_Report transfer. The first byte of @p data[] must
|
||||
contain the Report ID. For devices which only support a
|
||||
single report, this must be set to 0x0. The remaining bytes
|
||||
contain the report data. Since the Report ID is mandatory,
|
||||
calls to hid_send_feature_report() will always contain one
|
||||
more byte than the report contains. For example, if a hid
|
||||
report is 16 bytes long, 17 bytes must be passed to
|
||||
hid_send_feature_report(): the Report ID (or 0x0, for
|
||||
devices which do not use numbered reports), followed by the
|
||||
report data (16 bytes). In this example, the length passed
|
||||
in would be 17.
|
||||
|
||||
@ingroup API
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param data The data to send, including the report number as
|
||||
the first byte.
|
||||
@param length The length in bytes of the data to send, including
|
||||
the report number.
|
||||
|
||||
@returns
|
||||
This function returns the actual number of bytes written and
|
||||
-1 on error.
|
||||
Call hid_error(dev) to get the failure reason.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length);
|
||||
|
||||
/** @brief Get a feature report from a HID device.
|
||||
|
||||
Set the first byte of @p data[] to the Report ID of the
|
||||
report to be read. Make sure to allow space for this
|
||||
extra byte in @p data[]. Upon return, the first byte will
|
||||
still contain the Report ID, and the report data will
|
||||
start in data[1].
|
||||
|
||||
@ingroup API
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param data A buffer to put the read data into, including
|
||||
the Report ID. Set the first byte of @p data[] to the
|
||||
Report ID of the report to be read, or set it to zero
|
||||
if your device does not use numbered reports.
|
||||
@param length The number of bytes to read, including an
|
||||
extra byte for the report ID. The buffer can be longer
|
||||
than the actual report.
|
||||
|
||||
@returns
|
||||
This function returns the number of bytes read plus
|
||||
one for the report ID (which is still in the first
|
||||
byte), or -1 on error.
|
||||
Call hid_error(dev) to get the failure reason.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length);
|
||||
|
||||
/** @brief Get a input report from a HID device.
|
||||
|
||||
Since version 0.10.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 10, 0)
|
||||
|
||||
Set the first byte of @p data[] to the Report ID of the
|
||||
report to be read. Make sure to allow space for this
|
||||
extra byte in @p data[]. Upon return, the first byte will
|
||||
still contain the Report ID, and the report data will
|
||||
start in data[1].
|
||||
|
||||
@ingroup API
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param data A buffer to put the read data into, including
|
||||
the Report ID. Set the first byte of @p data[] to the
|
||||
Report ID of the report to be read, or set it to zero
|
||||
if your device does not use numbered reports.
|
||||
@param length The number of bytes to read, including an
|
||||
extra byte for the report ID. The buffer can be longer
|
||||
than the actual report.
|
||||
|
||||
@returns
|
||||
This function returns the number of bytes read plus
|
||||
one for the report ID (which is still in the first
|
||||
byte), or -1 on error.
|
||||
Call hid_error(dev) to get the failure reason.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length);
|
||||
|
||||
/** @brief Close a HID device.
|
||||
|
||||
@ingroup API
|
||||
@param dev A device handle returned from hid_open().
|
||||
*/
|
||||
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev);
|
||||
|
||||
/** @brief Get The Manufacturer String from a HID device.
|
||||
|
||||
@ingroup API
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param string A wide string buffer to put the data into.
|
||||
@param maxlen The length of the buffer in multiples of wchar_t.
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
Call hid_error(dev) to get the failure reason.
|
||||
*/
|
||||
int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen);
|
||||
|
||||
/** @brief Get The Product String from a HID device.
|
||||
|
||||
@ingroup API
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param string A wide string buffer to put the data into.
|
||||
@param maxlen The length of the buffer in multiples of wchar_t.
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
Call hid_error(dev) to get the failure reason.
|
||||
*/
|
||||
int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen);
|
||||
|
||||
/** @brief Get The Serial Number String from a HID device.
|
||||
|
||||
@ingroup API
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param string A wide string buffer to put the data into.
|
||||
@param maxlen The length of the buffer in multiples of wchar_t.
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
Call hid_error(dev) to get the failure reason.
|
||||
*/
|
||||
int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen);
|
||||
|
||||
/** @brief Get The struct #hid_device_info from a HID device.
|
||||
|
||||
Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0)
|
||||
|
||||
@ingroup API
|
||||
@param dev A device handle returned from hid_open().
|
||||
|
||||
@returns
|
||||
This function returns a pointer to the struct #hid_device_info
|
||||
for this hid_device, or NULL in the case of failure.
|
||||
Call hid_error(dev) to get the failure reason.
|
||||
This struct is valid until the device is closed with hid_close().
|
||||
|
||||
@note The returned object is owned by the @p dev, and SHOULD NOT be freed by the user.
|
||||
*/
|
||||
struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_get_device_info(hid_device *dev);
|
||||
|
||||
/** @brief Get a string from a HID device, based on its string index.
|
||||
|
||||
@ingroup API
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param string_index The index of the string to get.
|
||||
@param string A wide string buffer to put the data into.
|
||||
@param maxlen The length of the buffer in multiples of wchar_t.
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
Call hid_error(dev) to get the failure reason.
|
||||
*/
|
||||
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen);
|
||||
|
||||
/** @brief Get a report descriptor from a HID device.
|
||||
|
||||
Since version 0.14.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 14, 0)
|
||||
|
||||
User has to provide a preallocated buffer where descriptor will be copied to.
|
||||
The recommended size for preallocated buffer is @ref HID_API_MAX_REPORT_DESCRIPTOR_SIZE bytes.
|
||||
|
||||
@ingroup API
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param buf The buffer to copy descriptor into.
|
||||
@param buf_size The size of the buffer in bytes.
|
||||
|
||||
@returns
|
||||
This function returns non-negative number of bytes actually copied, or -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size);
|
||||
|
||||
/** @brief Get a string describing the last error which occurred.
|
||||
|
||||
This function is intended for logging/debugging purposes.
|
||||
|
||||
This function guarantees to never return NULL.
|
||||
If there was no error in the last function call -
|
||||
the returned string clearly indicates that.
|
||||
|
||||
Any HIDAPI function that can explicitly indicate an execution failure
|
||||
(e.g. by an error code, or by returning NULL) - may set the error string,
|
||||
to be returned by this function.
|
||||
|
||||
Strings returned from hid_error() must not be freed by the user,
|
||||
i.e. owned by HIDAPI library.
|
||||
Device-specific error string may remain allocated at most until hid_close() is called.
|
||||
Global error string may remain allocated at most until hid_exit() is called.
|
||||
|
||||
@ingroup API
|
||||
@param dev A device handle returned from hid_open(),
|
||||
or NULL to get the last non-device-specific error
|
||||
(e.g. for errors in hid_open() or hid_enumerate()).
|
||||
|
||||
@returns
|
||||
A string describing the last error (if any).
|
||||
*/
|
||||
HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev);
|
||||
|
||||
/** @brief Get a runtime version of the library.
|
||||
|
||||
This function is thread-safe.
|
||||
|
||||
@ingroup API
|
||||
|
||||
@returns
|
||||
Pointer to statically allocated struct, that contains version.
|
||||
*/
|
||||
HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version(void);
|
||||
|
||||
|
||||
/** @brief Get a runtime version string of the library.
|
||||
|
||||
This function is thread-safe.
|
||||
|
||||
@ingroup API
|
||||
|
||||
@returns
|
||||
Pointer to statically allocated string, that contains version string.
|
||||
*/
|
||||
HID_API_EXPORT const char* HID_API_CALL hid_version_str(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1522
thirdparty/sdl/hidapi/linux/hid.c
vendored
Normal file
1522
thirdparty/sdl/hidapi/linux/hid.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1594
thirdparty/sdl/hidapi/mac/hid.c
vendored
Normal file
1594
thirdparty/sdl/hidapi/mac/hid.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
98
thirdparty/sdl/hidapi/mac/hidapi_darwin.h
vendored
Normal file
98
thirdparty/sdl/hidapi/mac/hidapi_darwin.h
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
/*******************************************************
|
||||
HIDAPI - Multi-Platform library for
|
||||
communication with HID devices.
|
||||
|
||||
libusb/hidapi Team
|
||||
|
||||
Copyright 2022, All Rights Reserved.
|
||||
|
||||
At the discretion of the user of this library,
|
||||
this software may be licensed under the terms of the
|
||||
GNU General Public License v3, a BSD-Style license, or the
|
||||
original HIDAPI license as outlined in the LICENSE.txt,
|
||||
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
|
||||
files located at the root of the source distribution.
|
||||
These files may also be found in the public source
|
||||
code repository located at:
|
||||
https://github.com/libusb/hidapi .
|
||||
********************************************************/
|
||||
|
||||
/** @file
|
||||
* @defgroup API hidapi API
|
||||
|
||||
* Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
|
||||
*/
|
||||
|
||||
#ifndef HIDAPI_DARWIN_H__
|
||||
#define HIDAPI_DARWIN_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../hidapi/hidapi.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @brief Get the location ID for a HID device.
|
||||
|
||||
Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
|
||||
|
||||
@ingroup API
|
||||
@param dev A device handle returned from hid_open().
|
||||
@param location_id The device's location ID on return.
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT_CALL hid_darwin_get_location_id(hid_device *dev, uint32_t *location_id);
|
||||
|
||||
|
||||
/** @brief Changes the behavior of all further calls to @ref hid_open or @ref hid_open_path.
|
||||
|
||||
By default on Darwin platform all devices opened by HIDAPI with @ref hid_open or @ref hid_open_path
|
||||
are opened in exclusive mode (see kIOHIDOptionsTypeSeizeDevice).
|
||||
|
||||
Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
|
||||
|
||||
@ingroup API
|
||||
@param open_exclusive When set to 0 - all further devices will be opened
|
||||
in non-exclusive mode. Otherwise - all further devices will be opened
|
||||
in exclusive mode.
|
||||
|
||||
@note During the initialisation by @ref hid_init - this property is set to 1 (TRUE).
|
||||
This is done to preserve full backward compatibility with previous behavior.
|
||||
|
||||
@note Calling this function before @ref hid_init or after @ref hid_exit has no effect.
|
||||
*/
|
||||
void HID_API_EXPORT_CALL hid_darwin_set_open_exclusive(int open_exclusive);
|
||||
|
||||
/** @brief Getter for option set by @ref hid_darwin_set_open_exclusive.
|
||||
|
||||
Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
|
||||
|
||||
@ingroup API
|
||||
@return 1 if all further devices will be opened in exclusive mode.
|
||||
|
||||
@note Value returned by this function before calling to @ref hid_init or after @ref hid_exit
|
||||
is not reliable.
|
||||
*/
|
||||
int HID_API_EXPORT_CALL hid_darwin_get_open_exclusive(void);
|
||||
|
||||
/** @brief Check how the device was opened.
|
||||
|
||||
Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0)
|
||||
|
||||
@ingroup API
|
||||
@param dev A device to get property from.
|
||||
|
||||
@return 1 if the device is opened in exclusive mode, 0 - opened in non-exclusive,
|
||||
-1 - if dev is invalid.
|
||||
*/
|
||||
int HID_API_EXPORT_CALL hid_darwin_is_device_open_exclusive(hid_device *dev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1728
thirdparty/sdl/hidapi/windows/hid.c
vendored
Normal file
1728
thirdparty/sdl/hidapi/windows/hid.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
86
thirdparty/sdl/hidapi/windows/hidapi_cfgmgr32.h
vendored
Normal file
86
thirdparty/sdl/hidapi/windows/hidapi_cfgmgr32.h
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
/*******************************************************
|
||||
HIDAPI - Multi-Platform library for
|
||||
communication with HID devices.
|
||||
|
||||
libusb/hidapi Team
|
||||
|
||||
Copyright 2022, All Rights Reserved.
|
||||
|
||||
At the discretion of the user of this library,
|
||||
this software may be licensed under the terms of the
|
||||
GNU General Public License v3, a BSD-Style license, or the
|
||||
original HIDAPI license as outlined in the LICENSE.txt,
|
||||
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
|
||||
files located at the root of the source distribution.
|
||||
These files may also be found in the public source
|
||||
code repository located at:
|
||||
https://github.com/libusb/hidapi .
|
||||
********************************************************/
|
||||
|
||||
#ifndef HIDAPI_CFGMGR32_H
|
||||
#define HIDAPI_CFGMGR32_H
|
||||
|
||||
#ifdef HIDAPI_USE_DDK
|
||||
|
||||
#include <cfgmgr32.h>
|
||||
#include <initguid.h>
|
||||
#include <devpkey.h>
|
||||
#include <propkey.h>
|
||||
|
||||
#else
|
||||
|
||||
/* This part of the header mimics cfgmgr32.h,
|
||||
but only what is used by HIDAPI */
|
||||
|
||||
//#include <initguid.h>
|
||||
#include <devpropdef.h>
|
||||
//#include <propkeydef.h>
|
||||
|
||||
#ifndef PROPERTYKEY_DEFINED
|
||||
#define PROPERTYKEY_DEFINED
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GUID fmtid;
|
||||
DWORD pid;
|
||||
} PROPERTYKEY;
|
||||
|
||||
#endif /* PROPERTYKEY_DEFINED */
|
||||
|
||||
typedef DWORD RETURN_TYPE;
|
||||
typedef RETURN_TYPE CONFIGRET;
|
||||
typedef DWORD DEVNODE, DEVINST;
|
||||
typedef DEVNODE* PDEVNODE, * PDEVINST;
|
||||
typedef WCHAR* DEVNODEID_W, * DEVINSTID_W;
|
||||
|
||||
#define CR_SUCCESS (0x00000000)
|
||||
#define CR_BUFFER_SMALL (0x0000001A)
|
||||
#define CR_FAILURE (0x00000013)
|
||||
|
||||
#define CM_LOCATE_DEVNODE_NORMAL 0x00000000
|
||||
|
||||
#define CM_GET_DEVICE_INTERFACE_LIST_PRESENT (0x00000000)
|
||||
|
||||
typedef CONFIGRET(__stdcall* CM_Locate_DevNodeW_)(PDEVINST pdnDevInst, DEVINSTID_W pDeviceID, ULONG ulFlags);
|
||||
typedef CONFIGRET(__stdcall* CM_Get_Parent_)(PDEVINST pdnDevInst, DEVINST dnDevInst, ULONG ulFlags);
|
||||
typedef CONFIGRET(__stdcall* CM_Get_DevNode_PropertyW_)(DEVINST dnDevInst, CONST DEVPROPKEY* PropertyKey, DEVPROPTYPE* PropertyType, PBYTE PropertyBuffer, PULONG PropertyBufferSize, ULONG ulFlags);
|
||||
typedef CONFIGRET(__stdcall* CM_Get_Device_Interface_PropertyW_)(LPCWSTR pszDeviceInterface, CONST DEVPROPKEY* PropertyKey, DEVPROPTYPE* PropertyType, PBYTE PropertyBuffer, PULONG PropertyBufferSize, ULONG ulFlags);
|
||||
typedef CONFIGRET(__stdcall* CM_Get_Device_Interface_List_SizeW_)(PULONG pulLen, LPGUID InterfaceClassGuid, DEVINSTID_W pDeviceID, ULONG ulFlags);
|
||||
typedef CONFIGRET(__stdcall* CM_Get_Device_Interface_ListW_)(LPGUID InterfaceClassGuid, DEVINSTID_W pDeviceID, WCHAR* /*PZZWSTR*/ Buffer, ULONG BufferLen, ULONG ulFlags);
|
||||
|
||||
// from devpkey.h
|
||||
static DEVPROPKEY DEVPKEY_NAME = { { 0xb725f130, 0x47ef, 0x101a, {0xa5, 0xf1, 0x02, 0x60, 0x8c, 0x9e, 0xeb, 0xac} }, 10 }; // DEVPROP_TYPE_STRING
|
||||
static DEVPROPKEY DEVPKEY_Device_Manufacturer = { { 0xa45c254e, 0xdf1c, 0x4efd, {0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0} }, 13 }; // DEVPROP_TYPE_STRING
|
||||
static DEVPROPKEY DEVPKEY_Device_InstanceId = { { 0x78c34fc8, 0x104a, 0x4aca, {0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57} }, 256 }; // DEVPROP_TYPE_STRING
|
||||
static DEVPROPKEY DEVPKEY_Device_HardwareIds = { { 0xa45c254e, 0xdf1c, 0x4efd, {0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0} }, 3 }; // DEVPROP_TYPE_STRING_LIST
|
||||
static DEVPROPKEY DEVPKEY_Device_CompatibleIds = { { 0xa45c254e, 0xdf1c, 0x4efd, {0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0} }, 4 }; // DEVPROP_TYPE_STRING_LIST
|
||||
static DEVPROPKEY DEVPKEY_Device_ContainerId = { { 0x8c7ed206, 0x3f8a, 0x4827, {0xb3, 0xab, 0xae, 0x9e, 0x1f, 0xae, 0xfc, 0x6c} }, 2 }; // DEVPROP_TYPE_GUID
|
||||
|
||||
// from propkey.h
|
||||
static PROPERTYKEY PKEY_DeviceInterface_Bluetooth_DeviceAddress = { { 0x2bd67d8b, 0x8beb, 0x48d5, {0x87, 0xe0, 0x6c, 0xda, 0x34, 0x28, 0x04, 0x0a} }, 1 }; // DEVPROP_TYPE_STRING
|
||||
static PROPERTYKEY PKEY_DeviceInterface_Bluetooth_Manufacturer = { { 0x2bd67d8b, 0x8beb, 0x48d5, {0x87, 0xe0, 0x6c, 0xda, 0x34, 0x28, 0x04, 0x0a} }, 4 }; // DEVPROP_TYPE_STRING
|
||||
static PROPERTYKEY PKEY_DeviceInterface_Bluetooth_ModelNumber = { { 0x2BD67D8B, 0x8BEB, 0x48D5, {0x87, 0xE0, 0x6C, 0xDA, 0x34, 0x28, 0x04, 0x0A} }, 5 }; // DEVPROP_TYPE_STRING
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* HIDAPI_CFGMGR32_H */
|
||||
990
thirdparty/sdl/hidapi/windows/hidapi_descriptor_reconstruct.c
vendored
Normal file
990
thirdparty/sdl/hidapi/windows/hidapi_descriptor_reconstruct.c
vendored
Normal file
@@ -0,0 +1,990 @@
|
||||
/*******************************************************
|
||||
HIDAPI - Multi-Platform library for
|
||||
communication with HID devices.
|
||||
|
||||
libusb/hidapi Team
|
||||
|
||||
Copyright 2022, All Rights Reserved.
|
||||
|
||||
At the discretion of the user of this library,
|
||||
this software may be licensed under the terms of the
|
||||
GNU General Public License v3, a BSD-Style license, or the
|
||||
original HIDAPI license as outlined in the LICENSE.txt,
|
||||
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
|
||||
files located at the root of the source distribution.
|
||||
These files may also be found in the public source
|
||||
code repository located at:
|
||||
https://github.com/libusb/hidapi .
|
||||
********************************************************/
|
||||
#include "hidapi_descriptor_reconstruct.h"
|
||||
|
||||
/**
|
||||
* @brief References to report descriptor buffer.
|
||||
*
|
||||
*/
|
||||
struct rd_buffer {
|
||||
unsigned char* buf; /* Pointer to the array which stores the reconstructed descriptor */
|
||||
size_t buf_size; /* Size of the buffer in bytes */
|
||||
size_t byte_idx; /* Index of the next report byte to write to buf array */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Function that appends a byte to encoded report descriptor buffer.
|
||||
*
|
||||
* @param[in] byte Single byte to append.
|
||||
* @param rpt_desc Pointer to report descriptor buffer struct.
|
||||
*/
|
||||
static void rd_append_byte(unsigned char byte, struct rd_buffer* rpt_desc) {
|
||||
if (rpt_desc->byte_idx < rpt_desc->buf_size) {
|
||||
rpt_desc->buf[rpt_desc->byte_idx] = byte;
|
||||
rpt_desc->byte_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes a short report descriptor item according USB HID spec 1.11 chapter 6.2.2.2.
|
||||
*
|
||||
* @param[in] rd_item Enumeration identifying type (Main, Global, Local) and function (e.g Usage or Report Count) of the item.
|
||||
* @param[in] data Data (Size depends on rd_item 0,1,2 or 4bytes).
|
||||
* @param rpt_desc Pointer to report descriptor buffer struct.
|
||||
*
|
||||
* @return Returns 0 if successful, -1 for error.
|
||||
*/
|
||||
static int rd_write_short_item(rd_items rd_item, LONG64 data, struct rd_buffer* rpt_desc) {
|
||||
if (rd_item & 0x03) {
|
||||
// Invalid input data, last to bits are reserved for data size
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rd_item == rd_main_collection_end) {
|
||||
// Item without data (1Byte prefix only)
|
||||
unsigned char oneBytePrefix = (unsigned char) rd_item + 0x00;
|
||||
rd_append_byte(oneBytePrefix, rpt_desc);
|
||||
}
|
||||
else if ((rd_item == rd_global_logical_minimum) ||
|
||||
(rd_item == rd_global_logical_maximum) ||
|
||||
(rd_item == rd_global_physical_minimum) ||
|
||||
(rd_item == rd_global_physical_maximum)) {
|
||||
// Item with signed integer data
|
||||
if ((data >= -128) && (data <= 127)) {
|
||||
// 1Byte prefix + 1Byte data
|
||||
unsigned char oneBytePrefix = (unsigned char) rd_item + 0x01;
|
||||
char localData = (char)data;
|
||||
rd_append_byte(oneBytePrefix, rpt_desc);
|
||||
rd_append_byte(localData & 0xFF, rpt_desc);
|
||||
}
|
||||
else if ((data >= -32768) && (data <= 32767)) {
|
||||
// 1Byte prefix + 2Byte data
|
||||
unsigned char oneBytePrefix = (unsigned char) rd_item + 0x02;
|
||||
INT16 localData = (INT16)data;
|
||||
rd_append_byte(oneBytePrefix, rpt_desc);
|
||||
rd_append_byte(localData & 0xFF, rpt_desc);
|
||||
rd_append_byte(localData >> 8 & 0xFF, rpt_desc);
|
||||
}
|
||||
else if ((data >= -2147483648LL) && (data <= 2147483647)) {
|
||||
// 1Byte prefix + 4Byte data
|
||||
unsigned char oneBytePrefix = (unsigned char) rd_item + 0x03;
|
||||
INT32 localData = (INT32)data;
|
||||
rd_append_byte(oneBytePrefix, rpt_desc);
|
||||
rd_append_byte(localData & 0xFF, rpt_desc);
|
||||
rd_append_byte(localData >> 8 & 0xFF, rpt_desc);
|
||||
rd_append_byte(localData >> 16 & 0xFF, rpt_desc);
|
||||
rd_append_byte(localData >> 24 & 0xFF, rpt_desc);
|
||||
}
|
||||
else {
|
||||
// Data out of 32 bit signed integer range
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Item with unsigned integer data
|
||||
if ((data >= 0) && (data <= 0xFF)) {
|
||||
// 1Byte prefix + 1Byte data
|
||||
unsigned char oneBytePrefix = (unsigned char) rd_item + 0x01;
|
||||
unsigned char localData = (unsigned char)data;
|
||||
rd_append_byte(oneBytePrefix, rpt_desc);
|
||||
rd_append_byte(localData & 0xFF, rpt_desc);
|
||||
}
|
||||
else if ((data >= 0) && (data <= 0xFFFF)) {
|
||||
// 1Byte prefix + 2Byte data
|
||||
unsigned char oneBytePrefix = (unsigned char) rd_item + 0x02;
|
||||
UINT16 localData = (UINT16)data;
|
||||
rd_append_byte(oneBytePrefix, rpt_desc);
|
||||
rd_append_byte(localData & 0xFF, rpt_desc);
|
||||
rd_append_byte(localData >> 8 & 0xFF, rpt_desc);
|
||||
}
|
||||
else if ((data >= 0) && (data <= 0xFFFFFFFF)) {
|
||||
// 1Byte prefix + 4Byte data
|
||||
unsigned char oneBytePrefix = (unsigned char) rd_item + 0x03;
|
||||
UINT32 localData = (UINT32)data;
|
||||
rd_append_byte(oneBytePrefix, rpt_desc);
|
||||
rd_append_byte(localData & 0xFF, rpt_desc);
|
||||
rd_append_byte(localData >> 8 & 0xFF, rpt_desc);
|
||||
rd_append_byte(localData >> 16 & 0xFF, rpt_desc);
|
||||
rd_append_byte(localData >> 24 & 0xFF, rpt_desc);
|
||||
}
|
||||
else {
|
||||
// Data out of 32 bit unsigned integer range
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rd_main_item_node * rd_append_main_item_node(int first_bit, int last_bit, rd_node_type type_of_node, int caps_index, int collection_index, rd_main_items main_item_type, unsigned char report_id, struct rd_main_item_node **list) {
|
||||
struct rd_main_item_node *new_list_node;
|
||||
|
||||
// Determine last node in the list
|
||||
while (*list != NULL)
|
||||
{
|
||||
list = &(*list)->next;
|
||||
}
|
||||
|
||||
new_list_node = malloc(sizeof(*new_list_node)); // Create new list entry
|
||||
new_list_node->FirstBit = first_bit;
|
||||
new_list_node->LastBit = last_bit;
|
||||
new_list_node->TypeOfNode = type_of_node;
|
||||
new_list_node->CapsIndex = caps_index;
|
||||
new_list_node->CollectionIndex = collection_index;
|
||||
new_list_node->MainItemType = main_item_type;
|
||||
new_list_node->ReportID = report_id;
|
||||
new_list_node->next = NULL; // NULL marks last node in the list
|
||||
|
||||
*list = new_list_node;
|
||||
return new_list_node;
|
||||
}
|
||||
|
||||
static struct rd_main_item_node * rd_insert_main_item_node(int first_bit, int last_bit, rd_node_type type_of_node, int caps_index, int collection_index, rd_main_items main_item_type, unsigned char report_id, struct rd_main_item_node **list) {
|
||||
// Insert item after the main item node referenced by list
|
||||
struct rd_main_item_node *next_item = (*list)->next;
|
||||
(*list)->next = NULL;
|
||||
rd_append_main_item_node(first_bit, last_bit, type_of_node, caps_index, collection_index, main_item_type, report_id, list);
|
||||
(*list)->next->next = next_item;
|
||||
return (*list)->next;
|
||||
}
|
||||
|
||||
static struct rd_main_item_node * rd_search_main_item_list_for_bit_position(int search_bit, rd_main_items main_item_type, unsigned char report_id, struct rd_main_item_node **list) {
|
||||
// Determine first INPUT/OUTPUT/FEATURE main item, where the last bit position is equal or greater than the search bit position
|
||||
|
||||
while (((*list)->next->MainItemType != rd_collection) &&
|
||||
((*list)->next->MainItemType != rd_collection_end) &&
|
||||
!(((*list)->next->LastBit >= search_bit) &&
|
||||
((*list)->next->ReportID == report_id) &&
|
||||
((*list)->next->MainItemType == main_item_type))
|
||||
)
|
||||
{
|
||||
list = &(*list)->next;
|
||||
}
|
||||
return *list;
|
||||
}
|
||||
|
||||
int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned char *buf, size_t buf_size)
|
||||
{
|
||||
hidp_preparsed_data *pp_data = (hidp_preparsed_data *) preparsed_data;
|
||||
|
||||
// Check if MagicKey is correct, to ensure that pp_data points to an valid preparse data structure
|
||||
if (memcmp(pp_data->MagicKey, "HidP KDR", 8) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct rd_buffer rpt_desc;
|
||||
rpt_desc.buf = buf;
|
||||
rpt_desc.buf_size = buf_size;
|
||||
rpt_desc.byte_idx = 0;
|
||||
|
||||
// Set pointer to the first node of link_collection_nodes
|
||||
phid_pp_link_collection_node link_collection_nodes = (phid_pp_link_collection_node)(((unsigned char*)&pp_data->caps[0]) + pp_data->FirstByteOfLinkCollectionArray);
|
||||
|
||||
// ****************************************************************************************************************************
|
||||
// Create lookup tables for the bit range of each report per collection (position of first bit and last bit in each collection)
|
||||
// coll_bit_range[COLLECTION_INDEX][REPORT_ID][INPUT/OUTPUT/FEATURE]
|
||||
// ****************************************************************************************************************************
|
||||
|
||||
// Allocate memory and initialize lookup table
|
||||
rd_bit_range ****coll_bit_range;
|
||||
coll_bit_range = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_bit_range));
|
||||
for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
|
||||
coll_bit_range[collection_node_idx] = malloc(256 * sizeof(*coll_bit_range[0])); // 256 possible report IDs (incl. 0x00)
|
||||
for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
|
||||
coll_bit_range[collection_node_idx][reportid_idx] = malloc(NUM_OF_HIDP_REPORT_TYPES * sizeof(*coll_bit_range[0][0]));
|
||||
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
|
||||
coll_bit_range[collection_node_idx][reportid_idx][rt_idx] = malloc(sizeof(rd_bit_range));
|
||||
coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->FirstBit = -1;
|
||||
coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->LastBit = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the lookup table where caps exist
|
||||
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
|
||||
for (USHORT caps_idx = pp_data->caps_info[rt_idx].FirstCap; caps_idx < pp_data->caps_info[rt_idx].LastCap; caps_idx++) {
|
||||
int first_bit, last_bit;
|
||||
first_bit = (pp_data->caps[caps_idx].BytePosition - 1) * 8
|
||||
+ pp_data->caps[caps_idx].BitPosition;
|
||||
last_bit = first_bit + pp_data->caps[caps_idx].ReportSize
|
||||
* pp_data->caps[caps_idx].ReportCount - 1;
|
||||
if (coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit == -1 ||
|
||||
coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit > first_bit) {
|
||||
coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit = first_bit;
|
||||
}
|
||||
if (coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->LastBit < last_bit) {
|
||||
coll_bit_range[pp_data->caps[caps_idx].LinkCollection][pp_data->caps[caps_idx].ReportID][rt_idx]->LastBit = last_bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// *************************************************************************
|
||||
// -Determine hierarchy levels of each collections and store it in:
|
||||
// coll_levels[COLLECTION_INDEX]
|
||||
// -Determine number of direct childs of each collections and store it in:
|
||||
// coll_number_of_direct_childs[COLLECTION_INDEX]
|
||||
// *************************************************************************
|
||||
int max_coll_level = 0;
|
||||
int *coll_levels = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_levels[0]));
|
||||
int *coll_number_of_direct_childs = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_number_of_direct_childs[0]));
|
||||
for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
|
||||
coll_levels[collection_node_idx] = -1;
|
||||
coll_number_of_direct_childs[collection_node_idx] = 0;
|
||||
}
|
||||
|
||||
{
|
||||
int actual_coll_level = 0;
|
||||
USHORT collection_node_idx = 0;
|
||||
while (actual_coll_level >= 0) {
|
||||
coll_levels[collection_node_idx] = actual_coll_level;
|
||||
if ((link_collection_nodes[collection_node_idx].NumberOfChildren > 0) &&
|
||||
(coll_levels[link_collection_nodes[collection_node_idx].FirstChild] == -1)) {
|
||||
actual_coll_level++;
|
||||
coll_levels[collection_node_idx] = actual_coll_level;
|
||||
if (max_coll_level < actual_coll_level) {
|
||||
max_coll_level = actual_coll_level;
|
||||
}
|
||||
coll_number_of_direct_childs[collection_node_idx]++;
|
||||
collection_node_idx = link_collection_nodes[collection_node_idx].FirstChild;
|
||||
}
|
||||
else if (link_collection_nodes[collection_node_idx].NextSibling != 0) {
|
||||
coll_number_of_direct_childs[link_collection_nodes[collection_node_idx].Parent]++;
|
||||
collection_node_idx = link_collection_nodes[collection_node_idx].NextSibling;
|
||||
}
|
||||
else {
|
||||
actual_coll_level--;
|
||||
if (actual_coll_level >= 0) {
|
||||
collection_node_idx = link_collection_nodes[collection_node_idx].Parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// *********************************************************************************
|
||||
// Propagate the bit range of each report from the child collections to their parent
|
||||
// and store the merged result for the parent
|
||||
// *********************************************************************************
|
||||
for (int actual_coll_level = max_coll_level - 1; actual_coll_level >= 0; actual_coll_level--) {
|
||||
for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
|
||||
if (coll_levels[collection_node_idx] == actual_coll_level) {
|
||||
USHORT child_idx = link_collection_nodes[collection_node_idx].FirstChild;
|
||||
while (child_idx) {
|
||||
for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
|
||||
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
|
||||
// Merge bit range from childs
|
||||
if ((coll_bit_range[child_idx][reportid_idx][rt_idx]->FirstBit != -1) &&
|
||||
(coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->FirstBit > coll_bit_range[child_idx][reportid_idx][rt_idx]->FirstBit)) {
|
||||
coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->FirstBit = coll_bit_range[child_idx][reportid_idx][rt_idx]->FirstBit;
|
||||
}
|
||||
if (coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->LastBit < coll_bit_range[child_idx][reportid_idx][rt_idx]->LastBit) {
|
||||
coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->LastBit = coll_bit_range[child_idx][reportid_idx][rt_idx]->LastBit;
|
||||
}
|
||||
child_idx = link_collection_nodes[child_idx].NextSibling;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// **************************************************************************************************
|
||||
// Determine child collection order of the whole hierarchy, based on previously determined bit ranges
|
||||
// and store it this index coll_child_order[COLLECTION_INDEX][DIRECT_CHILD_INDEX]
|
||||
// **************************************************************************************************
|
||||
USHORT **coll_child_order;
|
||||
coll_child_order = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_child_order));
|
||||
{
|
||||
BOOLEAN *coll_parsed_flag;
|
||||
coll_parsed_flag = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_parsed_flag[0]));
|
||||
for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
|
||||
coll_parsed_flag[collection_node_idx] = FALSE;
|
||||
}
|
||||
int actual_coll_level = 0;
|
||||
USHORT collection_node_idx = 0;
|
||||
while (actual_coll_level >= 0) {
|
||||
if ((coll_number_of_direct_childs[collection_node_idx] != 0) &&
|
||||
(coll_parsed_flag[link_collection_nodes[collection_node_idx].FirstChild] == FALSE)) {
|
||||
coll_parsed_flag[link_collection_nodes[collection_node_idx].FirstChild] = TRUE;
|
||||
coll_child_order[collection_node_idx] = malloc((coll_number_of_direct_childs[collection_node_idx]) * sizeof(*coll_child_order[0]));
|
||||
|
||||
{
|
||||
// Create list of child collection indices
|
||||
// sorted reverse to the order returned to HidP_GetLinkCollectionNodeschild
|
||||
// which seems to match the original order, as long as no bit position needs to be considered
|
||||
USHORT child_idx = link_collection_nodes[collection_node_idx].FirstChild;
|
||||
int child_count = coll_number_of_direct_childs[collection_node_idx] - 1;
|
||||
coll_child_order[collection_node_idx][child_count] = child_idx;
|
||||
while (link_collection_nodes[child_idx].NextSibling) {
|
||||
child_count--;
|
||||
child_idx = link_collection_nodes[child_idx].NextSibling;
|
||||
coll_child_order[collection_node_idx][child_count] = child_idx;
|
||||
}
|
||||
}
|
||||
|
||||
if (coll_number_of_direct_childs[collection_node_idx] > 1) {
|
||||
// Sort child collections indices by bit positions
|
||||
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
|
||||
for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
|
||||
for (int child_idx = 1; child_idx < coll_number_of_direct_childs[collection_node_idx]; child_idx++) {
|
||||
// since the coll_bit_range array is not sorted, we need to reference the collection index in
|
||||
// our sorted coll_child_order array, and look up the corresponding bit ranges for comparing values to sort
|
||||
int prev_coll_idx = coll_child_order[collection_node_idx][child_idx - 1];
|
||||
int cur_coll_idx = coll_child_order[collection_node_idx][child_idx];
|
||||
if ((coll_bit_range[prev_coll_idx][reportid_idx][rt_idx]->FirstBit != -1) &&
|
||||
(coll_bit_range[cur_coll_idx][reportid_idx][rt_idx]->FirstBit != -1) &&
|
||||
(coll_bit_range[prev_coll_idx][reportid_idx][rt_idx]->FirstBit > coll_bit_range[cur_coll_idx][reportid_idx][rt_idx]->FirstBit)) {
|
||||
// Swap position indices of the two compared child collections
|
||||
USHORT idx_latch = coll_child_order[collection_node_idx][child_idx - 1];
|
||||
coll_child_order[collection_node_idx][child_idx - 1] = coll_child_order[collection_node_idx][child_idx];
|
||||
coll_child_order[collection_node_idx][child_idx] = idx_latch;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
actual_coll_level++;
|
||||
collection_node_idx = link_collection_nodes[collection_node_idx].FirstChild;
|
||||
}
|
||||
else if (link_collection_nodes[collection_node_idx].NextSibling != 0) {
|
||||
collection_node_idx = link_collection_nodes[collection_node_idx].NextSibling;
|
||||
}
|
||||
else {
|
||||
actual_coll_level--;
|
||||
if (actual_coll_level >= 0) {
|
||||
collection_node_idx = link_collection_nodes[collection_node_idx].Parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(coll_parsed_flag);
|
||||
}
|
||||
|
||||
|
||||
// ***************************************************************************************
|
||||
// Create sorted main_item_list containing all the Collection and CollectionEnd main items
|
||||
// ***************************************************************************************
|
||||
struct rd_main_item_node *main_item_list = NULL; // List root
|
||||
// Lookup table to find the Collection items in the list by index
|
||||
struct rd_main_item_node **coll_begin_lookup = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_begin_lookup));
|
||||
struct rd_main_item_node **coll_end_lookup = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_end_lookup));
|
||||
{
|
||||
int *coll_last_written_child = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_last_written_child[0]));
|
||||
for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
|
||||
coll_last_written_child[collection_node_idx] = -1;
|
||||
}
|
||||
|
||||
int actual_coll_level = 0;
|
||||
USHORT collection_node_idx = 0;
|
||||
struct rd_main_item_node *firstDelimiterNode = NULL;
|
||||
struct rd_main_item_node *delimiterCloseNode = NULL;
|
||||
coll_begin_lookup[0] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_collection, 0, &main_item_list);
|
||||
while (actual_coll_level >= 0) {
|
||||
if ((coll_number_of_direct_childs[collection_node_idx] != 0) &&
|
||||
(coll_last_written_child[collection_node_idx] == -1)) {
|
||||
// Collection has child collections, but none is written to the list yet
|
||||
|
||||
coll_last_written_child[collection_node_idx] = coll_child_order[collection_node_idx][0];
|
||||
collection_node_idx = coll_child_order[collection_node_idx][0];
|
||||
|
||||
// In a HID Report Descriptor, the first usage declared is the most preferred usage for the control.
|
||||
// While the order in the WIN32 capabiliy strutures is the opposite:
|
||||
// Here the preferred usage is the last aliased usage in the sequence.
|
||||
|
||||
if (link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode == NULL)) {
|
||||
// Alliased Collection (First node in link_collection_nodes -> Last entry in report descriptor output)
|
||||
firstDelimiterNode = main_item_list;
|
||||
coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_usage, 0, &main_item_list);
|
||||
coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_close, 0, &main_item_list);
|
||||
delimiterCloseNode = main_item_list;
|
||||
}
|
||||
else {
|
||||
// Normal not aliased collection
|
||||
coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_collection, 0, &main_item_list);
|
||||
actual_coll_level++;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else if ((coll_number_of_direct_childs[collection_node_idx] > 1) &&
|
||||
(coll_last_written_child[collection_node_idx] != coll_child_order[collection_node_idx][coll_number_of_direct_childs[collection_node_idx] - 1])) {
|
||||
// Collection has child collections, and this is not the first child
|
||||
|
||||
int nextChild = 1;
|
||||
while (coll_last_written_child[collection_node_idx] != coll_child_order[collection_node_idx][nextChild - 1]) {
|
||||
nextChild++;
|
||||
}
|
||||
coll_last_written_child[collection_node_idx] = coll_child_order[collection_node_idx][nextChild];
|
||||
collection_node_idx = coll_child_order[collection_node_idx][nextChild];
|
||||
|
||||
if (link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode == NULL)) {
|
||||
// Alliased Collection (First node in link_collection_nodes -> Last entry in report descriptor output)
|
||||
firstDelimiterNode = main_item_list;
|
||||
coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_usage, 0, &main_item_list);
|
||||
coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_close, 0, &main_item_list);
|
||||
delimiterCloseNode = main_item_list;
|
||||
}
|
||||
else if (link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode != NULL)) {
|
||||
coll_begin_lookup[collection_node_idx] = rd_insert_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_usage, 0, &firstDelimiterNode);
|
||||
}
|
||||
else if (!link_collection_nodes[collection_node_idx].IsAlias && (firstDelimiterNode != NULL)) {
|
||||
coll_begin_lookup[collection_node_idx] = rd_insert_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_usage, 0, &firstDelimiterNode);
|
||||
coll_begin_lookup[collection_node_idx] = rd_insert_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_delimiter_open, 0, &firstDelimiterNode);
|
||||
firstDelimiterNode = NULL;
|
||||
main_item_list = delimiterCloseNode;
|
||||
delimiterCloseNode = NULL; // Last entry of alias has .IsAlias == FALSE
|
||||
}
|
||||
if (!link_collection_nodes[collection_node_idx].IsAlias) {
|
||||
coll_begin_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_collection, 0, &main_item_list);
|
||||
actual_coll_level++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
actual_coll_level--;
|
||||
coll_end_lookup[collection_node_idx] = rd_append_main_item_node(0, 0, rd_item_node_collection, 0, collection_node_idx, rd_collection_end, 0, &main_item_list);
|
||||
collection_node_idx = link_collection_nodes[collection_node_idx].Parent;
|
||||
}
|
||||
}
|
||||
free(coll_last_written_child);
|
||||
}
|
||||
|
||||
|
||||
// ****************************************************************
|
||||
// Inserted Input/Output/Feature main items into the main_item_list
|
||||
// in order of reconstructed bit positions
|
||||
// ****************************************************************
|
||||
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
|
||||
// Add all value caps to node list
|
||||
struct rd_main_item_node *firstDelimiterNode = NULL;
|
||||
struct rd_main_item_node *delimiterCloseNode = NULL;
|
||||
for (USHORT caps_idx = pp_data->caps_info[rt_idx].FirstCap; caps_idx < pp_data->caps_info[rt_idx].LastCap; caps_idx++) {
|
||||
struct rd_main_item_node *coll_begin = coll_begin_lookup[pp_data->caps[caps_idx].LinkCollection];
|
||||
int first_bit, last_bit;
|
||||
first_bit = (pp_data->caps[caps_idx].BytePosition - 1) * 8 +
|
||||
pp_data->caps[caps_idx].BitPosition;
|
||||
last_bit = first_bit + pp_data->caps[caps_idx].ReportSize *
|
||||
pp_data->caps[caps_idx].ReportCount - 1;
|
||||
|
||||
for (int child_idx = 0; child_idx < coll_number_of_direct_childs[pp_data->caps[caps_idx].LinkCollection]; child_idx++) {
|
||||
// Determine in which section before/between/after child collection the item should be inserted
|
||||
if (first_bit < coll_bit_range[coll_child_order[pp_data->caps[caps_idx].LinkCollection][child_idx]][pp_data->caps[caps_idx].ReportID][rt_idx]->FirstBit)
|
||||
{
|
||||
// Note, that the default value for undefined coll_bit_range is -1, which can't be greater than the bit position
|
||||
break;
|
||||
}
|
||||
coll_begin = coll_end_lookup[coll_child_order[pp_data->caps[caps_idx].LinkCollection][child_idx]];
|
||||
}
|
||||
struct rd_main_item_node *list_node;
|
||||
list_node = rd_search_main_item_list_for_bit_position(first_bit, (rd_main_items) rt_idx, pp_data->caps[caps_idx].ReportID, &coll_begin);
|
||||
|
||||
// In a HID Report Descriptor, the first usage declared is the most preferred usage for the control.
|
||||
// While the order in the WIN32 capabiliy strutures is the opposite:
|
||||
// Here the preferred usage is the last aliased usage in the sequence.
|
||||
|
||||
if (pp_data->caps[caps_idx].IsAlias && (firstDelimiterNode == NULL)) {
|
||||
// Alliased Usage (First node in pp_data->caps -> Last entry in report descriptor output)
|
||||
firstDelimiterNode = list_node;
|
||||
rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, rd_delimiter_usage, pp_data->caps[caps_idx].ReportID, &list_node);
|
||||
rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, rd_delimiter_close, pp_data->caps[caps_idx].ReportID, &list_node);
|
||||
delimiterCloseNode = list_node;
|
||||
} else if (pp_data->caps[caps_idx].IsAlias && (firstDelimiterNode != NULL)) {
|
||||
rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, rd_delimiter_usage, pp_data->caps[caps_idx].ReportID, &list_node);
|
||||
}
|
||||
else if (!pp_data->caps[caps_idx].IsAlias && (firstDelimiterNode != NULL)) {
|
||||
// Alliased Collection (Last node in pp_data->caps -> First entry in report descriptor output)
|
||||
rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, rd_delimiter_usage, pp_data->caps[caps_idx].ReportID, &list_node);
|
||||
rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, rd_delimiter_open, pp_data->caps[caps_idx].ReportID, &list_node);
|
||||
firstDelimiterNode = NULL;
|
||||
list_node = delimiterCloseNode;
|
||||
delimiterCloseNode = NULL; // Last entry of alias has .IsAlias == FALSE
|
||||
}
|
||||
if (!pp_data->caps[caps_idx].IsAlias) {
|
||||
rd_insert_main_item_node(first_bit, last_bit, rd_item_node_cap, caps_idx, pp_data->caps[caps_idx].LinkCollection, (rd_main_items) rt_idx, pp_data->caps[caps_idx].ReportID, &list_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ***********************************************************
|
||||
// Add const main items for padding to main_item_list
|
||||
// -To fill all bit gaps
|
||||
// -At each report end for 8bit padding
|
||||
// Note that information about the padding at the report end,
|
||||
// is not stored in the preparsed data, but in practice all
|
||||
// report descriptors seem to have it, as assumed here.
|
||||
// ***********************************************************
|
||||
{
|
||||
int *last_bit_position[NUM_OF_HIDP_REPORT_TYPES];
|
||||
struct rd_main_item_node **last_report_item_lookup[NUM_OF_HIDP_REPORT_TYPES];
|
||||
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
|
||||
last_bit_position[rt_idx] = malloc(256 * sizeof(*last_bit_position[rt_idx]));
|
||||
last_report_item_lookup[rt_idx] = malloc(256 * sizeof(*last_report_item_lookup[rt_idx]));
|
||||
for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
|
||||
last_bit_position[rt_idx][reportid_idx] = -1;
|
||||
last_report_item_lookup[rt_idx][reportid_idx] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct rd_main_item_node *list = main_item_list; // List root;
|
||||
|
||||
while (list->next != NULL)
|
||||
{
|
||||
if ((list->MainItemType >= rd_input) &&
|
||||
(list->MainItemType <= rd_feature)) {
|
||||
// INPUT, OUTPUT or FEATURE
|
||||
if (list->FirstBit != -1) {
|
||||
if ((last_bit_position[list->MainItemType][list->ReportID] + 1 != list->FirstBit) &&
|
||||
(last_report_item_lookup[list->MainItemType][list->ReportID] != NULL) &&
|
||||
(last_report_item_lookup[list->MainItemType][list->ReportID]->FirstBit != list->FirstBit) // Happens in case of IsMultipleItemsForArray for multiple dedicated usages for a multi-button array
|
||||
) {
|
||||
struct rd_main_item_node *list_node = rd_search_main_item_list_for_bit_position(last_bit_position[list->MainItemType][list->ReportID], list->MainItemType, list->ReportID, &last_report_item_lookup[list->MainItemType][list->ReportID]);
|
||||
rd_insert_main_item_node(last_bit_position[list->MainItemType][list->ReportID] + 1, list->FirstBit - 1, rd_item_node_padding, -1, 0, list->MainItemType, list->ReportID, &list_node);
|
||||
}
|
||||
last_bit_position[list->MainItemType][list->ReportID] = list->LastBit;
|
||||
last_report_item_lookup[list->MainItemType][list->ReportID] = list;
|
||||
}
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
// Add 8 bit padding at each report end
|
||||
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
|
||||
for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
|
||||
if (last_bit_position[rt_idx][reportid_idx] != -1) {
|
||||
int padding = 8 - ((last_bit_position[rt_idx][reportid_idx] + 1) % 8);
|
||||
if (padding < 8) {
|
||||
// Insert padding item after item referenced in last_report_item_lookup
|
||||
rd_insert_main_item_node(last_bit_position[rt_idx][reportid_idx] + 1, last_bit_position[rt_idx][reportid_idx] + padding, rd_item_node_padding, -1, 0, (rd_main_items) rt_idx, (unsigned char) reportid_idx, &last_report_item_lookup[rt_idx][reportid_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(last_bit_position[rt_idx]);
|
||||
free(last_report_item_lookup[rt_idx]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ***********************************
|
||||
// Encode the report descriptor output
|
||||
// ***********************************
|
||||
UCHAR last_report_id = 0;
|
||||
USAGE last_usage_page = 0;
|
||||
LONG last_physical_min = 0;// If both, Physical Minimum and Physical Maximum are 0, the logical limits should be taken as physical limits according USB HID spec 1.11 chapter 6.2.2.7
|
||||
LONG last_physical_max = 0;
|
||||
ULONG last_unit_exponent = 0; // If Unit Exponent is Undefined it should be considered as 0 according USB HID spec 1.11 chapter 6.2.2.7
|
||||
ULONG last_unit = 0; // If the first nibble is 7, or second nibble of Unit is 0, the unit is None according USB HID spec 1.11 chapter 6.2.2.7
|
||||
BOOLEAN inhibit_write_of_usage = FALSE; // Needed in case of delimited usage print, before the normal collection or cap
|
||||
int report_count = 0;
|
||||
while (main_item_list != NULL)
|
||||
{
|
||||
int rt_idx = main_item_list->MainItemType;
|
||||
int caps_idx = main_item_list->CapsIndex;
|
||||
if (main_item_list->MainItemType == rd_collection) {
|
||||
if (last_usage_page != link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage) {
|
||||
// Write "Usage Page" at the begin of a collection - except it refers the same table as wrote last
|
||||
rd_write_short_item(rd_global_usage_page, link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage, &rpt_desc);
|
||||
last_usage_page = link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage;
|
||||
}
|
||||
if (inhibit_write_of_usage) {
|
||||
// Inhibit only once after DELIMITER statement
|
||||
inhibit_write_of_usage = FALSE;
|
||||
}
|
||||
else {
|
||||
// Write "Usage" of collection
|
||||
rd_write_short_item(rd_local_usage, link_collection_nodes[main_item_list->CollectionIndex].LinkUsage, &rpt_desc);
|
||||
}
|
||||
// Write begin of "Collection"
|
||||
rd_write_short_item(rd_main_collection, link_collection_nodes[main_item_list->CollectionIndex].CollectionType, &rpt_desc);
|
||||
}
|
||||
else if (main_item_list->MainItemType == rd_collection_end) {
|
||||
// Write "End Collection"
|
||||
rd_write_short_item(rd_main_collection_end, 0, &rpt_desc);
|
||||
}
|
||||
else if (main_item_list->MainItemType == rd_delimiter_open) {
|
||||
if (main_item_list->CollectionIndex != -1) {
|
||||
// Write "Usage Page" inside of a collection delmiter section
|
||||
if (last_usage_page != link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage) {
|
||||
rd_write_short_item(rd_global_usage_page, link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage, &rpt_desc);
|
||||
last_usage_page = link_collection_nodes[main_item_list->CollectionIndex].LinkUsagePage;
|
||||
}
|
||||
}
|
||||
else if (main_item_list->CapsIndex != 0) {
|
||||
// Write "Usage Page" inside of a main item delmiter section
|
||||
if (pp_data->caps[caps_idx].UsagePage != last_usage_page) {
|
||||
rd_write_short_item(rd_global_usage_page, pp_data->caps[caps_idx].UsagePage, &rpt_desc);
|
||||
last_usage_page = pp_data->caps[caps_idx].UsagePage;
|
||||
}
|
||||
}
|
||||
// Write "Delimiter Open"
|
||||
rd_write_short_item(rd_local_delimiter, 1, &rpt_desc); // 1 = open set of aliased usages
|
||||
}
|
||||
else if (main_item_list->MainItemType == rd_delimiter_usage) {
|
||||
if (main_item_list->CollectionIndex != -1) {
|
||||
// Write aliased collection "Usage"
|
||||
rd_write_short_item(rd_local_usage, link_collection_nodes[main_item_list->CollectionIndex].LinkUsage, &rpt_desc);
|
||||
} if (main_item_list->CapsIndex != 0) {
|
||||
// Write aliased main item range from "Usage Minimum" to "Usage Maximum"
|
||||
if (pp_data->caps[caps_idx].IsRange) {
|
||||
rd_write_short_item(rd_local_usage_minimum, pp_data->caps[caps_idx].Range.UsageMin, &rpt_desc);
|
||||
rd_write_short_item(rd_local_usage_maximum, pp_data->caps[caps_idx].Range.UsageMax, &rpt_desc);
|
||||
}
|
||||
else {
|
||||
// Write single aliased main item "Usage"
|
||||
rd_write_short_item(rd_local_usage, pp_data->caps[caps_idx].NotRange.Usage, &rpt_desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (main_item_list->MainItemType == rd_delimiter_close) {
|
||||
// Write "Delimiter Close"
|
||||
rd_write_short_item(rd_local_delimiter, 0, &rpt_desc); // 0 = close set of aliased usages
|
||||
// Inhibit next usage write
|
||||
inhibit_write_of_usage = TRUE;
|
||||
}
|
||||
else if (main_item_list->TypeOfNode == rd_item_node_padding) {
|
||||
// Padding
|
||||
// The preparsed data doesn't contain any information about padding. Therefore all undefined gaps
|
||||
// in the reports are filled with the same style of constant padding.
|
||||
|
||||
// Write "Report Size" with number of padding bits
|
||||
rd_write_short_item(rd_global_report_size, (main_item_list->LastBit - main_item_list->FirstBit + 1), &rpt_desc);
|
||||
|
||||
// Write "Report Count" for padding always as 1
|
||||
rd_write_short_item(rd_global_report_count, 1, &rpt_desc);
|
||||
|
||||
if (rt_idx == HidP_Input) {
|
||||
// Write "Input" main item - We know it's Constant - We can only guess the other bits, but they don't matter in case of const
|
||||
rd_write_short_item(rd_main_input, 0x03, &rpt_desc); // Const / Abs
|
||||
}
|
||||
else if (rt_idx == HidP_Output) {
|
||||
// Write "Output" main item - We know it's Constant - We can only guess the other bits, but they don't matter in case of const
|
||||
rd_write_short_item(rd_main_output, 0x03, &rpt_desc); // Const / Abs
|
||||
}
|
||||
else if (rt_idx == HidP_Feature) {
|
||||
// Write "Feature" main item - We know it's Constant - We can only guess the other bits, but they don't matter in case of const
|
||||
rd_write_short_item(rd_main_feature, 0x03, &rpt_desc); // Const / Abs
|
||||
}
|
||||
report_count = 0;
|
||||
}
|
||||
else if (pp_data->caps[caps_idx].IsButtonCap) {
|
||||
// Button
|
||||
// (The preparsed data contain different data for 1 bit Button caps, than for parametric Value caps)
|
||||
|
||||
if (last_report_id != pp_data->caps[caps_idx].ReportID) {
|
||||
// Write "Report ID" if changed
|
||||
rd_write_short_item(rd_global_report_id, pp_data->caps[caps_idx].ReportID, &rpt_desc);
|
||||
last_report_id = pp_data->caps[caps_idx].ReportID;
|
||||
}
|
||||
|
||||
// Write "Usage Page" when changed
|
||||
if (pp_data->caps[caps_idx].UsagePage != last_usage_page) {
|
||||
rd_write_short_item(rd_global_usage_page, pp_data->caps[caps_idx].UsagePage, &rpt_desc);
|
||||
last_usage_page = pp_data->caps[caps_idx].UsagePage;
|
||||
}
|
||||
|
||||
// Write only local report items for each cap, if ReportCount > 1
|
||||
if (pp_data->caps[caps_idx].IsRange) {
|
||||
report_count += (pp_data->caps[caps_idx].Range.DataIndexMax - pp_data->caps[caps_idx].Range.DataIndexMin);
|
||||
}
|
||||
|
||||
if (inhibit_write_of_usage) {
|
||||
// Inhibit only once after Delimiter - Reset flag
|
||||
inhibit_write_of_usage = FALSE;
|
||||
}
|
||||
else {
|
||||
if (pp_data->caps[caps_idx].IsRange) {
|
||||
// Write range from "Usage Minimum" to "Usage Maximum"
|
||||
rd_write_short_item(rd_local_usage_minimum, pp_data->caps[caps_idx].Range.UsageMin, &rpt_desc);
|
||||
rd_write_short_item(rd_local_usage_maximum, pp_data->caps[caps_idx].Range.UsageMax, &rpt_desc);
|
||||
}
|
||||
else {
|
||||
// Write single "Usage"
|
||||
rd_write_short_item(rd_local_usage, pp_data->caps[caps_idx].NotRange.Usage, &rpt_desc);
|
||||
}
|
||||
}
|
||||
|
||||
if (pp_data->caps[caps_idx].IsDesignatorRange) {
|
||||
// Write physical descriptor indices range from "Designator Minimum" to "Designator Maximum"
|
||||
rd_write_short_item(rd_local_designator_minimum, pp_data->caps[caps_idx].Range.DesignatorMin, &rpt_desc);
|
||||
rd_write_short_item(rd_local_designator_maximum, pp_data->caps[caps_idx].Range.DesignatorMax, &rpt_desc);
|
||||
}
|
||||
else if (pp_data->caps[caps_idx].NotRange.DesignatorIndex != 0) {
|
||||
// Designator set 0 is a special descriptor set (of the HID Physical Descriptor),
|
||||
// that specifies the number of additional descriptor sets.
|
||||
// Therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.
|
||||
// Write single "Designator Index"
|
||||
rd_write_short_item(rd_local_designator_index, pp_data->caps[caps_idx].NotRange.DesignatorIndex, &rpt_desc);
|
||||
}
|
||||
|
||||
if (pp_data->caps[caps_idx].IsStringRange) {
|
||||
// Write range of indices of the USB string descriptor, from "String Minimum" to "String Maximum"
|
||||
rd_write_short_item(rd_local_string_minimum, pp_data->caps[caps_idx].Range.StringMin, &rpt_desc);
|
||||
rd_write_short_item(rd_local_string_maximum, pp_data->caps[caps_idx].Range.StringMax, &rpt_desc);
|
||||
}
|
||||
else if (pp_data->caps[caps_idx].NotRange.StringIndex != 0) {
|
||||
// String Index 0 is a special entry of the USB string descriptor, that contains a list of supported languages,
|
||||
// therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.
|
||||
// Write single "String Index"
|
||||
rd_write_short_item(rd_local_string, pp_data->caps[caps_idx].NotRange.StringIndex, &rpt_desc);
|
||||
}
|
||||
|
||||
if ((main_item_list->next != NULL) &&
|
||||
((int)main_item_list->next->MainItemType == rt_idx) &&
|
||||
(main_item_list->next->TypeOfNode == rd_item_node_cap) &&
|
||||
(pp_data->caps[main_item_list->next->CapsIndex].IsButtonCap) &&
|
||||
(!pp_data->caps[caps_idx].IsRange) && // This node in list is no array
|
||||
(!pp_data->caps[main_item_list->next->CapsIndex].IsRange) && // Next node in list is no array
|
||||
(pp_data->caps[main_item_list->next->CapsIndex].UsagePage == pp_data->caps[caps_idx].UsagePage) &&
|
||||
(pp_data->caps[main_item_list->next->CapsIndex].ReportID == pp_data->caps[caps_idx].ReportID) &&
|
||||
(pp_data->caps[main_item_list->next->CapsIndex].BitField == pp_data->caps[caps_idx].BitField)
|
||||
) {
|
||||
if (main_item_list->next->FirstBit != main_item_list->FirstBit) {
|
||||
// In case of IsMultipleItemsForArray for multiple dedicated usages for a multi-button array, the report count should be incremented
|
||||
|
||||
// Skip global items until any of them changes, than use ReportCount item to write the count of identical report fields
|
||||
report_count++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
if ((pp_data->caps[caps_idx].Button.LogicalMin == 0) &&
|
||||
(pp_data->caps[caps_idx].Button.LogicalMax == 0)) {
|
||||
// While a HID report descriptor must always contain LogicalMinimum and LogicalMaximum,
|
||||
// the preparsed data contain both fields set to zero, for the case of simple buttons
|
||||
// Write "Logical Minimum" set to 0 and "Logical Maximum" set to 1
|
||||
rd_write_short_item(rd_global_logical_minimum, 0, &rpt_desc);
|
||||
rd_write_short_item(rd_global_logical_maximum, 1, &rpt_desc);
|
||||
}
|
||||
else {
|
||||
// Write logical range from "Logical Minimum" to "Logical Maximum"
|
||||
rd_write_short_item(rd_global_logical_minimum, pp_data->caps[caps_idx].Button.LogicalMin, &rpt_desc);
|
||||
rd_write_short_item(rd_global_logical_maximum, pp_data->caps[caps_idx].Button.LogicalMax, &rpt_desc);
|
||||
}
|
||||
|
||||
// Write "Report Size"
|
||||
rd_write_short_item(rd_global_report_size, pp_data->caps[caps_idx].ReportSize, &rpt_desc);
|
||||
|
||||
// Write "Report Count"
|
||||
if (!pp_data->caps[caps_idx].IsRange) {
|
||||
// Variable bit field with one bit per button
|
||||
// In case of multiple usages with the same items, only "Usage" is written per cap, and "Report Count" is incremented
|
||||
rd_write_short_item(rd_global_report_count, pp_data->caps[caps_idx].ReportCount + report_count, &rpt_desc);
|
||||
}
|
||||
else {
|
||||
// Button array of "Report Size" x "Report Count
|
||||
rd_write_short_item(rd_global_report_count, pp_data->caps[caps_idx].ReportCount, &rpt_desc);
|
||||
}
|
||||
|
||||
|
||||
// Buttons have only 1 bit and therefore no physical limits/units -> Set to undefined state
|
||||
if (last_physical_min != 0) {
|
||||
// Write "Physical Minimum", but only if changed
|
||||
last_physical_min = 0;
|
||||
rd_write_short_item(rd_global_physical_minimum, last_physical_min, &rpt_desc);
|
||||
}
|
||||
if (last_physical_max != 0) {
|
||||
// Write "Physical Maximum", but only if changed
|
||||
last_physical_max = 0;
|
||||
rd_write_short_item(rd_global_physical_maximum, last_physical_max, &rpt_desc);
|
||||
}
|
||||
if (last_unit_exponent != 0) {
|
||||
// Write "Unit Exponent", but only if changed
|
||||
last_unit_exponent = 0;
|
||||
rd_write_short_item(rd_global_unit_exponent, last_unit_exponent, &rpt_desc);
|
||||
}
|
||||
if (last_unit != 0) {
|
||||
// Write "Unit",but only if changed
|
||||
last_unit = 0;
|
||||
rd_write_short_item(rd_global_unit, last_unit, &rpt_desc);
|
||||
}
|
||||
|
||||
// Write "Input" main item
|
||||
if (rt_idx == HidP_Input) {
|
||||
rd_write_short_item(rd_main_input, pp_data->caps[caps_idx].BitField, &rpt_desc);
|
||||
}
|
||||
// Write "Output" main item
|
||||
else if (rt_idx == HidP_Output) {
|
||||
rd_write_short_item(rd_main_output, pp_data->caps[caps_idx].BitField, &rpt_desc);
|
||||
}
|
||||
// Write "Feature" main item
|
||||
else if (rt_idx == HidP_Feature) {
|
||||
rd_write_short_item(rd_main_feature, pp_data->caps[caps_idx].BitField, &rpt_desc);
|
||||
}
|
||||
report_count = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
if (last_report_id != pp_data->caps[caps_idx].ReportID) {
|
||||
// Write "Report ID" if changed
|
||||
rd_write_short_item(rd_global_report_id, pp_data->caps[caps_idx].ReportID, &rpt_desc);
|
||||
last_report_id = pp_data->caps[caps_idx].ReportID;
|
||||
}
|
||||
|
||||
// Write "Usage Page" if changed
|
||||
if (pp_data->caps[caps_idx].UsagePage != last_usage_page) {
|
||||
rd_write_short_item(rd_global_usage_page, pp_data->caps[caps_idx].UsagePage, &rpt_desc);
|
||||
last_usage_page = pp_data->caps[caps_idx].UsagePage;
|
||||
}
|
||||
|
||||
if (inhibit_write_of_usage) {
|
||||
// Inhibit only once after Delimiter - Reset flag
|
||||
inhibit_write_of_usage = FALSE;
|
||||
}
|
||||
else {
|
||||
if (pp_data->caps[caps_idx].IsRange) {
|
||||
// Write usage range from "Usage Minimum" to "Usage Maximum"
|
||||
rd_write_short_item(rd_local_usage_minimum, pp_data->caps[caps_idx].Range.UsageMin, &rpt_desc);
|
||||
rd_write_short_item(rd_local_usage_maximum, pp_data->caps[caps_idx].Range.UsageMax, &rpt_desc);
|
||||
}
|
||||
else {
|
||||
// Write single "Usage"
|
||||
rd_write_short_item(rd_local_usage, pp_data->caps[caps_idx].NotRange.Usage, &rpt_desc);
|
||||
}
|
||||
}
|
||||
|
||||
if (pp_data->caps[caps_idx].IsDesignatorRange) {
|
||||
// Write physical descriptor indices range from "Designator Minimum" to "Designator Maximum"
|
||||
rd_write_short_item(rd_local_designator_minimum, pp_data->caps[caps_idx].Range.DesignatorMin, &rpt_desc);
|
||||
rd_write_short_item(rd_local_designator_maximum, pp_data->caps[caps_idx].Range.DesignatorMax, &rpt_desc);
|
||||
}
|
||||
else if (pp_data->caps[caps_idx].NotRange.DesignatorIndex != 0) {
|
||||
// Designator set 0 is a special descriptor set (of the HID Physical Descriptor),
|
||||
// that specifies the number of additional descriptor sets.
|
||||
// Therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.
|
||||
// Write single "Designator Index"
|
||||
rd_write_short_item(rd_local_designator_index, pp_data->caps[caps_idx].NotRange.DesignatorIndex, &rpt_desc);
|
||||
}
|
||||
|
||||
if (pp_data->caps[caps_idx].IsStringRange) {
|
||||
// Write range of indices of the USB string descriptor, from "String Minimum" to "String Maximum"
|
||||
rd_write_short_item(rd_local_string_minimum, pp_data->caps[caps_idx].Range.StringMin, &rpt_desc);
|
||||
rd_write_short_item(rd_local_string_maximum, pp_data->caps[caps_idx].Range.StringMax, &rpt_desc);
|
||||
}
|
||||
else if (pp_data->caps[caps_idx].NotRange.StringIndex != 0) {
|
||||
// String Index 0 is a special entry of the USB string descriptor, that contains a list of supported languages,
|
||||
// therefore Designator Index 0 can never be a useful reference for a control and we can inhibit it.
|
||||
// Write single "String Index"
|
||||
rd_write_short_item(rd_local_string, pp_data->caps[caps_idx].NotRange.StringIndex, &rpt_desc);
|
||||
}
|
||||
|
||||
if ((pp_data->caps[caps_idx].BitField & 0x02) != 0x02) {
|
||||
// In case of an value array overwrite "Report Count"
|
||||
pp_data->caps[caps_idx].ReportCount = pp_data->caps[caps_idx].Range.DataIndexMax - pp_data->caps[caps_idx].Range.DataIndexMin + 1;
|
||||
}
|
||||
|
||||
|
||||
// Print only local report items for each cap, if ReportCount > 1
|
||||
if ((main_item_list->next != NULL) &&
|
||||
((int) main_item_list->next->MainItemType == rt_idx) &&
|
||||
(main_item_list->next->TypeOfNode == rd_item_node_cap) &&
|
||||
(!pp_data->caps[main_item_list->next->CapsIndex].IsButtonCap) &&
|
||||
(!pp_data->caps[caps_idx].IsRange) && // This node in list is no array
|
||||
(!pp_data->caps[main_item_list->next->CapsIndex].IsRange) && // Next node in list is no array
|
||||
(pp_data->caps[main_item_list->next->CapsIndex].UsagePage == pp_data->caps[caps_idx].UsagePage) &&
|
||||
(pp_data->caps[main_item_list->next->CapsIndex].NotButton.LogicalMin == pp_data->caps[caps_idx].NotButton.LogicalMin) &&
|
||||
(pp_data->caps[main_item_list->next->CapsIndex].NotButton.LogicalMax == pp_data->caps[caps_idx].NotButton.LogicalMax) &&
|
||||
(pp_data->caps[main_item_list->next->CapsIndex].NotButton.PhysicalMin == pp_data->caps[caps_idx].NotButton.PhysicalMin) &&
|
||||
(pp_data->caps[main_item_list->next->CapsIndex].NotButton.PhysicalMax == pp_data->caps[caps_idx].NotButton.PhysicalMax) &&
|
||||
(pp_data->caps[main_item_list->next->CapsIndex].UnitsExp == pp_data->caps[caps_idx].UnitsExp) &&
|
||||
(pp_data->caps[main_item_list->next->CapsIndex].Units == pp_data->caps[caps_idx].Units) &&
|
||||
(pp_data->caps[main_item_list->next->CapsIndex].ReportSize == pp_data->caps[caps_idx].ReportSize) &&
|
||||
(pp_data->caps[main_item_list->next->CapsIndex].ReportID == pp_data->caps[caps_idx].ReportID) &&
|
||||
(pp_data->caps[main_item_list->next->CapsIndex].BitField == pp_data->caps[caps_idx].BitField) &&
|
||||
(pp_data->caps[main_item_list->next->CapsIndex].ReportCount == 1) &&
|
||||
(pp_data->caps[caps_idx].ReportCount == 1)
|
||||
) {
|
||||
// Skip global items until any of them changes, than use ReportCount item to write the count of identical report fields
|
||||
report_count++;
|
||||
}
|
||||
else {
|
||||
// Value
|
||||
|
||||
// Write logical range from "Logical Minimum" to "Logical Maximum"
|
||||
rd_write_short_item(rd_global_logical_minimum, pp_data->caps[caps_idx].NotButton.LogicalMin, &rpt_desc);
|
||||
rd_write_short_item(rd_global_logical_maximum, pp_data->caps[caps_idx].NotButton.LogicalMax, &rpt_desc);
|
||||
|
||||
if ((last_physical_min != pp_data->caps[caps_idx].NotButton.PhysicalMin) ||
|
||||
(last_physical_max != pp_data->caps[caps_idx].NotButton.PhysicalMax)) {
|
||||
// Write range from "Physical Minimum" to " Physical Maximum", but only if one of them changed
|
||||
rd_write_short_item(rd_global_physical_minimum, pp_data->caps[caps_idx].NotButton.PhysicalMin, &rpt_desc);
|
||||
last_physical_min = pp_data->caps[caps_idx].NotButton.PhysicalMin;
|
||||
rd_write_short_item(rd_global_physical_maximum, pp_data->caps[caps_idx].NotButton.PhysicalMax, &rpt_desc);
|
||||
last_physical_max = pp_data->caps[caps_idx].NotButton.PhysicalMax;
|
||||
}
|
||||
|
||||
|
||||
if (last_unit_exponent != pp_data->caps[caps_idx].UnitsExp) {
|
||||
// Write "Unit Exponent", but only if changed
|
||||
rd_write_short_item(rd_global_unit_exponent, pp_data->caps[caps_idx].UnitsExp, &rpt_desc);
|
||||
last_unit_exponent = pp_data->caps[caps_idx].UnitsExp;
|
||||
}
|
||||
|
||||
if (last_unit != pp_data->caps[caps_idx].Units) {
|
||||
// Write physical "Unit", but only if changed
|
||||
rd_write_short_item(rd_global_unit, pp_data->caps[caps_idx].Units, &rpt_desc);
|
||||
last_unit = pp_data->caps[caps_idx].Units;
|
||||
}
|
||||
|
||||
// Write "Report Size"
|
||||
rd_write_short_item(rd_global_report_size, pp_data->caps[caps_idx].ReportSize, &rpt_desc);
|
||||
|
||||
// Write "Report Count"
|
||||
rd_write_short_item(rd_global_report_count, pp_data->caps[caps_idx].ReportCount + report_count, &rpt_desc);
|
||||
|
||||
if (rt_idx == HidP_Input) {
|
||||
// Write "Input" main item
|
||||
rd_write_short_item(rd_main_input, pp_data->caps[caps_idx].BitField, &rpt_desc);
|
||||
}
|
||||
else if (rt_idx == HidP_Output) {
|
||||
// Write "Output" main item
|
||||
rd_write_short_item(rd_main_output, pp_data->caps[caps_idx].BitField, &rpt_desc);
|
||||
}
|
||||
else if (rt_idx == HidP_Feature) {
|
||||
// Write "Feature" main item
|
||||
rd_write_short_item(rd_main_feature, pp_data->caps[caps_idx].BitField, &rpt_desc);
|
||||
}
|
||||
report_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Go to next item in main_item_list and free the memory of the actual item
|
||||
struct rd_main_item_node *main_item_list_prev = main_item_list;
|
||||
main_item_list = main_item_list->next;
|
||||
free(main_item_list_prev);
|
||||
}
|
||||
|
||||
// Free multidimensionable array: coll_bit_range[COLLECTION_INDEX][REPORT_ID][INPUT/OUTPUT/FEATURE]
|
||||
// Free multidimensionable array: coll_child_order[COLLECTION_INDEX][DIRECT_CHILD_INDEX]
|
||||
for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) {
|
||||
for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
|
||||
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
|
||||
free(coll_bit_range[collection_node_idx][reportid_idx][rt_idx]);
|
||||
}
|
||||
free(coll_bit_range[collection_node_idx][reportid_idx]);
|
||||
}
|
||||
free(coll_bit_range[collection_node_idx]);
|
||||
if (coll_number_of_direct_childs[collection_node_idx] != 0) free(coll_child_order[collection_node_idx]);
|
||||
}
|
||||
free(coll_bit_range);
|
||||
free(coll_child_order);
|
||||
|
||||
// Free one dimensional arrays
|
||||
free(coll_begin_lookup);
|
||||
free(coll_end_lookup);
|
||||
free(coll_levels);
|
||||
free(coll_number_of_direct_childs);
|
||||
|
||||
return (int) rpt_desc.byte_idx;
|
||||
}
|
||||
254
thirdparty/sdl/hidapi/windows/hidapi_descriptor_reconstruct.h
vendored
Normal file
254
thirdparty/sdl/hidapi/windows/hidapi_descriptor_reconstruct.h
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
/*******************************************************
|
||||
HIDAPI - Multi-Platform library for
|
||||
communication with HID devices.
|
||||
|
||||
libusb/hidapi Team
|
||||
|
||||
Copyright 2022, All Rights Reserved.
|
||||
|
||||
At the discretion of the user of this library,
|
||||
this software may be licensed under the terms of the
|
||||
GNU General Public License v3, a BSD-Style license, or the
|
||||
original HIDAPI license as outlined in the LICENSE.txt,
|
||||
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
|
||||
files located at the root of the source distribution.
|
||||
These files may also be found in the public source
|
||||
code repository located at:
|
||||
https://github.com/libusb/hidapi .
|
||||
********************************************************/
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef HIDAPI_DESCRIPTOR_RECONSTRUCT_H__
|
||||
#define HIDAPI_DESCRIPTOR_RECONSTRUCT_H__
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
/* Do not warn about wcsncpy usage.
|
||||
https://docs.microsoft.com/cpp/c-runtime-library/security-features-in-the-crt */
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "hidapi_winapi.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4200)
|
||||
#pragma warning(disable: 4201)
|
||||
#pragma warning(disable: 4214)
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "hidapi_hidsdi.h"
|
||||
/*#include <assert.h>*/
|
||||
|
||||
#define NUM_OF_HIDP_REPORT_TYPES 3
|
||||
|
||||
typedef enum rd_items_ {
|
||||
rd_main_input = 0x80, /* 1000 00 nn */
|
||||
rd_main_output = 0x90, /* 1001 00 nn */
|
||||
rd_main_feature = 0xB0, /* 1011 00 nn */
|
||||
rd_main_collection = 0xA0, /* 1010 00 nn */
|
||||
rd_main_collection_end = 0xC0, /* 1100 00 nn */
|
||||
rd_global_usage_page = 0x04, /* 0000 01 nn */
|
||||
rd_global_logical_minimum = 0x14, /* 0001 01 nn */
|
||||
rd_global_logical_maximum = 0x24, /* 0010 01 nn */
|
||||
rd_global_physical_minimum = 0x34, /* 0011 01 nn */
|
||||
rd_global_physical_maximum = 0x44, /* 0100 01 nn */
|
||||
rd_global_unit_exponent = 0x54, /* 0101 01 nn */
|
||||
rd_global_unit = 0x64, /* 0110 01 nn */
|
||||
rd_global_report_size = 0x74, /* 0111 01 nn */
|
||||
rd_global_report_id = 0x84, /* 1000 01 nn */
|
||||
rd_global_report_count = 0x94, /* 1001 01 nn */
|
||||
rd_global_push = 0xA4, /* 1010 01 nn */
|
||||
rd_global_pop = 0xB4, /* 1011 01 nn */
|
||||
rd_local_usage = 0x08, /* 0000 10 nn */
|
||||
rd_local_usage_minimum = 0x18, /* 0001 10 nn */
|
||||
rd_local_usage_maximum = 0x28, /* 0010 10 nn */
|
||||
rd_local_designator_index = 0x38, /* 0011 10 nn */
|
||||
rd_local_designator_minimum = 0x48, /* 0100 10 nn */
|
||||
rd_local_designator_maximum = 0x58, /* 0101 10 nn */
|
||||
rd_local_string = 0x78, /* 0111 10 nn */
|
||||
rd_local_string_minimum = 0x88, /* 1000 10 nn */
|
||||
rd_local_string_maximum = 0x98, /* 1001 10 nn */
|
||||
rd_local_delimiter = 0xA8 /* 1010 10 nn */
|
||||
} rd_items;
|
||||
|
||||
typedef enum rd_main_items_ {
|
||||
rd_input = HidP_Input,
|
||||
rd_output = HidP_Output,
|
||||
rd_feature = HidP_Feature,
|
||||
rd_collection,
|
||||
rd_collection_end,
|
||||
rd_delimiter_open,
|
||||
rd_delimiter_usage,
|
||||
rd_delimiter_close,
|
||||
} rd_main_items;
|
||||
|
||||
typedef struct rd_bit_range_ {
|
||||
int FirstBit;
|
||||
int LastBit;
|
||||
} rd_bit_range;
|
||||
|
||||
typedef enum rd_item_node_type_ {
|
||||
rd_item_node_cap,
|
||||
rd_item_node_padding,
|
||||
rd_item_node_collection,
|
||||
} rd_node_type;
|
||||
|
||||
struct rd_main_item_node {
|
||||
int FirstBit; /* Position of first bit in report (counting from 0) */
|
||||
int LastBit; /* Position of last bit in report (counting from 0) */
|
||||
rd_node_type TypeOfNode; /* Information if caps index refers to the array of button caps, value caps,
|
||||
or if the node is just a padding element to fill unused bit positions.
|
||||
The node can also be a collection node without any bits in the report. */
|
||||
int CapsIndex; /* Index in the array of caps */
|
||||
int CollectionIndex; /* Index in the array of link collections */
|
||||
rd_main_items MainItemType; /* Input, Output, Feature, Collection or Collection End */
|
||||
unsigned char ReportID;
|
||||
struct rd_main_item_node* next;
|
||||
};
|
||||
|
||||
typedef struct hid_pp_caps_info_ {
|
||||
USHORT FirstCap;
|
||||
USHORT NumberOfCaps; // Includes empty caps after LastCap
|
||||
USHORT LastCap;
|
||||
USHORT ReportByteLength;
|
||||
} hid_pp_caps_info, *phid_pp_caps_info;
|
||||
|
||||
typedef struct hid_pp_link_collection_node_ {
|
||||
USAGE LinkUsage;
|
||||
USAGE LinkUsagePage;
|
||||
USHORT Parent;
|
||||
USHORT NumberOfChildren;
|
||||
USHORT NextSibling;
|
||||
USHORT FirstChild;
|
||||
ULONG CollectionType : 8;
|
||||
ULONG IsAlias : 1;
|
||||
ULONG Reserved : 23;
|
||||
// Same as the public API structure HIDP_LINK_COLLECTION_NODE, but without PVOID UserContext at the end
|
||||
} hid_pp_link_collection_node, *phid_pp_link_collection_node;
|
||||
|
||||
// Note: This is risk-reduction-measure for this specific struct, as it has ULONG bit-field.
|
||||
// Although very unlikely, it might still be possible that the compiler creates a memory layout that is
|
||||
// not binary compatile.
|
||||
// Other structs are not checked at the time of writing.
|
||||
//static_assert(sizeof(struct hid_pp_link_collection_node_) == 16,
|
||||
// "Size of struct hid_pp_link_collection_node_ not as expected. This might break binary compatibility");
|
||||
SDL_COMPILE_TIME_ASSERT(hid_pp_link_collection_node_, sizeof(struct hid_pp_link_collection_node_) == 16);
|
||||
|
||||
typedef struct hidp_unknown_token_ {
|
||||
UCHAR Token; /* Specifies the one-byte prefix of a global item. */
|
||||
UCHAR Reserved[3];
|
||||
ULONG BitField; /* Specifies the data part of the global item. */
|
||||
} hidp_unknown_token, * phidp_unknown_token;
|
||||
|
||||
typedef struct hid_pp_cap_ {
|
||||
USAGE UsagePage;
|
||||
UCHAR ReportID;
|
||||
UCHAR BitPosition;
|
||||
USHORT ReportSize; // WIN32 term for this is BitSize
|
||||
USHORT ReportCount;
|
||||
USHORT BytePosition;
|
||||
USHORT BitCount;
|
||||
ULONG BitField;
|
||||
USHORT NextBytePosition;
|
||||
USHORT LinkCollection;
|
||||
USAGE LinkUsagePage;
|
||||
USAGE LinkUsage;
|
||||
|
||||
// Start of 8 Flags in one byte
|
||||
BOOLEAN IsMultipleItemsForArray:1;
|
||||
|
||||
BOOLEAN IsPadding:1;
|
||||
BOOLEAN IsButtonCap:1;
|
||||
BOOLEAN IsAbsolute:1;
|
||||
BOOLEAN IsRange:1;
|
||||
BOOLEAN IsAlias:1; // IsAlias is set to TRUE in the first n-1 capability structures added to the capability array. IsAlias set to FALSE in the nth capability structure.
|
||||
BOOLEAN IsStringRange:1;
|
||||
BOOLEAN IsDesignatorRange:1;
|
||||
// End of 8 Flags in one byte
|
||||
BOOLEAN Reserved1[3];
|
||||
|
||||
hidp_unknown_token UnknownTokens[4]; // 4 x 8 Byte
|
||||
|
||||
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;
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
LONG LogicalMin;
|
||||
LONG LogicalMax;
|
||||
} Button;
|
||||
struct {
|
||||
BOOLEAN HasNull;
|
||||
UCHAR Reserved4[3];
|
||||
LONG LogicalMin;
|
||||
LONG LogicalMax;
|
||||
LONG PhysicalMin;
|
||||
LONG PhysicalMax;
|
||||
} NotButton;
|
||||
};
|
||||
ULONG Units;
|
||||
ULONG UnitsExp;
|
||||
|
||||
} hid_pp_cap, *phid_pp_cap;
|
||||
|
||||
typedef struct hidp_preparsed_data_ {
|
||||
UCHAR MagicKey[8];
|
||||
USAGE Usage;
|
||||
USAGE UsagePage;
|
||||
USHORT Reserved[2];
|
||||
|
||||
// CAPS structure for Input, Output and Feature
|
||||
hid_pp_caps_info caps_info[3];
|
||||
|
||||
USHORT FirstByteOfLinkCollectionArray;
|
||||
USHORT NumberLinkCollectionNodes;
|
||||
|
||||
#ifndef _MSC_VER
|
||||
// MINGW fails with: Flexible array member in union not supported
|
||||
// Solution: https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
|
||||
union {
|
||||
#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||
#endif
|
||||
hid_pp_cap caps[0];
|
||||
hid_pp_link_collection_node LinkCollectionArray[0];
|
||||
#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
};
|
||||
#else
|
||||
union {
|
||||
hid_pp_cap caps[];
|
||||
hid_pp_link_collection_node LinkCollectionArray[];
|
||||
};
|
||||
#endif
|
||||
|
||||
} hidp_preparsed_data;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
40
thirdparty/sdl/hidapi/windows/hidapi_hidclass.h
vendored
Normal file
40
thirdparty/sdl/hidapi/windows/hidapi_hidclass.h
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/*******************************************************
|
||||
HIDAPI - Multi-Platform library for
|
||||
communication with HID devices.
|
||||
|
||||
libusb/hidapi Team
|
||||
|
||||
Copyright 2022, All Rights Reserved.
|
||||
|
||||
At the discretion of the user of this library,
|
||||
this software may be licensed under the terms of the
|
||||
GNU General Public License v3, a BSD-Style license, or the
|
||||
original HIDAPI license as outlined in the LICENSE.txt,
|
||||
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
|
||||
files located at the root of the source distribution.
|
||||
These files may also be found in the public source
|
||||
code repository located at:
|
||||
https://github.com/libusb/hidapi .
|
||||
********************************************************/
|
||||
|
||||
#ifndef HIDAPI_HIDCLASS_H
|
||||
#define HIDAPI_HIDCLASS_H
|
||||
|
||||
#ifdef HIDAPI_USE_DDK
|
||||
|
||||
#include <hidclass.h>
|
||||
|
||||
#else
|
||||
|
||||
#include <winioctl.h>
|
||||
|
||||
/* This part of the header mimics hidclass.h,
|
||||
but only what is used by HIDAPI */
|
||||
|
||||
#define HID_OUT_CTL_CODE(id) CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
|
||||
#define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100)
|
||||
#define IOCTL_HID_GET_INPUT_REPORT HID_OUT_CTL_CODE(104)
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* HIDAPI_HIDCLASS_H */
|
||||
72
thirdparty/sdl/hidapi/windows/hidapi_hidpi.h
vendored
Normal file
72
thirdparty/sdl/hidapi/windows/hidapi_hidpi.h
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
/*******************************************************
|
||||
HIDAPI - Multi-Platform library for
|
||||
communication with HID devices.
|
||||
|
||||
libusb/hidapi Team
|
||||
|
||||
Copyright 2022, All Rights Reserved.
|
||||
|
||||
At the discretion of the user of this library,
|
||||
this software may be licensed under the terms of the
|
||||
GNU General Public License v3, a BSD-Style license, or the
|
||||
original HIDAPI license as outlined in the LICENSE.txt,
|
||||
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
|
||||
files located at the root of the source distribution.
|
||||
These files may also be found in the public source
|
||||
code repository located at:
|
||||
https://github.com/libusb/hidapi .
|
||||
********************************************************/
|
||||
|
||||
#ifndef HIDAPI_HIDPI_H
|
||||
#define HIDAPI_HIDPI_H
|
||||
|
||||
#ifdef HIDAPI_USE_DDK
|
||||
|
||||
#include <hidpi.h>
|
||||
|
||||
#else
|
||||
|
||||
/* This part of the header mimics hidpi.h,
|
||||
but only what is used by HIDAPI */
|
||||
|
||||
typedef enum _HIDP_REPORT_TYPE
|
||||
{
|
||||
HidP_Input,
|
||||
HidP_Output,
|
||||
HidP_Feature
|
||||
} HIDP_REPORT_TYPE;
|
||||
|
||||
typedef struct _HIDP_PREPARSED_DATA * PHIDP_PREPARSED_DATA;
|
||||
|
||||
typedef struct _HIDP_CAPS
|
||||
{
|
||||
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;
|
||||
|
||||
#define HIDP_STATUS_SUCCESS 0x00110000
|
||||
#define HIDP_STATUS_INVALID_PREPARSED_DATA 0xc0110001
|
||||
|
||||
typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, PHIDP_CAPS caps);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* HIDAPI_HIDPI_H */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user