Merge commit 'dec0d4ec4153bf9fc2b78ae6c2df45b6ea8dde7a' as 'external/sdl/SDL'
This commit is contained in:
650
external/sdl/SDL/src/SDL.c
vendored
Normal file
650
external/sdl/SDL/src/SDL.c
vendored
Normal file
@ -0,0 +1,650 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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(__WIN32__) || defined(__GDK__)
|
||||
#include "core/windows/SDL_windows.h"
|
||||
#elif !defined(__WINRT__)
|
||||
#include <unistd.h> /* _exit(), etc. */
|
||||
#endif
|
||||
|
||||
/* this checks for HAVE_DBUS_DBUS_H internally. */
|
||||
#include "core/linux/SDL_dbus.h"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
/* Initialization code for SDL */
|
||||
|
||||
#include "SDL_assert_c.h"
|
||||
#include "SDL_log_c.h"
|
||||
#include "audio/SDL_audio_c.h"
|
||||
#include "video/SDL_video_c.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"
|
||||
|
||||
/* Initialization/Cleanup routines */
|
||||
#ifndef SDL_TIMERS_DISABLED
|
||||
#include "timer/SDL_timer_c.h"
|
||||
#endif
|
||||
#ifdef SDL_VIDEO_DRIVER_WINDOWS
|
||||
extern int SDL_HelperWindowCreate(void);
|
||||
extern int 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_PATCHLEVEL == SDL_BUILD_MICRO_VERSION);
|
||||
#endif
|
||||
|
||||
SDL_COMPILE_TIME_ASSERT(SDL_MAJOR_VERSION_min, SDL_MAJOR_VERSION >= 0);
|
||||
/* Limited only by the need to fit in SDL_version */
|
||||
SDL_COMPILE_TIME_ASSERT(SDL_MAJOR_VERSION_max, SDL_MAJOR_VERSION <= 255);
|
||||
|
||||
SDL_COMPILE_TIME_ASSERT(SDL_MINOR_VERSION_min, SDL_MINOR_VERSION >= 0);
|
||||
/* Limited only by the need to fit in SDL_version */
|
||||
SDL_COMPILE_TIME_ASSERT(SDL_MINOR_VERSION_max, SDL_MINOR_VERSION <= 255);
|
||||
|
||||
SDL_COMPILE_TIME_ASSERT(SDL_PATCHLEVEL_min, SDL_PATCHLEVEL >= 0);
|
||||
/* Limited by its encoding in SDL_VERSIONNUM and in the ABI versions */
|
||||
SDL_COMPILE_TIME_ASSERT(SDL_PATCHLEVEL_max, SDL_PATCHLEVEL <= 99);
|
||||
|
||||
/* 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(__WIN32__) || defined(__GDK__)
|
||||
/* "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(__EMSCRIPTEN__)
|
||||
emscripten_cancel_main_loop(); /* this should "kill" the app. */
|
||||
emscripten_force_exit(exitcode); /* this should "kill" the app. */
|
||||
exit(exitcode);
|
||||
#elif defined(__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
|
||||
}
|
||||
|
||||
/* The initialized subsystems */
|
||||
#ifdef SDL_MAIN_NEEDED
|
||||
static SDL_bool SDL_MainIsReady = SDL_FALSE;
|
||||
#else
|
||||
static SDL_bool SDL_MainIsReady = SDL_TRUE;
|
||||
#endif
|
||||
static SDL_bool SDL_bInMainQuit = SDL_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)) {
|
||||
--SDL_SubsystemRefCount[subsystem_index];
|
||||
}
|
||||
}
|
||||
|
||||
/* Private helper to check if a system needs init. */
|
||||
static SDL_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)) ? SDL_TRUE : SDL_FALSE;
|
||||
}
|
||||
|
||||
/* Private helper to check if a system needs to be quit. */
|
||||
static SDL_bool SDL_ShouldQuitSubsystem(Uint32 subsystem)
|
||||
{
|
||||
const int subsystem_index = SDL_MostSignificantBitIndex32(subsystem);
|
||||
if ((subsystem_index >= 0) && (SDL_SubsystemRefCount[subsystem_index] == 0)) {
|
||||
return SDL_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) ? SDL_TRUE : SDL_FALSE;
|
||||
}
|
||||
|
||||
void SDL_SetMainReady(void)
|
||||
{
|
||||
SDL_MainIsReady = SDL_TRUE;
|
||||
}
|
||||
|
||||
int SDL_InitSubSystem(Uint32 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_InitLog();
|
||||
|
||||
/* Clear the error message */
|
||||
SDL_ClearError();
|
||||
|
||||
#ifdef SDL_USE_LIBDBUS
|
||||
SDL_DBus_Init();
|
||||
#endif
|
||||
|
||||
if (flags & SDL_INIT_GAMEPAD) {
|
||||
/* game controller implies joystick */
|
||||
flags |= SDL_INIT_JOYSTICK;
|
||||
}
|
||||
|
||||
if (flags & (SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO)) {
|
||||
/* video or joystick or audio implies events */
|
||||
flags |= SDL_INIT_EVENTS;
|
||||
}
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_WINDOWS
|
||||
if (flags & (SDL_INIT_HAPTIC | SDL_INIT_JOYSTICK)) {
|
||||
if (SDL_HelperWindowCreate() < 0) {
|
||||
goto quit_and_error;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SDL_TIMERS_DISABLED
|
||||
SDL_InitTicks();
|
||||
#endif
|
||||
|
||||
/* Initialize the event subsystem */
|
||||
if (flags & SDL_INIT_EVENTS) {
|
||||
#ifndef SDL_EVENTS_DISABLED
|
||||
if (SDL_ShouldInitSubsystem(SDL_INIT_EVENTS)) {
|
||||
SDL_IncrementSubsystemRefCount(SDL_INIT_EVENTS);
|
||||
if (SDL_InitEvents() < 0) {
|
||||
SDL_DecrementSubsystemRefCount(SDL_INIT_EVENTS);
|
||||
goto quit_and_error;
|
||||
}
|
||||
} else {
|
||||
SDL_IncrementSubsystemRefCount(SDL_INIT_EVENTS);
|
||||
}
|
||||
flags_initialized |= SDL_INIT_EVENTS;
|
||||
#else
|
||||
SDL_SetError("SDL not built with events support");
|
||||
goto quit_and_error;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Initialize the timer subsystem */
|
||||
if (flags & SDL_INIT_TIMER) {
|
||||
#if !defined(SDL_TIMERS_DISABLED) && !defined(SDL_TIMER_DUMMY)
|
||||
if (SDL_ShouldInitSubsystem(SDL_INIT_TIMER)) {
|
||||
SDL_IncrementSubsystemRefCount(SDL_INIT_TIMER);
|
||||
if (SDL_InitTimers() < 0) {
|
||||
SDL_DecrementSubsystemRefCount(SDL_INIT_TIMER);
|
||||
goto quit_and_error;
|
||||
}
|
||||
} else {
|
||||
SDL_IncrementSubsystemRefCount(SDL_INIT_TIMER);
|
||||
}
|
||||
flags_initialized |= SDL_INIT_TIMER;
|
||||
#else
|
||||
SDL_SetError("SDL not built with timer support");
|
||||
goto quit_and_error;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Initialize the video subsystem */
|
||||
if (flags & SDL_INIT_VIDEO) {
|
||||
#ifndef SDL_VIDEO_DISABLED
|
||||
if (SDL_ShouldInitSubsystem(SDL_INIT_VIDEO)) {
|
||||
SDL_IncrementSubsystemRefCount(SDL_INIT_VIDEO);
|
||||
if (SDL_VideoInit(NULL) < 0) {
|
||||
SDL_DecrementSubsystemRefCount(SDL_INIT_VIDEO);
|
||||
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)) {
|
||||
SDL_IncrementSubsystemRefCount(SDL_INIT_AUDIO);
|
||||
if (SDL_InitAudio(NULL) < 0) {
|
||||
SDL_DecrementSubsystemRefCount(SDL_INIT_AUDIO);
|
||||
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)) {
|
||||
SDL_IncrementSubsystemRefCount(SDL_INIT_JOYSTICK);
|
||||
if (SDL_InitJoysticks() < 0) {
|
||||
SDL_DecrementSubsystemRefCount(SDL_INIT_JOYSTICK);
|
||||
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)) {
|
||||
SDL_IncrementSubsystemRefCount(SDL_INIT_GAMEPAD);
|
||||
if (SDL_InitGamepads() < 0) {
|
||||
SDL_DecrementSubsystemRefCount(SDL_INIT_GAMEPAD);
|
||||
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() < 0) {
|
||||
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() < 0) {
|
||||
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
|
||||
}
|
||||
|
||||
(void)flags_initialized; /* make static analysis happy, since this only gets used in error cases. */
|
||||
|
||||
return 0;
|
||||
|
||||
quit_and_error:
|
||||
SDL_QuitSubSystem(flags_initialized);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int SDL_Init(Uint32 flags)
|
||||
{
|
||||
return SDL_InitSubSystem(flags);
|
||||
}
|
||||
|
||||
void SDL_QuitSubSystem(Uint32 flags)
|
||||
{
|
||||
/* Shut down requested initialized subsystems */
|
||||
#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) {
|
||||
/* game controller implies joystick */
|
||||
flags |= SDL_INIT_JOYSTICK;
|
||||
|
||||
if (SDL_ShouldQuitSubsystem(SDL_INIT_GAMEPAD)) {
|
||||
SDL_QuitGamepads();
|
||||
}
|
||||
SDL_DecrementSubsystemRefCount(SDL_INIT_GAMEPAD);
|
||||
}
|
||||
|
||||
if (flags & SDL_INIT_JOYSTICK) {
|
||||
/* joystick implies events */
|
||||
flags |= SDL_INIT_EVENTS;
|
||||
|
||||
if (SDL_ShouldQuitSubsystem(SDL_INIT_JOYSTICK)) {
|
||||
SDL_QuitJoysticks();
|
||||
}
|
||||
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) {
|
||||
/* audio implies events */
|
||||
flags |= SDL_INIT_EVENTS;
|
||||
|
||||
if (SDL_ShouldQuitSubsystem(SDL_INIT_AUDIO)) {
|
||||
SDL_QuitAudio();
|
||||
}
|
||||
SDL_DecrementSubsystemRefCount(SDL_INIT_AUDIO);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SDL_VIDEO_DISABLED
|
||||
if (flags & SDL_INIT_VIDEO) {
|
||||
/* video implies events */
|
||||
flags |= SDL_INIT_EVENTS;
|
||||
|
||||
if (SDL_ShouldQuitSubsystem(SDL_INIT_VIDEO)) {
|
||||
SDL_VideoQuit();
|
||||
}
|
||||
SDL_DecrementSubsystemRefCount(SDL_INIT_VIDEO);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(SDL_TIMERS_DISABLED) && !defined(SDL_TIMER_DUMMY)
|
||||
if (flags & SDL_INIT_TIMER) {
|
||||
if (SDL_ShouldQuitSubsystem(SDL_INIT_TIMER)) {
|
||||
SDL_QuitTimers();
|
||||
}
|
||||
SDL_DecrementSubsystemRefCount(SDL_INIT_TIMER);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SDL_EVENTS_DISABLED
|
||||
if (flags & SDL_INIT_EVENTS) {
|
||||
if (SDL_ShouldQuitSubsystem(SDL_INIT_EVENTS)) {
|
||||
SDL_QuitEvents();
|
||||
}
|
||||
SDL_DecrementSubsystemRefCount(SDL_INIT_EVENTS);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Uint32 SDL_WasInit(Uint32 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 = SDL_TRUE;
|
||||
|
||||
/* Quit all subsystems */
|
||||
#ifdef SDL_VIDEO_DRIVER_WINDOWS
|
||||
SDL_HelperWindowDestroy();
|
||||
#endif
|
||||
SDL_QuitSubSystem(SDL_INIT_EVERYTHING);
|
||||
|
||||
#ifndef SDL_TIMERS_DISABLED
|
||||
SDL_QuitTicks();
|
||||
#endif
|
||||
|
||||
SDL_ClearHints();
|
||||
SDL_AssertionsQuit();
|
||||
|
||||
#ifdef SDL_USE_LIBDBUS
|
||||
SDL_DBus_Quit();
|
||||
#endif
|
||||
|
||||
SDL_QuitLog();
|
||||
|
||||
/* 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_CleanupTLS();
|
||||
|
||||
SDL_bInMainQuit = SDL_FALSE;
|
||||
}
|
||||
|
||||
/* Get the library version number */
|
||||
int SDL_GetVersion(SDL_version *ver)
|
||||
{
|
||||
static SDL_bool check_hint = SDL_TRUE;
|
||||
static SDL_bool legacy_version = SDL_FALSE;
|
||||
|
||||
if (ver == NULL) {
|
||||
return SDL_InvalidParamError("ver");
|
||||
}
|
||||
|
||||
SDL_VERSION(ver);
|
||||
|
||||
if (check_hint) {
|
||||
check_hint = SDL_FALSE;
|
||||
legacy_version = SDL_GetHintBoolean("SDL_LEGACY_VERSION", SDL_FALSE);
|
||||
}
|
||||
|
||||
if (legacy_version) {
|
||||
/* Prior to SDL 2.24.0, the patch version was incremented with every release */
|
||||
ver->patch = ver->minor;
|
||||
ver->minor = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the library source revision */
|
||||
const char *SDL_GetRevision(void)
|
||||
{
|
||||
return SDL_REVISION;
|
||||
}
|
||||
|
||||
/* Get the name of the platform */
|
||||
const char *SDL_GetPlatform(void)
|
||||
{
|
||||
#ifdef __AIX__
|
||||
return "AIX";
|
||||
#elif defined(__ANDROID__)
|
||||
return "Android";
|
||||
#elif defined(__BSDI__)
|
||||
return "BSDI";
|
||||
#elif defined(__DREAMCAST__)
|
||||
return "Dreamcast";
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
return "Emscripten";
|
||||
#elif defined(__FREEBSD__)
|
||||
return "FreeBSD";
|
||||
#elif defined(__HAIKU__)
|
||||
return "Haiku";
|
||||
#elif defined(__HPUX__)
|
||||
return "HP-UX";
|
||||
#elif defined(__IRIX__)
|
||||
return "Irix";
|
||||
#elif defined(__LINUX__)
|
||||
return "Linux";
|
||||
#elif defined(__MINT__)
|
||||
return "Atari MiNT";
|
||||
#elif defined(__MACOS__)
|
||||
return "macOS";
|
||||
#elif defined(__NACL__)
|
||||
return "NaCl";
|
||||
#elif defined(__NETBSD__)
|
||||
return "NetBSD";
|
||||
#elif defined(__OPENBSD__)
|
||||
return "OpenBSD";
|
||||
#elif defined(__OS2__)
|
||||
return "OS/2";
|
||||
#elif defined(__OSF__)
|
||||
return "OSF/1";
|
||||
#elif defined(__QNXNTO__)
|
||||
return "QNX Neutrino";
|
||||
#elif defined(__RISCOS__)
|
||||
return "RISC OS";
|
||||
#elif defined(__SOLARIS__)
|
||||
return "Solaris";
|
||||
#elif defined(__WIN32__)
|
||||
return "Windows";
|
||||
#elif defined(__WINRT__)
|
||||
return "WinRT";
|
||||
#elif defined(__WINGDK__)
|
||||
return "WinGDK";
|
||||
#elif defined(__XBOXONE__)
|
||||
return "Xbox One";
|
||||
#elif defined(__XBOXSERIES__)
|
||||
return "Xbox Series X|S";
|
||||
#elif defined(__IOS__)
|
||||
return "iOS";
|
||||
#elif defined(__TVOS__)
|
||||
return "tvOS";
|
||||
#elif defined(__PS2__)
|
||||
return "PlayStation 2";
|
||||
#elif defined(__PSP__)
|
||||
return "PlayStation Portable";
|
||||
#elif defined(__VITA__)
|
||||
return "PlayStation Vita";
|
||||
#elif defined(__NGAGE__)
|
||||
return "Nokia N-Gage";
|
||||
#elif defined(__3DS__)
|
||||
return "Nintendo 3DS";
|
||||
#elif defined(__managarm__)
|
||||
return "Managarm";
|
||||
#else
|
||||
return "Unknown (see SDL_platform.h)";
|
||||
#endif
|
||||
}
|
||||
|
||||
SDL_bool SDL_IsTablet(void)
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
extern SDL_bool SDL_IsAndroidTablet(void);
|
||||
return SDL_IsAndroidTablet();
|
||||
#elif defined(__IOS__)
|
||||
extern SDL_bool SDL_IsIPad(void);
|
||||
return SDL_IsIPad();
|
||||
#else
|
||||
return SDL_FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __WIN32__
|
||||
|
||||
#if (!defined(HAVE_LIBC) || defined(__WATCOMC__)) && !defined(SDL_STATIC_LIB)
|
||||
/* Need to include DllMain() on Watcom C for some reason.. */
|
||||
|
||||
BOOL APIENTRY _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(__WIN32__) || defined(__GDK__) */
|
445
external/sdl/SDL/src/SDL_assert.c
vendored
Normal file
445
external/sdl/SDL/src/SDL_assert.c
vendored
Normal file
@ -0,0 +1,445 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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(__WIN32__) || defined(__GDK__)
|
||||
#include "core/windows/SDL_windows.h"
|
||||
#endif
|
||||
|
||||
#include "SDL_assert_c.h"
|
||||
#include "video/SDL_sysvideo.h"
|
||||
|
||||
#if defined(__WIN32__) || defined(__GDK__)
|
||||
#ifndef WS_OVERLAPPEDWINDOW
|
||||
#define WS_OVERLAPPEDWINDOW 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __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(__WIN32__) || defined(__GDK__)
|
||||
#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 != NULL) && (assertion_handler != SDL_PromptAssertion)) {
|
||||
debug_print("\n\nSDL assertion report.\n");
|
||||
debug_print("All SDL assertions between last init/quit:\n\n");
|
||||
|
||||
while (item != NULL) {
|
||||
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)
|
||||
{
|
||||
const char *envr;
|
||||
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_overflow(len, 1, &buf_len) == 0) {
|
||||
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. */
|
||||
envr = SDL_getenv("SDL_ASSERT");
|
||||
if (envr != NULL) {
|
||||
if (message != stack_buf) {
|
||||
SDL_free(message);
|
||||
}
|
||||
|
||||
if (SDL_strcmp(envr, "abort") == 0) {
|
||||
return SDL_ASSERTION_ABORT;
|
||||
} else if (SDL_strcmp(envr, "break") == 0) {
|
||||
return SDL_ASSERTION_BREAK;
|
||||
} else if (SDL_strcmp(envr, "retry") == 0) {
|
||||
return SDL_ASSERTION_RETRY;
|
||||
} else if (SDL_strcmp(envr, "ignore") == 0) {
|
||||
return SDL_ASSERTION_IGNORE;
|
||||
} else if (SDL_strcmp(envr, "always_ignore") == 0) {
|
||||
return SDL_ASSERTION_ALWAYS_IGNORE;
|
||||
} else {
|
||||
return SDL_ASSERTION_ABORT; /* oh well. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Leave fullscreen mode, if possible (scary!) */
|
||||
window = SDL_GetFocusWindow();
|
||||
if (window) {
|
||||
if (window->fullscreen_exclusive) {
|
||||
SDL_MinimizeWindow(window);
|
||||
} else {
|
||||
/* !!! FIXME: ungrab the input if we're not fullscreen? */
|
||||
/* No need to mess with the window */
|
||||
window = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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) == 0) {
|
||||
if (selected == -1) {
|
||||
state = SDL_ASSERTION_IGNORE;
|
||||
} else {
|
||||
state = (SDL_AssertState)selected;
|
||||
}
|
||||
} else {
|
||||
#ifdef __EMSCRIPTEN__
|
||||
/* This is nasty, but we can't block on a custom UI. */
|
||||
for (;;) {
|
||||
SDL_bool okay = SDL_TRUE;
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
char *buf = (char *) 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 allocate(intArrayFromString(reply), 'i8', ALLOC_NORMAL);
|
||||
}, message);
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
if (SDL_strcmp(buf, "a") == 0) {
|
||||
state = SDL_ASSERTION_ABORT;
|
||||
#if 0 /* (currently) no break functionality on Emscripten */
|
||||
} else if (SDL_strcmp(buf, "b") == 0) {
|
||||
state = SDL_ASSERTION_BREAK;
|
||||
#endif
|
||||
} else if (SDL_strcmp(buf, "r") == 0) {
|
||||
state = SDL_ASSERTION_RETRY;
|
||||
} else if (SDL_strcmp(buf, "i") == 0) {
|
||||
state = SDL_ASSERTION_IGNORE;
|
||||
} else if (SDL_strcmp(buf, "A") == 0) {
|
||||
state = SDL_ASSERTION_ALWAYS_IGNORE;
|
||||
} else {
|
||||
okay = SDL_FALSE;
|
||||
}
|
||||
free(buf); /* This should NOT be SDL_free() */
|
||||
|
||||
if (okay) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#elif defined(HAVE_STDIO_H)
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
#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_AtomicLock(&spinlock);
|
||||
if (assertion_mutex == NULL) { /* never called SDL_Init()? */
|
||||
assertion_mutex = SDL_CreateMutex();
|
||||
if (assertion_mutex == NULL) {
|
||||
SDL_AtomicUnlock(&spinlock);
|
||||
return SDL_ASSERTION_IGNORE; /* oh well, I guess. */
|
||||
}
|
||||
}
|
||||
SDL_AtomicUnlock(&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 = 1;
|
||||
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 != NULL) {
|
||||
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 != NULL; item = next) {
|
||||
next = (SDL_AssertData *)item->next;
|
||||
item->always_ignore = SDL_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 != NULL) {
|
||||
*userdata = assertion_userdata;
|
||||
}
|
||||
return assertion_handler;
|
||||
}
|
28
external/sdl/SDL/src/SDL_assert_c.h
vendored
Normal file
28
external/sdl/SDL/src/SDL_assert_c.h
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_ */
|
318
external/sdl/SDL/src/SDL_dataqueue.c
vendored
Normal file
318
external/sdl/SDL/src/SDL_dataqueue.c
vendored
Normal file
@ -0,0 +1,318 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_dataqueue.h"
|
||||
|
||||
typedef struct SDL_DataQueuePacket
|
||||
{
|
||||
size_t datalen; /* bytes currently in use in this packet. */
|
||||
size_t startpos; /* bytes currently consumed in this packet. */
|
||||
struct SDL_DataQueuePacket *next; /* next item in linked list. */
|
||||
Uint8 data[SDL_VARIABLE_LENGTH_ARRAY]; /* packet data */
|
||||
} SDL_DataQueuePacket;
|
||||
|
||||
struct SDL_DataQueue
|
||||
{
|
||||
SDL_Mutex *lock;
|
||||
SDL_DataQueuePacket *head; /* device fed from here. */
|
||||
SDL_DataQueuePacket *tail; /* queue fills to here. */
|
||||
SDL_DataQueuePacket *pool; /* these are unused packets. */
|
||||
size_t packet_size; /* size of new packets */
|
||||
size_t queued_bytes; /* number of bytes of data in the queue. */
|
||||
};
|
||||
|
||||
static void SDL_FreeDataQueueList(SDL_DataQueuePacket *packet)
|
||||
{
|
||||
while (packet) {
|
||||
SDL_DataQueuePacket *next = packet->next;
|
||||
SDL_free(packet);
|
||||
packet = next;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_DataQueue *SDL_CreateDataQueue(const size_t _packetlen, const size_t initialslack)
|
||||
{
|
||||
SDL_DataQueue *queue = (SDL_DataQueue *)SDL_calloc(1, sizeof(SDL_DataQueue));
|
||||
|
||||
if (queue == NULL) {
|
||||
SDL_OutOfMemory();
|
||||
} else {
|
||||
const size_t packetlen = _packetlen ? _packetlen : 1024;
|
||||
const size_t wantpackets = (initialslack + (packetlen - 1)) / packetlen;
|
||||
size_t i;
|
||||
|
||||
queue->packet_size = packetlen;
|
||||
|
||||
queue->lock = SDL_CreateMutex();
|
||||
if (!queue->lock) {
|
||||
SDL_free(queue);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < wantpackets; i++) {
|
||||
SDL_DataQueuePacket *packet = (SDL_DataQueuePacket *)SDL_malloc(sizeof(SDL_DataQueuePacket) + packetlen);
|
||||
if (packet) { /* don't care if this fails, we'll deal later. */
|
||||
packet->datalen = 0;
|
||||
packet->startpos = 0;
|
||||
packet->next = queue->pool;
|
||||
queue->pool = packet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
void SDL_DestroyDataQueue(SDL_DataQueue *queue)
|
||||
{
|
||||
if (queue) {
|
||||
SDL_FreeDataQueueList(queue->head);
|
||||
SDL_FreeDataQueueList(queue->pool);
|
||||
SDL_DestroyMutex(queue->lock);
|
||||
SDL_free(queue);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_ClearDataQueue(SDL_DataQueue *queue, const size_t slack)
|
||||
{
|
||||
const size_t packet_size = queue ? queue->packet_size : 1;
|
||||
const size_t slackpackets = (slack + (packet_size - 1)) / packet_size;
|
||||
SDL_DataQueuePacket *packet;
|
||||
SDL_DataQueuePacket *prev = NULL;
|
||||
size_t i;
|
||||
|
||||
if (queue == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_LockMutex(queue->lock);
|
||||
|
||||
packet = queue->head;
|
||||
|
||||
/* merge the available pool and the current queue into one list. */
|
||||
if (packet) {
|
||||
queue->tail->next = queue->pool;
|
||||
} else {
|
||||
packet = queue->pool;
|
||||
}
|
||||
|
||||
/* Remove the queued packets from the device. */
|
||||
queue->tail = NULL;
|
||||
queue->head = NULL;
|
||||
queue->queued_bytes = 0;
|
||||
queue->pool = packet;
|
||||
|
||||
/* Optionally keep some slack in the pool to reduce memory allocation pressure. */
|
||||
for (i = 0; packet && (i < slackpackets); i++) {
|
||||
prev = packet;
|
||||
packet = packet->next;
|
||||
}
|
||||
|
||||
if (prev) {
|
||||
prev->next = NULL;
|
||||
} else {
|
||||
queue->pool = NULL;
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(queue->lock);
|
||||
|
||||
SDL_FreeDataQueueList(packet); /* free extra packets */
|
||||
}
|
||||
|
||||
/* You must hold queue->lock before calling this! */
|
||||
static SDL_DataQueuePacket *AllocateDataQueuePacket(SDL_DataQueue *queue)
|
||||
{
|
||||
SDL_DataQueuePacket *packet;
|
||||
|
||||
SDL_assert(queue != NULL);
|
||||
|
||||
packet = queue->pool;
|
||||
if (packet != NULL) {
|
||||
/* we have one available in the pool. */
|
||||
queue->pool = packet->next;
|
||||
} else {
|
||||
/* Have to allocate a new one! */
|
||||
packet = (SDL_DataQueuePacket *)SDL_malloc(sizeof(SDL_DataQueuePacket) + queue->packet_size);
|
||||
if (packet == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
packet->datalen = 0;
|
||||
packet->startpos = 0;
|
||||
packet->next = NULL;
|
||||
|
||||
SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0));
|
||||
if (queue->tail == NULL) {
|
||||
queue->head = packet;
|
||||
} else {
|
||||
queue->tail->next = packet;
|
||||
}
|
||||
queue->tail = packet;
|
||||
return packet;
|
||||
}
|
||||
|
||||
int SDL_WriteToDataQueue(SDL_DataQueue *queue, const void *_data, const size_t _len)
|
||||
{
|
||||
size_t len = _len;
|
||||
const Uint8 *data = (const Uint8 *)_data;
|
||||
const size_t packet_size = queue ? queue->packet_size : 0;
|
||||
SDL_DataQueuePacket *orighead;
|
||||
SDL_DataQueuePacket *origtail;
|
||||
size_t origlen;
|
||||
size_t datalen;
|
||||
|
||||
if (queue == NULL) {
|
||||
return SDL_InvalidParamError("queue");
|
||||
}
|
||||
|
||||
SDL_LockMutex(queue->lock);
|
||||
|
||||
orighead = queue->head;
|
||||
origtail = queue->tail;
|
||||
origlen = origtail ? origtail->datalen : 0;
|
||||
|
||||
while (len > 0) {
|
||||
SDL_DataQueuePacket *packet = queue->tail;
|
||||
SDL_assert(packet == NULL || (packet->datalen <= packet_size));
|
||||
if (packet == NULL || (packet->datalen >= packet_size)) {
|
||||
/* tail packet missing or completely full; we need a new packet. */
|
||||
packet = AllocateDataQueuePacket(queue);
|
||||
if (packet == NULL) {
|
||||
/* uhoh, reset so we've queued nothing new, free what we can. */
|
||||
if (origtail == NULL) {
|
||||
packet = queue->head; /* whole queue. */
|
||||
} else {
|
||||
packet = origtail->next; /* what we added to existing queue. */
|
||||
origtail->next = NULL;
|
||||
origtail->datalen = origlen;
|
||||
}
|
||||
queue->head = orighead;
|
||||
queue->tail = origtail;
|
||||
queue->pool = NULL;
|
||||
|
||||
SDL_UnlockMutex(queue->lock);
|
||||
SDL_FreeDataQueueList(packet); /* give back what we can. */
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
}
|
||||
|
||||
datalen = SDL_min(len, packet_size - packet->datalen);
|
||||
SDL_memcpy(packet->data + packet->datalen, data, datalen);
|
||||
data += datalen;
|
||||
len -= datalen;
|
||||
packet->datalen += datalen;
|
||||
queue->queued_bytes += datalen;
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(queue->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t SDL_PeekIntoDataQueue(SDL_DataQueue *queue, void *_buf, const size_t _len)
|
||||
{
|
||||
size_t len = _len;
|
||||
Uint8 *buf = (Uint8 *)_buf;
|
||||
Uint8 *ptr = buf;
|
||||
SDL_DataQueuePacket *packet;
|
||||
|
||||
if (queue == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_LockMutex(queue->lock);
|
||||
|
||||
for (packet = queue->head; len && packet; packet = packet->next) {
|
||||
const size_t avail = packet->datalen - packet->startpos;
|
||||
const size_t cpy = SDL_min(len, avail);
|
||||
SDL_assert(queue->queued_bytes >= avail);
|
||||
|
||||
SDL_memcpy(ptr, packet->data + packet->startpos, cpy);
|
||||
ptr += cpy;
|
||||
len -= cpy;
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(queue->lock);
|
||||
|
||||
return (size_t)(ptr - buf);
|
||||
}
|
||||
|
||||
size_t SDL_ReadFromDataQueue(SDL_DataQueue *queue, void *_buf, const size_t _len)
|
||||
{
|
||||
size_t len = _len;
|
||||
Uint8 *buf = (Uint8 *)_buf;
|
||||
Uint8 *ptr = buf;
|
||||
SDL_DataQueuePacket *packet;
|
||||
|
||||
if (queue == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SDL_LockMutex(queue->lock);
|
||||
|
||||
while ((len > 0) && ((packet = queue->head) != NULL)) {
|
||||
const size_t avail = packet->datalen - packet->startpos;
|
||||
const size_t cpy = SDL_min(len, avail);
|
||||
SDL_assert(queue->queued_bytes >= avail);
|
||||
|
||||
SDL_memcpy(ptr, packet->data + packet->startpos, cpy);
|
||||
packet->startpos += cpy;
|
||||
ptr += cpy;
|
||||
queue->queued_bytes -= cpy;
|
||||
len -= cpy;
|
||||
|
||||
if (packet->startpos == packet->datalen) { /* packet is done, put it in the pool. */
|
||||
queue->head = packet->next;
|
||||
SDL_assert((packet->next != NULL) || (packet == queue->tail));
|
||||
packet->next = queue->pool;
|
||||
queue->pool = packet;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_assert((queue->head != NULL) == (queue->queued_bytes != 0));
|
||||
|
||||
if (queue->head == NULL) {
|
||||
queue->tail = NULL; /* in case we drained the queue entirely. */
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(queue->lock);
|
||||
|
||||
return (size_t)(ptr - buf);
|
||||
}
|
||||
|
||||
size_t SDL_GetDataQueueSize(SDL_DataQueue *queue)
|
||||
{
|
||||
size_t retval = 0;
|
||||
if (queue) {
|
||||
SDL_LockMutex(queue->lock);
|
||||
retval = queue->queued_bytes;
|
||||
SDL_UnlockMutex(queue->lock);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
SDL_Mutex *SDL_GetDataQueueMutex(SDL_DataQueue *queue)
|
||||
{
|
||||
return queue ? queue->lock : NULL;
|
||||
}
|
||||
|
38
external/sdl/SDL/src/SDL_dataqueue.h
vendored
Normal file
38
external/sdl/SDL/src/SDL_dataqueue.h
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_dataqueue_h_
|
||||
#define SDL_dataqueue_h_
|
||||
|
||||
/* this is not (currently) a public API. But maybe it should be! */
|
||||
|
||||
struct SDL_DataQueue;
|
||||
typedef struct SDL_DataQueue SDL_DataQueue;
|
||||
|
||||
SDL_DataQueue *SDL_CreateDataQueue(const size_t packetlen, const size_t initialslack);
|
||||
void SDL_DestroyDataQueue(SDL_DataQueue *queue);
|
||||
void SDL_ClearDataQueue(SDL_DataQueue *queue, const size_t slack);
|
||||
int SDL_WriteToDataQueue(SDL_DataQueue *queue, const void *data, const size_t len);
|
||||
size_t SDL_ReadFromDataQueue(SDL_DataQueue *queue, void *buf, const size_t len);
|
||||
size_t SDL_PeekIntoDataQueue(SDL_DataQueue *queue, void *buf, const size_t len);
|
||||
size_t SDL_GetDataQueueSize(SDL_DataQueue *queue);
|
||||
SDL_Mutex *SDL_GetDataQueueMutex(SDL_DataQueue *queue); /* don't destroy this, obviously. */
|
||||
|
||||
#endif /* SDL_dataqueue_h_ */
|
104
external/sdl/SDL/src/SDL_error.c
vendored
Normal file
104
external/sdl/SDL/src/SDL_error.c
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
int SDL_SetError(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
|
||||
{
|
||||
/* Ignore call if invalid format pointer was passed */
|
||||
if (fmt != NULL) {
|
||||
va_list ap;
|
||||
int result;
|
||||
SDL_error *error = SDL_GetErrBuf();
|
||||
|
||||
error->error = 1; /* mark error as valid */
|
||||
|
||||
va_start(ap, fmt);
|
||||
result = SDL_vsnprintf(error->str, error->len, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
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_start(ap, fmt);
|
||||
(void)SDL_vsnprintf(error->str, error->len, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
if (SDL_LogGetPriority(SDL_LOG_CATEGORY_ERROR) <= SDL_LOG_PRIORITY_DEBUG) {
|
||||
/* If we are in debug mode, print out the error message */
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_ERROR, "%s", error->str);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Available for backwards compatibility */
|
||||
const char *SDL_GetError(void)
|
||||
{
|
||||
const SDL_error *error = SDL_GetErrBuf();
|
||||
return error->error ? error->str : "";
|
||||
}
|
||||
|
||||
void SDL_ClearError(void)
|
||||
{
|
||||
SDL_GetErrBuf()->error = 0;
|
||||
}
|
||||
|
||||
/* Very common errors go here */
|
||||
int SDL_Error(SDL_errorcode code)
|
||||
{
|
||||
switch (code) {
|
||||
case SDL_ENOMEM:
|
||||
return SDL_SetError("Out of memory");
|
||||
case SDL_EFREAD:
|
||||
return SDL_SetError("Error reading from datastream");
|
||||
case SDL_EFWRITE:
|
||||
return SDL_SetError("Error writing to datastream");
|
||||
case SDL_EFSEEK:
|
||||
return SDL_SetError("Error seeking in datastream");
|
||||
case SDL_UNSUPPORTED:
|
||||
return SDL_SetError("That operation is not supported");
|
||||
default:
|
||||
return SDL_SetError("Unknown SDL error");
|
||||
}
|
||||
}
|
||||
|
||||
char *SDL_GetErrorMsg(char *errstr, int maxlen)
|
||||
{
|
||||
const SDL_error *error = SDL_GetErrBuf();
|
||||
|
||||
if (error->error) {
|
||||
SDL_strlcpy(errstr, error->str, maxlen);
|
||||
} else {
|
||||
*errstr = '\0';
|
||||
}
|
||||
|
||||
return errstr;
|
||||
}
|
42
external/sdl/SDL/src/SDL_error_c.h
vendored
Normal file
42
external/sdl/SDL/src/SDL_error_c.h
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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 struct SDL_error
|
||||
{
|
||||
int error; /* This is a numeric value corresponding to the current 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(void);
|
||||
|
||||
#endif /* SDL_error_c_h_ */
|
92
external/sdl/SDL/src/SDL_guid.c
vendored
Normal file
92
external/sdl/SDL/src/SDL_guid.c
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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 */
|
||||
int SDL_GUIDToString(SDL_GUID guid, char *pszGUID, int cbGUID)
|
||||
{
|
||||
static const char k_rgchHexToASCII[] = "0123456789abcdef";
|
||||
int i;
|
||||
|
||||
if (pszGUID == NULL) {
|
||||
return SDL_InvalidParamError("pszGUID");
|
||||
}
|
||||
if (cbGUID <= 0) {
|
||||
return SDL_InvalidParamError("cbGUID");
|
||||
}
|
||||
|
||||
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';
|
||||
return 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_GUIDFromString(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;
|
||||
}
|
312
external/sdl/SDL/src/SDL_hints.c
vendored
Normal file
312
external/sdl/SDL/src/SDL_hints.c
vendored
Normal file
@ -0,0 +1,312 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
/* Assuming there aren't many hints set and they aren't being queried in
|
||||
critical performance paths, we'll just use linked lists here.
|
||||
*/
|
||||
typedef struct SDL_HintWatch
|
||||
{
|
||||
SDL_HintCallback callback;
|
||||
void *userdata;
|
||||
struct SDL_HintWatch *next;
|
||||
} SDL_HintWatch;
|
||||
|
||||
typedef struct SDL_Hint
|
||||
{
|
||||
char *name;
|
||||
char *value;
|
||||
SDL_HintPriority priority;
|
||||
SDL_HintWatch *callbacks;
|
||||
struct SDL_Hint *next;
|
||||
} SDL_Hint;
|
||||
|
||||
static SDL_Hint *SDL_hints;
|
||||
|
||||
SDL_bool SDL_SetHintWithPriority(const char *name, const char *value, SDL_HintPriority priority)
|
||||
{
|
||||
const char *env;
|
||||
SDL_Hint *hint;
|
||||
SDL_HintWatch *entry;
|
||||
|
||||
if (name == NULL) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
env = SDL_getenv(name);
|
||||
if (env && priority < SDL_HINT_OVERRIDE) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
for (hint = SDL_hints; hint; hint = hint->next) {
|
||||
if (SDL_strcmp(name, hint->name) == 0) {
|
||||
if (priority < hint->priority) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
if (hint->value != value &&
|
||||
(value == NULL || !hint->value || SDL_strcmp(hint->value, value) != 0)) {
|
||||
char *old_value = hint->value;
|
||||
|
||||
hint->value = value ? SDL_strdup(value) : NULL;
|
||||
for (entry = hint->callbacks; 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;
|
||||
}
|
||||
if (old_value) {
|
||||
SDL_free(old_value);
|
||||
}
|
||||
}
|
||||
hint->priority = priority;
|
||||
return SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Couldn't find the hint, add a new one */
|
||||
hint = (SDL_Hint *)SDL_malloc(sizeof(*hint));
|
||||
if (hint == NULL) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
hint->name = SDL_strdup(name);
|
||||
hint->value = value ? SDL_strdup(value) : NULL;
|
||||
hint->priority = priority;
|
||||
hint->callbacks = NULL;
|
||||
hint->next = SDL_hints;
|
||||
SDL_hints = hint;
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
SDL_bool SDL_ResetHint(const char *name)
|
||||
{
|
||||
const char *env;
|
||||
SDL_Hint *hint;
|
||||
SDL_HintWatch *entry;
|
||||
|
||||
if (name == NULL) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
env = SDL_getenv(name);
|
||||
for (hint = SDL_hints; hint; hint = hint->next) {
|
||||
if (SDL_strcmp(name, hint->name) == 0) {
|
||||
if ((env == NULL && hint->value != NULL) ||
|
||||
(env != NULL && hint->value == NULL) ||
|
||||
(env != NULL && SDL_strcmp(env, hint->value) != 0)) {
|
||||
for (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;
|
||||
return SDL_TRUE;
|
||||
}
|
||||
}
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
void SDL_ResetHints(void)
|
||||
{
|
||||
const char *env;
|
||||
SDL_Hint *hint;
|
||||
SDL_HintWatch *entry;
|
||||
|
||||
for (hint = SDL_hints; hint; hint = hint->next) {
|
||||
env = SDL_getenv(hint->name);
|
||||
if ((env == NULL && hint->value != NULL) ||
|
||||
(env != NULL && hint->value == NULL) ||
|
||||
(env != NULL && SDL_strcmp(env, hint->value) != 0)) {
|
||||
for (entry = hint->callbacks; entry;) {
|
||||
/* Save the next entry in case this one is deleted */
|
||||
SDL_HintWatch *next = entry->next;
|
||||
entry->callback(entry->userdata, hint->name, hint->value, env);
|
||||
entry = next;
|
||||
}
|
||||
}
|
||||
SDL_free(hint->value);
|
||||
hint->value = NULL;
|
||||
hint->priority = SDL_HINT_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_bool SDL_SetHint(const char *name, const char *value)
|
||||
{
|
||||
return SDL_SetHintWithPriority(name, value, SDL_HINT_NORMAL);
|
||||
}
|
||||
|
||||
const char *SDL_GetHint(const char *name)
|
||||
{
|
||||
const char *env;
|
||||
SDL_Hint *hint;
|
||||
|
||||
env = SDL_getenv(name);
|
||||
for (hint = SDL_hints; hint; hint = hint->next) {
|
||||
if (SDL_strcmp(name, hint->name) == 0) {
|
||||
if (env == NULL || hint->priority == SDL_HINT_OVERRIDE) {
|
||||
return hint->value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
int SDL_GetStringInteger(const char *value, int default_value)
|
||||
{
|
||||
if (value == NULL || !*value) {
|
||||
return default_value;
|
||||
}
|
||||
if (*value == '0' || SDL_strcasecmp(value, "false") == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (*value == '1' || SDL_strcasecmp(value, "true") == 0) {
|
||||
return 1;
|
||||
}
|
||||
if (*value == '-' || SDL_isdigit(*value)) {
|
||||
return SDL_atoi(value);
|
||||
}
|
||||
return default_value;
|
||||
}
|
||||
|
||||
SDL_bool SDL_GetStringBoolean(const char *value, SDL_bool default_value)
|
||||
{
|
||||
if (value == NULL || !*value) {
|
||||
return default_value;
|
||||
}
|
||||
if (*value == '0' || SDL_strcasecmp(value, "false") == 0) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
SDL_bool SDL_GetHintBoolean(const char *name, SDL_bool default_value)
|
||||
{
|
||||
const char *hint = SDL_GetHint(name);
|
||||
return SDL_GetStringBoolean(hint, default_value);
|
||||
}
|
||||
|
||||
int SDL_AddHintCallback(const char *name, SDL_HintCallback callback, void *userdata)
|
||||
{
|
||||
SDL_Hint *hint;
|
||||
SDL_HintWatch *entry;
|
||||
const char *value;
|
||||
|
||||
if (name == NULL || !*name) {
|
||||
return SDL_InvalidParamError("name");
|
||||
}
|
||||
if (!callback) {
|
||||
return SDL_InvalidParamError("callback");
|
||||
}
|
||||
|
||||
SDL_DelHintCallback(name, callback, userdata);
|
||||
|
||||
entry = (SDL_HintWatch *)SDL_malloc(sizeof(*entry));
|
||||
if (entry == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
entry->callback = callback;
|
||||
entry->userdata = userdata;
|
||||
|
||||
for (hint = SDL_hints; hint; hint = hint->next) {
|
||||
if (SDL_strcmp(name, hint->name) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hint == NULL) {
|
||||
/* Need to add a hint entry for this watcher */
|
||||
hint = (SDL_Hint *)SDL_malloc(sizeof(*hint));
|
||||
if (hint == NULL) {
|
||||
SDL_free(entry);
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
hint->name = SDL_strdup(name);
|
||||
if (!hint->name) {
|
||||
SDL_free(entry);
|
||||
SDL_free(hint);
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
hint->value = NULL;
|
||||
hint->priority = SDL_HINT_DEFAULT;
|
||||
hint->callbacks = NULL;
|
||||
hint->next = SDL_hints;
|
||||
SDL_hints = hint;
|
||||
}
|
||||
|
||||
/* Add it to the callbacks for this hint */
|
||||
entry->next = hint->callbacks;
|
||||
hint->callbacks = entry;
|
||||
|
||||
/* Now call it with the current value */
|
||||
value = SDL_GetHint(name);
|
||||
callback(userdata, name, value, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SDL_DelHintCallback(const char *name, SDL_HintCallback callback, void *userdata)
|
||||
{
|
||||
SDL_Hint *hint;
|
||||
SDL_HintWatch *entry, *prev;
|
||||
|
||||
for (hint = SDL_hints; hint; hint = hint->next) {
|
||||
if (SDL_strcmp(name, hint->name) == 0) {
|
||||
prev = NULL;
|
||||
for (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;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_ClearHints(void)
|
||||
{
|
||||
SDL_Hint *hint;
|
||||
SDL_HintWatch *entry;
|
||||
|
||||
while (SDL_hints) {
|
||||
hint = SDL_hints;
|
||||
SDL_hints = hint->next;
|
||||
|
||||
SDL_free(hint->name);
|
||||
SDL_free(hint->value);
|
||||
for (entry = hint->callbacks; entry;) {
|
||||
SDL_HintWatch *freeable = entry;
|
||||
entry = entry->next;
|
||||
SDL_free(freeable);
|
||||
}
|
||||
SDL_free(hint);
|
||||
}
|
||||
}
|
31
external/sdl/SDL/src/SDL_hints_c.h
vendored
Normal file
31
external/sdl/SDL/src/SDL_hints_c.h
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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 SDL_bool SDL_GetStringBoolean(const char *value, SDL_bool default_value);
|
||||
extern int SDL_GetStringInteger(const char *value, int default_value);
|
||||
|
||||
#endif /* SDL_hints_c_h_ */
|
209
external/sdl/SDL/src/SDL_internal.h
vendored
Normal file
209
external/sdl/SDL/src/SDL_internal.h
vendored
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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
|
||||
|
||||
#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 "build_config/SDL_build_config.h"
|
||||
|
||||
#include "dynapi/SDL_dynapi.h"
|
||||
|
||||
#if SDL_DYNAMIC_API
|
||||
#include "dynapi/SDL_dynapi_overrides.h"
|
||||
/* force DECLSPEC off...it's all internal symbols now.
|
||||
These will have actual #defines during SDL_dynapi.c only */
|
||||
#ifdef DECLSPEC
|
||||
#undef DECLSPEC
|
||||
#endif
|
||||
#define DECLSPEC
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#ifndef _DARWIN_C_SOURCE
|
||||
#define _DARWIN_C_SOURCE 1 /* for memset_pattern4() */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#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_CTYPE_H
|
||||
#include <ctype.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.
|
||||
You have to manually edit this file. */
|
||||
#ifndef SDL_LEAN_AND_MEAN
|
||||
#define SDL_LEAN_AND_MEAN 0
|
||||
#endif
|
||||
|
||||
/* Optimized functions from 'SDL_blit_0.c'
|
||||
- blit with source BitsPerPixel < 8, palette */
|
||||
#ifndef SDL_HAVE_BLIT_0
|
||||
#define SDL_HAVE_BLIT_0 !SDL_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
/* Optimized functions from 'SDL_blit_1.c'
|
||||
- blit with source BytesPerPixel == 1, palette */
|
||||
#ifndef SDL_HAVE_BLIT_1
|
||||
#define SDL_HAVE_BLIT_1 !SDL_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
/* Optimized functions from 'SDL_blit_A.c'
|
||||
- blit with 'SDL_BLENDMODE_BLEND' blending mode */
|
||||
#ifndef SDL_HAVE_BLIT_A
|
||||
#define SDL_HAVE_BLIT_A !SDL_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
/* Optimized functions from 'SDL_blit_N.c'
|
||||
- blit with COLORKEY mode, or nothing */
|
||||
#ifndef SDL_HAVE_BLIT_N
|
||||
#define SDL_HAVE_BLIT_N !SDL_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
/* Optimized functions from 'SDL_blit_N.c'
|
||||
- RGB565 conversion with Lookup tables */
|
||||
#ifndef SDL_HAVE_BLIT_N_RGB565
|
||||
#define SDL_HAVE_BLIT_N_RGB565 !SDL_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
/* Optimized functions from 'SDL_blit_AUTO.c'
|
||||
- blit with modulate color, modulate alpha, any blending mode
|
||||
- scaling or not */
|
||||
#ifndef SDL_HAVE_BLIT_AUTO
|
||||
#define SDL_HAVE_BLIT_AUTO !SDL_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
/* Run-Length-Encoding
|
||||
- SDL_SetSurfaceColorKey() called with SDL_RLEACCEL flag */
|
||||
#ifndef SDL_HAVE_RLE
|
||||
#define SDL_HAVE_RLE !SDL_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
/* Software SDL_Renderer
|
||||
- creation of software renderer
|
||||
- *not* general blitting functions
|
||||
- {blend,draw}{fillrect,line,point} internal functions */
|
||||
#ifndef SDL_VIDEO_RENDER_SW
|
||||
#define SDL_VIDEO_RENDER_SW !SDL_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
/* YUV formats
|
||||
- handling of YUV surfaces
|
||||
- blitting and conversion functions */
|
||||
#ifndef SDL_HAVE_YUV
|
||||
#define SDL_HAVE_YUV !SDL_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#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>
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
/* Set up for C function definitions, even when using C++ */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern DECLSPEC int SDLCALL SDL_WaitSemaphoreTimeoutNS(SDL_Semaphore *sem, Sint64 timeoutNS);
|
||||
extern DECLSPEC int SDLCALL SDL_WaitConditionTimeoutNS(SDL_Condition *cond, SDL_Mutex *mutex, Sint64 timeoutNS);
|
||||
extern DECLSPEC int SDLCALL SDL_WaitEventTimeoutNS(SDL_Event *event, Sint64 timeoutNS);
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SDL_internal_h_ */
|
86
external/sdl/SDL/src/SDL_list.c
vendored
Normal file
86
external/sdl/SDL/src/SDL_list.c
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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 */
|
||||
int SDL_ListAdd(SDL_ListNode **head, void *ent)
|
||||
{
|
||||
SDL_ListNode *node = SDL_malloc(sizeof(*node));
|
||||
|
||||
if (node == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
node->entry = ent;
|
||||
node->next = *head;
|
||||
*head = node;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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 == NULL || *head == NULL) {
|
||||
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
external/sdl/SDL/src/SDL_list.h
vendored
Normal file
36
external/sdl/SDL/src/SDL_list.h
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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;
|
||||
|
||||
int 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_ */
|
506
external/sdl/SDL/src/SDL_log.c
vendored
Normal file
506
external/sdl/SDL/src/SDL_log.c
vendored
Normal file
@ -0,0 +1,506 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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(__WIN32__) || defined(__WINRT__) || defined(__GDK__)
|
||||
#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 __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_PRIORITY SDL_LOG_PRIORITY_ERROR
|
||||
#define DEFAULT_ASSERT_PRIORITY SDL_LOG_PRIORITY_WARN
|
||||
#define DEFAULT_APPLICATION_PRIORITY SDL_LOG_PRIORITY_INFO
|
||||
#define DEFAULT_TEST_PRIORITY SDL_LOG_PRIORITY_VERBOSE
|
||||
|
||||
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 SDL_LogLevel *SDL_loglevels;
|
||||
static SDL_LogPriority SDL_default_priority = DEFAULT_PRIORITY;
|
||||
static SDL_LogPriority SDL_assert_priority = DEFAULT_ASSERT_PRIORITY;
|
||||
static SDL_LogPriority SDL_application_priority = DEFAULT_APPLICATION_PRIORITY;
|
||||
static SDL_LogPriority SDL_test_priority = DEFAULT_TEST_PRIORITY;
|
||||
static SDL_LogOutputFunction SDL_log_function = SDL_LogOutput;
|
||||
static void *SDL_log_userdata = NULL;
|
||||
static SDL_Mutex *log_function_mutex = NULL;
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
#endif
|
||||
|
||||
static const char *SDL_priority_prefixes[SDL_NUM_LOG_PRIORITIES] = {
|
||||
NULL,
|
||||
"VERBOSE",
|
||||
"DEBUG",
|
||||
"INFO",
|
||||
"WARN",
|
||||
"ERROR",
|
||||
"CRITICAL"
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
static const char *SDL_category_prefixes[] = {
|
||||
"APP",
|
||||
"ERROR",
|
||||
"ASSERT",
|
||||
"SYSTEM",
|
||||
"AUDIO",
|
||||
"VIDEO",
|
||||
"RENDER",
|
||||
"INPUT",
|
||||
"TEST"
|
||||
};
|
||||
|
||||
SDL_COMPILE_TIME_ASSERT(category_prefixes_enum, SDL_TABLESIZE(SDL_category_prefixes) == SDL_LOG_CATEGORY_RESERVED1);
|
||||
|
||||
static int SDL_android_priority[SDL_NUM_LOG_PRIORITIES] = {
|
||||
ANDROID_LOG_UNKNOWN,
|
||||
ANDROID_LOG_VERBOSE,
|
||||
ANDROID_LOG_DEBUG,
|
||||
ANDROID_LOG_INFO,
|
||||
ANDROID_LOG_WARN,
|
||||
ANDROID_LOG_ERROR,
|
||||
ANDROID_LOG_FATAL
|
||||
};
|
||||
#endif /* __ANDROID__ */
|
||||
|
||||
void SDL_InitLog(void)
|
||||
{
|
||||
if (log_function_mutex == NULL) {
|
||||
/* if this fails we'll try to continue without it. */
|
||||
log_function_mutex = SDL_CreateMutex();
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_QuitLog(void)
|
||||
{
|
||||
SDL_LogResetPriorities();
|
||||
if (log_function_mutex) {
|
||||
SDL_DestroyMutex(log_function_mutex);
|
||||
log_function_mutex = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_LogSetAllPriority(SDL_LogPriority priority)
|
||||
{
|
||||
SDL_LogLevel *entry;
|
||||
|
||||
for (entry = SDL_loglevels; entry; entry = entry->next) {
|
||||
entry->priority = priority;
|
||||
}
|
||||
SDL_default_priority = priority;
|
||||
SDL_assert_priority = priority;
|
||||
SDL_application_priority = priority;
|
||||
}
|
||||
|
||||
void SDL_LogSetPriority(int category, SDL_LogPriority priority)
|
||||
{
|
||||
SDL_LogLevel *entry;
|
||||
|
||||
for (entry = SDL_loglevels; entry; entry = entry->next) {
|
||||
if (entry->category == category) {
|
||||
entry->priority = priority;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a new entry */
|
||||
entry = (SDL_LogLevel *)SDL_malloc(sizeof(*entry));
|
||||
if (entry) {
|
||||
entry->category = category;
|
||||
entry->priority = priority;
|
||||
entry->next = SDL_loglevels;
|
||||
SDL_loglevels = entry;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_LogPriority SDL_LogGetPriority(int category)
|
||||
{
|
||||
SDL_LogLevel *entry;
|
||||
|
||||
for (entry = SDL_loglevels; entry; entry = entry->next) {
|
||||
if (entry->category == category) {
|
||||
return entry->priority;
|
||||
}
|
||||
}
|
||||
|
||||
if (category == SDL_LOG_CATEGORY_TEST) {
|
||||
return SDL_test_priority;
|
||||
} else if (category == SDL_LOG_CATEGORY_APPLICATION) {
|
||||
return SDL_application_priority;
|
||||
} else if (category == SDL_LOG_CATEGORY_ASSERT) {
|
||||
return SDL_assert_priority;
|
||||
} else {
|
||||
return SDL_default_priority;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_LogResetPriorities(void)
|
||||
{
|
||||
SDL_LogLevel *entry;
|
||||
|
||||
while (SDL_loglevels) {
|
||||
entry = SDL_loglevels;
|
||||
SDL_loglevels = entry->next;
|
||||
SDL_free(entry);
|
||||
}
|
||||
|
||||
SDL_default_priority = DEFAULT_PRIORITY;
|
||||
SDL_assert_priority = DEFAULT_ASSERT_PRIORITY;
|
||||
SDL_application_priority = DEFAULT_APPLICATION_PRIORITY;
|
||||
SDL_test_priority = DEFAULT_TEST_PRIORITY;
|
||||
}
|
||||
|
||||
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_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 __ANDROID__
|
||||
static const char *GetCategoryPrefix(int category)
|
||||
{
|
||||
if (category < SDL_LOG_CATEGORY_RESERVED1) {
|
||||
return SDL_category_prefixes[category];
|
||||
}
|
||||
if (category < SDL_LOG_CATEGORY_CUSTOM) {
|
||||
return "RESERVED";
|
||||
}
|
||||
return "CUSTOM";
|
||||
}
|
||||
#endif /* __ANDROID__ */
|
||||
|
||||
void SDL_LogMessageV(int category, SDL_LogPriority priority, 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;
|
||||
}
|
||||
|
||||
/* Make sure we don't exceed array bounds */
|
||||
if ((int)priority < 0 || priority >= SDL_NUM_LOG_PRIORITIES) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* See if we want to do anything with this message */
|
||||
if (priority < SDL_LogGetPriority(category)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (log_function_mutex == NULL) {
|
||||
/* this mutex creation can race if you log from two threads at startup. You should have called SDL_Init first! */
|
||||
log_function_mutex = SDL_CreateMutex();
|
||||
}
|
||||
|
||||
/* 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_overflow(len, 1, &len_plus_term) == 0) {
|
||||
/* Allocate exactly what we need, including the zero-terminator */
|
||||
message = (char *)SDL_malloc(len_plus_term);
|
||||
if (message == NULL) {
|
||||
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(log_function_mutex);
|
||||
SDL_log_function(SDL_log_userdata, category, priority, message);
|
||||
SDL_UnlockMutex(log_function_mutex);
|
||||
|
||||
/* Free only if dynamically allocated */
|
||||
if (message != stack_buf) {
|
||||
SDL_free(message);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__WIN32__) && !defined(HAVE_STDIO_H) && !defined(__WINRT__) && !defined(__GDK__)
|
||||
/* Flag tracking the attachment of the console: 0=unattached, 1=attached to a console, 2=attached to a file, -1=error */
|
||||
static int consoleAttached = 0;
|
||||
|
||||
/* 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(__WIN32__) || defined(__WINRT__) || defined(__GDK__)
|
||||
/* 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;
|
||||
SDL_bool isstack;
|
||||
|
||||
#if !defined(HAVE_STDIO_H) && !defined(__WINRT__) && !defined(__GDK__)
|
||||
BOOL attachResult;
|
||||
DWORD attachError;
|
||||
DWORD charsWritten;
|
||||
DWORD consoleMode;
|
||||
|
||||
/* Maybe attach console and get stderr handle */
|
||||
if (consoleAttached == 0) {
|
||||
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 = -1;
|
||||
} else if (attachError == ERROR_GEN_FAILURE) {
|
||||
OutputDebugString(TEXT("Could not attach to console of parent process\r\n"));
|
||||
consoleAttached = -1;
|
||||
} else if (attachError == ERROR_ACCESS_DENIED) {
|
||||
/* Already attached */
|
||||
consoleAttached = 1;
|
||||
} else {
|
||||
OutputDebugString(TEXT("Error attaching console\r\n"));
|
||||
consoleAttached = -1;
|
||||
}
|
||||
} else {
|
||||
/* Newly attached */
|
||||
consoleAttached = 1;
|
||||
}
|
||||
|
||||
if (consoleAttached == 1) {
|
||||
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 = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) && !defined(__GDK__) */
|
||||
|
||||
length = SDL_strlen(SDL_priority_prefixes[priority]) + 2 + SDL_strlen(message) + 1 + 1 + 1;
|
||||
output = SDL_small_alloc(char, length, &isstack);
|
||||
(void)SDL_snprintf(output, length, "%s: %s\r\n", SDL_priority_prefixes[priority], message);
|
||||
tstr = WIN_UTF8ToString(output);
|
||||
|
||||
/* Output to debugger */
|
||||
OutputDebugString(tstr);
|
||||
|
||||
#if !defined(HAVE_STDIO_H) && !defined(__WINRT__) && !defined(__GDK__)
|
||||
/* Screen output to stderr, if console was attached. */
|
||||
if (consoleAttached == 1) {
|
||||
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 == 2) {
|
||||
if (!WriteFile(stderrHandle, output, (DWORD)SDL_strlen(output), &charsWritten, NULL)) {
|
||||
OutputDebugString(TEXT("Error calling WriteFile\r\n"));
|
||||
}
|
||||
}
|
||||
#endif /* !defined(HAVE_STDIO_H) && !defined(__WINRT__) && !defined(__GDK__) */
|
||||
|
||||
SDL_free(tstr);
|
||||
SDL_small_free(output, isstack);
|
||||
}
|
||||
#elif defined(__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(__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(SDL_priority_prefixes[priority], message);
|
||||
return;
|
||||
}
|
||||
#elif defined(__PSP__) || defined(__PS2__)
|
||||
{
|
||||
FILE *pFile;
|
||||
pFile = fopen("SDL_Log.txt", "a");
|
||||
if (pFile != NULL) {
|
||||
(void)fprintf(pFile, "%s: %s\n", SDL_priority_prefixes[priority], message);
|
||||
(void)fclose(pFile);
|
||||
}
|
||||
}
|
||||
#elif defined(__VITA__)
|
||||
{
|
||||
FILE *pFile;
|
||||
pFile = fopen("ux0:/data/SDL_Log.txt", "a");
|
||||
if (pFile != NULL) {
|
||||
(void)fprintf(pFile, "%s: %s\n", SDL_priority_prefixes[priority], message);
|
||||
(void)fclose(pFile);
|
||||
}
|
||||
}
|
||||
#elif defined(__3DS__)
|
||||
{
|
||||
FILE *pFile;
|
||||
pFile = fopen("sdmc:/3ds/SDL_Log.txt", "a");
|
||||
if (pFile != NULL) {
|
||||
(void)fprintf(pFile, "%s: %s\n", SDL_priority_prefixes[priority], message);
|
||||
(void)fclose(pFile);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAVE_STDIO_H) && \
|
||||
!(defined(__APPLE__) && (defined(SDL_VIDEO_DRIVER_COCOA) || defined(SDL_VIDEO_DRIVER_UIKIT)))
|
||||
(void)fprintf(stderr, "%s: %s\n", SDL_priority_prefixes[priority], message);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SDL_LogGetOutputFunction(SDL_LogOutputFunction *callback, void **userdata)
|
||||
{
|
||||
if (callback) {
|
||||
*callback = SDL_log_function;
|
||||
}
|
||||
if (userdata) {
|
||||
*userdata = SDL_log_userdata;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_LogSetOutputFunction(SDL_LogOutputFunction callback, void *userdata)
|
||||
{
|
||||
SDL_log_function = callback;
|
||||
SDL_log_userdata = userdata;
|
||||
}
|
31
external/sdl/SDL/src/SDL_log_c.h
vendored
Normal file
31
external/sdl/SDL/src/SDL_log_c.h
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_ */
|
63
external/sdl/SDL/src/SDL_utils.c
vendored
Normal file
63
external/sdl/SDL/src/SDL_utils.c
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_utils_c.h"
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
SDL_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 SDL_TRUE;
|
||||
}
|
||||
}
|
||||
return SDL_FALSE;
|
||||
}
|
33
external/sdl/SDL/src/SDL_utils_c.h
vendored
Normal file
33
external/sdl/SDL/src/SDL_utils_c.h
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_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);
|
||||
|
||||
SDL_bool SDL_endswith(const char *string, const char *suffix);
|
||||
|
||||
#endif /* SDL_utils_h_ */
|
301
external/sdl/SDL/src/atomic/SDL_atomic.c
vendored
Normal file
301
external/sdl/SDL/src/atomic/SDL_atomic.c
vendored
Normal file
@ -0,0 +1,301 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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 __MACOS__ /* !!! FIXME: should we favor gcc atomics? */
|
||||
#include <libkern/OSAtomic.h>
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_GCC_ATOMICS) && defined(__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 __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(__MACOS__) && !defined(__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_AtomicLock(&locks[index]);
|
||||
}
|
||||
|
||||
static SDL_INLINE void leaveLock(void *a)
|
||||
{
|
||||
uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);
|
||||
|
||||
SDL_AtomicUnlock(&locks[index]);
|
||||
}
|
||||
#endif
|
||||
|
||||
SDL_bool SDL_AtomicCAS(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_bool)_SDL_cmpxchg_watcom(&a->value, newval, oldval);
|
||||
#elif defined(HAVE_GCC_ATOMICS)
|
||||
return (SDL_bool)__sync_bool_compare_and_swap(&a->value, oldval, newval);
|
||||
#elif defined(__MACOS__) /* this is deprecated in 10.12 sdk; favor gcc atomics. */
|
||||
return (SDL_bool)OSAtomicCompareAndSwap32Barrier(oldval, newval, &a->value);
|
||||
#elif defined(__SOLARIS__)
|
||||
return (SDL_bool)((int)atomic_cas_uint((volatile uint_t *)&a->value, (uint_t)oldval, (uint_t)newval) == oldval);
|
||||
#elif defined(EMULATE_CAS)
|
||||
SDL_bool retval = SDL_FALSE;
|
||||
|
||||
enterLock(a);
|
||||
if (a->value == oldval) {
|
||||
a->value = newval;
|
||||
retval = SDL_TRUE;
|
||||
}
|
||||
leaveLock(a);
|
||||
|
||||
return retval;
|
||||
#else
|
||||
#error Please define your platform.
|
||||
#endif
|
||||
}
|
||||
|
||||
SDL_bool SDL_AtomicCASPtr(void **a, void *oldval, void *newval)
|
||||
{
|
||||
#ifdef HAVE_MSC_ATOMICS
|
||||
return _InterlockedCompareExchangePointer(a, newval, oldval) == oldval;
|
||||
#elif defined(HAVE_WATCOM_ATOMICS)
|
||||
return (SDL_bool)_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(__MACOS__) && defined(__LP64__) /* this is deprecated in 10.12 sdk; favor gcc atomics. */
|
||||
return (SDL_bool)OSAtomicCompareAndSwap64Barrier((int64_t)oldval, (int64_t)newval, (int64_t *)a);
|
||||
#elif defined(__MACOS__) && !defined(__LP64__) /* this is deprecated in 10.12 sdk; favor gcc atomics. */
|
||||
return (SDL_bool)OSAtomicCompareAndSwap32Barrier((int32_t)oldval, (int32_t)newval, (int32_t *)a);
|
||||
#elif defined(__SOLARIS__)
|
||||
return (SDL_bool)(atomic_cas_ptr(a, oldval, newval) == oldval);
|
||||
#elif defined(EMULATE_CAS)
|
||||
SDL_bool retval = SDL_FALSE;
|
||||
|
||||
enterLock(a);
|
||||
if (*a == oldval) {
|
||||
*a = newval;
|
||||
retval = SDL_TRUE;
|
||||
}
|
||||
leaveLock(a);
|
||||
|
||||
return retval;
|
||||
#else
|
||||
#error Please define your platform.
|
||||
#endif
|
||||
}
|
||||
|
||||
int SDL_AtomicSet(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(__SOLARIS__)
|
||||
return (int)atomic_swap_uint((volatile uint_t *)&a->value, v);
|
||||
#else
|
||||
int value;
|
||||
do {
|
||||
value = a->value;
|
||||
} while (!SDL_AtomicCAS(a, value, v));
|
||||
return value;
|
||||
#endif
|
||||
}
|
||||
|
||||
void *SDL_AtomicSetPtr(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(__SOLARIS__)
|
||||
return atomic_swap_ptr(a, v);
|
||||
#else
|
||||
void *value;
|
||||
do {
|
||||
value = *a;
|
||||
} while (!SDL_AtomicCASPtr(a, value, v));
|
||||
return value;
|
||||
#endif
|
||||
}
|
||||
|
||||
int SDL_AtomicAdd(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)
|
||||
return _SDL_xadd_watcom(&a->value, v);
|
||||
#elif defined(HAVE_GCC_ATOMICS)
|
||||
return __sync_fetch_and_add(&a->value, v);
|
||||
#elif defined(__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_AtomicCAS(a, value, (value + v)));
|
||||
return value;
|
||||
#endif
|
||||
}
|
||||
|
||||
int SDL_AtomicGet(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(__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(__SOLARIS__)
|
||||
return atomic_or_uint((volatile uint_t *)&a->value, 0);
|
||||
#else
|
||||
int value;
|
||||
do {
|
||||
value = a->value;
|
||||
} while (!SDL_AtomicCAS(a, value, value));
|
||||
return value;
|
||||
#endif
|
||||
}
|
||||
|
||||
void *SDL_AtomicGetPtr(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(__SOLARIS__)
|
||||
return atomic_cas_ptr(a, (void *)0, (void *)0);
|
||||
#else
|
||||
void *value;
|
||||
do {
|
||||
value = *a;
|
||||
} while (!SDL_AtomicCASPtr(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();
|
||||
}
|
207
external/sdl/SDL/src/atomic/SDL_spinlock.c
vendored
Normal file
207
external/sdl/SDL/src/atomic/SDL_spinlock.c
vendored
Normal file
@ -0,0 +1,207 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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(__WIN32__) || defined(__WINRT__) || defined(__GDK__)
|
||||
#include "../core/windows/SDL_windows.h"
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_GCC_ATOMICS) && defined(__SOLARIS__)
|
||||
#include <atomic.h>
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_GCC_ATOMICS) && defined(__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(__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... */
|
||||
SDL_bool SDL_AtomicTryLock(SDL_SpinLock *lock)
|
||||
{
|
||||
#ifdef SDL_ATOMIC_DISABLED
|
||||
/* Terrible terrible damage */
|
||||
static SDL_Mutex *_spinlock_mutex;
|
||||
|
||||
if (_spinlock_mutex == NULL) {
|
||||
/* Race condition on first lock... */
|
||||
_spinlock_mutex = SDL_CreateMutex();
|
||||
}
|
||||
SDL_LockMutex(_spinlock_mutex);
|
||||
if (*lock == 0) {
|
||||
*lock = 1;
|
||||
SDL_UnlockMutex(_spinlock_mutex);
|
||||
return SDL_TRUE;
|
||||
} else {
|
||||
SDL_UnlockMutex(_spinlock_mutex);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
#elif 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 __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(__MACOS__) || defined(__IOS__) || defined(__TVOS__)
|
||||
/* Maybe used for PowerPC, but the Intel asm or gcc atomics are favored. */
|
||||
return OSAtomicCompareAndSwap32Barrier(0, 1, lock);
|
||||
|
||||
#elif defined(__SOLARIS__) && defined(_LP64)
|
||||
/* Used for Solaris with non-gcc compilers. */
|
||||
return (SDL_bool)((int)atomic_cas_64((volatile uint64_t *)lock, 0, 1) == 0);
|
||||
|
||||
#elif defined(__SOLARIS__) && !defined(_LP64)
|
||||
/* Used for Solaris with non-gcc compilers. */
|
||||
return (SDL_bool)((int)atomic_cas_32((volatile uint32_t *)lock, 0, 1) == 0);
|
||||
#elif defined(PS2)
|
||||
uint32_t oldintr;
|
||||
SDL_bool res = SDL_FALSE;
|
||||
// disable interuption
|
||||
oldintr = DIntr();
|
||||
|
||||
if (*lock == 0) {
|
||||
*lock = 1;
|
||||
res = SDL_TRUE;
|
||||
}
|
||||
// enable interuption
|
||||
if (oldintr) {
|
||||
EIntr();
|
||||
}
|
||||
return res;
|
||||
#else
|
||||
#error Please implement for your platform.
|
||||
return SDL_FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SDL_AtomicLock(SDL_SpinLock *lock)
|
||||
{
|
||||
int iterations = 0;
|
||||
/* FIXME: Should we have an eventual timeout? */
|
||||
while (!SDL_AtomicTryLock(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_AtomicUnlock(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(__SOLARIS__)
|
||||
/* Used for Solaris when not using gcc. */
|
||||
*lock = 0;
|
||||
membar_producer();
|
||||
|
||||
#else
|
||||
*lock = 0;
|
||||
#endif
|
||||
}
|
1583
external/sdl/SDL/src/audio/SDL_audio.c
vendored
Normal file
1583
external/sdl/SDL/src/audio/SDL_audio.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
80
external/sdl/SDL/src/audio/SDL_audio_c.h
vendored
Normal file
80
external/sdl/SDL/src/audio/SDL_audio_c.h
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_audio_c_h_
|
||||
#define SDL_audio_c_h_
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#define DEBUG_AUDIOSTREAM 0
|
||||
#define DEBUG_AUDIO_CONVERT 0
|
||||
|
||||
#if DEBUG_AUDIO_CONVERT
|
||||
#define LOG_DEBUG_AUDIO_CONVERT(from, to) SDL_Log("SDL_AUDIO_CONVERT: Converting %s to %s.\n", from, to);
|
||||
#else
|
||||
#define LOG_DEBUG_AUDIO_CONVERT(from, to)
|
||||
#endif
|
||||
|
||||
/* Functions and variables exported from SDL_audio.c for SDL_sysaudio.c */
|
||||
|
||||
/* Function to get a list of audio formats, ordered most similar to `format` to least, 0-terminated. Don't free results. */
|
||||
const SDL_AudioFormat *SDL_ClosestAudioFormats(SDL_AudioFormat format);
|
||||
|
||||
/* Function to calculate the size and silence for a SDL_AudioSpec */
|
||||
extern Uint8 SDL_GetSilenceValueForFormat(const SDL_AudioFormat format);
|
||||
extern void SDL_CalculateAudioSpec(SDL_AudioSpec *spec);
|
||||
|
||||
/* Must be called at least once before using converters (SDL_CreateAudioStream will call it). */
|
||||
extern void SDL_ChooseAudioConverters(void);
|
||||
|
||||
/* These pointers get set during SDL_ChooseAudioConverters() to various SIMD implementations. */
|
||||
extern void (*SDL_Convert_S8_to_F32)(float *dst, const Sint8 *src, int num_samples);
|
||||
extern void (*SDL_Convert_U8_to_F32)(float *dst, const Uint8 *src, int num_samples);
|
||||
extern void (*SDL_Convert_S16_to_F32)(float *dst, const Sint16 *src, int num_samples);
|
||||
extern void (*SDL_Convert_S32_to_F32)(float *dst, const Sint32 *src, int num_samples);
|
||||
extern void (*SDL_Convert_F32_to_S8)(Sint8 *dst, const float *src, int num_samples);
|
||||
extern void (*SDL_Convert_F32_to_U8)(Uint8 *dst, const float *src, int num_samples);
|
||||
extern void (*SDL_Convert_F32_to_S16)(Sint16 *dst, const float *src, int num_samples);
|
||||
extern void (*SDL_Convert_F32_to_S32)(Sint32 *dst, const float *src, int num_samples);
|
||||
|
||||
/**
|
||||
* Use this function to initialize a particular audio driver.
|
||||
*
|
||||
* This function is used internally, and should not be used unless you have a
|
||||
* specific need to designate the audio driver you want to use. You should
|
||||
* normally use SDL_Init() or SDL_InitSubSystem().
|
||||
*
|
||||
* \param driver_name the name of the desired audio driver
|
||||
* \returns 0 on success or a negative error code on failure; call
|
||||
* SDL_GetError() for more information.
|
||||
*/
|
||||
extern int SDL_InitAudio(const char *driver_name);
|
||||
|
||||
/**
|
||||
* Use this function to shut down audio if you initialized it with SDL_InitAudio().
|
||||
*
|
||||
* This function is used internally, and should not be used unless you have a
|
||||
* specific need to specify the audio driver you want to use. You should
|
||||
* normally use SDL_Quit() or SDL_QuitSubSystem().
|
||||
*/
|
||||
extern void SDL_QuitAudio(void);
|
||||
|
||||
#endif /* SDL_audio_c_h_ */
|
1068
external/sdl/SDL/src/audio/SDL_audio_channel_converters.h
vendored
Normal file
1068
external/sdl/SDL/src/audio/SDL_audio_channel_converters.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1059
external/sdl/SDL/src/audio/SDL_audio_resampler_filter.h
vendored
Normal file
1059
external/sdl/SDL/src/audio/SDL_audio_resampler_filter.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1122
external/sdl/SDL/src/audio/SDL_audiocvt.c
vendored
Normal file
1122
external/sdl/SDL/src/audio/SDL_audiocvt.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
125
external/sdl/SDL/src/audio/SDL_audiodev.c
vendored
Normal file
125
external/sdl/SDL/src/audio/SDL_audiodev.c
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
/* Get the name of the audio device we use for output */
|
||||
|
||||
#if defined(SDL_AUDIO_DRIVER_NETBSD) || defined(SDL_AUDIO_DRIVER_OSS)
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h> /* For close() */
|
||||
|
||||
#include "SDL_audiodev_c.h"
|
||||
|
||||
#ifndef SDL_PATH_DEV_DSP
|
||||
#if defined(__NETBSD__) || defined(__OPENBSD__)
|
||||
#define SDL_PATH_DEV_DSP "/dev/audio"
|
||||
#else
|
||||
#define SDL_PATH_DEV_DSP "/dev/dsp"
|
||||
#endif
|
||||
#endif
|
||||
#ifndef SDL_PATH_DEV_DSP24
|
||||
#define SDL_PATH_DEV_DSP24 "/dev/sound/dsp"
|
||||
#endif
|
||||
#ifndef SDL_PATH_DEV_AUDIO
|
||||
#define SDL_PATH_DEV_AUDIO "/dev/audio"
|
||||
#endif
|
||||
|
||||
static void test_device(const int iscapture, const char *fname, int flags, int (*test)(int fd))
|
||||
{
|
||||
struct stat sb;
|
||||
if ((stat(fname, &sb) == 0) && (S_ISCHR(sb.st_mode))) {
|
||||
const int audio_fd = open(fname, flags | O_CLOEXEC, 0);
|
||||
if (audio_fd >= 0) {
|
||||
const int okay = test(audio_fd);
|
||||
close(audio_fd);
|
||||
if (okay) {
|
||||
static size_t dummyhandle = 0;
|
||||
dummyhandle++;
|
||||
SDL_assert(dummyhandle != 0);
|
||||
|
||||
/* Note that spec is NULL; while we are opening the device
|
||||
* endpoint here, the endpoint does not provide any mix format
|
||||
* information, making this information inaccessible at
|
||||
* enumeration time
|
||||
*/
|
||||
SDL_AddAudioDevice(iscapture, fname, NULL, (void *)(uintptr_t)dummyhandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int test_stub(int fd)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void SDL_EnumUnixAudioDevices_Internal(const int iscapture, const int classic, int (*test)(int))
|
||||
{
|
||||
const int flags = iscapture ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT;
|
||||
const char *audiodev;
|
||||
char audiopath[1024];
|
||||
|
||||
if (test == NULL) {
|
||||
test = test_stub;
|
||||
}
|
||||
|
||||
/* Figure out what our audio device is */
|
||||
audiodev = SDL_getenv("SDL_PATH_DSP");
|
||||
if (audiodev == NULL) {
|
||||
audiodev = SDL_getenv("AUDIODEV");
|
||||
}
|
||||
if (audiodev == NULL) {
|
||||
if (classic) {
|
||||
audiodev = SDL_PATH_DEV_AUDIO;
|
||||
} else {
|
||||
struct stat sb;
|
||||
|
||||
/* Added support for /dev/sound/\* in Linux 2.4 */
|
||||
if (((stat("/dev/sound", &sb) == 0) && S_ISDIR(sb.st_mode)) && ((stat(SDL_PATH_DEV_DSP24, &sb) == 0) && S_ISCHR(sb.st_mode))) {
|
||||
audiodev = SDL_PATH_DEV_DSP24;
|
||||
} else {
|
||||
audiodev = SDL_PATH_DEV_DSP;
|
||||
}
|
||||
}
|
||||
}
|
||||
test_device(iscapture, audiodev, flags, test);
|
||||
|
||||
if (SDL_strlen(audiodev) < (sizeof(audiopath) - 3)) {
|
||||
int instance = 0;
|
||||
while (instance <= 64) {
|
||||
(void)SDL_snprintf(audiopath, SDL_arraysize(audiopath),
|
||||
"%s%d", audiodev, instance);
|
||||
instance++;
|
||||
test_device(iscapture, audiopath, flags, test);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_EnumUnixAudioDevices(const int classic, int (*test)(int))
|
||||
{
|
||||
SDL_EnumUnixAudioDevices_Internal(SDL_TRUE, classic, test);
|
||||
SDL_EnumUnixAudioDevices_Internal(SDL_FALSE, classic, test);
|
||||
}
|
||||
|
||||
#endif /* Audio driver selection */
|
41
external/sdl/SDL/src/audio/SDL_audiodev_c.h
vendored
Normal file
41
external/sdl/SDL/src/audio/SDL_audiodev_c.h
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_audiodev_c_h_
|
||||
#define SDL_audiodev_c_h_
|
||||
|
||||
#include "SDL_internal.h"
|
||||
#include "SDL_sysaudio.h"
|
||||
|
||||
/* Open the audio device for playback, and don't block if busy */
|
||||
/* #define USE_BLOCKING_WRITES */
|
||||
|
||||
#ifdef USE_BLOCKING_WRITES
|
||||
#define OPEN_FLAGS_OUTPUT O_WRONLY
|
||||
#define OPEN_FLAGS_INPUT O_RDONLY
|
||||
#else
|
||||
#define OPEN_FLAGS_OUTPUT (O_WRONLY | O_NONBLOCK)
|
||||
#define OPEN_FLAGS_INPUT (O_RDONLY | O_NONBLOCK)
|
||||
#endif
|
||||
|
||||
extern void SDL_EnumUnixAudioDevices(const int classic, int (*test)(int));
|
||||
|
||||
#endif /* SDL_audiodev_c_h_ */
|
978
external/sdl/SDL/src/audio/SDL_audiotypecvt.c
vendored
Normal file
978
external/sdl/SDL/src/audio/SDL_audiotypecvt.c
vendored
Normal file
@ -0,0 +1,978 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_audio_c.h"
|
||||
|
||||
#ifndef SDL_CPUINFO_DISABLED
|
||||
#if defined(__x86_64__) && defined(SDL_SSE2_INTRINSICS)
|
||||
#define NEED_SCALAR_CONVERTER_FALLBACKS 0 /* x86_64 guarantees SSE2. */
|
||||
#elif defined(__MACOS__) && defined(SDL_SSE2_INTRINSICS)
|
||||
#define NEED_SCALAR_CONVERTER_FALLBACKS 0 /* macOS/Intel guarantees SSE2. */
|
||||
#elif defined(__ARM_ARCH) && (__ARM_ARCH >= 8) && defined(SDL_NEON_INTRINSICS)
|
||||
#define NEED_SCALAR_CONVERTER_FALLBACKS 0 /* ARMv8+ promise NEON. */
|
||||
#elif defined(__APPLE__) && defined(__ARM_ARCH) && (__ARM_ARCH >= 7) && defined(SDL_NEON_INTRINSICS)
|
||||
#define NEED_SCALAR_CONVERTER_FALLBACKS 0 /* All Apple ARMv7 chips promise NEON support. */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Set to zero if platform is guaranteed to use a SIMD codepath here. */
|
||||
#if !defined(NEED_SCALAR_CONVERTER_FALLBACKS) || defined(SDL_CPUINFO_DISABLED)
|
||||
#define NEED_SCALAR_CONVERTER_FALLBACKS 1
|
||||
#endif
|
||||
|
||||
#define DIVBY128 0.0078125f
|
||||
#define DIVBY32768 0.000030517578125f
|
||||
#define DIVBY8388607 0.00000011920930376163766f
|
||||
|
||||
#if NEED_SCALAR_CONVERTER_FALLBACKS
|
||||
|
||||
/* these all convert backwards because (currently) float32 is >= to the size of anything it converts to, so it lets us safely convert in-place. */
|
||||
#define AUDIOCVT_TOFLOAT_SCALAR(from, fromtype, equation) \
|
||||
static void SDL_Convert_##from##_to_F32_Scalar(float *dst, const fromtype *src, int num_samples) { \
|
||||
int i; \
|
||||
LOG_DEBUG_AUDIO_CONVERT(#from, "F32"); \
|
||||
for (i = num_samples - 1; i >= 0; --i) { \
|
||||
dst[i] = equation; \
|
||||
} \
|
||||
}
|
||||
|
||||
AUDIOCVT_TOFLOAT_SCALAR(S8, Sint8, ((float)src[i]) * DIVBY128)
|
||||
AUDIOCVT_TOFLOAT_SCALAR(U8, Uint8, (((float)src[i]) * DIVBY128) - 1.0f)
|
||||
AUDIOCVT_TOFLOAT_SCALAR(S16, Sint16, ((float)src[i]) * DIVBY32768)
|
||||
AUDIOCVT_TOFLOAT_SCALAR(S32, Sint32, ((float)(src[i] >> 8)) * DIVBY8388607)
|
||||
#undef AUDIOCVT_FROMFLOAT_SCALAR
|
||||
|
||||
/* these all convert forwards because (currently) float32 is >= to the size of anything it converts from, so it lets us safely convert in-place. */
|
||||
#define AUDIOCVT_FROMFLOAT_SCALAR(to, totype, clampmin, clampmax, equation) \
|
||||
static void SDL_Convert_F32_to_##to##_Scalar(totype *dst, const float *src, int num_samples) { \
|
||||
int i; \
|
||||
LOG_DEBUG_AUDIO_CONVERT("F32", #to); \
|
||||
for (i = 0; i < num_samples; i++) { \
|
||||
const float sample = src[i]; \
|
||||
if (sample >= 1.0f) { \
|
||||
dst[i] = (totype) (clampmax); \
|
||||
} else if (sample <= -1.0f) { \
|
||||
dst[i] = (totype) (clampmin); \
|
||||
} else { \
|
||||
dst[i] = (totype) (equation); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
AUDIOCVT_FROMFLOAT_SCALAR(S8, Sint8, -128, 127, sample * 127.0f);
|
||||
AUDIOCVT_FROMFLOAT_SCALAR(U8, Uint8, 0, 255, (sample + 1.0f) * 127.0f);
|
||||
AUDIOCVT_FROMFLOAT_SCALAR(S16, Sint16, -32768, 32767, sample * 32767.0f);
|
||||
AUDIOCVT_FROMFLOAT_SCALAR(S32, Sint32, -2147483648LL, 2147483647, ((Sint32)(sample * 8388607.0f)) << 8);
|
||||
#undef AUDIOCVT_FROMFLOAT_SCALAR
|
||||
|
||||
#endif /* NEED_SCALAR_CONVERTER_FALLBACKS */
|
||||
|
||||
#ifdef SDL_SSE2_INTRINSICS
|
||||
static void SDL_TARGETING("sse2") SDL_Convert_S8_to_F32_SSE2(float *dst, const Sint8 *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("S8", "F32 (using SSE2)");
|
||||
|
||||
src += num_samples - 1;
|
||||
dst += num_samples - 1;
|
||||
|
||||
/* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
|
||||
for (i = num_samples; i && (((size_t)(dst - 15)) & 15); --i, --src, --dst) {
|
||||
*dst = ((float)*src) * DIVBY128;
|
||||
}
|
||||
|
||||
src -= 15;
|
||||
dst -= 15; /* adjust to read SSE blocks from the start. */
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
|
||||
const __m128i *mmsrc = (const __m128i *)src;
|
||||
const __m128i zero = _mm_setzero_si128();
|
||||
const __m128 divby128 = _mm_set1_ps(DIVBY128);
|
||||
while (i >= 16) { /* 16 * 8-bit */
|
||||
const __m128i bytes = _mm_load_si128(mmsrc); /* get 16 sint8 into an XMM register. */
|
||||
/* treat as int16, shift left to clear every other sint16, then back right with sign-extend. Now sint16. */
|
||||
const __m128i shorts1 = _mm_srai_epi16(_mm_slli_epi16(bytes, 8), 8);
|
||||
/* right-shift-sign-extend gets us sint16 with the other set of values. */
|
||||
const __m128i shorts2 = _mm_srai_epi16(bytes, 8);
|
||||
/* unpack against zero to make these int32, shift to make them sign-extend, convert to float, multiply. Whew! */
|
||||
const __m128 floats1 = _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_slli_epi32(_mm_unpacklo_epi16(shorts1, zero), 16), 16)), divby128);
|
||||
const __m128 floats2 = _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_slli_epi32(_mm_unpacklo_epi16(shorts2, zero), 16), 16)), divby128);
|
||||
const __m128 floats3 = _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_slli_epi32(_mm_unpackhi_epi16(shorts1, zero), 16), 16)), divby128);
|
||||
const __m128 floats4 = _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_slli_epi32(_mm_unpackhi_epi16(shorts2, zero), 16), 16)), divby128);
|
||||
/* Interleave back into correct order, store. */
|
||||
_mm_store_ps(dst, _mm_unpacklo_ps(floats1, floats2));
|
||||
_mm_store_ps(dst + 4, _mm_unpackhi_ps(floats1, floats2));
|
||||
_mm_store_ps(dst + 8, _mm_unpacklo_ps(floats3, floats4));
|
||||
_mm_store_ps(dst + 12, _mm_unpackhi_ps(floats3, floats4));
|
||||
i -= 16;
|
||||
mmsrc--;
|
||||
dst -= 16;
|
||||
}
|
||||
|
||||
src = (const Sint8 *)mmsrc;
|
||||
}
|
||||
|
||||
src += 15;
|
||||
dst += 15; /* adjust for any scalar finishing. */
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
*dst = ((float)*src) * DIVBY128;
|
||||
i--;
|
||||
src--;
|
||||
dst--;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_TARGETING("sse2") SDL_Convert_U8_to_F32_SSE2(float *dst, const Uint8 *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("U8", "F32 (using SSE2)");
|
||||
|
||||
src += num_samples - 1;
|
||||
dst += num_samples - 1;
|
||||
|
||||
/* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
|
||||
for (i = num_samples; i && (((size_t)(dst - 15)) & 15); --i, --src, --dst) {
|
||||
*dst = (((float)*src) * DIVBY128) - 1.0f;
|
||||
}
|
||||
|
||||
src -= 15;
|
||||
dst -= 15; /* adjust to read SSE blocks from the start. */
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
|
||||
const __m128i *mmsrc = (const __m128i *)src;
|
||||
const __m128i zero = _mm_setzero_si128();
|
||||
const __m128 divby128 = _mm_set1_ps(DIVBY128);
|
||||
const __m128 minus1 = _mm_set1_ps(-1.0f);
|
||||
while (i >= 16) { /* 16 * 8-bit */
|
||||
const __m128i bytes = _mm_load_si128(mmsrc); /* get 16 uint8 into an XMM register. */
|
||||
/* treat as int16, shift left to clear every other sint16, then back right with zero-extend. Now uint16. */
|
||||
const __m128i shorts1 = _mm_srli_epi16(_mm_slli_epi16(bytes, 8), 8);
|
||||
/* right-shift-zero-extend gets us uint16 with the other set of values. */
|
||||
const __m128i shorts2 = _mm_srli_epi16(bytes, 8);
|
||||
/* unpack against zero to make these int32, convert to float, multiply, add. Whew! */
|
||||
/* Note that AVX2 can do floating point multiply+add in one instruction, fwiw. SSE2 cannot. */
|
||||
const __m128 floats1 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(shorts1, zero)), divby128), minus1);
|
||||
const __m128 floats2 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi16(shorts2, zero)), divby128), minus1);
|
||||
const __m128 floats3 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi16(shorts1, zero)), divby128), minus1);
|
||||
const __m128 floats4 = _mm_add_ps(_mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi16(shorts2, zero)), divby128), minus1);
|
||||
/* Interleave back into correct order, store. */
|
||||
_mm_store_ps(dst, _mm_unpacklo_ps(floats1, floats2));
|
||||
_mm_store_ps(dst + 4, _mm_unpackhi_ps(floats1, floats2));
|
||||
_mm_store_ps(dst + 8, _mm_unpacklo_ps(floats3, floats4));
|
||||
_mm_store_ps(dst + 12, _mm_unpackhi_ps(floats3, floats4));
|
||||
i -= 16;
|
||||
mmsrc--;
|
||||
dst -= 16;
|
||||
}
|
||||
|
||||
src = (const Uint8 *)mmsrc;
|
||||
}
|
||||
|
||||
src += 15;
|
||||
dst += 15; /* adjust for any scalar finishing. */
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
*dst = (((float)*src) * DIVBY128) - 1.0f;
|
||||
i--;
|
||||
src--;
|
||||
dst--;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_TARGETING("sse2") SDL_Convert_S16_to_F32_SSE2(float *dst, const Sint16 *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("S16", "F32 (using SSE2)");
|
||||
|
||||
src += num_samples - 1;
|
||||
dst += num_samples - 1;
|
||||
|
||||
/* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
|
||||
for (i = num_samples; i && (((size_t)(dst - 7)) & 15); --i, --src, --dst) {
|
||||
*dst = ((float)*src) * DIVBY32768;
|
||||
}
|
||||
|
||||
src -= 7;
|
||||
dst -= 7; /* adjust to read SSE blocks from the start. */
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
|
||||
const __m128 divby32768 = _mm_set1_ps(DIVBY32768);
|
||||
while (i >= 8) { /* 8 * 16-bit */
|
||||
const __m128i ints = _mm_load_si128((__m128i const *)src); /* get 8 sint16 into an XMM register. */
|
||||
/* treat as int32, shift left to clear every other sint16, then back right with sign-extend. Now sint32. */
|
||||
const __m128i a = _mm_srai_epi32(_mm_slli_epi32(ints, 16), 16);
|
||||
/* right-shift-sign-extend gets us sint32 with the other set of values. */
|
||||
const __m128i b = _mm_srai_epi32(ints, 16);
|
||||
/* Interleave these back into the right order, convert to float, multiply, store. */
|
||||
_mm_store_ps(dst, _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpacklo_epi32(a, b)), divby32768));
|
||||
_mm_store_ps(dst + 4, _mm_mul_ps(_mm_cvtepi32_ps(_mm_unpackhi_epi32(a, b)), divby32768));
|
||||
i -= 8;
|
||||
src -= 8;
|
||||
dst -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
src += 7;
|
||||
dst += 7; /* adjust for any scalar finishing. */
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
*dst = ((float)*src) * DIVBY32768;
|
||||
i--;
|
||||
src--;
|
||||
dst--;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_TARGETING("sse2") SDL_Convert_S32_to_F32_SSE2(float *dst, const Sint32 *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("S32", "F32 (using SSE2)");
|
||||
|
||||
/* Get dst aligned to 16 bytes */
|
||||
for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
|
||||
*dst = ((float)(*src >> 8)) * DIVBY8388607;
|
||||
}
|
||||
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
|
||||
const __m128 divby8388607 = _mm_set1_ps(DIVBY8388607);
|
||||
const __m128i *mmsrc = (const __m128i *)src;
|
||||
while (i >= 4) { /* 4 * sint32 */
|
||||
/* shift out lowest bits so int fits in a float32. Small precision loss, but much faster. */
|
||||
_mm_store_ps(dst, _mm_mul_ps(_mm_cvtepi32_ps(_mm_srai_epi32(_mm_load_si128(mmsrc), 8)), divby8388607));
|
||||
i -= 4;
|
||||
mmsrc++;
|
||||
dst += 4;
|
||||
}
|
||||
src = (const Sint32 *)mmsrc;
|
||||
}
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
*dst = ((float)(*src >> 8)) * DIVBY8388607;
|
||||
i--;
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_TARGETING("sse2") SDL_Convert_F32_to_S8_SSE2(Sint8 *dst, const float *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("F32", "S8 (using SSE2)");
|
||||
|
||||
/* Get dst aligned to 16 bytes */
|
||||
for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 127;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = -128;
|
||||
} else {
|
||||
*dst = (Sint8)(sample * 127.0f);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
|
||||
const __m128 one = _mm_set1_ps(1.0f);
|
||||
const __m128 negone = _mm_set1_ps(-1.0f);
|
||||
const __m128 mulby127 = _mm_set1_ps(127.0f);
|
||||
__m128i *mmdst = (__m128i *)dst;
|
||||
while (i >= 16) { /* 16 * float32 */
|
||||
const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
|
||||
const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 4)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
|
||||
const __m128i ints3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 8)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
|
||||
const __m128i ints4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 12)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
|
||||
_mm_store_si128(mmdst, _mm_packs_epi16(_mm_packs_epi32(ints1, ints2), _mm_packs_epi32(ints3, ints4))); /* pack down, store out. */
|
||||
i -= 16;
|
||||
src += 16;
|
||||
mmdst++;
|
||||
}
|
||||
dst = (Sint8 *)mmdst;
|
||||
}
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 127;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = -128;
|
||||
} else {
|
||||
*dst = (Sint8)(sample * 127.0f);
|
||||
}
|
||||
i--;
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_TARGETING("sse2") SDL_Convert_F32_to_U8_SSE2(Uint8 *dst, const float *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("F32", "U8 (using SSE2)");
|
||||
|
||||
/* Get dst aligned to 16 bytes */
|
||||
for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 255;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = 0;
|
||||
} else {
|
||||
*dst = (Uint8)((sample + 1.0f) * 127.0f);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
|
||||
const __m128 one = _mm_set1_ps(1.0f);
|
||||
const __m128 negone = _mm_set1_ps(-1.0f);
|
||||
const __m128 mulby127 = _mm_set1_ps(127.0f);
|
||||
__m128i *mmdst = (__m128i *)dst;
|
||||
while (i >= 16) { /* 16 * float32 */
|
||||
const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
|
||||
const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 4)), one), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
|
||||
const __m128i ints3 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 8)), one), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
|
||||
const __m128i ints4 = _mm_cvtps_epi32(_mm_mul_ps(_mm_add_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 12)), one), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
|
||||
_mm_store_si128(mmdst, _mm_packus_epi16(_mm_packs_epi32(ints1, ints2), _mm_packs_epi32(ints3, ints4))); /* pack down, store out. */
|
||||
i -= 16;
|
||||
src += 16;
|
||||
mmdst++;
|
||||
}
|
||||
dst = (Uint8 *)mmdst;
|
||||
}
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 255;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = 0;
|
||||
} else {
|
||||
*dst = (Uint8)((sample + 1.0f) * 127.0f);
|
||||
}
|
||||
i--;
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_TARGETING("sse2") SDL_Convert_F32_to_S16_SSE2(Sint16 *dst, const float *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("F32", "S16 (using SSE2)");
|
||||
|
||||
/* Get dst aligned to 16 bytes */
|
||||
for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 32767;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = -32768;
|
||||
} else {
|
||||
*dst = (Sint16)(sample * 32767.0f);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
|
||||
const __m128 one = _mm_set1_ps(1.0f);
|
||||
const __m128 negone = _mm_set1_ps(-1.0f);
|
||||
const __m128 mulby32767 = _mm_set1_ps(32767.0f);
|
||||
__m128i *mmdst = (__m128i *)dst;
|
||||
while (i >= 8) { /* 8 * float32 */
|
||||
const __m128i ints1 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), mulby32767)); /* load 4 floats, clamp, convert to sint32 */
|
||||
const __m128i ints2 = _mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src + 4)), one), mulby32767)); /* load 4 floats, clamp, convert to sint32 */
|
||||
_mm_store_si128(mmdst, _mm_packs_epi32(ints1, ints2)); /* pack to sint16, store out. */
|
||||
i -= 8;
|
||||
src += 8;
|
||||
mmdst++;
|
||||
}
|
||||
dst = (Sint16 *)mmdst;
|
||||
}
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 32767;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = -32768;
|
||||
} else {
|
||||
*dst = (Sint16)(sample * 32767.0f);
|
||||
}
|
||||
i--;
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_TARGETING("sse2") SDL_Convert_F32_to_S32_SSE2(Sint32 *dst, const float *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("F32", "S32 (using SSE2)");
|
||||
|
||||
/* Get dst aligned to 16 bytes */
|
||||
for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 2147483647;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = (Sint32)-2147483648LL;
|
||||
} else {
|
||||
*dst = ((Sint32)(sample * 8388607.0f)) << 8;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
SDL_assert(!i || !(((size_t)src) & 15));
|
||||
|
||||
{
|
||||
/* Aligned! Do SSE blocks as long as we have 16 bytes available. */
|
||||
const __m128 one = _mm_set1_ps(1.0f);
|
||||
const __m128 negone = _mm_set1_ps(-1.0f);
|
||||
const __m128 mulby8388607 = _mm_set1_ps(8388607.0f);
|
||||
__m128i *mmdst = (__m128i *)dst;
|
||||
while (i >= 4) { /* 4 * float32 */
|
||||
_mm_store_si128(mmdst, _mm_slli_epi32(_mm_cvtps_epi32(_mm_mul_ps(_mm_min_ps(_mm_max_ps(negone, _mm_load_ps(src)), one), mulby8388607)), 8)); /* load 4 floats, clamp, convert to sint32 */
|
||||
i -= 4;
|
||||
src += 4;
|
||||
mmdst++;
|
||||
}
|
||||
dst = (Sint32 *)mmdst;
|
||||
}
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 2147483647;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = (Sint32)-2147483648LL;
|
||||
} else {
|
||||
*dst = ((Sint32)(sample * 8388607.0f)) << 8;
|
||||
}
|
||||
i--;
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SDL_NEON_INTRINSICS
|
||||
static void SDL_Convert_S8_to_F32_NEON(float *dst, const Sint8 *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("S8", "F32 (using NEON)");
|
||||
|
||||
src += num_samples - 1;
|
||||
dst += num_samples - 1;
|
||||
|
||||
/* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
|
||||
for (i = num_samples; i && (((size_t)(dst - 15)) & 15); --i, --src, --dst) {
|
||||
*dst = ((float)*src) * DIVBY128;
|
||||
}
|
||||
|
||||
src -= 15;
|
||||
dst -= 15; /* adjust to read NEON blocks from the start. */
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do NEON blocks as long as we have 16 bytes available. */
|
||||
const int8_t *mmsrc = (const int8_t *)src;
|
||||
const float32x4_t divby128 = vdupq_n_f32(DIVBY128);
|
||||
while (i >= 16) { /* 16 * 8-bit */
|
||||
const int8x16_t bytes = vld1q_s8(mmsrc); /* get 16 sint8 into a NEON register. */
|
||||
const int16x8_t int16hi = vmovl_s8(vget_high_s8(bytes)); /* convert top 8 bytes to 8 int16 */
|
||||
const int16x8_t int16lo = vmovl_s8(vget_low_s8(bytes)); /* convert bottom 8 bytes to 8 int16 */
|
||||
/* split int16 to two int32, then convert to float, then multiply to normalize, store. */
|
||||
vst1q_f32(dst, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_low_s16(int16lo))), divby128));
|
||||
vst1q_f32(dst + 4, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_high_s16(int16lo))), divby128));
|
||||
vst1q_f32(dst + 8, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_low_s16(int16hi))), divby128));
|
||||
vst1q_f32(dst + 12, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_high_s16(int16hi))), divby128));
|
||||
i -= 16;
|
||||
mmsrc -= 16;
|
||||
dst -= 16;
|
||||
}
|
||||
|
||||
src = (const Sint8 *)mmsrc;
|
||||
}
|
||||
|
||||
src += 15;
|
||||
dst += 15; /* adjust for any scalar finishing. */
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
*dst = ((float)*src) * DIVBY128;
|
||||
i--;
|
||||
src--;
|
||||
dst--;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_Convert_U8_to_F32_NEON(float *dst, const Uint8 *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("U8", "F32 (using NEON)");
|
||||
|
||||
src += num_samples - 1;
|
||||
dst += num_samples - 1;
|
||||
|
||||
/* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
|
||||
for (i = num_samples; i && (((size_t)(dst - 15)) & 15); --i, --src, --dst) {
|
||||
*dst = (((float)*src) * DIVBY128) - 1.0f;
|
||||
}
|
||||
|
||||
src -= 15;
|
||||
dst -= 15; /* adjust to read NEON blocks from the start. */
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do NEON blocks as long as we have 16 bytes available. */
|
||||
const uint8_t *mmsrc = (const uint8_t *)src;
|
||||
const float32x4_t divby128 = vdupq_n_f32(DIVBY128);
|
||||
const float32x4_t negone = vdupq_n_f32(-1.0f);
|
||||
while (i >= 16) { /* 16 * 8-bit */
|
||||
const uint8x16_t bytes = vld1q_u8(mmsrc); /* get 16 uint8 into a NEON register. */
|
||||
const uint16x8_t uint16hi = vmovl_u8(vget_high_u8(bytes)); /* convert top 8 bytes to 8 uint16 */
|
||||
const uint16x8_t uint16lo = vmovl_u8(vget_low_u8(bytes)); /* convert bottom 8 bytes to 8 uint16 */
|
||||
/* split uint16 to two uint32, then convert to float, then multiply to normalize, subtract to adjust for sign, store. */
|
||||
vst1q_f32(dst, vmlaq_f32(negone, vcvtq_f32_u32(vmovl_u16(vget_low_u16(uint16lo))), divby128));
|
||||
vst1q_f32(dst + 4, vmlaq_f32(negone, vcvtq_f32_u32(vmovl_u16(vget_high_u16(uint16lo))), divby128));
|
||||
vst1q_f32(dst + 8, vmlaq_f32(negone, vcvtq_f32_u32(vmovl_u16(vget_low_u16(uint16hi))), divby128));
|
||||
vst1q_f32(dst + 12, vmlaq_f32(negone, vcvtq_f32_u32(vmovl_u16(vget_high_u16(uint16hi))), divby128));
|
||||
i -= 16;
|
||||
mmsrc -= 16;
|
||||
dst -= 16;
|
||||
}
|
||||
|
||||
src = (const Uint8 *)mmsrc;
|
||||
}
|
||||
|
||||
src += 15;
|
||||
dst += 15; /* adjust for any scalar finishing. */
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
*dst = (((float)*src) * DIVBY128) - 1.0f;
|
||||
i--;
|
||||
src--;
|
||||
dst--;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_Convert_S16_to_F32_NEON(float *dst, const Sint16 *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("S16", "F32 (using NEON)");
|
||||
|
||||
src += num_samples - 1;
|
||||
dst += num_samples - 1;
|
||||
|
||||
/* Get dst aligned to 16 bytes (since buffer is growing, we don't have to worry about overreading from src) */
|
||||
for (i = num_samples; i && (((size_t)(dst - 7)) & 15); --i, --src, --dst) {
|
||||
*dst = ((float)*src) * DIVBY32768;
|
||||
}
|
||||
|
||||
src -= 7;
|
||||
dst -= 7; /* adjust to read NEON blocks from the start. */
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do NEON blocks as long as we have 16 bytes available. */
|
||||
const float32x4_t divby32768 = vdupq_n_f32(DIVBY32768);
|
||||
while (i >= 8) { /* 8 * 16-bit */
|
||||
const int16x8_t ints = vld1q_s16((int16_t const *)src); /* get 8 sint16 into a NEON register. */
|
||||
/* split int16 to two int32, then convert to float, then multiply to normalize, store. */
|
||||
vst1q_f32(dst, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_low_s16(ints))), divby32768));
|
||||
vst1q_f32(dst + 4, vmulq_f32(vcvtq_f32_s32(vmovl_s16(vget_high_s16(ints))), divby32768));
|
||||
i -= 8;
|
||||
src -= 8;
|
||||
dst -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
src += 7;
|
||||
dst += 7; /* adjust for any scalar finishing. */
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
*dst = ((float)*src) * DIVBY32768;
|
||||
i--;
|
||||
src--;
|
||||
dst--;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_Convert_S32_to_F32_NEON(float *dst, const Sint32 *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("S32", "F32 (using NEON)");
|
||||
|
||||
/* Get dst aligned to 16 bytes */
|
||||
for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
|
||||
*dst = ((float)(*src >> 8)) * DIVBY8388607;
|
||||
}
|
||||
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do NEON blocks as long as we have 16 bytes available. */
|
||||
const float32x4_t divby8388607 = vdupq_n_f32(DIVBY8388607);
|
||||
const int32_t *mmsrc = (const int32_t *)src;
|
||||
while (i >= 4) { /* 4 * sint32 */
|
||||
/* shift out lowest bits so int fits in a float32. Small precision loss, but much faster. */
|
||||
vst1q_f32(dst, vmulq_f32(vcvtq_f32_s32(vshrq_n_s32(vld1q_s32(mmsrc), 8)), divby8388607));
|
||||
i -= 4;
|
||||
mmsrc += 4;
|
||||
dst += 4;
|
||||
}
|
||||
src = (const Sint32 *)mmsrc;
|
||||
}
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
*dst = ((float)(*src >> 8)) * DIVBY8388607;
|
||||
i--;
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_Convert_F32_to_S8_NEON(Sint8 *dst, const float *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("F32", "S8 (using NEON)");
|
||||
|
||||
/* Get dst aligned to 16 bytes */
|
||||
for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 127;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = -128;
|
||||
} else {
|
||||
*dst = (Sint8)(sample * 127.0f);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do NEON blocks as long as we have 16 bytes available. */
|
||||
const float32x4_t one = vdupq_n_f32(1.0f);
|
||||
const float32x4_t negone = vdupq_n_f32(-1.0f);
|
||||
const float32x4_t mulby127 = vdupq_n_f32(127.0f);
|
||||
int8_t *mmdst = (int8_t *)dst;
|
||||
while (i >= 16) { /* 16 * float32 */
|
||||
const int32x4_t ints1 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
|
||||
const int32x4_t ints2 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src + 4)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
|
||||
const int32x4_t ints3 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src + 8)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
|
||||
const int32x4_t ints4 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src + 12)), one), mulby127)); /* load 4 floats, clamp, convert to sint32 */
|
||||
const int8x8_t i8lo = vmovn_s16(vcombine_s16(vmovn_s32(ints1), vmovn_s32(ints2))); /* narrow to sint16, combine, narrow to sint8 */
|
||||
const int8x8_t i8hi = vmovn_s16(vcombine_s16(vmovn_s32(ints3), vmovn_s32(ints4))); /* narrow to sint16, combine, narrow to sint8 */
|
||||
vst1q_s8(mmdst, vcombine_s8(i8lo, i8hi)); /* combine to int8x16_t, store out */
|
||||
i -= 16;
|
||||
src += 16;
|
||||
mmdst += 16;
|
||||
}
|
||||
dst = (Sint8 *)mmdst;
|
||||
}
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 127;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = -128;
|
||||
} else {
|
||||
*dst = (Sint8)(sample * 127.0f);
|
||||
}
|
||||
i--;
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_Convert_F32_to_U8_NEON(Uint8 *dst, const float *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("F32", "U8 (using NEON)");
|
||||
|
||||
/* Get dst aligned to 16 bytes */
|
||||
for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 255;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = 0;
|
||||
} else {
|
||||
*dst = (Uint8)((sample + 1.0f) * 127.0f);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do NEON blocks as long as we have 16 bytes available. */
|
||||
const float32x4_t one = vdupq_n_f32(1.0f);
|
||||
const float32x4_t negone = vdupq_n_f32(-1.0f);
|
||||
const float32x4_t mulby127 = vdupq_n_f32(127.0f);
|
||||
uint8_t *mmdst = (uint8_t *)dst;
|
||||
while (i >= 16) { /* 16 * float32 */
|
||||
const uint32x4_t uints1 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src)), one), one), mulby127)); /* load 4 floats, clamp, convert to uint32 */
|
||||
const uint32x4_t uints2 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src + 4)), one), one), mulby127)); /* load 4 floats, clamp, convert to uint32 */
|
||||
const uint32x4_t uints3 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src + 8)), one), one), mulby127)); /* load 4 floats, clamp, convert to uint32 */
|
||||
const uint32x4_t uints4 = vcvtq_u32_f32(vmulq_f32(vaddq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src + 12)), one), one), mulby127)); /* load 4 floats, clamp, convert to uint32 */
|
||||
const uint8x8_t ui8lo = vmovn_u16(vcombine_u16(vmovn_u32(uints1), vmovn_u32(uints2))); /* narrow to uint16, combine, narrow to uint8 */
|
||||
const uint8x8_t ui8hi = vmovn_u16(vcombine_u16(vmovn_u32(uints3), vmovn_u32(uints4))); /* narrow to uint16, combine, narrow to uint8 */
|
||||
vst1q_u8(mmdst, vcombine_u8(ui8lo, ui8hi)); /* combine to uint8x16_t, store out */
|
||||
i -= 16;
|
||||
src += 16;
|
||||
mmdst += 16;
|
||||
}
|
||||
|
||||
dst = (Uint8 *)mmdst;
|
||||
}
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 255;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = 0;
|
||||
} else {
|
||||
*dst = (Uint8)((sample + 1.0f) * 127.0f);
|
||||
}
|
||||
i--;
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_Convert_F32_to_S16_NEON(Sint16 *dst, const float *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("F32", "S16 (using NEON)");
|
||||
|
||||
/* Get dst aligned to 16 bytes */
|
||||
for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 32767;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = -32768;
|
||||
} else {
|
||||
*dst = (Sint16)(sample * 32767.0f);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
|
||||
/* Make sure src is aligned too. */
|
||||
if (!(((size_t)src) & 15)) {
|
||||
/* Aligned! Do NEON blocks as long as we have 16 bytes available. */
|
||||
const float32x4_t one = vdupq_n_f32(1.0f);
|
||||
const float32x4_t negone = vdupq_n_f32(-1.0f);
|
||||
const float32x4_t mulby32767 = vdupq_n_f32(32767.0f);
|
||||
int16_t *mmdst = (int16_t *)dst;
|
||||
while (i >= 8) { /* 8 * float32 */
|
||||
const int32x4_t ints1 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src)), one), mulby32767)); /* load 4 floats, clamp, convert to sint32 */
|
||||
const int32x4_t ints2 = vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src + 4)), one), mulby32767)); /* load 4 floats, clamp, convert to sint32 */
|
||||
vst1q_s16(mmdst, vcombine_s16(vmovn_s32(ints1), vmovn_s32(ints2))); /* narrow to sint16, combine, store out. */
|
||||
i -= 8;
|
||||
src += 8;
|
||||
mmdst += 8;
|
||||
}
|
||||
dst = (Sint16 *)mmdst;
|
||||
}
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 32767;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = -32768;
|
||||
} else {
|
||||
*dst = (Sint16)(sample * 32767.0f);
|
||||
}
|
||||
i--;
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
static void SDL_Convert_F32_to_S32_NEON(Sint32 *dst, const float *src, int num_samples)
|
||||
{
|
||||
int i;
|
||||
|
||||
LOG_DEBUG_AUDIO_CONVERT("F32", "S32 (using NEON)");
|
||||
|
||||
/* Get dst aligned to 16 bytes */
|
||||
for (i = num_samples; i && (((size_t)dst) & 15); --i, ++src, ++dst) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 2147483647;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = (-2147483647) - 1;
|
||||
} else {
|
||||
*dst = ((Sint32)(sample * 8388607.0f)) << 8;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_assert(!i || !(((size_t)dst) & 15));
|
||||
SDL_assert(!i || !(((size_t)src) & 15));
|
||||
|
||||
{
|
||||
/* Aligned! Do NEON blocks as long as we have 16 bytes available. */
|
||||
const float32x4_t one = vdupq_n_f32(1.0f);
|
||||
const float32x4_t negone = vdupq_n_f32(-1.0f);
|
||||
const float32x4_t mulby8388607 = vdupq_n_f32(8388607.0f);
|
||||
int32_t *mmdst = (int32_t *)dst;
|
||||
while (i >= 4) { /* 4 * float32 */
|
||||
vst1q_s32(mmdst, vshlq_n_s32(vcvtq_s32_f32(vmulq_f32(vminq_f32(vmaxq_f32(negone, vld1q_f32(src)), one), mulby8388607)), 8));
|
||||
i -= 4;
|
||||
src += 4;
|
||||
mmdst += 4;
|
||||
}
|
||||
dst = (Sint32 *)mmdst;
|
||||
}
|
||||
|
||||
/* Finish off any leftovers with scalar operations. */
|
||||
while (i) {
|
||||
const float sample = *src;
|
||||
if (sample >= 1.0f) {
|
||||
*dst = 2147483647;
|
||||
} else if (sample <= -1.0f) {
|
||||
*dst = (-2147483647) - 1;
|
||||
} else {
|
||||
*dst = ((Sint32)(sample * 8388607.0f)) << 8;
|
||||
}
|
||||
i--;
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Function pointers set to a CPU-specific implementation. */
|
||||
void (*SDL_Convert_S8_to_F32)(float *dst, const Sint8 *src, int num_samples) = NULL;
|
||||
void (*SDL_Convert_U8_to_F32)(float *dst, const Uint8 *src, int num_samples) = NULL;
|
||||
void (*SDL_Convert_S16_to_F32)(float *dst, const Sint16 *src, int num_samples) = NULL;
|
||||
void (*SDL_Convert_S32_to_F32)(float *dst, const Sint32 *src, int num_samples) = NULL;
|
||||
void (*SDL_Convert_F32_to_S8)(Sint8 *dst, const float *src, int num_samples) = NULL;
|
||||
void (*SDL_Convert_F32_to_U8)(Uint8 *dst, const float *src, int num_samples) = NULL;
|
||||
void (*SDL_Convert_F32_to_S16)(Sint16 *dst, const float *src, int num_samples) = NULL;
|
||||
void (*SDL_Convert_F32_to_S32)(Sint32 *dst, const float *src, int num_samples) = NULL;
|
||||
|
||||
void SDL_ChooseAudioConverters(void)
|
||||
{
|
||||
static SDL_bool converters_chosen = SDL_FALSE;
|
||||
if (converters_chosen) {
|
||||
return;
|
||||
}
|
||||
|
||||
#define SET_CONVERTER_FUNCS(fntype) \
|
||||
SDL_Convert_S8_to_F32 = SDL_Convert_S8_to_F32_##fntype; \
|
||||
SDL_Convert_U8_to_F32 = SDL_Convert_U8_to_F32_##fntype; \
|
||||
SDL_Convert_S16_to_F32 = SDL_Convert_S16_to_F32_##fntype; \
|
||||
SDL_Convert_S32_to_F32 = SDL_Convert_S32_to_F32_##fntype; \
|
||||
SDL_Convert_F32_to_S8 = SDL_Convert_F32_to_S8_##fntype; \
|
||||
SDL_Convert_F32_to_U8 = SDL_Convert_F32_to_U8_##fntype; \
|
||||
SDL_Convert_F32_to_S16 = SDL_Convert_F32_to_S16_##fntype; \
|
||||
SDL_Convert_F32_to_S32 = SDL_Convert_F32_to_S32_##fntype; \
|
||||
converters_chosen = SDL_TRUE
|
||||
|
||||
#ifdef SDL_SSE2_INTRINSICS
|
||||
if (SDL_HasSSE2()) {
|
||||
SET_CONVERTER_FUNCS(SSE2);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SDL_NEON_INTRINSICS
|
||||
if (SDL_HasNEON()) {
|
||||
SET_CONVERTER_FUNCS(NEON);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NEED_SCALAR_CONVERTER_FALLBACKS
|
||||
SET_CONVERTER_FUNCS(Scalar);
|
||||
#endif
|
||||
|
||||
#undef SET_CONVERTER_FUNCS
|
||||
|
||||
SDL_assert(converters_chosen == SDL_TRUE);
|
||||
}
|
293
external/sdl/SDL/src/audio/SDL_mixer.c
vendored
Normal file
293
external/sdl/SDL/src/audio/SDL_mixer.c
vendored
Normal file
@ -0,0 +1,293 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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 provides the default mixing callback for the SDL audio routines */
|
||||
|
||||
#include "SDL_sysaudio.h"
|
||||
|
||||
/* This table is used to add two sound values together and pin
|
||||
* the value to avoid overflow. (used with permission from ARDI)
|
||||
*/
|
||||
static const Uint8 mix8[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
|
||||
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
|
||||
0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
|
||||
0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
|
||||
0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
|
||||
0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
|
||||
0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
|
||||
0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B,
|
||||
0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
|
||||
0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
|
||||
0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C,
|
||||
0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
||||
0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92,
|
||||
0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D,
|
||||
0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
|
||||
0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3,
|
||||
0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE,
|
||||
0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9,
|
||||
0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4,
|
||||
0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
|
||||
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA,
|
||||
0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5,
|
||||
0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
/* The volume ranges from 0 - 128 */
|
||||
#define ADJUST_VOLUME(type, s, v) ((s) = (type)(((s) * (v)) / SDL_MIX_MAXVOLUME))
|
||||
#define ADJUST_VOLUME_U8(s, v) ((s) = (Uint8)(((((s) - 128) * (v)) / SDL_MIX_MAXVOLUME) + 128))
|
||||
|
||||
|
||||
/* !!! FIXME: this needs some SIMD magic. */
|
||||
|
||||
int SDL_MixAudioFormat(Uint8 *dst, const Uint8 *src, SDL_AudioFormat format,
|
||||
Uint32 len, int volume)
|
||||
{
|
||||
if (volume == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
|
||||
case SDL_AUDIO_U8:
|
||||
{
|
||||
Uint8 src_sample;
|
||||
|
||||
while (len--) {
|
||||
src_sample = *src;
|
||||
ADJUST_VOLUME_U8(src_sample, volume);
|
||||
*dst = mix8[*dst + src_sample];
|
||||
++dst;
|
||||
++src;
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_AUDIO_S8:
|
||||
{
|
||||
Sint8 *dst8, *src8;
|
||||
Sint8 src_sample;
|
||||
int dst_sample;
|
||||
const int max_audioval = SDL_MAX_SINT8;
|
||||
const int min_audioval = SDL_MIN_SINT8;
|
||||
|
||||
src8 = (Sint8 *)src;
|
||||
dst8 = (Sint8 *)dst;
|
||||
while (len--) {
|
||||
src_sample = *src8;
|
||||
ADJUST_VOLUME(Sint8, src_sample, volume);
|
||||
dst_sample = *dst8 + src_sample;
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*dst8 = (Sint8)dst_sample;
|
||||
++dst8;
|
||||
++src8;
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_AUDIO_S16LSB:
|
||||
{
|
||||
Sint16 src1, src2;
|
||||
int dst_sample;
|
||||
const int max_audioval = SDL_MAX_SINT16;
|
||||
const int min_audioval = SDL_MIN_SINT16;
|
||||
|
||||
len /= 2;
|
||||
while (len--) {
|
||||
src1 = SDL_SwapLE16(*(Sint16 *)src);
|
||||
ADJUST_VOLUME(Sint16, src1, volume);
|
||||
src2 = SDL_SwapLE16(*(Sint16 *)dst);
|
||||
src += 2;
|
||||
dst_sample = src1 + src2;
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*(Sint16 *)dst = SDL_SwapLE16((Sint16)dst_sample);
|
||||
dst += 2;
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_AUDIO_S16MSB:
|
||||
{
|
||||
Sint16 src1, src2;
|
||||
int dst_sample;
|
||||
const int max_audioval = SDL_MAX_SINT16;
|
||||
const int min_audioval = SDL_MIN_SINT16;
|
||||
|
||||
len /= 2;
|
||||
while (len--) {
|
||||
src1 = SDL_SwapBE16(*(Sint16 *)src);
|
||||
ADJUST_VOLUME(Sint16, src1, volume);
|
||||
src2 = SDL_SwapBE16(*(Sint16 *)dst);
|
||||
src += 2;
|
||||
dst_sample = src1 + src2;
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*(Sint16 *)dst = SDL_SwapBE16((Sint16)dst_sample);
|
||||
dst += 2;
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_AUDIO_S32LSB:
|
||||
{
|
||||
const Uint32 *src32 = (Uint32 *)src;
|
||||
Uint32 *dst32 = (Uint32 *)dst;
|
||||
Sint64 src1, src2;
|
||||
Sint64 dst_sample;
|
||||
const Sint64 max_audioval = SDL_MAX_SINT32;
|
||||
const Sint64 min_audioval = SDL_MIN_SINT32;
|
||||
|
||||
len /= 4;
|
||||
while (len--) {
|
||||
src1 = (Sint64)((Sint32)SDL_SwapLE32(*src32));
|
||||
src32++;
|
||||
ADJUST_VOLUME(Sint64, src1, volume);
|
||||
src2 = (Sint64)((Sint32)SDL_SwapLE32(*dst32));
|
||||
dst_sample = src1 + src2;
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*(dst32++) = SDL_SwapLE32((Uint32)((Sint32)dst_sample));
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_AUDIO_S32MSB:
|
||||
{
|
||||
const Uint32 *src32 = (Uint32 *)src;
|
||||
Uint32 *dst32 = (Uint32 *)dst;
|
||||
Sint64 src1, src2;
|
||||
Sint64 dst_sample;
|
||||
const Sint64 max_audioval = SDL_MAX_SINT32;
|
||||
const Sint64 min_audioval = SDL_MIN_SINT32;
|
||||
|
||||
len /= 4;
|
||||
while (len--) {
|
||||
src1 = (Sint64)((Sint32)SDL_SwapBE32(*src32));
|
||||
src32++;
|
||||
ADJUST_VOLUME(Sint64, src1, volume);
|
||||
src2 = (Sint64)((Sint32)SDL_SwapBE32(*dst32));
|
||||
dst_sample = src1 + src2;
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*(dst32++) = SDL_SwapBE32((Uint32)((Sint32)dst_sample));
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_AUDIO_F32LSB:
|
||||
{
|
||||
const float fmaxvolume = 1.0f / ((float)SDL_MIX_MAXVOLUME);
|
||||
const float fvolume = (float)volume;
|
||||
const float *src32 = (float *)src;
|
||||
float *dst32 = (float *)dst;
|
||||
float src1, src2;
|
||||
double dst_sample;
|
||||
/* !!! FIXME: are these right? */
|
||||
const double max_audioval = 3.402823466e+38F;
|
||||
const double min_audioval = -3.402823466e+38F;
|
||||
|
||||
len /= 4;
|
||||
while (len--) {
|
||||
src1 = ((SDL_SwapFloatLE(*src32) * fvolume) * fmaxvolume);
|
||||
src2 = SDL_SwapFloatLE(*dst32);
|
||||
src32++;
|
||||
|
||||
dst_sample = ((double)src1) + ((double)src2);
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*(dst32++) = SDL_SwapFloatLE((float)dst_sample);
|
||||
}
|
||||
} break;
|
||||
|
||||
case SDL_AUDIO_F32MSB:
|
||||
{
|
||||
const float fmaxvolume = 1.0f / ((float)SDL_MIX_MAXVOLUME);
|
||||
const float fvolume = (float)volume;
|
||||
const float *src32 = (float *)src;
|
||||
float *dst32 = (float *)dst;
|
||||
float src1, src2;
|
||||
double dst_sample;
|
||||
/* !!! FIXME: are these right? */
|
||||
const double max_audioval = 3.402823466e+38F;
|
||||
const double min_audioval = -3.402823466e+38F;
|
||||
|
||||
len /= 4;
|
||||
while (len--) {
|
||||
src1 = ((SDL_SwapFloatBE(*src32) * fvolume) * fmaxvolume);
|
||||
src2 = SDL_SwapFloatBE(*dst32);
|
||||
src32++;
|
||||
|
||||
dst_sample = ((double)src1) + ((double)src2);
|
||||
if (dst_sample > max_audioval) {
|
||||
dst_sample = max_audioval;
|
||||
} else if (dst_sample < min_audioval) {
|
||||
dst_sample = min_audioval;
|
||||
}
|
||||
*(dst32++) = SDL_SwapFloatBE((float)dst_sample);
|
||||
}
|
||||
} break;
|
||||
|
||||
default: /* If this happens... FIXME! */
|
||||
return SDL_SetError("SDL_MixAudioFormat(): unknown audio format");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
204
external/sdl/SDL/src/audio/SDL_sysaudio.h
vendored
Normal file
204
external/sdl/SDL/src/audio/SDL_sysaudio.h
vendored
Normal file
@ -0,0 +1,204 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_sysaudio_h_
|
||||
#define SDL_sysaudio_h_
|
||||
|
||||
#include "../SDL_dataqueue.h"
|
||||
#include "./SDL_audio_c.h"
|
||||
|
||||
/* !!! FIXME: These are wordy and unlocalized... */
|
||||
#define DEFAULT_OUTPUT_DEVNAME "System audio output device"
|
||||
#define DEFAULT_INPUT_DEVNAME "System audio capture device"
|
||||
|
||||
/* The SDL audio driver */
|
||||
typedef struct SDL_AudioDevice SDL_AudioDevice;
|
||||
|
||||
/* Audio targets should call this as devices are added to the system (such as
|
||||
a USB headset being plugged in), and should also be called for
|
||||
for every device found during DetectDevices(). */
|
||||
extern void SDL_AddAudioDevice(const SDL_bool iscapture, const char *name, SDL_AudioSpec *spec, void *handle);
|
||||
|
||||
/* Audio targets should call this as devices are removed, so SDL can update
|
||||
its list of available devices. */
|
||||
extern void SDL_RemoveAudioDevice(const SDL_bool iscapture, void *handle);
|
||||
|
||||
/* Audio targets should call this if an opened audio device is lost while
|
||||
being used. This can happen due to i/o errors, or a device being unplugged,
|
||||
etc. If the device is totally gone, please also call SDL_RemoveAudioDevice()
|
||||
as appropriate so SDL's list of devices is accurate. */
|
||||
extern void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device);
|
||||
|
||||
/* This is the size of a packet when using SDL_QueueAudio(). We allocate
|
||||
these as necessary and pool them, under the assumption that we'll
|
||||
eventually end up with a handful that keep recycling, meeting whatever
|
||||
the app needs. We keep packing data tightly as more arrives to avoid
|
||||
wasting space, and if we get a giant block of data, we'll split them
|
||||
into multiple packets behind the scenes. My expectation is that most
|
||||
apps will have 2-3 of these in the pool. 8k should cover most needs, but
|
||||
if this is crippling for some embedded system, we can #ifdef this.
|
||||
The system preallocates enough packets for 2 callbacks' worth of data. */
|
||||
#define SDL_AUDIOBUFFERQUEUE_PACKETLEN (8 * 1024)
|
||||
|
||||
typedef struct SDL_AudioDriverImpl
|
||||
{
|
||||
void (*DetectDevices)(void);
|
||||
int (*OpenDevice)(SDL_AudioDevice *_this, const char *devname);
|
||||
void (*ThreadInit)(SDL_AudioDevice *_this); /* Called by audio thread at start */
|
||||
void (*ThreadDeinit)(SDL_AudioDevice *_this); /* Called by audio thread at end */
|
||||
void (*WaitDevice)(SDL_AudioDevice *_this);
|
||||
void (*PlayDevice)(SDL_AudioDevice *_this);
|
||||
Uint8 *(*GetDeviceBuf)(SDL_AudioDevice *_this);
|
||||
int (*CaptureFromDevice)(SDL_AudioDevice *_this, void *buffer, int buflen);
|
||||
void (*FlushCapture)(SDL_AudioDevice *_this);
|
||||
void (*CloseDevice)(SDL_AudioDevice *_this);
|
||||
void (*LockDevice)(SDL_AudioDevice *_this);
|
||||
void (*UnlockDevice)(SDL_AudioDevice *_this);
|
||||
void (*FreeDeviceHandle)(void *handle); /**< SDL is done with handle from SDL_AddAudioDevice() */
|
||||
void (*Deinitialize)(void);
|
||||
int (*GetDefaultAudioInfo)(char **name, SDL_AudioSpec *spec, int iscapture);
|
||||
|
||||
/* !!! FIXME: add pause(), so we can optimize instead of mixing silence. */
|
||||
|
||||
/* Some flags to push duplicate code into the core and reduce #ifdefs. */
|
||||
SDL_bool ProvidesOwnCallbackThread;
|
||||
SDL_bool HasCaptureSupport;
|
||||
SDL_bool OnlyHasDefaultOutputDevice;
|
||||
SDL_bool OnlyHasDefaultCaptureDevice;
|
||||
SDL_bool AllowsArbitraryDeviceNames;
|
||||
SDL_bool SupportsNonPow2Samples;
|
||||
} SDL_AudioDriverImpl;
|
||||
|
||||
typedef struct SDL_AudioDeviceItem
|
||||
{
|
||||
void *handle;
|
||||
char *name;
|
||||
char *original_name;
|
||||
SDL_AudioSpec spec;
|
||||
int dupenum;
|
||||
struct SDL_AudioDeviceItem *next;
|
||||
} SDL_AudioDeviceItem;
|
||||
|
||||
typedef struct SDL_AudioDriver
|
||||
{
|
||||
/* * * */
|
||||
/* The name of this audio driver */
|
||||
const char *name;
|
||||
|
||||
/* * * */
|
||||
/* The description of this audio driver */
|
||||
const char *desc;
|
||||
|
||||
SDL_AudioDriverImpl impl;
|
||||
|
||||
/* A mutex for device detection */
|
||||
SDL_Mutex *detectionLock;
|
||||
SDL_bool captureDevicesRemoved;
|
||||
SDL_bool outputDevicesRemoved;
|
||||
int outputDeviceCount;
|
||||
int inputDeviceCount;
|
||||
SDL_AudioDeviceItem *outputDevices;
|
||||
SDL_AudioDeviceItem *inputDevices;
|
||||
} SDL_AudioDriver;
|
||||
|
||||
/* Define the SDL audio driver structure */
|
||||
struct SDL_AudioDevice
|
||||
{
|
||||
/* * * */
|
||||
/* Data common to all devices */
|
||||
SDL_AudioDeviceID id;
|
||||
|
||||
/* The device's current audio specification */
|
||||
SDL_AudioSpec spec;
|
||||
|
||||
/* The callback's expected audio specification (converted vs device's spec). */
|
||||
SDL_AudioSpec callbackspec;
|
||||
|
||||
/* Stream that converts and resamples. NULL if not needed. */
|
||||
SDL_AudioStream *stream;
|
||||
|
||||
/* Current state flags */
|
||||
SDL_AtomicInt shutdown; /* true if we are signaling the play thread to end. */
|
||||
SDL_AtomicInt enabled; /* true if device is functioning and connected. */
|
||||
SDL_AtomicInt paused;
|
||||
SDL_bool iscapture;
|
||||
|
||||
/* Scratch buffer used in the bridge between SDL and the user callback. */
|
||||
Uint8 *work_buffer;
|
||||
|
||||
/* Size, in bytes, of work_buffer. */
|
||||
Uint32 work_buffer_len;
|
||||
|
||||
/* A mutex for locking the mixing buffers */
|
||||
SDL_Mutex *mixer_lock;
|
||||
|
||||
/* A thread to feed the audio device */
|
||||
SDL_Thread *thread;
|
||||
SDL_threadID threadid;
|
||||
|
||||
/* Queued buffers (if app not using callback). */
|
||||
SDL_DataQueue *buffer_queue;
|
||||
|
||||
/* * * */
|
||||
/* Data private to this driver */
|
||||
struct SDL_PrivateAudioData *hidden;
|
||||
|
||||
void *handle;
|
||||
};
|
||||
|
||||
typedef struct AudioBootStrap
|
||||
{
|
||||
const char *name;
|
||||
const char *desc;
|
||||
SDL_bool (*init)(SDL_AudioDriverImpl *impl);
|
||||
SDL_bool demand_only; /* 1==request explicitly, or it won't be available. */
|
||||
} AudioBootStrap;
|
||||
|
||||
/* Not all of these are available in a given build. Use #ifdefs, etc. */
|
||||
extern AudioBootStrap PIPEWIRE_bootstrap;
|
||||
extern AudioBootStrap PULSEAUDIO_bootstrap;
|
||||
extern AudioBootStrap ALSA_bootstrap;
|
||||
extern AudioBootStrap JACK_bootstrap;
|
||||
extern AudioBootStrap SNDIO_bootstrap;
|
||||
extern AudioBootStrap NETBSDAUDIO_bootstrap;
|
||||
extern AudioBootStrap DSP_bootstrap;
|
||||
extern AudioBootStrap WASAPI_bootstrap;
|
||||
extern AudioBootStrap DSOUND_bootstrap;
|
||||
extern AudioBootStrap WINMM_bootstrap;
|
||||
extern AudioBootStrap HAIKUAUDIO_bootstrap;
|
||||
extern AudioBootStrap COREAUDIO_bootstrap;
|
||||
extern AudioBootStrap DISKAUDIO_bootstrap;
|
||||
extern AudioBootStrap DUMMYAUDIO_bootstrap;
|
||||
extern AudioBootStrap aaudio_bootstrap;
|
||||
extern AudioBootStrap openslES_bootstrap;
|
||||
extern AudioBootStrap ANDROIDAUDIO_bootstrap;
|
||||
extern AudioBootStrap PS2AUDIO_bootstrap;
|
||||
extern AudioBootStrap PSPAUDIO_bootstrap;
|
||||
extern AudioBootStrap VITAAUD_bootstrap;
|
||||
extern AudioBootStrap N3DSAUDIO_bootstrap;
|
||||
extern AudioBootStrap EMSCRIPTENAUDIO_bootstrap;
|
||||
extern AudioBootStrap QSAAUDIO_bootstrap;
|
||||
|
||||
extern SDL_AudioDevice *get_audio_dev(SDL_AudioDeviceID id);
|
||||
extern int get_max_num_audio_dev(void);
|
||||
|
||||
#endif /* SDL_sysaudio_h_ */
|
2131
external/sdl/SDL/src/audio/SDL_wave.c
vendored
Normal file
2131
external/sdl/SDL/src/audio/SDL_wave.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
151
external/sdl/SDL/src/audio/SDL_wave.h
vendored
Normal file
151
external/sdl/SDL/src/audio/SDL_wave.h
vendored
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
/* RIFF WAVE files are little-endian */
|
||||
|
||||
/*******************************************/
|
||||
/* Define values for Microsoft WAVE format */
|
||||
/*******************************************/
|
||||
/* FOURCC */
|
||||
#define RIFF 0x46464952 /* "RIFF" */
|
||||
#define WAVE 0x45564157 /* "WAVE" */
|
||||
#define FACT 0x74636166 /* "fact" */
|
||||
#define LIST 0x5453494c /* "LIST" */
|
||||
#define BEXT 0x74786562 /* "bext" */
|
||||
#define JUNK 0x4B4E554A /* "JUNK" */
|
||||
#define FMT 0x20746D66 /* "fmt " */
|
||||
#define DATA 0x61746164 /* "data" */
|
||||
/* Format tags */
|
||||
#define UNKNOWN_CODE 0x0000
|
||||
#define PCM_CODE 0x0001
|
||||
#define MS_ADPCM_CODE 0x0002
|
||||
#define IEEE_FLOAT_CODE 0x0003
|
||||
#define ALAW_CODE 0x0006
|
||||
#define MULAW_CODE 0x0007
|
||||
#define IMA_ADPCM_CODE 0x0011
|
||||
#define MPEG_CODE 0x0050
|
||||
#define MPEGLAYER3_CODE 0x0055
|
||||
#define EXTENSIBLE_CODE 0xFFFE
|
||||
|
||||
/* Stores the WAVE format information. */
|
||||
typedef struct WaveFormat
|
||||
{
|
||||
Uint16 formattag; /* Raw value of the first field in the fmt chunk data. */
|
||||
Uint16 encoding; /* Actual encoding, possibly from the extensible header. */
|
||||
Uint16 channels; /* Number of channels. */
|
||||
Uint32 frequency; /* Sampling rate in Hz. */
|
||||
Uint32 byterate; /* Average bytes per second. */
|
||||
Uint16 blockalign; /* Bytes per block. */
|
||||
Uint16 bitspersample; /* Currently supported are 8, 16, 24, 32, and 4 for ADPCM. */
|
||||
|
||||
/* Extra information size. Number of extra bytes starting at byte 18 in the
|
||||
* fmt chunk data. This is at least 22 for the extensible header.
|
||||
*/
|
||||
Uint16 extsize;
|
||||
|
||||
/* Extensible WAVE header fields */
|
||||
Uint16 validsamplebits;
|
||||
Uint32 samplesperblock; /* For compressed formats. Can be zero. Actually 16 bits in the header. */
|
||||
Uint32 channelmask;
|
||||
Uint8 subformat[16]; /* A format GUID. */
|
||||
} WaveFormat;
|
||||
|
||||
/* Stores information on the fact chunk. */
|
||||
typedef struct WaveFact
|
||||
{
|
||||
/* Represents the state of the fact chunk in the WAVE file.
|
||||
* Set to -1 if the fact chunk is invalid.
|
||||
* Set to 0 if the fact chunk is not present
|
||||
* Set to 1 if the fact chunk is present and valid.
|
||||
* Set to 2 if samplelength is going to be used as the number of sample frames.
|
||||
*/
|
||||
Sint32 status;
|
||||
|
||||
/* Version 1 of the RIFF specification calls the field in the fact chunk
|
||||
* dwFileSize. The Standards Update then calls it dwSampleLength and specifies
|
||||
* that it is 'the length of the data in samples'. WAVE files from Windows
|
||||
* with this chunk have it set to the samples per channel (sample frames).
|
||||
* This is useful to truncate compressed audio to a specific sample count
|
||||
* because a compressed block is usually decoded to a fixed number of
|
||||
* sample frames.
|
||||
*/
|
||||
Uint32 samplelength; /* Raw sample length value from the fact chunk. */
|
||||
} WaveFact;
|
||||
|
||||
/* Generic struct for the chunks in the WAVE file. */
|
||||
typedef struct WaveChunk
|
||||
{
|
||||
Uint32 fourcc; /* FOURCC of the chunk. */
|
||||
Uint32 length; /* Size of the chunk data. */
|
||||
Sint64 position; /* Position of the data in the stream. */
|
||||
Uint8 *data; /* When allocated, this points to the chunk data. length is used for the memory allocation size. */
|
||||
size_t size; /* Number of bytes in data that could be read from the stream. Can be smaller than length. */
|
||||
} WaveChunk;
|
||||
|
||||
/* Controls how the size of the RIFF chunk affects the loading of a WAVE file. */
|
||||
typedef enum WaveRiffSizeHint
|
||||
{
|
||||
RiffSizeNoHint,
|
||||
RiffSizeForce,
|
||||
RiffSizeIgnoreZero,
|
||||
RiffSizeIgnore,
|
||||
RiffSizeMaximum
|
||||
} WaveRiffSizeHint;
|
||||
|
||||
/* Controls how a truncated WAVE file is handled. */
|
||||
typedef enum WaveTruncationHint
|
||||
{
|
||||
TruncNoHint,
|
||||
TruncVeryStrict,
|
||||
TruncStrict,
|
||||
TruncDropFrame,
|
||||
TruncDropBlock
|
||||
} WaveTruncationHint;
|
||||
|
||||
/* Controls how the fact chunk affects the loading of a WAVE file. */
|
||||
typedef enum WaveFactChunkHint
|
||||
{
|
||||
FactNoHint,
|
||||
FactTruncate,
|
||||
FactStrict,
|
||||
FactIgnoreZero,
|
||||
FactIgnore
|
||||
} WaveFactChunkHint;
|
||||
|
||||
typedef struct WaveFile
|
||||
{
|
||||
WaveChunk chunk;
|
||||
WaveFormat format;
|
||||
WaveFact fact;
|
||||
|
||||
/* Number of sample frames that will be decoded. Calculated either with the
|
||||
* size of the data chunk or, if the appropriate hint is enabled, with the
|
||||
* sample length value from the fact chunk.
|
||||
*/
|
||||
Sint64 sampleframes;
|
||||
|
||||
void *decoderdata; /* Some decoders require extra data for a state. */
|
||||
|
||||
WaveRiffSizeHint riffhint;
|
||||
WaveTruncationHint trunchint;
|
||||
WaveFactChunkHint facthint;
|
||||
} WaveFile;
|
522
external/sdl/SDL/src/audio/aaudio/SDL_aaudio.c
vendored
Normal file
522
external/sdl/SDL/src/audio/aaudio/SDL_aaudio.c
vendored
Normal file
@ -0,0 +1,522 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_AUDIO_DRIVER_AAUDIO
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_aaudio.h"
|
||||
|
||||
#include "../../core/android/SDL_android.h"
|
||||
#include <stdbool.h>
|
||||
#include <aaudio/AAudio.h>
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
AAudioStream *stream;
|
||||
|
||||
/* Raw mixing buffer */
|
||||
Uint8 *mixbuf;
|
||||
int mixlen;
|
||||
int frame_size;
|
||||
|
||||
/* Resume device if it was paused automatically */
|
||||
int resume;
|
||||
};
|
||||
|
||||
/* Debug */
|
||||
#if 0
|
||||
#define LOGI(...) SDL_Log(__VA_ARGS__);
|
||||
#else
|
||||
#define LOGI(...)
|
||||
#endif
|
||||
|
||||
typedef struct AAUDIO_Data
|
||||
{
|
||||
AAudioStreamBuilder *builder;
|
||||
void *handle;
|
||||
#define SDL_PROC(ret, func, params) ret (*func) params;
|
||||
#include "SDL_aaudiofuncs.h"
|
||||
#undef SDL_PROC
|
||||
} AAUDIO_Data;
|
||||
static AAUDIO_Data ctx;
|
||||
|
||||
static int aaudio_LoadFunctions(AAUDIO_Data *data)
|
||||
{
|
||||
#define SDL_PROC(ret, func, params) \
|
||||
do { \
|
||||
data->func = (ret (*) params)SDL_LoadFunction(data->handle, #func); \
|
||||
if (!data->func) { \
|
||||
return SDL_SetError("Couldn't load AAUDIO function %s: %s", #func, SDL_GetError()); \
|
||||
} \
|
||||
} while (0);
|
||||
#include "SDL_aaudiofuncs.h"
|
||||
#undef SDL_PROC
|
||||
return 0;
|
||||
}
|
||||
|
||||
void aaudio_errorCallback(AAudioStream *stream, void *userData, aaudio_result_t error);
|
||||
void aaudio_errorCallback(AAudioStream *stream, void *userData, aaudio_result_t error)
|
||||
{
|
||||
LOGI("SDL aaudio_errorCallback: %d - %s", error, ctx.AAudio_convertResultToText(error));
|
||||
}
|
||||
|
||||
#define LIB_AAUDIO_SO "libaaudio.so"
|
||||
|
||||
static int aaudio_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
{
|
||||
struct SDL_PrivateAudioData *private;
|
||||
SDL_bool iscapture = _this->iscapture;
|
||||
aaudio_result_t res;
|
||||
LOGI(__func__);
|
||||
|
||||
if (iscapture) {
|
||||
if (!Android_JNI_RequestPermission("android.permission.RECORD_AUDIO")) {
|
||||
LOGI("This app doesn't have RECORD_AUDIO permission");
|
||||
return SDL_SetError("This app doesn't have RECORD_AUDIO permission");
|
||||
}
|
||||
}
|
||||
|
||||
_this->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*_this->hidden));
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
private = _this->hidden;
|
||||
|
||||
ctx.AAudioStreamBuilder_setSampleRate(ctx.builder, _this->spec.freq);
|
||||
ctx.AAudioStreamBuilder_setChannelCount(ctx.builder, _this->spec.channels);
|
||||
if(devname != NULL) {
|
||||
int aaudio_device_id = SDL_atoi(devname);
|
||||
LOGI("Opening device id %d", aaudio_device_id);
|
||||
ctx.AAudioStreamBuilder_setDeviceId(ctx.builder, aaudio_device_id);
|
||||
}
|
||||
{
|
||||
aaudio_direction_t direction = (iscapture ? AAUDIO_DIRECTION_INPUT : AAUDIO_DIRECTION_OUTPUT);
|
||||
ctx.AAudioStreamBuilder_setDirection(ctx.builder, direction);
|
||||
}
|
||||
{
|
||||
aaudio_format_t format = AAUDIO_FORMAT_PCM_FLOAT;
|
||||
if (_this->spec.format == SDL_AUDIO_S16SYS) {
|
||||
format = AAUDIO_FORMAT_PCM_I16;
|
||||
} else if (_this->spec.format == SDL_AUDIO_S16SYS) {
|
||||
format = AAUDIO_FORMAT_PCM_FLOAT;
|
||||
}
|
||||
ctx.AAudioStreamBuilder_setFormat(ctx.builder, format);
|
||||
}
|
||||
|
||||
ctx.AAudioStreamBuilder_setErrorCallback(ctx.builder, aaudio_errorCallback, private);
|
||||
|
||||
LOGI("AAudio Try to open %u hz %u bit chan %u %s samples %u",
|
||||
_this->spec.freq, SDL_AUDIO_BITSIZE(_this->spec.format),
|
||||
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples);
|
||||
|
||||
res = ctx.AAudioStreamBuilder_openStream(ctx.builder, &private->stream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStreamBuilder_openStream %d", res);
|
||||
return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
}
|
||||
|
||||
_this->spec.freq = ctx.AAudioStream_getSampleRate(private->stream);
|
||||
_this->spec.channels = ctx.AAudioStream_getChannelCount(private->stream);
|
||||
{
|
||||
aaudio_format_t fmt = ctx.AAudioStream_getFormat(private->stream);
|
||||
if (fmt == AAUDIO_FORMAT_PCM_I16) {
|
||||
_this->spec.format = SDL_AUDIO_S16SYS;
|
||||
} else if (fmt == AAUDIO_FORMAT_PCM_FLOAT) {
|
||||
_this->spec.format = SDL_AUDIO_F32SYS;
|
||||
}
|
||||
}
|
||||
|
||||
LOGI("AAudio Try to open %u hz %u bit chan %u %s samples %u",
|
||||
_this->spec.freq, SDL_AUDIO_BITSIZE(_this->spec.format),
|
||||
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples);
|
||||
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
/* Allocate mixing buffer */
|
||||
if (!iscapture) {
|
||||
private->mixlen = _this->spec.size;
|
||||
private->mixbuf = (Uint8 *)SDL_malloc(private->mixlen);
|
||||
if (private->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(private->mixbuf, _this->spec.silence, _this->spec.size);
|
||||
}
|
||||
|
||||
private->frame_size = _this->spec.channels * (SDL_AUDIO_BITSIZE(_this->spec.format) / 8);
|
||||
|
||||
res = ctx.AAudioStream_requestStart(private->stream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStream_requestStart %d iscapture:%d", res, iscapture);
|
||||
return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
}
|
||||
|
||||
LOGI("SDL AAudioStream_requestStart OK");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aaudio_CloseDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
struct SDL_PrivateAudioData *private = _this->hidden;
|
||||
aaudio_result_t res;
|
||||
LOGI(__func__);
|
||||
|
||||
if (private->stream) {
|
||||
res = ctx.AAudioStream_requestStop(private->stream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStream_requestStop %d", res);
|
||||
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
return;
|
||||
}
|
||||
|
||||
res = ctx.AAudioStream_close(private->stream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStreamBuilder_delete %d", res);
|
||||
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_free(_this->hidden->mixbuf);
|
||||
SDL_free(_this->hidden);
|
||||
}
|
||||
|
||||
static Uint8 *aaudio_GetDeviceBuf(SDL_AudioDevice *_this)
|
||||
{
|
||||
struct SDL_PrivateAudioData *private = _this->hidden;
|
||||
return private->mixbuf;
|
||||
}
|
||||
|
||||
static void aaudio_PlayDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
struct SDL_PrivateAudioData *private = _this->hidden;
|
||||
aaudio_result_t res;
|
||||
int64_t timeoutNanoseconds = 1 * 1000 * 1000; /* 8 ms */
|
||||
res = ctx.AAudioStream_write(private->stream, private->mixbuf, private->mixlen / private->frame_size, timeoutNanoseconds);
|
||||
if (res < 0) {
|
||||
LOGI("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
} else {
|
||||
LOGI("SDL AAudio play: %d frames, wanted:%d frames", (int)res, private->mixlen / private->frame_size);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Log under-run count */
|
||||
{
|
||||
static int prev = 0;
|
||||
int32_t cnt = ctx.AAudioStream_getXRunCount(private->stream);
|
||||
if (cnt != prev) {
|
||||
SDL_Log("AAudio underrun: %d - total: %d", cnt - prev, cnt);
|
||||
prev = cnt;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int aaudio_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *private = _this->hidden;
|
||||
aaudio_result_t res;
|
||||
int64_t timeoutNanoseconds = 8 * 1000 * 1000; /* 8 ms */
|
||||
res = ctx.AAudioStream_read(private->stream, buffer, buflen / private->frame_size, timeoutNanoseconds);
|
||||
if (res < 0) {
|
||||
LOGI("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
return -1;
|
||||
}
|
||||
LOGI("SDL AAudio capture:%d frames, wanted:%d frames", (int)res, buflen / private->frame_size);
|
||||
return res * private->frame_size;
|
||||
}
|
||||
|
||||
static void aaudio_Deinitialize(void)
|
||||
{
|
||||
LOGI(__func__);
|
||||
if (ctx.handle) {
|
||||
if (ctx.builder) {
|
||||
aaudio_result_t res;
|
||||
res = ctx.AAudioStreamBuilder_delete(ctx.builder);
|
||||
if (res != AAUDIO_OK) {
|
||||
SDL_SetError("Failed AAudioStreamBuilder_delete %s", ctx.AAudio_convertResultToText(res));
|
||||
}
|
||||
}
|
||||
SDL_UnloadObject(ctx.handle);
|
||||
}
|
||||
ctx.handle = NULL;
|
||||
ctx.builder = NULL;
|
||||
LOGI("End AAUDIO %s", SDL_GetError());
|
||||
}
|
||||
|
||||
static SDL_bool aaudio_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
aaudio_result_t res;
|
||||
LOGI(__func__);
|
||||
|
||||
/* AAudio was introduced in Android 8.0, but has reference counting crash issues in that release,
|
||||
* so don't use it until 8.1.
|
||||
*
|
||||
* See https://github.com/google/oboe/issues/40 for more information.
|
||||
*/
|
||||
if (SDL_GetAndroidSDKVersion() < 27) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
SDL_zero(ctx);
|
||||
|
||||
ctx.handle = SDL_LoadObject(LIB_AAUDIO_SO);
|
||||
if (ctx.handle == NULL) {
|
||||
LOGI("SDL couldn't find " LIB_AAUDIO_SO);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
if (aaudio_LoadFunctions(&ctx) < 0) {
|
||||
goto failure;
|
||||
}
|
||||
|
||||
res = ctx.AAudio_createStreamBuilder(&ctx.builder);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudio_createStreamBuilder %d", res);
|
||||
goto failure;
|
||||
}
|
||||
|
||||
if (ctx.builder == NULL) {
|
||||
LOGI("SDL Failed AAudio_createStreamBuilder - builder NULL");
|
||||
goto failure;
|
||||
}
|
||||
|
||||
impl->DetectDevices = Android_DetectDevices;
|
||||
impl->Deinitialize = aaudio_Deinitialize;
|
||||
impl->OpenDevice = aaudio_OpenDevice;
|
||||
impl->CloseDevice = aaudio_CloseDevice;
|
||||
impl->PlayDevice = aaudio_PlayDevice;
|
||||
impl->GetDeviceBuf = aaudio_GetDeviceBuf;
|
||||
impl->CaptureFromDevice = aaudio_CaptureFromDevice;
|
||||
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
|
||||
|
||||
/* and the capabilities */
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_FALSE;
|
||||
impl->OnlyHasDefaultCaptureDevice = SDL_FALSE;
|
||||
|
||||
/* this audio target is available. */
|
||||
LOGI("SDL aaudio_Init OK");
|
||||
return SDL_TRUE;
|
||||
|
||||
failure:
|
||||
if (ctx.handle) {
|
||||
if (ctx.builder) {
|
||||
ctx.AAudioStreamBuilder_delete(ctx.builder);
|
||||
}
|
||||
SDL_UnloadObject(ctx.handle);
|
||||
}
|
||||
ctx.handle = NULL;
|
||||
ctx.builder = NULL;
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
AudioBootStrap aaudio_bootstrap = {
|
||||
"AAudio", "AAudio audio driver", aaudio_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
/* Pause (block) all non already paused audio devices by taking their mixer lock */
|
||||
void aaudio_PauseDevices(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* AAUDIO driver is not used */
|
||||
if (ctx.handle == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < get_max_num_audio_dev(); i++) {
|
||||
SDL_AudioDevice *_this = get_audio_dev(i);
|
||||
SDL_AudioDevice *audioDevice = NULL;
|
||||
SDL_AudioDevice *captureDevice = NULL;
|
||||
|
||||
if (_this == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_this->iscapture) {
|
||||
captureDevice = _this;
|
||||
} else {
|
||||
audioDevice = _this;
|
||||
}
|
||||
|
||||
if (audioDevice != NULL && audioDevice->hidden != NULL) {
|
||||
struct SDL_PrivateAudioData *private = (struct SDL_PrivateAudioData *)audioDevice->hidden;
|
||||
|
||||
if (private->stream) {
|
||||
aaudio_result_t res = ctx.AAudioStream_requestPause(private->stream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStream_requestPause %d", res);
|
||||
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
}
|
||||
}
|
||||
|
||||
if (SDL_AtomicGet(&audioDevice->paused)) {
|
||||
/* The device is already paused, leave it alone */
|
||||
private->resume = SDL_FALSE;
|
||||
} else {
|
||||
SDL_LockMutex(audioDevice->mixer_lock);
|
||||
SDL_AtomicSet(&audioDevice->paused, 1);
|
||||
private->resume = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (captureDevice != NULL && captureDevice->hidden != NULL) {
|
||||
struct SDL_PrivateAudioData *private = (struct SDL_PrivateAudioData *)audioDevice->hidden;
|
||||
|
||||
if (private->stream) {
|
||||
/* Pause() isn't implemented for 'capture', use Stop() */
|
||||
aaudio_result_t res = ctx.AAudioStream_requestStop(private->stream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStream_requestStop %d", res);
|
||||
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
}
|
||||
}
|
||||
|
||||
if (SDL_AtomicGet(&captureDevice->paused)) {
|
||||
/* The device is already paused, leave it alone */
|
||||
private->resume = SDL_FALSE;
|
||||
} else {
|
||||
SDL_LockMutex(captureDevice->mixer_lock);
|
||||
SDL_AtomicSet(&captureDevice->paused, 1);
|
||||
private->resume = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Resume (unblock) all non already paused audio devices by releasing their mixer lock */
|
||||
void aaudio_ResumeDevices(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* AAUDIO driver is not used */
|
||||
if (ctx.handle == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < get_max_num_audio_dev(); i++) {
|
||||
SDL_AudioDevice *_this = get_audio_dev(i);
|
||||
SDL_AudioDevice *audioDevice = NULL;
|
||||
SDL_AudioDevice *captureDevice = NULL;
|
||||
|
||||
if (_this == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_this->iscapture) {
|
||||
captureDevice = _this;
|
||||
} else {
|
||||
audioDevice = _this;
|
||||
}
|
||||
|
||||
if (audioDevice != NULL && audioDevice->hidden != NULL) {
|
||||
struct SDL_PrivateAudioData *private = audioDevice->hidden;
|
||||
|
||||
if (private->resume) {
|
||||
SDL_AtomicSet(&audioDevice->paused, 0);
|
||||
private->resume = SDL_FALSE;
|
||||
SDL_UnlockMutex(audioDevice->mixer_lock);
|
||||
}
|
||||
|
||||
if (private->stream) {
|
||||
aaudio_result_t res = ctx.AAudioStream_requestStart(private->stream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStream_requestStart %d", res);
|
||||
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (captureDevice != NULL && captureDevice->hidden != NULL) {
|
||||
struct SDL_PrivateAudioData *private = audioDevice->hidden;
|
||||
|
||||
if (private->resume) {
|
||||
SDL_AtomicSet(&captureDevice->paused, 0);
|
||||
private->resume = SDL_FALSE;
|
||||
SDL_UnlockMutex(captureDevice->mixer_lock);
|
||||
}
|
||||
|
||||
if (private->stream) {
|
||||
aaudio_result_t res = ctx.AAudioStream_requestStart(private->stream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOGI("SDL Failed AAudioStream_requestStart %d", res);
|
||||
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
We can sometimes get into a state where AAudioStream_write() will just block forever until we pause and unpause.
|
||||
None of the standard state queries indicate any problem in my testing. And the error callback doesn't actually get called.
|
||||
But, AAudioStream_getTimestamp() does return AAUDIO_ERROR_INVALID_STATE
|
||||
*/
|
||||
SDL_bool aaudio_DetectBrokenPlayState(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* AAUDIO driver is not used */
|
||||
if (ctx.handle == NULL) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
for (i = 0; i < get_max_num_audio_dev(); i++) {
|
||||
SDL_AudioDevice *_this = get_audio_dev(i);
|
||||
SDL_AudioDevice *audioDevice = NULL;
|
||||
SDL_AudioDevice *captureDevice = NULL;
|
||||
|
||||
if (_this == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_this->iscapture) {
|
||||
captureDevice = _this;
|
||||
} else {
|
||||
audioDevice = _this;
|
||||
}
|
||||
|
||||
if (audioDevice != NULL && audioDevice->hidden != NULL) {
|
||||
struct SDL_PrivateAudioData *private = audioDevice->hidden;
|
||||
int64_t framePosition, timeNanoseconds;
|
||||
|
||||
aaudio_result_t res = ctx.AAudioStream_getTimestamp(private->stream, CLOCK_MONOTONIC, &framePosition, &timeNanoseconds);
|
||||
if (res == AAUDIO_ERROR_INVALID_STATE) {
|
||||
aaudio_stream_state_t currentState = ctx.AAudioStream_getState(private->stream);
|
||||
/* AAudioStream_getTimestamp() will also return AAUDIO_ERROR_INVALID_STATE while the stream is still initially starting. But we only care if it silently went invalid while playing. */
|
||||
if (currentState == AAUDIO_STREAM_STATE_STARTED) {
|
||||
LOGI("SDL aaudio_DetectBrokenPlayState: detected invalid audio device state: AAudioStream_getTimestamp result=%d, framePosition=%lld, timeNanoseconds=%lld, getState=%d", (int)res, (long long)framePosition, (long long)timeNanoseconds, (int)currentState);
|
||||
return SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
(void) captureDevice;
|
||||
}
|
||||
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_AAUDIO */
|
40
external/sdl/SDL/src/audio/aaudio/SDL_aaudio.h
vendored
Normal file
40
external/sdl/SDL/src/audio/aaudio/SDL_aaudio.h
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_aaudio_h_
|
||||
#define SDL_aaudio_h_
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_AAUDIO
|
||||
|
||||
void aaudio_ResumeDevices(void);
|
||||
void aaudio_PauseDevices(void);
|
||||
SDL_bool aaudio_DetectBrokenPlayState(void);
|
||||
|
||||
#else
|
||||
|
||||
static void aaudio_ResumeDevices(void) {}
|
||||
static void aaudio_PauseDevices(void) {}
|
||||
static SDL_bool aaudio_DetectBrokenPlayState(void) { return SDL_FALSE; }
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* SDL_aaudio_h_ */
|
79
external/sdl/SDL/src/audio/aaudio/SDL_aaudiofuncs.h
vendored
Normal file
79
external/sdl/SDL/src/audio/aaudio/SDL_aaudiofuncs.h
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright , (C) 1997-2023 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 SDL_PROC_UNUSED(ret, func, params)
|
||||
|
||||
SDL_PROC(const char *, AAudio_convertResultToText, (aaudio_result_t returnCode))
|
||||
SDL_PROC(const char *, AAudio_convertStreamStateToText, (aaudio_stream_state_t state))
|
||||
SDL_PROC(aaudio_result_t, AAudio_createStreamBuilder, (AAudioStreamBuilder * *builder))
|
||||
SDL_PROC(void, AAudioStreamBuilder_setDeviceId, (AAudioStreamBuilder * builder, int32_t deviceId))
|
||||
SDL_PROC(void, AAudioStreamBuilder_setSampleRate, (AAudioStreamBuilder * builder, int32_t sampleRate))
|
||||
SDL_PROC(void, AAudioStreamBuilder_setChannelCount, (AAudioStreamBuilder * builder, int32_t channelCount))
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setSamplesPerFrame, (AAudioStreamBuilder * builder, int32_t samplesPerFrame))
|
||||
SDL_PROC(void, AAudioStreamBuilder_setFormat, (AAudioStreamBuilder * builder, aaudio_format_t format))
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setSharingMode, (AAudioStreamBuilder * builder, aaudio_sharing_mode_t sharingMode))
|
||||
SDL_PROC(void, AAudioStreamBuilder_setDirection, (AAudioStreamBuilder * builder, aaudio_direction_t direction))
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setBufferCapacityInFrames, (AAudioStreamBuilder * builder, int32_t numFrames))
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setPerformanceMode, (AAudioStreamBuilder * builder, aaudio_performance_mode_t mode))
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setUsage, (AAudioStreamBuilder * builder, aaudio_usage_t usage)) /* API 28 */
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setContentType, (AAudioStreamBuilder * builder, aaudio_content_type_t contentType)) /* API 28 */
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setInputPreset, (AAudioStreamBuilder * builder, aaudio_input_preset_t inputPreset)) /* API 28 */
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setAllowedCapturePolicy, (AAudioStreamBuilder * builder, aaudio_allowed_capture_policy_t capturePolicy)) /* API 29 */
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setSessionId, (AAudioStreamBuilder * builder, aaudio_session_id_t sessionId)) /* API 28 */
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setPrivacySensitive, (AAudioStreamBuilder * builder, bool privacySensitive)) /* API 30 */
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setDataCallback, (AAudioStreamBuilder * builder, AAudioStream_dataCallback callback, void *userData))
|
||||
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setFramesPerDataCallback, (AAudioStreamBuilder * builder, int32_t numFrames))
|
||||
SDL_PROC(void, AAudioStreamBuilder_setErrorCallback, (AAudioStreamBuilder * builder, AAudioStream_errorCallback callback, void *userData))
|
||||
SDL_PROC(aaudio_result_t, AAudioStreamBuilder_openStream, (AAudioStreamBuilder * builder, AAudioStream **stream))
|
||||
SDL_PROC(aaudio_result_t, AAudioStreamBuilder_delete, (AAudioStreamBuilder * builder))
|
||||
SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_release, (AAudioStream * stream)) /* API 30 */
|
||||
SDL_PROC(aaudio_result_t, AAudioStream_close, (AAudioStream * stream))
|
||||
SDL_PROC(aaudio_result_t, AAudioStream_requestStart, (AAudioStream * stream))
|
||||
SDL_PROC(aaudio_result_t, AAudioStream_requestPause, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_requestFlush, (AAudioStream * stream))
|
||||
SDL_PROC(aaudio_result_t, AAudioStream_requestStop, (AAudioStream * stream))
|
||||
SDL_PROC(aaudio_stream_state_t, AAudioStream_getState, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_waitForStateChange, (AAudioStream * stream, aaudio_stream_state_t inputState, aaudio_stream_state_t *nextState, int64_t timeoutNanoseconds))
|
||||
SDL_PROC(aaudio_result_t, AAudioStream_read, (AAudioStream * stream, void *buffer, int32_t numFrames, int64_t timeoutNanoseconds))
|
||||
SDL_PROC(aaudio_result_t, AAudioStream_write, (AAudioStream * stream, const void *buffer, int32_t numFrames, int64_t timeoutNanoseconds))
|
||||
SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_setBufferSizeInFrames, (AAudioStream * stream, int32_t numFrames))
|
||||
SDL_PROC_UNUSED(int32_t, AAudioStream_getBufferSizeInFrames, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(int32_t, AAudioStream_getFramesPerBurst, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(int32_t, AAudioStream_getBufferCapacityInFrames, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(int32_t, AAudioStream_getFramesPerDataCallback, (AAudioStream * stream))
|
||||
SDL_PROC(int32_t, AAudioStream_getXRunCount, (AAudioStream * stream))
|
||||
SDL_PROC(int32_t, AAudioStream_getSampleRate, (AAudioStream * stream))
|
||||
SDL_PROC(int32_t, AAudioStream_getChannelCount, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(int32_t, AAudioStream_getSamplesPerFrame, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(int32_t, AAudioStream_getDeviceId, (AAudioStream * stream))
|
||||
SDL_PROC(aaudio_format_t, AAudioStream_getFormat, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(aaudio_sharing_mode_t, AAudioStream_getSharingMode, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(aaudio_performance_mode_t, AAudioStream_getPerformanceMode, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(aaudio_direction_t, AAudioStream_getDirection, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(int64_t, AAudioStream_getFramesWritten, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(int64_t, AAudioStream_getFramesRead, (AAudioStream * stream))
|
||||
SDL_PROC_UNUSED(aaudio_session_id_t, AAudioStream_getSessionId, (AAudioStream * stream)) /* API 28 */
|
||||
SDL_PROC(aaudio_result_t, AAudioStream_getTimestamp, (AAudioStream * stream, clockid_t clockid, int64_t *framePosition, int64_t *timeNanoseconds))
|
||||
SDL_PROC_UNUSED(aaudio_usage_t, AAudioStream_getUsage, (AAudioStream * stream)) /* API 28 */
|
||||
SDL_PROC_UNUSED(aaudio_content_type_t, AAudioStream_getContentType, (AAudioStream * stream)) /* API 28 */
|
||||
SDL_PROC_UNUSED(aaudio_input_preset_t, AAudioStream_getInputPreset, (AAudioStream * stream)) /* API 28 */
|
||||
SDL_PROC_UNUSED(aaudio_allowed_capture_policy_t, AAudioStream_getAllowedCapturePolicy, (AAudioStream * stream)) /* API 29 */
|
||||
SDL_PROC_UNUSED(bool, AAudioStream_isPrivacySensitive, (AAudioStream * stream)) /* API 30 */
|
982
external/sdl/SDL/src/audio/alsa/SDL_alsa_audio.c
vendored
Normal file
982
external/sdl/SDL/src/audio/alsa/SDL_alsa_audio.c
vendored
Normal file
@ -0,0 +1,982 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_AUDIO_DRIVER_ALSA
|
||||
|
||||
#ifndef SDL_ALSA_NON_BLOCKING
|
||||
#define SDL_ALSA_NON_BLOCKING 0
|
||||
#endif
|
||||
|
||||
/* without the thread, you will detect devices on startup, but will not get further hotplug events. But that might be okay. */
|
||||
#ifndef SDL_ALSA_HOTPLUG_THREAD
|
||||
#define SDL_ALSA_HOTPLUG_THREAD 1
|
||||
#endif
|
||||
|
||||
/* Allow access to a raw mixing buffer */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <signal.h> /* For kill() */
|
||||
#include <string.h>
|
||||
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_alsa_audio.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
|
||||
#endif
|
||||
|
||||
static int (*ALSA_snd_pcm_open)(snd_pcm_t **, const char *, snd_pcm_stream_t, int);
|
||||
static int (*ALSA_snd_pcm_close)(snd_pcm_t *pcm);
|
||||
static snd_pcm_sframes_t (*ALSA_snd_pcm_writei)(snd_pcm_t *, const void *, snd_pcm_uframes_t);
|
||||
static snd_pcm_sframes_t (*ALSA_snd_pcm_readi)(snd_pcm_t *, void *, snd_pcm_uframes_t);
|
||||
static int (*ALSA_snd_pcm_recover)(snd_pcm_t *, int, int);
|
||||
static int (*ALSA_snd_pcm_prepare)(snd_pcm_t *);
|
||||
static int (*ALSA_snd_pcm_drain)(snd_pcm_t *);
|
||||
static const char *(*ALSA_snd_strerror)(int);
|
||||
static size_t (*ALSA_snd_pcm_hw_params_sizeof)(void);
|
||||
static size_t (*ALSA_snd_pcm_sw_params_sizeof)(void);
|
||||
static void (*ALSA_snd_pcm_hw_params_copy)(snd_pcm_hw_params_t *, const snd_pcm_hw_params_t *);
|
||||
static int (*ALSA_snd_pcm_hw_params_any)(snd_pcm_t *, snd_pcm_hw_params_t *);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_access)(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_format)(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_channels)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int);
|
||||
static int (*ALSA_snd_pcm_hw_params_get_channels)(const snd_pcm_hw_params_t *, unsigned int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_rate_near)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_period_size_near)(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_get_period_size)(const snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_periods_min)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_periods_first)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_get_periods)(const snd_pcm_hw_params_t *, unsigned int *, int *);
|
||||
static int (*ALSA_snd_pcm_hw_params_set_buffer_size_near)(snd_pcm_t *pcm, snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
|
||||
static int (*ALSA_snd_pcm_hw_params_get_buffer_size)(const snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
|
||||
static int (*ALSA_snd_pcm_hw_params)(snd_pcm_t *, snd_pcm_hw_params_t *);
|
||||
static int (*ALSA_snd_pcm_sw_params_current)(snd_pcm_t *,
|
||||
snd_pcm_sw_params_t *);
|
||||
static int (*ALSA_snd_pcm_sw_params_set_start_threshold)(snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
|
||||
static int (*ALSA_snd_pcm_sw_params)(snd_pcm_t *, snd_pcm_sw_params_t *);
|
||||
static int (*ALSA_snd_pcm_nonblock)(snd_pcm_t *, int);
|
||||
static int (*ALSA_snd_pcm_wait)(snd_pcm_t *, int);
|
||||
static int (*ALSA_snd_pcm_sw_params_set_avail_min)(snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
|
||||
static int (*ALSA_snd_pcm_reset)(snd_pcm_t *);
|
||||
static int (*ALSA_snd_device_name_hint)(int, const char *, void ***);
|
||||
static char *(*ALSA_snd_device_name_get_hint)(const void *, const char *);
|
||||
static int (*ALSA_snd_device_name_free_hint)(void **);
|
||||
static snd_pcm_sframes_t (*ALSA_snd_pcm_avail)(snd_pcm_t *);
|
||||
#ifdef SND_CHMAP_API_VERSION
|
||||
static snd_pcm_chmap_t *(*ALSA_snd_pcm_get_chmap)(snd_pcm_t *);
|
||||
static int (*ALSA_snd_pcm_chmap_print)(const snd_pcm_chmap_t *map, size_t maxlen, char *buf);
|
||||
#endif
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
|
||||
#define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof
|
||||
#define snd_pcm_sw_params_sizeof ALSA_snd_pcm_sw_params_sizeof
|
||||
|
||||
static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC;
|
||||
static void *alsa_handle = NULL;
|
||||
|
||||
static int load_alsa_sym(const char *fn, void **addr)
|
||||
{
|
||||
*addr = SDL_LoadFunction(alsa_handle, fn);
|
||||
if (*addr == NULL) {
|
||||
/* Don't call SDL_SetError(): SDL_LoadFunction already did. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* cast funcs to char* first, to please GCC's strict aliasing rules. */
|
||||
#define SDL_ALSA_SYM(x) \
|
||||
if (!load_alsa_sym(#x, (void **)(char *)&ALSA_##x)) \
|
||||
return -1
|
||||
#else
|
||||
#define SDL_ALSA_SYM(x) ALSA_##x = x
|
||||
#endif
|
||||
|
||||
static int load_alsa_syms(void)
|
||||
{
|
||||
SDL_ALSA_SYM(snd_pcm_open);
|
||||
SDL_ALSA_SYM(snd_pcm_close);
|
||||
SDL_ALSA_SYM(snd_pcm_writei);
|
||||
SDL_ALSA_SYM(snd_pcm_readi);
|
||||
SDL_ALSA_SYM(snd_pcm_recover);
|
||||
SDL_ALSA_SYM(snd_pcm_prepare);
|
||||
SDL_ALSA_SYM(snd_pcm_drain);
|
||||
SDL_ALSA_SYM(snd_strerror);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_sizeof);
|
||||
SDL_ALSA_SYM(snd_pcm_sw_params_sizeof);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_copy);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_any);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_access);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_format);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_channels);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_get_channels);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_rate_near);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_period_size_near);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_get_period_size);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_min);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_first);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_get_periods);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_set_buffer_size_near);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params_get_buffer_size);
|
||||
SDL_ALSA_SYM(snd_pcm_hw_params);
|
||||
SDL_ALSA_SYM(snd_pcm_sw_params_current);
|
||||
SDL_ALSA_SYM(snd_pcm_sw_params_set_start_threshold);
|
||||
SDL_ALSA_SYM(snd_pcm_sw_params);
|
||||
SDL_ALSA_SYM(snd_pcm_nonblock);
|
||||
SDL_ALSA_SYM(snd_pcm_wait);
|
||||
SDL_ALSA_SYM(snd_pcm_sw_params_set_avail_min);
|
||||
SDL_ALSA_SYM(snd_pcm_reset);
|
||||
SDL_ALSA_SYM(snd_device_name_hint);
|
||||
SDL_ALSA_SYM(snd_device_name_get_hint);
|
||||
SDL_ALSA_SYM(snd_device_name_free_hint);
|
||||
SDL_ALSA_SYM(snd_pcm_avail);
|
||||
#ifdef SND_CHMAP_API_VERSION
|
||||
SDL_ALSA_SYM(snd_pcm_get_chmap);
|
||||
SDL_ALSA_SYM(snd_pcm_chmap_print);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef SDL_ALSA_SYM
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
|
||||
|
||||
static void UnloadALSALibrary(void)
|
||||
{
|
||||
if (alsa_handle != NULL) {
|
||||
SDL_UnloadObject(alsa_handle);
|
||||
alsa_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int LoadALSALibrary(void)
|
||||
{
|
||||
int retval = 0;
|
||||
if (alsa_handle == NULL) {
|
||||
alsa_handle = SDL_LoadObject(alsa_library);
|
||||
if (alsa_handle == NULL) {
|
||||
retval = -1;
|
||||
/* Don't call SDL_SetError(): SDL_LoadObject already did. */
|
||||
} else {
|
||||
retval = load_alsa_syms();
|
||||
if (retval < 0) {
|
||||
UnloadALSALibrary();
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void UnloadALSALibrary(void)
|
||||
{
|
||||
}
|
||||
|
||||
static int LoadALSALibrary(void)
|
||||
{
|
||||
load_alsa_syms();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */
|
||||
|
||||
static const char *get_audio_device(void *handle, const int channels)
|
||||
{
|
||||
const char *device;
|
||||
|
||||
if (handle != NULL) {
|
||||
return (const char *)handle;
|
||||
}
|
||||
|
||||
/* !!! FIXME: we also check "SDL_AUDIO_DEVICE_NAME" at the higher level. */
|
||||
device = SDL_getenv("AUDIODEV"); /* Is there a standard variable name? */
|
||||
if (device != NULL) {
|
||||
return device;
|
||||
}
|
||||
|
||||
if (channels == 6) {
|
||||
return "plug:surround51";
|
||||
} else if (channels == 4) {
|
||||
return "plug:surround40";
|
||||
}
|
||||
|
||||
return "default";
|
||||
}
|
||||
|
||||
/* This function waits until it is possible to write a full sound buffer */
|
||||
static void ALSA_WaitDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
#if SDL_ALSA_NON_BLOCKING
|
||||
const snd_pcm_sframes_t needed = (snd_pcm_sframes_t)_this->spec.samples;
|
||||
while (SDL_AtomicGet(&_this->enabled)) {
|
||||
const snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(_this->hidden->pcm_handle);
|
||||
if ((rc < 0) && (rc != -EAGAIN)) {
|
||||
/* Hmm, not much we can do - abort */
|
||||
fprintf(stderr, "ALSA snd_pcm_avail failed (unrecoverable): %s\n",
|
||||
ALSA_snd_strerror(rc));
|
||||
SDL_OpenedAudioDeviceDisconnected(_this);
|
||||
return;
|
||||
} else if (rc < needed) {
|
||||
const Uint32 delay = ((needed - (SDL_max(rc, 0))) * 1000) / _this->spec.freq;
|
||||
SDL_Delay(SDL_max(delay, 10));
|
||||
} else {
|
||||
break; /* ready to go! */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* !!! FIXME: is there a channel swizzler in alsalib instead? */
|
||||
/*
|
||||
* https://bugzilla.libsdl.org/show_bug.cgi?id=110
|
||||
* "For Linux ALSA, this is FL-FR-RL-RR-C-LFE
|
||||
* and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR"
|
||||
*/
|
||||
#define SWIZ6(T) \
|
||||
static void swizzle_alsa_channels_6_##T(void *buffer, const Uint32 bufferlen) \
|
||||
{ \
|
||||
T *ptr = (T *)buffer; \
|
||||
Uint32 i; \
|
||||
for (i = 0; i < bufferlen; i++, ptr += 6) { \
|
||||
T tmp; \
|
||||
tmp = ptr[2]; \
|
||||
ptr[2] = ptr[4]; \
|
||||
ptr[4] = tmp; \
|
||||
tmp = ptr[3]; \
|
||||
ptr[3] = ptr[5]; \
|
||||
ptr[5] = tmp; \
|
||||
} \
|
||||
}
|
||||
|
||||
/* !!! FIXME: is there a channel swizzler in alsalib instead? */
|
||||
/* !!! FIXME: this screams for a SIMD shuffle operation. */
|
||||
/*
|
||||
* https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/mapping-stream-formats-to-speaker-configurations
|
||||
* For Linux ALSA, this appears to be FL-FR-RL-RR-C-LFE-SL-SR
|
||||
* and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-SL-SR-RL-RR"
|
||||
*/
|
||||
#define SWIZ8(T) \
|
||||
static void swizzle_alsa_channels_8_##T(void *buffer, const Uint32 bufferlen) \
|
||||
{ \
|
||||
T *ptr = (T *)buffer; \
|
||||
Uint32 i; \
|
||||
for (i = 0; i < bufferlen; i++, ptr += 6) { \
|
||||
const T center = ptr[2]; \
|
||||
const T subwoofer = ptr[3]; \
|
||||
const T side_left = ptr[4]; \
|
||||
const T side_right = ptr[5]; \
|
||||
const T rear_left = ptr[6]; \
|
||||
const T rear_right = ptr[7]; \
|
||||
ptr[2] = rear_left; \
|
||||
ptr[3] = rear_right; \
|
||||
ptr[4] = center; \
|
||||
ptr[5] = subwoofer; \
|
||||
ptr[6] = side_left; \
|
||||
ptr[7] = side_right; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CHANNEL_SWIZZLE(x) \
|
||||
x(Uint64) \
|
||||
x(Uint32) \
|
||||
x(Uint16) \
|
||||
x(Uint8)
|
||||
|
||||
CHANNEL_SWIZZLE(SWIZ6)
|
||||
CHANNEL_SWIZZLE(SWIZ8)
|
||||
|
||||
#undef CHANNEL_SWIZZLE
|
||||
#undef SWIZ6
|
||||
#undef SWIZ8
|
||||
|
||||
/*
|
||||
* Called right before feeding _this->hidden->mixbuf to the hardware. Swizzle
|
||||
* channels from Windows/Mac order to the format alsalib will want.
|
||||
*/
|
||||
static void swizzle_alsa_channels(SDL_AudioDevice *_this, void *buffer, Uint32 bufferlen)
|
||||
{
|
||||
switch (_this->spec.channels) {
|
||||
#define CHANSWIZ(chans) \
|
||||
case chans: \
|
||||
switch ((_this->spec.format & (0xFF))) { \
|
||||
case 8: \
|
||||
swizzle_alsa_channels_##chans##_Uint8(buffer, bufferlen); \
|
||||
break; \
|
||||
case 16: \
|
||||
swizzle_alsa_channels_##chans##_Uint16(buffer, bufferlen); \
|
||||
break; \
|
||||
case 32: \
|
||||
swizzle_alsa_channels_##chans##_Uint32(buffer, bufferlen); \
|
||||
break; \
|
||||
case 64: \
|
||||
swizzle_alsa_channels_##chans##_Uint64(buffer, bufferlen); \
|
||||
break; \
|
||||
default: \
|
||||
SDL_assert(!"unhandled bitsize"); \
|
||||
break; \
|
||||
} \
|
||||
return;
|
||||
|
||||
CHANSWIZ(6);
|
||||
CHANSWIZ(8);
|
||||
#undef CHANSWIZ
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SND_CHMAP_API_VERSION
|
||||
/* Some devices have the right channel map, no swizzling necessary */
|
||||
static void no_swizzle(SDL_AudioDevice *_this, void *buffer, Uint32 bufferlen)
|
||||
{
|
||||
}
|
||||
#endif /* SND_CHMAP_API_VERSION */
|
||||
|
||||
static void ALSA_PlayDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
const Uint8 *sample_buf = (const Uint8 *)_this->hidden->mixbuf;
|
||||
const int frame_size = ((SDL_AUDIO_BITSIZE(_this->spec.format)) / 8) *
|
||||
_this->spec.channels;
|
||||
snd_pcm_uframes_t frames_left = ((snd_pcm_uframes_t)_this->spec.samples);
|
||||
|
||||
_this->hidden->swizzle_func(_this, _this->hidden->mixbuf, frames_left);
|
||||
|
||||
while (frames_left > 0 && SDL_AtomicGet(&_this->enabled)) {
|
||||
int status = ALSA_snd_pcm_writei(_this->hidden->pcm_handle,
|
||||
sample_buf, frames_left);
|
||||
|
||||
if (status < 0) {
|
||||
if (status == -EAGAIN) {
|
||||
/* Apparently snd_pcm_recover() doesn't handle this case -
|
||||
does it assume snd_pcm_wait() above? */
|
||||
SDL_Delay(1);
|
||||
continue;
|
||||
}
|
||||
status = ALSA_snd_pcm_recover(_this->hidden->pcm_handle, status, 0);
|
||||
if (status < 0) {
|
||||
/* Hmm, not much we can do - abort */
|
||||
SDL_LogError(SDL_LOG_CATEGORY_AUDIO,
|
||||
"ALSA write failed (unrecoverable): %s\n",
|
||||
ALSA_snd_strerror(status));
|
||||
SDL_OpenedAudioDeviceDisconnected(_this);
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
} else if (status == 0) {
|
||||
/* No frames were written (no available space in pcm device).
|
||||
Allow other threads to catch up. */
|
||||
Uint32 delay = (frames_left / 2 * 1000) / _this->spec.freq;
|
||||
SDL_Delay(delay);
|
||||
}
|
||||
|
||||
sample_buf += status * frame_size;
|
||||
frames_left -= status;
|
||||
}
|
||||
}
|
||||
|
||||
static Uint8 *ALSA_GetDeviceBuf(SDL_AudioDevice *_this)
|
||||
{
|
||||
return _this->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static int ALSA_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
|
||||
{
|
||||
Uint8 *sample_buf = (Uint8 *)buffer;
|
||||
const int frame_size = ((SDL_AUDIO_BITSIZE(_this->spec.format)) / 8) *
|
||||
_this->spec.channels;
|
||||
const int total_frames = buflen / frame_size;
|
||||
snd_pcm_uframes_t frames_left = total_frames;
|
||||
snd_pcm_uframes_t wait_time = frame_size / 2;
|
||||
|
||||
SDL_assert((buflen % frame_size) == 0);
|
||||
|
||||
while (frames_left > 0 && SDL_AtomicGet(&_this->enabled)) {
|
||||
int status;
|
||||
|
||||
status = ALSA_snd_pcm_readi(_this->hidden->pcm_handle,
|
||||
sample_buf, frames_left);
|
||||
|
||||
if (status == -EAGAIN) {
|
||||
ALSA_snd_pcm_wait(_this->hidden->pcm_handle, wait_time);
|
||||
status = 0;
|
||||
} else if (status < 0) {
|
||||
/*printf("ALSA: capture error %d\n", status);*/
|
||||
status = ALSA_snd_pcm_recover(_this->hidden->pcm_handle, status, 0);
|
||||
if (status < 0) {
|
||||
/* Hmm, not much we can do - abort */
|
||||
SDL_LogError(SDL_LOG_CATEGORY_AUDIO,
|
||||
"ALSA read failed (unrecoverable): %s\n",
|
||||
ALSA_snd_strerror(status));
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/*printf("ALSA: captured %d bytes\n", status * frame_size);*/
|
||||
sample_buf += status * frame_size;
|
||||
frames_left -= status;
|
||||
}
|
||||
|
||||
_this->hidden->swizzle_func(_this, buffer, total_frames - frames_left);
|
||||
|
||||
return (total_frames - frames_left) * frame_size;
|
||||
}
|
||||
|
||||
static void ALSA_FlushCapture(SDL_AudioDevice *_this)
|
||||
{
|
||||
ALSA_snd_pcm_reset(_this->hidden->pcm_handle);
|
||||
}
|
||||
|
||||
static void ALSA_CloseDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
if (_this->hidden->pcm_handle) {
|
||||
/* Wait for the submitted audio to drain
|
||||
ALSA_snd_pcm_drop() can hang, so don't use that.
|
||||
*/
|
||||
Uint32 delay = ((_this->spec.samples * 1000) / _this->spec.freq) * 2;
|
||||
SDL_Delay(delay);
|
||||
|
||||
ALSA_snd_pcm_close(_this->hidden->pcm_handle);
|
||||
}
|
||||
SDL_free(_this->hidden->mixbuf);
|
||||
SDL_free(_this->hidden);
|
||||
}
|
||||
|
||||
static int ALSA_set_buffer_size(SDL_AudioDevice *_this, snd_pcm_hw_params_t *params)
|
||||
{
|
||||
int status;
|
||||
snd_pcm_hw_params_t *hwparams;
|
||||
snd_pcm_uframes_t persize;
|
||||
unsigned int periods;
|
||||
|
||||
/* Copy the hardware parameters for this setup */
|
||||
snd_pcm_hw_params_alloca(&hwparams);
|
||||
ALSA_snd_pcm_hw_params_copy(hwparams, params);
|
||||
|
||||
/* Attempt to match the period size to the requested buffer size */
|
||||
persize = _this->spec.samples;
|
||||
status = ALSA_snd_pcm_hw_params_set_period_size_near(
|
||||
_this->hidden->pcm_handle, hwparams, &persize, NULL);
|
||||
if (status < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Need to at least double buffer */
|
||||
periods = 2;
|
||||
status = ALSA_snd_pcm_hw_params_set_periods_min(
|
||||
_this->hidden->pcm_handle, hwparams, &periods, NULL);
|
||||
if (status < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = ALSA_snd_pcm_hw_params_set_periods_first(
|
||||
_this->hidden->pcm_handle, hwparams, &periods, NULL);
|
||||
if (status < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* "set" the hardware with the desired parameters */
|
||||
status = ALSA_snd_pcm_hw_params(_this->hidden->pcm_handle, hwparams);
|
||||
if (status < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
_this->spec.samples = persize;
|
||||
|
||||
/* This is useful for debugging */
|
||||
if (SDL_getenv("SDL_AUDIO_ALSA_DEBUG")) {
|
||||
snd_pcm_uframes_t bufsize;
|
||||
|
||||
ALSA_snd_pcm_hw_params_get_buffer_size(hwparams, &bufsize);
|
||||
|
||||
SDL_LogError(SDL_LOG_CATEGORY_AUDIO,
|
||||
"ALSA: period size = %ld, periods = %u, buffer size = %lu\n",
|
||||
persize, periods, bufsize);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ALSA_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
{
|
||||
int status = 0;
|
||||
SDL_bool iscapture = _this->iscapture;
|
||||
snd_pcm_t *pcm_handle = NULL;
|
||||
snd_pcm_hw_params_t *hwparams = NULL;
|
||||
snd_pcm_sw_params_t *swparams = NULL;
|
||||
snd_pcm_format_t format = 0;
|
||||
SDL_AudioFormat test_format = 0;
|
||||
const SDL_AudioFormat *closefmts;
|
||||
unsigned int rate = 0;
|
||||
unsigned int channels = 0;
|
||||
#ifdef SND_CHMAP_API_VERSION
|
||||
snd_pcm_chmap_t *chmap;
|
||||
char chmap_str[64];
|
||||
#endif
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
_this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(*_this->hidden));
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(_this->hidden);
|
||||
|
||||
/* Open the audio device */
|
||||
/* Name of device should depend on # channels in spec */
|
||||
status = ALSA_snd_pcm_open(&pcm_handle,
|
||||
get_audio_device(_this->handle, _this->spec.channels),
|
||||
iscapture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
|
||||
SND_PCM_NONBLOCK);
|
||||
|
||||
if (status < 0) {
|
||||
return SDL_SetError("ALSA: Couldn't open audio device: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
|
||||
_this->hidden->pcm_handle = pcm_handle;
|
||||
|
||||
/* Figure out what the hardware is capable of */
|
||||
snd_pcm_hw_params_alloca(&hwparams);
|
||||
status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("ALSA: Couldn't get hardware config: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
|
||||
/* SDL only uses interleaved sample output */
|
||||
status = ALSA_snd_pcm_hw_params_set_access(pcm_handle, hwparams,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("ALSA: Couldn't set interleaved access: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
|
||||
/* Try for a closest match on audio format */
|
||||
closefmts = SDL_ClosestAudioFormats(_this->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
switch (test_format) {
|
||||
case SDL_AUDIO_U8:
|
||||
format = SND_PCM_FORMAT_U8;
|
||||
break;
|
||||
case SDL_AUDIO_S8:
|
||||
format = SND_PCM_FORMAT_S8;
|
||||
break;
|
||||
case SDL_AUDIO_S16LSB:
|
||||
format = SND_PCM_FORMAT_S16_LE;
|
||||
break;
|
||||
case SDL_AUDIO_S16MSB:
|
||||
format = SND_PCM_FORMAT_S16_BE;
|
||||
break;
|
||||
case SDL_AUDIO_S32LSB:
|
||||
format = SND_PCM_FORMAT_S32_LE;
|
||||
break;
|
||||
case SDL_AUDIO_S32MSB:
|
||||
format = SND_PCM_FORMAT_S32_BE;
|
||||
break;
|
||||
case SDL_AUDIO_F32LSB:
|
||||
format = SND_PCM_FORMAT_FLOAT_LE;
|
||||
break;
|
||||
case SDL_AUDIO_F32MSB:
|
||||
format = SND_PCM_FORMAT_FLOAT_BE;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
if (ALSA_snd_pcm_hw_params_set_format(pcm_handle, hwparams, format) >= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!test_format) {
|
||||
return SDL_SetError("%s: Unsupported audio format", "alsa");
|
||||
}
|
||||
_this->spec.format = test_format;
|
||||
|
||||
/* Validate number of channels and determine if swizzling is necessary
|
||||
* Assume original swizzling, until proven otherwise.
|
||||
*/
|
||||
_this->hidden->swizzle_func = swizzle_alsa_channels;
|
||||
#ifdef SND_CHMAP_API_VERSION
|
||||
chmap = ALSA_snd_pcm_get_chmap(pcm_handle);
|
||||
if (chmap) {
|
||||
if (ALSA_snd_pcm_chmap_print(chmap, sizeof(chmap_str), chmap_str) > 0) {
|
||||
if (SDL_strcmp("FL FR FC LFE RL RR", chmap_str) == 0 ||
|
||||
SDL_strcmp("FL FR FC LFE SL SR", chmap_str) == 0) {
|
||||
_this->hidden->swizzle_func = no_swizzle;
|
||||
}
|
||||
}
|
||||
free(chmap); /* This should NOT be SDL_free() */
|
||||
}
|
||||
#endif /* SND_CHMAP_API_VERSION */
|
||||
|
||||
/* Set the number of channels */
|
||||
status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams,
|
||||
_this->spec.channels);
|
||||
channels = _this->spec.channels;
|
||||
if (status < 0) {
|
||||
status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("ALSA: Couldn't set audio channels");
|
||||
}
|
||||
_this->spec.channels = channels;
|
||||
}
|
||||
|
||||
/* Set the audio rate */
|
||||
rate = _this->spec.freq;
|
||||
status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams,
|
||||
&rate, NULL);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("ALSA: Couldn't set audio frequency: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
_this->spec.freq = rate;
|
||||
|
||||
/* Set the buffer size, in samples */
|
||||
status = ALSA_set_buffer_size(_this, hwparams);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
|
||||
/* Set the software parameters */
|
||||
snd_pcm_sw_params_alloca(&swparams);
|
||||
status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("ALSA: Couldn't get software config: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
status = ALSA_snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, _this->spec.samples);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("Couldn't set minimum available samples: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
status =
|
||||
ALSA_snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, 1);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("ALSA: Couldn't set start threshold: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
status = ALSA_snd_pcm_sw_params(pcm_handle, swparams);
|
||||
if (status < 0) {
|
||||
return SDL_SetError("Couldn't set software audio parameters: %s", ALSA_snd_strerror(status));
|
||||
}
|
||||
|
||||
/* Calculate the final parameters for this audio specification */
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
/* Allocate mixing buffer */
|
||||
if (!iscapture) {
|
||||
_this->hidden->mixlen = _this->spec.size;
|
||||
_this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->hidden->mixlen);
|
||||
if (_this->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(_this->hidden->mixbuf, _this->spec.silence, _this->hidden->mixlen);
|
||||
}
|
||||
|
||||
#if !SDL_ALSA_NON_BLOCKING
|
||||
if (!iscapture) {
|
||||
ALSA_snd_pcm_nonblock(pcm_handle, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* We're ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct ALSA_Device
|
||||
{
|
||||
char *name;
|
||||
SDL_bool iscapture;
|
||||
struct ALSA_Device *next;
|
||||
} ALSA_Device;
|
||||
|
||||
static void add_device(const int iscapture, const char *name, void *hint, ALSA_Device **pSeen)
|
||||
{
|
||||
ALSA_Device *dev = SDL_malloc(sizeof(ALSA_Device));
|
||||
char *desc;
|
||||
char *handle = NULL;
|
||||
char *ptr;
|
||||
|
||||
if (dev == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Not all alsa devices are enumerable via snd_device_name_get_hint
|
||||
(i.e. bluetooth devices). Therefore if hint is passed in to this
|
||||
function as NULL, assume name contains desc.
|
||||
Make sure not to free the storage associated with desc in this case */
|
||||
if (hint) {
|
||||
desc = ALSA_snd_device_name_get_hint(hint, "DESC");
|
||||
if (desc == NULL) {
|
||||
SDL_free(dev);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
desc = (char *)name;
|
||||
}
|
||||
|
||||
SDL_assert(name != NULL);
|
||||
|
||||
/* some strings have newlines, like "HDA NVidia, HDMI 0\nHDMI Audio Output".
|
||||
just chop the extra lines off, this seems to get a reasonable device
|
||||
name without extra details. */
|
||||
ptr = SDL_strchr(desc, '\n');
|
||||
if (ptr != NULL) {
|
||||
*ptr = '\0';
|
||||
}
|
||||
|
||||
/*printf("ALSA: adding %s device '%s' (%s)\n", iscapture ? "capture" : "output", name, desc);*/
|
||||
|
||||
handle = SDL_strdup(name);
|
||||
if (handle == NULL) {
|
||||
if (hint) {
|
||||
free(desc); /* This should NOT be SDL_free() */
|
||||
}
|
||||
SDL_free(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Note that spec is NULL, because we are required to open the device before
|
||||
* acquiring the mix format, making this information inaccessible at
|
||||
* enumeration time
|
||||
*/
|
||||
SDL_AddAudioDevice(iscapture, desc, NULL, handle);
|
||||
if (hint) {
|
||||
free(desc); /* This should NOT be SDL_free() */
|
||||
}
|
||||
dev->name = handle;
|
||||
dev->iscapture = iscapture;
|
||||
dev->next = *pSeen;
|
||||
*pSeen = dev;
|
||||
}
|
||||
|
||||
static ALSA_Device *hotplug_devices = NULL;
|
||||
|
||||
static void ALSA_HotplugIteration(void)
|
||||
{
|
||||
void **hints = NULL;
|
||||
ALSA_Device *dev;
|
||||
ALSA_Device *unseen;
|
||||
ALSA_Device *seen;
|
||||
ALSA_Device *next;
|
||||
ALSA_Device *prev;
|
||||
|
||||
if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == 0) {
|
||||
int i, j;
|
||||
const char *match = NULL;
|
||||
int bestmatch = 0xFFFF;
|
||||
size_t match_len = 0;
|
||||
int defaultdev = -1;
|
||||
static const char *const prefixes[] = {
|
||||
"hw:", "sysdefault:", "default:", NULL
|
||||
};
|
||||
|
||||
unseen = hotplug_devices;
|
||||
seen = NULL;
|
||||
|
||||
/* Apparently there are several different ways that ALSA lists
|
||||
actual hardware. It could be prefixed with "hw:" or "default:"
|
||||
or "sysdefault:" and maybe others. Go through the list and see
|
||||
if we can find a preferred prefix for the system. */
|
||||
for (i = 0; hints[i]; i++) {
|
||||
char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
|
||||
if (name == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* full name, not a prefix */
|
||||
if ((defaultdev == -1) && (SDL_strcmp(name, "default") == 0)) {
|
||||
defaultdev = i;
|
||||
}
|
||||
|
||||
for (j = 0; prefixes[j]; j++) {
|
||||
const char *prefix = prefixes[j];
|
||||
const size_t prefixlen = SDL_strlen(prefix);
|
||||
if (SDL_strncmp(name, prefix, prefixlen) == 0) {
|
||||
if (j < bestmatch) {
|
||||
bestmatch = j;
|
||||
match = prefix;
|
||||
match_len = prefixlen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(name); /* This should NOT be SDL_free() */
|
||||
}
|
||||
|
||||
/* look through the list of device names to find matches */
|
||||
for (i = 0; hints[i]; i++) {
|
||||
char *name;
|
||||
|
||||
/* if we didn't find a device name prefix we like at all... */
|
||||
if ((match == NULL) && (defaultdev != i)) {
|
||||
continue; /* ...skip anything that isn't the default device. */
|
||||
}
|
||||
|
||||
name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
|
||||
if (name == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* only want physical hardware interfaces */
|
||||
if (match == NULL || (SDL_strncmp(name, match, match_len) == 0)) {
|
||||
char *ioid = ALSA_snd_device_name_get_hint(hints[i], "IOID");
|
||||
const SDL_bool isoutput = (ioid == NULL) || (SDL_strcmp(ioid, "Output") == 0);
|
||||
const SDL_bool isinput = (ioid == NULL) || (SDL_strcmp(ioid, "Input") == 0);
|
||||
SDL_bool have_output = SDL_FALSE;
|
||||
SDL_bool have_input = SDL_FALSE;
|
||||
|
||||
free(ioid); /* This should NOT be SDL_free() */
|
||||
|
||||
if (!isoutput && !isinput) {
|
||||
free(name); /* This should NOT be SDL_free() */
|
||||
continue;
|
||||
}
|
||||
|
||||
prev = NULL;
|
||||
for (dev = unseen; dev; dev = next) {
|
||||
next = dev->next;
|
||||
if ((SDL_strcmp(dev->name, name) == 0) && (((isinput) && dev->iscapture) || ((isoutput) && !dev->iscapture))) {
|
||||
if (prev) {
|
||||
prev->next = next;
|
||||
} else {
|
||||
unseen = next;
|
||||
}
|
||||
dev->next = seen;
|
||||
seen = dev;
|
||||
if (isinput) {
|
||||
have_input = SDL_TRUE;
|
||||
}
|
||||
if (isoutput) {
|
||||
have_output = SDL_TRUE;
|
||||
}
|
||||
} else {
|
||||
prev = dev;
|
||||
}
|
||||
}
|
||||
|
||||
if (isinput && !have_input) {
|
||||
add_device(SDL_TRUE, name, hints[i], &seen);
|
||||
}
|
||||
if (isoutput && !have_output) {
|
||||
add_device(SDL_FALSE, name, hints[i], &seen);
|
||||
}
|
||||
}
|
||||
|
||||
free(name); /* This should NOT be SDL_free() */
|
||||
}
|
||||
|
||||
ALSA_snd_device_name_free_hint(hints);
|
||||
|
||||
hotplug_devices = seen; /* now we have a known-good list of attached devices. */
|
||||
|
||||
/* report anything still in unseen as removed. */
|
||||
for (dev = unseen; dev; dev = next) {
|
||||
/*printf("ALSA: removing usb %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
|
||||
next = dev->next;
|
||||
SDL_RemoveAudioDevice(dev->iscapture, dev->name);
|
||||
SDL_free(dev->name);
|
||||
SDL_free(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if SDL_ALSA_HOTPLUG_THREAD
|
||||
static SDL_AtomicInt ALSA_hotplug_shutdown;
|
||||
static SDL_Thread *ALSA_hotplug_thread;
|
||||
|
||||
static int SDLCALL ALSA_HotplugThread(void *arg)
|
||||
{
|
||||
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW);
|
||||
|
||||
while (!SDL_AtomicGet(&ALSA_hotplug_shutdown)) {
|
||||
/* Block awhile before checking again, unless we're told to stop. */
|
||||
const Uint64 ticks = SDL_GetTicks() + 5000;
|
||||
while (!SDL_AtomicGet(&ALSA_hotplug_shutdown) && SDL_GetTicks() < ticks) {
|
||||
SDL_Delay(100);
|
||||
}
|
||||
|
||||
ALSA_HotplugIteration(); /* run the check. */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ALSA_DetectDevices(void)
|
||||
{
|
||||
ALSA_HotplugIteration(); /* run once now before a thread continues to check. */
|
||||
|
||||
#if SDL_ALSA_HOTPLUG_THREAD
|
||||
SDL_AtomicSet(&ALSA_hotplug_shutdown, 0);
|
||||
ALSA_hotplug_thread = SDL_CreateThread(ALSA_HotplugThread, "SDLHotplugALSA", NULL);
|
||||
/* if the thread doesn't spin, oh well, you just don't get further hotplug events. */
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ALSA_Deinitialize(void)
|
||||
{
|
||||
ALSA_Device *dev;
|
||||
ALSA_Device *next;
|
||||
|
||||
#if SDL_ALSA_HOTPLUG_THREAD
|
||||
if (ALSA_hotplug_thread != NULL) {
|
||||
SDL_AtomicSet(&ALSA_hotplug_shutdown, 1);
|
||||
SDL_WaitThread(ALSA_hotplug_thread, NULL);
|
||||
ALSA_hotplug_thread = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Shutting down! Clean up any data we've gathered. */
|
||||
for (dev = hotplug_devices; dev; dev = next) {
|
||||
/*printf("ALSA: at shutdown, removing %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
|
||||
next = dev->next;
|
||||
SDL_free(dev->name);
|
||||
SDL_free(dev);
|
||||
}
|
||||
hotplug_devices = NULL;
|
||||
|
||||
UnloadALSALibrary();
|
||||
}
|
||||
|
||||
static SDL_bool ALSA_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (LoadALSALibrary() < 0) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->DetectDevices = ALSA_DetectDevices;
|
||||
impl->OpenDevice = ALSA_OpenDevice;
|
||||
impl->WaitDevice = ALSA_WaitDevice;
|
||||
impl->GetDeviceBuf = ALSA_GetDeviceBuf;
|
||||
impl->PlayDevice = ALSA_PlayDevice;
|
||||
impl->CloseDevice = ALSA_CloseDevice;
|
||||
impl->Deinitialize = ALSA_Deinitialize;
|
||||
impl->CaptureFromDevice = ALSA_CaptureFromDevice;
|
||||
impl->FlushCapture = ALSA_FlushCapture;
|
||||
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->SupportsNonPow2Samples = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap ALSA_bootstrap = {
|
||||
"alsa", "ALSA PCM audio", ALSA_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_ALSA */
|
43
external/sdl/SDL/src/audio/alsa/SDL_alsa_audio.h
vendored
Normal file
43
external/sdl/SDL/src/audio/alsa/SDL_alsa_audio.h
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_ALSA_audio_h_
|
||||
#define SDL_ALSA_audio_h_
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The audio device handle */
|
||||
snd_pcm_t *pcm_handle;
|
||||
|
||||
/* Raw mixing buffer */
|
||||
Uint8 *mixbuf;
|
||||
int mixlen;
|
||||
|
||||
/* swizzle function */
|
||||
void (*swizzle_func)(SDL_AudioDevice *_this, void *buffer, Uint32 bufferlen);
|
||||
};
|
||||
|
||||
#endif /* SDL_ALSA_audio_h_ */
|
218
external/sdl/SDL/src/audio/android/SDL_androidaudio.c
vendored
Normal file
218
external/sdl/SDL/src/audio/android/SDL_androidaudio.c
vendored
Normal file
@ -0,0 +1,218 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_AUDIO_DRIVER_ANDROID
|
||||
|
||||
/* Output audio to Android */
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_androidaudio.h"
|
||||
|
||||
#include "../../core/android/SDL_android.h"
|
||||
#include <android/log.h>
|
||||
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* Resume device if it was paused automatically */
|
||||
int resume;
|
||||
};
|
||||
|
||||
static SDL_AudioDevice *audioDevice = NULL;
|
||||
static SDL_AudioDevice *captureDevice = NULL;
|
||||
|
||||
|
||||
static int ANDROIDAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
{
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts;
|
||||
SDL_bool iscapture = _this->iscapture;
|
||||
|
||||
if (iscapture) {
|
||||
if (captureDevice) {
|
||||
return SDL_SetError("An audio capture device is already opened");
|
||||
}
|
||||
}
|
||||
|
||||
if (!iscapture) {
|
||||
if (audioDevice) {
|
||||
return SDL_SetError("An audio playback device is already opened");
|
||||
}
|
||||
}
|
||||
|
||||
if (iscapture) {
|
||||
captureDevice = _this;
|
||||
} else {
|
||||
audioDevice = _this;
|
||||
}
|
||||
|
||||
_this->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*_this->hidden));
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
closefmts = SDL_ClosestAudioFormats(_this->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
if ((test_format == SDL_AUDIO_U8) ||
|
||||
(test_format == SDL_AUDIO_S16) ||
|
||||
(test_format == SDL_AUDIO_F32)) {
|
||||
_this->spec.format = test_format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
/* Didn't find a compatible format :( */
|
||||
return SDL_SetError("%s: Unsupported audio format", "android");
|
||||
}
|
||||
|
||||
{
|
||||
int audio_device_id = 0;
|
||||
if (devname != NULL) {
|
||||
audio_device_id = SDL_atoi(devname);
|
||||
}
|
||||
if (Android_JNI_OpenAudioDevice(iscapture, audio_device_id, &_this->spec) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ANDROIDAUDIO_PlayDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
Android_JNI_WriteAudioBuffer();
|
||||
}
|
||||
|
||||
static Uint8 *ANDROIDAUDIO_GetDeviceBuf(SDL_AudioDevice *_this)
|
||||
{
|
||||
return Android_JNI_GetAudioBuffer();
|
||||
}
|
||||
|
||||
static int ANDROIDAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
|
||||
{
|
||||
return Android_JNI_CaptureAudioBuffer(buffer, buflen);
|
||||
}
|
||||
|
||||
static void ANDROIDAUDIO_FlushCapture(SDL_AudioDevice *_this)
|
||||
{
|
||||
Android_JNI_FlushCapturedAudio();
|
||||
}
|
||||
|
||||
static void ANDROIDAUDIO_CloseDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
/* At this point SDL_CloseAudioDevice via close_audio_device took care of terminating the audio thread
|
||||
so it's safe to terminate the Java side buffer and AudioTrack
|
||||
*/
|
||||
Android_JNI_CloseAudioDevice(_this->iscapture);
|
||||
if (_this->iscapture) {
|
||||
SDL_assert(captureDevice == _this);
|
||||
captureDevice = NULL;
|
||||
} else {
|
||||
SDL_assert(audioDevice == _this);
|
||||
audioDevice = NULL;
|
||||
}
|
||||
SDL_free(_this->hidden);
|
||||
}
|
||||
|
||||
static SDL_bool ANDROIDAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
/* Set the function pointers */
|
||||
impl->DetectDevices = Android_DetectDevices;
|
||||
impl->OpenDevice = ANDROIDAUDIO_OpenDevice;
|
||||
impl->PlayDevice = ANDROIDAUDIO_PlayDevice;
|
||||
impl->GetDeviceBuf = ANDROIDAUDIO_GetDeviceBuf;
|
||||
impl->CloseDevice = ANDROIDAUDIO_CloseDevice;
|
||||
impl->CaptureFromDevice = ANDROIDAUDIO_CaptureFromDevice;
|
||||
impl->FlushCapture = ANDROIDAUDIO_FlushCapture;
|
||||
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
|
||||
|
||||
/* and the capabilities */
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_FALSE;
|
||||
impl->OnlyHasDefaultCaptureDevice = SDL_FALSE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap ANDROIDAUDIO_bootstrap = {
|
||||
"android", "SDL Android audio driver", ANDROIDAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
/* Pause (block) all non already paused audio devices by taking their mixer lock */
|
||||
void ANDROIDAUDIO_PauseDevices(void)
|
||||
{
|
||||
/* TODO: Handle multiple devices? */
|
||||
struct SDL_PrivateAudioData *private;
|
||||
if (audioDevice != NULL && audioDevice->hidden != NULL) {
|
||||
private = (struct SDL_PrivateAudioData *)audioDevice->hidden;
|
||||
if (SDL_AtomicGet(&audioDevice->paused)) {
|
||||
/* The device is already paused, leave it alone */
|
||||
private->resume = SDL_FALSE;
|
||||
} else {
|
||||
SDL_LockMutex(audioDevice->mixer_lock);
|
||||
SDL_AtomicSet(&audioDevice->paused, 1);
|
||||
private->resume = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (captureDevice != NULL && captureDevice->hidden != NULL) {
|
||||
private = (struct SDL_PrivateAudioData *)captureDevice->hidden;
|
||||
if (SDL_AtomicGet(&captureDevice->paused)) {
|
||||
/* The device is already paused, leave it alone */
|
||||
private->resume = SDL_FALSE;
|
||||
} else {
|
||||
SDL_LockMutex(captureDevice->mixer_lock);
|
||||
SDL_AtomicSet(&captureDevice->paused, 1);
|
||||
private->resume = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Resume (unblock) all non already paused audio devices by releasing their mixer lock */
|
||||
void ANDROIDAUDIO_ResumeDevices(void)
|
||||
{
|
||||
/* TODO: Handle multiple devices? */
|
||||
struct SDL_PrivateAudioData *private;
|
||||
if (audioDevice != NULL && audioDevice->hidden != NULL) {
|
||||
private = (struct SDL_PrivateAudioData *)audioDevice->hidden;
|
||||
if (private->resume) {
|
||||
SDL_AtomicSet(&audioDevice->paused, 0);
|
||||
private->resume = SDL_FALSE;
|
||||
SDL_UnlockMutex(audioDevice->mixer_lock);
|
||||
}
|
||||
}
|
||||
|
||||
if (captureDevice != NULL && captureDevice->hidden != NULL) {
|
||||
private = (struct SDL_PrivateAudioData *)captureDevice->hidden;
|
||||
if (private->resume) {
|
||||
SDL_AtomicSet(&captureDevice->paused, 0);
|
||||
private->resume = SDL_FALSE;
|
||||
SDL_UnlockMutex(captureDevice->mixer_lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_ANDROID */
|
38
external/sdl/SDL/src/audio/android/SDL_androidaudio.h
vendored
Normal file
38
external/sdl/SDL/src/audio/android/SDL_androidaudio.h
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_androidaudio_h_
|
||||
#define SDL_androidaudio_h_
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_ANDROID
|
||||
|
||||
void ANDROIDAUDIO_ResumeDevices(void);
|
||||
void ANDROIDAUDIO_PauseDevices(void);
|
||||
|
||||
#else
|
||||
|
||||
static void ANDROIDAUDIO_ResumeDevices(void) {}
|
||||
static void ANDROIDAUDIO_PauseDevices(void) {}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* SDL_androidaudio_h_ */
|
71
external/sdl/SDL/src/audio/coreaudio/SDL_coreaudio.h
vendored
Normal file
71
external/sdl/SDL/src/audio/coreaudio/SDL_coreaudio.h
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_coreaudio_h_
|
||||
#define SDL_coreaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
#ifndef __IOS__
|
||||
#define MACOSX_COREAUDIO
|
||||
#endif
|
||||
|
||||
#ifdef MACOSX_COREAUDIO
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#else
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <UIKit/UIApplication.h>
|
||||
#endif
|
||||
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
|
||||
/* Things named "Master" were renamed to "Main" in macOS 12.0's SDK. */
|
||||
#ifdef MACOSX_COREAUDIO
|
||||
#include <AvailabilityMacros.h>
|
||||
#ifndef MAC_OS_VERSION_12_0
|
||||
#define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
SDL_Thread *thread;
|
||||
AudioQueueRef audioQueue;
|
||||
int numAudioBuffers;
|
||||
AudioQueueBufferRef *audioBuffer;
|
||||
void *buffer;
|
||||
UInt32 bufferOffset;
|
||||
UInt32 bufferSize;
|
||||
AudioStreamBasicDescription strdesc;
|
||||
SDL_Semaphore *ready_semaphore;
|
||||
char *thread_error;
|
||||
#ifdef MACOSX_COREAUDIO
|
||||
AudioDeviceID deviceID;
|
||||
SDL_AtomicInt device_change_flag;
|
||||
#else
|
||||
SDL_bool interrupted;
|
||||
CFTypeRef interruption_listener;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* SDL_coreaudio_h_ */
|
1309
external/sdl/SDL/src/audio/coreaudio/SDL_coreaudio.m
vendored
Normal file
1309
external/sdl/SDL/src/audio/coreaudio/SDL_coreaudio.m
vendored
Normal file
File diff suppressed because it is too large
Load Diff
656
external/sdl/SDL/src/audio/directsound/SDL_directsound.c
vendored
Normal file
656
external/sdl/SDL/src/audio/directsound/SDL_directsound.c
vendored
Normal file
@ -0,0 +1,656 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_AUDIO_DRIVER_DSOUND
|
||||
|
||||
/* Allow access to a raw mixing buffer */
|
||||
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_directsound.h"
|
||||
#include <mmreg.h>
|
||||
#ifdef HAVE_MMDEVICEAPI_H
|
||||
#include "../../core/windows/SDL_immdevice.h"
|
||||
#endif /* HAVE_MMDEVICEAPI_H */
|
||||
|
||||
#ifndef WAVE_FORMAT_IEEE_FLOAT
|
||||
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
|
||||
#endif
|
||||
|
||||
/* For Vista+, we can enumerate DSound devices with IMMDevice */
|
||||
#ifdef HAVE_MMDEVICEAPI_H
|
||||
static SDL_bool SupportsIMMDevice = SDL_FALSE;
|
||||
#endif /* HAVE_MMDEVICEAPI_H */
|
||||
|
||||
/* DirectX function pointers for audio */
|
||||
static void *DSoundDLL = NULL;
|
||||
typedef HRESULT(WINAPI *fnDirectSoundCreate8)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
|
||||
typedef HRESULT(WINAPI *fnDirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
|
||||
typedef HRESULT(WINAPI *fnDirectSoundCaptureCreate8)(LPCGUID, LPDIRECTSOUNDCAPTURE8 *, LPUNKNOWN);
|
||||
typedef HRESULT(WINAPI *fnDirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
|
||||
static fnDirectSoundCreate8 pDirectSoundCreate8 = NULL;
|
||||
static fnDirectSoundEnumerateW pDirectSoundEnumerateW = NULL;
|
||||
static fnDirectSoundCaptureCreate8 pDirectSoundCaptureCreate8 = NULL;
|
||||
static fnDirectSoundCaptureEnumerateW pDirectSoundCaptureEnumerateW = NULL;
|
||||
|
||||
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 } };
|
||||
|
||||
static void DSOUND_Unload(void)
|
||||
{
|
||||
pDirectSoundCreate8 = NULL;
|
||||
pDirectSoundEnumerateW = NULL;
|
||||
pDirectSoundCaptureCreate8 = NULL;
|
||||
pDirectSoundCaptureEnumerateW = NULL;
|
||||
|
||||
if (DSoundDLL != NULL) {
|
||||
SDL_UnloadObject(DSoundDLL);
|
||||
DSoundDLL = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int DSOUND_Load(void)
|
||||
{
|
||||
int loaded = 0;
|
||||
|
||||
DSOUND_Unload();
|
||||
|
||||
DSoundDLL = SDL_LoadObject("DSOUND.DLL");
|
||||
if (DSoundDLL == NULL) {
|
||||
SDL_SetError("DirectSound: failed to load DSOUND.DLL");
|
||||
} else {
|
||||
/* Now make sure we have DirectX 8 or better... */
|
||||
#define DSOUNDLOAD(f) \
|
||||
{ \
|
||||
p##f = (fn##f)SDL_LoadFunction(DSoundDLL, #f); \
|
||||
if (!p##f) \
|
||||
loaded = 0; \
|
||||
}
|
||||
loaded = 1; /* will reset if necessary. */
|
||||
DSOUNDLOAD(DirectSoundCreate8);
|
||||
DSOUNDLOAD(DirectSoundEnumerateW);
|
||||
DSOUNDLOAD(DirectSoundCaptureCreate8);
|
||||
DSOUNDLOAD(DirectSoundCaptureEnumerateW);
|
||||
#undef DSOUNDLOAD
|
||||
|
||||
if (!loaded) {
|
||||
SDL_SetError("DirectSound: System doesn't appear to have DX8.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!loaded) {
|
||||
DSOUND_Unload();
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
static int SetDSerror(const char *function, int code)
|
||||
{
|
||||
const char *error;
|
||||
|
||||
switch (code) {
|
||||
case E_NOINTERFACE:
|
||||
error = "Unsupported interface -- Is DirectX 8.0 or later installed?";
|
||||
break;
|
||||
case DSERR_ALLOCATED:
|
||||
error = "Audio device in use";
|
||||
break;
|
||||
case DSERR_BADFORMAT:
|
||||
error = "Unsupported audio format";
|
||||
break;
|
||||
case DSERR_BUFFERLOST:
|
||||
error = "Mixing buffer was lost";
|
||||
break;
|
||||
case DSERR_CONTROLUNAVAIL:
|
||||
error = "Control requested is not available";
|
||||
break;
|
||||
case DSERR_INVALIDCALL:
|
||||
error = "Invalid call for the current state";
|
||||
break;
|
||||
case DSERR_INVALIDPARAM:
|
||||
error = "Invalid parameter";
|
||||
break;
|
||||
case DSERR_NODRIVER:
|
||||
error = "No audio device found";
|
||||
break;
|
||||
case DSERR_OUTOFMEMORY:
|
||||
error = "Out of memory";
|
||||
break;
|
||||
case DSERR_PRIOLEVELNEEDED:
|
||||
error = "Caller doesn't have priority";
|
||||
break;
|
||||
case DSERR_UNSUPPORTED:
|
||||
error = "Function not supported";
|
||||
break;
|
||||
default:
|
||||
error = "Unknown DirectSound error";
|
||||
break;
|
||||
}
|
||||
|
||||
return SDL_SetError("%s: %s (0x%x)", function, error, code);
|
||||
}
|
||||
|
||||
static void DSOUND_FreeDeviceHandle(void *handle)
|
||||
{
|
||||
SDL_free(handle);
|
||||
}
|
||||
|
||||
static int DSOUND_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
|
||||
{
|
||||
#ifdef HAVE_MMDEVICEAPI_H
|
||||
if (SupportsIMMDevice) {
|
||||
return SDL_IMMDevice_GetDefaultAudioInfo(name, spec, iscapture);
|
||||
}
|
||||
#endif /* HAVE_MMDEVICEAPI_H */
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
static BOOL CALLBACK FindAllDevs(LPGUID guid, LPCWSTR desc, LPCWSTR module, LPVOID data)
|
||||
{
|
||||
const int iscapture = (int)((size_t)data);
|
||||
if (guid != NULL) { /* skip default device */
|
||||
char *str = WIN_LookupAudioDeviceName(desc, guid);
|
||||
if (str != NULL) {
|
||||
LPGUID cpyguid = (LPGUID)SDL_malloc(sizeof(GUID));
|
||||
SDL_memcpy(cpyguid, guid, sizeof(GUID));
|
||||
|
||||
/* Note that spec is NULL, because we are required to connect to the
|
||||
* device before getting the channel mask and output format, making
|
||||
* this information inaccessible at enumeration time
|
||||
*/
|
||||
SDL_AddAudioDevice(iscapture, str, NULL, cpyguid);
|
||||
SDL_free(str); /* addfn() makes a copy of this string. */
|
||||
}
|
||||
}
|
||||
return TRUE; /* keep enumerating. */
|
||||
}
|
||||
|
||||
static void DSOUND_DetectDevices(void)
|
||||
{
|
||||
#ifdef HAVE_MMDEVICEAPI_H
|
||||
if (SupportsIMMDevice) {
|
||||
SDL_IMMDevice_EnumerateEndpoints(SDL_TRUE);
|
||||
} else {
|
||||
#endif /* HAVE_MMDEVICEAPI_H */
|
||||
pDirectSoundCaptureEnumerateW(FindAllDevs, (void *)((size_t)1));
|
||||
pDirectSoundEnumerateW(FindAllDevs, (void *)((size_t)0));
|
||||
#ifdef HAVE_MMDEVICEAPI_H
|
||||
}
|
||||
#endif /* HAVE_MMDEVICEAPI_H*/
|
||||
}
|
||||
|
||||
static void DSOUND_WaitDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
DWORD status = 0;
|
||||
DWORD cursor = 0;
|
||||
DWORD junk = 0;
|
||||
HRESULT result = DS_OK;
|
||||
|
||||
/* Semi-busy wait, since we have no way of getting play notification
|
||||
on a primary mixing buffer located in hardware (DirectX 5.0)
|
||||
*/
|
||||
result = IDirectSoundBuffer_GetCurrentPosition(_this->hidden->mixbuf,
|
||||
&junk, &cursor);
|
||||
if (result != DS_OK) {
|
||||
if (result == DSERR_BUFFERLOST) {
|
||||
IDirectSoundBuffer_Restore(_this->hidden->mixbuf);
|
||||
}
|
||||
#ifdef DEBUG_SOUND
|
||||
SetDSerror("DirectSound GetCurrentPosition", result);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
while ((cursor / _this->spec.size) == _this->hidden->lastchunk) {
|
||||
/* FIXME: find out how much time is left and sleep that long */
|
||||
SDL_Delay(1);
|
||||
|
||||
/* Try to restore a lost sound buffer */
|
||||
IDirectSoundBuffer_GetStatus(_this->hidden->mixbuf, &status);
|
||||
if (status & DSBSTATUS_BUFFERLOST) {
|
||||
IDirectSoundBuffer_Restore(_this->hidden->mixbuf);
|
||||
IDirectSoundBuffer_GetStatus(_this->hidden->mixbuf, &status);
|
||||
if (status & DSBSTATUS_BUFFERLOST) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!(status & DSBSTATUS_PLAYING)) {
|
||||
result = IDirectSoundBuffer_Play(_this->hidden->mixbuf, 0, 0,
|
||||
DSBPLAY_LOOPING);
|
||||
if (result == DS_OK) {
|
||||
continue;
|
||||
}
|
||||
#ifdef DEBUG_SOUND
|
||||
SetDSerror("DirectSound Play", result);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
/* Find out where we are playing */
|
||||
result = IDirectSoundBuffer_GetCurrentPosition(_this->hidden->mixbuf,
|
||||
&junk, &cursor);
|
||||
if (result != DS_OK) {
|
||||
SetDSerror("DirectSound GetCurrentPosition", result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void DSOUND_PlayDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
/* Unlock the buffer, allowing it to play */
|
||||
if (_this->hidden->locked_buf) {
|
||||
IDirectSoundBuffer_Unlock(_this->hidden->mixbuf,
|
||||
_this->hidden->locked_buf,
|
||||
_this->spec.size, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static Uint8 *DSOUND_GetDeviceBuf(SDL_AudioDevice *_this)
|
||||
{
|
||||
DWORD cursor = 0;
|
||||
DWORD junk = 0;
|
||||
HRESULT result = DS_OK;
|
||||
DWORD rawlen = 0;
|
||||
|
||||
/* Figure out which blocks to fill next */
|
||||
_this->hidden->locked_buf = NULL;
|
||||
result = IDirectSoundBuffer_GetCurrentPosition(_this->hidden->mixbuf,
|
||||
&junk, &cursor);
|
||||
if (result == DSERR_BUFFERLOST) {
|
||||
IDirectSoundBuffer_Restore(_this->hidden->mixbuf);
|
||||
result = IDirectSoundBuffer_GetCurrentPosition(_this->hidden->mixbuf,
|
||||
&junk, &cursor);
|
||||
}
|
||||
if (result != DS_OK) {
|
||||
SetDSerror("DirectSound GetCurrentPosition", result);
|
||||
return NULL;
|
||||
}
|
||||
cursor /= _this->spec.size;
|
||||
#ifdef DEBUG_SOUND
|
||||
/* Detect audio dropouts */
|
||||
{
|
||||
DWORD spot = cursor;
|
||||
if (spot < _this->hidden->lastchunk) {
|
||||
spot += _this->hidden->num_buffers;
|
||||
}
|
||||
if (spot > _this->hidden->lastchunk + 1) {
|
||||
fprintf(stderr, "Audio dropout, missed %d fragments\n",
|
||||
(spot - (_this->hidden->lastchunk + 1)));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
_this->hidden->lastchunk = cursor;
|
||||
cursor = (cursor + 1) % _this->hidden->num_buffers;
|
||||
cursor *= _this->spec.size;
|
||||
|
||||
/* Lock the audio buffer */
|
||||
result = IDirectSoundBuffer_Lock(_this->hidden->mixbuf, cursor,
|
||||
_this->spec.size,
|
||||
(LPVOID *)&_this->hidden->locked_buf,
|
||||
&rawlen, NULL, &junk, 0);
|
||||
if (result == DSERR_BUFFERLOST) {
|
||||
IDirectSoundBuffer_Restore(_this->hidden->mixbuf);
|
||||
result = IDirectSoundBuffer_Lock(_this->hidden->mixbuf, cursor,
|
||||
_this->spec.size,
|
||||
(LPVOID *)&_this->hidden->locked_buf, &rawlen, NULL,
|
||||
&junk, 0);
|
||||
}
|
||||
if (result != DS_OK) {
|
||||
SetDSerror("DirectSound Lock", result);
|
||||
return NULL;
|
||||
}
|
||||
return _this->hidden->locked_buf;
|
||||
}
|
||||
|
||||
static int DSOUND_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = _this->hidden;
|
||||
DWORD junk, cursor, ptr1len, ptr2len;
|
||||
VOID *ptr1, *ptr2;
|
||||
|
||||
SDL_assert((Uint32)buflen == _this->spec.size);
|
||||
|
||||
while (SDL_TRUE) {
|
||||
if (SDL_AtomicGet(&_this->shutdown)) { /* in case the buffer froze... */
|
||||
SDL_memset(buffer, _this->spec.silence, buflen);
|
||||
return buflen;
|
||||
}
|
||||
|
||||
if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) != DS_OK) {
|
||||
return -1;
|
||||
}
|
||||
if ((cursor / _this->spec.size) == h->lastchunk) {
|
||||
SDL_Delay(1); /* FIXME: find out how much time is left and sleep that long */
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (IDirectSoundCaptureBuffer_Lock(h->capturebuf, h->lastchunk * _this->spec.size, _this->spec.size, &ptr1, &ptr1len, &ptr2, &ptr2len, 0) != DS_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_assert(ptr1len == _this->spec.size);
|
||||
SDL_assert(ptr2 == NULL);
|
||||
SDL_assert(ptr2len == 0);
|
||||
|
||||
SDL_memcpy(buffer, ptr1, ptr1len);
|
||||
|
||||
if (IDirectSoundCaptureBuffer_Unlock(h->capturebuf, ptr1, ptr1len, ptr2, ptr2len) != DS_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
h->lastchunk = (h->lastchunk + 1) % h->num_buffers;
|
||||
|
||||
return ptr1len;
|
||||
}
|
||||
|
||||
static void DSOUND_FlushCapture(SDL_AudioDevice *_this)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = _this->hidden;
|
||||
DWORD junk, cursor;
|
||||
if (IDirectSoundCaptureBuffer_GetCurrentPosition(h->capturebuf, &junk, &cursor) == DS_OK) {
|
||||
h->lastchunk = cursor / _this->spec.size;
|
||||
}
|
||||
}
|
||||
|
||||
static void DSOUND_CloseDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
if (_this->hidden->mixbuf != NULL) {
|
||||
IDirectSoundBuffer_Stop(_this->hidden->mixbuf);
|
||||
IDirectSoundBuffer_Release(_this->hidden->mixbuf);
|
||||
}
|
||||
if (_this->hidden->sound != NULL) {
|
||||
IDirectSound_Release(_this->hidden->sound);
|
||||
}
|
||||
if (_this->hidden->capturebuf != NULL) {
|
||||
IDirectSoundCaptureBuffer_Stop(_this->hidden->capturebuf);
|
||||
IDirectSoundCaptureBuffer_Release(_this->hidden->capturebuf);
|
||||
}
|
||||
if (_this->hidden->capture != NULL) {
|
||||
IDirectSoundCapture_Release(_this->hidden->capture);
|
||||
}
|
||||
SDL_free(_this->hidden);
|
||||
}
|
||||
|
||||
/* This function tries to create a secondary audio buffer, and returns the
|
||||
number of audio chunks available in the created buffer. This is for
|
||||
playback devices, not capture.
|
||||
*/
|
||||
static int CreateSecondary(SDL_AudioDevice *_this, const DWORD bufsize, WAVEFORMATEX *wfmt)
|
||||
{
|
||||
LPDIRECTSOUND sndObj = _this->hidden->sound;
|
||||
LPDIRECTSOUNDBUFFER *sndbuf = &_this->hidden->mixbuf;
|
||||
HRESULT result = DS_OK;
|
||||
DSBUFFERDESC format;
|
||||
LPVOID pvAudioPtr1, pvAudioPtr2;
|
||||
DWORD dwAudioBytes1, dwAudioBytes2;
|
||||
|
||||
/* Try to create the secondary buffer */
|
||||
SDL_zero(format);
|
||||
format.dwSize = sizeof(format);
|
||||
format.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
|
||||
format.dwFlags |= DSBCAPS_GLOBALFOCUS;
|
||||
format.dwBufferBytes = bufsize;
|
||||
format.lpwfxFormat = wfmt;
|
||||
result = IDirectSound_CreateSoundBuffer(sndObj, &format, sndbuf, NULL);
|
||||
if (result != DS_OK) {
|
||||
return SetDSerror("DirectSound CreateSoundBuffer", result);
|
||||
}
|
||||
IDirectSoundBuffer_SetFormat(*sndbuf, wfmt);
|
||||
|
||||
/* Silence the initial audio buffer */
|
||||
result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes,
|
||||
(LPVOID *)&pvAudioPtr1, &dwAudioBytes1,
|
||||
(LPVOID *)&pvAudioPtr2, &dwAudioBytes2,
|
||||
DSBLOCK_ENTIREBUFFER);
|
||||
if (result == DS_OK) {
|
||||
SDL_memset(pvAudioPtr1, _this->spec.silence, dwAudioBytes1);
|
||||
IDirectSoundBuffer_Unlock(*sndbuf,
|
||||
(LPVOID)pvAudioPtr1, dwAudioBytes1,
|
||||
(LPVOID)pvAudioPtr2, dwAudioBytes2);
|
||||
}
|
||||
|
||||
/* We're ready to go */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function tries to create a capture buffer, and returns the
|
||||
number of audio chunks available in the created buffer. This is for
|
||||
capture devices, not playback.
|
||||
*/
|
||||
static int CreateCaptureBuffer(SDL_AudioDevice *_this, const DWORD bufsize, WAVEFORMATEX *wfmt)
|
||||
{
|
||||
LPDIRECTSOUNDCAPTURE capture = _this->hidden->capture;
|
||||
LPDIRECTSOUNDCAPTUREBUFFER *capturebuf = &_this->hidden->capturebuf;
|
||||
DSCBUFFERDESC format;
|
||||
HRESULT result;
|
||||
|
||||
SDL_zero(format);
|
||||
format.dwSize = sizeof(format);
|
||||
format.dwFlags = DSCBCAPS_WAVEMAPPED;
|
||||
format.dwBufferBytes = bufsize;
|
||||
format.lpwfxFormat = wfmt;
|
||||
|
||||
result = IDirectSoundCapture_CreateCaptureBuffer(capture, &format, capturebuf, NULL);
|
||||
if (result != DS_OK) {
|
||||
return SetDSerror("DirectSound CreateCaptureBuffer", result);
|
||||
}
|
||||
|
||||
result = IDirectSoundCaptureBuffer_Start(*capturebuf, DSCBSTART_LOOPING);
|
||||
if (result != DS_OK) {
|
||||
IDirectSoundCaptureBuffer_Release(*capturebuf);
|
||||
return SetDSerror("DirectSound Start", result);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* presumably this starts at zero, but just in case... */
|
||||
result = IDirectSoundCaptureBuffer_GetCurrentPosition(*capturebuf, &junk, &cursor);
|
||||
if (result != DS_OK) {
|
||||
IDirectSoundCaptureBuffer_Stop(*capturebuf);
|
||||
IDirectSoundCaptureBuffer_Release(*capturebuf);
|
||||
return SetDSerror("DirectSound GetCurrentPosition", result);
|
||||
}
|
||||
|
||||
_this->hidden->lastchunk = cursor / _this->spec.size;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int DSOUND_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
{
|
||||
const DWORD numchunks = 8;
|
||||
HRESULT result;
|
||||
SDL_bool tried_format = SDL_FALSE;
|
||||
SDL_bool iscapture = _this->iscapture;
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts;
|
||||
LPGUID guid = (LPGUID)_this->handle;
|
||||
DWORD bufsize;
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
_this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(*_this->hidden));
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(_this->hidden);
|
||||
|
||||
/* Open the audio device */
|
||||
if (iscapture) {
|
||||
result = pDirectSoundCaptureCreate8(guid, &_this->hidden->capture, NULL);
|
||||
if (result != DS_OK) {
|
||||
return SetDSerror("DirectSoundCaptureCreate8", result);
|
||||
}
|
||||
} else {
|
||||
result = pDirectSoundCreate8(guid, &_this->hidden->sound, NULL);
|
||||
if (result != DS_OK) {
|
||||
return SetDSerror("DirectSoundCreate8", result);
|
||||
}
|
||||
result = IDirectSound_SetCooperativeLevel(_this->hidden->sound,
|
||||
GetDesktopWindow(),
|
||||
DSSCL_NORMAL);
|
||||
if (result != DS_OK) {
|
||||
return SetDSerror("DirectSound SetCooperativeLevel", result);
|
||||
}
|
||||
}
|
||||
|
||||
closefmts = SDL_ClosestAudioFormats(_this->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
switch (test_format) {
|
||||
case SDL_AUDIO_U8:
|
||||
case SDL_AUDIO_S16:
|
||||
case SDL_AUDIO_S32:
|
||||
case SDL_AUDIO_F32:
|
||||
tried_format = SDL_TRUE;
|
||||
|
||||
_this->spec.format = test_format;
|
||||
|
||||
/* Update the fragment size as size in bytes */
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
bufsize = numchunks * _this->spec.size;
|
||||
if ((bufsize < DSBSIZE_MIN) || (bufsize > DSBSIZE_MAX)) {
|
||||
SDL_SetError("Sound buffer size must be between %d and %d",
|
||||
(int)((DSBSIZE_MIN < numchunks) ? 1 : DSBSIZE_MIN / numchunks),
|
||||
(int)(DSBSIZE_MAX / numchunks));
|
||||
} else {
|
||||
int rc;
|
||||
WAVEFORMATEXTENSIBLE wfmt;
|
||||
SDL_zero(wfmt);
|
||||
if (_this->spec.channels > 2) {
|
||||
wfmt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
wfmt.Format.cbSize = sizeof(wfmt) - sizeof(WAVEFORMATEX);
|
||||
|
||||
if (SDL_AUDIO_ISFLOAT(_this->spec.format)) {
|
||||
SDL_memcpy(&wfmt.SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID));
|
||||
} else {
|
||||
SDL_memcpy(&wfmt.SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID));
|
||||
}
|
||||
wfmt.Samples.wValidBitsPerSample = SDL_AUDIO_BITSIZE(_this->spec.format);
|
||||
|
||||
switch (_this->spec.channels) {
|
||||
case 3: /* 3.0 (or 2.1) */
|
||||
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER;
|
||||
break;
|
||||
case 4: /* 4.0 */
|
||||
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
|
||||
break;
|
||||
case 5: /* 5.0 (or 4.1) */
|
||||
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
|
||||
break;
|
||||
case 6: /* 5.1 */
|
||||
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
|
||||
break;
|
||||
case 7: /* 6.1 */
|
||||
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_BACK_CENTER;
|
||||
break;
|
||||
case 8: /* 7.1 */
|
||||
wfmt.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT;
|
||||
break;
|
||||
default:
|
||||
SDL_assert(0 && "Unsupported channel count!");
|
||||
break;
|
||||
}
|
||||
} else if (SDL_AUDIO_ISFLOAT(_this->spec.format)) {
|
||||
wfmt.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||
} else {
|
||||
wfmt.Format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
}
|
||||
|
||||
wfmt.Format.wBitsPerSample = SDL_AUDIO_BITSIZE(_this->spec.format);
|
||||
wfmt.Format.nChannels = _this->spec.channels;
|
||||
wfmt.Format.nSamplesPerSec = _this->spec.freq;
|
||||
wfmt.Format.nBlockAlign = wfmt.Format.nChannels * (wfmt.Format.wBitsPerSample / 8);
|
||||
wfmt.Format.nAvgBytesPerSec = wfmt.Format.nSamplesPerSec * wfmt.Format.nBlockAlign;
|
||||
|
||||
rc = iscapture ? CreateCaptureBuffer(_this, bufsize, (WAVEFORMATEX *)&wfmt) : CreateSecondary(_this, bufsize, (WAVEFORMATEX *)&wfmt);
|
||||
if (rc == 0) {
|
||||
_this->hidden->num_buffers = numchunks;
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
if (tried_format) {
|
||||
return -1; /* CreateSecondary() should have called SDL_SetError(). */
|
||||
}
|
||||
return SDL_SetError("%s: Unsupported audio format", "directsound");
|
||||
}
|
||||
|
||||
/* Playback buffers will auto-start playing in DSOUND_WaitDevice() */
|
||||
|
||||
return 0; /* good to go. */
|
||||
}
|
||||
|
||||
static void DSOUND_Deinitialize(void)
|
||||
{
|
||||
#ifdef HAVE_MMDEVICEAPI_H
|
||||
if (SupportsIMMDevice) {
|
||||
SDL_IMMDevice_Quit();
|
||||
SupportsIMMDevice = SDL_FALSE;
|
||||
}
|
||||
#endif /* HAVE_MMDEVICEAPI_H */
|
||||
DSOUND_Unload();
|
||||
}
|
||||
|
||||
static SDL_bool DSOUND_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (!DSOUND_Load()) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
#ifdef HAVE_MMDEVICEAPI_H
|
||||
SupportsIMMDevice = !(SDL_IMMDevice_Init() < 0);
|
||||
#endif /* HAVE_MMDEVICEAPI_H */
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->DetectDevices = DSOUND_DetectDevices;
|
||||
impl->OpenDevice = DSOUND_OpenDevice;
|
||||
impl->PlayDevice = DSOUND_PlayDevice;
|
||||
impl->WaitDevice = DSOUND_WaitDevice;
|
||||
impl->GetDeviceBuf = DSOUND_GetDeviceBuf;
|
||||
impl->CaptureFromDevice = DSOUND_CaptureFromDevice;
|
||||
impl->FlushCapture = DSOUND_FlushCapture;
|
||||
impl->CloseDevice = DSOUND_CloseDevice;
|
||||
impl->FreeDeviceHandle = DSOUND_FreeDeviceHandle;
|
||||
impl->Deinitialize = DSOUND_Deinitialize;
|
||||
impl->GetDefaultAudioInfo = DSOUND_GetDefaultAudioInfo;
|
||||
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->SupportsNonPow2Samples = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap DSOUND_bootstrap = {
|
||||
"directsound", "DirectSound", DSOUND_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_DSOUND */
|
42
external/sdl/SDL/src/audio/directsound/SDL_directsound.h
vendored
Normal file
42
external/sdl/SDL/src/audio/directsound/SDL_directsound.h
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_directsound_h_
|
||||
#define SDL_directsound_h_
|
||||
|
||||
#include "../../core/windows/SDL_directx.h"
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
/* The DirectSound objects */
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
LPDIRECTSOUND sound;
|
||||
LPDIRECTSOUNDBUFFER mixbuf;
|
||||
LPDIRECTSOUNDCAPTURE capture;
|
||||
LPDIRECTSOUNDCAPTUREBUFFER capturebuf;
|
||||
int num_buffers;
|
||||
DWORD lastchunk;
|
||||
Uint8 *locked_buf;
|
||||
};
|
||||
|
||||
#endif /* SDL_directsound_h_ */
|
192
external/sdl/SDL/src/audio/disk/SDL_diskaudio.c
vendored
Normal file
192
external/sdl/SDL/src/audio/disk/SDL_diskaudio.c
vendored
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_AUDIO_DRIVER_DISK
|
||||
|
||||
/* Output raw audio data to a file. */
|
||||
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_diskaudio.h"
|
||||
|
||||
/* !!! FIXME: these should be SDL hints, not environment variables. */
|
||||
/* environment variables and defaults. */
|
||||
#define DISKENVR_OUTFILE "SDL_DISKAUDIOFILE"
|
||||
#define DISKDEFAULT_OUTFILE "sdlaudio.raw"
|
||||
#define DISKENVR_INFILE "SDL_DISKAUDIOFILEIN"
|
||||
#define DISKDEFAULT_INFILE "sdlaudio-in.raw"
|
||||
#define DISKENVR_IODELAY "SDL_DISKAUDIODELAY"
|
||||
|
||||
/* This function waits until it is possible to write a full sound buffer */
|
||||
static void DISKAUDIO_WaitDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
SDL_Delay(_this->hidden->io_delay);
|
||||
}
|
||||
|
||||
static void DISKAUDIO_PlayDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
const Sint64 written = SDL_RWwrite(_this->hidden->io,
|
||||
_this->hidden->mixbuf,
|
||||
_this->spec.size);
|
||||
|
||||
/* If we couldn't write, assume fatal error for now */
|
||||
if (written != _this->spec.size) {
|
||||
SDL_OpenedAudioDeviceDisconnected(_this);
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", (int) written);
|
||||
#endif
|
||||
}
|
||||
|
||||
static Uint8 *DISKAUDIO_GetDeviceBuf(SDL_AudioDevice *_this)
|
||||
{
|
||||
return _this->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static int DISKAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = _this->hidden;
|
||||
const int origbuflen = buflen;
|
||||
|
||||
SDL_Delay(h->io_delay);
|
||||
|
||||
if (h->io) {
|
||||
const int br = (int) SDL_RWread(h->io, buffer, (Sint64) buflen);
|
||||
buflen -= br;
|
||||
buffer = ((Uint8 *)buffer) + br;
|
||||
if (buflen > 0) { /* EOF (or error, but whatever). */
|
||||
SDL_RWclose(h->io);
|
||||
h->io = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* if we ran out of file, just write silence. */
|
||||
SDL_memset(buffer, _this->spec.silence, buflen);
|
||||
|
||||
return origbuflen;
|
||||
}
|
||||
|
||||
static void DISKAUDIO_FlushCapture(SDL_AudioDevice *_this)
|
||||
{
|
||||
/* no op...we don't advance the file pointer or anything. */
|
||||
}
|
||||
|
||||
static void DISKAUDIO_CloseDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
if (_this->hidden->io != NULL) {
|
||||
SDL_RWclose(_this->hidden->io);
|
||||
}
|
||||
SDL_free(_this->hidden->mixbuf);
|
||||
SDL_free(_this->hidden);
|
||||
}
|
||||
|
||||
static const char *get_filename(const SDL_bool iscapture, const char *devname)
|
||||
{
|
||||
if (devname == NULL) {
|
||||
devname = SDL_getenv(iscapture ? DISKENVR_INFILE : DISKENVR_OUTFILE);
|
||||
if (devname == NULL) {
|
||||
devname = iscapture ? DISKDEFAULT_INFILE : DISKDEFAULT_OUTFILE;
|
||||
}
|
||||
}
|
||||
return devname;
|
||||
}
|
||||
|
||||
static int DISKAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
{
|
||||
void *handle = _this->handle;
|
||||
/* handle != NULL means "user specified the placeholder name on the fake detected device list" */
|
||||
SDL_bool iscapture = _this->iscapture;
|
||||
const char *fname = get_filename(iscapture, handle ? NULL : devname);
|
||||
const char *envr = SDL_getenv(DISKENVR_IODELAY);
|
||||
|
||||
_this->hidden = (struct SDL_PrivateAudioData *)
|
||||
SDL_malloc(sizeof(*_this->hidden));
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(_this->hidden);
|
||||
|
||||
if (envr != NULL) {
|
||||
_this->hidden->io_delay = SDL_atoi(envr);
|
||||
} else {
|
||||
_this->hidden->io_delay = ((_this->spec.samples * 1000) / _this->spec.freq);
|
||||
}
|
||||
|
||||
/* Open the audio device */
|
||||
_this->hidden->io = SDL_RWFromFile(fname, iscapture ? "rb" : "wb");
|
||||
if (_this->hidden->io == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Allocate mixing buffer */
|
||||
if (!iscapture) {
|
||||
_this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->spec.size);
|
||||
if (_this->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(_this->hidden->mixbuf, _this->spec.silence, _this->spec.size);
|
||||
}
|
||||
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_AUDIO,
|
||||
"You are using the SDL disk i/o audio driver!\n");
|
||||
SDL_LogCritical(SDL_LOG_CATEGORY_AUDIO,
|
||||
" %s file [%s].\n", iscapture ? "Reading from" : "Writing to",
|
||||
fname);
|
||||
|
||||
/* We're ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void DISKAUDIO_DetectDevices(void)
|
||||
{
|
||||
SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, NULL, (void *)0x1);
|
||||
SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, NULL, (void *)0x2);
|
||||
}
|
||||
|
||||
static SDL_bool DISKAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
/* Set the function pointers */
|
||||
impl->OpenDevice = DISKAUDIO_OpenDevice;
|
||||
impl->WaitDevice = DISKAUDIO_WaitDevice;
|
||||
impl->PlayDevice = DISKAUDIO_PlayDevice;
|
||||
impl->GetDeviceBuf = DISKAUDIO_GetDeviceBuf;
|
||||
impl->CaptureFromDevice = DISKAUDIO_CaptureFromDevice;
|
||||
impl->FlushCapture = DISKAUDIO_FlushCapture;
|
||||
|
||||
impl->CloseDevice = DISKAUDIO_CloseDevice;
|
||||
impl->DetectDevices = DISKAUDIO_DetectDevices;
|
||||
|
||||
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->SupportsNonPow2Samples = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap DISKAUDIO_bootstrap = {
|
||||
"disk", "direct-to-disk audio", DISKAUDIO_Init, SDL_TRUE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_DISK */
|
36
external/sdl/SDL/src/audio/disk/SDL_diskaudio.h
vendored
Normal file
36
external/sdl/SDL/src/audio/disk/SDL_diskaudio.h
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_diskaudio_h_
|
||||
#define SDL_diskaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The file descriptor for the audio device */
|
||||
SDL_RWops *io;
|
||||
Uint32 io_delay;
|
||||
Uint8 *mixbuf;
|
||||
};
|
||||
|
||||
#endif /* SDL_diskaudio_h_ */
|
301
external/sdl/SDL/src/audio/dsp/SDL_dspaudio.c
vendored
Normal file
301
external/sdl/SDL/src/audio/dsp/SDL_dspaudio.c
vendored
Normal file
@ -0,0 +1,301 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_AUDIO_DRIVER_OSS
|
||||
|
||||
/* Allow access to a raw mixing buffer */
|
||||
|
||||
#include <stdio.h> /* For perror() */
|
||||
#include <string.h> /* For strerror() */
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <sys/soundcard.h>
|
||||
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../SDL_audiodev_c.h"
|
||||
#include "SDL_dspaudio.h"
|
||||
|
||||
static void DSP_DetectDevices(void)
|
||||
{
|
||||
SDL_EnumUnixAudioDevices(0, NULL);
|
||||
}
|
||||
|
||||
static void DSP_CloseDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
if (_this->hidden->audio_fd >= 0) {
|
||||
close(_this->hidden->audio_fd);
|
||||
}
|
||||
SDL_free(_this->hidden->mixbuf);
|
||||
SDL_free(_this->hidden);
|
||||
}
|
||||
|
||||
static int DSP_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
{
|
||||
SDL_bool iscapture = _this->iscapture;
|
||||
const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
|
||||
int format = 0;
|
||||
int value;
|
||||
int frag_spec;
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts;
|
||||
|
||||
/* We don't care what the devname is...we'll try to open anything. */
|
||||
/* ...but default to first name in the list... */
|
||||
if (devname == NULL) {
|
||||
devname = SDL_GetAudioDeviceName(0, iscapture);
|
||||
if (devname == NULL) {
|
||||
return SDL_SetError("No such audio device");
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure fragment size stays a power of 2, or OSS fails. */
|
||||
/* I don't know which of these are actually legal values, though... */
|
||||
if (_this->spec.channels > 8) {
|
||||
_this->spec.channels = 8;
|
||||
} else if (_this->spec.channels > 4) {
|
||||
_this->spec.channels = 4;
|
||||
} else if (_this->spec.channels > 2) {
|
||||
_this->spec.channels = 2;
|
||||
}
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
_this->hidden = (struct SDL_PrivateAudioData *) SDL_malloc(sizeof(*_this->hidden));
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(_this->hidden);
|
||||
|
||||
/* Open the audio device */
|
||||
_this->hidden->audio_fd = open(devname, flags | O_CLOEXEC, 0);
|
||||
if (_this->hidden->audio_fd < 0) {
|
||||
return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
|
||||
}
|
||||
|
||||
/* Make the file descriptor use blocking i/o with fcntl() */
|
||||
{
|
||||
long ctlflags;
|
||||
ctlflags = fcntl(_this->hidden->audio_fd, F_GETFL);
|
||||
ctlflags &= ~O_NONBLOCK;
|
||||
if (fcntl(_this->hidden->audio_fd, F_SETFL, ctlflags) < 0) {
|
||||
return SDL_SetError("Couldn't set audio blocking mode");
|
||||
}
|
||||
}
|
||||
|
||||
/* Get a list of supported hardware formats */
|
||||
if (ioctl(_this->hidden->audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) {
|
||||
perror("SNDCTL_DSP_GETFMTS");
|
||||
return SDL_SetError("Couldn't get audio format list");
|
||||
}
|
||||
|
||||
/* Try for a closest match on audio format */
|
||||
closefmts = SDL_ClosestAudioFormats(_this->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
|
||||
#endif
|
||||
switch (test_format) {
|
||||
case SDL_AUDIO_U8:
|
||||
if (value & AFMT_U8) {
|
||||
format = AFMT_U8;
|
||||
}
|
||||
break;
|
||||
case SDL_AUDIO_S16LSB:
|
||||
if (value & AFMT_S16_LE) {
|
||||
format = AFMT_S16_LE;
|
||||
}
|
||||
break;
|
||||
case SDL_AUDIO_S16MSB:
|
||||
if (value & AFMT_S16_BE) {
|
||||
format = AFMT_S16_BE;
|
||||
}
|
||||
break;
|
||||
#if 0
|
||||
/*
|
||||
* These formats are not used by any real life systems so they are not
|
||||
* needed here.
|
||||
*/
|
||||
case SDL_AUDIO_S8:
|
||||
if (value & AFMT_S8) {
|
||||
format = AFMT_S8;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (format == 0) {
|
||||
return SDL_SetError("Couldn't find any hardware audio formats");
|
||||
}
|
||||
_this->spec.format = test_format;
|
||||
|
||||
/* Set the audio format */
|
||||
value = format;
|
||||
if ((ioctl(_this->hidden->audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
|
||||
(value != format)) {
|
||||
perror("SNDCTL_DSP_SETFMT");
|
||||
return SDL_SetError("Couldn't set audio format");
|
||||
}
|
||||
|
||||
/* Set the number of channels of output */
|
||||
value = _this->spec.channels;
|
||||
if (ioctl(_this->hidden->audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) {
|
||||
perror("SNDCTL_DSP_CHANNELS");
|
||||
return SDL_SetError("Cannot set the number of channels");
|
||||
}
|
||||
_this->spec.channels = value;
|
||||
|
||||
/* Set the DSP frequency */
|
||||
value = _this->spec.freq;
|
||||
if (ioctl(_this->hidden->audio_fd, SNDCTL_DSP_SPEED, &value) < 0) {
|
||||
perror("SNDCTL_DSP_SPEED");
|
||||
return SDL_SetError("Couldn't set audio frequency");
|
||||
}
|
||||
_this->spec.freq = value;
|
||||
|
||||
/* Calculate the final parameters for this audio specification */
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
/* Determine the power of two of the fragment size */
|
||||
for (frag_spec = 0; (0x01U << frag_spec) < _this->spec.size; ++frag_spec) {
|
||||
}
|
||||
if ((0x01U << frag_spec) != _this->spec.size) {
|
||||
return SDL_SetError("Fragment size must be a power of two");
|
||||
}
|
||||
frag_spec |= 0x00020000; /* two fragments, for low latency */
|
||||
|
||||
/* Set the audio buffering parameters */
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Requesting %d fragments of size %d\n",
|
||||
(frag_spec >> 16), 1 << (frag_spec & 0xFFFF));
|
||||
#endif
|
||||
if (ioctl(_this->hidden->audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) {
|
||||
perror("SNDCTL_DSP_SETFRAGMENT");
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
{
|
||||
audio_buf_info info;
|
||||
ioctl(_this->hidden->audio_fd, SNDCTL_DSP_GETOSPACE, &info);
|
||||
fprintf(stderr, "fragments = %d\n", info.fragments);
|
||||
fprintf(stderr, "fragstotal = %d\n", info.fragstotal);
|
||||
fprintf(stderr, "fragsize = %d\n", info.fragsize);
|
||||
fprintf(stderr, "bytes = %d\n", info.bytes);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Allocate mixing buffer */
|
||||
if (!iscapture) {
|
||||
_this->hidden->mixlen = _this->spec.size;
|
||||
_this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->hidden->mixlen);
|
||||
if (_this->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(_this->hidden->mixbuf, _this->spec.silence, _this->spec.size);
|
||||
}
|
||||
|
||||
/* We're ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void DSP_PlayDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = _this->hidden;
|
||||
if (write(h->audio_fd, h->mixbuf, h->mixlen) == -1) {
|
||||
perror("Audio write");
|
||||
SDL_OpenedAudioDeviceDisconnected(_this);
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", h->mixlen);
|
||||
#endif
|
||||
}
|
||||
|
||||
static Uint8 *DSP_GetDeviceBuf(SDL_AudioDevice *_this)
|
||||
{
|
||||
return _this->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static int DSP_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
|
||||
{
|
||||
return (int)read(_this->hidden->audio_fd, buffer, buflen);
|
||||
}
|
||||
|
||||
static void DSP_FlushCapture(SDL_AudioDevice *_this)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = _this->hidden;
|
||||
audio_buf_info info;
|
||||
if (ioctl(h->audio_fd, SNDCTL_DSP_GETISPACE, &info) == 0) {
|
||||
while (info.bytes > 0) {
|
||||
char buf[512];
|
||||
const size_t len = SDL_min(sizeof(buf), info.bytes);
|
||||
const ssize_t br = read(h->audio_fd, buf, len);
|
||||
if (br <= 0) {
|
||||
break;
|
||||
}
|
||||
info.bytes -= br;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_bool InitTimeDevicesExist = SDL_FALSE;
|
||||
static int look_for_devices_test(int fd)
|
||||
{
|
||||
InitTimeDevicesExist = SDL_TRUE; /* note that _something_ exists. */
|
||||
/* Don't add to the device list, we're just seeing if any devices exist. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SDL_bool DSP_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
InitTimeDevicesExist = SDL_FALSE;
|
||||
SDL_EnumUnixAudioDevices(0, look_for_devices_test);
|
||||
if (!InitTimeDevicesExist) {
|
||||
SDL_SetError("dsp: No such audio device");
|
||||
return SDL_FALSE; /* maybe try a different backend. */
|
||||
}
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->DetectDevices = DSP_DetectDevices;
|
||||
impl->OpenDevice = DSP_OpenDevice;
|
||||
impl->PlayDevice = DSP_PlayDevice;
|
||||
impl->GetDeviceBuf = DSP_GetDeviceBuf;
|
||||
impl->CloseDevice = DSP_CloseDevice;
|
||||
impl->CaptureFromDevice = DSP_CaptureFromDevice;
|
||||
impl->FlushCapture = DSP_FlushCapture;
|
||||
|
||||
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap DSP_bootstrap = {
|
||||
"dsp", "OSS /dev/dsp standard audio", DSP_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_OSS */
|
39
external/sdl/SDL/src/audio/dsp/SDL_dspaudio.h
vendored
Normal file
39
external/sdl/SDL/src/audio/dsp/SDL_dspaudio.h
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_dspaudio_h_
|
||||
#define SDL_dspaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The file descriptor for the audio device */
|
||||
int audio_fd;
|
||||
|
||||
/* Raw mixing buffer */
|
||||
Uint8 *mixbuf;
|
||||
int mixlen;
|
||||
};
|
||||
#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */
|
||||
|
||||
#endif /* SDL_dspaudio_h_ */
|
60
external/sdl/SDL/src/audio/dummy/SDL_dummyaudio.c
vendored
Normal file
60
external/sdl/SDL/src/audio/dummy/SDL_dummyaudio.c
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
/* Output audio to nowhere... */
|
||||
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_dummyaudio.h"
|
||||
|
||||
static int DUMMYAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
{
|
||||
_this->hidden = (void *)0x1; /* just something non-NULL */
|
||||
|
||||
return 0; /* always succeeds. */
|
||||
}
|
||||
|
||||
static int DUMMYAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
|
||||
{
|
||||
/* Delay to make this sort of simulate real audio input. */
|
||||
SDL_Delay((_this->spec.samples * 1000) / _this->spec.freq);
|
||||
|
||||
/* always return a full buffer of silence. */
|
||||
SDL_memset(buffer, _this->spec.silence, buflen);
|
||||
return buflen;
|
||||
}
|
||||
|
||||
static SDL_bool DUMMYAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
/* Set the function pointers */
|
||||
impl->OpenDevice = DUMMYAUDIO_OpenDevice;
|
||||
impl->CaptureFromDevice = DUMMYAUDIO_CaptureFromDevice;
|
||||
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap DUMMYAUDIO_bootstrap = {
|
||||
"dummy", "SDL dummy audio driver", DUMMYAUDIO_Init, SDL_TRUE
|
||||
};
|
37
external/sdl/SDL/src/audio/dummy/SDL_dummyaudio.h
vendored
Normal file
37
external/sdl/SDL/src/audio/dummy/SDL_dummyaudio.h
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_dummyaudio_h_
|
||||
#define SDL_dummyaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The file descriptor for the audio device */
|
||||
Uint8 *mixbuf;
|
||||
Uint32 mixlen;
|
||||
Uint32 write_delay;
|
||||
Uint32 initial_calls;
|
||||
};
|
||||
|
||||
#endif /* SDL_dummyaudio_h_ */
|
409
external/sdl/SDL/src/audio/emscripten/SDL_emscriptenaudio.c
vendored
Normal file
409
external/sdl/SDL/src/audio/emscripten/SDL_emscriptenaudio.c
vendored
Normal file
@ -0,0 +1,409 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_AUDIO_DRIVER_EMSCRIPTEN
|
||||
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_emscriptenaudio.h"
|
||||
|
||||
#include <emscripten/emscripten.h>
|
||||
|
||||
/* !!! FIXME: this currently expects that the audio callback runs in the main thread,
|
||||
!!! FIXME: in intervals when the application isn't running, but that may not be
|
||||
!!! FIXME: true always once pthread support becomes widespread. Revisit this code
|
||||
!!! FIXME: at some point and see what needs to be done for that! */
|
||||
|
||||
static void FeedAudioDevice(SDL_AudioDevice *_this, const void *buf, const int buflen)
|
||||
{
|
||||
const int framelen = (SDL_AUDIO_BITSIZE(_this->spec.format) / 8) * _this->spec.channels;
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
MAIN_THREAD_EM_ASM({
|
||||
var SDL3 = Module['SDL3'];
|
||||
var numChannels = SDL3.audio.currentOutputBuffer['numberOfChannels'];
|
||||
for (var c = 0; c < numChannels; ++c) {
|
||||
var channelData = SDL3.audio.currentOutputBuffer['getChannelData'](c);
|
||||
if (channelData.length != $1) {
|
||||
throw 'Web Audio output buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!';
|
||||
}
|
||||
|
||||
for (var j = 0; j < $1; ++j) {
|
||||
channelData[j] = HEAPF32[$0 + ((j*numChannels + c) << 2) >> 2]; /* !!! FIXME: why are these shifts here? */
|
||||
}
|
||||
}
|
||||
}, buf, buflen / framelen);
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
}
|
||||
|
||||
static void HandleAudioProcess(SDL_AudioDevice *_this)
|
||||
{
|
||||
SDL_AudioCallback callback = _this->callbackspec.callback;
|
||||
const int stream_len = _this->callbackspec.size;
|
||||
|
||||
/* Only do something if audio is enabled */
|
||||
if (!SDL_AtomicGet(&_this->enabled) || SDL_AtomicGet(&_this->paused)) {
|
||||
if (_this->stream) {
|
||||
SDL_ClearAudioStream(_this->stream);
|
||||
}
|
||||
|
||||
SDL_memset(_this->work_buffer, _this->spec.silence, _this->spec.size);
|
||||
FeedAudioDevice(_this, _this->work_buffer, _this->spec.size);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_this->stream == NULL) { /* no conversion necessary. */
|
||||
SDL_assert(_this->spec.size == stream_len);
|
||||
callback(_this->callbackspec.userdata, _this->work_buffer, stream_len);
|
||||
} else { /* streaming/converting */
|
||||
int got;
|
||||
while (SDL_GetAudioStreamAvailable(_this->stream) < ((int)_this->spec.size)) {
|
||||
callback(_this->callbackspec.userdata, _this->work_buffer, stream_len);
|
||||
if (SDL_PutAudioStreamData(_this->stream, _this->work_buffer, stream_len) == -1) {
|
||||
SDL_ClearAudioStream(_this->stream);
|
||||
SDL_AtomicSet(&_this->enabled, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
got = SDL_GetAudioStreamData(_this->stream, _this->work_buffer, _this->spec.size);
|
||||
SDL_assert((got < 0) || (got == _this->spec.size));
|
||||
if (got != _this->spec.size) {
|
||||
SDL_memset(_this->work_buffer, _this->spec.silence, _this->spec.size);
|
||||
}
|
||||
}
|
||||
|
||||
FeedAudioDevice(_this, _this->work_buffer, _this->spec.size);
|
||||
}
|
||||
|
||||
static void HandleCaptureProcess(SDL_AudioDevice *_this)
|
||||
{
|
||||
SDL_AudioCallback callback = _this->callbackspec.callback;
|
||||
const int stream_len = _this->callbackspec.size;
|
||||
|
||||
/* Only do something if audio is enabled */
|
||||
if (!SDL_AtomicGet(&_this->enabled) || SDL_AtomicGet(&_this->paused)) {
|
||||
SDL_ClearAudioStream(_this->stream);
|
||||
return;
|
||||
}
|
||||
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
MAIN_THREAD_EM_ASM({
|
||||
var SDL3 = Module['SDL3'];
|
||||
var numChannels = SDL3.capture.currentCaptureBuffer.numberOfChannels;
|
||||
for (var c = 0; c < numChannels; ++c) {
|
||||
var channelData = SDL3.capture.currentCaptureBuffer.getChannelData(c);
|
||||
if (channelData.length != $1) {
|
||||
throw 'Web Audio capture buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!';
|
||||
}
|
||||
|
||||
if (numChannels == 1) { /* fastpath this a little for the common (mono) case. */
|
||||
for (var j = 0; j < $1; ++j) {
|
||||
setValue($0 + (j * 4), channelData[j], 'float');
|
||||
}
|
||||
} else {
|
||||
for (var j = 0; j < $1; ++j) {
|
||||
setValue($0 + (((j * numChannels) + c) * 4), channelData[j], 'float');
|
||||
}
|
||||
}
|
||||
}
|
||||
}, _this->work_buffer, (_this->spec.size / sizeof(float)) / _this->spec.channels);
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
/* okay, we've got an interleaved float32 array in C now. */
|
||||
|
||||
if (_this->stream == NULL) { /* no conversion necessary. */
|
||||
SDL_assert(_this->spec.size == stream_len);
|
||||
callback(_this->callbackspec.userdata, _this->work_buffer, stream_len);
|
||||
} else { /* streaming/converting */
|
||||
if (SDL_PutAudioStreamData(_this->stream, _this->work_buffer, _this->spec.size) == -1) {
|
||||
SDL_AtomicSet(&_this->enabled, 0);
|
||||
}
|
||||
|
||||
while (SDL_GetAudioStreamAvailable(_this->stream) >= stream_len) {
|
||||
const int got = SDL_GetAudioStreamData(_this->stream, _this->work_buffer, stream_len);
|
||||
SDL_assert((got < 0) || (got == stream_len));
|
||||
if (got != stream_len) {
|
||||
SDL_memset(_this->work_buffer, _this->callbackspec.silence, stream_len);
|
||||
}
|
||||
callback(_this->callbackspec.userdata, _this->work_buffer, stream_len); /* Send it to the app. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void EMSCRIPTENAUDIO_CloseDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
MAIN_THREAD_EM_ASM({
|
||||
var SDL3 = Module['SDL3'];
|
||||
if ($0) {
|
||||
if (SDL3.capture.silenceTimer !== undefined) {
|
||||
clearTimeout(SDL3.capture.silenceTimer);
|
||||
}
|
||||
if (SDL3.capture.stream !== undefined) {
|
||||
var tracks = SDL3.capture.stream.getAudioTracks();
|
||||
for (var i = 0; i < tracks.length; i++) {
|
||||
SDL3.capture.stream.removeTrack(tracks[i]);
|
||||
}
|
||||
SDL3.capture.stream = undefined;
|
||||
}
|
||||
if (SDL3.capture.scriptProcessorNode !== undefined) {
|
||||
SDL3.capture.scriptProcessorNode.onaudioprocess = function(audioProcessingEvent) {};
|
||||
SDL3.capture.scriptProcessorNode.disconnect();
|
||||
SDL3.capture.scriptProcessorNode = undefined;
|
||||
}
|
||||
if (SDL3.capture.mediaStreamNode !== undefined) {
|
||||
SDL3.capture.mediaStreamNode.disconnect();
|
||||
SDL3.capture.mediaStreamNode = undefined;
|
||||
}
|
||||
if (SDL3.capture.silenceBuffer !== undefined) {
|
||||
SDL3.capture.silenceBuffer = undefined
|
||||
}
|
||||
SDL3.capture = undefined;
|
||||
} else {
|
||||
if (SDL3.audio.scriptProcessorNode != undefined) {
|
||||
SDL3.audio.scriptProcessorNode.disconnect();
|
||||
SDL3.audio.scriptProcessorNode = undefined;
|
||||
}
|
||||
SDL3.audio = undefined;
|
||||
}
|
||||
if ((SDL3.audioContext !== undefined) && (SDL3.audio === undefined) && (SDL3.capture === undefined)) {
|
||||
SDL3.audioContext.close();
|
||||
SDL3.audioContext = undefined;
|
||||
}
|
||||
}, _this->iscapture);
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
#if 0 /* !!! FIXME: currently not used. Can we move some stuff off the SDL3 namespace? --ryan. */
|
||||
SDL_free(_this->hidden);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
EM_JS_DEPS(sdlaudio, "$autoResumeAudioContext,$dynCall");
|
||||
|
||||
static int EMSCRIPTENAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
{
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts;
|
||||
SDL_bool iscapture = _this->iscapture;
|
||||
int result;
|
||||
|
||||
/* based on parts of library_sdl.js */
|
||||
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
/* create context */
|
||||
result = MAIN_THREAD_EM_ASM_INT({
|
||||
if (typeof(Module['SDL3']) === 'undefined') {
|
||||
Module['SDL3'] = {};
|
||||
}
|
||||
var SDL3 = Module['SDL3'];
|
||||
if (!$0) {
|
||||
SDL3.audio = {};
|
||||
} else {
|
||||
SDL3.capture = {};
|
||||
}
|
||||
|
||||
if (!SDL3.audioContext) {
|
||||
if (typeof(AudioContext) !== 'undefined') {
|
||||
SDL3.audioContext = new AudioContext();
|
||||
} else if (typeof(webkitAudioContext) !== 'undefined') {
|
||||
SDL3.audioContext = new webkitAudioContext();
|
||||
}
|
||||
if (SDL3.audioContext) {
|
||||
autoResumeAudioContext(SDL3.audioContext);
|
||||
}
|
||||
}
|
||||
return SDL3.audioContext === undefined ? -1 : 0;
|
||||
}, iscapture);
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
if (result < 0) {
|
||||
return SDL_SetError("Web Audio API is not available!");
|
||||
}
|
||||
|
||||
closefmts = SDL_ClosestAudioFormats(_this->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
switch (test_format) {
|
||||
case SDL_AUDIO_F32: /* web audio only supports floats */
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
/* Didn't find a compatible format :( */
|
||||
return SDL_SetError("%s: Unsupported audio format", "emscripten");
|
||||
}
|
||||
_this->spec.format = test_format;
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
#if 0 /* !!! FIXME: currently not used. Can we move some stuff off the SDL3 namespace? --ryan. */
|
||||
_this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(*_this->hidden));
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(_this->hidden);
|
||||
#endif
|
||||
_this->hidden = (struct SDL_PrivateAudioData *)0x1;
|
||||
|
||||
/* limit to native freq */
|
||||
_this->spec.freq = EM_ASM_INT({
|
||||
var SDL3 = Module['SDL3'];
|
||||
return SDL3.audioContext.sampleRate;
|
||||
});
|
||||
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
if (iscapture) {
|
||||
/* The idea is to take the capture media stream, hook it up to an
|
||||
audio graph where we can pass it through a ScriptProcessorNode
|
||||
to access the raw PCM samples and push them to the SDL app's
|
||||
callback. From there, we "process" the audio data into silence
|
||||
and forget about it. */
|
||||
|
||||
/* This should, strictly speaking, use MediaRecorder for capture, but
|
||||
this API is cleaner to use and better supported, and fires a
|
||||
callback whenever there's enough data to fire down into the app.
|
||||
The downside is that we are spending CPU time silencing a buffer
|
||||
that the audiocontext uselessly mixes into any output. On the
|
||||
upside, both of those things are not only run in native code in
|
||||
the browser, they're probably SIMD code, too. MediaRecorder
|
||||
feels like it's a pretty inefficient tapdance in similar ways,
|
||||
to be honest. */
|
||||
|
||||
MAIN_THREAD_EM_ASM({
|
||||
var SDL3 = Module['SDL3'];
|
||||
var have_microphone = function(stream) {
|
||||
//console.log('SDL audio capture: we have a microphone! Replacing silence callback.');
|
||||
if (SDL3.capture.silenceTimer !== undefined) {
|
||||
clearTimeout(SDL3.capture.silenceTimer);
|
||||
SDL3.capture.silenceTimer = undefined;
|
||||
}
|
||||
SDL3.capture.mediaStreamNode = SDL3.audioContext.createMediaStreamSource(stream);
|
||||
SDL3.capture.scriptProcessorNode = SDL3.audioContext.createScriptProcessor($1, $0, 1);
|
||||
SDL3.capture.scriptProcessorNode.onaudioprocess = function(audioProcessingEvent) {
|
||||
if ((SDL3 === undefined) || (SDL3.capture === undefined)) { return; }
|
||||
audioProcessingEvent.outputBuffer.getChannelData(0).fill(0.0);
|
||||
SDL3.capture.currentCaptureBuffer = audioProcessingEvent.inputBuffer;
|
||||
dynCall('vi', $2, [$3]);
|
||||
};
|
||||
SDL3.capture.mediaStreamNode.connect(SDL3.capture.scriptProcessorNode);
|
||||
SDL3.capture.scriptProcessorNode.connect(SDL3.audioContext.destination);
|
||||
SDL3.capture.stream = stream;
|
||||
};
|
||||
|
||||
var no_microphone = function(error) {
|
||||
//console.log('SDL audio capture: we DO NOT have a microphone! (' + error.name + ')...leaving silence callback running.');
|
||||
};
|
||||
|
||||
/* we write silence to the audio callback until the microphone is available (user approves use, etc). */
|
||||
SDL3.capture.silenceBuffer = SDL3.audioContext.createBuffer($0, $1, SDL3.audioContext.sampleRate);
|
||||
SDL3.capture.silenceBuffer.getChannelData(0).fill(0.0);
|
||||
var silence_callback = function() {
|
||||
SDL3.capture.currentCaptureBuffer = SDL3.capture.silenceBuffer;
|
||||
dynCall('vi', $2, [$3]);
|
||||
};
|
||||
|
||||
SDL3.capture.silenceTimer = setTimeout(silence_callback, ($1 / SDL3.audioContext.sampleRate) * 1000);
|
||||
|
||||
if ((navigator.mediaDevices !== undefined) && (navigator.mediaDevices.getUserMedia !== undefined)) {
|
||||
navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(have_microphone).catch(no_microphone);
|
||||
} else if (navigator.webkitGetUserMedia !== undefined) {
|
||||
navigator.webkitGetUserMedia({ audio: true, video: false }, have_microphone, no_microphone);
|
||||
}
|
||||
}, _this->spec.channels, _this->spec.samples, HandleCaptureProcess, _this);
|
||||
} else {
|
||||
/* setup a ScriptProcessorNode */
|
||||
MAIN_THREAD_EM_ASM({
|
||||
var SDL3 = Module['SDL3'];
|
||||
SDL3.audio.scriptProcessorNode = SDL3.audioContext['createScriptProcessor']($1, 0, $0);
|
||||
SDL3.audio.scriptProcessorNode['onaudioprocess'] = function (e) {
|
||||
if ((SDL3 === undefined) || (SDL3.audio === undefined)) { return; }
|
||||
SDL3.audio.currentOutputBuffer = e['outputBuffer'];
|
||||
dynCall('vi', $2, [$3]);
|
||||
};
|
||||
SDL3.audio.scriptProcessorNode['connect'](SDL3.audioContext['destination']);
|
||||
}, _this->spec.channels, _this->spec.samples, HandleAudioProcess, _this);
|
||||
}
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void EMSCRIPTENAUDIO_LockOrUnlockDeviceWithNoMixerLock(SDL_AudioDevice *device)
|
||||
{
|
||||
}
|
||||
|
||||
static SDL_bool EMSCRIPTENAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
SDL_bool available, capture_available;
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->OpenDevice = EMSCRIPTENAUDIO_OpenDevice;
|
||||
impl->CloseDevice = EMSCRIPTENAUDIO_CloseDevice;
|
||||
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
|
||||
/* no threads here */
|
||||
impl->LockDevice = impl->UnlockDevice = EMSCRIPTENAUDIO_LockOrUnlockDeviceWithNoMixerLock;
|
||||
impl->ProvidesOwnCallbackThread = SDL_TRUE;
|
||||
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
/* check availability */
|
||||
available = MAIN_THREAD_EM_ASM_INT({
|
||||
if (typeof(AudioContext) !== 'undefined') {
|
||||
return true;
|
||||
} else if (typeof(webkitAudioContext) !== 'undefined') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
if (!available) {
|
||||
SDL_SetError("No audio context available");
|
||||
}
|
||||
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
capture_available = available && MAIN_THREAD_EM_ASM_INT({
|
||||
if ((typeof(navigator.mediaDevices) !== 'undefined') && (typeof(navigator.mediaDevices.getUserMedia) !== 'undefined')) {
|
||||
return true;
|
||||
} else if (typeof(navigator.webkitGetUserMedia) !== 'undefined') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
impl->HasCaptureSupport = capture_available ? SDL_TRUE : SDL_FALSE;
|
||||
impl->OnlyHasDefaultCaptureDevice = capture_available ? SDL_TRUE : SDL_FALSE;
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
AudioBootStrap EMSCRIPTENAUDIO_bootstrap = {
|
||||
"emscripten", "SDL emscripten audio driver", EMSCRIPTENAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_EMSCRIPTEN */
|
33
external/sdl/SDL/src/audio/emscripten/SDL_emscriptenaudio.h
vendored
Normal file
33
external/sdl/SDL/src/audio/emscripten/SDL_emscriptenaudio.h
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_emscriptenaudio_h_
|
||||
#define SDL_emscriptenaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
int unused;
|
||||
};
|
||||
|
||||
#endif /* SDL_emscriptenaudio_h_ */
|
238
external/sdl/SDL/src/audio/haiku/SDL_haikuaudio.cc
vendored
Normal file
238
external/sdl/SDL/src/audio/haiku/SDL_haikuaudio.cc
vendored
Normal file
@ -0,0 +1,238 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_AUDIO_DRIVER_HAIKU
|
||||
|
||||
/* Allow access to the audio stream on Haiku */
|
||||
|
||||
#include <SoundPlayer.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "../../core/haiku/SDL_BeApp.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_haikuaudio.h"
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* !!! FIXME: have the callback call the higher level to avoid code dupe. */
|
||||
/* The Haiku callback for handling the audio buffer */
|
||||
static void FillSound(void *device, void *stream, size_t len,
|
||||
const media_raw_audio_format & format)
|
||||
{
|
||||
SDL_AudioDevice *audio = (SDL_AudioDevice *) device;
|
||||
SDL_AudioCallback callback = audio->callbackspec.callback;
|
||||
|
||||
SDL_LockMutex(audio->mixer_lock);
|
||||
|
||||
/* Only do something if audio is enabled */
|
||||
if (!SDL_AtomicGet(&audio->enabled) || SDL_AtomicGet(&audio->paused)) {
|
||||
if (audio->stream) {
|
||||
SDL_ClearAudioStream(audio->stream);
|
||||
}
|
||||
SDL_memset(stream, audio->spec.silence, len);
|
||||
} else {
|
||||
SDL_assert(audio->spec.size == len);
|
||||
|
||||
if (audio->stream == NULL) { /* no conversion necessary. */
|
||||
callback(audio->callbackspec.userdata, (Uint8 *) stream, len);
|
||||
} else { /* streaming/converting */
|
||||
const int stream_len = audio->callbackspec.size;
|
||||
const int ilen = (int) len;
|
||||
while (SDL_GetAudioStreamAvailable(audio->stream) < ilen) {
|
||||
callback(audio->callbackspec.userdata, audio->work_buffer, stream_len);
|
||||
if (SDL_PutAudioStreamData(audio->stream, audio->work_buffer, stream_len) == -1) {
|
||||
SDL_ClearAudioStream(audio->stream);
|
||||
SDL_AtomicSet(&audio->enabled, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const int got = SDL_GetAudioStreamData(audio->stream, stream, ilen);
|
||||
SDL_assert((got < 0) || (got == ilen));
|
||||
if (got != ilen) {
|
||||
SDL_memset(stream, audio->spec.silence, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_UnlockMutex(audio->mixer_lock);
|
||||
}
|
||||
|
||||
static void HAIKUAUDIO_CloseDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
if (_this->hidden->audio_obj) {
|
||||
_this->hidden->audio_obj->Stop();
|
||||
delete _this->hidden->audio_obj;
|
||||
}
|
||||
delete _this->hidden;
|
||||
}
|
||||
|
||||
|
||||
static const int sig_list[] = {
|
||||
SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGALRM, SIGTERM, SIGWINCH, 0
|
||||
};
|
||||
|
||||
static inline void MaskSignals(sigset_t * omask)
|
||||
{
|
||||
sigset_t mask;
|
||||
int i;
|
||||
|
||||
sigemptyset(&mask);
|
||||
for (i = 0; sig_list[i]; ++i) {
|
||||
sigaddset(&mask, sig_list[i]);
|
||||
}
|
||||
sigprocmask(SIG_BLOCK, &mask, omask);
|
||||
}
|
||||
|
||||
static inline void UnmaskSignals(sigset_t * omask)
|
||||
{
|
||||
sigprocmask(SIG_SETMASK, omask, NULL);
|
||||
}
|
||||
|
||||
|
||||
static int HAIKUAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
{
|
||||
media_raw_audio_format format;
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts;
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
_this->hidden = new SDL_PrivateAudioData;
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(_this->hidden);
|
||||
|
||||
/* Parse the audio format and fill the Be raw audio format */
|
||||
SDL_zero(format);
|
||||
format.byte_order = B_MEDIA_LITTLE_ENDIAN;
|
||||
format.frame_rate = (float) _this->spec.freq;
|
||||
format.channel_count = _this->spec.channels; /* !!! FIXME: support > 2? */
|
||||
|
||||
closefmts = SDL_ClosestAudioFormats(_this->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
switch (test_format) {
|
||||
case SDL_AUDIO_S8:
|
||||
format.format = media_raw_audio_format::B_AUDIO_CHAR;
|
||||
break;
|
||||
|
||||
case SDL_AUDIO_U8:
|
||||
format.format = media_raw_audio_format::B_AUDIO_UCHAR;
|
||||
break;
|
||||
|
||||
case SDL_AUDIO_S16LSB:
|
||||
format.format = media_raw_audio_format::B_AUDIO_SHORT;
|
||||
break;
|
||||
|
||||
case SDL_AUDIO_S16MSB:
|
||||
format.format = media_raw_audio_format::B_AUDIO_SHORT;
|
||||
format.byte_order = B_MEDIA_BIG_ENDIAN;
|
||||
break;
|
||||
|
||||
case SDL_AUDIO_S32LSB:
|
||||
format.format = media_raw_audio_format::B_AUDIO_INT;
|
||||
break;
|
||||
|
||||
case SDL_AUDIO_S32MSB:
|
||||
format.format = media_raw_audio_format::B_AUDIO_INT;
|
||||
format.byte_order = B_MEDIA_BIG_ENDIAN;
|
||||
break;
|
||||
|
||||
case SDL_AUDIO_F32LSB:
|
||||
format.format = media_raw_audio_format::B_AUDIO_FLOAT;
|
||||
break;
|
||||
|
||||
case SDL_AUDIO_F32MSB:
|
||||
format.format = media_raw_audio_format::B_AUDIO_FLOAT;
|
||||
format.byte_order = B_MEDIA_BIG_ENDIAN;
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!test_format) { /* shouldn't happen, but just in case... */
|
||||
return SDL_SetError("%s: Unsupported audio format", "haiku");
|
||||
}
|
||||
_this->spec.format = test_format;
|
||||
|
||||
/* Calculate the final parameters for this audio specification */
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
format.buffer_size = _this->spec.size;
|
||||
|
||||
/* Subscribe to the audio stream (creates a new thread) */
|
||||
sigset_t omask;
|
||||
MaskSignals(&omask);
|
||||
_this->hidden->audio_obj = new BSoundPlayer(&format, "SDL Audio",
|
||||
FillSound, NULL, _this);
|
||||
UnmaskSignals(&omask);
|
||||
|
||||
if (_this->hidden->audio_obj->Start() == B_NO_ERROR) {
|
||||
_this->hidden->audio_obj->SetHasData(true);
|
||||
} else {
|
||||
return SDL_SetError("Unable to start Be audio");
|
||||
}
|
||||
|
||||
/* We're running! */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void HAIKUAUDIO_Deinitialize(void)
|
||||
{
|
||||
SDL_QuitBeApp();
|
||||
}
|
||||
|
||||
static SDL_bool HAIKUAUDIO_Init(SDL_AudioDriverImpl * impl)
|
||||
{
|
||||
/* Initialize the Be Application, if it's not already started */
|
||||
if (SDL_InitBeApp() < 0) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->OpenDevice = HAIKUAUDIO_OpenDevice;
|
||||
impl->CloseDevice = HAIKUAUDIO_CloseDevice;
|
||||
impl->Deinitialize = HAIKUAUDIO_Deinitialize;
|
||||
impl->ProvidesOwnCallbackThread = SDL_TRUE;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
extern "C"
|
||||
{
|
||||
extern AudioBootStrap HAIKUAUDIO_bootstrap;
|
||||
}
|
||||
AudioBootStrap HAIKUAUDIO_bootstrap = {
|
||||
"haiku", "Haiku BSoundPlayer", HAIKUAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_HAIKU */
|
33
external/sdl/SDL/src/audio/haiku/SDL_haikuaudio.h
vendored
Normal file
33
external/sdl/SDL/src/audio/haiku/SDL_haikuaudio.h
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_haikuaudio_h_
|
||||
#define SDL_haikuaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
BSoundPlayer *audio_obj;
|
||||
};
|
||||
|
||||
#endif /* SDL_haikuaudio_h_ */
|
426
external/sdl/SDL/src/audio/jack/SDL_jackaudio.c
vendored
Normal file
426
external/sdl/SDL/src/audio/jack/SDL_jackaudio.c
vendored
Normal file
@ -0,0 +1,426 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_AUDIO_DRIVER_JACK
|
||||
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_jackaudio.h"
|
||||
#include "../../thread/SDL_systhread.h"
|
||||
|
||||
static jack_client_t *(*JACK_jack_client_open)(const char *, jack_options_t, jack_status_t *, ...);
|
||||
static int (*JACK_jack_client_close)(jack_client_t *);
|
||||
static void (*JACK_jack_on_shutdown)(jack_client_t *, JackShutdownCallback, void *);
|
||||
static int (*JACK_jack_activate)(jack_client_t *);
|
||||
static int (*JACK_jack_deactivate)(jack_client_t *);
|
||||
static void *(*JACK_jack_port_get_buffer)(jack_port_t *, jack_nframes_t);
|
||||
static int (*JACK_jack_port_unregister)(jack_client_t *, jack_port_t *);
|
||||
static void (*JACK_jack_free)(void *);
|
||||
static const char **(*JACK_jack_get_ports)(jack_client_t *, const char *, const char *, unsigned long);
|
||||
static jack_nframes_t (*JACK_jack_get_sample_rate)(jack_client_t *);
|
||||
static jack_nframes_t (*JACK_jack_get_buffer_size)(jack_client_t *);
|
||||
static jack_port_t *(*JACK_jack_port_register)(jack_client_t *, const char *, const char *, unsigned long, unsigned long);
|
||||
static jack_port_t *(*JACK_jack_port_by_name)(jack_client_t *, const char *);
|
||||
static const char *(*JACK_jack_port_name)(const jack_port_t *);
|
||||
static const char *(*JACK_jack_port_type)(const jack_port_t *);
|
||||
static int (*JACK_jack_connect)(jack_client_t *, const char *, const char *);
|
||||
static int (*JACK_jack_set_process_callback)(jack_client_t *, JackProcessCallback, void *);
|
||||
|
||||
static int load_jack_syms(void);
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_JACK_DYNAMIC
|
||||
|
||||
static const char *jack_library = SDL_AUDIO_DRIVER_JACK_DYNAMIC;
|
||||
static void *jack_handle = NULL;
|
||||
|
||||
/* !!! FIXME: this is copy/pasted in several places now */
|
||||
static int load_jack_sym(const char *fn, void **addr)
|
||||
{
|
||||
*addr = SDL_LoadFunction(jack_handle, fn);
|
||||
if (*addr == NULL) {
|
||||
/* Don't call SDL_SetError(): SDL_LoadFunction already did. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* cast funcs to char* first, to please GCC's strict aliasing rules. */
|
||||
#define SDL_JACK_SYM(x) \
|
||||
if (!load_jack_sym(#x, (void **)(char *)&JACK_##x)) \
|
||||
return -1
|
||||
|
||||
static void UnloadJackLibrary(void)
|
||||
{
|
||||
if (jack_handle != NULL) {
|
||||
SDL_UnloadObject(jack_handle);
|
||||
jack_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int LoadJackLibrary(void)
|
||||
{
|
||||
int retval = 0;
|
||||
if (jack_handle == NULL) {
|
||||
jack_handle = SDL_LoadObject(jack_library);
|
||||
if (jack_handle == NULL) {
|
||||
retval = -1;
|
||||
/* Don't call SDL_SetError(): SDL_LoadObject already did. */
|
||||
} else {
|
||||
retval = load_jack_syms();
|
||||
if (retval < 0) {
|
||||
UnloadJackLibrary();
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define SDL_JACK_SYM(x) JACK_##x = x
|
||||
|
||||
static void UnloadJackLibrary(void)
|
||||
{
|
||||
}
|
||||
|
||||
static int LoadJackLibrary(void)
|
||||
{
|
||||
load_jack_syms();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_JACK_DYNAMIC */
|
||||
|
||||
static int load_jack_syms(void)
|
||||
{
|
||||
SDL_JACK_SYM(jack_client_open);
|
||||
SDL_JACK_SYM(jack_client_close);
|
||||
SDL_JACK_SYM(jack_on_shutdown);
|
||||
SDL_JACK_SYM(jack_activate);
|
||||
SDL_JACK_SYM(jack_deactivate);
|
||||
SDL_JACK_SYM(jack_port_get_buffer);
|
||||
SDL_JACK_SYM(jack_port_unregister);
|
||||
SDL_JACK_SYM(jack_free);
|
||||
SDL_JACK_SYM(jack_get_ports);
|
||||
SDL_JACK_SYM(jack_get_sample_rate);
|
||||
SDL_JACK_SYM(jack_get_buffer_size);
|
||||
SDL_JACK_SYM(jack_port_register);
|
||||
SDL_JACK_SYM(jack_port_by_name);
|
||||
SDL_JACK_SYM(jack_port_name);
|
||||
SDL_JACK_SYM(jack_port_type);
|
||||
SDL_JACK_SYM(jack_connect);
|
||||
SDL_JACK_SYM(jack_set_process_callback);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jackShutdownCallback(void *arg) /* JACK went away; device is lost. */
|
||||
{
|
||||
SDL_AudioDevice *_this = (SDL_AudioDevice *)arg;
|
||||
SDL_OpenedAudioDeviceDisconnected(_this);
|
||||
SDL_PostSemaphore(_this->hidden->iosem); /* unblock the SDL thread. */
|
||||
}
|
||||
|
||||
// !!! FIXME: implement and register these!
|
||||
// typedef int(* JackSampleRateCallback)(jack_nframes_t nframes, void *arg)
|
||||
// typedef int(* JackBufferSizeCallback)(jack_nframes_t nframes, void *arg)
|
||||
|
||||
static int jackProcessPlaybackCallback(jack_nframes_t nframes, void *arg)
|
||||
{
|
||||
SDL_AudioDevice *_this = (SDL_AudioDevice *)arg;
|
||||
jack_port_t **ports = _this->hidden->sdlports;
|
||||
const int total_channels = _this->spec.channels;
|
||||
const int total_frames = _this->spec.samples;
|
||||
int channelsi;
|
||||
|
||||
if (!SDL_AtomicGet(&_this->enabled)) {
|
||||
/* silence the buffer to avoid repeats and corruption. */
|
||||
SDL_memset(_this->hidden->iobuffer, '\0', _this->spec.size);
|
||||
}
|
||||
|
||||
for (channelsi = 0; channelsi < total_channels; channelsi++) {
|
||||
float *dst = (float *)JACK_jack_port_get_buffer(ports[channelsi], nframes);
|
||||
if (dst) {
|
||||
const float *src = _this->hidden->iobuffer + channelsi;
|
||||
int framesi;
|
||||
for (framesi = 0; framesi < total_frames; framesi++) {
|
||||
*(dst++) = *src;
|
||||
src += total_channels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_PostSemaphore(_this->hidden->iosem); /* tell SDL thread we're done; refill the buffer. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function waits until it is possible to write a full sound buffer */
|
||||
static void JACK_WaitDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
if (SDL_AtomicGet(&_this->enabled)) {
|
||||
if (SDL_WaitSemaphore(_this->hidden->iosem) == -1) {
|
||||
SDL_OpenedAudioDeviceDisconnected(_this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Uint8 *JACK_GetDeviceBuf(SDL_AudioDevice *_this)
|
||||
{
|
||||
return (Uint8 *)_this->hidden->iobuffer;
|
||||
}
|
||||
|
||||
static int jackProcessCaptureCallback(jack_nframes_t nframes, void *arg)
|
||||
{
|
||||
SDL_AudioDevice *_this = (SDL_AudioDevice *)arg;
|
||||
if (SDL_AtomicGet(&_this->enabled)) {
|
||||
jack_port_t **ports = _this->hidden->sdlports;
|
||||
const int total_channels = _this->spec.channels;
|
||||
const int total_frames = _this->spec.samples;
|
||||
int channelsi;
|
||||
|
||||
for (channelsi = 0; channelsi < total_channels; channelsi++) {
|
||||
const float *src = (const float *)JACK_jack_port_get_buffer(ports[channelsi], nframes);
|
||||
if (src) {
|
||||
float *dst = _this->hidden->iobuffer + channelsi;
|
||||
int framesi;
|
||||
for (framesi = 0; framesi < total_frames; framesi++) {
|
||||
*dst = *(src++);
|
||||
dst += total_channels;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_PostSemaphore(_this->hidden->iosem); /* tell SDL thread we're done; new buffer is ready! */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int JACK_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
|
||||
{
|
||||
SDL_assert(buflen == _this->spec.size); /* we always fill a full buffer. */
|
||||
|
||||
/* Wait for JACK to fill the iobuffer */
|
||||
if (SDL_WaitSemaphore(_this->hidden->iosem) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_memcpy(buffer, _this->hidden->iobuffer, buflen);
|
||||
return buflen;
|
||||
}
|
||||
|
||||
static void JACK_FlushCapture(SDL_AudioDevice *_this)
|
||||
{
|
||||
SDL_WaitSemaphore(_this->hidden->iosem);
|
||||
}
|
||||
|
||||
static void JACK_CloseDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
if (_this->hidden->client) {
|
||||
JACK_jack_deactivate(_this->hidden->client);
|
||||
|
||||
if (_this->hidden->sdlports) {
|
||||
const int channels = _this->spec.channels;
|
||||
int i;
|
||||
for (i = 0; i < channels; i++) {
|
||||
JACK_jack_port_unregister(_this->hidden->client, _this->hidden->sdlports[i]);
|
||||
}
|
||||
SDL_free(_this->hidden->sdlports);
|
||||
}
|
||||
|
||||
JACK_jack_client_close(_this->hidden->client);
|
||||
}
|
||||
|
||||
if (_this->hidden->iosem) {
|
||||
SDL_DestroySemaphore(_this->hidden->iosem);
|
||||
}
|
||||
|
||||
SDL_free(_this->hidden->iobuffer);
|
||||
SDL_free(_this->hidden);
|
||||
}
|
||||
|
||||
static int JACK_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
{
|
||||
/* Note that JACK uses "output" for capture devices (they output audio
|
||||
data to us) and "input" for playback (we input audio data to them).
|
||||
Likewise, SDL's playback port will be "output" (we write data out)
|
||||
and capture will be "input" (we read data in). */
|
||||
SDL_bool iscapture = _this->iscapture;
|
||||
const unsigned long sysportflags = iscapture ? JackPortIsOutput : JackPortIsInput;
|
||||
const unsigned long sdlportflags = iscapture ? JackPortIsInput : JackPortIsOutput;
|
||||
const JackProcessCallback callback = iscapture ? jackProcessCaptureCallback : jackProcessPlaybackCallback;
|
||||
const char *sdlportstr = iscapture ? "input" : "output";
|
||||
const char **devports = NULL;
|
||||
int *audio_ports;
|
||||
jack_client_t *client = NULL;
|
||||
jack_status_t status;
|
||||
int channels = 0;
|
||||
int ports = 0;
|
||||
int i;
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
_this->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*_this->hidden));
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
/* !!! FIXME: we _still_ need an API to specify an app name */
|
||||
client = JACK_jack_client_open("SDL", JackNoStartServer, &status, NULL);
|
||||
_this->hidden->client = client;
|
||||
if (client == NULL) {
|
||||
return SDL_SetError("Can't open JACK client");
|
||||
}
|
||||
|
||||
devports = JACK_jack_get_ports(client, NULL, NULL, JackPortIsPhysical | sysportflags);
|
||||
if (devports == NULL || !devports[0]) {
|
||||
return SDL_SetError("No physical JACK ports available");
|
||||
}
|
||||
|
||||
while (devports[++ports]) {
|
||||
/* spin to count devports */
|
||||
}
|
||||
|
||||
/* Filter out non-audio ports */
|
||||
audio_ports = SDL_calloc(ports, sizeof(*audio_ports));
|
||||
for (i = 0; i < ports; i++) {
|
||||
const jack_port_t *dport = JACK_jack_port_by_name(client, devports[i]);
|
||||
const char *type = JACK_jack_port_type(dport);
|
||||
const int len = SDL_strlen(type);
|
||||
/* See if type ends with "audio" */
|
||||
if (len >= 5 && !SDL_memcmp(type + len - 5, "audio", 5)) {
|
||||
audio_ports[channels++] = i;
|
||||
}
|
||||
}
|
||||
if (channels == 0) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_SetError("No physical JACK ports available");
|
||||
}
|
||||
|
||||
/* !!! FIXME: docs say about buffer size: "This size may change, clients that depend on it must register a bufsize_callback so they will be notified if it does." */
|
||||
|
||||
/* Jack pretty much demands what it wants. */
|
||||
_this->spec.format = SDL_AUDIO_F32SYS;
|
||||
_this->spec.freq = JACK_jack_get_sample_rate(client);
|
||||
_this->spec.channels = channels;
|
||||
_this->spec.samples = JACK_jack_get_buffer_size(client);
|
||||
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
_this->hidden->iosem = SDL_CreateSemaphore(0);
|
||||
if (!_this->hidden->iosem) {
|
||||
SDL_free(audio_ports);
|
||||
return -1; /* error was set by SDL_CreateSemaphore */
|
||||
}
|
||||
|
||||
_this->hidden->iobuffer = (float *)SDL_calloc(1, _this->spec.size);
|
||||
if (!_this->hidden->iobuffer) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
/* Build SDL's ports, which we will connect to the device ports. */
|
||||
_this->hidden->sdlports = (jack_port_t **)SDL_calloc(channels, sizeof(jack_port_t *));
|
||||
if (_this->hidden->sdlports == NULL) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
char portname[32];
|
||||
(void)SDL_snprintf(portname, sizeof(portname), "sdl_jack_%s_%d", sdlportstr, i);
|
||||
_this->hidden->sdlports[i] = JACK_jack_port_register(client, portname, JACK_DEFAULT_AUDIO_TYPE, sdlportflags, 0);
|
||||
if (_this->hidden->sdlports[i] == NULL) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_SetError("jack_port_register failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (JACK_jack_set_process_callback(client, callback, _this) != 0) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_SetError("JACK: Couldn't set process callback");
|
||||
}
|
||||
|
||||
JACK_jack_on_shutdown(client, jackShutdownCallback, _this);
|
||||
|
||||
if (JACK_jack_activate(client) != 0) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_SetError("Failed to activate JACK client");
|
||||
}
|
||||
|
||||
/* once activated, we can connect all the ports. */
|
||||
for (i = 0; i < channels; i++) {
|
||||
const char *sdlport = JACK_jack_port_name(_this->hidden->sdlports[i]);
|
||||
const char *srcport = iscapture ? devports[audio_ports[i]] : sdlport;
|
||||
const char *dstport = iscapture ? sdlport : devports[audio_ports[i]];
|
||||
if (JACK_jack_connect(client, srcport, dstport) != 0) {
|
||||
SDL_free(audio_ports);
|
||||
return SDL_SetError("Couldn't connect JACK ports: %s => %s", srcport, dstport);
|
||||
}
|
||||
}
|
||||
|
||||
/* don't need these anymore. */
|
||||
JACK_jack_free(devports);
|
||||
SDL_free(audio_ports);
|
||||
|
||||
/* We're ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void JACK_Deinitialize(void)
|
||||
{
|
||||
UnloadJackLibrary();
|
||||
}
|
||||
|
||||
static SDL_bool JACK_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (LoadJackLibrary() < 0) {
|
||||
return SDL_FALSE;
|
||||
} else {
|
||||
/* Make sure a JACK server is running and available. */
|
||||
jack_status_t status;
|
||||
jack_client_t *client = JACK_jack_client_open("SDL", JackNoStartServer, &status, NULL);
|
||||
if (client == NULL) {
|
||||
UnloadJackLibrary();
|
||||
return SDL_FALSE;
|
||||
}
|
||||
JACK_jack_client_close(client);
|
||||
}
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->OpenDevice = JACK_OpenDevice;
|
||||
impl->WaitDevice = JACK_WaitDevice;
|
||||
impl->GetDeviceBuf = JACK_GetDeviceBuf;
|
||||
impl->CloseDevice = JACK_CloseDevice;
|
||||
impl->Deinitialize = JACK_Deinitialize;
|
||||
impl->CaptureFromDevice = JACK_CaptureFromDevice;
|
||||
impl->FlushCapture = JACK_FlushCapture;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap JACK_bootstrap = {
|
||||
"jack", "JACK Audio Connection Kit", JACK_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_JACK */
|
36
external/sdl/SDL/src/audio/jack/SDL_jackaudio.h
vendored
Normal file
36
external/sdl/SDL/src/audio/jack/SDL_jackaudio.h
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_jackaudio_h_
|
||||
#define SDL_jackaudio_h_
|
||||
|
||||
#include <jack/jack.h>
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
jack_client_t *client;
|
||||
SDL_Semaphore *iosem;
|
||||
float *iobuffer;
|
||||
jack_port_t **sdlports;
|
||||
};
|
||||
|
||||
#endif /* SDL_jackaudio_h_ */
|
336
external/sdl/SDL/src/audio/n3ds/SDL_n3dsaudio.c
vendored
Normal file
336
external/sdl/SDL/src/audio/n3ds/SDL_n3dsaudio.c
vendored
Normal file
@ -0,0 +1,336 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_AUDIO_DRIVER_N3DS
|
||||
|
||||
/* N3DS Audio driver */
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_n3dsaudio.h"
|
||||
|
||||
#define N3DSAUDIO_DRIVER_NAME "n3ds"
|
||||
|
||||
static dspHookCookie dsp_hook;
|
||||
static SDL_AudioDevice *audio_device;
|
||||
|
||||
static void FreePrivateData(SDL_AudioDevice *_this);
|
||||
static int FindAudioFormat(SDL_AudioDevice *_this);
|
||||
|
||||
static SDL_INLINE void contextLock(SDL_AudioDevice *_this)
|
||||
{
|
||||
LightLock_Lock(&_this->hidden->lock);
|
||||
}
|
||||
|
||||
static SDL_INLINE void contextUnlock(SDL_AudioDevice *_this)
|
||||
{
|
||||
LightLock_Unlock(&_this->hidden->lock);
|
||||
}
|
||||
|
||||
static void N3DSAUD_LockAudio(SDL_AudioDevice *_this)
|
||||
{
|
||||
contextLock(_this);
|
||||
}
|
||||
|
||||
static void N3DSAUD_UnlockAudio(SDL_AudioDevice *_this)
|
||||
{
|
||||
contextUnlock(_this);
|
||||
}
|
||||
|
||||
static void N3DSAUD_DspHook(DSP_HookType hook)
|
||||
{
|
||||
if (hook == DSPHOOK_ONCANCEL) {
|
||||
contextLock(audio_device);
|
||||
audio_device->hidden->isCancelled = SDL_TRUE;
|
||||
SDL_AtomicSet(&audio_device->enabled, SDL_FALSE);
|
||||
CondVar_Broadcast(&audio_device->hidden->cv);
|
||||
contextUnlock(audio_device);
|
||||
}
|
||||
}
|
||||
|
||||
static void AudioFrameFinished(void *device)
|
||||
{
|
||||
bool shouldBroadcast = false;
|
||||
unsigned i;
|
||||
SDL_AudioDevice *_this = (SDL_AudioDevice *)device;
|
||||
|
||||
contextLock(_this);
|
||||
|
||||
for (i = 0; i < NUM_BUFFERS; i++) {
|
||||
if (_this->hidden->waveBuf[i].status == NDSP_WBUF_DONE) {
|
||||
_this->hidden->waveBuf[i].status = NDSP_WBUF_FREE;
|
||||
shouldBroadcast = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldBroadcast) {
|
||||
CondVar_Broadcast(&_this->hidden->cv);
|
||||
}
|
||||
|
||||
contextUnlock(_this);
|
||||
}
|
||||
|
||||
static int N3DSAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
{
|
||||
Result ndsp_init_res;
|
||||
Uint8 *data_vaddr;
|
||||
float mix[12];
|
||||
_this->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*_this->hidden));
|
||||
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
/* Initialise the DSP service */
|
||||
ndsp_init_res = ndspInit();
|
||||
if (R_FAILED(ndsp_init_res)) {
|
||||
if ((R_SUMMARY(ndsp_init_res) == RS_NOTFOUND) && (R_MODULE(ndsp_init_res) == RM_DSP)) {
|
||||
SDL_SetError("DSP init failed: dspfirm.cdc missing!");
|
||||
} else {
|
||||
SDL_SetError("DSP init failed. Error code: 0x%lX", ndsp_init_res);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Initialise internal state */
|
||||
LightLock_Init(&_this->hidden->lock);
|
||||
CondVar_Init(&_this->hidden->cv);
|
||||
|
||||
if (_this->spec.channels > 2) {
|
||||
_this->spec.channels = 2;
|
||||
}
|
||||
|
||||
/* Should not happen but better be safe. */
|
||||
if (FindAudioFormat(_this) < 0) {
|
||||
return SDL_SetError("No supported audio format found.");
|
||||
}
|
||||
|
||||
/* Update the fragment size as size in bytes */
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
/* Allocate mixing buffer */
|
||||
if (_this->spec.size >= SDL_MAX_UINT32 / 2) {
|
||||
return SDL_SetError("Mixing buffer is too large.");
|
||||
}
|
||||
|
||||
_this->hidden->mixlen = _this->spec.size;
|
||||
_this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->spec.size);
|
||||
if (_this->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
SDL_memset(_this->hidden->mixbuf, _this->spec.silence, _this->spec.size);
|
||||
|
||||
data_vaddr = (Uint8 *)linearAlloc(_this->hidden->mixlen * NUM_BUFFERS);
|
||||
if (data_vaddr == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
SDL_memset(data_vaddr, 0, _this->hidden->mixlen * NUM_BUFFERS);
|
||||
DSP_FlushDataCache(data_vaddr, _this->hidden->mixlen * NUM_BUFFERS);
|
||||
|
||||
_this->hidden->nextbuf = 0;
|
||||
_this->hidden->channels = _this->spec.channels;
|
||||
_this->hidden->samplerate = _this->spec.freq;
|
||||
|
||||
ndspChnReset(0);
|
||||
|
||||
ndspChnSetInterp(0, NDSP_INTERP_LINEAR);
|
||||
ndspChnSetRate(0, _this->spec.freq);
|
||||
ndspChnSetFormat(0, _this->hidden->format);
|
||||
|
||||
SDL_memset(mix, 0, sizeof(mix));
|
||||
mix[0] = 1.0;
|
||||
mix[1] = 1.0;
|
||||
ndspChnSetMix(0, mix);
|
||||
|
||||
SDL_memset(_this->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS);
|
||||
|
||||
for (unsigned i = 0; i < NUM_BUFFERS; i++) {
|
||||
_this->hidden->waveBuf[i].data_vaddr = data_vaddr;
|
||||
_this->hidden->waveBuf[i].nsamples = _this->hidden->mixlen / _this->hidden->bytePerSample;
|
||||
data_vaddr += _this->hidden->mixlen;
|
||||
}
|
||||
|
||||
/* Setup callback */
|
||||
audio_device = _this;
|
||||
ndspSetCallback(AudioFrameFinished, _this);
|
||||
dspHook(&dsp_hook, N3DSAUD_DspHook);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int N3DSAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
|
||||
{
|
||||
/* Delay to make this sort of simulate real audio input. */
|
||||
SDL_Delay((_this->spec.samples * 1000) / _this->spec.freq);
|
||||
|
||||
/* always return a full buffer of silence. */
|
||||
SDL_memset(buffer, _this->spec.silence, buflen);
|
||||
return buflen;
|
||||
}
|
||||
|
||||
static void N3DSAUDIO_PlayDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
size_t nextbuf;
|
||||
size_t sampleLen;
|
||||
contextLock(_this);
|
||||
|
||||
nextbuf = _this->hidden->nextbuf;
|
||||
sampleLen = _this->hidden->mixlen;
|
||||
|
||||
if (_this->hidden->isCancelled ||
|
||||
_this->hidden->waveBuf[nextbuf].status != NDSP_WBUF_FREE) {
|
||||
contextUnlock(_this);
|
||||
return;
|
||||
}
|
||||
|
||||
_this->hidden->nextbuf = (nextbuf + 1) % NUM_BUFFERS;
|
||||
|
||||
contextUnlock(_this);
|
||||
|
||||
SDL_memcpy((void *)_this->hidden->waveBuf[nextbuf].data_vaddr,
|
||||
_this->hidden->mixbuf, sampleLen);
|
||||
DSP_FlushDataCache(_this->hidden->waveBuf[nextbuf].data_vaddr, sampleLen);
|
||||
|
||||
ndspChnWaveBufAdd(0, &_this->hidden->waveBuf[nextbuf]);
|
||||
}
|
||||
|
||||
static void N3DSAUDIO_WaitDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
contextLock(_this);
|
||||
while (!_this->hidden->isCancelled &&
|
||||
_this->hidden->waveBuf[_this->hidden->nextbuf].status != NDSP_WBUF_FREE) {
|
||||
CondVar_Wait(&_this->hidden->cv, &_this->hidden->lock);
|
||||
}
|
||||
contextUnlock(_this);
|
||||
}
|
||||
|
||||
static Uint8 *N3DSAUDIO_GetDeviceBuf(SDL_AudioDevice *_this)
|
||||
{
|
||||
return _this->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static void N3DSAUDIO_CloseDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
contextLock(_this);
|
||||
|
||||
dspUnhook(&dsp_hook);
|
||||
ndspSetCallback(NULL, NULL);
|
||||
|
||||
if (!_this->hidden->isCancelled) {
|
||||
ndspChnReset(0);
|
||||
SDL_memset(_this->hidden->waveBuf, 0, sizeof(ndspWaveBuf) * NUM_BUFFERS);
|
||||
CondVar_Broadcast(&_this->hidden->cv);
|
||||
}
|
||||
|
||||
contextUnlock(_this);
|
||||
|
||||
ndspExit();
|
||||
|
||||
FreePrivateData(_this);
|
||||
}
|
||||
|
||||
static void N3DSAUDIO_ThreadInit(SDL_AudioDevice *_this)
|
||||
{
|
||||
s32 current_priority;
|
||||
svcGetThreadPriority(¤t_priority, CUR_THREAD_HANDLE);
|
||||
current_priority--;
|
||||
/* 0x18 is reserved for video, 0x30 is the default for main thread */
|
||||
current_priority = SDL_clamp(current_priority, 0x19, 0x2F);
|
||||
svcSetThreadPriority(CUR_THREAD_HANDLE, current_priority);
|
||||
}
|
||||
|
||||
static SDL_bool N3DSAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
/* Set the function pointers */
|
||||
impl->OpenDevice = N3DSAUDIO_OpenDevice;
|
||||
impl->PlayDevice = N3DSAUDIO_PlayDevice;
|
||||
impl->WaitDevice = N3DSAUDIO_WaitDevice;
|
||||
impl->GetDeviceBuf = N3DSAUDIO_GetDeviceBuf;
|
||||
impl->CloseDevice = N3DSAUDIO_CloseDevice;
|
||||
impl->ThreadInit = N3DSAUDIO_ThreadInit;
|
||||
impl->LockDevice = N3DSAUD_LockAudio;
|
||||
impl->UnlockDevice = N3DSAUD_UnlockAudio;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
|
||||
/* Should be possible, but micInit would fail */
|
||||
impl->HasCaptureSupport = SDL_FALSE;
|
||||
impl->CaptureFromDevice = N3DSAUDIO_CaptureFromDevice;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap N3DSAUDIO_bootstrap = {
|
||||
N3DSAUDIO_DRIVER_NAME,
|
||||
"SDL N3DS audio driver",
|
||||
N3DSAUDIO_Init,
|
||||
0
|
||||
};
|
||||
|
||||
/**
|
||||
* Cleans up all allocated memory, safe to call with null pointers
|
||||
*/
|
||||
static void FreePrivateData(SDL_AudioDevice *_this)
|
||||
{
|
||||
if (!_this->hidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_this->hidden->waveBuf[0].data_vaddr) {
|
||||
linearFree((void *)_this->hidden->waveBuf[0].data_vaddr);
|
||||
}
|
||||
|
||||
if (_this->hidden->mixbuf) {
|
||||
SDL_free(_this->hidden->mixbuf);
|
||||
_this->hidden->mixbuf = NULL;
|
||||
}
|
||||
|
||||
SDL_free(_this->hidden);
|
||||
_this->hidden = NULL;
|
||||
}
|
||||
|
||||
static int FindAudioFormat(SDL_AudioDevice *_this)
|
||||
{
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(_this->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
_this->spec.format = test_format;
|
||||
switch (test_format) {
|
||||
case SDL_AUDIO_S8:
|
||||
/* Signed 8-bit audio supported */
|
||||
_this->hidden->format = (_this->spec.channels == 2) ? NDSP_FORMAT_STEREO_PCM8 : NDSP_FORMAT_MONO_PCM8;
|
||||
_this->hidden->isSigned = 1;
|
||||
_this->hidden->bytePerSample = _this->spec.channels;
|
||||
return 0;
|
||||
case SDL_AUDIO_S16:
|
||||
/* Signed 16-bit audio supported */
|
||||
_this->hidden->format = (_this->spec.channels == 2) ? NDSP_FORMAT_STEREO_PCM16 : NDSP_FORMAT_MONO_PCM16;
|
||||
_this->hidden->isSigned = 1;
|
||||
_this->hidden->bytePerSample = _this->spec.channels * 2;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_N3DS */
|
46
external/sdl/SDL/src/audio/n3ds/SDL_n3dsaudio.h
vendored
Normal file
46
external/sdl/SDL/src/audio/n3ds/SDL_n3dsaudio.h
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_n3dsaudio_h
|
||||
#define SDL_n3dsaudio_h
|
||||
|
||||
#include <3ds.h>
|
||||
|
||||
#define NUM_BUFFERS 2 /* -- Don't lower this! */
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* Speaker data */
|
||||
Uint8 *mixbuf;
|
||||
Uint32 mixlen;
|
||||
Uint32 format;
|
||||
Uint32 samplerate;
|
||||
Uint32 channels;
|
||||
Uint8 bytePerSample;
|
||||
Uint32 isSigned;
|
||||
Uint32 nextbuf;
|
||||
ndspWaveBuf waveBuf[NUM_BUFFERS];
|
||||
LightLock lock;
|
||||
CondVar cv;
|
||||
SDL_bool isCancelled;
|
||||
};
|
||||
|
||||
#endif /* SDL_n3dsaudio_h */
|
326
external/sdl/SDL/src/audio/netbsd/SDL_netbsdaudio.c
vendored
Normal file
326
external/sdl/SDL/src/audio/netbsd/SDL_netbsdaudio.c
vendored
Normal file
@ -0,0 +1,326 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_AUDIO_DRIVER_NETBSD
|
||||
|
||||
/*
|
||||
* Driver for native NetBSD audio(4).
|
||||
* nia@NetBSD.org
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/audioio.h>
|
||||
|
||||
#include "../../core/unix/SDL_poll.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../SDL_audiodev_c.h"
|
||||
#include "SDL_netbsdaudio.h"
|
||||
|
||||
/* #define DEBUG_AUDIO */
|
||||
|
||||
static void NETBSDAUDIO_DetectDevices(void)
|
||||
{
|
||||
SDL_EnumUnixAudioDevices(0, NULL);
|
||||
}
|
||||
|
||||
static void NETBSDAUDIO_Status(SDL_AudioDevice *_this)
|
||||
{
|
||||
#ifdef DEBUG_AUDIO
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
audio_info_t info;
|
||||
const struct audio_prinfo *prinfo;
|
||||
|
||||
if (ioctl(_this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
|
||||
fprintf(stderr, "AUDIO_GETINFO failed.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
prinfo = _this->iscapture ? &info.record : &info.play;
|
||||
|
||||
fprintf(stderr, "\n"
|
||||
"[%s info]\n"
|
||||
"buffer size : %d bytes\n"
|
||||
"sample rate : %i Hz\n"
|
||||
"channels : %i\n"
|
||||
"precision : %i-bit\n"
|
||||
"encoding : 0x%x\n"
|
||||
"seek : %i\n"
|
||||
"sample count : %i\n"
|
||||
"EOF count : %i\n"
|
||||
"paused : %s\n"
|
||||
"error occurred : %s\n"
|
||||
"waiting : %s\n"
|
||||
"active : %s\n"
|
||||
"",
|
||||
_this->iscapture ? "record" : "play",
|
||||
prinfo->buffer_size,
|
||||
prinfo->sample_rate,
|
||||
prinfo->channels,
|
||||
prinfo->precision,
|
||||
prinfo->encoding,
|
||||
prinfo->seek,
|
||||
prinfo->samples,
|
||||
prinfo->eof,
|
||||
prinfo->pause ? "yes" : "no",
|
||||
prinfo->error ? "yes" : "no",
|
||||
prinfo->waiting ? "yes" : "no",
|
||||
prinfo->active ? "yes" : "no");
|
||||
|
||||
fprintf(stderr, "\n"
|
||||
"[audio info]\n"
|
||||
"monitor_gain : %i\n"
|
||||
"hw block size : %d bytes\n"
|
||||
"hi watermark : %i\n"
|
||||
"lo watermark : %i\n"
|
||||
"audio mode : %s\n"
|
||||
"",
|
||||
info.monitor_gain,
|
||||
info.blocksize,
|
||||
info.hiwat, info.lowat,
|
||||
(info.mode == AUMODE_PLAY) ? "PLAY"
|
||||
: (info.mode = AUMODE_RECORD) ? "RECORD"
|
||||
: (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" : "?"));
|
||||
|
||||
fprintf(stderr, "\n"
|
||||
"[audio spec]\n"
|
||||
"format : 0x%x\n"
|
||||
"size : %u\n"
|
||||
"",
|
||||
_this->spec.format,
|
||||
_this->spec.size);
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
||||
|
||||
#endif /* DEBUG_AUDIO */
|
||||
}
|
||||
|
||||
static void NETBSDAUDIO_PlayDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = _this->hidden;
|
||||
int written;
|
||||
|
||||
/* Write the audio data */
|
||||
written = write(h->audio_fd, h->mixbuf, h->mixlen);
|
||||
if (written == -1) {
|
||||
/* Non recoverable error has occurred. It should be reported!!! */
|
||||
SDL_OpenedAudioDeviceDisconnected(_this);
|
||||
perror("audio");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
||||
#endif
|
||||
}
|
||||
|
||||
static Uint8 *NETBSDAUDIO_GetDeviceBuf(SDL_AudioDevice *_this)
|
||||
{
|
||||
return _this->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static int NETBSDAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *_buffer, int buflen)
|
||||
{
|
||||
Uint8 *buffer = (Uint8 *)_buffer;
|
||||
int br;
|
||||
|
||||
br = read(_this->hidden->audio_fd, buffer, buflen);
|
||||
if (br == -1) {
|
||||
/* Non recoverable error has occurred. It should be reported!!! */
|
||||
perror("audio");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Captured %d bytes of audio data\n", br);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void NETBSDAUDIO_FlushCapture(SDL_AudioDevice *_this)
|
||||
{
|
||||
audio_info_t info;
|
||||
size_t remain;
|
||||
Uint8 buf[512];
|
||||
|
||||
if (ioctl(_this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
|
||||
return; /* oh well. */
|
||||
}
|
||||
|
||||
remain = (size_t)(info.record.samples * (SDL_AUDIO_BITSIZE(_this->spec.format) / 8));
|
||||
while (remain > 0) {
|
||||
const size_t len = SDL_min(sizeof(buf), remain);
|
||||
const int br = read(_this->hidden->audio_fd, buf, len);
|
||||
if (br <= 0) {
|
||||
return; /* oh well. */
|
||||
}
|
||||
remain -= br;
|
||||
}
|
||||
}
|
||||
|
||||
static void NETBSDAUDIO_CloseDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
if (_this->hidden->audio_fd >= 0) {
|
||||
close(_this->hidden->audio_fd);
|
||||
}
|
||||
SDL_free(_this->hidden->mixbuf);
|
||||
SDL_free(_this->hidden);
|
||||
}
|
||||
|
||||
static int NETBSDAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
{
|
||||
SDL_bool iscapture = _this->iscapture;
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts;
|
||||
int encoding = AUDIO_ENCODING_NONE;
|
||||
audio_info_t info, hwinfo;
|
||||
struct audio_prinfo *prinfo = iscapture ? &info.record : &info.play;
|
||||
|
||||
/* We don't care what the devname is...we'll try to open anything. */
|
||||
/* ...but default to first name in the list... */
|
||||
if (devname == NULL) {
|
||||
devname = SDL_GetAudioDeviceName(0, iscapture);
|
||||
if (devname == NULL) {
|
||||
return SDL_SetError("No such audio device");
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
_this->hidden = (struct SDL_PrivateAudioData *) SDL_malloc(sizeof(*_this->hidden));
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(_this->hidden);
|
||||
|
||||
/* Open the audio device */
|
||||
_this->hidden->audio_fd = open(devname, (iscapture ? O_RDONLY : O_WRONLY) | O_CLOEXEC);
|
||||
if (_this->hidden->audio_fd < 0) {
|
||||
return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
|
||||
}
|
||||
|
||||
AUDIO_INITINFO(&info);
|
||||
|
||||
#ifdef AUDIO_GETFORMAT /* Introduced in NetBSD 9.0 */
|
||||
if (ioctl(_this->hidden->audio_fd, AUDIO_GETFORMAT, &hwinfo) != -1) {
|
||||
/*
|
||||
* Use the device's native sample rate so the kernel doesn't have to
|
||||
* resample.
|
||||
*/
|
||||
_this->spec.freq = iscapture ? hwinfo.record.sample_rate : hwinfo.play.sample_rate;
|
||||
}
|
||||
#endif
|
||||
|
||||
prinfo->sample_rate = _this->spec.freq;
|
||||
prinfo->channels = _this->spec.channels;
|
||||
|
||||
closefmts = SDL_ClosestAudioFormats(_this->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
switch (test_format) {
|
||||
case SDL_AUDIO_U8:
|
||||
encoding = AUDIO_ENCODING_ULINEAR;
|
||||
break;
|
||||
case SDL_AUDIO_S8:
|
||||
encoding = AUDIO_ENCODING_SLINEAR;
|
||||
break;
|
||||
case SDL_AUDIO_S16LSB:
|
||||
encoding = AUDIO_ENCODING_SLINEAR_LE;
|
||||
break;
|
||||
case SDL_AUDIO_S16MSB:
|
||||
encoding = AUDIO_ENCODING_SLINEAR_BE;
|
||||
break;
|
||||
case SDL_AUDIO_S32LSB:
|
||||
encoding = AUDIO_ENCODING_SLINEAR_LE;
|
||||
break;
|
||||
case SDL_AUDIO_S32MSB:
|
||||
encoding = AUDIO_ENCODING_SLINEAR_BE;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
return SDL_SetError("%s: Unsupported audio format", "netbsd");
|
||||
}
|
||||
prinfo->encoding = encoding;
|
||||
prinfo->precision = SDL_AUDIO_BITSIZE(test_format);
|
||||
|
||||
info.hiwat = 5;
|
||||
info.lowat = 3;
|
||||
if (ioctl(_this->hidden->audio_fd, AUDIO_SETINFO, &info) < 0) {
|
||||
return SDL_SetError("AUDIO_SETINFO failed for %s: %s", devname, strerror(errno));
|
||||
}
|
||||
|
||||
if (ioctl(_this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
|
||||
return SDL_SetError("AUDIO_GETINFO failed for %s: %s", devname, strerror(errno));
|
||||
}
|
||||
|
||||
/* Final spec used for the device. */
|
||||
_this->spec.format = test_format;
|
||||
_this->spec.freq = prinfo->sample_rate;
|
||||
_this->spec.channels = prinfo->channels;
|
||||
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
if (!iscapture) {
|
||||
/* Allocate mixing buffer */
|
||||
_this->hidden->mixlen = _this->spec.size;
|
||||
_this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->hidden->mixlen);
|
||||
if (_this->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(_this->hidden->mixbuf, _this->spec.silence, _this->spec.size);
|
||||
}
|
||||
|
||||
NETBSDAUDIO_Status(_this);
|
||||
|
||||
/* We're ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SDL_bool NETBSDAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
/* Set the function pointers */
|
||||
impl->DetectDevices = NETBSDAUDIO_DetectDevices;
|
||||
impl->OpenDevice = NETBSDAUDIO_OpenDevice;
|
||||
impl->PlayDevice = NETBSDAUDIO_PlayDevice;
|
||||
impl->GetDeviceBuf = NETBSDAUDIO_GetDeviceBuf;
|
||||
impl->CloseDevice = NETBSDAUDIO_CloseDevice;
|
||||
impl->CaptureFromDevice = NETBSDAUDIO_CaptureFromDevice;
|
||||
impl->FlushCapture = NETBSDAUDIO_FlushCapture;
|
||||
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap NETBSDAUDIO_bootstrap = {
|
||||
"netbsd", "NetBSD audio", NETBSDAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_NETBSD */
|
44
external/sdl/SDL/src/audio/netbsd/SDL_netbsdaudio.h
vendored
Normal file
44
external/sdl/SDL/src/audio/netbsd/SDL_netbsdaudio.h
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_netbsdaudio_h_
|
||||
#define SDL_netbsdaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The file descriptor for the audio device */
|
||||
int audio_fd;
|
||||
|
||||
/* Raw mixing buffer */
|
||||
Uint8 *mixbuf;
|
||||
int mixlen;
|
||||
|
||||
/* Support for audio timing using a timer, in addition to SDL_IOReady() */
|
||||
float frame_ticks;
|
||||
float next_frame;
|
||||
};
|
||||
|
||||
#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */
|
||||
|
||||
#endif /* SDL_netbsdaudio_h_ */
|
783
external/sdl/SDL/src/audio/openslES/SDL_openslES.c
vendored
Normal file
783
external/sdl/SDL/src/audio/openslES/SDL_openslES.c
vendored
Normal file
@ -0,0 +1,783 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_AUDIO_DRIVER_OPENSLES
|
||||
|
||||
/* For more discussion of low latency audio on Android, see this:
|
||||
https://googlesamples.github.io/android-audio-high-performance/guides/opensl_es.html
|
||||
*/
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_openslES.h"
|
||||
|
||||
#include "../../core/android/SDL_android.h"
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
#include <android/log.h>
|
||||
|
||||
|
||||
#define NUM_BUFFERS 2 /* -- Don't lower this! */
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
Uint8 *mixbuff;
|
||||
int next_buffer;
|
||||
Uint8 *pmixbuff[NUM_BUFFERS];
|
||||
SDL_Semaphore *playsem;
|
||||
};
|
||||
|
||||
#if 0
|
||||
#define LOG_TAG "SDL_openslES"
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||
//#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__)
|
||||
#define LOGV(...)
|
||||
#else
|
||||
#define LOGE(...)
|
||||
#define LOGI(...)
|
||||
#define LOGV(...)
|
||||
#endif
|
||||
|
||||
/*
|
||||
#define SL_SPEAKER_FRONT_LEFT ((SLuint32) 0x00000001)
|
||||
#define SL_SPEAKER_FRONT_RIGHT ((SLuint32) 0x00000002)
|
||||
#define SL_SPEAKER_FRONT_CENTER ((SLuint32) 0x00000004)
|
||||
#define SL_SPEAKER_LOW_FREQUENCY ((SLuint32) 0x00000008)
|
||||
#define SL_SPEAKER_BACK_LEFT ((SLuint32) 0x00000010)
|
||||
#define SL_SPEAKER_BACK_RIGHT ((SLuint32) 0x00000020)
|
||||
#define SL_SPEAKER_FRONT_LEFT_OF_CENTER ((SLuint32) 0x00000040)
|
||||
#define SL_SPEAKER_FRONT_RIGHT_OF_CENTER ((SLuint32) 0x00000080)
|
||||
#define SL_SPEAKER_BACK_CENTER ((SLuint32) 0x00000100)
|
||||
#define SL_SPEAKER_SIDE_LEFT ((SLuint32) 0x00000200)
|
||||
#define SL_SPEAKER_SIDE_RIGHT ((SLuint32) 0x00000400)
|
||||
#define SL_SPEAKER_TOP_CENTER ((SLuint32) 0x00000800)
|
||||
#define SL_SPEAKER_TOP_FRONT_LEFT ((SLuint32) 0x00001000)
|
||||
#define SL_SPEAKER_TOP_FRONT_CENTER ((SLuint32) 0x00002000)
|
||||
#define SL_SPEAKER_TOP_FRONT_RIGHT ((SLuint32) 0x00004000)
|
||||
#define SL_SPEAKER_TOP_BACK_LEFT ((SLuint32) 0x00008000)
|
||||
#define SL_SPEAKER_TOP_BACK_CENTER ((SLuint32) 0x00010000)
|
||||
#define SL_SPEAKER_TOP_BACK_RIGHT ((SLuint32) 0x00020000)
|
||||
*/
|
||||
#define SL_ANDROID_SPEAKER_STEREO (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT)
|
||||
#define SL_ANDROID_SPEAKER_QUAD (SL_ANDROID_SPEAKER_STEREO | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT)
|
||||
#define SL_ANDROID_SPEAKER_5DOT1 (SL_ANDROID_SPEAKER_QUAD | SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY)
|
||||
#define SL_ANDROID_SPEAKER_7DOT1 (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT)
|
||||
|
||||
/* engine interfaces */
|
||||
static SLObjectItf engineObject = NULL;
|
||||
static SLEngineItf engineEngine = NULL;
|
||||
|
||||
/* output mix interfaces */
|
||||
static SLObjectItf outputMixObject = NULL;
|
||||
|
||||
/* buffer queue player interfaces */
|
||||
static SLObjectItf bqPlayerObject = NULL;
|
||||
static SLPlayItf bqPlayerPlay = NULL;
|
||||
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL;
|
||||
#if 0
|
||||
static SLVolumeItf bqPlayerVolume;
|
||||
#endif
|
||||
|
||||
/* recorder interfaces */
|
||||
static SLObjectItf recorderObject = NULL;
|
||||
static SLRecordItf recorderRecord = NULL;
|
||||
static SLAndroidSimpleBufferQueueItf recorderBufferQueue = NULL;
|
||||
|
||||
#if 0
|
||||
static const char *sldevaudiorecorderstr = "SLES Audio Recorder";
|
||||
static const char *sldevaudioplayerstr = "SLES Audio Player";
|
||||
|
||||
#define SLES_DEV_AUDIO_RECORDER sldevaudiorecorderstr
|
||||
#define SLES_DEV_AUDIO_PLAYER sldevaudioplayerstr
|
||||
static void openslES_DetectDevices( int iscapture )
|
||||
{
|
||||
LOGI( "openSLES_DetectDevices()" );
|
||||
if ( iscapture )
|
||||
addfn( SLES_DEV_AUDIO_RECORDER );
|
||||
else
|
||||
addfn( SLES_DEV_AUDIO_PLAYER );
|
||||
}
|
||||
#endif
|
||||
|
||||
static void openslES_DestroyEngine(void)
|
||||
{
|
||||
LOGI("openslES_DestroyEngine()");
|
||||
|
||||
/* destroy output mix object, and invalidate all associated interfaces */
|
||||
if (outputMixObject != NULL) {
|
||||
(*outputMixObject)->Destroy(outputMixObject);
|
||||
outputMixObject = NULL;
|
||||
}
|
||||
|
||||
/* destroy engine object, and invalidate all associated interfaces */
|
||||
if (engineObject != NULL) {
|
||||
(*engineObject)->Destroy(engineObject);
|
||||
engineObject = NULL;
|
||||
engineEngine = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int openslES_CreateEngine(void)
|
||||
{
|
||||
const SLInterfaceID ids[1] = { SL_IID_VOLUME };
|
||||
const SLboolean req[1] = { SL_BOOLEAN_FALSE };
|
||||
SLresult result;
|
||||
|
||||
LOGI("openSLES_CreateEngine()");
|
||||
|
||||
/* create engine */
|
||||
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("slCreateEngine failed: %d", result);
|
||||
goto error;
|
||||
}
|
||||
LOGI("slCreateEngine OK");
|
||||
|
||||
/* realize the engine */
|
||||
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RealizeEngine failed: %d", result);
|
||||
goto error;
|
||||
}
|
||||
LOGI("RealizeEngine OK");
|
||||
|
||||
/* get the engine interface, which is needed in order to create other objects */
|
||||
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("EngineGetInterface failed: %d", result);
|
||||
goto error;
|
||||
}
|
||||
LOGI("EngineGetInterface OK");
|
||||
|
||||
/* create output mix */
|
||||
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("CreateOutputMix failed: %d", result);
|
||||
goto error;
|
||||
}
|
||||
LOGI("CreateOutputMix OK");
|
||||
|
||||
/* realize the output mix */
|
||||
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RealizeOutputMix failed: %d", result);
|
||||
goto error;
|
||||
}
|
||||
return 1;
|
||||
|
||||
error:
|
||||
openslES_DestroyEngine();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this callback handler is called every time a buffer finishes recording */
|
||||
static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *)context;
|
||||
|
||||
LOGV("SLES: Recording Callback");
|
||||
SDL_PostSemaphore(audiodata->playsem);
|
||||
}
|
||||
|
||||
static void openslES_DestroyPCMRecorder(SDL_AudioDevice *_this)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = _this->hidden;
|
||||
SLresult result;
|
||||
|
||||
/* stop recording */
|
||||
if (recorderRecord != NULL) {
|
||||
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SetRecordState stopped: %d", result);
|
||||
}
|
||||
}
|
||||
|
||||
/* destroy audio recorder object, and invalidate all associated interfaces */
|
||||
if (recorderObject != NULL) {
|
||||
(*recorderObject)->Destroy(recorderObject);
|
||||
recorderObject = NULL;
|
||||
recorderRecord = NULL;
|
||||
recorderBufferQueue = NULL;
|
||||
}
|
||||
|
||||
if (audiodata->playsem) {
|
||||
SDL_DestroySemaphore(audiodata->playsem);
|
||||
audiodata->playsem = NULL;
|
||||
}
|
||||
|
||||
if (audiodata->mixbuff) {
|
||||
SDL_free(audiodata->mixbuff);
|
||||
}
|
||||
}
|
||||
|
||||
static int openslES_CreatePCMRecorder(SDL_AudioDevice *_this)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = _this->hidden;
|
||||
SLDataFormat_PCM format_pcm;
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
|
||||
SLDataSink audioSnk;
|
||||
SLDataLocator_IODevice loc_dev;
|
||||
SLDataSource audioSrc;
|
||||
const SLInterfaceID ids[1] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
|
||||
const SLboolean req[1] = { SL_BOOLEAN_TRUE };
|
||||
SLresult result;
|
||||
int i;
|
||||
|
||||
if (!Android_JNI_RequestPermission("android.permission.RECORD_AUDIO")) {
|
||||
LOGE("This app doesn't have RECORD_AUDIO permission");
|
||||
return SDL_SetError("This app doesn't have RECORD_AUDIO permission");
|
||||
}
|
||||
|
||||
/* Just go with signed 16-bit audio as it's the most compatible */
|
||||
_this->spec.format = SDL_AUDIO_S16SYS;
|
||||
_this->spec.channels = 1;
|
||||
/*_this->spec.freq = SL_SAMPLINGRATE_16 / 1000;*/
|
||||
|
||||
/* Update the fragment size as size in bytes */
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
LOGI("Try to open %u hz %u bit chan %u %s samples %u",
|
||||
_this->spec.freq, SDL_AUDIO_BITSIZE(_this->spec.format),
|
||||
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples);
|
||||
|
||||
/* configure audio source */
|
||||
loc_dev.locatorType = SL_DATALOCATOR_IODEVICE;
|
||||
loc_dev.deviceType = SL_IODEVICE_AUDIOINPUT;
|
||||
loc_dev.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
|
||||
loc_dev.device = NULL;
|
||||
audioSrc.pLocator = &loc_dev;
|
||||
audioSrc.pFormat = NULL;
|
||||
|
||||
/* configure audio sink */
|
||||
loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
|
||||
loc_bufq.numBuffers = NUM_BUFFERS;
|
||||
|
||||
format_pcm.formatType = SL_DATAFORMAT_PCM;
|
||||
format_pcm.numChannels = _this->spec.channels;
|
||||
format_pcm.samplesPerSec = _this->spec.freq * 1000; /* / kilo Hz to milli Hz */
|
||||
format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(_this->spec.format);
|
||||
format_pcm.containerSize = SDL_AUDIO_BITSIZE(_this->spec.format);
|
||||
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
|
||||
format_pcm.channelMask = SL_SPEAKER_FRONT_CENTER;
|
||||
|
||||
audioSnk.pLocator = &loc_bufq;
|
||||
audioSnk.pFormat = &format_pcm;
|
||||
|
||||
/* create audio recorder */
|
||||
/* (requires the RECORD_AUDIO permission) */
|
||||
result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audioSrc, &audioSnk, 1, ids, req);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("CreateAudioRecorder failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* realize the recorder */
|
||||
result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RealizeAudioPlayer failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* get the record interface */
|
||||
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SL_IID_RECORD interface get failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* get the buffer queue interface */
|
||||
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* register callback on the buffer queue */
|
||||
/* context is '(SDL_PrivateAudioData *)_this->hidden' */
|
||||
result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, bqRecorderCallback, _this->hidden);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RegisterCallback failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Create the audio buffer semaphore */
|
||||
audiodata->playsem = SDL_CreateSemaphore(0);
|
||||
if (!audiodata->playsem) {
|
||||
LOGE("cannot create Semaphore!");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Create the sound buffers */
|
||||
audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * _this->spec.size);
|
||||
if (audiodata->mixbuff == NULL) {
|
||||
LOGE("mixbuffer allocate - out of memory");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_BUFFERS; i++) {
|
||||
audiodata->pmixbuff[i] = audiodata->mixbuff + i * _this->spec.size;
|
||||
}
|
||||
|
||||
/* in case already recording, stop recording and clear buffer queue */
|
||||
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("Record set state failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* enqueue empty buffers to be filled by the recorder */
|
||||
for (i = 0; i < NUM_BUFFERS; i++) {
|
||||
result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[i], _this->spec.size);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("Record enqueue buffers failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
/* start recording */
|
||||
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("Record set state failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
return SDL_SetError("Open device failed!");
|
||||
}
|
||||
|
||||
/* this callback handler is called every time a buffer finishes playing */
|
||||
static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *)context;
|
||||
|
||||
LOGV("SLES: Playback Callback");
|
||||
SDL_PostSemaphore(audiodata->playsem);
|
||||
}
|
||||
|
||||
static void openslES_DestroyPCMPlayer(SDL_AudioDevice *_this)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = _this->hidden;
|
||||
SLresult result;
|
||||
|
||||
/* set the player's state to 'stopped' */
|
||||
if (bqPlayerPlay != NULL) {
|
||||
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SetPlayState stopped failed: %d", result);
|
||||
}
|
||||
}
|
||||
|
||||
/* destroy buffer queue audio player object, and invalidate all associated interfaces */
|
||||
if (bqPlayerObject != NULL) {
|
||||
(*bqPlayerObject)->Destroy(bqPlayerObject);
|
||||
|
||||
bqPlayerObject = NULL;
|
||||
bqPlayerPlay = NULL;
|
||||
bqPlayerBufferQueue = NULL;
|
||||
}
|
||||
|
||||
if (audiodata->playsem) {
|
||||
SDL_DestroySemaphore(audiodata->playsem);
|
||||
audiodata->playsem = NULL;
|
||||
}
|
||||
|
||||
if (audiodata->mixbuff) {
|
||||
SDL_free(audiodata->mixbuff);
|
||||
}
|
||||
}
|
||||
|
||||
static int openslES_CreatePCMPlayer(SDL_AudioDevice *_this)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = _this->hidden;
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
|
||||
SLDataFormat_PCM format_pcm;
|
||||
SLAndroidDataFormat_PCM_EX format_pcm_ex;
|
||||
SLDataSource audioSrc;
|
||||
SLDataSink audioSnk;
|
||||
SLDataLocator_OutputMix loc_outmix;
|
||||
const SLInterfaceID ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME };
|
||||
const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE };
|
||||
SLresult result;
|
||||
int i;
|
||||
|
||||
/* If we want to add floating point audio support (requires API level 21)
|
||||
it can be done as described here:
|
||||
https://developer.android.com/ndk/guides/audio/opensl/android-extensions.html#floating-point
|
||||
*/
|
||||
if (SDL_GetAndroidSDKVersion() >= 21) {
|
||||
const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(_this->spec.format);
|
||||
SDL_AudioFormat test_format;
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
if (SDL_AUDIO_ISSIGNED(test_format)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
/* Didn't find a compatible format : */
|
||||
LOGI("No compatible audio format, using signed 16-bit audio");
|
||||
test_format = SDL_AUDIO_S16SYS;
|
||||
}
|
||||
_this->spec.format = test_format;
|
||||
} else {
|
||||
/* Just go with signed 16-bit audio as it's the most compatible */
|
||||
_this->spec.format = SDL_AUDIO_S16SYS;
|
||||
}
|
||||
|
||||
/* Update the fragment size as size in bytes */
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
LOGI("Try to open %u hz %s %u bit chan %u %s samples %u",
|
||||
_this->spec.freq, SDL_AUDIO_ISFLOAT(_this->spec.format) ? "float" : "pcm", SDL_AUDIO_BITSIZE(_this->spec.format),
|
||||
_this->spec.channels, (_this->spec.format & 0x1000) ? "BE" : "LE", _this->spec.samples);
|
||||
|
||||
/* configure audio source */
|
||||
loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
|
||||
loc_bufq.numBuffers = NUM_BUFFERS;
|
||||
|
||||
format_pcm.formatType = SL_DATAFORMAT_PCM;
|
||||
format_pcm.numChannels = _this->spec.channels;
|
||||
format_pcm.samplesPerSec = _this->spec.freq * 1000; /* / kilo Hz to milli Hz */
|
||||
format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(_this->spec.format);
|
||||
format_pcm.containerSize = SDL_AUDIO_BITSIZE(_this->spec.format);
|
||||
|
||||
if (SDL_AUDIO_ISBIGENDIAN(_this->spec.format)) {
|
||||
format_pcm.endianness = SL_BYTEORDER_BIGENDIAN;
|
||||
} else {
|
||||
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
|
||||
}
|
||||
|
||||
switch (_this->spec.channels) {
|
||||
case 1:
|
||||
format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT;
|
||||
break;
|
||||
case 2:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_STEREO;
|
||||
break;
|
||||
case 3:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_STEREO | SL_SPEAKER_FRONT_CENTER;
|
||||
break;
|
||||
case 4:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_QUAD;
|
||||
break;
|
||||
case 5:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_QUAD | SL_SPEAKER_FRONT_CENTER;
|
||||
break;
|
||||
case 6:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_5DOT1;
|
||||
break;
|
||||
case 7:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_BACK_CENTER;
|
||||
break;
|
||||
case 8:
|
||||
format_pcm.channelMask = SL_ANDROID_SPEAKER_7DOT1;
|
||||
break;
|
||||
default:
|
||||
/* Unknown number of channels, fall back to stereo */
|
||||
_this->spec.channels = 2;
|
||||
format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (SDL_AUDIO_ISFLOAT(_this->spec.format)) {
|
||||
/* Copy all setup into PCM EX structure */
|
||||
format_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
|
||||
format_pcm_ex.endianness = format_pcm.endianness;
|
||||
format_pcm_ex.channelMask = format_pcm.channelMask;
|
||||
format_pcm_ex.numChannels = format_pcm.numChannels;
|
||||
format_pcm_ex.sampleRate = format_pcm.samplesPerSec;
|
||||
format_pcm_ex.bitsPerSample = format_pcm.bitsPerSample;
|
||||
format_pcm_ex.containerSize = format_pcm.containerSize;
|
||||
format_pcm_ex.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
|
||||
}
|
||||
|
||||
audioSrc.pLocator = &loc_bufq;
|
||||
audioSrc.pFormat = SDL_AUDIO_ISFLOAT(_this->spec.format) ? (void *)&format_pcm_ex : (void *)&format_pcm;
|
||||
|
||||
/* configure audio sink */
|
||||
loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
|
||||
loc_outmix.outputMix = outputMixObject;
|
||||
audioSnk.pLocator = &loc_outmix;
|
||||
audioSnk.pFormat = NULL;
|
||||
|
||||
/* create audio player */
|
||||
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("CreateAudioPlayer failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* realize the player */
|
||||
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RealizeAudioPlayer failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* get the play interface */
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SL_IID_PLAY interface get failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* get the buffer queue interface */
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bqPlayerBufferQueue);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* register callback on the buffer queue */
|
||||
/* context is '(SDL_PrivateAudioData *)_this->hidden' */
|
||||
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, _this->hidden);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("RegisterCallback failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* get the volume interface */
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("SL_IID_VOLUME interface get failed: %d", result);
|
||||
/* goto failed; */
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Create the audio buffer semaphore */
|
||||
audiodata->playsem = SDL_CreateSemaphore(NUM_BUFFERS - 1);
|
||||
if (!audiodata->playsem) {
|
||||
LOGE("cannot create Semaphore!");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Create the sound buffers */
|
||||
audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * _this->spec.size);
|
||||
if (audiodata->mixbuff == NULL) {
|
||||
LOGE("mixbuffer allocate - out of memory");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_BUFFERS; i++) {
|
||||
audiodata->pmixbuff[i] = audiodata->mixbuff + i * _this->spec.size;
|
||||
}
|
||||
|
||||
/* set the player's state to playing */
|
||||
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("Play set state failed: %d", result);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int openslES_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
{
|
||||
_this->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*_this->hidden));
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
if (_this->iscapture) {
|
||||
LOGI("openslES_OpenDevice() %s for capture", devname);
|
||||
return openslES_CreatePCMRecorder(_this);
|
||||
} else {
|
||||
int ret;
|
||||
LOGI("openslES_OpenDevice() %s for playing", devname);
|
||||
ret = openslES_CreatePCMPlayer(_this);
|
||||
if (ret < 0) {
|
||||
/* Another attempt to open the device with a lower frequency */
|
||||
if (_this->spec.freq > 48000) {
|
||||
openslES_DestroyPCMPlayer(_this);
|
||||
_this->spec.freq = 48000;
|
||||
ret = openslES_CreatePCMPlayer(_this);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return SDL_SetError("Open device failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void openslES_WaitDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = _this->hidden;
|
||||
|
||||
LOGV("openslES_WaitDevice()");
|
||||
|
||||
/* Wait for an audio chunk to finish */
|
||||
SDL_WaitSemaphore(audiodata->playsem);
|
||||
}
|
||||
|
||||
static void openslES_PlayDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = _this->hidden;
|
||||
SLresult result;
|
||||
|
||||
LOGV("======openslES_PlayDevice()======");
|
||||
|
||||
/* Queue it up */
|
||||
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], _this->spec.size);
|
||||
|
||||
audiodata->next_buffer++;
|
||||
if (audiodata->next_buffer >= NUM_BUFFERS) {
|
||||
audiodata->next_buffer = 0;
|
||||
}
|
||||
|
||||
/* If Enqueue fails, callback won't be called.
|
||||
* Post the semaphore, not to run out of buffer */
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
SDL_PostSemaphore(audiodata->playsem);
|
||||
}
|
||||
}
|
||||
|
||||
/*/ n playn sem */
|
||||
/* getbuf 0 - 1 */
|
||||
/* fill buff 0 - 1 */
|
||||
/* play 0 - 0 1 */
|
||||
/* wait 1 0 0 */
|
||||
/* getbuf 1 0 0 */
|
||||
/* fill buff 1 0 0 */
|
||||
/* play 0 0 0 */
|
||||
/* wait */
|
||||
/* */
|
||||
/* okay.. */
|
||||
|
||||
static Uint8 *openslES_GetDeviceBuf(SDL_AudioDevice *_this)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = _this->hidden;
|
||||
|
||||
LOGV("openslES_GetDeviceBuf()");
|
||||
return audiodata->pmixbuff[audiodata->next_buffer];
|
||||
}
|
||||
|
||||
static int openslES_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *audiodata = _this->hidden;
|
||||
SLresult result;
|
||||
|
||||
/* Wait for new recorded data */
|
||||
SDL_WaitSemaphore(audiodata->playsem);
|
||||
|
||||
/* Copy it to the output buffer */
|
||||
SDL_assert(buflen == _this->spec.size);
|
||||
SDL_memcpy(buffer, audiodata->pmixbuff[audiodata->next_buffer], _this->spec.size);
|
||||
|
||||
/* Re-enqueue the buffer */
|
||||
result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], _this->spec.size);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("Record enqueue buffers failed: %d", result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
audiodata->next_buffer++;
|
||||
if (audiodata->next_buffer >= NUM_BUFFERS) {
|
||||
audiodata->next_buffer = 0;
|
||||
}
|
||||
|
||||
return _this->spec.size;
|
||||
}
|
||||
|
||||
static void openslES_CloseDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
/* struct SDL_PrivateAudioData *audiodata = _this->hidden; */
|
||||
|
||||
if (_this->iscapture) {
|
||||
LOGI("openslES_CloseDevice() for capture");
|
||||
openslES_DestroyPCMRecorder(_this);
|
||||
} else {
|
||||
LOGI("openslES_CloseDevice() for playing");
|
||||
openslES_DestroyPCMPlayer(_this);
|
||||
}
|
||||
|
||||
SDL_free(_this->hidden);
|
||||
}
|
||||
|
||||
static SDL_bool openslES_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
LOGI("openslES_Init() called");
|
||||
|
||||
if (!openslES_CreateEngine()) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
LOGI("openslES_Init() - set pointers");
|
||||
|
||||
/* Set the function pointers */
|
||||
/* impl->DetectDevices = openslES_DetectDevices; */
|
||||
impl->OpenDevice = openslES_OpenDevice;
|
||||
impl->WaitDevice = openslES_WaitDevice;
|
||||
impl->PlayDevice = openslES_PlayDevice;
|
||||
impl->GetDeviceBuf = openslES_GetDeviceBuf;
|
||||
impl->CaptureFromDevice = openslES_CaptureFromDevice;
|
||||
impl->CloseDevice = openslES_CloseDevice;
|
||||
impl->Deinitialize = openslES_DestroyEngine;
|
||||
|
||||
/* and the capabilities */
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
|
||||
|
||||
LOGI("openslES_Init() - success");
|
||||
|
||||
/* this audio target is available. */
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
AudioBootStrap openslES_bootstrap = {
|
||||
"openslES", "opensl ES audio driver", openslES_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
void openslES_ResumeDevices(void)
|
||||
{
|
||||
if (bqPlayerPlay != NULL) {
|
||||
/* set the player's state to 'playing' */
|
||||
SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("openslES_ResumeDevices failed: %d", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void openslES_PauseDevices(void)
|
||||
{
|
||||
if (bqPlayerPlay != NULL) {
|
||||
/* set the player's state to 'paused' */
|
||||
SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PAUSED);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("openslES_PauseDevices failed: %d", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_OPENSLES */
|
38
external/sdl/SDL/src/audio/openslES/SDL_openslES.h
vendored
Normal file
38
external/sdl/SDL/src/audio/openslES/SDL_openslES.h
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_openslesaudio_h_
|
||||
#define SDL_openslesaudio_h_
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_OPENSLES
|
||||
|
||||
void openslES_ResumeDevices(void);
|
||||
void openslES_PauseDevices(void);
|
||||
|
||||
#else
|
||||
|
||||
static void openslES_ResumeDevices(void) {}
|
||||
static void openslES_PauseDevices(void) {}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* SDL_openslesaudio_h_ */
|
1382
external/sdl/SDL/src/audio/pipewire/SDL_pipewire.c
vendored
Normal file
1382
external/sdl/SDL/src/audio/pipewire/SDL_pipewire.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
42
external/sdl/SDL/src/audio/pipewire/SDL_pipewire.h
vendored
Normal file
42
external/sdl/SDL/src/audio/pipewire/SDL_pipewire.h
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_pipewire_h_
|
||||
#define SDL_pipewire_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
struct pw_thread_loop *loop;
|
||||
struct pw_stream *stream;
|
||||
struct pw_context *context;
|
||||
struct SDL_DataQueue *buffer;
|
||||
|
||||
size_t input_buffer_packet_size;
|
||||
Sint32 stride; /* Bytes-per-frame */
|
||||
int stream_init_status;
|
||||
};
|
||||
|
||||
#endif /* SDL_pipewire_h_ */
|
172
external/sdl/SDL/src/audio/ps2/SDL_ps2audio.c
vendored
Normal file
172
external/sdl/SDL/src/audio/ps2/SDL_ps2audio.c
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
/* Output audio to nowhere... */
|
||||
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_ps2audio.h"
|
||||
|
||||
#include <kernel.h>
|
||||
#include <audsrv.h>
|
||||
#include <ps2_audio_driver.h>
|
||||
|
||||
/* The tag name used by PS2 audio */
|
||||
#define PS2AUDIO_DRIVER_NAME "ps2"
|
||||
|
||||
static int PS2AUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
{
|
||||
int i, mixlen;
|
||||
struct audsrv_fmt_t format;
|
||||
|
||||
_this->hidden = (struct SDL_PrivateAudioData *)
|
||||
SDL_malloc(sizeof(*_this->hidden));
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(_this->hidden);
|
||||
|
||||
/* These are the native supported audio PS2 configs */
|
||||
switch (_this->spec.freq) {
|
||||
case 11025:
|
||||
case 12000:
|
||||
case 22050:
|
||||
case 24000:
|
||||
case 32000:
|
||||
case 44100:
|
||||
case 48000:
|
||||
_this->spec.freq = _this->spec.freq;
|
||||
break;
|
||||
default:
|
||||
_this->spec.freq = 48000;
|
||||
break;
|
||||
}
|
||||
|
||||
_this->spec.samples = 512;
|
||||
_this->spec.channels = _this->spec.channels == 1 ? 1 : 2;
|
||||
_this->spec.format = _this->spec.format == SDL_AUDIO_S8 ? SDL_AUDIO_S8 : SDL_AUDIO_S16;
|
||||
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
format.bits = _this->spec.format == SDL_AUDIO_S8 ? 8 : 16;
|
||||
format.freq = _this->spec.freq;
|
||||
format.channels = _this->spec.channels;
|
||||
|
||||
_this->hidden->channel = audsrv_set_format(&format);
|
||||
audsrv_set_volume(MAX_VOLUME);
|
||||
|
||||
if (_this->hidden->channel < 0) {
|
||||
SDL_aligned_free(_this->hidden->rawbuf);
|
||||
_this->hidden->rawbuf = NULL;
|
||||
return SDL_SetError("Couldn't reserve hardware channel");
|
||||
}
|
||||
|
||||
/* Update the fragment size as size in bytes. */
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
/* Allocate the mixing buffer. Its size and starting address must
|
||||
be a multiple of 64 bytes. Our sample count is already a multiple of
|
||||
64, so spec->size should be a multiple of 64 as well. */
|
||||
mixlen = _this->spec.size * NUM_BUFFERS;
|
||||
_this->hidden->rawbuf = (Uint8 *)SDL_aligned_alloc(64, mixlen);
|
||||
if (_this->hidden->rawbuf == NULL) {
|
||||
return SDL_SetError("Couldn't allocate mixing buffer");
|
||||
}
|
||||
|
||||
SDL_memset(_this->hidden->rawbuf, 0, mixlen);
|
||||
for (i = 0; i < NUM_BUFFERS; i++) {
|
||||
_this->hidden->mixbufs[i] = &_this->hidden->rawbuf[i * _this->spec.size];
|
||||
}
|
||||
|
||||
_this->hidden->next_buffer = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void PS2AUDIO_PlayDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
uint8_t *mixbuf = _this->hidden->mixbufs[_this->hidden->next_buffer];
|
||||
audsrv_play_audio((char *)mixbuf, _this->spec.size);
|
||||
|
||||
_this->hidden->next_buffer = (_this->hidden->next_buffer + 1) % NUM_BUFFERS;
|
||||
}
|
||||
|
||||
/* This function waits until it is possible to write a full sound buffer */
|
||||
static void PS2AUDIO_WaitDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
audsrv_wait_audio(_this->spec.size);
|
||||
}
|
||||
|
||||
static Uint8 *PS2AUDIO_GetDeviceBuf(SDL_AudioDevice *_this)
|
||||
{
|
||||
return _this->hidden->mixbufs[_this->hidden->next_buffer];
|
||||
}
|
||||
|
||||
static void PS2AUDIO_CloseDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
if (_this->hidden->channel >= 0) {
|
||||
audsrv_stop_audio();
|
||||
_this->hidden->channel = -1;
|
||||
}
|
||||
|
||||
if (_this->hidden->rawbuf != NULL) {
|
||||
SDL_aligned_free(_this->hidden->rawbuf);
|
||||
_this->hidden->rawbuf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void PS2AUDIO_ThreadInit(SDL_AudioDevice *_this)
|
||||
{
|
||||
/* Increase the priority of this audio thread by 1 to put it
|
||||
ahead of other SDL threads. */
|
||||
int32_t thid;
|
||||
ee_thread_status_t status;
|
||||
thid = GetThreadId();
|
||||
if (ReferThreadStatus(GetThreadId(), &status) == 0) {
|
||||
ChangeThreadPriority(thid, status.current_priority - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void PS2AUDIO_Deinitialize(void)
|
||||
{
|
||||
deinit_audio_driver();
|
||||
}
|
||||
|
||||
static SDL_bool PS2AUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (init_audio_driver() < 0) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->OpenDevice = PS2AUDIO_OpenDevice;
|
||||
impl->PlayDevice = PS2AUDIO_PlayDevice;
|
||||
impl->WaitDevice = PS2AUDIO_WaitDevice;
|
||||
impl->GetDeviceBuf = PS2AUDIO_GetDeviceBuf;
|
||||
impl->CloseDevice = PS2AUDIO_CloseDevice;
|
||||
impl->ThreadInit = PS2AUDIO_ThreadInit;
|
||||
impl->Deinitialize = PS2AUDIO_Deinitialize;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap PS2AUDIO_bootstrap = {
|
||||
"ps2", "PS2 audio driver", PS2AUDIO_Init, SDL_FALSE
|
||||
};
|
42
external/sdl/SDL/src/audio/ps2/SDL_ps2audio.h
vendored
Normal file
42
external/sdl/SDL/src/audio/ps2/SDL_ps2audio.h
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_ps2audio_h_
|
||||
#define SDL_ps2audio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
#define NUM_BUFFERS 2
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The hardware output channel. */
|
||||
int channel;
|
||||
/* The raw allocated mixing buffer. */
|
||||
Uint8 *rawbuf;
|
||||
/* Individual mixing buffers. */
|
||||
Uint8 *mixbufs[NUM_BUFFERS];
|
||||
/* Index of the next available mixing buffer. */
|
||||
int next_buffer;
|
||||
};
|
||||
|
||||
#endif /* SDL_ps2audio_h_ */
|
197
external/sdl/SDL/src/audio/psp/SDL_pspaudio.c
vendored
Normal file
197
external/sdl/SDL/src/audio/psp/SDL_pspaudio.c
vendored
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_AUDIO_DRIVER_PSP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../SDL_audiodev_c.h"
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_pspaudio.h"
|
||||
|
||||
#include <pspaudio.h>
|
||||
#include <pspthreadman.h>
|
||||
|
||||
/* The tag name used by PSP audio */
|
||||
#define PSPAUDIO_DRIVER_NAME "psp"
|
||||
|
||||
static inline SDL_bool isBasicAudioConfig(const SDL_AudioSpec *spec)
|
||||
{
|
||||
return spec->freq == 44100;
|
||||
}
|
||||
|
||||
static int PSPAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
{
|
||||
int format, mixlen, i;
|
||||
|
||||
_this->hidden = (struct SDL_PrivateAudioData *)
|
||||
SDL_malloc(sizeof(*_this->hidden));
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(_this->hidden);
|
||||
|
||||
/* device only natively supports S16LSB */
|
||||
_this->spec.format = SDL_AUDIO_S16LSB;
|
||||
|
||||
/* PSP has some limitations with the Audio. It fully supports 44.1KHz (Mono & Stereo),
|
||||
however with frequencies different than 44.1KHz, it just supports Stereo,
|
||||
so a resampler must be done for these scenarios */
|
||||
if (isBasicAudioConfig(&_this->spec)) {
|
||||
/* The sample count must be a multiple of 64. */
|
||||
_this->spec.samples = PSP_AUDIO_SAMPLE_ALIGN(_this->spec.samples);
|
||||
/* The number of channels (1 or 2). */
|
||||
_this->spec.channels = _this->spec.channels == 1 ? 1 : 2;
|
||||
format = _this->spec.channels == 1 ? PSP_AUDIO_FORMAT_MONO : PSP_AUDIO_FORMAT_STEREO;
|
||||
_this->hidden->channel = sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL, _this->spec.samples, format);
|
||||
} else {
|
||||
/* 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11050, 8000 */
|
||||
switch (_this->spec.freq) {
|
||||
case 8000:
|
||||
case 11025:
|
||||
case 12000:
|
||||
case 16000:
|
||||
case 22050:
|
||||
case 24000:
|
||||
case 32000:
|
||||
case 44100:
|
||||
case 48000:
|
||||
_this->spec.freq = _this->spec.freq;
|
||||
break;
|
||||
default:
|
||||
_this->spec.freq = 48000;
|
||||
break;
|
||||
}
|
||||
/* The number of samples to output in one output call (min 17, max 4111). */
|
||||
_this->spec.samples = _this->spec.samples < 17 ? 17 : (_this->spec.samples > 4111 ? 4111 : _this->spec.samples);
|
||||
_this->spec.channels = 2; /* we're forcing the hardware to stereo. */
|
||||
_this->hidden->channel = sceAudioSRCChReserve(_this->spec.samples, _this->spec.freq, 2);
|
||||
}
|
||||
|
||||
if (_this->hidden->channel < 0) {
|
||||
SDL_aligned_free(_this->hidden->rawbuf);
|
||||
_this->hidden->rawbuf = NULL;
|
||||
return SDL_SetError("Couldn't reserve hardware channel");
|
||||
}
|
||||
|
||||
/* Update the fragment size as size in bytes. */
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
/* Allocate the mixing buffer. Its size and starting address must
|
||||
be a multiple of 64 bytes. Our sample count is already a multiple of
|
||||
64, so spec->size should be a multiple of 64 as well. */
|
||||
mixlen = _this->spec.size * NUM_BUFFERS;
|
||||
_this->hidden->rawbuf = (Uint8 *)SDL_aligned_alloc(64, mixlen);
|
||||
if (_this->hidden->rawbuf == NULL) {
|
||||
return SDL_SetError("Couldn't allocate mixing buffer");
|
||||
}
|
||||
|
||||
SDL_memset(_this->hidden->rawbuf, 0, mixlen);
|
||||
for (i = 0; i < NUM_BUFFERS; i++) {
|
||||
_this->hidden->mixbufs[i] = &_this->hidden->rawbuf[i * _this->spec.size];
|
||||
}
|
||||
|
||||
_this->hidden->next_buffer = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void PSPAUDIO_PlayDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
Uint8 *mixbuf = _this->hidden->mixbufs[_this->hidden->next_buffer];
|
||||
if (!isBasicAudioConfig(&_this->spec)) {
|
||||
SDL_assert(_this->spec.channels == 2);
|
||||
sceAudioSRCOutputBlocking(PSP_AUDIO_VOLUME_MAX, mixbuf);
|
||||
} else {
|
||||
sceAudioOutputPannedBlocking(_this->hidden->channel, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, mixbuf);
|
||||
}
|
||||
|
||||
_this->hidden->next_buffer = (_this->hidden->next_buffer + 1) % NUM_BUFFERS;
|
||||
}
|
||||
|
||||
/* This function waits until it is possible to write a full sound buffer */
|
||||
static void PSPAUDIO_WaitDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
/* Because we block when sending audio, there's no need for this function to do anything. */
|
||||
}
|
||||
|
||||
static Uint8 *PSPAUDIO_GetDeviceBuf(SDL_AudioDevice *_this)
|
||||
{
|
||||
return _this->hidden->mixbufs[_this->hidden->next_buffer];
|
||||
}
|
||||
|
||||
static void PSPAUDIO_CloseDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
if (_this->hidden->channel >= 0) {
|
||||
if (!isBasicAudioConfig(&_this->spec)) {
|
||||
sceAudioSRCChRelease();
|
||||
} else {
|
||||
sceAudioChRelease(_this->hidden->channel);
|
||||
}
|
||||
_this->hidden->channel = -1;
|
||||
}
|
||||
|
||||
if (_this->hidden->rawbuf != NULL) {
|
||||
SDL_aligned_free(_this->hidden->rawbuf);
|
||||
_this->hidden->rawbuf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void PSPAUDIO_ThreadInit(SDL_AudioDevice *_this)
|
||||
{
|
||||
/* Increase the priority of this audio thread by 1 to put it
|
||||
ahead of other SDL threads. */
|
||||
SceUID thid;
|
||||
SceKernelThreadInfo status;
|
||||
thid = sceKernelGetThreadId();
|
||||
status.size = sizeof(SceKernelThreadInfo);
|
||||
if (sceKernelReferThreadStatus(thid, &status) == 0) {
|
||||
sceKernelChangeThreadPriority(thid, status.currentPriority - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_bool PSPAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
/* Set the function pointers */
|
||||
impl->OpenDevice = PSPAUDIO_OpenDevice;
|
||||
impl->PlayDevice = PSPAUDIO_PlayDevice;
|
||||
impl->WaitDevice = PSPAUDIO_WaitDevice;
|
||||
impl->GetDeviceBuf = PSPAUDIO_GetDeviceBuf;
|
||||
impl->CloseDevice = PSPAUDIO_CloseDevice;
|
||||
impl->ThreadInit = PSPAUDIO_ThreadInit;
|
||||
|
||||
/* PSP audio device */
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
/*
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
|
||||
*/
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap PSPAUDIO_bootstrap = {
|
||||
"psp", "PSP audio driver", PSPAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_PSP */
|
41
external/sdl/SDL/src/audio/psp/SDL_pspaudio.h
vendored
Normal file
41
external/sdl/SDL/src/audio/psp/SDL_pspaudio.h
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_pspaudio_h_
|
||||
#define SDL_pspaudio_h_
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
#define NUM_BUFFERS 2
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The hardware output channel. */
|
||||
int channel;
|
||||
/* The raw allocated mixing buffer. */
|
||||
Uint8 *rawbuf;
|
||||
/* Individual mixing buffers. */
|
||||
Uint8 *mixbufs[NUM_BUFFERS];
|
||||
/* Index of the next available mixing buffer. */
|
||||
int next_buffer;
|
||||
};
|
||||
|
||||
#endif /* SDL_pspaudio_h_ */
|
985
external/sdl/SDL/src/audio/pulseaudio/SDL_pulseaudio.c
vendored
Normal file
985
external/sdl/SDL/src/audio/pulseaudio/SDL_pulseaudio.c
vendored
Normal file
@ -0,0 +1,985 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_AUDIO_DRIVER_PULSEAUDIO
|
||||
|
||||
/* Allow access to a raw mixing buffer */
|
||||
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
#include <signal.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_pulseaudio.h"
|
||||
#include "../../thread/SDL_systhread.h"
|
||||
|
||||
/* should we include monitors in the device list? Set at SDL_Init time */
|
||||
static SDL_bool include_monitors = SDL_FALSE;
|
||||
|
||||
static pa_threaded_mainloop *pulseaudio_threaded_mainloop = NULL;
|
||||
static pa_context *pulseaudio_context = NULL;
|
||||
static SDL_Thread *pulseaudio_hotplug_thread = NULL;
|
||||
static SDL_AtomicInt pulseaudio_hotplug_thread_active;
|
||||
|
||||
/* These are the OS identifiers (i.e. ALSA strings)... */
|
||||
static char *default_sink_path = NULL;
|
||||
static char *default_source_path = NULL;
|
||||
/* ... and these are the descriptions we use in GetDefaultAudioInfo. */
|
||||
static char *default_sink_name = NULL;
|
||||
static char *default_source_name = NULL;
|
||||
|
||||
|
||||
static const char *(*PULSEAUDIO_pa_get_library_version)(void);
|
||||
static pa_channel_map *(*PULSEAUDIO_pa_channel_map_init_auto)(
|
||||
pa_channel_map *, unsigned, pa_channel_map_def_t);
|
||||
static const char *(*PULSEAUDIO_pa_strerror)(int);
|
||||
|
||||
static pa_threaded_mainloop *(*PULSEAUDIO_pa_threaded_mainloop_new)(void);
|
||||
static void (*PULSEAUDIO_pa_threaded_mainloop_set_name)(pa_threaded_mainloop *, const char *);
|
||||
static pa_mainloop_api *(*PULSEAUDIO_pa_threaded_mainloop_get_api)(pa_threaded_mainloop *);
|
||||
static int (*PULSEAUDIO_pa_threaded_mainloop_start)(pa_threaded_mainloop *);
|
||||
static void (*PULSEAUDIO_pa_threaded_mainloop_stop)(pa_threaded_mainloop *);
|
||||
static void (*PULSEAUDIO_pa_threaded_mainloop_lock)(pa_threaded_mainloop *);
|
||||
static void (*PULSEAUDIO_pa_threaded_mainloop_unlock)(pa_threaded_mainloop *);
|
||||
static void (*PULSEAUDIO_pa_threaded_mainloop_wait)(pa_threaded_mainloop *);
|
||||
static void (*PULSEAUDIO_pa_threaded_mainloop_signal)(pa_threaded_mainloop *, int);
|
||||
static void (*PULSEAUDIO_pa_threaded_mainloop_free)(pa_threaded_mainloop *);
|
||||
|
||||
static pa_operation_state_t (*PULSEAUDIO_pa_operation_get_state)(
|
||||
const pa_operation *);
|
||||
static void (*PULSEAUDIO_pa_operation_set_state_callback)(pa_operation *, pa_operation_notify_cb_t, void *);
|
||||
static void (*PULSEAUDIO_pa_operation_cancel)(pa_operation *);
|
||||
static void (*PULSEAUDIO_pa_operation_unref)(pa_operation *);
|
||||
|
||||
static pa_context *(*PULSEAUDIO_pa_context_new)(pa_mainloop_api *,
|
||||
const char *);
|
||||
static void (*PULSEAUDIO_pa_context_set_state_callback)(pa_context *, pa_context_notify_cb_t, void *);
|
||||
static int (*PULSEAUDIO_pa_context_connect)(pa_context *, const char *,
|
||||
pa_context_flags_t, const pa_spawn_api *);
|
||||
static pa_operation *(*PULSEAUDIO_pa_context_get_sink_info_list)(pa_context *, pa_sink_info_cb_t, void *);
|
||||
static pa_operation *(*PULSEAUDIO_pa_context_get_source_info_list)(pa_context *, pa_source_info_cb_t, void *);
|
||||
static pa_operation *(*PULSEAUDIO_pa_context_get_sink_info_by_index)(pa_context *, uint32_t, pa_sink_info_cb_t, void *);
|
||||
static pa_operation *(*PULSEAUDIO_pa_context_get_source_info_by_index)(pa_context *, uint32_t, pa_source_info_cb_t, void *);
|
||||
static pa_context_state_t (*PULSEAUDIO_pa_context_get_state)(const pa_context *);
|
||||
static pa_operation *(*PULSEAUDIO_pa_context_subscribe)(pa_context *, pa_subscription_mask_t, pa_context_success_cb_t, void *);
|
||||
static void (*PULSEAUDIO_pa_context_set_subscribe_callback)(pa_context *, pa_context_subscribe_cb_t, void *);
|
||||
static void (*PULSEAUDIO_pa_context_disconnect)(pa_context *);
|
||||
static void (*PULSEAUDIO_pa_context_unref)(pa_context *);
|
||||
|
||||
static pa_stream *(*PULSEAUDIO_pa_stream_new)(pa_context *, const char *,
|
||||
const pa_sample_spec *, const pa_channel_map *);
|
||||
static void (*PULSEAUDIO_pa_stream_set_state_callback)(pa_stream *, pa_stream_notify_cb_t, void *);
|
||||
static int (*PULSEAUDIO_pa_stream_connect_playback)(pa_stream *, const char *,
|
||||
const pa_buffer_attr *, pa_stream_flags_t, const pa_cvolume *, pa_stream *);
|
||||
static int (*PULSEAUDIO_pa_stream_connect_record)(pa_stream *, const char *,
|
||||
const pa_buffer_attr *, pa_stream_flags_t);
|
||||
static pa_stream_state_t (*PULSEAUDIO_pa_stream_get_state)(const pa_stream *);
|
||||
static size_t (*PULSEAUDIO_pa_stream_writable_size)(const pa_stream *);
|
||||
static size_t (*PULSEAUDIO_pa_stream_readable_size)(const pa_stream *);
|
||||
static int (*PULSEAUDIO_pa_stream_write)(pa_stream *, const void *, size_t,
|
||||
pa_free_cb_t, int64_t, pa_seek_mode_t);
|
||||
static pa_operation *(*PULSEAUDIO_pa_stream_drain)(pa_stream *,
|
||||
pa_stream_success_cb_t, void *);
|
||||
static int (*PULSEAUDIO_pa_stream_peek)(pa_stream *, const void **, size_t *);
|
||||
static int (*PULSEAUDIO_pa_stream_drop)(pa_stream *);
|
||||
static pa_operation *(*PULSEAUDIO_pa_stream_flush)(pa_stream *,
|
||||
pa_stream_success_cb_t, void *);
|
||||
static int (*PULSEAUDIO_pa_stream_disconnect)(pa_stream *);
|
||||
static void (*PULSEAUDIO_pa_stream_unref)(pa_stream *);
|
||||
static void (*PULSEAUDIO_pa_stream_set_write_callback)(pa_stream *, pa_stream_request_cb_t, void *);
|
||||
static void (*PULSEAUDIO_pa_stream_set_read_callback)(pa_stream *, pa_stream_request_cb_t, void *);
|
||||
static pa_operation *(*PULSEAUDIO_pa_context_get_server_info)(pa_context *, pa_server_info_cb_t, void *);
|
||||
|
||||
static int load_pulseaudio_syms(void);
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC
|
||||
|
||||
static const char *pulseaudio_library = SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC;
|
||||
static void *pulseaudio_handle = NULL;
|
||||
|
||||
static int load_pulseaudio_sym(const char *fn, void **addr)
|
||||
{
|
||||
*addr = SDL_LoadFunction(pulseaudio_handle, fn);
|
||||
if (*addr == NULL) {
|
||||
/* Don't call SDL_SetError(): SDL_LoadFunction already did. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* cast funcs to char* first, to please GCC's strict aliasing rules. */
|
||||
#define SDL_PULSEAUDIO_SYM(x) \
|
||||
if (!load_pulseaudio_sym(#x, (void **)(char *)&PULSEAUDIO_##x)) \
|
||||
return -1
|
||||
|
||||
static void UnloadPulseAudioLibrary(void)
|
||||
{
|
||||
if (pulseaudio_handle != NULL) {
|
||||
SDL_UnloadObject(pulseaudio_handle);
|
||||
pulseaudio_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int LoadPulseAudioLibrary(void)
|
||||
{
|
||||
int retval = 0;
|
||||
if (pulseaudio_handle == NULL) {
|
||||
pulseaudio_handle = SDL_LoadObject(pulseaudio_library);
|
||||
if (pulseaudio_handle == NULL) {
|
||||
retval = -1;
|
||||
/* Don't call SDL_SetError(): SDL_LoadObject already did. */
|
||||
} else {
|
||||
retval = load_pulseaudio_syms();
|
||||
if (retval < 0) {
|
||||
UnloadPulseAudioLibrary();
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define SDL_PULSEAUDIO_SYM(x) PULSEAUDIO_##x = x
|
||||
|
||||
static void UnloadPulseAudioLibrary(void)
|
||||
{
|
||||
}
|
||||
|
||||
static int LoadPulseAudioLibrary(void)
|
||||
{
|
||||
load_pulseaudio_syms();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC */
|
||||
|
||||
static int load_pulseaudio_syms(void)
|
||||
{
|
||||
SDL_PULSEAUDIO_SYM(pa_get_library_version);
|
||||
SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_new);
|
||||
SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_get_api);
|
||||
SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_start);
|
||||
SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_stop);
|
||||
SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_lock);
|
||||
SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_unlock);
|
||||
SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_wait);
|
||||
SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_signal);
|
||||
SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_free);
|
||||
SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_set_name);
|
||||
SDL_PULSEAUDIO_SYM(pa_operation_get_state);
|
||||
SDL_PULSEAUDIO_SYM(pa_operation_cancel);
|
||||
SDL_PULSEAUDIO_SYM(pa_operation_set_state_callback);
|
||||
SDL_PULSEAUDIO_SYM(pa_operation_unref);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_new);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_set_state_callback);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_connect);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_get_sink_info_list);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_get_source_info_list);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_get_sink_info_by_index);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_get_source_info_by_index);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_get_state);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_subscribe);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_set_subscribe_callback);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_disconnect);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_unref);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_new);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_set_state_callback);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_connect_playback);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_connect_record);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_get_state);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_writable_size);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_readable_size);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_write);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_drain);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_disconnect);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_peek);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_drop);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_flush);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_unref);
|
||||
SDL_PULSEAUDIO_SYM(pa_channel_map_init_auto);
|
||||
SDL_PULSEAUDIO_SYM(pa_strerror);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_set_write_callback);
|
||||
SDL_PULSEAUDIO_SYM(pa_stream_set_read_callback);
|
||||
SDL_PULSEAUDIO_SYM(pa_context_get_server_info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SDL_INLINE int squashVersion(const int major, const int minor, const int patch)
|
||||
{
|
||||
return ((major & 0xFF) << 16) | ((minor & 0xFF) << 8) | (patch & 0xFF);
|
||||
}
|
||||
|
||||
/* Workaround for older pulse: pa_context_new() must have non-NULL appname */
|
||||
static const char *getAppName(void)
|
||||
{
|
||||
const char *retval = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME);
|
||||
if (retval && *retval) {
|
||||
return retval;
|
||||
}
|
||||
retval = SDL_GetHint(SDL_HINT_APP_NAME);
|
||||
if (retval && *retval) {
|
||||
return retval;
|
||||
} else {
|
||||
const char *verstr = PULSEAUDIO_pa_get_library_version();
|
||||
retval = "SDL Application"; /* the "oh well" default. */
|
||||
if (verstr != NULL) {
|
||||
int maj, min, patch;
|
||||
if (SDL_sscanf(verstr, "%d.%d.%d", &maj, &min, &patch) == 3) {
|
||||
if (squashVersion(maj, min, patch) >= squashVersion(0, 9, 15)) {
|
||||
retval = NULL; /* 0.9.15+ handles NULL correctly. */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void OperationStateChangeCallback(pa_operation *o, void *userdata)
|
||||
{
|
||||
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); // just signal any waiting code, it can look up the details.
|
||||
}
|
||||
|
||||
/* This function assume you are holding `mainloop`'s lock. The operation is unref'd in here, assuming
|
||||
you did the work in the callback and just want to know it's done, though. */
|
||||
static void WaitForPulseOperation(pa_operation *o)
|
||||
{
|
||||
/* This checks for NO errors currently. Either fix that, check results elsewhere, or do things you don't care about. */
|
||||
SDL_assert(pulseaudio_threaded_mainloop != NULL);
|
||||
if (o) {
|
||||
PULSEAUDIO_pa_operation_set_state_callback(o, OperationStateChangeCallback, NULL);
|
||||
while (PULSEAUDIO_pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
|
||||
PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop); /* this releases the lock and blocks on an internal condition variable. */
|
||||
}
|
||||
PULSEAUDIO_pa_operation_unref(o);
|
||||
}
|
||||
}
|
||||
|
||||
static void DisconnectFromPulseServer(void)
|
||||
{
|
||||
if (pulseaudio_context) {
|
||||
PULSEAUDIO_pa_context_disconnect(pulseaudio_context);
|
||||
PULSEAUDIO_pa_context_unref(pulseaudio_context);
|
||||
pulseaudio_context = NULL;
|
||||
}
|
||||
if (pulseaudio_threaded_mainloop != NULL) {
|
||||
PULSEAUDIO_pa_threaded_mainloop_stop(pulseaudio_threaded_mainloop);
|
||||
PULSEAUDIO_pa_threaded_mainloop_free(pulseaudio_threaded_mainloop);
|
||||
pulseaudio_threaded_mainloop = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void PulseContextStateChangeCallback(pa_context *context, void *userdata)
|
||||
{
|
||||
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); /* just signal any waiting code, it can look up the details. */
|
||||
}
|
||||
|
||||
static int ConnectToPulseServer(void)
|
||||
{
|
||||
pa_mainloop_api *mainloop_api = NULL;
|
||||
int state = 0;
|
||||
|
||||
SDL_assert(pulseaudio_threaded_mainloop == NULL);
|
||||
SDL_assert(pulseaudio_context == NULL);
|
||||
|
||||
/* Set up a new main loop */
|
||||
if (!(pulseaudio_threaded_mainloop = PULSEAUDIO_pa_threaded_mainloop_new())) {
|
||||
return SDL_SetError("pa_threaded_mainloop_new() failed");
|
||||
}
|
||||
|
||||
PULSEAUDIO_pa_threaded_mainloop_set_name(pulseaudio_threaded_mainloop, "PulseMainloop");
|
||||
|
||||
if (PULSEAUDIO_pa_threaded_mainloop_start(pulseaudio_threaded_mainloop) < 0) {
|
||||
PULSEAUDIO_pa_threaded_mainloop_free(pulseaudio_threaded_mainloop);
|
||||
pulseaudio_threaded_mainloop = NULL;
|
||||
return SDL_SetError("pa_threaded_mainloop_start() failed");
|
||||
}
|
||||
|
||||
PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop);
|
||||
|
||||
mainloop_api = PULSEAUDIO_pa_threaded_mainloop_get_api(pulseaudio_threaded_mainloop);
|
||||
SDL_assert(mainloop_api); /* this never fails, right? */
|
||||
|
||||
pulseaudio_context = PULSEAUDIO_pa_context_new(mainloop_api, getAppName());
|
||||
if (pulseaudio_context == NULL) {
|
||||
SDL_SetError("pa_context_new() failed");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
PULSEAUDIO_pa_context_set_state_callback(pulseaudio_context, PulseContextStateChangeCallback, NULL);
|
||||
|
||||
/* Connect to the PulseAudio server */
|
||||
if (PULSEAUDIO_pa_context_connect(pulseaudio_context, NULL, 0, NULL) < 0) {
|
||||
SDL_SetError("Could not setup connection to PulseAudio");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
state = PULSEAUDIO_pa_context_get_state(pulseaudio_context);
|
||||
while (PA_CONTEXT_IS_GOOD(state) && (state != PA_CONTEXT_READY)) {
|
||||
PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop);
|
||||
state = PULSEAUDIO_pa_context_get_state(pulseaudio_context);
|
||||
}
|
||||
|
||||
if (state != PA_CONTEXT_READY) {
|
||||
return SDL_SetError("Could not connect to PulseAudio");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
|
||||
|
||||
return 0; /* connected and ready! */
|
||||
|
||||
failed:
|
||||
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
|
||||
DisconnectFromPulseServer();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* This function waits until it is possible to write a full sound buffer */
|
||||
static void PULSEAUDIO_WaitDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
/* this is a no-op; we wait in PULSEAUDIO_PlayDevice now. */
|
||||
}
|
||||
|
||||
static void WriteCallback(pa_stream *p, size_t nbytes, void *userdata)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = (struct SDL_PrivateAudioData *)userdata;
|
||||
/*printf("PULSEAUDIO WRITE CALLBACK! nbytes=%u\n", (unsigned int) nbytes);*/
|
||||
h->bytes_requested += nbytes;
|
||||
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);
|
||||
}
|
||||
|
||||
static void PULSEAUDIO_PlayDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = _this->hidden;
|
||||
int available = h->mixlen;
|
||||
int written = 0;
|
||||
int cpy;
|
||||
|
||||
/*printf("PULSEAUDIO PLAYDEVICE START! mixlen=%d\n", available);*/
|
||||
|
||||
PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop);
|
||||
|
||||
while (SDL_AtomicGet(&_this->enabled) && (available > 0)) {
|
||||
cpy = SDL_min(h->bytes_requested, available);
|
||||
if (cpy) {
|
||||
if (PULSEAUDIO_pa_stream_write(h->stream, h->mixbuf + written, cpy, NULL, 0LL, PA_SEEK_RELATIVE) < 0) {
|
||||
SDL_OpenedAudioDeviceDisconnected(_this);
|
||||
break;
|
||||
}
|
||||
/*printf("PULSEAUDIO FEED! nbytes=%u\n", (unsigned int) cpy);*/
|
||||
h->bytes_requested -= cpy;
|
||||
written += cpy;
|
||||
available -= cpy;
|
||||
}
|
||||
|
||||
if (available > 0) {
|
||||
/* let WriteCallback fire if necessary. */
|
||||
/*printf("PULSEAUDIO WAIT IN PLAYDEVICE!\n");*/
|
||||
PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop);
|
||||
|
||||
if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) {
|
||||
/*printf("PULSEAUDIO DEVICE FAILURE IN PLAYDEVICE!\n");*/
|
||||
SDL_OpenedAudioDeviceDisconnected(_this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
|
||||
/*printf("PULSEAUDIO PLAYDEVICE END! written=%d\n", written);*/
|
||||
}
|
||||
|
||||
static Uint8 *PULSEAUDIO_GetDeviceBuf(SDL_AudioDevice *_this)
|
||||
{
|
||||
return _this->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static void ReadCallback(pa_stream *p, size_t nbytes, void *userdata)
|
||||
{
|
||||
/*printf("PULSEAUDIO READ CALLBACK! nbytes=%u\n", (unsigned int) nbytes);*/
|
||||
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); /* the capture code queries what it needs, we just need to signal to end any wait */
|
||||
}
|
||||
|
||||
static int PULSEAUDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = _this->hidden;
|
||||
const void *data = NULL;
|
||||
size_t nbytes = 0;
|
||||
int retval = 0;
|
||||
|
||||
PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop);
|
||||
|
||||
while (SDL_AtomicGet(&_this->enabled)) {
|
||||
if (h->capturebuf != NULL) {
|
||||
const int cpy = SDL_min(buflen, h->capturelen);
|
||||
SDL_memcpy(buffer, h->capturebuf, cpy);
|
||||
/*printf("PULSEAUDIO: fed %d captured bytes\n", cpy);*/
|
||||
h->capturebuf += cpy;
|
||||
h->capturelen -= cpy;
|
||||
if (h->capturelen == 0) {
|
||||
h->capturebuf = NULL;
|
||||
PULSEAUDIO_pa_stream_drop(h->stream); /* done with this fragment. */
|
||||
}
|
||||
retval = cpy; /* new data, return it. */
|
||||
break;
|
||||
}
|
||||
|
||||
while (SDL_AtomicGet(&_this->enabled) && (PULSEAUDIO_pa_stream_readable_size(h->stream) == 0)) {
|
||||
PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop);
|
||||
if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) {
|
||||
/*printf("PULSEAUDIO DEVICE FAILURE IN CAPTUREFROMDEVICE!\n");*/
|
||||
SDL_OpenedAudioDeviceDisconnected(_this);
|
||||
retval = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((retval == -1) || !SDL_AtomicGet(&_this->enabled)) { /* in case this happened while we were blocking. */
|
||||
retval = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* a new fragment is available! */
|
||||
PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes);
|
||||
SDL_assert(nbytes > 0);
|
||||
/* If data == NULL, then the buffer had a hole, ignore that */
|
||||
if (data == NULL) {
|
||||
PULSEAUDIO_pa_stream_drop(h->stream); /* drop this fragment. */
|
||||
} else {
|
||||
/* store this fragment's data, start feeding it to SDL. */
|
||||
/*printf("PULSEAUDIO: captured %d new bytes\n", (int) nbytes);*/
|
||||
h->capturebuf = (const Uint8 *)data;
|
||||
h->capturelen = nbytes;
|
||||
}
|
||||
}
|
||||
|
||||
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void PULSEAUDIO_FlushCapture(SDL_AudioDevice *_this)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = _this->hidden;
|
||||
const void *data = NULL;
|
||||
size_t nbytes = 0;
|
||||
|
||||
PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop);
|
||||
|
||||
if (h->capturebuf != NULL) {
|
||||
PULSEAUDIO_pa_stream_drop(h->stream);
|
||||
h->capturebuf = NULL;
|
||||
h->capturelen = 0;
|
||||
}
|
||||
|
||||
while (SDL_AtomicGet(&_this->enabled) && (PULSEAUDIO_pa_stream_readable_size(h->stream) > 0)) {
|
||||
PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop);
|
||||
if ((PULSEAUDIO_pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY) || (PULSEAUDIO_pa_stream_get_state(h->stream) != PA_STREAM_READY)) {
|
||||
/*printf("PULSEAUDIO DEVICE FAILURE IN FLUSHCAPTURE!\n");*/
|
||||
SDL_OpenedAudioDeviceDisconnected(_this);
|
||||
break;
|
||||
}
|
||||
|
||||
if (PULSEAUDIO_pa_stream_readable_size(h->stream) > 0) {
|
||||
/* a new fragment is available! Just dump it. */
|
||||
PULSEAUDIO_pa_stream_peek(h->stream, &data, &nbytes);
|
||||
PULSEAUDIO_pa_stream_drop(h->stream); /* drop this fragment. */
|
||||
}
|
||||
}
|
||||
|
||||
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
|
||||
}
|
||||
|
||||
static void PULSEAUDIO_CloseDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop);
|
||||
|
||||
if (_this->hidden->stream) {
|
||||
if (_this->hidden->capturebuf != NULL) {
|
||||
PULSEAUDIO_pa_stream_drop(_this->hidden->stream);
|
||||
}
|
||||
PULSEAUDIO_pa_stream_disconnect(_this->hidden->stream);
|
||||
PULSEAUDIO_pa_stream_unref(_this->hidden->stream);
|
||||
}
|
||||
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
|
||||
|
||||
SDL_free(_this->hidden->mixbuf);
|
||||
SDL_free(_this->hidden->device_name);
|
||||
SDL_free(_this->hidden);
|
||||
}
|
||||
|
||||
static void SinkDeviceNameCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
|
||||
{
|
||||
if (i) {
|
||||
char **devname = (char **)data;
|
||||
*devname = SDL_strdup(i->name);
|
||||
}
|
||||
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);
|
||||
}
|
||||
|
||||
static void SourceDeviceNameCallback(pa_context *c, const pa_source_info *i, int is_last, void *data)
|
||||
{
|
||||
if (i) {
|
||||
char **devname = (char **)data;
|
||||
*devname = SDL_strdup(i->name);
|
||||
}
|
||||
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);
|
||||
}
|
||||
|
||||
static SDL_bool FindDeviceName(struct SDL_PrivateAudioData *h, const SDL_bool iscapture, void *handle)
|
||||
{
|
||||
const uint32_t idx = ((uint32_t)((intptr_t)handle)) - 1;
|
||||
|
||||
if (handle == NULL) { /* NULL == default device. */
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
if (iscapture) {
|
||||
WaitForPulseOperation(PULSEAUDIO_pa_context_get_source_info_by_index(pulseaudio_context, idx, SourceDeviceNameCallback, &h->device_name));
|
||||
} else {
|
||||
WaitForPulseOperation(PULSEAUDIO_pa_context_get_sink_info_by_index(pulseaudio_context, idx, SinkDeviceNameCallback, &h->device_name));
|
||||
}
|
||||
|
||||
return h->device_name != NULL;
|
||||
}
|
||||
|
||||
static void PulseStreamStateChangeCallback(pa_stream *stream, void *userdata)
|
||||
{
|
||||
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); /* just signal any waiting code, it can look up the details. */
|
||||
}
|
||||
|
||||
static int PULSEAUDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
{
|
||||
struct SDL_PrivateAudioData *h = NULL;
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts;
|
||||
pa_sample_spec paspec;
|
||||
pa_buffer_attr paattr;
|
||||
pa_channel_map pacmap;
|
||||
pa_stream_flags_t flags = 0;
|
||||
SDL_bool iscapture = _this->iscapture;
|
||||
int format = PA_SAMPLE_INVALID;
|
||||
int retval = 0;
|
||||
|
||||
SDL_assert(pulseaudio_threaded_mainloop != NULL);
|
||||
SDL_assert(pulseaudio_context != NULL);
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
h = _this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(*_this->hidden));
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(_this->hidden);
|
||||
|
||||
/* Try for a closest match on audio format */
|
||||
closefmts = SDL_ClosestAudioFormats(_this->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
|
||||
#endif
|
||||
switch (test_format) {
|
||||
case SDL_AUDIO_U8:
|
||||
format = PA_SAMPLE_U8;
|
||||
break;
|
||||
case SDL_AUDIO_S16LSB:
|
||||
format = PA_SAMPLE_S16LE;
|
||||
break;
|
||||
case SDL_AUDIO_S16MSB:
|
||||
format = PA_SAMPLE_S16BE;
|
||||
break;
|
||||
case SDL_AUDIO_S32LSB:
|
||||
format = PA_SAMPLE_S32LE;
|
||||
break;
|
||||
case SDL_AUDIO_S32MSB:
|
||||
format = PA_SAMPLE_S32BE;
|
||||
break;
|
||||
case SDL_AUDIO_F32LSB:
|
||||
format = PA_SAMPLE_FLOAT32LE;
|
||||
break;
|
||||
case SDL_AUDIO_F32MSB:
|
||||
format = PA_SAMPLE_FLOAT32BE;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!test_format) {
|
||||
return SDL_SetError("pulseaudio: Unsupported audio format");
|
||||
}
|
||||
_this->spec.format = test_format;
|
||||
paspec.format = format;
|
||||
|
||||
/* Calculate the final parameters for this audio specification */
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
/* Allocate mixing buffer */
|
||||
if (!iscapture) {
|
||||
h->mixlen = _this->spec.size;
|
||||
h->mixbuf = (Uint8 *)SDL_malloc(h->mixlen);
|
||||
if (h->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(h->mixbuf, _this->spec.silence, _this->spec.size);
|
||||
}
|
||||
|
||||
paspec.channels = _this->spec.channels;
|
||||
paspec.rate = _this->spec.freq;
|
||||
|
||||
/* Reduced prebuffering compared to the defaults. */
|
||||
paattr.fragsize = _this->spec.size;
|
||||
paattr.tlength = h->mixlen;
|
||||
paattr.prebuf = -1;
|
||||
paattr.maxlength = -1;
|
||||
paattr.minreq = -1;
|
||||
flags |= PA_STREAM_ADJUST_LATENCY;
|
||||
|
||||
PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop);
|
||||
|
||||
if (!FindDeviceName(h, iscapture, _this->handle)) {
|
||||
retval = SDL_SetError("Requested PulseAudio sink/source missing?");
|
||||
} else {
|
||||
const char *name = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME);
|
||||
/* The SDL ALSA output hints us that we use Windows' channel mapping */
|
||||
/* https://bugzilla.libsdl.org/show_bug.cgi?id=110 */
|
||||
PULSEAUDIO_pa_channel_map_init_auto(&pacmap, _this->spec.channels,
|
||||
PA_CHANNEL_MAP_WAVEEX);
|
||||
|
||||
h->stream = PULSEAUDIO_pa_stream_new(
|
||||
pulseaudio_context,
|
||||
(name && *name) ? name : "Audio Stream", /* stream description */
|
||||
&paspec, /* sample format spec */
|
||||
&pacmap /* channel map */
|
||||
);
|
||||
|
||||
if (h->stream == NULL) {
|
||||
retval = SDL_SetError("Could not set up PulseAudio stream");
|
||||
} else {
|
||||
int rc;
|
||||
|
||||
PULSEAUDIO_pa_stream_set_state_callback(h->stream, PulseStreamStateChangeCallback, NULL);
|
||||
|
||||
/* now that we have multi-device support, don't move a stream from
|
||||
a device that was unplugged to something else, unless we're default. */
|
||||
if (h->device_name != NULL) {
|
||||
flags |= PA_STREAM_DONT_MOVE;
|
||||
}
|
||||
|
||||
if (iscapture) {
|
||||
PULSEAUDIO_pa_stream_set_read_callback(h->stream, ReadCallback, h);
|
||||
rc = PULSEAUDIO_pa_stream_connect_record(h->stream, h->device_name, &paattr, flags);
|
||||
} else {
|
||||
PULSEAUDIO_pa_stream_set_write_callback(h->stream, WriteCallback, h);
|
||||
rc = PULSEAUDIO_pa_stream_connect_playback(h->stream, h->device_name, &paattr, flags, NULL, NULL);
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
retval = SDL_SetError("Could not connect PulseAudio stream");
|
||||
} else {
|
||||
int state = PULSEAUDIO_pa_stream_get_state(h->stream);
|
||||
while (PA_STREAM_IS_GOOD(state) && (state != PA_STREAM_READY)) {
|
||||
PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop);
|
||||
state = PULSEAUDIO_pa_stream_get_state(h->stream);
|
||||
}
|
||||
|
||||
if (!PA_STREAM_IS_GOOD(state)) {
|
||||
retval = SDL_SetError("Could not connect PulseAudio stream");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
|
||||
|
||||
/* We're (hopefully) ready to rock and roll. :-) */
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* device handles are device index + 1, cast to void*, so we never pass a NULL. */
|
||||
|
||||
static SDL_AudioFormat PulseFormatToSDLFormat(pa_sample_format_t format)
|
||||
{
|
||||
switch (format) {
|
||||
case PA_SAMPLE_U8:
|
||||
return SDL_AUDIO_U8;
|
||||
case PA_SAMPLE_S16LE:
|
||||
return SDL_AUDIO_S16LSB;
|
||||
case PA_SAMPLE_S16BE:
|
||||
return SDL_AUDIO_S16MSB;
|
||||
case PA_SAMPLE_S32LE:
|
||||
return SDL_AUDIO_S32LSB;
|
||||
case PA_SAMPLE_S32BE:
|
||||
return SDL_AUDIO_S32MSB;
|
||||
case PA_SAMPLE_FLOAT32LE:
|
||||
return SDL_AUDIO_F32LSB;
|
||||
case PA_SAMPLE_FLOAT32BE:
|
||||
return SDL_AUDIO_F32MSB;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* This is called when PulseAudio adds an output ("sink") device. */
|
||||
static void SinkInfoCallback(pa_context *c, const pa_sink_info *i, int is_last, void *data)
|
||||
{
|
||||
SDL_AudioSpec spec;
|
||||
SDL_bool add = (SDL_bool)((intptr_t)data);
|
||||
if (i) {
|
||||
spec.freq = i->sample_spec.rate;
|
||||
spec.channels = i->sample_spec.channels;
|
||||
spec.format = PulseFormatToSDLFormat(i->sample_spec.format);
|
||||
spec.silence = 0;
|
||||
spec.samples = 0;
|
||||
spec.size = 0;
|
||||
spec.callback = NULL;
|
||||
spec.userdata = NULL;
|
||||
|
||||
if (add) {
|
||||
SDL_AddAudioDevice(SDL_FALSE, i->description, &spec, (void *)((intptr_t)i->index + 1));
|
||||
}
|
||||
|
||||
if (default_sink_path != NULL && SDL_strcmp(i->name, default_sink_path) == 0) {
|
||||
if (default_sink_name != NULL) {
|
||||
SDL_free(default_sink_name);
|
||||
}
|
||||
default_sink_name = SDL_strdup(i->description);
|
||||
}
|
||||
}
|
||||
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);
|
||||
}
|
||||
|
||||
/* This is called when PulseAudio adds a capture ("source") device. */
|
||||
static void SourceInfoCallback(pa_context *c, const pa_source_info *i, int is_last, void *data)
|
||||
{
|
||||
SDL_AudioSpec spec;
|
||||
SDL_bool add = (SDL_bool)((intptr_t)data);
|
||||
if (i) {
|
||||
/* Maybe skip "monitor" sources. These are just output from other sinks. */
|
||||
if (include_monitors || (i->monitor_of_sink == PA_INVALID_INDEX)) {
|
||||
spec.freq = i->sample_spec.rate;
|
||||
spec.channels = i->sample_spec.channels;
|
||||
spec.format = PulseFormatToSDLFormat(i->sample_spec.format);
|
||||
spec.silence = 0;
|
||||
spec.samples = 0;
|
||||
spec.size = 0;
|
||||
spec.callback = NULL;
|
||||
spec.userdata = NULL;
|
||||
|
||||
if (add) {
|
||||
SDL_AddAudioDevice(SDL_TRUE, i->description, &spec, (void *)((intptr_t)i->index + 1));
|
||||
}
|
||||
|
||||
if (default_source_path != NULL && SDL_strcmp(i->name, default_source_path) == 0) {
|
||||
if (default_source_name != NULL) {
|
||||
SDL_free(default_source_name);
|
||||
}
|
||||
default_source_name = SDL_strdup(i->description);
|
||||
}
|
||||
}
|
||||
}
|
||||
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);
|
||||
}
|
||||
|
||||
static void ServerInfoCallback(pa_context *c, const pa_server_info *i, void *data)
|
||||
{
|
||||
SDL_free(default_sink_path);
|
||||
SDL_free(default_source_path);
|
||||
default_sink_path = SDL_strdup(i->default_sink_name);
|
||||
default_source_path = SDL_strdup(i->default_source_name);
|
||||
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);
|
||||
}
|
||||
|
||||
/* This is called when PulseAudio has a device connected/removed/changed. */
|
||||
static void HotplugCallback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *data)
|
||||
{
|
||||
const SDL_bool added = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW);
|
||||
const SDL_bool removed = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE);
|
||||
const SDL_bool changed = ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE);
|
||||
|
||||
if (added || removed || changed) { /* we only care about add/remove events. */
|
||||
const SDL_bool sink = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK);
|
||||
const SDL_bool source = ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
|
||||
|
||||
/* adds need sink details from the PulseAudio server. Another callback... */
|
||||
/* (just unref all these operations right away, because we aren't going to wait on them and their callbacks will handle any work, so they can free as soon as that happens.) */
|
||||
if ((added || changed) && sink) {
|
||||
if (changed) {
|
||||
PULSEAUDIO_pa_operation_unref(PULSEAUDIO_pa_context_get_server_info(pulseaudio_context, ServerInfoCallback, NULL));
|
||||
}
|
||||
PULSEAUDIO_pa_operation_unref(PULSEAUDIO_pa_context_get_sink_info_by_index(pulseaudio_context, idx, SinkInfoCallback, (void *)((intptr_t)added)));
|
||||
} else if ((added || changed) && source) {
|
||||
if (changed) {
|
||||
PULSEAUDIO_pa_operation_unref(PULSEAUDIO_pa_context_get_server_info(pulseaudio_context, ServerInfoCallback, NULL));
|
||||
}
|
||||
PULSEAUDIO_pa_operation_unref(PULSEAUDIO_pa_context_get_source_info_by_index(pulseaudio_context, idx, SourceInfoCallback, (void *)((intptr_t)added)));
|
||||
} else if (removed && (sink || source)) {
|
||||
/* removes we can handle just with the device index. */
|
||||
SDL_RemoveAudioDevice(source != 0, (void *)((intptr_t)idx + 1));
|
||||
}
|
||||
}
|
||||
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);
|
||||
}
|
||||
|
||||
/* this runs as a thread while the Pulse target is initialized to catch hotplug events. */
|
||||
static int SDLCALL HotplugThread(void *data)
|
||||
{
|
||||
pa_operation *op;
|
||||
|
||||
SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW);
|
||||
PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop);
|
||||
PULSEAUDIO_pa_context_set_subscribe_callback(pulseaudio_context, HotplugCallback, NULL);
|
||||
|
||||
/* don't WaitForPulseOperation on the subscription; when it's done we'll be able to get hotplug events, but waiting doesn't changing anything. */
|
||||
op = PULSEAUDIO_pa_context_subscribe(pulseaudio_context, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE, NULL, NULL);
|
||||
|
||||
SDL_PostSemaphore((SDL_Semaphore *) data);
|
||||
|
||||
while (SDL_AtomicGet(&pulseaudio_hotplug_thread_active)) {
|
||||
PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop);
|
||||
if (op && PULSEAUDIO_pa_operation_get_state(op) != PA_OPERATION_RUNNING) {
|
||||
PULSEAUDIO_pa_operation_unref(op);
|
||||
op = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (op) {
|
||||
PULSEAUDIO_pa_operation_unref(op);
|
||||
}
|
||||
|
||||
PULSEAUDIO_pa_context_set_subscribe_callback(pulseaudio_context, NULL, NULL);
|
||||
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void PULSEAUDIO_DetectDevices(void)
|
||||
{
|
||||
SDL_Semaphore *ready_sem = SDL_CreateSemaphore(0);
|
||||
|
||||
PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop);
|
||||
WaitForPulseOperation(PULSEAUDIO_pa_context_get_server_info(pulseaudio_context, ServerInfoCallback, NULL));
|
||||
WaitForPulseOperation(PULSEAUDIO_pa_context_get_sink_info_list(pulseaudio_context, SinkInfoCallback, (void *)((intptr_t)SDL_TRUE)));
|
||||
WaitForPulseOperation(PULSEAUDIO_pa_context_get_source_info_list(pulseaudio_context, SourceInfoCallback, (void *)((intptr_t)SDL_TRUE)));
|
||||
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
|
||||
|
||||
/* ok, we have a sane list, let's set up hotplug notifications now... */
|
||||
SDL_AtomicSet(&pulseaudio_hotplug_thread_active, 1);
|
||||
pulseaudio_hotplug_thread = SDL_CreateThreadInternal(HotplugThread, "PulseHotplug", 256 * 1024, ready_sem); /* !!! FIXME: this can probably survive in significantly less stack space. */
|
||||
SDL_WaitSemaphore(ready_sem);
|
||||
SDL_DestroySemaphore(ready_sem);
|
||||
}
|
||||
|
||||
static int PULSEAUDIO_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
|
||||
{
|
||||
int i;
|
||||
int numdevices;
|
||||
|
||||
char *target;
|
||||
if (iscapture) {
|
||||
if (default_source_name == NULL) {
|
||||
return SDL_SetError("PulseAudio could not find a default source");
|
||||
}
|
||||
target = default_source_name;
|
||||
} else {
|
||||
if (default_sink_name == NULL) {
|
||||
return SDL_SetError("PulseAudio could not find a default sink");
|
||||
}
|
||||
target = default_sink_name;
|
||||
}
|
||||
|
||||
numdevices = SDL_GetNumAudioDevices(iscapture);
|
||||
for (i = 0; i < numdevices; i += 1) {
|
||||
if (SDL_strcmp(SDL_GetAudioDeviceName(i, iscapture), target) == 0) {
|
||||
if (name != NULL) {
|
||||
*name = SDL_strdup(target);
|
||||
}
|
||||
SDL_GetAudioDeviceSpec(i, iscapture, spec);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return SDL_SetError("Could not find default PulseAudio device");
|
||||
}
|
||||
|
||||
static void PULSEAUDIO_Deinitialize(void)
|
||||
{
|
||||
if (pulseaudio_hotplug_thread) {
|
||||
PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop);
|
||||
SDL_AtomicSet(&pulseaudio_hotplug_thread_active, 0);
|
||||
PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0);
|
||||
PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop);
|
||||
SDL_WaitThread(pulseaudio_hotplug_thread, NULL);
|
||||
pulseaudio_hotplug_thread = NULL;
|
||||
}
|
||||
|
||||
DisconnectFromPulseServer();
|
||||
|
||||
SDL_free(default_sink_path);
|
||||
default_sink_path = NULL;
|
||||
SDL_free(default_source_path);
|
||||
default_source_path = NULL;
|
||||
SDL_free(default_sink_name);
|
||||
default_sink_name = NULL;
|
||||
SDL_free(default_source_name);
|
||||
default_source_name = NULL;
|
||||
|
||||
UnloadPulseAudioLibrary();
|
||||
}
|
||||
|
||||
static SDL_bool PULSEAUDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (LoadPulseAudioLibrary() < 0) {
|
||||
return SDL_FALSE;
|
||||
} else if (ConnectToPulseServer() < 0) {
|
||||
UnloadPulseAudioLibrary();
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
include_monitors = SDL_GetHintBoolean(SDL_HINT_AUDIO_INCLUDE_MONITORS, SDL_FALSE);
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->DetectDevices = PULSEAUDIO_DetectDevices;
|
||||
impl->OpenDevice = PULSEAUDIO_OpenDevice;
|
||||
impl->PlayDevice = PULSEAUDIO_PlayDevice;
|
||||
impl->WaitDevice = PULSEAUDIO_WaitDevice;
|
||||
impl->GetDeviceBuf = PULSEAUDIO_GetDeviceBuf;
|
||||
impl->CloseDevice = PULSEAUDIO_CloseDevice;
|
||||
impl->Deinitialize = PULSEAUDIO_Deinitialize;
|
||||
impl->CaptureFromDevice = PULSEAUDIO_CaptureFromDevice;
|
||||
impl->FlushCapture = PULSEAUDIO_FlushCapture;
|
||||
impl->GetDefaultAudioInfo = PULSEAUDIO_GetDefaultAudioInfo;
|
||||
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->SupportsNonPow2Samples = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap PULSEAUDIO_bootstrap = {
|
||||
"pulseaudio", "PulseAudio", PULSEAUDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_PULSEAUDIO */
|
47
external/sdl/SDL/src/audio/pulseaudio/SDL_pulseaudio.h
vendored
Normal file
47
external/sdl/SDL/src/audio/pulseaudio/SDL_pulseaudio.h
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_pulseaudio_h_
|
||||
#define SDL_pulseaudio_h_
|
||||
|
||||
#include <pulse/pulseaudio.h>
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
char *device_name;
|
||||
|
||||
/* pulseaudio structures */
|
||||
pa_stream *stream;
|
||||
|
||||
/* Raw mixing buffer */
|
||||
Uint8 *mixbuf;
|
||||
int mixlen;
|
||||
|
||||
int bytes_requested; /* bytes of data the hardware wants _now_. */
|
||||
|
||||
const Uint8 *capturebuf;
|
||||
int capturelen;
|
||||
};
|
||||
|
||||
#endif /* SDL_pulseaudio_h_ */
|
612
external/sdl/SDL/src/audio/qnx/SDL_qsa_audio.c
vendored
Normal file
612
external/sdl/SDL/src/audio/qnx/SDL_qsa_audio.c
vendored
Normal file
@ -0,0 +1,612 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2021 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* !!! FIXME: streamline this a little by removing all the
|
||||
* !!! FIXME: if (capture) {} else {} sections that are identical
|
||||
* !!! FIXME: except for one flag.
|
||||
*/
|
||||
|
||||
/* !!! FIXME: can this target support hotplugging? */
|
||||
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_QNX
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sched.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/neutrino.h>
|
||||
#include <sys/asoundlib.h>
|
||||
|
||||
#include "SDL3/SDL_timer.h"
|
||||
#include "SDL3/SDL_audio.h"
|
||||
#include "../../core/unix/SDL_poll.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_qsa_audio.h"
|
||||
|
||||
/* default channel communication parameters */
|
||||
#define DEFAULT_CPARAMS_RATE 44100
|
||||
#define DEFAULT_CPARAMS_VOICES 1
|
||||
|
||||
#define DEFAULT_CPARAMS_FRAG_SIZE 4096
|
||||
#define DEFAULT_CPARAMS_FRAGS_MIN 1
|
||||
#define DEFAULT_CPARAMS_FRAGS_MAX 1
|
||||
|
||||
/* List of found devices */
|
||||
#define QSA_MAX_DEVICES 32
|
||||
#define QSA_MAX_NAME_LENGTH 81+16 /* Hardcoded in QSA, can't be changed */
|
||||
|
||||
typedef struct _QSA_Device
|
||||
{
|
||||
char name[QSA_MAX_NAME_LENGTH]; /* Long audio device name for SDL */
|
||||
int cardno;
|
||||
int deviceno;
|
||||
} QSA_Device;
|
||||
|
||||
QSA_Device qsa_playback_device[QSA_MAX_DEVICES];
|
||||
uint32_t qsa_playback_devices;
|
||||
|
||||
QSA_Device qsa_capture_device[QSA_MAX_DEVICES];
|
||||
uint32_t qsa_capture_devices;
|
||||
|
||||
static int QSA_SetError(const char *fn, int status)
|
||||
{
|
||||
return SDL_SetError("QSA: %s() failed: %s", fn, snd_strerror(status));
|
||||
}
|
||||
|
||||
/* !!! FIXME: does this need to be here? Does the SDL version not work? */
|
||||
static void QSA_ThreadInit(SDL_AudioDevice *_this)
|
||||
{
|
||||
/* Increase default 10 priority to 25 to avoid jerky sound */
|
||||
struct sched_param param;
|
||||
if (SchedGet(0, 0, ¶m) != -1) {
|
||||
param.sched_priority = param.sched_curpriority + 15;
|
||||
SchedSet(0, 0, SCHED_NOCHANGE, ¶m);
|
||||
}
|
||||
}
|
||||
|
||||
/* PCM channel parameters initialize function */
|
||||
static void QSA_InitAudioParams(snd_pcm_channel_params_t * cpars)
|
||||
{
|
||||
SDL_zerop(cpars);
|
||||
cpars->channel = SND_PCM_CHANNEL_PLAYBACK;
|
||||
cpars->mode = SND_PCM_MODE_BLOCK;
|
||||
cpars->start_mode = SND_PCM_START_DATA;
|
||||
cpars->stop_mode = SND_PCM_STOP_STOP;
|
||||
cpars->format.format = SND_PCM_SFMT_S16_LE;
|
||||
cpars->format.interleave = 1;
|
||||
cpars->format.rate = DEFAULT_CPARAMS_RATE;
|
||||
cpars->format.voices = DEFAULT_CPARAMS_VOICES;
|
||||
cpars->buf.block.frag_size = DEFAULT_CPARAMS_FRAG_SIZE;
|
||||
cpars->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN;
|
||||
cpars->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX;
|
||||
}
|
||||
|
||||
/* This function waits until it is possible to write a full sound buffer */
|
||||
static void QSA_WaitDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
int result;
|
||||
|
||||
/* Setup timeout for playing one fragment equal to 2 seconds */
|
||||
/* If timeout occurred than something wrong with hardware or driver */
|
||||
/* For example, Vortex 8820 audio driver stucks on second DAC because */
|
||||
/* it doesn't exist ! */
|
||||
result = SDL_IOReady(_this->hidden->audio_fd,
|
||||
_this->hidden->iscapture ? SDL_IOR_READ : SDL_IOR_WRITE,
|
||||
2 * 1000);
|
||||
switch (result) {
|
||||
case -1:
|
||||
SDL_SetError("QSA: SDL_IOReady() failed: %s", strerror(errno));
|
||||
break;
|
||||
case 0:
|
||||
SDL_SetError("QSA: timeout on buffer waiting occurred");
|
||||
_this->hidden->timeout_on_wait = 1;
|
||||
break;
|
||||
default:
|
||||
_this->hidden->timeout_on_wait = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void QSA_PlayDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
snd_pcm_channel_status_t cstatus;
|
||||
int written;
|
||||
int status;
|
||||
int towrite;
|
||||
void *pcmbuffer;
|
||||
|
||||
if (!SDL_AtomicGet(&_this->enabled) || !_this->hidden) {
|
||||
return;
|
||||
}
|
||||
|
||||
towrite = _this->spec.size;
|
||||
pcmbuffer = _this->hidden->pcm_buf;
|
||||
|
||||
/* Write the audio data, checking for EAGAIN (buffer full) and underrun */
|
||||
do {
|
||||
written =
|
||||
snd_pcm_plugin_write(_this->hidden->audio_handle, pcmbuffer,
|
||||
towrite);
|
||||
if (written != towrite) {
|
||||
/* Check if samples playback got stuck somewhere in hardware or in */
|
||||
/* the audio device driver */
|
||||
if ((errno == EAGAIN) && (written == 0)) {
|
||||
if (_this->hidden->timeout_on_wait != 0) {
|
||||
SDL_SetError("QSA: buffer playback timeout");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for errors or conditions */
|
||||
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
|
||||
/* Let a little CPU time go by and try to write again */
|
||||
SDL_Delay(1);
|
||||
|
||||
/* if we wrote some data */
|
||||
towrite -= written;
|
||||
pcmbuffer += written * _this->spec.channels;
|
||||
continue;
|
||||
} else {
|
||||
if ((errno == EINVAL) || (errno == EIO)) {
|
||||
SDL_zero(cstatus);
|
||||
if (!_this->hidden->iscapture) {
|
||||
cstatus.channel = SND_PCM_CHANNEL_PLAYBACK;
|
||||
} else {
|
||||
cstatus.channel = SND_PCM_CHANNEL_CAPTURE;
|
||||
}
|
||||
|
||||
status =
|
||||
snd_pcm_plugin_status(_this->hidden->audio_handle,
|
||||
&cstatus);
|
||||
if (status < 0) {
|
||||
QSA_SetError("snd_pcm_plugin_status", status);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) ||
|
||||
(cstatus.status == SND_PCM_STATUS_READY)) {
|
||||
if (!_this->hidden->iscapture) {
|
||||
status =
|
||||
snd_pcm_plugin_prepare(_this->hidden->
|
||||
audio_handle,
|
||||
SND_PCM_CHANNEL_PLAYBACK);
|
||||
} else {
|
||||
status =
|
||||
snd_pcm_plugin_prepare(_this->hidden->
|
||||
audio_handle,
|
||||
SND_PCM_CHANNEL_CAPTURE);
|
||||
}
|
||||
if (status < 0) {
|
||||
QSA_SetError("snd_pcm_plugin_prepare", status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* we wrote all remaining data */
|
||||
towrite -= written;
|
||||
pcmbuffer += written * _this->spec.channels;
|
||||
}
|
||||
} while ((towrite > 0) && SDL_AtomicGet(&_this->enabled));
|
||||
|
||||
/* If we couldn't write, assume fatal error for now */
|
||||
if (towrite != 0) {
|
||||
SDL_OpenedAudioDeviceDisconnected(_this);
|
||||
}
|
||||
}
|
||||
|
||||
static Uint8 *QSA_GetDeviceBuf(SDL_AudioDevice *_this)
|
||||
{
|
||||
return _this->hidden->pcm_buf;
|
||||
}
|
||||
|
||||
static void QSA_CloseDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
if (_this->hidden->audio_handle != NULL) {
|
||||
#if _NTO_VERSION < 710
|
||||
if (!_this->hidden->iscapture) {
|
||||
/* Finish playing available samples */
|
||||
snd_pcm_plugin_flush(_this->hidden->audio_handle,
|
||||
SND_PCM_CHANNEL_PLAYBACK);
|
||||
} else {
|
||||
/* Cancel unread samples during capture */
|
||||
snd_pcm_plugin_flush(_this->hidden->audio_handle,
|
||||
SND_PCM_CHANNEL_CAPTURE);
|
||||
}
|
||||
#endif
|
||||
snd_pcm_close(_this->hidden->audio_handle);
|
||||
}
|
||||
|
||||
SDL_free(_this->hidden->pcm_buf);
|
||||
SDL_free(_this->hidden);
|
||||
}
|
||||
|
||||
static int QSA_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
{
|
||||
#if 0
|
||||
/* !!! FIXME: SDL2 used to pass this handle. What's the alternative? */
|
||||
const QSA_Device *device = (const QSA_Device *) handle;
|
||||
#else
|
||||
const QSA_Device *device = NULL;
|
||||
#endif
|
||||
int status = 0;
|
||||
int format = 0;
|
||||
SDL_AudioFormat test_format = 0;
|
||||
const SDL_AudioFormat *closefmts;
|
||||
snd_pcm_channel_setup_t csetup;
|
||||
snd_pcm_channel_params_t cparams;
|
||||
SDL_bool iscapture = _this->iscapture;
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
_this->hidden =
|
||||
(struct SDL_PrivateAudioData *) SDL_calloc(1,
|
||||
(sizeof
|
||||
(struct
|
||||
SDL_PrivateAudioData)));
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
/* Initialize channel transfer parameters to default */
|
||||
QSA_InitAudioParams(&cparams);
|
||||
|
||||
/* Initialize channel direction: capture or playback */
|
||||
_this->hidden->iscapture = iscapture ? SDL_TRUE : SDL_FALSE;
|
||||
|
||||
if (device != NULL) {
|
||||
/* Open requested audio device */
|
||||
_this->hidden->deviceno = device->deviceno;
|
||||
_this->hidden->cardno = device->cardno;
|
||||
status = snd_pcm_open(&_this->hidden->audio_handle,
|
||||
device->cardno, device->deviceno,
|
||||
iscapture ? SND_PCM_OPEN_CAPTURE : SND_PCM_OPEN_PLAYBACK);
|
||||
} else {
|
||||
/* Open system default audio device */
|
||||
status = snd_pcm_open_preferred(&_this->hidden->audio_handle,
|
||||
&_this->hidden->cardno,
|
||||
&_this->hidden->deviceno,
|
||||
iscapture ? SND_PCM_OPEN_CAPTURE : SND_PCM_OPEN_PLAYBACK);
|
||||
}
|
||||
|
||||
/* Check if requested device is opened */
|
||||
if (status < 0) {
|
||||
_this->hidden->audio_handle = NULL;
|
||||
return QSA_SetError("snd_pcm_open", status);
|
||||
}
|
||||
|
||||
/* Try for a closest match on audio format */
|
||||
closefmts = SDL_ClosestAudioFormats(_this->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
/* if match found set format to equivalent QSA format */
|
||||
switch (test_format) {
|
||||
#define CHECKFMT(sdlfmt, qsafmt) case SDL_AUDIO_##sdlfmt: format = SND_PCM_SFMT_##qsafmt; break
|
||||
CHECKFMT(U8, U8);
|
||||
CHECKFMT(S8, S8);
|
||||
CHECKFMT(S16LSB, S16_LE);
|
||||
CHECKFMT(S16MSB, S16_BE);
|
||||
CHECKFMT(S32LSB, S32_LE);
|
||||
CHECKFMT(S32MSB, S32_BE);
|
||||
CHECKFMT(F32LSB, FLOAT_LE);
|
||||
CHECKFMT(F32MSB, FLOAT_BE);
|
||||
#undef CHECKFMT
|
||||
default: continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* assumes test_format not 0 on success */
|
||||
if (test_format == 0) {
|
||||
return SDL_SetError("QSA: Couldn't find any hardware audio formats");
|
||||
}
|
||||
|
||||
_this->spec.format = test_format;
|
||||
|
||||
/* Set the audio format */
|
||||
cparams.format.format = format;
|
||||
|
||||
/* Set mono/stereo/4ch/6ch/8ch audio */
|
||||
cparams.format.voices = _this->spec.channels;
|
||||
|
||||
/* Set rate */
|
||||
cparams.format.rate = _this->spec.freq;
|
||||
|
||||
/* Setup the transfer parameters according to cparams */
|
||||
status = snd_pcm_plugin_params(_this->hidden->audio_handle, &cparams);
|
||||
if (status < 0) {
|
||||
return QSA_SetError("snd_pcm_plugin_params", status);
|
||||
}
|
||||
|
||||
/* Make sure channel is setup right one last time */
|
||||
SDL_zero(csetup);
|
||||
if (!_this->hidden->iscapture) {
|
||||
csetup.channel = SND_PCM_CHANNEL_PLAYBACK;
|
||||
} else {
|
||||
csetup.channel = SND_PCM_CHANNEL_CAPTURE;
|
||||
}
|
||||
|
||||
/* Setup an audio channel */
|
||||
if (snd_pcm_plugin_setup(_this->hidden->audio_handle, &csetup) < 0) {
|
||||
return SDL_SetError("QSA: Unable to setup channel");
|
||||
}
|
||||
|
||||
/* Calculate the final parameters for this audio specification */
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
_this->hidden->pcm_len = _this->spec.size;
|
||||
|
||||
if (_this->hidden->pcm_len == 0) {
|
||||
_this->hidden->pcm_len =
|
||||
csetup.buf.block.frag_size * _this->spec.channels *
|
||||
(snd_pcm_format_width(format) / 8);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate memory to the audio buffer and initialize with silence
|
||||
* (Note that buffer size must be a multiple of fragment size, so find
|
||||
* closest multiple)
|
||||
*/
|
||||
_this->hidden->pcm_buf =
|
||||
(Uint8 *) SDL_malloc(_this->hidden->pcm_len);
|
||||
if (_this->hidden->pcm_buf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(_this->hidden->pcm_buf, _this->spec.silence,
|
||||
_this->hidden->pcm_len);
|
||||
|
||||
/* get the file descriptor */
|
||||
if (!_this->hidden->iscapture) {
|
||||
_this->hidden->audio_fd =
|
||||
snd_pcm_file_descriptor(_this->hidden->audio_handle,
|
||||
SND_PCM_CHANNEL_PLAYBACK);
|
||||
} else {
|
||||
_this->hidden->audio_fd =
|
||||
snd_pcm_file_descriptor(_this->hidden->audio_handle,
|
||||
SND_PCM_CHANNEL_CAPTURE);
|
||||
}
|
||||
|
||||
if (_this->hidden->audio_fd < 0) {
|
||||
return QSA_SetError("snd_pcm_file_descriptor", status);
|
||||
}
|
||||
|
||||
/* Prepare an audio channel */
|
||||
if (!_this->hidden->iscapture) {
|
||||
/* Prepare audio playback */
|
||||
status =
|
||||
snd_pcm_plugin_prepare(_this->hidden->audio_handle,
|
||||
SND_PCM_CHANNEL_PLAYBACK);
|
||||
} else {
|
||||
/* Prepare audio capture */
|
||||
status =
|
||||
snd_pcm_plugin_prepare(_this->hidden->audio_handle,
|
||||
SND_PCM_CHANNEL_CAPTURE);
|
||||
}
|
||||
|
||||
if (status < 0) {
|
||||
return QSA_SetError("snd_pcm_plugin_prepare", status);
|
||||
}
|
||||
|
||||
/* We're really ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void QSA_DetectDevices(void)
|
||||
{
|
||||
uint32_t it;
|
||||
uint32_t cards;
|
||||
uint32_t devices;
|
||||
int32_t status;
|
||||
|
||||
/* Detect amount of available devices */
|
||||
/* this value can be changed in the runtime */
|
||||
cards = snd_cards();
|
||||
|
||||
/* If io-audio manager is not running we will get 0 as number */
|
||||
/* of available audio devices */
|
||||
if (cards == 0) {
|
||||
/* We have no any available audio devices */
|
||||
return;
|
||||
}
|
||||
|
||||
/* !!! FIXME: code duplication */
|
||||
/* Find requested devices by type */
|
||||
{ /* output devices */
|
||||
/* Playback devices enumeration requested */
|
||||
for (it = 0; it < cards; it++) {
|
||||
devices = 0;
|
||||
do {
|
||||
status =
|
||||
snd_card_get_longname(it,
|
||||
qsa_playback_device
|
||||
[qsa_playback_devices].name,
|
||||
QSA_MAX_NAME_LENGTH);
|
||||
if (status == EOK) {
|
||||
snd_pcm_t *handle;
|
||||
|
||||
/* Add device number to device name */
|
||||
sprintf(qsa_playback_device[qsa_playback_devices].name +
|
||||
SDL_strlen(qsa_playback_device
|
||||
[qsa_playback_devices].name), " d%d",
|
||||
devices);
|
||||
|
||||
/* Store associated card number id */
|
||||
qsa_playback_device[qsa_playback_devices].cardno = it;
|
||||
|
||||
/* Check if this device id could play anything */
|
||||
status =
|
||||
snd_pcm_open(&handle, it, devices,
|
||||
SND_PCM_OPEN_PLAYBACK);
|
||||
if (status == EOK) {
|
||||
qsa_playback_device[qsa_playback_devices].deviceno =
|
||||
devices;
|
||||
status = snd_pcm_close(handle);
|
||||
if (status == EOK) {
|
||||
/* Note that spec is NULL, because we are required to open the device before
|
||||
* acquiring the mix format, making this information inaccessible at
|
||||
* enumeration time
|
||||
*/
|
||||
SDL_AddAudioDevice(SDL_FALSE, qsa_playback_device[qsa_playback_devices].name, NULL, &qsa_playback_device[qsa_playback_devices]);
|
||||
qsa_playback_devices++;
|
||||
}
|
||||
} else {
|
||||
/* Check if we got end of devices list */
|
||||
if (status == -ENOENT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if we reached maximum devices count */
|
||||
if (qsa_playback_devices >= QSA_MAX_DEVICES) {
|
||||
break;
|
||||
}
|
||||
devices++;
|
||||
} while (1);
|
||||
|
||||
/* Check if we reached maximum devices count */
|
||||
if (qsa_playback_devices >= QSA_MAX_DEVICES) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{ /* capture devices */
|
||||
/* Capture devices enumeration requested */
|
||||
for (it = 0; it < cards; it++) {
|
||||
devices = 0;
|
||||
do {
|
||||
status =
|
||||
snd_card_get_longname(it,
|
||||
qsa_capture_device
|
||||
[qsa_capture_devices].name,
|
||||
QSA_MAX_NAME_LENGTH);
|
||||
if (status == EOK) {
|
||||
snd_pcm_t *handle;
|
||||
|
||||
/* Add device number to device name */
|
||||
sprintf(qsa_capture_device[qsa_capture_devices].name +
|
||||
SDL_strlen(qsa_capture_device
|
||||
[qsa_capture_devices].name), " d%d",
|
||||
devices);
|
||||
|
||||
/* Store associated card number id */
|
||||
qsa_capture_device[qsa_capture_devices].cardno = it;
|
||||
|
||||
/* Check if this device id could play anything */
|
||||
status =
|
||||
snd_pcm_open(&handle, it, devices,
|
||||
SND_PCM_OPEN_CAPTURE);
|
||||
if (status == EOK) {
|
||||
qsa_capture_device[qsa_capture_devices].deviceno =
|
||||
devices;
|
||||
status = snd_pcm_close(handle);
|
||||
if (status == EOK) {
|
||||
/* Note that spec is NULL, because we are required to open the device before
|
||||
* acquiring the mix format, making this information inaccessible at
|
||||
* enumeration time
|
||||
*/
|
||||
SDL_AddAudioDevice(SDL_TRUE, qsa_capture_device[qsa_capture_devices].name, NULL, &qsa_capture_device[qsa_capture_devices]);
|
||||
qsa_capture_devices++;
|
||||
}
|
||||
} else {
|
||||
/* Check if we got end of devices list */
|
||||
if (status == -ENOENT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if we reached maximum devices count */
|
||||
if (qsa_capture_devices >= QSA_MAX_DEVICES) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
devices++;
|
||||
} while (1);
|
||||
|
||||
/* Check if we reached maximum devices count */
|
||||
if (qsa_capture_devices >= QSA_MAX_DEVICES) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void QSA_Deinitialize(void)
|
||||
{
|
||||
/* Clear devices array on shutdown */
|
||||
/* !!! FIXME: we zero these on init...any reason to do it here? */
|
||||
SDL_zeroa(qsa_playback_device);
|
||||
SDL_zeroa(qsa_capture_device);
|
||||
qsa_playback_devices = 0;
|
||||
qsa_capture_devices = 0;
|
||||
}
|
||||
|
||||
static SDL_bool QSA_Init(SDL_AudioDriverImpl * impl)
|
||||
{
|
||||
/* Clear devices array */
|
||||
SDL_zeroa(qsa_playback_device);
|
||||
SDL_zeroa(qsa_capture_device);
|
||||
qsa_playback_devices = 0;
|
||||
qsa_capture_devices = 0;
|
||||
|
||||
/* Set function pointers */
|
||||
/* DeviceLock and DeviceUnlock functions are used default, */
|
||||
/* provided by SDL, which uses pthread_mutex for lock/unlock */
|
||||
impl->DetectDevices = QSA_DetectDevices;
|
||||
impl->OpenDevice = QSA_OpenDevice;
|
||||
impl->ThreadInit = QSA_ThreadInit;
|
||||
impl->WaitDevice = QSA_WaitDevice;
|
||||
impl->PlayDevice = QSA_PlayDevice;
|
||||
impl->GetDeviceBuf = QSA_GetDeviceBuf;
|
||||
impl->CloseDevice = QSA_CloseDevice;
|
||||
impl->Deinitialize = QSA_Deinitialize;
|
||||
impl->LockDevice = NULL;
|
||||
impl->UnlockDevice = NULL;
|
||||
|
||||
impl->ProvidesOwnCallbackThread = 0;
|
||||
impl->HasCaptureSupport = 1;
|
||||
impl->OnlyHasDefaultOutputDevice = 0;
|
||||
impl->OnlyHasDefaultCaptureDevice = 0;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap QSAAUDIO_bootstrap = {
|
||||
"qsa", "QNX QSA Audio", QSA_Init, 0
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_QNX */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
54
external/sdl/SDL/src/audio/qnx/SDL_qsa_audio.h
vendored
Normal file
54
external/sdl/SDL/src/audio/qnx/SDL_qsa_audio.h
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2021 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_QSA_AUDIO_H__
|
||||
#define __SDL_QSA_AUDIO_H__
|
||||
|
||||
#include <sys/asoundlib.h>
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* SDL capture state */
|
||||
SDL_bool iscapture;
|
||||
|
||||
/* The audio device handle */
|
||||
int cardno;
|
||||
int deviceno;
|
||||
snd_pcm_t *audio_handle;
|
||||
|
||||
/* The audio file descriptor */
|
||||
int audio_fd;
|
||||
|
||||
/* Select timeout status */
|
||||
uint32_t timeout_on_wait;
|
||||
|
||||
/* Raw mixing buffer */
|
||||
Uint8 *pcm_buf;
|
||||
Uint32 pcm_len;
|
||||
};
|
||||
|
||||
#endif /* __SDL_QSA_AUDIO_H__ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
365
external/sdl/SDL/src/audio/sndio/SDL_sndioaudio.c
vendored
Normal file
365
external/sdl/SDL/src/audio/sndio/SDL_sndioaudio.c
vendored
Normal file
@ -0,0 +1,365 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_AUDIO_DRIVER_SNDIO
|
||||
|
||||
/* OpenBSD sndio target */
|
||||
|
||||
#ifdef HAVE_STDIO_H
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "SDL_sndioaudio.h"
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
|
||||
#endif
|
||||
|
||||
#ifndef INFTIM
|
||||
#define INFTIM -1
|
||||
#endif
|
||||
|
||||
#ifndef SIO_DEVANY
|
||||
#define SIO_DEVANY "default"
|
||||
#endif
|
||||
|
||||
static struct sio_hdl *(*SNDIO_sio_open)(const char *, unsigned int, int);
|
||||
static void (*SNDIO_sio_close)(struct sio_hdl *);
|
||||
static int (*SNDIO_sio_setpar)(struct sio_hdl *, struct sio_par *);
|
||||
static int (*SNDIO_sio_getpar)(struct sio_hdl *, struct sio_par *);
|
||||
static int (*SNDIO_sio_start)(struct sio_hdl *);
|
||||
static int (*SNDIO_sio_stop)(struct sio_hdl *);
|
||||
static size_t (*SNDIO_sio_read)(struct sio_hdl *, void *, size_t);
|
||||
static size_t (*SNDIO_sio_write)(struct sio_hdl *, const void *, size_t);
|
||||
static int (*SNDIO_sio_nfds)(struct sio_hdl *);
|
||||
static int (*SNDIO_sio_pollfd)(struct sio_hdl *, struct pollfd *, int);
|
||||
static int (*SNDIO_sio_revents)(struct sio_hdl *, struct pollfd *);
|
||||
static int (*SNDIO_sio_eof)(struct sio_hdl *);
|
||||
static void (*SNDIO_sio_initpar)(struct sio_par *);
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
|
||||
static const char *sndio_library = SDL_AUDIO_DRIVER_SNDIO_DYNAMIC;
|
||||
static void *sndio_handle = NULL;
|
||||
|
||||
static int load_sndio_sym(const char *fn, void **addr)
|
||||
{
|
||||
*addr = SDL_LoadFunction(sndio_handle, fn);
|
||||
if (*addr == NULL) {
|
||||
/* Don't call SDL_SetError(): SDL_LoadFunction already did. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* cast funcs to char* first, to please GCC's strict aliasing rules. */
|
||||
#define SDL_SNDIO_SYM(x) \
|
||||
if (!load_sndio_sym(#x, (void **)(char *)&SNDIO_##x)) \
|
||||
return -1
|
||||
#else
|
||||
#define SDL_SNDIO_SYM(x) SNDIO_##x = x
|
||||
#endif
|
||||
|
||||
static int load_sndio_syms(void)
|
||||
{
|
||||
SDL_SNDIO_SYM(sio_open);
|
||||
SDL_SNDIO_SYM(sio_close);
|
||||
SDL_SNDIO_SYM(sio_setpar);
|
||||
SDL_SNDIO_SYM(sio_getpar);
|
||||
SDL_SNDIO_SYM(sio_start);
|
||||
SDL_SNDIO_SYM(sio_stop);
|
||||
SDL_SNDIO_SYM(sio_read);
|
||||
SDL_SNDIO_SYM(sio_write);
|
||||
SDL_SNDIO_SYM(sio_nfds);
|
||||
SDL_SNDIO_SYM(sio_pollfd);
|
||||
SDL_SNDIO_SYM(sio_revents);
|
||||
SDL_SNDIO_SYM(sio_eof);
|
||||
SDL_SNDIO_SYM(sio_initpar);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef SDL_SNDIO_SYM
|
||||
|
||||
#ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
|
||||
|
||||
static void UnloadSNDIOLibrary(void)
|
||||
{
|
||||
if (sndio_handle != NULL) {
|
||||
SDL_UnloadObject(sndio_handle);
|
||||
sndio_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int LoadSNDIOLibrary(void)
|
||||
{
|
||||
int retval = 0;
|
||||
if (sndio_handle == NULL) {
|
||||
sndio_handle = SDL_LoadObject(sndio_library);
|
||||
if (sndio_handle == NULL) {
|
||||
retval = -1;
|
||||
/* Don't call SDL_SetError(): SDL_LoadObject already did. */
|
||||
} else {
|
||||
retval = load_sndio_syms();
|
||||
if (retval < 0) {
|
||||
UnloadSNDIOLibrary();
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void UnloadSNDIOLibrary(void)
|
||||
{
|
||||
}
|
||||
|
||||
static int LoadSNDIOLibrary(void)
|
||||
{
|
||||
load_sndio_syms();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_SNDIO_DYNAMIC */
|
||||
|
||||
static void SNDIO_WaitDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
/* no-op; SNDIO_sio_write() blocks if necessary. */
|
||||
}
|
||||
|
||||
static void SNDIO_PlayDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
const int written = SNDIO_sio_write(_this->hidden->dev,
|
||||
_this->hidden->mixbuf,
|
||||
_this->hidden->mixlen);
|
||||
|
||||
/* If we couldn't write, assume fatal error for now */
|
||||
if (written == 0) {
|
||||
SDL_OpenedAudioDeviceDisconnected(_this);
|
||||
}
|
||||
#ifdef DEBUG_AUDIO
|
||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int SNDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
|
||||
{
|
||||
size_t r;
|
||||
int revents;
|
||||
int nfds;
|
||||
|
||||
/* Emulate a blocking read */
|
||||
r = SNDIO_sio_read(_this->hidden->dev, buffer, buflen);
|
||||
while (r == 0 && !SNDIO_sio_eof(_this->hidden->dev)) {
|
||||
nfds = SNDIO_sio_pollfd(_this->hidden->dev, _this->hidden->pfd, POLLIN);
|
||||
if (nfds <= 0 || poll(_this->hidden->pfd, nfds, INFTIM) < 0) {
|
||||
return -1;
|
||||
}
|
||||
revents = SNDIO_sio_revents(_this->hidden->dev, _this->hidden->pfd);
|
||||
if (revents & POLLIN) {
|
||||
r = SNDIO_sio_read(_this->hidden->dev, buffer, buflen);
|
||||
}
|
||||
if (revents & POLLHUP) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (int)r;
|
||||
}
|
||||
|
||||
static void SNDIO_FlushCapture(SDL_AudioDevice *_this)
|
||||
{
|
||||
char buf[512];
|
||||
|
||||
while (SNDIO_sio_read(_this->hidden->dev, buf, sizeof(buf)) != 0) {
|
||||
/* do nothing */;
|
||||
}
|
||||
}
|
||||
|
||||
static Uint8 *SNDIO_GetDeviceBuf(SDL_AudioDevice *_this)
|
||||
{
|
||||
return _this->hidden->mixbuf;
|
||||
}
|
||||
|
||||
static void SNDIO_CloseDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
if (_this->hidden->pfd != NULL) {
|
||||
SDL_free(_this->hidden->pfd);
|
||||
}
|
||||
if (_this->hidden->dev != NULL) {
|
||||
SNDIO_sio_stop(_this->hidden->dev);
|
||||
SNDIO_sio_close(_this->hidden->dev);
|
||||
}
|
||||
SDL_free(_this->hidden->mixbuf);
|
||||
SDL_free(_this->hidden);
|
||||
}
|
||||
|
||||
static int SNDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
{
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts;
|
||||
struct sio_par par;
|
||||
SDL_bool iscapture = _this->iscapture;
|
||||
|
||||
_this->hidden = (struct SDL_PrivateAudioData *)
|
||||
SDL_malloc(sizeof(*_this->hidden));
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(_this->hidden);
|
||||
|
||||
_this->hidden->mixlen = _this->spec.size;
|
||||
|
||||
/* Capture devices must be non-blocking for SNDIO_FlushCapture */
|
||||
_this->hidden->dev = SNDIO_sio_open(devname != NULL ? devname : SIO_DEVANY,
|
||||
iscapture ? SIO_REC : SIO_PLAY, iscapture);
|
||||
if (_this->hidden->dev == NULL) {
|
||||
return SDL_SetError("sio_open() failed");
|
||||
}
|
||||
|
||||
/* Allocate the pollfd array for capture devices */
|
||||
if (iscapture) {
|
||||
_this->hidden->pfd = SDL_malloc(sizeof(struct pollfd) * SNDIO_sio_nfds(_this->hidden->dev));
|
||||
if (_this->hidden->pfd == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
}
|
||||
|
||||
SNDIO_sio_initpar(&par);
|
||||
|
||||
par.rate = _this->spec.freq;
|
||||
par.pchan = _this->spec.channels;
|
||||
par.round = _this->spec.samples;
|
||||
par.appbufsz = par.round * 2;
|
||||
|
||||
/* Try for a closest match on audio format */
|
||||
closefmts = SDL_ClosestAudioFormats(_this->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
if (!SDL_AUDIO_ISFLOAT(test_format)) {
|
||||
par.le = SDL_AUDIO_ISLITTLEENDIAN(test_format) ? 1 : 0;
|
||||
par.sig = SDL_AUDIO_ISSIGNED(test_format) ? 1 : 0;
|
||||
par.bits = SDL_AUDIO_BITSIZE(test_format);
|
||||
|
||||
if (SNDIO_sio_setpar(_this->hidden->dev, &par) == 0) {
|
||||
continue;
|
||||
}
|
||||
if (SNDIO_sio_getpar(_this->hidden->dev, &par) == 0) {
|
||||
return SDL_SetError("sio_getpar() failed");
|
||||
}
|
||||
if (par.bps != SIO_BPS(par.bits)) {
|
||||
continue;
|
||||
}
|
||||
if ((par.bits == 8 * par.bps) || (par.msb)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
return SDL_SetError("%s: Unsupported audio format", "sndio");
|
||||
}
|
||||
|
||||
if ((par.bps == 4) && (par.sig) && (par.le)) {
|
||||
_this->spec.format = SDL_AUDIO_S32LSB;
|
||||
} else if ((par.bps == 4) && (par.sig) && (!par.le)) {
|
||||
_this->spec.format = SDL_AUDIO_S32MSB;
|
||||
} else if ((par.bps == 2) && (par.sig) && (par.le)) {
|
||||
_this->spec.format = SDL_AUDIO_S16LSB;
|
||||
} else if ((par.bps == 2) && (par.sig) && (!par.le)) {
|
||||
_this->spec.format = SDL_AUDIO_S16MSB;
|
||||
} else if ((par.bps == 1) && (par.sig)) {
|
||||
_this->spec.format = SDL_AUDIO_S8;
|
||||
} else if ((par.bps == 1) && (!par.sig)) {
|
||||
_this->spec.format = SDL_AUDIO_U8;
|
||||
} else {
|
||||
return SDL_SetError("sndio: Got unsupported hardware audio format.");
|
||||
}
|
||||
|
||||
_this->spec.freq = par.rate;
|
||||
_this->spec.channels = par.pchan;
|
||||
_this->spec.samples = par.round;
|
||||
|
||||
/* Calculate the final parameters for this audio specification */
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
/* Allocate mixing buffer */
|
||||
_this->hidden->mixlen = _this->spec.size;
|
||||
_this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->hidden->mixlen);
|
||||
if (_this->hidden->mixbuf == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(_this->hidden->mixbuf, _this->spec.silence, _this->hidden->mixlen);
|
||||
|
||||
if (!SNDIO_sio_start(_this->hidden->dev)) {
|
||||
return SDL_SetError("sio_start() failed");
|
||||
}
|
||||
|
||||
/* We're ready to rock and roll. :-) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void SNDIO_Deinitialize(void)
|
||||
{
|
||||
UnloadSNDIOLibrary();
|
||||
}
|
||||
|
||||
static void SNDIO_DetectDevices(void)
|
||||
{
|
||||
SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, NULL, (void *)0x1);
|
||||
SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, NULL, (void *)0x2);
|
||||
}
|
||||
|
||||
static SDL_bool SNDIO_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (LoadSNDIOLibrary() < 0) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->OpenDevice = SNDIO_OpenDevice;
|
||||
impl->WaitDevice = SNDIO_WaitDevice;
|
||||
impl->PlayDevice = SNDIO_PlayDevice;
|
||||
impl->GetDeviceBuf = SNDIO_GetDeviceBuf;
|
||||
impl->CloseDevice = SNDIO_CloseDevice;
|
||||
impl->CaptureFromDevice = SNDIO_CaptureFromDevice;
|
||||
impl->FlushCapture = SNDIO_FlushCapture;
|
||||
impl->Deinitialize = SNDIO_Deinitialize;
|
||||
impl->DetectDevices = SNDIO_DetectDevices;
|
||||
|
||||
impl->AllowsArbitraryDeviceNames = SDL_TRUE;
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap SNDIO_bootstrap = {
|
||||
"sndio", "OpenBSD sndio", SNDIO_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_SNDIO */
|
44
external/sdl/SDL/src/audio/sndio/SDL_sndioaudio.h
vendored
Normal file
44
external/sdl/SDL/src/audio/sndio/SDL_sndioaudio.h
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_sndioaudio_h_
|
||||
#define SDL_sndioaudio_h_
|
||||
|
||||
#include <poll.h>
|
||||
#include <sndio.h>
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The audio device handle */
|
||||
struct sio_hdl *dev;
|
||||
|
||||
/* Raw mixing buffer */
|
||||
Uint8 *mixbuf;
|
||||
int mixlen;
|
||||
|
||||
/* Polling structures for non-blocking sndio devices */
|
||||
struct pollfd *pfd;
|
||||
};
|
||||
|
||||
#endif /* SDL_sndioaudio_h_ */
|
217
external/sdl/SDL/src/audio/vita/SDL_vitaaudio.c
vendored
Normal file
217
external/sdl/SDL/src/audio/vita/SDL_vitaaudio.c
vendored
Normal file
@ -0,0 +1,217 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_AUDIO_DRIVER_VITA
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../SDL_audiodev_c.h"
|
||||
#include "../SDL_sysaudio.h"
|
||||
#include "SDL_vitaaudio.h"
|
||||
|
||||
#include <psp2/kernel/threadmgr.h>
|
||||
#include <psp2/audioout.h>
|
||||
#include <psp2/audioin.h>
|
||||
|
||||
#define SCE_AUDIO_SAMPLE_ALIGN(s) (((s) + 63) & ~63)
|
||||
#define SCE_AUDIO_MAX_VOLUME 0x8000
|
||||
|
||||
static int VITAAUD_OpenCaptureDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
_this->spec.freq = 16000;
|
||||
_this->spec.samples = 512;
|
||||
_this->spec.channels = 1;
|
||||
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
_this->hidden->port = sceAudioInOpenPort(SCE_AUDIO_IN_PORT_TYPE_VOICE, 512, 16000, SCE_AUDIO_IN_PARAM_FORMAT_S16_MONO);
|
||||
|
||||
if (_this->hidden->port < 0) {
|
||||
return SDL_SetError("Couldn't open audio in port: %x", _this->hidden->port);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int VITAAUD_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
{
|
||||
int format, mixlen, i, port = SCE_AUDIO_OUT_PORT_TYPE_MAIN;
|
||||
int vols[2] = { SCE_AUDIO_MAX_VOLUME, SCE_AUDIO_MAX_VOLUME };
|
||||
SDL_AudioFormat test_format;
|
||||
const SDL_AudioFormat *closefmts;
|
||||
|
||||
_this->hidden = (struct SDL_PrivateAudioData *)
|
||||
SDL_malloc(sizeof(*_this->hidden));
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_memset(_this->hidden, 0, sizeof(*_this->hidden));
|
||||
|
||||
closefmts = SDL_ClosestAudioFormats(_this->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
if (test_format == SDL_AUDIO_S16LSB) {
|
||||
_this->spec.format = test_format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
return SDL_SetError("Unsupported audio format");
|
||||
}
|
||||
|
||||
if (_this->iscapture) {
|
||||
return VITAAUD_OpenCaptureDevice(_this);
|
||||
}
|
||||
|
||||
/* The sample count must be a multiple of 64. */
|
||||
_this->spec.samples = SCE_AUDIO_SAMPLE_ALIGN(_this->spec.samples);
|
||||
|
||||
/* Update the fragment size as size in bytes. */
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
/* Allocate the mixing buffer. Its size and starting address must
|
||||
be a multiple of 64 bytes. Our sample count is already a multiple of
|
||||
64, so spec->size should be a multiple of 64 as well. */
|
||||
mixlen = _this->spec.size * NUM_BUFFERS;
|
||||
_this->hidden->rawbuf = (Uint8 *)SDL_aligned_alloc(64, mixlen);
|
||||
if (_this->hidden->rawbuf == NULL) {
|
||||
return SDL_SetError("Couldn't allocate mixing buffer");
|
||||
}
|
||||
|
||||
/* Setup the hardware channel. */
|
||||
if (_this->spec.channels == 1) {
|
||||
format = SCE_AUDIO_OUT_MODE_MONO;
|
||||
} else {
|
||||
format = SCE_AUDIO_OUT_MODE_STEREO;
|
||||
}
|
||||
|
||||
if (_this->spec.freq < 48000) {
|
||||
port = SCE_AUDIO_OUT_PORT_TYPE_BGM;
|
||||
}
|
||||
|
||||
_this->hidden->port = sceAudioOutOpenPort(port, _this->spec.samples, _this->spec.freq, format);
|
||||
if (_this->hidden->port < 0) {
|
||||
SDL_aligned_free(_this->hidden->rawbuf);
|
||||
_this->hidden->rawbuf = NULL;
|
||||
return SDL_SetError("Couldn't open audio out port: %x", _this->hidden->port);
|
||||
}
|
||||
|
||||
sceAudioOutSetVolume(_this->hidden->port, SCE_AUDIO_VOLUME_FLAG_L_CH | SCE_AUDIO_VOLUME_FLAG_R_CH, vols);
|
||||
|
||||
SDL_memset(_this->hidden->rawbuf, 0, mixlen);
|
||||
for (i = 0; i < NUM_BUFFERS; i++) {
|
||||
_this->hidden->mixbufs[i] = &_this->hidden->rawbuf[i * _this->spec.size];
|
||||
}
|
||||
|
||||
_this->hidden->next_buffer = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void VITAAUD_PlayDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
Uint8 *mixbuf = _this->hidden->mixbufs[_this->hidden->next_buffer];
|
||||
|
||||
sceAudioOutOutput(_this->hidden->port, mixbuf);
|
||||
|
||||
_this->hidden->next_buffer = (_this->hidden->next_buffer + 1) % NUM_BUFFERS;
|
||||
}
|
||||
|
||||
/* This function waits until it is possible to write a full sound buffer */
|
||||
static void VITAAUD_WaitDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
/* Because we block when sending audio, there's no need for this function to do anything. */
|
||||
}
|
||||
|
||||
static Uint8 *VITAAUD_GetDeviceBuf(SDL_AudioDevice *_this)
|
||||
{
|
||||
return _this->hidden->mixbufs[_this->hidden->next_buffer];
|
||||
}
|
||||
|
||||
static void VITAAUD_CloseDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
if (_this->hidden->port >= 0) {
|
||||
if (_this->iscapture) {
|
||||
sceAudioInReleasePort(_this->hidden->port);
|
||||
} else {
|
||||
sceAudioOutReleasePort(_this->hidden->port);
|
||||
}
|
||||
_this->hidden->port = -1;
|
||||
}
|
||||
|
||||
if (!_this->iscapture && _this->hidden->rawbuf != NULL) {
|
||||
SDL_aligned_free(_this->hidden->rawbuf);
|
||||
_this->hidden->rawbuf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int VITAAUD_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
|
||||
{
|
||||
int ret;
|
||||
SDL_assert(buflen == _this->spec.size);
|
||||
ret = sceAudioInInput(_this->hidden->port, buffer);
|
||||
if (ret < 0) {
|
||||
return SDL_SetError("Failed to capture from device: %x", ret);
|
||||
}
|
||||
return _this->spec.size;
|
||||
}
|
||||
|
||||
static void VITAAUD_ThreadInit(SDL_AudioDevice *_this)
|
||||
{
|
||||
/* Increase the priority of this audio thread by 1 to put it
|
||||
ahead of other SDL threads. */
|
||||
SceUID thid;
|
||||
SceKernelThreadInfo info;
|
||||
thid = sceKernelGetThreadId();
|
||||
info.size = sizeof(SceKernelThreadInfo);
|
||||
if (sceKernelGetThreadInfo(thid, &info) == 0) {
|
||||
sceKernelChangeThreadPriority(thid, info.currentPriority - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_bool VITAAUD_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
/* Set the function pointers */
|
||||
impl->OpenDevice = VITAAUD_OpenDevice;
|
||||
impl->PlayDevice = VITAAUD_PlayDevice;
|
||||
impl->WaitDevice = VITAAUD_WaitDevice;
|
||||
impl->GetDeviceBuf = VITAAUD_GetDeviceBuf;
|
||||
impl->CloseDevice = VITAAUD_CloseDevice;
|
||||
impl->ThreadInit = VITAAUD_ThreadInit;
|
||||
|
||||
impl->CaptureFromDevice = VITAAUD_CaptureFromDevice;
|
||||
|
||||
/* and the capabilities */
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
|
||||
impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap VITAAUD_bootstrap = {
|
||||
"vita", "VITA audio driver", VITAAUD_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_VITA */
|
41
external/sdl/SDL/src/audio/vita/SDL_vitaaudio.h
vendored
Normal file
41
external/sdl/SDL/src/audio/vita/SDL_vitaaudio.h
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_vitaaudio_h
|
||||
#define SDL_vitaaudio_h
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
#define NUM_BUFFERS 2
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
/* The hardware input/output port. */
|
||||
int port;
|
||||
/* The raw allocated mixing buffer. */
|
||||
Uint8 *rawbuf;
|
||||
/* Individual mixing buffers. */
|
||||
Uint8 *mixbufs[NUM_BUFFERS];
|
||||
/* Index of the next available mixing buffer. */
|
||||
int next_buffer;
|
||||
};
|
||||
|
||||
#endif /* SDL_vitaaudio_h */
|
610
external/sdl/SDL/src/audio/wasapi/SDL_wasapi.c
vendored
Normal file
610
external/sdl/SDL/src/audio/wasapi/SDL_wasapi.c
vendored
Normal file
@ -0,0 +1,610 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_AUDIO_DRIVER_WASAPI
|
||||
|
||||
#include "../../core/windows/SDL_windows.h"
|
||||
#include "../../core/windows/SDL_immdevice.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
#define COBJMACROS
|
||||
#include <audioclient.h>
|
||||
|
||||
#include "SDL_wasapi.h"
|
||||
|
||||
/* These constants aren't available in older SDKs */
|
||||
#ifndef AUDCLNT_STREAMFLAGS_RATEADJUST
|
||||
#define AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000
|
||||
#endif
|
||||
#ifndef AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY
|
||||
#define AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000
|
||||
#endif
|
||||
#ifndef AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
|
||||
#define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
|
||||
#endif
|
||||
|
||||
/* Some GUIDs we need to know without linking to libraries that aren't available before Vista. */
|
||||
static const IID SDL_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483, { 0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2 } };
|
||||
static const IID SDL_IID_IAudioCaptureClient = { 0xc8adbd64, 0xe71e, 0x48a0, { 0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c, 0xd3, 0x17 } };
|
||||
|
||||
|
||||
static void WASAPI_DetectDevices(void)
|
||||
{
|
||||
WASAPI_EnumerateEndpoints();
|
||||
}
|
||||
|
||||
static SDL_INLINE SDL_bool WasapiFailed(SDL_AudioDevice *_this, const HRESULT err)
|
||||
{
|
||||
if (err == S_OK) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
if (err == AUDCLNT_E_DEVICE_INVALIDATED) {
|
||||
_this->hidden->device_lost = SDL_TRUE;
|
||||
} else if (SDL_AtomicGet(&_this->enabled)) {
|
||||
IAudioClient_Stop(_this->hidden->client);
|
||||
SDL_OpenedAudioDeviceDisconnected(_this);
|
||||
SDL_assert(!SDL_AtomicGet(&_this->enabled));
|
||||
}
|
||||
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
static int UpdateAudioStream(SDL_AudioDevice *_this, const SDL_AudioSpec *oldspec)
|
||||
{
|
||||
/* Since WASAPI requires us to handle all audio conversion, and our
|
||||
device format might have changed, we might have to add/remove/change
|
||||
the audio stream that the higher level uses to convert data, so
|
||||
SDL keeps firing the callback as if nothing happened here. */
|
||||
|
||||
if ((_this->callbackspec.channels == _this->spec.channels) &&
|
||||
(_this->callbackspec.format == _this->spec.format) &&
|
||||
(_this->callbackspec.freq == _this->spec.freq) &&
|
||||
(_this->callbackspec.samples == _this->spec.samples)) {
|
||||
/* no need to buffer/convert in an AudioStream! */
|
||||
SDL_DestroyAudioStream(_this->stream);
|
||||
_this->stream = NULL;
|
||||
} else if ((oldspec->channels == _this->spec.channels) &&
|
||||
(oldspec->format == _this->spec.format) &&
|
||||
(oldspec->freq == _this->spec.freq)) {
|
||||
/* The existing audio stream is okay to keep using. */
|
||||
} else {
|
||||
/* replace the audiostream for new format */
|
||||
SDL_DestroyAudioStream(_this->stream);
|
||||
if (_this->iscapture) {
|
||||
_this->stream = SDL_CreateAudioStream(_this->spec.format,
|
||||
_this->spec.channels, _this->spec.freq,
|
||||
_this->callbackspec.format,
|
||||
_this->callbackspec.channels,
|
||||
_this->callbackspec.freq);
|
||||
} else {
|
||||
_this->stream = SDL_CreateAudioStream(_this->callbackspec.format,
|
||||
_this->callbackspec.channels,
|
||||
_this->callbackspec.freq, _this->spec.format,
|
||||
_this->spec.channels, _this->spec.freq);
|
||||
}
|
||||
|
||||
if (!_this->stream) {
|
||||
return -1; /* SDL_CreateAudioStream should have called SDL_SetError. */
|
||||
}
|
||||
}
|
||||
|
||||
/* make sure our scratch buffer can cover the new device spec. */
|
||||
if (_this->spec.size > _this->work_buffer_len) {
|
||||
Uint8 *ptr = (Uint8 *)SDL_realloc(_this->work_buffer, _this->spec.size);
|
||||
if (ptr == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
_this->work_buffer = ptr;
|
||||
_this->work_buffer_len = _this->spec.size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ReleaseWasapiDevice(SDL_AudioDevice *_this);
|
||||
|
||||
static SDL_bool RecoverWasapiDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
ReleaseWasapiDevice(_this); /* dump the lost device's handles. */
|
||||
|
||||
if (_this->hidden->default_device_generation) {
|
||||
_this->hidden->default_device_generation = SDL_AtomicGet(_this->iscapture ? &SDL_IMMDevice_DefaultCaptureGeneration : &SDL_IMMDevice_DefaultPlaybackGeneration);
|
||||
}
|
||||
|
||||
/* this can fail for lots of reasons, but the most likely is we had a
|
||||
non-default device that was disconnected, so we can't recover. Default
|
||||
devices try to reinitialize whatever the new default is, so it's more
|
||||
likely to carry on here, but this handles a non-default device that
|
||||
simply had its format changed in the Windows Control Panel. */
|
||||
if (WASAPI_ActivateDevice(_this, SDL_TRUE) == -1) {
|
||||
SDL_OpenedAudioDeviceDisconnected(_this);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
_this->hidden->device_lost = SDL_FALSE;
|
||||
|
||||
return SDL_TRUE; /* okay, carry on with new device details! */
|
||||
}
|
||||
|
||||
static SDL_bool RecoverWasapiIfLost(SDL_AudioDevice *_this)
|
||||
{
|
||||
const int generation = _this->hidden->default_device_generation;
|
||||
SDL_bool lost = _this->hidden->device_lost;
|
||||
|
||||
if (!SDL_AtomicGet(&_this->enabled)) {
|
||||
return SDL_FALSE; /* already failed. */
|
||||
}
|
||||
|
||||
if (!_this->hidden->client) {
|
||||
return SDL_TRUE; /* still waiting for activation. */
|
||||
}
|
||||
|
||||
if (!lost && (generation > 0)) { /* is a default device? */
|
||||
const int newgen = SDL_AtomicGet(_this->iscapture ? &SDL_IMMDevice_DefaultCaptureGeneration : &SDL_IMMDevice_DefaultPlaybackGeneration);
|
||||
if (generation != newgen) { /* the desired default device was changed, jump over to it. */
|
||||
lost = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return lost ? RecoverWasapiDevice(_this) : SDL_TRUE;
|
||||
}
|
||||
|
||||
static Uint8 *WASAPI_GetDeviceBuf(SDL_AudioDevice *_this)
|
||||
{
|
||||
/* get an endpoint buffer from WASAPI. */
|
||||
BYTE *buffer = NULL;
|
||||
|
||||
while (RecoverWasapiIfLost(_this) && _this->hidden->render) {
|
||||
if (!WasapiFailed(_this, IAudioRenderClient_GetBuffer(_this->hidden->render, _this->spec.samples, &buffer))) {
|
||||
return (Uint8 *)buffer;
|
||||
}
|
||||
SDL_assert(buffer == NULL);
|
||||
}
|
||||
|
||||
return (Uint8 *)buffer;
|
||||
}
|
||||
|
||||
static void WASAPI_PlayDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
if (_this->hidden->render != NULL) { /* definitely activated? */
|
||||
/* WasapiFailed() will mark the device for reacquisition or removal elsewhere. */
|
||||
WasapiFailed(_this, IAudioRenderClient_ReleaseBuffer(_this->hidden->render, _this->spec.samples, 0));
|
||||
}
|
||||
}
|
||||
|
||||
static void WASAPI_WaitDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
while (RecoverWasapiIfLost(_this) && _this->hidden->client && _this->hidden->event) {
|
||||
DWORD waitResult = WaitForSingleObjectEx(_this->hidden->event, 200, FALSE);
|
||||
if (waitResult == WAIT_OBJECT_0) {
|
||||
const UINT32 maxpadding = _this->spec.samples;
|
||||
UINT32 padding = 0;
|
||||
if (!WasapiFailed(_this, IAudioClient_GetCurrentPadding(_this->hidden->client, &padding))) {
|
||||
/*SDL_Log("WASAPI EVENT! padding=%u maxpadding=%u", (unsigned int)padding, (unsigned int)maxpadding);*/
|
||||
if (_this->iscapture) {
|
||||
if (padding > 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (padding <= maxpadding) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (waitResult != WAIT_TIMEOUT) {
|
||||
/*SDL_Log("WASAPI FAILED EVENT!");*/
|
||||
IAudioClient_Stop(_this->hidden->client);
|
||||
SDL_OpenedAudioDeviceDisconnected(_this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int WASAPI_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
|
||||
{
|
||||
SDL_AudioStream *stream = _this->hidden->capturestream;
|
||||
const int avail = SDL_GetAudioStreamAvailable(stream);
|
||||
if (avail > 0) {
|
||||
const int cpy = SDL_min(buflen, avail);
|
||||
SDL_GetAudioStreamData(stream, buffer, cpy);
|
||||
return cpy;
|
||||
}
|
||||
|
||||
while (RecoverWasapiIfLost(_this)) {
|
||||
HRESULT ret;
|
||||
BYTE *ptr = NULL;
|
||||
UINT32 frames = 0;
|
||||
DWORD flags = 0;
|
||||
|
||||
/* uhoh, client isn't activated yet, just return silence. */
|
||||
if (!_this->hidden->capture) {
|
||||
/* Delay so we run at about the speed that audio would be arriving. */
|
||||
SDL_Delay(((_this->spec.samples * 1000) / _this->spec.freq));
|
||||
SDL_memset(buffer, _this->spec.silence, buflen);
|
||||
return buflen;
|
||||
}
|
||||
|
||||
ret = IAudioCaptureClient_GetBuffer(_this->hidden->capture, &ptr, &frames, &flags, NULL, NULL);
|
||||
if (ret != AUDCLNT_S_BUFFER_EMPTY) {
|
||||
WasapiFailed(_this, ret); /* mark device lost/failed if necessary. */
|
||||
}
|
||||
|
||||
if ((ret == AUDCLNT_S_BUFFER_EMPTY) || !frames) {
|
||||
WASAPI_WaitDevice(_this);
|
||||
} else if (ret == S_OK) {
|
||||
const int total = ((int)frames) * _this->hidden->framesize;
|
||||
const int cpy = SDL_min(buflen, total);
|
||||
const int leftover = total - cpy;
|
||||
const SDL_bool silent = (flags & AUDCLNT_BUFFERFLAGS_SILENT) ? SDL_TRUE : SDL_FALSE;
|
||||
|
||||
if (silent) {
|
||||
SDL_memset(buffer, _this->spec.silence, cpy);
|
||||
} else {
|
||||
SDL_memcpy(buffer, ptr, cpy);
|
||||
}
|
||||
|
||||
if (leftover > 0) {
|
||||
ptr += cpy;
|
||||
if (silent) {
|
||||
SDL_memset(ptr, _this->spec.silence, leftover); /* I guess this is safe? */
|
||||
}
|
||||
|
||||
if (SDL_PutAudioStreamData(stream, ptr, leftover) == -1) {
|
||||
return -1; /* uhoh, out of memory, etc. Kill device. :( */
|
||||
}
|
||||
}
|
||||
|
||||
ret = IAudioCaptureClient_ReleaseBuffer(_this->hidden->capture, frames);
|
||||
WasapiFailed(_this, ret); /* mark device lost/failed if necessary. */
|
||||
|
||||
return cpy;
|
||||
}
|
||||
}
|
||||
|
||||
return -1; /* unrecoverable error. */
|
||||
}
|
||||
|
||||
static void WASAPI_FlushCapture(SDL_AudioDevice *_this)
|
||||
{
|
||||
BYTE *ptr = NULL;
|
||||
UINT32 frames = 0;
|
||||
DWORD flags = 0;
|
||||
|
||||
if (!_this->hidden->capture) {
|
||||
return; /* not activated yet? */
|
||||
}
|
||||
|
||||
/* just read until we stop getting packets, throwing them away. */
|
||||
while (SDL_TRUE) {
|
||||
const HRESULT ret = IAudioCaptureClient_GetBuffer(_this->hidden->capture, &ptr, &frames, &flags, NULL, NULL);
|
||||
if (ret == AUDCLNT_S_BUFFER_EMPTY) {
|
||||
break; /* no more buffered data; we're done. */
|
||||
} else if (WasapiFailed(_this, ret)) {
|
||||
break; /* failed for some other reason, abort. */
|
||||
} else if (WasapiFailed(_this, IAudioCaptureClient_ReleaseBuffer(_this->hidden->capture, frames))) {
|
||||
break; /* something broke. */
|
||||
}
|
||||
}
|
||||
SDL_ClearAudioStream(_this->hidden->capturestream);
|
||||
}
|
||||
|
||||
static void ReleaseWasapiDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
if (_this->hidden->client) {
|
||||
IAudioClient_Stop(_this->hidden->client);
|
||||
IAudioClient_Release(_this->hidden->client);
|
||||
_this->hidden->client = NULL;
|
||||
}
|
||||
|
||||
if (_this->hidden->render) {
|
||||
IAudioRenderClient_Release(_this->hidden->render);
|
||||
_this->hidden->render = NULL;
|
||||
}
|
||||
|
||||
if (_this->hidden->capture) {
|
||||
IAudioCaptureClient_Release(_this->hidden->capture);
|
||||
_this->hidden->capture = NULL;
|
||||
}
|
||||
|
||||
if (_this->hidden->waveformat) {
|
||||
CoTaskMemFree(_this->hidden->waveformat);
|
||||
_this->hidden->waveformat = NULL;
|
||||
}
|
||||
|
||||
if (_this->hidden->capturestream) {
|
||||
SDL_DestroyAudioStream(_this->hidden->capturestream);
|
||||
_this->hidden->capturestream = NULL;
|
||||
}
|
||||
|
||||
if (_this->hidden->activation_handler) {
|
||||
WASAPI_PlatformDeleteActivationHandler(_this->hidden->activation_handler);
|
||||
_this->hidden->activation_handler = NULL;
|
||||
}
|
||||
|
||||
if (_this->hidden->event) {
|
||||
CloseHandle(_this->hidden->event);
|
||||
_this->hidden->event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void WASAPI_CloseDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
WASAPI_UnrefDevice(_this);
|
||||
}
|
||||
|
||||
void WASAPI_RefDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
SDL_AtomicIncRef(&_this->hidden->refcount);
|
||||
}
|
||||
|
||||
void WASAPI_UnrefDevice(SDL_AudioDevice *_this)
|
||||
{
|
||||
if (!SDL_AtomicDecRef(&_this->hidden->refcount)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* actual closing happens here. */
|
||||
|
||||
/* don't touch _this->hidden->task in here; it has to be reverted from
|
||||
our callback thread. We do that in WASAPI_ThreadDeinit().
|
||||
(likewise for _this->hidden->coinitialized). */
|
||||
ReleaseWasapiDevice(_this);
|
||||
SDL_free(_this->hidden->devid);
|
||||
SDL_free(_this->hidden);
|
||||
}
|
||||
|
||||
/* This is called once a device is activated, possibly asynchronously. */
|
||||
int WASAPI_PrepDevice(SDL_AudioDevice *_this, const SDL_bool updatestream)
|
||||
{
|
||||
/* !!! FIXME: we could request an exclusive mode stream, which is lower latency;
|
||||
!!! it will write into the kernel's audio buffer directly instead of
|
||||
!!! shared memory that a user-mode mixer then writes to the kernel with
|
||||
!!! everything else. Doing this means any other sound using this device will
|
||||
!!! stop playing, including the user's MP3 player and system notification
|
||||
!!! sounds. You'd probably need to release the device when the app isn't in
|
||||
!!! the foreground, to be a good citizen of the system. It's doable, but it's
|
||||
!!! more work and causes some annoyances, and I don't know what the latency
|
||||
!!! wins actually look like. Maybe add a hint to force exclusive mode at
|
||||
!!! some point. To be sure, defaulting to shared mode is the right thing to
|
||||
!!! do in any case. */
|
||||
const SDL_AudioSpec oldspec = _this->spec;
|
||||
const AUDCLNT_SHAREMODE sharemode = AUDCLNT_SHAREMODE_SHARED;
|
||||
UINT32 bufsize = 0; /* this is in sample frames, not samples, not bytes. */
|
||||
REFERENCE_TIME default_period = 0;
|
||||
IAudioClient *client = _this->hidden->client;
|
||||
IAudioRenderClient *render = NULL;
|
||||
IAudioCaptureClient *capture = NULL;
|
||||
WAVEFORMATEX *waveformat = NULL;
|
||||
SDL_AudioFormat test_format;
|
||||
SDL_AudioFormat wasapi_format = 0;
|
||||
const SDL_AudioFormat *closefmts;
|
||||
HRESULT ret = S_OK;
|
||||
DWORD streamflags = 0;
|
||||
|
||||
SDL_assert(client != NULL);
|
||||
|
||||
#if defined(__WINRT__) || defined(__GDK__) /* CreateEventEx() arrived in Vista, so we need an #ifdef for XP. */
|
||||
_this->hidden->event = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
|
||||
#else
|
||||
_this->hidden->event = CreateEventW(NULL, 0, 0, NULL);
|
||||
#endif
|
||||
|
||||
if (_this->hidden->event == NULL) {
|
||||
return WIN_SetError("WASAPI can't create an event handle");
|
||||
}
|
||||
|
||||
ret = IAudioClient_GetMixFormat(client, &waveformat);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't determine mix format", ret);
|
||||
}
|
||||
|
||||
SDL_assert(waveformat != NULL);
|
||||
_this->hidden->waveformat = waveformat;
|
||||
|
||||
_this->spec.channels = (Uint8)waveformat->nChannels;
|
||||
|
||||
/* Make sure we have a valid format that we can convert to whatever WASAPI wants. */
|
||||
wasapi_format = WaveFormatToSDLFormat(waveformat);
|
||||
|
||||
closefmts = SDL_ClosestAudioFormats(_this->spec.format);
|
||||
while ((test_format = *(closefmts++)) != 0) {
|
||||
if (test_format == wasapi_format) {
|
||||
_this->spec.format = test_format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!test_format) {
|
||||
return SDL_SetError("%s: Unsupported audio format", "wasapi");
|
||||
}
|
||||
|
||||
ret = IAudioClient_GetDevicePeriod(client, &default_period, NULL);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't determine minimum device period", ret);
|
||||
}
|
||||
|
||||
/* we've gotten reports that WASAPI's resampler introduces distortions, but in the short term
|
||||
it fixes some other WASAPI-specific quirks we haven't quite tracked down.
|
||||
Refer to bug #6326 for the immediate concern. */
|
||||
#if 0
|
||||
_this->spec.freq = waveformat->nSamplesPerSec; /* force sampling rate so our resampler kicks in, if necessary. */
|
||||
#else
|
||||
/* favor WASAPI's resampler over our own */
|
||||
if ((DWORD)_this->spec.freq != waveformat->nSamplesPerSec) {
|
||||
streamflags |= (AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY);
|
||||
waveformat->nSamplesPerSec = _this->spec.freq;
|
||||
waveformat->nAvgBytesPerSec = waveformat->nSamplesPerSec * waveformat->nChannels * (waveformat->wBitsPerSample / 8);
|
||||
}
|
||||
#endif
|
||||
|
||||
streamflags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
|
||||
ret = IAudioClient_Initialize(client, sharemode, streamflags, 0, 0, waveformat, NULL);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't initialize audio client", ret);
|
||||
}
|
||||
|
||||
ret = IAudioClient_SetEventHandle(client, _this->hidden->event);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't set event handle", ret);
|
||||
}
|
||||
|
||||
ret = IAudioClient_GetBufferSize(client, &bufsize);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't determine buffer size", ret);
|
||||
}
|
||||
|
||||
/* Match the callback size to the period size to cut down on the number of
|
||||
interrupts waited for in each call to WaitDevice */
|
||||
{
|
||||
const float period_millis = default_period / 10000.0f;
|
||||
const float period_frames = period_millis * _this->spec.freq / 1000.0f;
|
||||
_this->spec.samples = (Uint16)SDL_ceilf(period_frames);
|
||||
}
|
||||
|
||||
/* Update the fragment size as size in bytes */
|
||||
SDL_CalculateAudioSpec(&_this->spec);
|
||||
|
||||
_this->hidden->framesize = (SDL_AUDIO_BITSIZE(_this->spec.format) / 8) * _this->spec.channels;
|
||||
|
||||
if (_this->iscapture) {
|
||||
_this->hidden->capturestream = SDL_CreateAudioStream(_this->spec.format, _this->spec.channels, _this->spec.freq, _this->spec.format, _this->spec.channels, _this->spec.freq);
|
||||
if (!_this->hidden->capturestream) {
|
||||
return -1; /* already set SDL_Error */
|
||||
}
|
||||
|
||||
ret = IAudioClient_GetService(client, &SDL_IID_IAudioCaptureClient, (void **)&capture);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't get capture client service", ret);
|
||||
}
|
||||
|
||||
SDL_assert(capture != NULL);
|
||||
_this->hidden->capture = capture;
|
||||
ret = IAudioClient_Start(client);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't start capture", ret);
|
||||
}
|
||||
|
||||
WASAPI_FlushCapture(_this); /* MSDN says you should flush capture endpoint right after startup. */
|
||||
} else {
|
||||
ret = IAudioClient_GetService(client, &SDL_IID_IAudioRenderClient, (void **)&render);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't get render client service", ret);
|
||||
}
|
||||
|
||||
SDL_assert(render != NULL);
|
||||
_this->hidden->render = render;
|
||||
ret = IAudioClient_Start(client);
|
||||
if (FAILED(ret)) {
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't start playback", ret);
|
||||
}
|
||||
}
|
||||
|
||||
if (updatestream) {
|
||||
return UpdateAudioStream(_this, &oldspec);
|
||||
}
|
||||
|
||||
return 0; /* good to go. */
|
||||
}
|
||||
|
||||
static int WASAPI_OpenDevice(SDL_AudioDevice *_this, const char *devname)
|
||||
{
|
||||
LPCWSTR devid = (LPCWSTR)_this->handle;
|
||||
|
||||
/* Initialize all variables that we clean on shutdown */
|
||||
_this->hidden = (struct SDL_PrivateAudioData *) SDL_malloc(sizeof(*_this->hidden));
|
||||
if (_this->hidden == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
SDL_zerop(_this->hidden);
|
||||
|
||||
WASAPI_RefDevice(_this); /* so CloseDevice() will unref to zero. */
|
||||
|
||||
if (!devid) { /* is default device? */
|
||||
_this->hidden->default_device_generation = SDL_AtomicGet(_this->iscapture ? &SDL_IMMDevice_DefaultCaptureGeneration : &SDL_IMMDevice_DefaultPlaybackGeneration);
|
||||
} else {
|
||||
_this->hidden->devid = SDL_wcsdup(devid);
|
||||
if (!_this->hidden->devid) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
}
|
||||
|
||||
if (WASAPI_ActivateDevice(_this, SDL_FALSE) == -1) {
|
||||
return -1; /* already set error. */
|
||||
}
|
||||
|
||||
/* Ready, but waiting for async device activation.
|
||||
Until activation is successful, we will report silence from capture
|
||||
devices and ignore data on playback devices.
|
||||
Also, since we don't know the _actual_ device format until after
|
||||
activation, we let the app have whatever it asks for. We set up
|
||||
an SDL_AudioStream to convert, if necessary, once the activation
|
||||
completes. */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void WASAPI_ThreadInit(SDL_AudioDevice *_this)
|
||||
{
|
||||
WASAPI_PlatformThreadInit(_this);
|
||||
}
|
||||
|
||||
static void WASAPI_ThreadDeinit(SDL_AudioDevice *_this)
|
||||
{
|
||||
WASAPI_PlatformThreadDeinit(_this);
|
||||
}
|
||||
|
||||
static void WASAPI_Deinitialize(void)
|
||||
{
|
||||
WASAPI_PlatformDeinit();
|
||||
}
|
||||
|
||||
static SDL_bool WASAPI_Init(SDL_AudioDriverImpl *impl)
|
||||
{
|
||||
if (WASAPI_PlatformInit() == -1) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
/* Set the function pointers */
|
||||
impl->DetectDevices = WASAPI_DetectDevices;
|
||||
impl->ThreadInit = WASAPI_ThreadInit;
|
||||
impl->ThreadDeinit = WASAPI_ThreadDeinit;
|
||||
impl->OpenDevice = WASAPI_OpenDevice;
|
||||
impl->PlayDevice = WASAPI_PlayDevice;
|
||||
impl->WaitDevice = WASAPI_WaitDevice;
|
||||
impl->GetDeviceBuf = WASAPI_GetDeviceBuf;
|
||||
impl->CaptureFromDevice = WASAPI_CaptureFromDevice;
|
||||
impl->FlushCapture = WASAPI_FlushCapture;
|
||||
impl->CloseDevice = WASAPI_CloseDevice;
|
||||
impl->Deinitialize = WASAPI_Deinitialize;
|
||||
impl->GetDefaultAudioInfo = WASAPI_GetDefaultAudioInfo;
|
||||
impl->HasCaptureSupport = SDL_TRUE;
|
||||
impl->SupportsNonPow2Samples = SDL_TRUE;
|
||||
|
||||
return SDL_TRUE; /* this audio target is available. */
|
||||
}
|
||||
|
||||
AudioBootStrap WASAPI_bootstrap = {
|
||||
"wasapi", "WASAPI", WASAPI_Init, SDL_FALSE
|
||||
};
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_WASAPI */
|
70
external/sdl/SDL/src/audio/wasapi/SDL_wasapi.h
vendored
Normal file
70
external/sdl/SDL/src/audio/wasapi/SDL_wasapi.h
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_wasapi_h_
|
||||
#define SDL_wasapi_h_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
struct SDL_PrivateAudioData
|
||||
{
|
||||
SDL_AtomicInt refcount;
|
||||
WCHAR *devid;
|
||||
WAVEFORMATEX *waveformat;
|
||||
IAudioClient *client;
|
||||
IAudioRenderClient *render;
|
||||
IAudioCaptureClient *capture;
|
||||
SDL_AudioStream *capturestream;
|
||||
HANDLE event;
|
||||
HANDLE task;
|
||||
SDL_bool coinitialized;
|
||||
int framesize;
|
||||
int default_device_generation;
|
||||
SDL_bool device_lost;
|
||||
void *activation_handler;
|
||||
SDL_AtomicInt just_activated;
|
||||
};
|
||||
|
||||
/* win32 and winrt implementations call into these. */
|
||||
int WASAPI_PrepDevice(SDL_AudioDevice *_this, const SDL_bool updatestream);
|
||||
void WASAPI_RefDevice(SDL_AudioDevice *_this);
|
||||
void WASAPI_UnrefDevice(SDL_AudioDevice *_this);
|
||||
|
||||
/* These are functions that are implemented differently for Windows vs WinRT. */
|
||||
int WASAPI_PlatformInit(void);
|
||||
void WASAPI_PlatformDeinit(void);
|
||||
void WASAPI_EnumerateEndpoints(void);
|
||||
int WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture);
|
||||
int WASAPI_ActivateDevice(SDL_AudioDevice *_this, const SDL_bool isrecovery);
|
||||
void WASAPI_PlatformThreadInit(SDL_AudioDevice *_this);
|
||||
void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *_this);
|
||||
void WASAPI_PlatformDeleteActivationHandler(void *handler);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SDL_wasapi_h_ */
|
149
external/sdl/SDL/src/audio/wasapi/SDL_wasapi_win32.c
vendored
Normal file
149
external/sdl/SDL/src/audio/wasapi/SDL_wasapi_win32.c
vendored
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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 is code that Windows uses to talk to WASAPI-related system APIs.
|
||||
This is for non-WinRT desktop apps. The C++/CX implementation of these
|
||||
functions, exclusive to WinRT, are in SDL_wasapi_winrt.cpp.
|
||||
The code in SDL_wasapi.c is used by both standard Windows and WinRT builds
|
||||
to deal with audio and calls into these functions. */
|
||||
|
||||
#if defined(SDL_AUDIO_DRIVER_WASAPI) && !defined(__WINRT__)
|
||||
|
||||
#include "../../core/windows/SDL_windows.h"
|
||||
#include "../../core/windows/SDL_immdevice.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../SDL_sysaudio.h"
|
||||
|
||||
#include <audioclient.h>
|
||||
|
||||
#include "SDL_wasapi.h"
|
||||
|
||||
/* handle to Avrt.dll--Vista and later!--for flagging the callback thread as "Pro Audio" (low latency). */
|
||||
static HMODULE libavrt = NULL;
|
||||
typedef HANDLE(WINAPI *pfnAvSetMmThreadCharacteristicsW)(LPCWSTR, LPDWORD);
|
||||
typedef BOOL(WINAPI *pfnAvRevertMmThreadCharacteristics)(HANDLE);
|
||||
static pfnAvSetMmThreadCharacteristicsW pAvSetMmThreadCharacteristicsW = NULL;
|
||||
static pfnAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics = NULL;
|
||||
|
||||
/* Some GUIDs we need to know without linking to libraries that aren't available before Vista. */
|
||||
static const IID SDL_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, { 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } };
|
||||
|
||||
int WASAPI_PlatformInit(void)
|
||||
{
|
||||
if (SDL_IMMDevice_Init() < 0) {
|
||||
return -1; /* This is set by SDL_IMMDevice_Init */
|
||||
}
|
||||
|
||||
libavrt = LoadLibrary(TEXT("avrt.dll")); /* this library is available in Vista and later. No WinXP, so have to LoadLibrary to use it for now! */
|
||||
if (libavrt) {
|
||||
pAvSetMmThreadCharacteristicsW = (pfnAvSetMmThreadCharacteristicsW)GetProcAddress(libavrt, "AvSetMmThreadCharacteristicsW");
|
||||
pAvRevertMmThreadCharacteristics = (pfnAvRevertMmThreadCharacteristics)GetProcAddress(libavrt, "AvRevertMmThreadCharacteristics");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WASAPI_PlatformDeinit(void)
|
||||
{
|
||||
if (libavrt) {
|
||||
FreeLibrary(libavrt);
|
||||
libavrt = NULL;
|
||||
}
|
||||
|
||||
pAvSetMmThreadCharacteristicsW = NULL;
|
||||
pAvRevertMmThreadCharacteristics = NULL;
|
||||
|
||||
SDL_IMMDevice_Quit();
|
||||
}
|
||||
|
||||
void WASAPI_PlatformThreadInit(SDL_AudioDevice *_this)
|
||||
{
|
||||
/* this thread uses COM. */
|
||||
if (SUCCEEDED(WIN_CoInitialize())) { /* can't report errors, hope it worked! */
|
||||
_this->hidden->coinitialized = SDL_TRUE;
|
||||
}
|
||||
|
||||
/* Set this thread to very high "Pro Audio" priority. */
|
||||
if (pAvSetMmThreadCharacteristicsW) {
|
||||
DWORD idx = 0;
|
||||
_this->hidden->task = pAvSetMmThreadCharacteristicsW(L"Pro Audio", &idx);
|
||||
}
|
||||
}
|
||||
|
||||
void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *_this)
|
||||
{
|
||||
/* Set this thread back to normal priority. */
|
||||
if (_this->hidden->task && pAvRevertMmThreadCharacteristics) {
|
||||
pAvRevertMmThreadCharacteristics(_this->hidden->task);
|
||||
_this->hidden->task = NULL;
|
||||
}
|
||||
|
||||
if (_this->hidden->coinitialized) {
|
||||
WIN_CoUninitialize();
|
||||
_this->hidden->coinitialized = SDL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
int WASAPI_ActivateDevice(SDL_AudioDevice *_this, const SDL_bool isrecovery)
|
||||
{
|
||||
IMMDevice *device = NULL;
|
||||
HRESULT ret;
|
||||
|
||||
if (SDL_IMMDevice_Get(_this->hidden->devid, &device, _this->iscapture) < 0) {
|
||||
_this->hidden->client = NULL;
|
||||
return -1; /* This is already set by SDL_IMMDevice_Get */
|
||||
}
|
||||
|
||||
/* this is not async in standard win32, yay! */
|
||||
ret = IMMDevice_Activate(device, &SDL_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&_this->hidden->client);
|
||||
IMMDevice_Release(device);
|
||||
|
||||
if (FAILED(ret)) {
|
||||
SDL_assert(_this->hidden->client == NULL);
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't activate audio endpoint", ret);
|
||||
}
|
||||
|
||||
SDL_assert(_this->hidden->client != NULL);
|
||||
if (WASAPI_PrepDevice(_this, isrecovery) == -1) { /* not async, fire it right away. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0; /* good to go. */
|
||||
}
|
||||
|
||||
void WASAPI_EnumerateEndpoints(void)
|
||||
{
|
||||
SDL_IMMDevice_EnumerateEndpoints(SDL_FALSE);
|
||||
}
|
||||
|
||||
int WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
|
||||
{
|
||||
return SDL_IMMDevice_GetDefaultAudioInfo(name, spec, iscapture);
|
||||
}
|
||||
|
||||
void WASAPI_PlatformDeleteActivationHandler(void *handler)
|
||||
{
|
||||
/* not asynchronous. */
|
||||
SDL_assert(!"This function should have only been called on WinRT.");
|
||||
}
|
||||
|
||||
#endif /* SDL_AUDIO_DRIVER_WASAPI && !defined(__WINRT__) */
|
427
external/sdl/SDL/src/audio/wasapi/SDL_wasapi_winrt.cpp
vendored
Normal file
427
external/sdl/SDL/src/audio/wasapi/SDL_wasapi_winrt.cpp
vendored
Normal file
@ -0,0 +1,427 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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 is C++/CX code that the WinRT port uses to talk to WASAPI-related
|
||||
// system APIs. The C implementation of these functions, for non-WinRT apps,
|
||||
// is in SDL_wasapi_win32.c. The code in SDL_wasapi.c is used by both standard
|
||||
// Windows and WinRT builds to deal with audio and calls into these functions.
|
||||
|
||||
#if defined(SDL_AUDIO_DRIVER_WASAPI) && defined(__WINRT__)
|
||||
|
||||
#include <Windows.h>
|
||||
#include <windows.ui.core.h>
|
||||
#include <windows.devices.enumeration.h>
|
||||
#include <windows.media.devices.h>
|
||||
#include <wrl/implements.h>
|
||||
#include <collection.h>
|
||||
|
||||
extern "C" {
|
||||
#include "../../core/windows/SDL_windows.h"
|
||||
#include "../SDL_audio_c.h"
|
||||
#include "../SDL_sysaudio.h"
|
||||
}
|
||||
|
||||
#define COBJMACROS
|
||||
#include <mmdeviceapi.h>
|
||||
#include <audioclient.h>
|
||||
|
||||
#include "SDL_wasapi.h"
|
||||
|
||||
using namespace Windows::Devices::Enumeration;
|
||||
using namespace Windows::Media::Devices;
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
static Platform::String ^ SDL_PKEY_AudioEngine_DeviceFormat = L"{f19f064d-082c-4e27-bc73-6882a1bb8e4c} 0";
|
||||
|
||||
static void WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid);
|
||||
static void WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid);
|
||||
extern "C" {
|
||||
SDL_AtomicInt SDL_IMMDevice_DefaultPlaybackGeneration;
|
||||
SDL_AtomicInt SDL_IMMDevice_DefaultCaptureGeneration;
|
||||
}
|
||||
|
||||
/* This is a list of device id strings we have inflight, so we have consistent pointers to the same device. */
|
||||
typedef struct DevIdList
|
||||
{
|
||||
WCHAR *str;
|
||||
struct DevIdList *next;
|
||||
} DevIdList;
|
||||
|
||||
static DevIdList *deviceid_list = NULL;
|
||||
|
||||
class SDL_WasapiDeviceEventHandler
|
||||
{
|
||||
public:
|
||||
SDL_WasapiDeviceEventHandler(const SDL_bool _iscapture);
|
||||
~SDL_WasapiDeviceEventHandler();
|
||||
void OnDeviceAdded(DeviceWatcher ^ sender, DeviceInformation ^ args);
|
||||
void OnDeviceRemoved(DeviceWatcher ^ sender, DeviceInformationUpdate ^ args);
|
||||
void OnDeviceUpdated(DeviceWatcher ^ sender, DeviceInformationUpdate ^ args);
|
||||
void OnEnumerationCompleted(DeviceWatcher ^ sender, Platform::Object ^ args);
|
||||
void OnDefaultRenderDeviceChanged(Platform::Object ^ sender, DefaultAudioRenderDeviceChangedEventArgs ^ args);
|
||||
void OnDefaultCaptureDeviceChanged(Platform::Object ^ sender, DefaultAudioCaptureDeviceChangedEventArgs ^ args);
|
||||
SDL_Semaphore *completed;
|
||||
|
||||
private:
|
||||
const SDL_bool iscapture;
|
||||
DeviceWatcher ^ watcher;
|
||||
Windows::Foundation::EventRegistrationToken added_handler;
|
||||
Windows::Foundation::EventRegistrationToken removed_handler;
|
||||
Windows::Foundation::EventRegistrationToken updated_handler;
|
||||
Windows::Foundation::EventRegistrationToken completed_handler;
|
||||
Windows::Foundation::EventRegistrationToken default_changed_handler;
|
||||
};
|
||||
|
||||
SDL_WasapiDeviceEventHandler::SDL_WasapiDeviceEventHandler(const SDL_bool _iscapture)
|
||||
: iscapture(_iscapture), completed(SDL_CreateSemaphore(0))
|
||||
{
|
||||
if (!completed)
|
||||
return; // uhoh.
|
||||
|
||||
Platform::String ^ selector = _iscapture ? MediaDevice::GetAudioCaptureSelector() : MediaDevice::GetAudioRenderSelector();
|
||||
Platform::Collections::Vector<Platform::String ^> properties;
|
||||
properties.Append(SDL_PKEY_AudioEngine_DeviceFormat);
|
||||
watcher = DeviceInformation::CreateWatcher(selector, properties.GetView());
|
||||
if (!watcher)
|
||||
return; // uhoh.
|
||||
|
||||
// !!! FIXME: this doesn't need a lambda here, I think, if I make SDL_WasapiDeviceEventHandler a proper C++/CX class. --ryan.
|
||||
added_handler = watcher->Added += ref new TypedEventHandler<DeviceWatcher ^, DeviceInformation ^>([this](DeviceWatcher ^ sender, DeviceInformation ^ args) { OnDeviceAdded(sender, args); });
|
||||
removed_handler = watcher->Removed += ref new TypedEventHandler<DeviceWatcher ^, DeviceInformationUpdate ^>([this](DeviceWatcher ^ sender, DeviceInformationUpdate ^ args) { OnDeviceRemoved(sender, args); });
|
||||
updated_handler = watcher->Updated += ref new TypedEventHandler<DeviceWatcher ^, DeviceInformationUpdate ^>([this](DeviceWatcher ^ sender, DeviceInformationUpdate ^ args) { OnDeviceUpdated(sender, args); });
|
||||
completed_handler = watcher->EnumerationCompleted += ref new TypedEventHandler<DeviceWatcher ^, Platform::Object ^>([this](DeviceWatcher ^ sender, Platform::Object ^ args) { OnEnumerationCompleted(sender, args); });
|
||||
if (iscapture) {
|
||||
default_changed_handler = MediaDevice::DefaultAudioCaptureDeviceChanged += ref new TypedEventHandler<Platform::Object ^, DefaultAudioCaptureDeviceChangedEventArgs ^>([this](Platform::Object ^ sender, DefaultAudioCaptureDeviceChangedEventArgs ^ args) { OnDefaultCaptureDeviceChanged(sender, args); });
|
||||
} else {
|
||||
default_changed_handler = MediaDevice::DefaultAudioRenderDeviceChanged += ref new TypedEventHandler<Platform::Object ^, DefaultAudioRenderDeviceChangedEventArgs ^>([this](Platform::Object ^ sender, DefaultAudioRenderDeviceChangedEventArgs ^ args) { OnDefaultRenderDeviceChanged(sender, args); });
|
||||
}
|
||||
watcher->Start();
|
||||
}
|
||||
|
||||
SDL_WasapiDeviceEventHandler::~SDL_WasapiDeviceEventHandler()
|
||||
{
|
||||
if (watcher) {
|
||||
watcher->Added -= added_handler;
|
||||
watcher->Removed -= removed_handler;
|
||||
watcher->Updated -= updated_handler;
|
||||
watcher->EnumerationCompleted -= completed_handler;
|
||||
watcher->Stop();
|
||||
watcher = nullptr;
|
||||
}
|
||||
if (completed) {
|
||||
SDL_DestroySemaphore(completed);
|
||||
completed = nullptr;
|
||||
}
|
||||
|
||||
if (iscapture) {
|
||||
MediaDevice::DefaultAudioCaptureDeviceChanged -= default_changed_handler;
|
||||
} else {
|
||||
MediaDevice::DefaultAudioRenderDeviceChanged -= default_changed_handler;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_WasapiDeviceEventHandler::OnDeviceAdded(DeviceWatcher ^ sender, DeviceInformation ^ info)
|
||||
{
|
||||
SDL_assert(sender == this->watcher);
|
||||
char *utf8dev = WIN_StringToUTF8(info->Name->Data());
|
||||
if (utf8dev) {
|
||||
WAVEFORMATEXTENSIBLE fmt;
|
||||
Platform::Object ^ obj = info->Properties->Lookup(SDL_PKEY_AudioEngine_DeviceFormat);
|
||||
if (obj) {
|
||||
IPropertyValue ^ property = (IPropertyValue ^) obj;
|
||||
Platform::Array<unsigned char> ^ data;
|
||||
property->GetUInt8Array(&data);
|
||||
SDL_memcpy(&fmt, data->Data, SDL_min(data->Length, sizeof(WAVEFORMATEXTENSIBLE)));
|
||||
} else {
|
||||
SDL_zero(fmt);
|
||||
}
|
||||
|
||||
WASAPI_AddDevice(this->iscapture, utf8dev, &fmt, info->Id->Data());
|
||||
SDL_free(utf8dev);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_WasapiDeviceEventHandler::OnDeviceRemoved(DeviceWatcher ^ sender, DeviceInformationUpdate ^ info)
|
||||
{
|
||||
SDL_assert(sender == this->watcher);
|
||||
WASAPI_RemoveDevice(this->iscapture, info->Id->Data());
|
||||
}
|
||||
|
||||
void SDL_WasapiDeviceEventHandler::OnDeviceUpdated(DeviceWatcher ^ sender, DeviceInformationUpdate ^ args)
|
||||
{
|
||||
SDL_assert(sender == this->watcher);
|
||||
}
|
||||
|
||||
void SDL_WasapiDeviceEventHandler::OnEnumerationCompleted(DeviceWatcher ^ sender, Platform::Object ^ args)
|
||||
{
|
||||
SDL_assert(sender == this->watcher);
|
||||
SDL_PostSemaphore(this->completed);
|
||||
}
|
||||
|
||||
void SDL_WasapiDeviceEventHandler::OnDefaultRenderDeviceChanged(Platform::Object ^ sender, DefaultAudioRenderDeviceChangedEventArgs ^ args)
|
||||
{
|
||||
SDL_assert(!this->iscapture);
|
||||
SDL_AtomicAdd(&SDL_IMMDevice_DefaultPlaybackGeneration, 1);
|
||||
}
|
||||
|
||||
void SDL_WasapiDeviceEventHandler::OnDefaultCaptureDeviceChanged(Platform::Object ^ sender, DefaultAudioCaptureDeviceChangedEventArgs ^ args)
|
||||
{
|
||||
SDL_assert(this->iscapture);
|
||||
SDL_AtomicAdd(&SDL_IMMDevice_DefaultCaptureGeneration, 1);
|
||||
}
|
||||
|
||||
static SDL_WasapiDeviceEventHandler *playback_device_event_handler;
|
||||
static SDL_WasapiDeviceEventHandler *capture_device_event_handler;
|
||||
|
||||
int WASAPI_PlatformInit(void)
|
||||
{
|
||||
SDL_AtomicSet(&SDL_IMMDevice_DefaultPlaybackGeneration, 1);
|
||||
SDL_AtomicSet(&SDL_IMMDevice_DefaultCaptureGeneration, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WASAPI_PlatformDeinit(void)
|
||||
{
|
||||
DevIdList *devidlist;
|
||||
DevIdList *next;
|
||||
|
||||
delete playback_device_event_handler;
|
||||
playback_device_event_handler = nullptr;
|
||||
delete capture_device_event_handler;
|
||||
capture_device_event_handler = nullptr;
|
||||
|
||||
for (devidlist = deviceid_list; devidlist; devidlist = next) {
|
||||
next = devidlist->next;
|
||||
SDL_free(devidlist->str);
|
||||
SDL_free(devidlist);
|
||||
}
|
||||
deviceid_list = NULL;
|
||||
}
|
||||
|
||||
void WASAPI_EnumerateEndpoints(void)
|
||||
{
|
||||
// DeviceWatchers will fire an Added event for each existing device at
|
||||
// startup, so we don't need to enumerate them separately before
|
||||
// listening for updates.
|
||||
playback_device_event_handler = new SDL_WasapiDeviceEventHandler(SDL_FALSE);
|
||||
capture_device_event_handler = new SDL_WasapiDeviceEventHandler(SDL_TRUE);
|
||||
SDL_WaitSemaphore(playback_device_event_handler->completed);
|
||||
SDL_WaitSemaphore(capture_device_event_handler->completed);
|
||||
}
|
||||
|
||||
struct SDL_WasapiActivationHandler : public RuntimeClass<RuntimeClassFlags<ClassicCom>, FtmBase, IActivateAudioInterfaceCompletionHandler>
|
||||
{
|
||||
SDL_WasapiActivationHandler() : device(nullptr) {}
|
||||
STDMETHOD(ActivateCompleted)
|
||||
(IActivateAudioInterfaceAsyncOperation *operation);
|
||||
SDL_AudioDevice *device;
|
||||
};
|
||||
|
||||
HRESULT
|
||||
SDL_WasapiActivationHandler::ActivateCompleted(IActivateAudioInterfaceAsyncOperation *async)
|
||||
{
|
||||
// Just set a flag, since we're probably in a different thread. We'll pick it up and init everything on our own thread to prevent races.
|
||||
SDL_AtomicSet(&device->hidden->just_activated, 1);
|
||||
WASAPI_UnrefDevice(device);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void WASAPI_PlatformDeleteActivationHandler(void *handler)
|
||||
{
|
||||
((SDL_WasapiActivationHandler *)handler)->Release();
|
||||
}
|
||||
|
||||
int WASAPI_GetDefaultAudioInfo(char **name, SDL_AudioSpec *spec, int iscapture)
|
||||
{
|
||||
return SDL_Unsupported();
|
||||
}
|
||||
|
||||
int WASAPI_ActivateDevice(SDL_AudioDevice *_this, const SDL_bool isrecovery)
|
||||
{
|
||||
LPCWSTR devid = _this->hidden->devid;
|
||||
Platform::String ^ defdevid;
|
||||
|
||||
if (devid == nullptr) {
|
||||
defdevid = _this->iscapture ? MediaDevice::GetDefaultAudioCaptureId(AudioDeviceRole::Default) : MediaDevice::GetDefaultAudioRenderId(AudioDeviceRole::Default);
|
||||
if (defdevid) {
|
||||
devid = defdevid->Data();
|
||||
}
|
||||
}
|
||||
|
||||
SDL_AtomicSet(&_this->hidden->just_activated, 0);
|
||||
|
||||
ComPtr<SDL_WasapiActivationHandler> handler = Make<SDL_WasapiActivationHandler>();
|
||||
if (handler == nullptr) {
|
||||
return SDL_SetError("Failed to allocate WASAPI activation handler");
|
||||
}
|
||||
|
||||
handler.Get()->AddRef(); // we hold a reference after ComPtr destructs on return, causing a Release, and Release ourselves in WASAPI_PlatformDeleteActivationHandler(), etc.
|
||||
handler.Get()->device = _this;
|
||||
_this->hidden->activation_handler = handler.Get();
|
||||
|
||||
WASAPI_RefDevice(_this); /* completion handler will unref it. */
|
||||
IActivateAudioInterfaceAsyncOperation *async = nullptr;
|
||||
const HRESULT ret = ActivateAudioInterfaceAsync(devid, __uuidof(IAudioClient), nullptr, handler.Get(), &async);
|
||||
|
||||
if (FAILED(ret) || async == nullptr) {
|
||||
if (async != nullptr) {
|
||||
async->Release();
|
||||
}
|
||||
handler.Get()->Release();
|
||||
WASAPI_UnrefDevice(_this);
|
||||
return WIN_SetErrorFromHRESULT("WASAPI can't activate requested audio endpoint", ret);
|
||||
}
|
||||
|
||||
/* Spin until the async operation is complete.
|
||||
* If we don't PrepDevice before leaving this function, the bug list gets LONG:
|
||||
* - device.spec is not filled with the correct information
|
||||
* - The 'obtained' spec will be wrong for ALLOW_CHANGE properties
|
||||
* - SDL_AudioStreams will/will not be allocated at the right time
|
||||
* - SDL_assert(device->callbackspec.size == device->spec.size) will fail
|
||||
* - When the assert is ignored, skipping or a buffer overflow will occur
|
||||
*/
|
||||
while (!SDL_AtomicCAS(&_this->hidden->just_activated, 1, 0)) {
|
||||
SDL_Delay(1);
|
||||
}
|
||||
|
||||
HRESULT activateRes = S_OK;
|
||||
IUnknown *iunknown = nullptr;
|
||||
const HRESULT getActivateRes = async->GetActivateResult(&activateRes, &iunknown);
|
||||
async->Release();
|
||||
if (FAILED(getActivateRes)) {
|
||||
return WIN_SetErrorFromHRESULT("Failed to get WASAPI activate result", getActivateRes);
|
||||
} else if (FAILED(activateRes)) {
|
||||
return WIN_SetErrorFromHRESULT("Failed to activate WASAPI device", activateRes);
|
||||
}
|
||||
|
||||
iunknown->QueryInterface(IID_PPV_ARGS(&_this->hidden->client));
|
||||
if (!_this->hidden->client) {
|
||||
return SDL_SetError("Failed to query WASAPI client interface");
|
||||
}
|
||||
|
||||
if (WASAPI_PrepDevice(_this, isrecovery) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WASAPI_PlatformThreadInit(SDL_AudioDevice *_this)
|
||||
{
|
||||
// !!! FIXME: set this thread to "Pro Audio" priority.
|
||||
}
|
||||
|
||||
void WASAPI_PlatformThreadDeinit(SDL_AudioDevice *_this)
|
||||
{
|
||||
// !!! FIXME: set this thread to "Pro Audio" priority.
|
||||
}
|
||||
|
||||
/* Everything below was copied from SDL_wasapi.c, before it got moved to SDL_immdevice.c! */
|
||||
|
||||
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 } };
|
||||
|
||||
extern "C" SDL_AudioFormat
|
||||
WaveFormatToSDLFormat(WAVEFORMATEX *waveformat)
|
||||
{
|
||||
if ((waveformat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) && (waveformat->wBitsPerSample == 32)) {
|
||||
return SDL_AUDIO_F32SYS;
|
||||
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 16)) {
|
||||
return SDL_AUDIO_S16SYS;
|
||||
} else if ((waveformat->wFormatTag == WAVE_FORMAT_PCM) && (waveformat->wBitsPerSample == 32)) {
|
||||
return SDL_AUDIO_S32SYS;
|
||||
} 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_F32SYS;
|
||||
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 16)) {
|
||||
return SDL_AUDIO_S16SYS;
|
||||
} else if ((SDL_memcmp(&ext->SubFormat, &SDL_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)) == 0) && (waveformat->wBitsPerSample == 32)) {
|
||||
return SDL_AUDIO_S32SYS;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void WASAPI_RemoveDevice(const SDL_bool iscapture, LPCWSTR devid)
|
||||
{
|
||||
DevIdList *i;
|
||||
DevIdList *next;
|
||||
DevIdList *prev = NULL;
|
||||
for (i = deviceid_list; i; i = next) {
|
||||
next = i->next;
|
||||
if (SDL_wcscmp(i->str, devid) == 0) {
|
||||
if (prev) {
|
||||
prev->next = next;
|
||||
} else {
|
||||
deviceid_list = next;
|
||||
}
|
||||
SDL_RemoveAudioDevice(iscapture, i->str);
|
||||
SDL_free(i->str);
|
||||
SDL_free(i);
|
||||
} else {
|
||||
prev = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void WASAPI_AddDevice(const SDL_bool iscapture, const char *devname, WAVEFORMATEXTENSIBLE *fmt, LPCWSTR devid)
|
||||
{
|
||||
DevIdList *devidlist;
|
||||
SDL_AudioSpec spec;
|
||||
|
||||
/* 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...?) */
|
||||
|
||||
/* see if we already have this one. */
|
||||
for (devidlist = deviceid_list; devidlist; devidlist = devidlist->next) {
|
||||
if (SDL_wcscmp(devidlist->str, devid) == 0) {
|
||||
return; /* we already have this. */
|
||||
}
|
||||
}
|
||||
|
||||
devidlist = (DevIdList *)SDL_malloc(sizeof(*devidlist));
|
||||
if (devidlist == NULL) {
|
||||
return; /* oh well. */
|
||||
}
|
||||
|
||||
devid = SDL_wcsdup(devid);
|
||||
if (!devid) {
|
||||
SDL_free(devidlist);
|
||||
return; /* oh well. */
|
||||
}
|
||||
|
||||
devidlist->str = (WCHAR *)devid;
|
||||
devidlist->next = deviceid_list;
|
||||
deviceid_list = devidlist;
|
||||
|
||||
SDL_zero(spec);
|
||||
spec.channels = (Uint8)fmt->Format.nChannels;
|
||||
spec.freq = fmt->Format.nSamplesPerSec;
|
||||
spec.format = WaveFormatToSDLFormat((WAVEFORMATEX *)fmt);
|
||||
SDL_AddAudioDevice(iscapture, devname, &spec, (void *)devid);
|
||||
}
|
||||
|
||||
#endif // SDL_AUDIO_DRIVER_WASAPI && defined(__WINRT__)
|
45
external/sdl/SDL/src/core/SDL_runapp.c
vendored
Normal file
45
external/sdl/SDL/src/core/SDL_runapp.c
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
/* Most platforms that use/need SDL_main have their own SDL_RunApp() implementation.
|
||||
* If not, you can special case it here by appending || defined(__YOUR_PLATFORM__) */
|
||||
#if ( !defined(SDL_MAIN_NEEDED) && !defined(SDL_MAIN_AVAILABLE) ) || defined(__ANDROID__)
|
||||
|
||||
DECLSPEC int
|
||||
SDL_RunApp(int argc, char* argv[], SDL_main_func mainFunction, void * reserved)
|
||||
{
|
||||
char empty[1] = {0};
|
||||
char* argvdummy[2] = { empty, NULL };
|
||||
|
||||
(void)reserved;
|
||||
|
||||
if(argv == NULL)
|
||||
{
|
||||
argc = 0;
|
||||
/* make sure argv isn't NULL, in case some user code doesn't like that */
|
||||
argv = argvdummy;
|
||||
}
|
||||
|
||||
return mainFunction(argc, argv);
|
||||
}
|
||||
|
||||
#endif
|
2763
external/sdl/SDL/src/core/android/SDL_android.c
vendored
Normal file
2763
external/sdl/SDL/src/core/android/SDL_android.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
146
external/sdl/SDL/src/core/android/SDL_android.h
vendored
Normal file
146
external/sdl/SDL/src/core/android/SDL_android.h
vendored
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
/* Set up for C function definitions, even when using C++ */
|
||||
#ifdef __cplusplus
|
||||
/* *INDENT-OFF* */
|
||||
extern "C" {
|
||||
/* *INDENT-ON* */
|
||||
#endif
|
||||
|
||||
#include <EGL/eglplatform.h>
|
||||
#include <android/native_window_jni.h>
|
||||
|
||||
/* Interface from the SDL library into the Android Java activity */
|
||||
extern void Android_JNI_SetActivityTitle(const char *title);
|
||||
extern void Android_JNI_SetWindowStyle(SDL_bool fullscreen);
|
||||
extern void Android_JNI_SetOrientation(int w, int h, int resizable, const char *hint);
|
||||
extern void Android_JNI_MinizeWindow(void);
|
||||
extern SDL_bool Android_JNI_ShouldMinimizeOnFocusLoss(void);
|
||||
|
||||
extern SDL_bool Android_JNI_GetAccelerometerValues(float values[3]);
|
||||
extern void Android_JNI_ShowTextInput(SDL_Rect *inputRect);
|
||||
extern void Android_JNI_HideTextInput(void);
|
||||
extern SDL_bool Android_JNI_IsScreenKeyboardShown(void);
|
||||
extern ANativeWindow *Android_JNI_GetNativeWindow(void);
|
||||
|
||||
extern SDL_DisplayOrientation Android_JNI_GetDisplayNaturalOrientation(void);
|
||||
extern SDL_DisplayOrientation Android_JNI_GetDisplayCurrentOrientation(void);
|
||||
|
||||
/* Audio support */
|
||||
extern void Android_DetectDevices(void);
|
||||
extern int Android_JNI_OpenAudioDevice(int iscapture, int device_id, SDL_AudioSpec *spec);
|
||||
extern void *Android_JNI_GetAudioBuffer(void);
|
||||
extern void Android_JNI_WriteAudioBuffer(void);
|
||||
extern int Android_JNI_CaptureAudioBuffer(void *buffer, int buflen);
|
||||
extern void Android_JNI_FlushCapturedAudio(void);
|
||||
extern void Android_JNI_CloseAudioDevice(const int iscapture);
|
||||
extern void Android_JNI_AudioSetThreadPriority(int iscapture, int device_id);
|
||||
|
||||
/* Detecting device type */
|
||||
extern SDL_bool Android_IsDeXMode(void);
|
||||
extern SDL_bool Android_IsChromebook(void);
|
||||
|
||||
int Android_JNI_FileOpen(SDL_RWops *ctx, const char *fileName, const char *mode);
|
||||
Sint64 Android_JNI_FileSize(SDL_RWops *ctx);
|
||||
Sint64 Android_JNI_FileSeek(SDL_RWops *ctx, Sint64 offset, int whence);
|
||||
Sint64 Android_JNI_FileRead(SDL_RWops *ctx, void *buffer, Sint64 size);
|
||||
Sint64 Android_JNI_FileWrite(SDL_RWops *ctx, const void *buffer, Sint64 size);
|
||||
int Android_JNI_FileClose(SDL_RWops *ctx);
|
||||
|
||||
/* Environment support */
|
||||
void Android_JNI_GetManifestEnvironmentVariables(void);
|
||||
|
||||
/* Clipboard support */
|
||||
int Android_JNI_SetClipboardText(const char *text);
|
||||
char *Android_JNI_GetClipboardText(void);
|
||||
SDL_bool Android_JNI_HasClipboardText(void);
|
||||
|
||||
/* Power support */
|
||||
int Android_JNI_GetPowerInfo(int *plugged, int *charged, int *battery, int *seconds, int *percent);
|
||||
|
||||
/* Joystick support */
|
||||
void Android_JNI_PollInputDevices(void);
|
||||
|
||||
/* Haptic support */
|
||||
void Android_JNI_PollHapticDevices(void);
|
||||
void Android_JNI_HapticRun(int device_id, float intensity, int length);
|
||||
void Android_JNI_HapticStop(int device_id);
|
||||
|
||||
/* Video */
|
||||
int Android_JNI_SuspendScreenSaver(SDL_bool suspend);
|
||||
|
||||
/* Touch support */
|
||||
void Android_JNI_InitTouch(void);
|
||||
|
||||
/* Threads */
|
||||
#include <jni.h>
|
||||
JNIEnv *Android_JNI_GetEnv(void);
|
||||
int Android_JNI_SetupThread(void);
|
||||
|
||||
/* Locale */
|
||||
int Android_JNI_GetLocale(char *buf, size_t buflen);
|
||||
|
||||
/* Generic messages */
|
||||
int Android_JNI_SendMessage(int command, int param);
|
||||
|
||||
/* Init */
|
||||
JNIEXPORT void JNICALL SDL_Android_Init(JNIEnv *mEnv, jclass cls);
|
||||
|
||||
/* MessageBox */
|
||||
int Android_JNI_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid);
|
||||
|
||||
/* Cursor support */
|
||||
int Android_JNI_CreateCustomCursor(SDL_Surface *surface, int hot_x, int hot_y);
|
||||
void Android_JNI_DestroyCustomCursor(int cursorID);
|
||||
SDL_bool Android_JNI_SetCustomCursor(int cursorID);
|
||||
SDL_bool Android_JNI_SetSystemCursor(int cursorID);
|
||||
|
||||
/* Relative mouse support */
|
||||
SDL_bool Android_JNI_SupportsRelativeMouse(void);
|
||||
SDL_bool Android_JNI_SetRelativeMouseEnabled(SDL_bool enabled);
|
||||
|
||||
/* Request permission */
|
||||
SDL_bool Android_JNI_RequestPermission(const char *permission);
|
||||
|
||||
/* Show toast notification */
|
||||
int Android_JNI_ShowToast(const char *message, int duration, int gravity, int xOffset, int yOffset);
|
||||
|
||||
int Android_JNI_OpenURL(const char *url);
|
||||
|
||||
int SDL_GetAndroidSDKVersion(void);
|
||||
|
||||
SDL_bool SDL_IsAndroidTablet(void);
|
||||
SDL_bool SDL_IsAndroidTV(void);
|
||||
SDL_bool SDL_IsChromebook(void);
|
||||
SDL_bool SDL_IsDeXMode(void);
|
||||
|
||||
void Android_ActivityMutex_Lock(void);
|
||||
void Android_ActivityMutex_Unlock(void);
|
||||
void Android_ActivityMutex_Lock_Running(void);
|
||||
|
||||
/* Ends C function definitions when using C++ */
|
||||
#ifdef __cplusplus
|
||||
/* *INDENT-OFF* */
|
||||
}
|
||||
/* *INDENT-ON* */
|
||||
#endif
|
167
external/sdl/SDL/src/core/freebsd/SDL_evdev_kbd_default_keyaccmap.h
vendored
Normal file
167
external/sdl/SDL/src/core/freebsd/SDL_evdev_kbd_default_keyaccmap.h
vendored
Normal file
@ -0,0 +1,167 @@
|
||||
#include <sys/kbio.h>
|
||||
|
||||
/* *INDENT-OFF* */ /* clang-format off */
|
||||
/*
|
||||
* Automatically generated from /usr/share/vt/keymaps/us.acc.kbd.
|
||||
* DO NOT EDIT!
|
||||
*/
|
||||
static keymap_t keymap_default_us_acc = { 0x6d, {
|
||||
/* alt
|
||||
* scan cntrl alt alt cntrl
|
||||
* code base shift cntrl shift alt shift cntrl shift spcl flgs
|
||||
* ---------------------------------------------------------------------------
|
||||
*/
|
||||
/*00*/{{ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, }, 0xFF,0x00 },
|
||||
/*01*/{{ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, DBG, }, 0x03,0x00 },
|
||||
/*02*/{{ '1', '!', NOP, NOP, '1', '!', NOP, NOP, }, 0x33,0x00 },
|
||||
/*03*/{{ '2', '@', 0x00, 0x00, '2', '@', 0x00, 0x00, }, 0x00,0x00 },
|
||||
/*04*/{{ '3', '#', NOP, NOP, '3', '#', NOP, NOP, }, 0x33,0x00 },
|
||||
/*05*/{{ '4', '$', NOP, NOP, '4', '$', NOP, NOP, }, 0x33,0x00 },
|
||||
/*06*/{{ '5', '%', NOP, NOP, '5', '%', NOP, NOP, }, 0x33,0x00 },
|
||||
/*07*/{{ '6', '^', 0x1E, 0x1E, '6', DCIR, 0x1E, 0x1E, }, 0x04,0x00 },
|
||||
/*08*/{{ '7', '&', NOP, NOP, '7', '&', NOP, NOP, }, 0x33,0x00 },
|
||||
/*09*/{{ '8', '*', NOP, NOP, '8', DRIN, NOP, NOP, }, 0x37,0x00 },
|
||||
/*0a*/{{ '9', '(', NOP, NOP, '9', '(', NOP, NOP, }, 0x33,0x00 },
|
||||
/*0b*/{{ '0', ')', NOP, NOP, '0', ')', NOP, NOP, }, 0x33,0x00 },
|
||||
/*0c*/{{ '-', '_', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, }, 0x00,0x00 },
|
||||
/*0d*/{{ '=', '+', NOP, NOP, '=', '+', NOP, NOP, }, 0x33,0x00 },
|
||||
/*0e*/{{ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, }, 0x00,0x00 },
|
||||
/*0f*/{{ 0x09, BTAB, NEXT, NEXT, 0x09, BTAB, NOP, NOP, }, 0x77,0x00 },
|
||||
/*10*/{{ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, }, 0x00,0x01 },
|
||||
/*11*/{{ 'w', 'W', 0x17, 0x17, 'w', 'W', 0x17, 0x17, }, 0x00,0x01 },
|
||||
/*12*/{{ 'e', 'E', 0x05, 0x05, 'e', 'E', 0x05, 0x05, }, 0x00,0x01 },
|
||||
/*13*/{{ 'r', 'R', 0x12, 0x12, 'r', 'R', 0x12, 0x12, }, 0x00,0x01 },
|
||||
/*14*/{{ 't', 'T', 0x14, 0x14, 't', 'T', 0x14, 0x14, }, 0x00,0x01 },
|
||||
/*15*/{{ 'y', 'Y', 0x19, 0x19, 'y', 'Y', 0x19, 0x19, }, 0x00,0x01 },
|
||||
/*16*/{{ 'u', 'U', 0x15, 0x15, 'u', 'U', 0x15, 0x15, }, 0x00,0x01 },
|
||||
/*17*/{{ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, }, 0x00,0x01 },
|
||||
/*18*/{{ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, }, 0x00,0x01 },
|
||||
/*19*/{{ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, }, 0x00,0x01 },
|
||||
/*1a*/{{ '[', '{', 0x1B, 0x1B, '[', '{', 0x1B, 0x1B, }, 0x00,0x00 },
|
||||
/*1b*/{{ ']', '}', 0x1D, 0x1D, ']', '}', 0x1D, 0x1D, }, 0x00,0x00 },
|
||||
/*1c*/{{ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, }, 0x00,0x00 },
|
||||
/*1d*/{{ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, }, 0xFF,0x00 },
|
||||
/*1e*/{{ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, }, 0x00,0x01 },
|
||||
/*1f*/{{ 's', 'S', 0x13, 0x13, 's', 'S', 0x13, 0x13, }, 0x00,0x01 },
|
||||
/*20*/{{ 'd', 'D', 0x04, 0x04, 'd', 'D', 0x04, 0x04, }, 0x00,0x01 },
|
||||
/*21*/{{ 'f', 'F', 0x06, 0x06, 'f', 'F', 0x06, 0x06, }, 0x00,0x01 },
|
||||
/*22*/{{ 'g', 'G', 0x07, 0x07, 'g', 'G', 0x07, 0x07, }, 0x00,0x01 },
|
||||
/*23*/{{ 'h', 'H', 0x08, 0x08, 'h', 'H', 0x08, 0x08, }, 0x00,0x01 },
|
||||
/*24*/{{ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, }, 0x00,0x01 },
|
||||
/*25*/{{ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, }, 0x00,0x01 },
|
||||
/*26*/{{ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, }, 0x00,0x01 },
|
||||
/*27*/{{ ';', ':', NOP, NOP, ';', ':', NOP, NOP, }, 0x33,0x00 },
|
||||
/*28*/{{ '\'', '"', NOP, NOP, DACU, DUML, NOP, NOP, }, 0x3F,0x00 },
|
||||
/*29*/{{ '`', '~', NOP, NOP, DGRA, DTIL, NOP, NOP, }, 0x3F,0x00 },
|
||||
/*2a*/{{ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, }, 0xFF,0x00 },
|
||||
/*2b*/{{ '\\', '|', 0x1C, 0x1C, '\\', '|', 0x1C, 0x1C, }, 0x00,0x00 },
|
||||
/*2c*/{{ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, }, 0x00,0x01 },
|
||||
/*2d*/{{ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, }, 0x00,0x01 },
|
||||
/*2e*/{{ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, }, 0x00,0x01 },
|
||||
/*2f*/{{ 'v', 'V', 0x16, 0x16, 'v', 'V', 0x16, 0x16, }, 0x00,0x01 },
|
||||
/*30*/{{ 'b', 'B', 0x02, 0x02, 'b', 'B', 0x02, 0x02, }, 0x00,0x01 },
|
||||
/*31*/{{ 'n', 'N', 0x0E, 0x0E, 'n', 'N', 0x0E, 0x0E, }, 0x00,0x01 },
|
||||
/*32*/{{ 'm', 'M', 0x0D, 0x0D, 'm', 'M', 0x0D, 0x0D, }, 0x00,0x01 },
|
||||
/*33*/{{ ',', '<', NOP, NOP, DCED, '<', NOP, NOP, }, 0x3B,0x00 },
|
||||
/*34*/{{ '.', '>', NOP, NOP, '.', '>', NOP, NOP, }, 0x33,0x00 },
|
||||
/*35*/{{ '/', '?', NOP, NOP, '/', '?', NOP, NOP, }, 0x33,0x00 },
|
||||
/*36*/{{ RSH, RSH, RSH, RSH, RSH, RSH, RSH, RSH, }, 0xFF,0x00 },
|
||||
/*37*/{{ '*', '*', '*', '*', '*', '*', '*', '*', }, 0x00,0x00 },
|
||||
/*38*/{{ LALT, LALT, LALT, LALT, LALT, LALT, LALT, LALT, }, 0xFF,0x00 },
|
||||
/*39*/{{ ' ', ' ', 0x00, 0x00, ' ', ' ', SUSP, SUSP, }, 0x03,0x00 },
|
||||
/*3a*/{{ CLK, CLK, CLK, CLK, CLK, CLK, CLK, CLK, }, 0xFF,0x00 },
|
||||
/*3b*/{{ F( 1), F(13), F(25), F(37), S( 1), S(11), S( 1), S(11),}, 0xFF,0x00 },
|
||||
/*3c*/{{ F( 2), F(14), F(26), F(38), S( 2), S(12), S( 2), S(12),}, 0xFF,0x00 },
|
||||
/*3d*/{{ F( 3), F(15), F(27), F(39), S( 3), S(13), S( 3), S(13),}, 0xFF,0x00 },
|
||||
/*3e*/{{ F( 4), F(16), F(28), F(40), S( 4), S(14), S( 4), S(14),}, 0xFF,0x00 },
|
||||
/*3f*/{{ F( 5), F(17), F(29), F(41), S( 5), S(15), S( 5), S(15),}, 0xFF,0x00 },
|
||||
/*40*/{{ F( 6), F(18), F(30), F(42), S( 6), S(16), S( 6), S(16),}, 0xFF,0x00 },
|
||||
/*41*/{{ F( 7), F(19), F(31), F(43), S( 7), S( 7), S( 7), S( 7),}, 0xFF,0x00 },
|
||||
/*42*/{{ F( 8), F(20), F(32), F(44), S( 8), S( 8), S( 8), S( 8),}, 0xFF,0x00 },
|
||||
/*43*/{{ F( 9), F(21), F(33), F(45), S( 9), S( 9), S( 9), S( 9),}, 0xFF,0x00 },
|
||||
/*44*/{{ F(10), F(22), F(34), F(46), S(10), S(10), S(10), S(10),}, 0xFF,0x00 },
|
||||
/*45*/{{ NLK, NLK, NLK, NLK, NLK, NLK, NLK, NLK, }, 0xFF,0x00 },
|
||||
/*46*/{{ SLK, SLK, SLK, SLK, SLK, SLK, SLK, SLK, }, 0xFF,0x00 },
|
||||
/*47*/{{ F(49), '7', '7', '7', '7', '7', '7', '7', }, 0x80,0x02 },
|
||||
/*48*/{{ F(50), '8', '8', '8', '8', '8', '8', '8', }, 0x80,0x02 },
|
||||
/*49*/{{ F(51), '9', '9', '9', '9', '9', '9', '9', }, 0x80,0x02 },
|
||||
/*4a*/{{ F(52), '-', '-', '-', '-', '-', '-', '-', }, 0x80,0x02 },
|
||||
/*4b*/{{ F(53), '4', '4', '4', '4', '4', '4', '4', }, 0x80,0x02 },
|
||||
/*4c*/{{ F(54), '5', '5', '5', '5', '5', '5', '5', }, 0x80,0x02 },
|
||||
/*4d*/{{ F(55), '6', '6', '6', '6', '6', '6', '6', }, 0x80,0x02 },
|
||||
/*4e*/{{ F(56), '+', '+', '+', '+', '+', '+', '+', }, 0x80,0x02 },
|
||||
/*4f*/{{ F(57), '1', '1', '1', '1', '1', '1', '1', }, 0x80,0x02 },
|
||||
/*50*/{{ F(58), '2', '2', '2', '2', '2', '2', '2', }, 0x80,0x02 },
|
||||
/*51*/{{ F(59), '3', '3', '3', '3', '3', '3', '3', }, 0x80,0x02 },
|
||||
/*52*/{{ F(60), '0', '0', '0', '0', '0', '0', '0', }, 0x80,0x02 },
|
||||
/*53*/{{ 0x7F, '.', '.', '.', '.', '.', RBT, RBT, }, 0x03,0x02 },
|
||||
/*54*/{{ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, }, 0xFF,0x00 },
|
||||
/*55*/{{ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, }, 0xFF,0x00 },
|
||||
/*56*/{{ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, }, 0xFF,0x00 },
|
||||
/*57*/{{ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11),}, 0xFF,0x00 },
|
||||
/*58*/{{ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12),}, 0xFF,0x00 },
|
||||
/*59*/{{ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, }, 0x00,0x00 },
|
||||
/*5a*/{{ RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, RCTR, }, 0xFF,0x00 },
|
||||
/*5b*/{{ '/', '/', '/', '/', '/', '/', '/', '/', }, 0x00,0x02 },
|
||||
/*5c*/{{ NEXT, NEXT, NOP, NOP, DBG, DBG, DBG, DBG, }, 0xFF,0x00 },
|
||||
/*5d*/{{ RALT, RALT, RALT, RALT, RALT, RALT, RALT, RALT, }, 0xFF,0x00 },
|
||||
/*5e*/{{ F(49), F(49), F(49), F(49), F(49), F(49), F(49), F(49),}, 0xFF,0x00 },
|
||||
/*5f*/{{ F(50), F(50), F(50), F(50), F(50), F(50), F(50), F(50),}, 0xFF,0x00 },
|
||||
/*60*/{{ F(51), F(51), F(51), F(51), F(51), F(51), F(51), F(51),}, 0xFF,0x00 },
|
||||
/*61*/{{ F(53), F(53), F(53), F(53), F(53), F(53), F(53), F(53),}, 0xFF,0x00 },
|
||||
/*62*/{{ F(55), F(55), F(55), F(55), F(55), F(55), F(55), F(55),}, 0xFF,0x00 },
|
||||
/*63*/{{ F(57), F(57), F(57), F(57), F(57), F(57), F(57), F(57),}, 0xFF,0x00 },
|
||||
/*64*/{{ F(58), F(58), F(58), F(58), F(58), F(58), F(58), F(58),}, 0xFF,0x00 },
|
||||
/*65*/{{ F(59), F(59), F(59), F(59), F(59), F(59), F(59), F(59),}, 0xFF,0x00 },
|
||||
/*66*/{{ F(60), F(60), F(60), F(60), F(60), F(60), F(60), F(60),}, 0xFF,0x00 },
|
||||
/*67*/{{ F(61), F(61), F(61), F(61), F(61), F(61), RBT, F(61),}, 0xFF,0x00 },
|
||||
/*68*/{{ SPSC, SPSC, SUSP, SUSP, NOP, NOP, SUSP, SUSP, }, 0xFF,0x00 },
|
||||
/*69*/{{ F(62), F(62), F(62), F(62), F(62), F(62), F(62), F(62),}, 0xFF,0x00 },
|
||||
/*6a*/{{ F(63), F(63), F(63), F(63), F(63), F(63), F(63), F(63),}, 0xFF,0x00 },
|
||||
/*6b*/{{ F(64), F(64), F(64), F(64), F(64), F(64), F(64), F(64),}, 0xFF,0x00 },
|
||||
/*6c*/{{ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, }, 0xFF,0x00 },
|
||||
} };
|
||||
|
||||
static accentmap_t accentmap_default_us_acc = { 11, {
|
||||
/* dgra=0 */
|
||||
{ '`', { { 'a',0xe0 }, { 'A',0xc0 }, { 'e',0xe8 }, { 'E',0xc8 },
|
||||
{ 'i',0xec }, { 'I',0xcc }, { 'o',0xf2 }, { 'O',0xd2 },
|
||||
{ 'u',0xf9 }, { 'U',0xd9 }, }, },
|
||||
/* dacu=1 */
|
||||
{ 0xb4, { { 'a',0xe1 }, { 'A',0xc1 }, { 'e',0xe9 }, { 'E',0xc9 },
|
||||
{ 'i',0xed }, { 'I',0xcd }, { 'o',0xf3 }, { 'O',0xd3 },
|
||||
{ 'u',0xfa }, { 'U',0xda }, { 'y',0xfd }, { 'Y',0xdd }, }, },
|
||||
/* dcir=2 */
|
||||
{ '^', { { 'a',0xe2 }, { 'A',0xc2 }, { 'e',0xea }, { 'E',0xca },
|
||||
{ 'i',0xee }, { 'I',0xce }, { 'o',0xf4 }, { 'O',0xd4 },
|
||||
{ 'u',0xfb }, { 'U',0xdb }, }, },
|
||||
/* dtil=3 */
|
||||
{ '~', { { 'a',0xe3 }, { 'A',0xc3 }, { 'n',0xf1 }, { 'N',0xd1 },
|
||||
{ 'o',0xf5 }, { 'O',0xd5 }, }, },
|
||||
/* dmac=4 */
|
||||
{ 0x00 },
|
||||
/* dbre=5 */
|
||||
{ 0x00 },
|
||||
/* ddot=6 */
|
||||
{ 0x00 },
|
||||
/* duml=7 */
|
||||
{ 0xa8, { { 'a',0xe4 }, { 'A',0xc4 }, { 'e',0xeb }, { 'E',0xcb },
|
||||
{ 'i',0xef }, { 'I',0xcf }, { 'o',0xf6 }, { 'O',0xd6 },
|
||||
{ 'u',0xfc }, { 'U',0xdc }, { 'y',0xff }, }, },
|
||||
/* dsla=8 */
|
||||
{ 0x00 },
|
||||
/* drin=9 */
|
||||
{ 0xb0, { { 'a',0xe5 }, { 'A',0xc5 }, }, },
|
||||
/* dced=10 */
|
||||
{ 0xb8, { { 'c',0xe7 }, { 'C',0xc7 }, }, },
|
||||
/* dapo=11 */
|
||||
{ 0x00 },
|
||||
/* ddac=12 */
|
||||
{ 0x00 },
|
||||
/* dogo=13 */
|
||||
{ 0x00 },
|
||||
/* dcar=14 */
|
||||
{ 0x00 },
|
||||
} };
|
||||
|
||||
/* *INDENT-ON* */ /* clang-format on */
|
598
external/sdl/SDL/src/core/freebsd/SDL_evdev_kbd_freebsd.c
vendored
Normal file
598
external/sdl/SDL/src/core/freebsd/SDL_evdev_kbd_freebsd.c
vendored
Normal file
@ -0,0 +1,598 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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 "../linux/SDL_evdev_kbd.h"
|
||||
|
||||
#ifdef SDL_INPUT_FBSDKBIO
|
||||
|
||||
/* This logic is adapted from drivers/tty/vt/keyboard.c in the Linux kernel source, slightly modified to work with FreeBSD */
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/kbio.h>
|
||||
#include <sys/consio.h>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "../../events/SDL_events_c.h"
|
||||
#include "SDL_evdev_kbd_default_keyaccmap.h"
|
||||
|
||||
typedef void(fn_handler_fn)(SDL_EVDEV_keyboard_state *kbd);
|
||||
|
||||
/*
|
||||
* Keyboard State
|
||||
*/
|
||||
|
||||
struct SDL_EVDEV_keyboard_state
|
||||
{
|
||||
int console_fd;
|
||||
int keyboard_fd;
|
||||
unsigned long old_kbd_mode;
|
||||
unsigned short **key_maps;
|
||||
keymap_t *key_map;
|
||||
keyboard_info_t *kbInfo;
|
||||
unsigned char shift_down[4]; /* shift state counters.. */
|
||||
SDL_bool dead_key_next;
|
||||
int npadch; /* -1 or number assembled on pad */
|
||||
accentmap_t *accents;
|
||||
unsigned int diacr;
|
||||
SDL_bool rep; /* flag telling character repeat */
|
||||
unsigned char lockstate;
|
||||
unsigned char ledflagstate;
|
||||
char shift_state;
|
||||
char text[128];
|
||||
unsigned int text_len;
|
||||
};
|
||||
|
||||
static int SDL_EVDEV_kbd_load_keymaps(SDL_EVDEV_keyboard_state *kbd)
|
||||
{
|
||||
return ioctl(kbd->keyboard_fd, GIO_KEYMAP, kbd->key_map) >= 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct mouse_info mData;
|
||||
SDL_EVDEV_keyboard_state *kbd = kbd_cleanup_state;
|
||||
if (kbd == NULL) {
|
||||
return;
|
||||
}
|
||||
kbd_cleanup_state = NULL;
|
||||
SDL_zero(mData);
|
||||
mData.operation = MOUSE_SHOW;
|
||||
ioctl(kbd->keyboard_fd, KDSKBMODE, kbd->old_kbd_mode);
|
||||
if (kbd->keyboard_fd != kbd->console_fd) {
|
||||
close(kbd->keyboard_fd);
|
||||
}
|
||||
ioctl(kbd->console_fd, CONS_SETKBD, (unsigned long)(kbd->kbInfo->kb_index));
|
||||
ioctl(kbd->console_fd, CONS_MOUSECTL, &mData);
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_reraise_signal(int sig)
|
||||
{
|
||||
raise(sig);
|
||||
}
|
||||
|
||||
siginfo_t *SDL_EVDEV_kdb_cleanup_siginfo = NULL;
|
||||
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()
|
||||
{
|
||||
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 != NULL) {
|
||||
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)
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_EVDEV_keyboard_state *SDL_EVDEV_kbd_init(void)
|
||||
{
|
||||
SDL_EVDEV_keyboard_state *kbd;
|
||||
struct mouse_info mData;
|
||||
char flag_state;
|
||||
char *devicePath;
|
||||
|
||||
SDL_zero(mData);
|
||||
mData.operation = MOUSE_HIDE;
|
||||
kbd = (SDL_EVDEV_keyboard_state *)SDL_calloc(1, sizeof(SDL_EVDEV_keyboard_state));
|
||||
if (kbd == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
kbd->npadch = -1;
|
||||
|
||||
/* This might fail if we're not connected to a tty (e.g. on the Steam Link) */
|
||||
kbd->keyboard_fd = kbd->console_fd = open("/dev/tty", O_RDONLY | O_CLOEXEC);
|
||||
|
||||
kbd->shift_state = 0;
|
||||
|
||||
kbd->accents = SDL_calloc(sizeof(accentmap_t), 1);
|
||||
kbd->key_map = SDL_calloc(sizeof(keymap_t), 1);
|
||||
kbd->kbInfo = SDL_calloc(sizeof(keyboard_info_t), 1);
|
||||
|
||||
ioctl(kbd->console_fd, KDGKBINFO, kbd->kbInfo);
|
||||
ioctl(kbd->console_fd, CONS_MOUSECTL, &mData);
|
||||
|
||||
if (ioctl(kbd->console_fd, KDGKBSTATE, &flag_state) == 0) {
|
||||
kbd->ledflagstate = flag_state;
|
||||
}
|
||||
|
||||
if (ioctl(kbd->console_fd, GIO_DEADKEYMAP, kbd->accents) < 0) {
|
||||
SDL_free(kbd->accents);
|
||||
kbd->accents = &accentmap_default_us_acc;
|
||||
}
|
||||
|
||||
if (ioctl(kbd->console_fd, KDGKBMODE, &kbd->old_kbd_mode) == 0) {
|
||||
/* Set the keyboard in XLATE mode and load the keymaps */
|
||||
ioctl(kbd->console_fd, KDSKBMODE, (unsigned long)(K_XLATE));
|
||||
if (!SDL_EVDEV_kbd_load_keymaps(kbd)) {
|
||||
SDL_free(kbd->key_map);
|
||||
kbd->key_map = &keymap_default_us_acc;
|
||||
}
|
||||
/* Allow inhibiting keyboard mute with env. variable for debugging etc. */
|
||||
if (SDL_getenv("SDL_INPUT_FREEBSD_KEEP_KBD") == NULL) {
|
||||
/* Take keyboard from console and open the actual keyboard device.
|
||||
* Ensures that the keystrokes do not leak through to the console.
|
||||
*/
|
||||
ioctl(kbd->console_fd, CONS_RELKBD, 1ul);
|
||||
SDL_asprintf(&devicePath, "/dev/kbd%d", kbd->kbInfo->kb_index);
|
||||
kbd->keyboard_fd = open(devicePath, O_WRONLY | O_CLOEXEC);
|
||||
if (kbd->keyboard_fd == -1) {
|
||||
// Give keyboard back.
|
||||
ioctl(kbd->console_fd, CONS_SETKBD, (unsigned long)(kbd->kbInfo->kb_index));
|
||||
kbd->keyboard_fd = kbd->console_fd;
|
||||
}
|
||||
|
||||
/* 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, SDL_FALSE)) {
|
||||
kbd_register_emerg_cleanup(kbd);
|
||||
}
|
||||
SDL_free(devicePath);
|
||||
} else
|
||||
kbd->keyboard_fd = kbd->console_fd;
|
||||
}
|
||||
|
||||
return kbd;
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *kbd)
|
||||
{
|
||||
struct mouse_info mData;
|
||||
|
||||
if (kbd == NULL) {
|
||||
return;
|
||||
}
|
||||
SDL_zero(mData);
|
||||
mData.operation = MOUSE_SHOW;
|
||||
ioctl(kbd->console_fd, CONS_MOUSECTL, &mData);
|
||||
|
||||
kbd_unregister_emerg_cleanup();
|
||||
|
||||
if (kbd->keyboard_fd >= 0) {
|
||||
/* Restore the original keyboard mode */
|
||||
ioctl(kbd->keyboard_fd, KDSKBMODE, kbd->old_kbd_mode);
|
||||
|
||||
close(kbd->keyboard_fd);
|
||||
if (kbd->console_fd != kbd->keyboard_fd && kbd->console_fd >= 0) {
|
||||
// Give back keyboard.
|
||||
ioctl(kbd->console_fd, CONS_SETKBD, (unsigned long)(kbd->kbInfo->kb_index));
|
||||
}
|
||||
kbd->console_fd = kbd->keyboard_fd = -1;
|
||||
}
|
||||
|
||||
SDL_free(kbd);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
/* 0******* */
|
||||
put_queue(kbd, c);
|
||||
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, j;
|
||||
|
||||
kbd->diacr = 0;
|
||||
|
||||
for (i = 0; i < kbd->accents->n_accs; i++) {
|
||||
if (kbd->accents->acc[i].accchar == d) {
|
||||
for (j = 0; j < NUM_ACCENTCHARS; ++j) {
|
||||
if (kbd->accents->acc[i].map[j][0] == 0) { /* end of table */
|
||||
break;
|
||||
}
|
||||
if (kbd->accents->acc[i].map[j][0] == ch) {
|
||||
return kbd->accents->acc[i].map[j][1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == ' ' || ch == d) {
|
||||
put_utf8(kbd, d);
|
||||
return 0;
|
||||
}
|
||||
put_utf8(kbd, d);
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
static int vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
|
||||
{
|
||||
return (kbd->ledflagstate & flag) != 0;
|
||||
}
|
||||
|
||||
static void chg_vc_kbd_led(SDL_EVDEV_keyboard_state *kbd, int flag)
|
||||
{
|
||||
kbd->ledflagstate ^= flag;
|
||||
ioctl(kbd->keyboard_fd, KDSKBSTATE, (unsigned long)(kbd->ledflagstate));
|
||||
}
|
||||
|
||||
/*
|
||||
* Special function handlers
|
||||
*/
|
||||
|
||||
static void k_self(SDL_EVDEV_keyboard_state *kbd, unsigned int 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 = SDL_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_shift(SDL_EVDEV_keyboard_state *kbd, unsigned char value, char up_flag)
|
||||
{
|
||||
int old_state = kbd->shift_state;
|
||||
|
||||
if (kbd->rep)
|
||||
return;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_keycode(SDL_EVDEV_keyboard_state *kbd, unsigned int keycode, int down)
|
||||
{
|
||||
keymap_t key_map;
|
||||
struct keyent_t keysym;
|
||||
unsigned int final_key_state;
|
||||
unsigned int map_from_key_sym;
|
||||
|
||||
if (kbd == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
key_map = *kbd->key_map;
|
||||
|
||||
kbd->rep = (down == 2);
|
||||
|
||||
if (keycode < NUM_KEYS) {
|
||||
if (keycode >= 89 && keycode <= 95) {
|
||||
/* These constitute unprintable language-related keys, so ignore them. */
|
||||
return;
|
||||
}
|
||||
if (keycode > 95) {
|
||||
keycode -= 7;
|
||||
}
|
||||
if (vc_kbd_led(kbd, ALKED) || (kbd->shift_state & 0x8)) {
|
||||
keycode += ALTGR_OFFSET;
|
||||
}
|
||||
keysym = key_map.key[keycode];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
final_key_state = kbd->shift_state & 0x7;
|
||||
if ((keysym.flgs & FLAG_LOCK_C) && vc_kbd_led(kbd, LED_CAP)) {
|
||||
final_key_state ^= 0x1;
|
||||
}
|
||||
if ((keysym.flgs & FLAG_LOCK_N) && vc_kbd_led(kbd, LED_NUM)) {
|
||||
final_key_state ^= 0x1;
|
||||
}
|
||||
|
||||
map_from_key_sym = keysym.map[final_key_state];
|
||||
if ((keysym.spcl & (0x80 >> final_key_state)) || (map_from_key_sym & SPCLKEY)) {
|
||||
/* Special function.*/
|
||||
if (map_from_key_sym == 0)
|
||||
return; /* Nothing to do. */
|
||||
if (map_from_key_sym & SPCLKEY) {
|
||||
map_from_key_sym &= ~SPCLKEY;
|
||||
}
|
||||
if (map_from_key_sym >= F_ACC && map_from_key_sym <= L_ACC) {
|
||||
/* Accent function.*/
|
||||
unsigned int accent_index = map_from_key_sym - F_ACC;
|
||||
if (kbd->accents->acc[accent_index].accchar != 0) {
|
||||
k_deadunicode(kbd, kbd->accents->acc[accent_index].accchar, !down);
|
||||
}
|
||||
} else {
|
||||
switch (map_from_key_sym) {
|
||||
case ASH: /* alt/meta shift */
|
||||
k_shift(kbd, 3, down == 0);
|
||||
break;
|
||||
case LSHA: /* left shift + alt lock */
|
||||
case RSHA: /* right shift + alt lock */
|
||||
if (down == 0) {
|
||||
chg_vc_kbd_led(kbd, ALKED);
|
||||
}
|
||||
case LSH: /* left shift */
|
||||
case RSH: /* right shift */
|
||||
k_shift(kbd, 0, down == 0);
|
||||
break;
|
||||
case LCTRA: /* left ctrl + alt lock */
|
||||
case RCTRA: /* right ctrl + alt lock */
|
||||
if (down == 0) {
|
||||
chg_vc_kbd_led(kbd, ALKED);
|
||||
}
|
||||
case LCTR: /* left ctrl */
|
||||
case RCTR: /* right ctrl */
|
||||
k_shift(kbd, 1, down == 0);
|
||||
break;
|
||||
case LALTA: /* left alt + alt lock */
|
||||
case RALTA: /* right alt + alt lock */
|
||||
if (down == 0) {
|
||||
chg_vc_kbd_led(kbd, ALKED);
|
||||
}
|
||||
case LALT: /* left alt */
|
||||
case RALT: /* right alt */
|
||||
k_shift(kbd, 2, down == 0);
|
||||
break;
|
||||
case ALK: /* alt lock */
|
||||
if (down == 1) {
|
||||
chg_vc_kbd_led(kbd, ALKED);
|
||||
}
|
||||
break;
|
||||
case CLK: /* caps lock*/
|
||||
if (down == 1) {
|
||||
chg_vc_kbd_led(kbd, CLKED);
|
||||
}
|
||||
break;
|
||||
case NLK: /* num lock */
|
||||
if (down == 1) {
|
||||
chg_vc_kbd_led(kbd, NLKED);
|
||||
}
|
||||
break;
|
||||
case SLK: /* scroll lock */
|
||||
if (down == 1) {
|
||||
chg_vc_kbd_led(kbd, SLKED);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (map_from_key_sym == '\n' || map_from_key_sym == '\r') {
|
||||
if (kbd->diacr) {
|
||||
kbd->diacr = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (map_from_key_sym >= ' ' && map_from_key_sym != 127) {
|
||||
k_self(kbd, map_from_key_sym, !down);
|
||||
}
|
||||
}
|
||||
|
||||
if (kbd->text_len > 0) {
|
||||
kbd->text[kbd->text_len] = '\0';
|
||||
SDL_SendKeyboardText(kbd->text);
|
||||
kbd->text_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SDL_INPUT_FBSDKBIO */
|
216
external/sdl/SDL/src/core/gdk/SDL_gdk.cpp
vendored
Normal file
216
external/sdl/SDL/src/core/gdk/SDL_gdk.cpp
vendored
Normal file
@ -0,0 +1,216 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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"
|
||||
|
||||
extern "C" {
|
||||
#include "../windows/SDL_windows.h"
|
||||
#include "../../events/SDL_events_c.h"
|
||||
}
|
||||
#include <XGameRuntime.h>
|
||||
#include <xsapi-c/services_c.h>
|
||||
#include <shellapi.h> /* CommandLineToArgvW() */
|
||||
#include <appnotify.h>
|
||||
|
||||
static XTaskQueueHandle GDK_GlobalTaskQueue;
|
||||
|
||||
PAPPSTATE_REGISTRATION hPLM = {};
|
||||
HANDLE plmSuspendComplete = nullptr;
|
||||
|
||||
extern "C" DECLSPEC int
|
||||
SDL_GDKGetTaskQueue(XTaskQueueHandle *outTaskQueue)
|
||||
{
|
||||
/* If this is the first call, first create the global task queue. */
|
||||
if (!GDK_GlobalTaskQueue) {
|
||||
HRESULT hr;
|
||||
|
||||
hr = XTaskQueueCreate(XTaskQueueDispatchMode::ThreadPool,
|
||||
XTaskQueueDispatchMode::Manual,
|
||||
&GDK_GlobalTaskQueue);
|
||||
if (FAILED(hr)) {
|
||||
return SDL_SetError("[GDK] Could not create global task queue");
|
||||
}
|
||||
|
||||
/* The initial call gets the non-duplicated handle so they can clean it up */
|
||||
*outTaskQueue = GDK_GlobalTaskQueue;
|
||||
} else {
|
||||
/* Duplicate the global task queue handle into outTaskQueue */
|
||||
if (FAILED(XTaskQueueDuplicateHandle(GDK_GlobalTaskQueue, outTaskQueue))) {
|
||||
return SDL_SetError("[GDK] Unable to acquire global task queue");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" void
|
||||
GDK_DispatchTaskQueue(void)
|
||||
{
|
||||
/* If there is no global task queue, don't do anything.
|
||||
* This gives the option to opt-out for those who want to handle everything themselves.
|
||||
*/
|
||||
if (GDK_GlobalTaskQueue) {
|
||||
/* Dispatch any callbacks which are ready. */
|
||||
while (XTaskQueueDispatch(GDK_GlobalTaskQueue, XTaskQueuePort::Completion, 0))
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pop up an out of memory message, returns to Windows */
|
||||
extern "C" static BOOL
|
||||
OutOfMemory(void)
|
||||
{
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Out of memory - aborting", NULL);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Gets the arguments with GetCommandLine, converts them to argc and argv
|
||||
and calls SDL_main */
|
||||
extern "C" DECLSPEC int
|
||||
SDL_RunApp(int, char**, SDL_main_func mainFunction, void *reserved)
|
||||
{
|
||||
LPWSTR *argvw;
|
||||
char **argv;
|
||||
int i, argc, result;
|
||||
HRESULT hr;
|
||||
XTaskQueueHandle taskQueue;
|
||||
|
||||
argvw = CommandLineToArgvW(GetCommandLineW(), &argc);
|
||||
if (argvw == NULL) {
|
||||
return OutOfMemory();
|
||||
}
|
||||
|
||||
/* Note that we need to be careful about how we allocate/free memory here.
|
||||
* If the application calls SDL_SetMemoryFunctions(), we can't rely on
|
||||
* SDL_free() to use the same allocator after SDL_main() returns.
|
||||
*/
|
||||
|
||||
/* Parse it into argv and argc */
|
||||
argv = (char **)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (argc + 1) * sizeof(*argv));
|
||||
if (argv == NULL) {
|
||||
return OutOfMemory();
|
||||
}
|
||||
for (i = 0; i < argc; ++i) {
|
||||
DWORD len;
|
||||
char *arg = WIN_StringToUTF8W(argvw[i]);
|
||||
if (arg == NULL) {
|
||||
return OutOfMemory();
|
||||
}
|
||||
len = (DWORD)SDL_strlen(arg);
|
||||
argv[i] = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len + 1);
|
||||
if (!argv[i]) {
|
||||
return OutOfMemory();
|
||||
}
|
||||
SDL_memcpy(argv[i], arg, len);
|
||||
SDL_free(arg);
|
||||
}
|
||||
argv[i] = NULL;
|
||||
LocalFree(argvw);
|
||||
|
||||
hr = XGameRuntimeInitialize();
|
||||
|
||||
if (SUCCEEDED(hr) && SDL_GDKGetTaskQueue(&taskQueue) == 0) {
|
||||
Uint32 titleid = 0;
|
||||
char scidBuffer[64];
|
||||
XblInitArgs xblArgs;
|
||||
|
||||
XTaskQueueSetCurrentProcessTaskQueue(taskQueue);
|
||||
|
||||
/* Try to get the title ID and initialize Xbox Live */
|
||||
hr = XGameGetXboxTitleId(&titleid);
|
||||
if (SUCCEEDED(hr)) {
|
||||
SDL_zero(xblArgs);
|
||||
xblArgs.queue = taskQueue;
|
||||
SDL_snprintf(scidBuffer, 64, "00000000-0000-0000-0000-0000%08X", titleid);
|
||||
xblArgs.scid = scidBuffer;
|
||||
hr = XblInitialize(&xblArgs);
|
||||
} else {
|
||||
SDL_SetError("[GDK] Unable to get titleid. Will not call XblInitialize. Check MicrosoftGame.config!");
|
||||
}
|
||||
|
||||
SDL_SetMainReady();
|
||||
|
||||
/* Register suspend/resume handling */
|
||||
plmSuspendComplete = CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
|
||||
if (!plmSuspendComplete) {
|
||||
SDL_SetError("[GDK] Unable to create plmSuspendComplete event");
|
||||
return -1;
|
||||
}
|
||||
auto rascn = [](BOOLEAN quiesced, PVOID context) {
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "[GDK] in RegisterAppStateChangeNotification handler");
|
||||
if (quiesced) {
|
||||
ResetEvent(plmSuspendComplete);
|
||||
SDL_SendAppEvent(SDL_EVENT_DID_ENTER_BACKGROUND);
|
||||
|
||||
// To defer suspension, we must wait to exit this callback.
|
||||
// IMPORTANT: The app must call SDL_GDKSuspendComplete() to release this lock.
|
||||
(void)WaitForSingleObject(plmSuspendComplete, INFINITE);
|
||||
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "[GDK] in RegisterAppStateChangeNotification handler: plmSuspendComplete event signaled.");
|
||||
} else {
|
||||
SDL_SendAppEvent(SDL_EVENT_WILL_ENTER_FOREGROUND);
|
||||
}
|
||||
};
|
||||
if (RegisterAppStateChangeNotification(rascn, NULL, &hPLM)) {
|
||||
SDL_SetError("[GDK] Unable to call RegisterAppStateChangeNotification");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Run the application main() code */
|
||||
result = mainFunction(argc, argv);
|
||||
|
||||
/* Unregister suspend/resume handling */
|
||||
UnregisterAppStateChangeNotification(hPLM);
|
||||
CloseHandle(plmSuspendComplete);
|
||||
|
||||
/* !!! FIXME: This follows the docs exactly, but for some reason still leaks handles on exit? */
|
||||
/* Terminate the task queue and dispatch any pending tasks */
|
||||
XTaskQueueTerminate(taskQueue, false, nullptr, nullptr);
|
||||
while (XTaskQueueDispatch(taskQueue, XTaskQueuePort::Completion, 0))
|
||||
;
|
||||
|
||||
XTaskQueueCloseHandle(taskQueue);
|
||||
|
||||
XGameRuntimeUninitialize();
|
||||
} else {
|
||||
#ifdef __WINGDK__
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "[GDK] Could not initialize - aborting", NULL);
|
||||
#else
|
||||
SDL_assert_always(0 && "[GDK] Could not initialize - aborting");
|
||||
#endif
|
||||
result = -1;
|
||||
}
|
||||
|
||||
/* Free argv, to avoid memory leak */
|
||||
for (i = 0; i < argc; ++i) {
|
||||
HeapFree(GetProcessHeap(), 0, argv[i]);
|
||||
}
|
||||
HeapFree(GetProcessHeap(), 0, argv);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
extern "C" DECLSPEC void
|
||||
SDL_GDKSuspendComplete()
|
||||
{
|
||||
if (plmSuspendComplete) {
|
||||
SetEvent(plmSuspendComplete);
|
||||
}
|
||||
}
|
24
external/sdl/SDL/src/core/gdk/SDL_gdk.h
vendored
Normal file
24
external/sdl/SDL/src/core/gdk/SDL_gdk.h
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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 is called from WIN_PumpEvents on GDK */
|
||||
extern void GDK_DispatchTaskQueue(void);
|
425
external/sdl/SDL/src/core/haiku/SDL_BApp.h
vendored
Normal file
425
external/sdl/SDL/src/core/haiku/SDL_BApp.h
vendored
Normal file
@ -0,0 +1,425 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_BAPP_H
|
||||
#define SDL_BAPP_H
|
||||
|
||||
#include <Path.h>
|
||||
#include <InterfaceKit.h>
|
||||
#include <LocaleRoster.h>
|
||||
#ifdef SDL_VIDEO_OPENGL
|
||||
#include <OpenGLKit.h>
|
||||
#endif
|
||||
|
||||
#include "../../video/haiku/SDL_bkeyboard.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
/* Local includes */
|
||||
#include "../../events/SDL_events_c.h"
|
||||
#include "../../video/haiku/SDL_bframebuffer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
|
||||
/* Forward declarations */
|
||||
class SDL_BLooper;
|
||||
class SDL_BWin;
|
||||
|
||||
/* Message constants */
|
||||
enum ToSDL
|
||||
{
|
||||
/* Intercepted by BWindow on its way to BView */
|
||||
BAPP_MOUSE_MOVED,
|
||||
BAPP_MOUSE_BUTTON,
|
||||
BAPP_MOUSE_WHEEL,
|
||||
BAPP_KEY,
|
||||
BAPP_REPAINT, /* from _UPDATE_ */
|
||||
/* From BWindow */
|
||||
BAPP_MAXIMIZE, /* from B_ZOOM */
|
||||
BAPP_MINIMIZE,
|
||||
BAPP_RESTORE, /* TODO: IMPLEMENT! */
|
||||
BAPP_SHOW,
|
||||
BAPP_HIDE,
|
||||
BAPP_MOUSE_FOCUS, /* caused by MOUSE_MOVE */
|
||||
BAPP_KEYBOARD_FOCUS, /* from WINDOW_ACTIVATED */
|
||||
BAPP_WINDOW_CLOSE_REQUESTED,
|
||||
BAPP_WINDOW_MOVED,
|
||||
BAPP_WINDOW_RESIZED,
|
||||
BAPP_SCREEN_CHANGED
|
||||
};
|
||||
|
||||
|
||||
extern "C" SDL_BLooper *SDL_Looper;
|
||||
|
||||
|
||||
/* Create a descendant of BLooper */
|
||||
class SDL_BLooper : public BLooper
|
||||
{
|
||||
public:
|
||||
SDL_BLooper(const char* name) : BLooper(name)
|
||||
{
|
||||
#ifdef SDL_VIDEO_OPENGL
|
||||
_current_context = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual ~SDL_BLooper()
|
||||
{
|
||||
}
|
||||
|
||||
/* Event-handling functions */
|
||||
virtual void MessageReceived(BMessage *message)
|
||||
{
|
||||
/* Sort out SDL-related messages */
|
||||
switch (message->what) {
|
||||
case BAPP_MOUSE_MOVED:
|
||||
_HandleMouseMove(message);
|
||||
break;
|
||||
|
||||
case BAPP_MOUSE_BUTTON:
|
||||
_HandleMouseButton(message);
|
||||
break;
|
||||
|
||||
case BAPP_MOUSE_WHEEL:
|
||||
_HandleMouseWheel(message);
|
||||
break;
|
||||
|
||||
case BAPP_KEY:
|
||||
_HandleKey(message);
|
||||
break;
|
||||
|
||||
case BAPP_REPAINT:
|
||||
_HandleBasicWindowEvent(message, SDL_EVENT_WINDOW_EXPOSED);
|
||||
break;
|
||||
|
||||
case BAPP_MAXIMIZE:
|
||||
_HandleBasicWindowEvent(message, SDL_EVENT_WINDOW_MAXIMIZED);
|
||||
break;
|
||||
|
||||
case BAPP_MINIMIZE:
|
||||
_HandleBasicWindowEvent(message, SDL_EVENT_WINDOW_MINIMIZED);
|
||||
break;
|
||||
|
||||
case BAPP_SHOW:
|
||||
_HandleBasicWindowEvent(message, SDL_EVENT_WINDOW_SHOWN);
|
||||
break;
|
||||
|
||||
case BAPP_HIDE:
|
||||
_HandleBasicWindowEvent(message, SDL_EVENT_WINDOW_HIDDEN);
|
||||
break;
|
||||
|
||||
case BAPP_MOUSE_FOCUS:
|
||||
_HandleMouseFocus(message);
|
||||
break;
|
||||
|
||||
case BAPP_KEYBOARD_FOCUS:
|
||||
_HandleKeyboardFocus(message);
|
||||
break;
|
||||
|
||||
case BAPP_WINDOW_CLOSE_REQUESTED:
|
||||
_HandleBasicWindowEvent(message, SDL_EVENT_WINDOW_CLOSE_REQUESTED);
|
||||
break;
|
||||
|
||||
case BAPP_WINDOW_MOVED:
|
||||
_HandleWindowMoved(message);
|
||||
break;
|
||||
|
||||
case BAPP_WINDOW_RESIZED:
|
||||
_HandleWindowResized(message);
|
||||
break;
|
||||
|
||||
case B_LOCALE_CHANGED:
|
||||
SDL_SendLocaleChangedEvent();
|
||||
break;
|
||||
|
||||
case BAPP_SCREEN_CHANGED:
|
||||
/* TODO: Handle screen resize or workspace change */
|
||||
break;
|
||||
|
||||
default:
|
||||
BLooper::MessageReceived(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Window creation/destruction methods */
|
||||
int32 GetID(SDL_Window *win)
|
||||
{
|
||||
int32 i;
|
||||
for (i = 0; i < _GetNumWindowSlots(); ++i) {
|
||||
if (GetSDLWindow(i) == NULL) {
|
||||
_SetSDLWindow(win, i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
/* Expand the vector if all slots are full */
|
||||
if (i == _GetNumWindowSlots()) {
|
||||
_PushBackWindow(win);
|
||||
return i;
|
||||
}
|
||||
|
||||
/* TODO: error handling */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME: Bad coding practice, but I can't include SDL_BWin.h here. Is
|
||||
there another way to do this? */
|
||||
void ClearID(SDL_BWin *bwin); /* Defined in SDL_BeApp.cc */
|
||||
|
||||
SDL_Window *GetSDLWindow(int32 winID)
|
||||
{
|
||||
return _window_map[winID];
|
||||
}
|
||||
|
||||
#ifdef SDL_VIDEO_OPENGL
|
||||
BGLView *GetCurrentContext()
|
||||
{
|
||||
return _current_context;
|
||||
}
|
||||
|
||||
void SetCurrentContext(BGLView *newContext)
|
||||
{
|
||||
if (_current_context)
|
||||
_current_context->UnlockGL();
|
||||
_current_context = newContext;
|
||||
if (_current_context)
|
||||
_current_context->LockGL();
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
/* Event management */
|
||||
void _HandleBasicWindowEvent(BMessage *msg, SDL_EventType sdlEventType)
|
||||
{
|
||||
SDL_Window *win;
|
||||
int32 winID;
|
||||
if (
|
||||
!_GetWinID(msg, &winID)) {
|
||||
return;
|
||||
}
|
||||
win = GetSDLWindow(winID);
|
||||
SDL_SendWindowEvent(win, sdlEventType, 0, 0);
|
||||
}
|
||||
|
||||
void _HandleMouseMove(BMessage *msg)
|
||||
{
|
||||
SDL_Window *win;
|
||||
int32 winID;
|
||||
int32 x = 0, y = 0;
|
||||
if (
|
||||
!_GetWinID(msg, &winID) ||
|
||||
msg->FindInt32("x", &x) != B_OK || /* x movement */
|
||||
msg->FindInt32("y", &y) != B_OK /* y movement */
|
||||
) {
|
||||
return;
|
||||
}
|
||||
win = GetSDLWindow(winID);
|
||||
|
||||
// Simple relative mode support for mouse.
|
||||
if (SDL_GetMouse()->relative_mode) {
|
||||
int winWidth, winHeight, winPosX, winPosY;
|
||||
SDL_GetWindowSize(win, &winWidth, &winHeight);
|
||||
SDL_GetWindowPosition(win, &winPosX, &winPosY);
|
||||
int dx = x - (winWidth / 2);
|
||||
int dy = y - (winHeight / 2);
|
||||
SDL_SendMouseMotion(0, win, 0, SDL_GetMouse()->relative_mode, (float)dx, (float)dy);
|
||||
set_mouse_position((winPosX + winWidth / 2), (winPosY + winHeight / 2));
|
||||
if (!be_app->IsCursorHidden())
|
||||
be_app->HideCursor();
|
||||
} else {
|
||||
SDL_SendMouseMotion(0, win, 0, 0, (float)x, (float)y);
|
||||
if (SDL_CursorVisible() && be_app->IsCursorHidden())
|
||||
be_app->ShowCursor();
|
||||
}
|
||||
}
|
||||
|
||||
void _HandleMouseButton(BMessage *msg)
|
||||
{
|
||||
SDL_Window *win;
|
||||
int32 winID;
|
||||
int32 button, state; /* left/middle/right, pressed/released */
|
||||
if (
|
||||
!_GetWinID(msg, &winID) ||
|
||||
msg->FindInt32("button-id", &button) != B_OK ||
|
||||
msg->FindInt32("button-state", &state) != B_OK) {
|
||||
return;
|
||||
}
|
||||
win = GetSDLWindow(winID);
|
||||
SDL_SendMouseButton(0, win, 0, state, button);
|
||||
}
|
||||
|
||||
void _HandleMouseWheel(BMessage *msg)
|
||||
{
|
||||
SDL_Window *win;
|
||||
int32 winID;
|
||||
int32 xTicks, yTicks;
|
||||
if (
|
||||
!_GetWinID(msg, &winID) ||
|
||||
msg->FindInt32("xticks", &xTicks) != B_OK ||
|
||||
msg->FindInt32("yticks", &yTicks) != B_OK) {
|
||||
return;
|
||||
}
|
||||
win = GetSDLWindow(winID);
|
||||
SDL_SendMouseWheel(0, win, 0, xTicks, -yTicks, SDL_MOUSEWHEEL_NORMAL);
|
||||
}
|
||||
|
||||
void _HandleKey(BMessage *msg)
|
||||
{
|
||||
int32 scancode, state; /* scancode, pressed/released */
|
||||
if (
|
||||
msg->FindInt32("key-state", &state) != B_OK ||
|
||||
msg->FindInt32("key-scancode", &scancode) != B_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make sure this isn't a repeated event (key pressed and held) */
|
||||
if (state == SDL_PRESSED && HAIKU_GetKeyState(scancode) == SDL_PRESSED) {
|
||||
return;
|
||||
}
|
||||
HAIKU_SetKeyState(scancode, state);
|
||||
SDL_SendKeyboardKey(0, state, HAIKU_GetScancodeFromBeKey(scancode));
|
||||
|
||||
if (state == SDL_PRESSED && SDL_EventEnabled(SDL_EVENT_TEXT_INPUT)) {
|
||||
const int8 *keyUtf8;
|
||||
ssize_t count;
|
||||
if (msg->FindData("key-utf8", B_INT8_TYPE, (const void **)&keyUtf8, &count) == B_OK) {
|
||||
char text[SDL_TEXTINPUTEVENT_TEXT_SIZE];
|
||||
SDL_zeroa(text);
|
||||
SDL_memcpy(text, keyUtf8, count);
|
||||
SDL_SendKeyboardText(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _HandleMouseFocus(BMessage *msg)
|
||||
{
|
||||
SDL_Window *win;
|
||||
int32 winID;
|
||||
bool bSetFocus; /* If false, lose focus */
|
||||
if (
|
||||
!_GetWinID(msg, &winID) ||
|
||||
msg->FindBool("focusGained", &bSetFocus) != B_OK) {
|
||||
return;
|
||||
}
|
||||
win = GetSDLWindow(winID);
|
||||
if (bSetFocus) {
|
||||
SDL_SetMouseFocus(win);
|
||||
} else if (SDL_GetMouseFocus() == win) {
|
||||
/* Only lose all focus if this window was the current focus */
|
||||
SDL_SetMouseFocus(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void _HandleKeyboardFocus(BMessage *msg)
|
||||
{
|
||||
SDL_Window *win;
|
||||
int32 winID;
|
||||
bool bSetFocus; /* If false, lose focus */
|
||||
if (
|
||||
!_GetWinID(msg, &winID) ||
|
||||
msg->FindBool("focusGained", &bSetFocus) != B_OK) {
|
||||
return;
|
||||
}
|
||||
win = GetSDLWindow(winID);
|
||||
if (bSetFocus) {
|
||||
SDL_SetKeyboardFocus(win);
|
||||
} else if (SDL_GetKeyboardFocus() == win) {
|
||||
/* Only lose all focus if this window was the current focus */
|
||||
SDL_SetKeyboardFocus(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void _HandleWindowMoved(BMessage *msg)
|
||||
{
|
||||
SDL_Window *win;
|
||||
int32 winID;
|
||||
int32 xPos, yPos;
|
||||
/* Get the window id and new x/y position of the window */
|
||||
if (
|
||||
!_GetWinID(msg, &winID) ||
|
||||
msg->FindInt32("window-x", &xPos) != B_OK ||
|
||||
msg->FindInt32("window-y", &yPos) != B_OK) {
|
||||
return;
|
||||
}
|
||||
win = GetSDLWindow(winID);
|
||||
SDL_SendWindowEvent(win, SDL_EVENT_WINDOW_MOVED, xPos, yPos);
|
||||
}
|
||||
|
||||
void _HandleWindowResized(BMessage *msg)
|
||||
{
|
||||
SDL_Window *win;
|
||||
int32 winID;
|
||||
int32 w, h;
|
||||
/* Get the window id ]and new x/y position of the window */
|
||||
if (
|
||||
!_GetWinID(msg, &winID) ||
|
||||
msg->FindInt32("window-w", &w) != B_OK ||
|
||||
msg->FindInt32("window-h", &h) != B_OK) {
|
||||
return;
|
||||
}
|
||||
win = GetSDLWindow(winID);
|
||||
SDL_SendWindowEvent(win, SDL_EVENT_WINDOW_RESIZED, w, h);
|
||||
}
|
||||
|
||||
bool _GetWinID(BMessage *msg, int32 *winID)
|
||||
{
|
||||
return msg->FindInt32("window-id", winID) == B_OK;
|
||||
}
|
||||
|
||||
/* Vector functions: Wraps vector stuff in case we need to change
|
||||
implementation */
|
||||
void _SetSDLWindow(SDL_Window *win, int32 winID)
|
||||
{
|
||||
_window_map[winID] = win;
|
||||
}
|
||||
|
||||
int32 _GetNumWindowSlots()
|
||||
{
|
||||
return _window_map.size();
|
||||
}
|
||||
|
||||
void _PopBackWindow()
|
||||
{
|
||||
_window_map.pop_back();
|
||||
}
|
||||
|
||||
void _PushBackWindow(SDL_Window *win)
|
||||
{
|
||||
_window_map.push_back(win);
|
||||
}
|
||||
|
||||
/* Members */
|
||||
std::vector<SDL_Window *> _window_map; /* Keeps track of SDL_Windows by index-id */
|
||||
|
||||
#ifdef SDL_VIDEO_OPENGL
|
||||
BGLView *_current_context;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
195
external/sdl/SDL/src/core/haiku/SDL_BeApp.cc
vendored
Normal file
195
external/sdl/SDL/src/core/haiku/SDL_BeApp.cc
vendored
Normal file
@ -0,0 +1,195 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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 __HAIKU__
|
||||
|
||||
/* Handle the BeApp specific portions of the application */
|
||||
|
||||
#include <AppKit.h>
|
||||
#include <storage/AppFileInfo.h>
|
||||
#include <storage/Path.h>
|
||||
#include <storage/Entry.h>
|
||||
#include <storage/File.h>
|
||||
#include <unistd.h>
|
||||
#include <memory>
|
||||
|
||||
#include "SDL_BApp.h" /* SDL_BLooper class definition */
|
||||
#include "SDL_BeApp.h"
|
||||
|
||||
#include "../../video/haiku/SDL_BWin.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "../../thread/SDL_systhread.h"
|
||||
|
||||
/* Flag to tell whether or not the Be application and looper are active or not */
|
||||
static int SDL_BeAppActive = 0;
|
||||
static SDL_Thread *SDL_AppThread = NULL;
|
||||
SDL_BLooper *SDL_Looper = NULL;
|
||||
|
||||
|
||||
/* Default application signature */
|
||||
const char *SDL_signature = "application/x-SDL-executable";
|
||||
|
||||
|
||||
/* Create a descendant of BApplication */
|
||||
class SDL_BApp : public BApplication {
|
||||
public:
|
||||
SDL_BApp(const char* signature) :
|
||||
BApplication(signature) {
|
||||
}
|
||||
|
||||
|
||||
virtual ~SDL_BApp() {
|
||||
}
|
||||
|
||||
|
||||
virtual void RefsReceived(BMessage* message) {
|
||||
entry_ref entryRef;
|
||||
for (int32 i = 0; message->FindRef("refs", i, &entryRef) == B_OK; i++) {
|
||||
BPath referencePath = BPath(&entryRef);
|
||||
SDL_SendDropFile(NULL, referencePath.Path());
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static int StartBeApp(void *unused)
|
||||
{
|
||||
std::unique_ptr<BApplication> App;
|
||||
|
||||
(void)unused;
|
||||
// dig resources for correct signature
|
||||
image_info info;
|
||||
int32 cookie = 0;
|
||||
if (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) {
|
||||
BFile f(info.name, O_RDONLY);
|
||||
if (f.InitCheck() == B_OK) {
|
||||
BAppFileInfo app_info(&f);
|
||||
if (app_info.InitCheck() == B_OK) {
|
||||
char sig[B_MIME_TYPE_LENGTH];
|
||||
if (app_info.GetSignature(sig) == B_OK) {
|
||||
SDL_signature = strndup(sig, B_MIME_TYPE_LENGTH);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
App = std::unique_ptr<BApplication>(new SDL_BApp(SDL_signature));
|
||||
|
||||
App->Run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int StartBeLooper()
|
||||
{
|
||||
if (!be_app) {
|
||||
SDL_AppThread = SDL_CreateThreadInternal(StartBeApp, "SDLApplication", 0, NULL);
|
||||
if (SDL_AppThread == NULL) {
|
||||
return SDL_SetError("Couldn't create BApplication thread");
|
||||
}
|
||||
|
||||
do {
|
||||
SDL_Delay(10);
|
||||
} while ((be_app == NULL) || be_app->IsLaunching());
|
||||
}
|
||||
|
||||
/* Change working directory to that of executable */
|
||||
app_info info;
|
||||
if (B_OK == be_app->GetAppInfo(&info)) {
|
||||
entry_ref ref = info.ref;
|
||||
BEntry entry;
|
||||
if (B_OK == entry.SetTo(&ref)) {
|
||||
BPath path;
|
||||
if (B_OK == path.SetTo(&entry)) {
|
||||
if (B_OK == path.GetParent(&path)) {
|
||||
chdir(path.Path());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Looper = new SDL_BLooper("SDLLooper");
|
||||
SDL_Looper->Run();
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/* Initialize the Be Application, if it's not already started */
|
||||
int SDL_InitBeApp(void)
|
||||
{
|
||||
/* Create the BApplication that handles appserver interaction */
|
||||
if (SDL_BeAppActive <= 0) {
|
||||
StartBeLooper();
|
||||
|
||||
/* Mark the application active */
|
||||
SDL_BeAppActive = 0;
|
||||
}
|
||||
|
||||
/* Increment the application reference count */
|
||||
++SDL_BeAppActive;
|
||||
|
||||
/* The app is running, and we're ready to go */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Quit the Be Application, if there's nothing left to do */
|
||||
void SDL_QuitBeApp(void)
|
||||
{
|
||||
/* Decrement the application reference count */
|
||||
--SDL_BeAppActive;
|
||||
|
||||
/* If the reference count reached zero, clean up the app */
|
||||
if (SDL_BeAppActive == 0) {
|
||||
SDL_Looper->Lock();
|
||||
SDL_Looper->Quit();
|
||||
SDL_Looper = NULL;
|
||||
if (SDL_AppThread != NULL) {
|
||||
if (be_app != NULL) { /* Not tested */
|
||||
be_app->PostMessage(B_QUIT_REQUESTED);
|
||||
}
|
||||
SDL_WaitThread(SDL_AppThread, NULL);
|
||||
SDL_AppThread = NULL;
|
||||
}
|
||||
/* be_app should now be NULL since be_app has quit */
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/* SDL_BApp functions */
|
||||
void SDL_BLooper::ClearID(SDL_BWin *bwin) {
|
||||
_SetSDLWindow(NULL, bwin->GetID());
|
||||
int32 i = _GetNumWindowSlots() - 1;
|
||||
while (i >= 0 && GetSDLWindow(i) == NULL) {
|
||||
_PopBackWindow();
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __HAIKU__ */
|
40
external/sdl/SDL/src/core/haiku/SDL_BeApp.h
vendored
Normal file
40
external/sdl/SDL/src/core/haiku/SDL_BeApp.h
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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 __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/* Handle the BeApp specific portions of the application */
|
||||
|
||||
/* Initialize the Be Application, if it's not already started */
|
||||
extern int SDL_InitBeApp(void);
|
||||
|
||||
/* Quit the Be Application, if there's nothing left to do */
|
||||
extern void SDL_QuitBeApp(void);
|
||||
|
||||
/* Be Application Signature*/
|
||||
extern const char *SDL_signature;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
568
external/sdl/SDL/src/core/linux/SDL_dbus.c
vendored
Normal file
568
external/sdl/SDL/src/core/linux/SDL_dbus.c
vendored
Normal file
@ -0,0 +1,568 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_sandbox.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 void *dbus_handle = NULL;
|
||||
static char *inhibit_handle = NULL;
|
||||
static unsigned int screensaver_cookie = 0;
|
||||
static SDL_DBusContext dbus;
|
||||
|
||||
static int 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 -1
|
||||
|
||||
#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 *, 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(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 0;
|
||||
}
|
||||
|
||||
static void UnloadDBUSLibrary(void)
|
||||
{
|
||||
if (dbus_handle != NULL) {
|
||||
SDL_UnloadObject(dbus_handle);
|
||||
dbus_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int LoadDBUSLibrary(void)
|
||||
{
|
||||
int retval = 0;
|
||||
if (dbus_handle == NULL) {
|
||||
dbus_handle = SDL_LoadObject(dbus_library);
|
||||
if (dbus_handle == NULL) {
|
||||
retval = -1;
|
||||
/* Don't call SDL_SetError(): SDL_LoadObject already did. */
|
||||
} else {
|
||||
retval = LoadDBUSSyms();
|
||||
if (retval < 0) {
|
||||
UnloadDBUSLibrary();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static SDL_SpinLock spinlock_dbus_init = 0;
|
||||
|
||||
/* you must hold spinlock_dbus_init before calling this! */
|
||||
static void SDL_DBus_Init_Spinlocked(void)
|
||||
{
|
||||
static SDL_bool is_dbus_available = SDL_TRUE;
|
||||
if (!is_dbus_available) {
|
||||
return; /* don't keep trying if this fails. */
|
||||
}
|
||||
|
||||
if (!dbus.session_conn) {
|
||||
DBusError err;
|
||||
|
||||
if (LoadDBUSLibrary() == -1) {
|
||||
is_dbus_available = SDL_FALSE; /* can't load at all? Don't keep trying. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dbus.threads_init_default()) {
|
||||
is_dbus_available = SDL_FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
SDL_DBus_Quit();
|
||||
is_dbus_available = SDL_FALSE;
|
||||
return; /* oh well */
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_DBus_Init(void)
|
||||
{
|
||||
SDL_AtomicLock(&spinlock_dbus_init); /* make sure two threads can't init at same time, since this can happen before SDL_Init. */
|
||||
SDL_DBus_Init_Spinlocked();
|
||||
SDL_AtomicUnlock(&spinlock_dbus_init);
|
||||
}
|
||||
|
||||
void SDL_DBus_Quit(void)
|
||||
{
|
||||
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);
|
||||
}
|
||||
/* Don't do this - bug 3950
|
||||
dbus_shutdown() is a debug feature which closes all global resources in the dbus library. Calling this should be done by the app, not a library, because if there are multiple users of dbus in the process then SDL could shut it down even though another part is using it.
|
||||
*/
|
||||
#if 0
|
||||
if (dbus.shutdown) {
|
||||
dbus.shutdown();
|
||||
}
|
||||
#endif
|
||||
SDL_zero(dbus);
|
||||
UnloadDBUSLibrary();
|
||||
SDL_free(inhibit_handle);
|
||||
inhibit_handle = NULL;
|
||||
}
|
||||
|
||||
SDL_DBusContext *SDL_DBus_GetContext(void)
|
||||
{
|
||||
if (dbus_handle == NULL || !dbus.session_conn) {
|
||||
SDL_DBus_Init();
|
||||
}
|
||||
|
||||
return (dbus_handle && dbus.session_conn) ? &dbus : NULL;
|
||||
}
|
||||
|
||||
static SDL_bool SDL_DBus_CallMethodInternal(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, va_list ap)
|
||||
{
|
||||
SDL_bool retval = SDL_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)) {
|
||||
retval = SDL_TRUE;
|
||||
}
|
||||
dbus.message_unref(reply);
|
||||
}
|
||||
}
|
||||
va_end(ap_reply);
|
||||
dbus.message_unref(msg);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
SDL_bool SDL_DBus_CallMethodOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...)
|
||||
{
|
||||
SDL_bool retval;
|
||||
va_list ap;
|
||||
va_start(ap, method);
|
||||
retval = SDL_DBus_CallMethodInternal(conn, node, path, interface, method, ap);
|
||||
va_end(ap);
|
||||
return retval;
|
||||
}
|
||||
|
||||
SDL_bool SDL_DBus_CallMethod(const char *node, const char *path, const char *interface, const char *method, ...)
|
||||
{
|
||||
SDL_bool retval;
|
||||
va_list ap;
|
||||
va_start(ap, method);
|
||||
retval = SDL_DBus_CallMethodInternal(dbus.session_conn, node, path, interface, method, ap);
|
||||
va_end(ap);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static SDL_bool SDL_DBus_CallVoidMethodInternal(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, va_list ap)
|
||||
{
|
||||
SDL_bool retval = SDL_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);
|
||||
retval = SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
dbus.message_unref(msg);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static SDL_bool SDL_DBus_CallWithBasicReply(DBusConnection *conn, DBusMessage *msg, const int expectedtype, void *result)
|
||||
{
|
||||
SDL_bool retval = SDL_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 = SDL_TRUE;
|
||||
}
|
||||
|
||||
dbus.message_unref(reply);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
SDL_bool SDL_DBus_CallVoidMethodOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...)
|
||||
{
|
||||
SDL_bool retval;
|
||||
va_list ap;
|
||||
va_start(ap, method);
|
||||
retval = SDL_DBus_CallVoidMethodInternal(conn, node, path, interface, method, ap);
|
||||
va_end(ap);
|
||||
return retval;
|
||||
}
|
||||
|
||||
SDL_bool SDL_DBus_CallVoidMethod(const char *node, const char *path, const char *interface, const char *method, ...)
|
||||
{
|
||||
SDL_bool retval;
|
||||
va_list ap;
|
||||
va_start(ap, method);
|
||||
retval = SDL_DBus_CallVoidMethodInternal(dbus.session_conn, node, path, interface, method, ap);
|
||||
va_end(ap);
|
||||
return retval;
|
||||
}
|
||||
|
||||
SDL_bool SDL_DBus_QueryPropertyOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *property, const int expectedtype, void *result)
|
||||
{
|
||||
SDL_bool retval = SDL_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;
|
||||
}
|
||||
|
||||
SDL_bool SDL_DBus_QueryProperty(const char *node, const char *path, const char *interface, const char *property, const 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 == NULL) { /* 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 SDL_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 SDL_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 SDL_FALSE;
|
||||
}
|
||||
|
||||
static SDL_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);
|
||||
}
|
||||
|
||||
SDL_bool SDL_DBus_ScreensaverInhibit(SDL_bool inhibit)
|
||||
{
|
||||
const char *default_inhibit_reason = "Playing a game";
|
||||
|
||||
if ((inhibit && (screensaver_cookie != 0 || inhibit_handle != NULL)) || (!inhibit && (screensaver_cookie == 0 && inhibit_handle == NULL))) {
|
||||
return SDL_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 SDL_FALSE;
|
||||
}
|
||||
|
||||
if (SDL_DetectSandbox() != 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;
|
||||
SDL_bool retval = SDL_FALSE;
|
||||
const char *key = "reason";
|
||||
const char *reply = NULL;
|
||||
const char *reason = SDL_GetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME);
|
||||
if (reason == NULL || !reason[0]) {
|
||||
reason = default_inhibit_reason;
|
||||
}
|
||||
|
||||
msg = dbus.message_new_method_call(bus_name, path, interface, "Inhibit");
|
||||
if (msg == NULL) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
if (!dbus.message_append_args(msg, DBUS_TYPE_STRING, &window, DBUS_TYPE_UINT32, &INHIBIT_IDLE, DBUS_TYPE_INVALID)) {
|
||||
dbus.message_unref(msg);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
dbus.message_iter_init_append(msg, &iterInit);
|
||||
|
||||
/* a{sv} */
|
||||
if (!SDL_DBus_AppendDictWithKeyValue(&iterInit, key, reason)) {
|
||||
dbus.message_unref(msg);
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
if (SDL_DBus_CallWithBasicReply(dbus.session_conn, msg, DBUS_TYPE_OBJECT_PATH, &reply)) {
|
||||
inhibit_handle = SDL_strdup(reply);
|
||||
retval = SDL_TRUE;
|
||||
}
|
||||
|
||||
dbus.message_unref(msg);
|
||||
return retval;
|
||||
} else {
|
||||
if (!SDL_DBus_CallVoidMethod(bus_name, inhibit_handle, "org.freedesktop.portal.Request", "Close", DBUS_TYPE_INVALID)) {
|
||||
return SDL_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_GetHint(SDL_HINT_APP_NAME);
|
||||
const char *reason = SDL_GetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME);
|
||||
if (app == NULL || !app[0]) {
|
||||
app = "My SDL application";
|
||||
}
|
||||
if (reason == NULL || !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 SDL_FALSE;
|
||||
}
|
||||
return (screensaver_cookie != 0) ? SDL_TRUE : SDL_FALSE;
|
||||
} else {
|
||||
if (!SDL_DBus_CallVoidMethod(bus_name, path, interface, "UnInhibit", DBUS_TYPE_UINT32, &screensaver_cookie, DBUS_TYPE_INVALID)) {
|
||||
return SDL_FALSE;
|
||||
}
|
||||
screensaver_cookie = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return SDL_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;
|
||||
}
|
||||
#endif
|
107
external/sdl/SDL/src/core/linux/SDL_dbus.h
vendored
Normal file
107
external/sdl/SDL/src/core/linux/SDL_dbus.h
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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
|
||||
|
||||
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_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 *);
|
||||
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 SDL_bool SDL_DBus_CallMethod(const char *node, const char *path, const char *interface, const char *method, ...);
|
||||
extern SDL_bool SDL_DBus_CallVoidMethod(const char *node, const char *path, const char *interface, const char *method, ...);
|
||||
extern SDL_bool SDL_DBus_QueryProperty(const char *node, const char *path, const char *interface, const char *property, const int expectedtype, void *result);
|
||||
|
||||
/* These use whatever connection you like. */
|
||||
extern SDL_bool SDL_DBus_CallMethodOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...);
|
||||
extern SDL_bool SDL_DBus_CallVoidMethodOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...);
|
||||
extern SDL_bool SDL_DBus_QueryPropertyOnConnection(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *property, const int expectedtype, void *result);
|
||||
|
||||
extern void SDL_DBus_ScreensaverTickle(void);
|
||||
extern SDL_bool SDL_DBus_ScreensaverInhibit(SDL_bool inhibit);
|
||||
|
||||
extern void SDL_DBus_PumpEvents(void);
|
||||
extern char *SDL_DBus_GetLocalMachineId(void);
|
||||
|
||||
#endif /* HAVE_DBUS_DBUS_H */
|
||||
|
||||
#endif /* SDL_dbus_h_ */
|
962
external/sdl/SDL/src/core/linux/SDL_evdev.c
vendored
Normal file
962
external/sdl/SDL/src/core/linux/SDL_evdev.c
vendored
Normal file
@ -0,0 +1,962 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_INPUT_LINUXEV
|
||||
|
||||
/* This is based on the linux joystick driver */
|
||||
/* References: https://www.kernel.org/doc/Documentation/input/input.txt
|
||||
* https://www.kernel.org/doc/Documentation/input/event-codes.txt
|
||||
* /usr/include/linux/input.h
|
||||
* The evtest application is also useful to debug the protocol
|
||||
*/
|
||||
|
||||
#include "SDL_evdev.h"
|
||||
#include "SDL_evdev_kbd.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#include "../../events/SDL_events_c.h"
|
||||
#include "../../events/SDL_scancode_tables_c.h"
|
||||
#include "../../core/linux/SDL_evdev_capabilities.h"
|
||||
#include "../../core/linux/SDL_udev.h"
|
||||
|
||||
/* These are not defined in older Linux kernel headers */
|
||||
#ifndef SYN_DROPPED
|
||||
#define SYN_DROPPED 3
|
||||
#endif
|
||||
#ifndef ABS_MT_SLOT
|
||||
#define ABS_MT_SLOT 0x2f
|
||||
#define ABS_MT_POSITION_X 0x35
|
||||
#define ABS_MT_POSITION_Y 0x36
|
||||
#define ABS_MT_TRACKING_ID 0x39
|
||||
#define ABS_MT_PRESSURE 0x3a
|
||||
#endif
|
||||
#ifndef REL_WHEEL_HI_RES
|
||||
#define REL_WHEEL_HI_RES 0x0b
|
||||
#define REL_HWHEEL_HI_RES 0x0c
|
||||
#endif
|
||||
|
||||
/* The field to look up in struct input_event for integer seconds */
|
||||
#ifndef input_event_sec
|
||||
#define input_event_sec time.tv_sec
|
||||
#endif
|
||||
|
||||
/* The field to look up in struct input_event for fractional seconds */
|
||||
#ifndef input_event_usec
|
||||
#define input_event_usec time.tv_usec
|
||||
#endif
|
||||
|
||||
typedef struct SDL_evdevlist_item
|
||||
{
|
||||
char *path;
|
||||
int fd;
|
||||
|
||||
/* TODO: use this for every device, not just touchscreen */
|
||||
SDL_bool out_of_sync;
|
||||
|
||||
/* TODO: expand on this to have data for every possible class (mouse,
|
||||
keyboard, touchpad, etc.). Also there's probably some things in here we
|
||||
can pull out to the SDL_evdevlist_item i.e. name */
|
||||
SDL_bool is_touchscreen;
|
||||
struct
|
||||
{
|
||||
char *name;
|
||||
|
||||
int min_x, max_x, range_x;
|
||||
int min_y, max_y, range_y;
|
||||
int min_pressure, max_pressure, range_pressure;
|
||||
|
||||
int max_slots;
|
||||
int current_slot;
|
||||
struct
|
||||
{
|
||||
enum
|
||||
{
|
||||
EVDEV_TOUCH_SLOTDELTA_NONE = 0,
|
||||
EVDEV_TOUCH_SLOTDELTA_DOWN,
|
||||
EVDEV_TOUCH_SLOTDELTA_UP,
|
||||
EVDEV_TOUCH_SLOTDELTA_MOVE
|
||||
} delta;
|
||||
int tracking_id;
|
||||
int x, y, pressure;
|
||||
} *slots;
|
||||
|
||||
} *touchscreen_data;
|
||||
|
||||
/* Mouse state */
|
||||
SDL_bool high_res_wheel;
|
||||
SDL_bool high_res_hwheel;
|
||||
SDL_bool relative_mouse;
|
||||
int mouse_x, mouse_y;
|
||||
int mouse_wheel, mouse_hwheel;
|
||||
int min_x, max_x, range_x;
|
||||
int min_y, max_y, range_y;
|
||||
|
||||
struct SDL_evdevlist_item *next;
|
||||
} SDL_evdevlist_item;
|
||||
|
||||
typedef struct SDL_EVDEV_PrivateData
|
||||
{
|
||||
int ref_count;
|
||||
int num_devices;
|
||||
SDL_evdevlist_item *first;
|
||||
SDL_evdevlist_item *last;
|
||||
SDL_EVDEV_keyboard_state *kbd;
|
||||
} SDL_EVDEV_PrivateData;
|
||||
|
||||
static SDL_EVDEV_PrivateData *_this = NULL;
|
||||
|
||||
static SDL_Scancode SDL_EVDEV_translate_keycode(int keycode);
|
||||
static void SDL_EVDEV_sync_device(SDL_evdevlist_item *item);
|
||||
static int SDL_EVDEV_device_removed(const char *dev_path);
|
||||
|
||||
static int SDL_EVDEV_device_added(const char *dev_path, int udev_class);
|
||||
#ifdef SDL_USE_LIBUDEV
|
||||
static void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_event, int udev_class, const char *dev_path);
|
||||
#endif /* SDL_USE_LIBUDEV */
|
||||
|
||||
static Uint8 EVDEV_MouseButtons[] = {
|
||||
SDL_BUTTON_LEFT, /* BTN_LEFT 0x110 */
|
||||
SDL_BUTTON_RIGHT, /* BTN_RIGHT 0x111 */
|
||||
SDL_BUTTON_MIDDLE, /* BTN_MIDDLE 0x112 */
|
||||
SDL_BUTTON_X1, /* BTN_SIDE 0x113 */
|
||||
SDL_BUTTON_X2, /* BTN_EXTRA 0x114 */
|
||||
SDL_BUTTON_X2 + 1, /* BTN_FORWARD 0x115 */
|
||||
SDL_BUTTON_X2 + 2, /* BTN_BACK 0x116 */
|
||||
SDL_BUTTON_X2 + 3 /* BTN_TASK 0x117 */
|
||||
};
|
||||
|
||||
static int SDL_EVDEV_SetRelativeMouseMode(SDL_bool enabled)
|
||||
{
|
||||
/* Mice already send relative events through this interface */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SDL_EVDEV_Init(void)
|
||||
{
|
||||
if (_this == NULL) {
|
||||
_this = (SDL_EVDEV_PrivateData *)SDL_calloc(1, sizeof(*_this));
|
||||
if (_this == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
#ifdef SDL_USE_LIBUDEV
|
||||
if (SDL_UDEV_Init() < 0) {
|
||||
SDL_free(_this);
|
||||
_this = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set up the udev callback */
|
||||
if (SDL_UDEV_AddCallback(SDL_EVDEV_udev_callback) < 0) {
|
||||
SDL_UDEV_Quit();
|
||||
SDL_free(_this);
|
||||
_this = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Force a scan to build the initial device list */
|
||||
SDL_UDEV_Scan();
|
||||
#else
|
||||
{
|
||||
/* Allow the user to specify a list of devices explicitly of
|
||||
the form:
|
||||
deviceclass:path[,deviceclass:path[,...]]
|
||||
where device class is an integer representing the
|
||||
SDL_UDEV_deviceclass and path is the full path to
|
||||
the event device. */
|
||||
const char *devices = SDL_getenv("SDL_EVDEV_DEVICES");
|
||||
if (devices) {
|
||||
/* Assume this is the old use of the env var and it is not in
|
||||
ROM. */
|
||||
char *rest = (char *)devices;
|
||||
char *spec;
|
||||
while ((spec = SDL_strtok_r(rest, ",", &rest))) {
|
||||
char *endofcls = 0;
|
||||
long cls = SDL_strtol(spec, &endofcls, 0);
|
||||
if (endofcls) {
|
||||
SDL_EVDEV_device_added(endofcls + 1, cls);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* TODO: Scan the devices manually, like a caveman */
|
||||
}
|
||||
}
|
||||
#endif /* SDL_USE_LIBUDEV */
|
||||
|
||||
_this->kbd = SDL_EVDEV_kbd_init();
|
||||
}
|
||||
|
||||
SDL_GetMouse()->SetRelativeMouseMode = SDL_EVDEV_SetRelativeMouseMode;
|
||||
|
||||
_this->ref_count += 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SDL_EVDEV_Quit(void)
|
||||
{
|
||||
if (_this == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
_this->ref_count -= 1;
|
||||
|
||||
if (_this->ref_count < 1) {
|
||||
#ifdef SDL_USE_LIBUDEV
|
||||
SDL_UDEV_DelCallback(SDL_EVDEV_udev_callback);
|
||||
SDL_UDEV_Quit();
|
||||
#endif /* SDL_USE_LIBUDEV */
|
||||
|
||||
SDL_EVDEV_kbd_quit(_this->kbd);
|
||||
|
||||
/* Remove existing devices */
|
||||
while (_this->first != NULL) {
|
||||
SDL_EVDEV_device_removed(_this->first->path);
|
||||
}
|
||||
|
||||
SDL_assert(_this->first == NULL);
|
||||
SDL_assert(_this->last == NULL);
|
||||
SDL_assert(_this->num_devices == 0);
|
||||
|
||||
SDL_free(_this);
|
||||
_this = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SDL_USE_LIBUDEV
|
||||
static void SDL_EVDEV_udev_callback(SDL_UDEV_deviceevent udev_event, int udev_class,
|
||||
const char *dev_path)
|
||||
{
|
||||
if (dev_path == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (udev_event) {
|
||||
case SDL_UDEV_DEVICEADDED:
|
||||
if (!(udev_class & (SDL_UDEV_DEVICE_MOUSE | SDL_UDEV_DEVICE_HAS_KEYS | SDL_UDEV_DEVICE_TOUCHSCREEN | SDL_UDEV_DEVICE_TOUCHPAD))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (udev_class & SDL_UDEV_DEVICE_JOYSTICK) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_EVDEV_device_added(dev_path, udev_class);
|
||||
break;
|
||||
case SDL_UDEV_DEVICEREMOVED:
|
||||
SDL_EVDEV_device_removed(dev_path);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* SDL_USE_LIBUDEV */
|
||||
|
||||
void SDL_EVDEV_Poll(void)
|
||||
{
|
||||
struct input_event events[32];
|
||||
int i, j, len;
|
||||
SDL_evdevlist_item *item;
|
||||
SDL_Scancode scan_code;
|
||||
int mouse_button;
|
||||
SDL_Mouse *mouse;
|
||||
float norm_x, norm_y, norm_pressure;
|
||||
|
||||
if (!_this) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef SDL_USE_LIBUDEV
|
||||
SDL_UDEV_Poll();
|
||||
#endif
|
||||
|
||||
mouse = SDL_GetMouse();
|
||||
|
||||
for (item = _this->first; item != NULL; item = item->next) {
|
||||
while ((len = read(item->fd, events, sizeof(events))) > 0) {
|
||||
len /= sizeof(events[0]);
|
||||
for (i = 0; i < len; ++i) {
|
||||
struct input_event *event = &events[i];
|
||||
|
||||
/* special handling for touchscreen, that should eventually be
|
||||
used for all devices */
|
||||
if (item->out_of_sync && item->is_touchscreen &&
|
||||
event->type == EV_SYN && event->code != SYN_REPORT) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (event->type) {
|
||||
case EV_KEY:
|
||||
if (event->code >= BTN_MOUSE && event->code < BTN_MOUSE + SDL_arraysize(EVDEV_MouseButtons)) {
|
||||
mouse_button = event->code - BTN_MOUSE;
|
||||
if (event->value == 0) {
|
||||
SDL_SendMouseButton(SDL_EVDEV_GetEventTimestamp(event), mouse->focus, (SDL_MouseID)item->fd, SDL_RELEASED, EVDEV_MouseButtons[mouse_button]);
|
||||
} else if (event->value == 1) {
|
||||
SDL_SendMouseButton(SDL_EVDEV_GetEventTimestamp(event), mouse->focus, (SDL_MouseID)item->fd, SDL_PRESSED, EVDEV_MouseButtons[mouse_button]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* BTN_TOUCH event value 1 indicates there is contact with
|
||||
a touchscreen or trackpad (earlist finger's current
|
||||
position is sent in EV_ABS ABS_X/ABS_Y, switching to
|
||||
next finger after earlist is released) */
|
||||
if (item->is_touchscreen && event->code == BTN_TOUCH) {
|
||||
if (item->touchscreen_data->max_slots == 1) {
|
||||
if (event->value) {
|
||||
item->touchscreen_data->slots[0].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
|
||||
} else {
|
||||
item->touchscreen_data->slots[0].delta = EVDEV_TOUCH_SLOTDELTA_UP;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Probably keyboard */
|
||||
scan_code = SDL_EVDEV_translate_keycode(event->code);
|
||||
if (scan_code != SDL_SCANCODE_UNKNOWN) {
|
||||
if (event->value == 0) {
|
||||
SDL_SendKeyboardKey(SDL_EVDEV_GetEventTimestamp(event), SDL_RELEASED, scan_code);
|
||||
} else if (event->value == 1 || event->value == 2 /* key repeated */) {
|
||||
SDL_SendKeyboardKey(SDL_EVDEV_GetEventTimestamp(event), SDL_PRESSED, scan_code);
|
||||
}
|
||||
}
|
||||
SDL_EVDEV_kbd_keycode(_this->kbd, event->code, event->value);
|
||||
break;
|
||||
case EV_ABS:
|
||||
switch (event->code) {
|
||||
case ABS_MT_SLOT:
|
||||
if (!item->is_touchscreen) { /* FIXME: temp hack */
|
||||
break;
|
||||
}
|
||||
item->touchscreen_data->current_slot = event->value;
|
||||
break;
|
||||
case ABS_MT_TRACKING_ID:
|
||||
if (!item->is_touchscreen) { /* FIXME: temp hack */
|
||||
break;
|
||||
}
|
||||
if (event->value >= 0) {
|
||||
item->touchscreen_data->slots[item->touchscreen_data->current_slot].tracking_id = event->value;
|
||||
item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
|
||||
} else {
|
||||
item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_UP;
|
||||
}
|
||||
break;
|
||||
case ABS_MT_POSITION_X:
|
||||
if (!item->is_touchscreen) { /* FIXME: temp hack */
|
||||
break;
|
||||
}
|
||||
item->touchscreen_data->slots[item->touchscreen_data->current_slot].x = event->value;
|
||||
if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
|
||||
item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
|
||||
}
|
||||
break;
|
||||
case ABS_MT_POSITION_Y:
|
||||
if (!item->is_touchscreen) { /* FIXME: temp hack */
|
||||
break;
|
||||
}
|
||||
item->touchscreen_data->slots[item->touchscreen_data->current_slot].y = event->value;
|
||||
if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
|
||||
item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
|
||||
}
|
||||
break;
|
||||
case ABS_MT_PRESSURE:
|
||||
if (!item->is_touchscreen) { /* FIXME: temp hack */
|
||||
break;
|
||||
}
|
||||
item->touchscreen_data->slots[item->touchscreen_data->current_slot].pressure = event->value;
|
||||
if (item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta == EVDEV_TOUCH_SLOTDELTA_NONE) {
|
||||
item->touchscreen_data->slots[item->touchscreen_data->current_slot].delta = EVDEV_TOUCH_SLOTDELTA_MOVE;
|
||||
}
|
||||
break;
|
||||
case ABS_X:
|
||||
if (item->is_touchscreen) {
|
||||
if (item->touchscreen_data->max_slots != 1) {
|
||||
break;
|
||||
}
|
||||
item->touchscreen_data->slots[0].x = event->value;
|
||||
} else if (!item->relative_mouse) {
|
||||
item->mouse_x = event->value;
|
||||
}
|
||||
break;
|
||||
case ABS_Y:
|
||||
if (item->is_touchscreen) {
|
||||
if (item->touchscreen_data->max_slots != 1) {
|
||||
break;
|
||||
}
|
||||
item->touchscreen_data->slots[0].y = event->value;
|
||||
} else if (!item->relative_mouse) {
|
||||
item->mouse_y = event->value;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case EV_REL:
|
||||
switch (event->code) {
|
||||
case REL_X:
|
||||
if (item->relative_mouse) {
|
||||
item->mouse_x += event->value;
|
||||
}
|
||||
break;
|
||||
case REL_Y:
|
||||
if (item->relative_mouse) {
|
||||
item->mouse_y += event->value;
|
||||
}
|
||||
break;
|
||||
case REL_WHEEL:
|
||||
if (!item->high_res_wheel) {
|
||||
item->mouse_wheel += event->value;
|
||||
}
|
||||
break;
|
||||
case REL_WHEEL_HI_RES:
|
||||
SDL_assert(item->high_res_wheel);
|
||||
item->mouse_wheel += event->value;
|
||||
break;
|
||||
case REL_HWHEEL:
|
||||
if (!item->high_res_hwheel) {
|
||||
item->mouse_hwheel += event->value;
|
||||
}
|
||||
break;
|
||||
case REL_HWHEEL_HI_RES:
|
||||
SDL_assert(item->high_res_hwheel);
|
||||
item->mouse_hwheel += event->value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case EV_SYN:
|
||||
switch (event->code) {
|
||||
case SYN_REPORT:
|
||||
/* Send mouse axis changes together to ensure consistency and reduce event processing overhead */
|
||||
if (item->relative_mouse) {
|
||||
if (item->mouse_x != 0 || item->mouse_y != 0) {
|
||||
SDL_SendMouseMotion(SDL_EVDEV_GetEventTimestamp(event), mouse->focus, (SDL_MouseID)item->fd, item->relative_mouse, (float)item->mouse_x, (float)item->mouse_y);
|
||||
item->mouse_x = item->mouse_y = 0;
|
||||
}
|
||||
} else if (item->range_x > 0 && item->range_y > 0) {
|
||||
int screen_w = 0, screen_h = 0;
|
||||
const SDL_DisplayMode *mode = NULL;
|
||||
|
||||
if (mouse->focus) {
|
||||
mode = SDL_GetCurrentDisplayMode(SDL_GetDisplayForWindow(mouse->focus));
|
||||
}
|
||||
if (!mode) {
|
||||
mode = SDL_GetCurrentDisplayMode(SDL_GetPrimaryDisplay());
|
||||
}
|
||||
if (mode) {
|
||||
screen_w = mode->w;
|
||||
screen_h = mode->h;
|
||||
}
|
||||
SDL_SendMouseMotion(SDL_EVDEV_GetEventTimestamp(event), mouse->focus, (SDL_MouseID)item->fd, item->relative_mouse,
|
||||
(float)(item->mouse_x - item->min_x) * screen_w / item->range_x,
|
||||
(float)(item->mouse_y - item->min_y) * screen_h / item->range_y);
|
||||
}
|
||||
|
||||
if (item->mouse_wheel != 0 || item->mouse_hwheel != 0) {
|
||||
SDL_SendMouseWheel(SDL_EVDEV_GetEventTimestamp(event),
|
||||
mouse->focus, (SDL_MouseID)item->fd,
|
||||
item->mouse_hwheel / (item->high_res_hwheel ? 120.0f : 1.0f),
|
||||
item->mouse_wheel / (item->high_res_wheel ? 120.0f : 1.0f),
|
||||
SDL_MOUSEWHEEL_NORMAL);
|
||||
item->mouse_wheel = item->mouse_hwheel = 0;
|
||||
}
|
||||
|
||||
if (!item->is_touchscreen) { /* FIXME: temp hack */
|
||||
break;
|
||||
}
|
||||
|
||||
for (j = 0; j < item->touchscreen_data->max_slots; j++) {
|
||||
norm_x = (float)(item->touchscreen_data->slots[j].x - item->touchscreen_data->min_x) /
|
||||
(float)item->touchscreen_data->range_x;
|
||||
norm_y = (float)(item->touchscreen_data->slots[j].y - item->touchscreen_data->min_y) /
|
||||
(float)item->touchscreen_data->range_y;
|
||||
|
||||
if (item->touchscreen_data->range_pressure > 0) {
|
||||
norm_pressure = (float)(item->touchscreen_data->slots[j].pressure - item->touchscreen_data->min_pressure) /
|
||||
(float)item->touchscreen_data->range_pressure;
|
||||
} else {
|
||||
/* This touchscreen does not support pressure */
|
||||
norm_pressure = 1.0f;
|
||||
}
|
||||
|
||||
/* FIXME: the touch's window shouldn't be null, but
|
||||
* the coordinate space of touch positions needs to
|
||||
* be window-relative in that case. */
|
||||
switch (item->touchscreen_data->slots[j].delta) {
|
||||
case EVDEV_TOUCH_SLOTDELTA_DOWN:
|
||||
SDL_SendTouch(SDL_EVDEV_GetEventTimestamp(event), item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, SDL_TRUE, norm_x, norm_y, norm_pressure);
|
||||
item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
|
||||
break;
|
||||
case EVDEV_TOUCH_SLOTDELTA_UP:
|
||||
SDL_SendTouch(SDL_EVDEV_GetEventTimestamp(event), item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, SDL_FALSE, norm_x, norm_y, norm_pressure);
|
||||
item->touchscreen_data->slots[j].tracking_id = -1;
|
||||
item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
|
||||
break;
|
||||
case EVDEV_TOUCH_SLOTDELTA_MOVE:
|
||||
SDL_SendTouchMotion(SDL_EVDEV_GetEventTimestamp(event), item->fd, item->touchscreen_data->slots[j].tracking_id, NULL, norm_x, norm_y, norm_pressure);
|
||||
item->touchscreen_data->slots[j].delta = EVDEV_TOUCH_SLOTDELTA_NONE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->out_of_sync) {
|
||||
item->out_of_sync = SDL_FALSE;
|
||||
}
|
||||
break;
|
||||
case SYN_DROPPED:
|
||||
if (item->is_touchscreen) {
|
||||
item->out_of_sync = SDL_TRUE;
|
||||
}
|
||||
SDL_EVDEV_sync_device(item);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static SDL_Scancode SDL_EVDEV_translate_keycode(int keycode)
|
||||
{
|
||||
SDL_Scancode scancode = SDL_GetScancodeFromTable(SDL_SCANCODE_TABLE_LINUX, keycode);
|
||||
|
||||
#ifdef DEBUG_SCANCODES
|
||||
if (scancode == SDL_SCANCODE_UNKNOWN) {
|
||||
/* BTN_TOUCH is handled elsewhere, but we might still end up here if
|
||||
you get an unexpected BTN_TOUCH from something SDL believes is not
|
||||
a touch device. In this case, we'd rather not get a misleading
|
||||
SDL_Log message about an unknown key. */
|
||||
if (keycode != BTN_TOUCH) {
|
||||
SDL_Log("The key you just pressed is not recognized by SDL. To help "
|
||||
"get this fixed, please report this to the SDL forums/mailing list "
|
||||
"<https://discourse.libsdl.org/> EVDEV KeyCode %d",
|
||||
keycode);
|
||||
}
|
||||
}
|
||||
#endif /* DEBUG_SCANCODES */
|
||||
|
||||
return scancode;
|
||||
}
|
||||
|
||||
static int SDL_EVDEV_init_mouse(SDL_evdevlist_item *item, int udev_class)
|
||||
{
|
||||
int ret;
|
||||
struct input_absinfo abs_info;
|
||||
|
||||
ret = ioctl(item->fd, EVIOCGABS(ABS_X), &abs_info);
|
||||
if (ret < 0) {
|
||||
// no absolute mode info, continue
|
||||
return 0;
|
||||
}
|
||||
item->min_x = abs_info.minimum;
|
||||
item->max_x = abs_info.maximum;
|
||||
item->range_x = abs_info.maximum - abs_info.minimum;
|
||||
|
||||
ret = ioctl(item->fd, EVIOCGABS(ABS_Y), &abs_info);
|
||||
if (ret < 0) {
|
||||
// no absolute mode info, continue
|
||||
return 0;
|
||||
}
|
||||
item->min_y = abs_info.minimum;
|
||||
item->max_y = abs_info.maximum;
|
||||
item->range_y = abs_info.maximum - abs_info.minimum;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int SDL_EVDEV_init_touchscreen(SDL_evdevlist_item *item, int udev_class)
|
||||
{
|
||||
int ret, i;
|
||||
unsigned long xreq, yreq;
|
||||
char name[64];
|
||||
struct input_absinfo abs_info;
|
||||
|
||||
if (!item->is_touchscreen) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
item->touchscreen_data = SDL_calloc(1, sizeof(*item->touchscreen_data));
|
||||
if (item->touchscreen_data == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
ret = ioctl(item->fd, EVIOCGNAME(sizeof(name)), name);
|
||||
if (ret < 0) {
|
||||
SDL_free(item->touchscreen_data);
|
||||
return SDL_SetError("Failed to get evdev touchscreen name");
|
||||
}
|
||||
|
||||
item->touchscreen_data->name = SDL_strdup(name);
|
||||
if (item->touchscreen_data->name == NULL) {
|
||||
SDL_free(item->touchscreen_data);
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
|
||||
if (ret < 0) {
|
||||
SDL_free(item->touchscreen_data->name);
|
||||
SDL_free(item->touchscreen_data);
|
||||
return SDL_SetError("Failed to get evdev touchscreen limits");
|
||||
}
|
||||
|
||||
if (abs_info.maximum == 0) {
|
||||
item->touchscreen_data->max_slots = 1;
|
||||
xreq = EVIOCGABS(ABS_X);
|
||||
yreq = EVIOCGABS(ABS_Y);
|
||||
} else {
|
||||
item->touchscreen_data->max_slots = abs_info.maximum + 1;
|
||||
xreq = EVIOCGABS(ABS_MT_POSITION_X);
|
||||
yreq = EVIOCGABS(ABS_MT_POSITION_Y);
|
||||
}
|
||||
|
||||
ret = ioctl(item->fd, xreq, &abs_info);
|
||||
if (ret < 0) {
|
||||
SDL_free(item->touchscreen_data->name);
|
||||
SDL_free(item->touchscreen_data);
|
||||
return SDL_SetError("Failed to get evdev touchscreen limits");
|
||||
}
|
||||
item->touchscreen_data->min_x = abs_info.minimum;
|
||||
item->touchscreen_data->max_x = abs_info.maximum;
|
||||
item->touchscreen_data->range_x = abs_info.maximum - abs_info.minimum;
|
||||
|
||||
ret = ioctl(item->fd, yreq, &abs_info);
|
||||
if (ret < 0) {
|
||||
SDL_free(item->touchscreen_data->name);
|
||||
SDL_free(item->touchscreen_data);
|
||||
return SDL_SetError("Failed to get evdev touchscreen limits");
|
||||
}
|
||||
item->touchscreen_data->min_y = abs_info.minimum;
|
||||
item->touchscreen_data->max_y = abs_info.maximum;
|
||||
item->touchscreen_data->range_y = abs_info.maximum - abs_info.minimum;
|
||||
|
||||
ret = ioctl(item->fd, EVIOCGABS(ABS_MT_PRESSURE), &abs_info);
|
||||
if (ret < 0) {
|
||||
SDL_free(item->touchscreen_data->name);
|
||||
SDL_free(item->touchscreen_data);
|
||||
return SDL_SetError("Failed to get evdev touchscreen limits");
|
||||
}
|
||||
item->touchscreen_data->min_pressure = abs_info.minimum;
|
||||
item->touchscreen_data->max_pressure = abs_info.maximum;
|
||||
item->touchscreen_data->range_pressure = abs_info.maximum - abs_info.minimum;
|
||||
|
||||
item->touchscreen_data->slots = SDL_calloc(
|
||||
item->touchscreen_data->max_slots,
|
||||
sizeof(*item->touchscreen_data->slots));
|
||||
if (item->touchscreen_data->slots == NULL) {
|
||||
SDL_free(item->touchscreen_data->name);
|
||||
SDL_free(item->touchscreen_data);
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
for (i = 0; i < item->touchscreen_data->max_slots; i++) {
|
||||
item->touchscreen_data->slots[i].tracking_id = -1;
|
||||
}
|
||||
|
||||
ret = SDL_AddTouch(item->fd, /* I guess our fd is unique enough */
|
||||
(udev_class & SDL_UDEV_DEVICE_TOUCHPAD) ? SDL_TOUCH_DEVICE_INDIRECT_ABSOLUTE : SDL_TOUCH_DEVICE_DIRECT,
|
||||
item->touchscreen_data->name);
|
||||
if (ret < 0) {
|
||||
SDL_free(item->touchscreen_data->slots);
|
||||
SDL_free(item->touchscreen_data->name);
|
||||
SDL_free(item->touchscreen_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void SDL_EVDEV_destroy_touchscreen(SDL_evdevlist_item *item)
|
||||
{
|
||||
if (!item->is_touchscreen) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_DelTouch(item->fd);
|
||||
SDL_free(item->touchscreen_data->slots);
|
||||
SDL_free(item->touchscreen_data->name);
|
||||
SDL_free(item->touchscreen_data);
|
||||
}
|
||||
|
||||
static void SDL_EVDEV_sync_device(SDL_evdevlist_item *item)
|
||||
{
|
||||
#ifdef EVIOCGMTSLOTS
|
||||
int i, ret;
|
||||
struct input_absinfo abs_info;
|
||||
/*
|
||||
* struct input_mt_request_layout {
|
||||
* __u32 code;
|
||||
* __s32 values[num_slots];
|
||||
* };
|
||||
*
|
||||
* this is the structure we're trying to emulate
|
||||
*/
|
||||
Uint32 *mt_req_code;
|
||||
Sint32 *mt_req_values;
|
||||
size_t mt_req_size;
|
||||
|
||||
/* TODO: sync devices other than touchscreen */
|
||||
if (!item->is_touchscreen) {
|
||||
return;
|
||||
}
|
||||
|
||||
mt_req_size = sizeof(*mt_req_code) +
|
||||
sizeof(*mt_req_values) * item->touchscreen_data->max_slots;
|
||||
|
||||
mt_req_code = SDL_calloc(1, mt_req_size);
|
||||
if (mt_req_code == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
mt_req_values = (Sint32 *)mt_req_code + 1;
|
||||
|
||||
*mt_req_code = ABS_MT_TRACKING_ID;
|
||||
ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
|
||||
if (ret < 0) {
|
||||
SDL_free(mt_req_code);
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < item->touchscreen_data->max_slots; i++) {
|
||||
/*
|
||||
* This doesn't account for the very edge case of the user removing their
|
||||
* finger and replacing it on the screen during the time we're out of sync,
|
||||
* which'll mean that we're not going from down -> up or up -> down, we're
|
||||
* going from down -> down but with a different tracking id, meaning we'd
|
||||
* have to tell SDL of the two events, but since we wait till SYN_REPORT in
|
||||
* SDL_EVDEV_Poll to tell SDL, the current structure of this code doesn't
|
||||
* allow it. Lets just pray to God it doesn't happen.
|
||||
*/
|
||||
if (item->touchscreen_data->slots[i].tracking_id < 0 &&
|
||||
mt_req_values[i] >= 0) {
|
||||
item->touchscreen_data->slots[i].tracking_id = mt_req_values[i];
|
||||
item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_DOWN;
|
||||
} else if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
|
||||
mt_req_values[i] < 0) {
|
||||
item->touchscreen_data->slots[i].tracking_id = -1;
|
||||
item->touchscreen_data->slots[i].delta = EVDEV_TOUCH_SLOTDELTA_UP;
|
||||
}
|
||||
}
|
||||
|
||||
*mt_req_code = ABS_MT_POSITION_X;
|
||||
ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
|
||||
if (ret < 0) {
|
||||
SDL_free(mt_req_code);
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < item->touchscreen_data->max_slots; i++) {
|
||||
if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
|
||||
item->touchscreen_data->slots[i].x != mt_req_values[i]) {
|
||||
item->touchscreen_data->slots[i].x = mt_req_values[i];
|
||||
if (item->touchscreen_data->slots[i].delta ==
|
||||
EVDEV_TOUCH_SLOTDELTA_NONE) {
|
||||
item->touchscreen_data->slots[i].delta =
|
||||
EVDEV_TOUCH_SLOTDELTA_MOVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*mt_req_code = ABS_MT_POSITION_Y;
|
||||
ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
|
||||
if (ret < 0) {
|
||||
SDL_free(mt_req_code);
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < item->touchscreen_data->max_slots; i++) {
|
||||
if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
|
||||
item->touchscreen_data->slots[i].y != mt_req_values[i]) {
|
||||
item->touchscreen_data->slots[i].y = mt_req_values[i];
|
||||
if (item->touchscreen_data->slots[i].delta ==
|
||||
EVDEV_TOUCH_SLOTDELTA_NONE) {
|
||||
item->touchscreen_data->slots[i].delta =
|
||||
EVDEV_TOUCH_SLOTDELTA_MOVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*mt_req_code = ABS_MT_PRESSURE;
|
||||
ret = ioctl(item->fd, EVIOCGMTSLOTS(mt_req_size), mt_req_code);
|
||||
if (ret < 0) {
|
||||
SDL_free(mt_req_code);
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < item->touchscreen_data->max_slots; i++) {
|
||||
if (item->touchscreen_data->slots[i].tracking_id >= 0 &&
|
||||
item->touchscreen_data->slots[i].pressure != mt_req_values[i]) {
|
||||
item->touchscreen_data->slots[i].pressure = mt_req_values[i];
|
||||
if (item->touchscreen_data->slots[i].delta ==
|
||||
EVDEV_TOUCH_SLOTDELTA_NONE) {
|
||||
item->touchscreen_data->slots[i].delta =
|
||||
EVDEV_TOUCH_SLOTDELTA_MOVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = ioctl(item->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info);
|
||||
if (ret < 0) {
|
||||
SDL_free(mt_req_code);
|
||||
return;
|
||||
}
|
||||
item->touchscreen_data->current_slot = abs_info.value;
|
||||
|
||||
SDL_free(mt_req_code);
|
||||
|
||||
#endif /* EVIOCGMTSLOTS */
|
||||
}
|
||||
|
||||
static int SDL_EVDEV_device_added(const char *dev_path, int udev_class)
|
||||
{
|
||||
SDL_evdevlist_item *item;
|
||||
unsigned long relbit[NBITS(REL_MAX)] = { 0 };
|
||||
|
||||
/* Check to make sure it's not already in list. */
|
||||
for (item = _this->first; item != NULL; item = item->next) {
|
||||
if (SDL_strcmp(dev_path, item->path) == 0) {
|
||||
return -1; /* already have this one */
|
||||
}
|
||||
}
|
||||
|
||||
item = (SDL_evdevlist_item *)SDL_calloc(1, sizeof(SDL_evdevlist_item));
|
||||
if (item == NULL) {
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
item->fd = open(dev_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
|
||||
if (item->fd < 0) {
|
||||
SDL_free(item);
|
||||
return SDL_SetError("Unable to open %s", dev_path);
|
||||
}
|
||||
|
||||
item->path = SDL_strdup(dev_path);
|
||||
if (item->path == NULL) {
|
||||
close(item->fd);
|
||||
SDL_free(item);
|
||||
return SDL_OutOfMemory();
|
||||
}
|
||||
|
||||
if (ioctl(item->fd, EVIOCGBIT(EV_REL, sizeof(relbit)), relbit) >= 0) {
|
||||
item->relative_mouse = test_bit(REL_X, relbit) && test_bit(REL_Y, relbit);
|
||||
item->high_res_wheel = test_bit(REL_WHEEL_HI_RES, relbit);
|
||||
item->high_res_hwheel = test_bit(REL_HWHEEL_HI_RES, relbit);
|
||||
}
|
||||
|
||||
/* For now, we just treat a touchpad like a touchscreen */
|
||||
if (udev_class & (SDL_UDEV_DEVICE_TOUCHSCREEN | SDL_UDEV_DEVICE_TOUCHPAD)) {
|
||||
int ret;
|
||||
item->is_touchscreen = SDL_TRUE;
|
||||
ret = SDL_EVDEV_init_touchscreen(item, udev_class);
|
||||
if (ret < 0) {
|
||||
close(item->fd);
|
||||
SDL_free(item->path);
|
||||
SDL_free(item);
|
||||
return ret;
|
||||
}
|
||||
} else if (udev_class & SDL_UDEV_DEVICE_MOUSE) {
|
||||
int ret = SDL_EVDEV_init_mouse(item, udev_class);
|
||||
if (ret < 0) {
|
||||
close(item->fd);
|
||||
SDL_free(item->path);
|
||||
SDL_free(item);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (_this->last == NULL) {
|
||||
_this->first = _this->last = item;
|
||||
} else {
|
||||
_this->last->next = item;
|
||||
_this->last = item;
|
||||
}
|
||||
|
||||
SDL_EVDEV_sync_device(item);
|
||||
|
||||
return _this->num_devices++;
|
||||
}
|
||||
|
||||
static int SDL_EVDEV_device_removed(const char *dev_path)
|
||||
{
|
||||
SDL_evdevlist_item *item;
|
||||
SDL_evdevlist_item *prev = NULL;
|
||||
|
||||
for (item = _this->first; item != NULL; item = item->next) {
|
||||
/* found it, remove it. */
|
||||
if (SDL_strcmp(dev_path, item->path) == 0) {
|
||||
if (prev != NULL) {
|
||||
prev->next = item->next;
|
||||
} else {
|
||||
SDL_assert(_this->first == item);
|
||||
_this->first = item->next;
|
||||
}
|
||||
if (item == _this->last) {
|
||||
_this->last = prev;
|
||||
}
|
||||
if (item->is_touchscreen) {
|
||||
SDL_EVDEV_destroy_touchscreen(item);
|
||||
}
|
||||
close(item->fd);
|
||||
SDL_free(item->path);
|
||||
SDL_free(item);
|
||||
_this->num_devices--;
|
||||
return 0;
|
||||
}
|
||||
prev = item;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
Uint64 SDL_EVDEV_GetEventTimestamp(struct input_event *event)
|
||||
{
|
||||
static Uint64 timestamp_offset;
|
||||
Uint64 timestamp;
|
||||
Uint64 now = SDL_GetTicksNS();
|
||||
|
||||
/* The kernel internally has nanosecond timestamps, but converts it
|
||||
to microseconds when delivering the events */
|
||||
timestamp = event->input_event_sec;
|
||||
timestamp *= SDL_NS_PER_SECOND;
|
||||
timestamp += SDL_US_TO_NS(event->input_event_usec);
|
||||
|
||||
if (!timestamp_offset) {
|
||||
timestamp_offset = (now - timestamp);
|
||||
}
|
||||
timestamp += timestamp_offset;
|
||||
|
||||
if (timestamp > now) {
|
||||
timestamp_offset -= (timestamp - now);
|
||||
timestamp = now;
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
#endif /* SDL_INPUT_LINUXEV */
|
38
external/sdl/SDL/src/core/linux/SDL_evdev.h
vendored
Normal file
38
external/sdl/SDL/src/core/linux/SDL_evdev.h
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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 int SDL_EVDEV_Init(void);
|
||||
extern void SDL_EVDEV_Quit(void);
|
||||
extern void SDL_EVDEV_Poll(void);
|
||||
extern Uint64 SDL_EVDEV_GetEventTimestamp(struct input_event *event);
|
||||
|
||||
#endif /* SDL_INPUT_LINUXEV */
|
||||
|
||||
#endif /* SDL_evdev_h_ */
|
167
external/sdl/SDL/src/core/linux/SDL_evdev_capabilities.c
vendored
Normal file
167
external/sdl/SDL/src/core/linux/SDL_evdev_capabilities.c
vendored
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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)) {
|
||||
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 */
|
72
external/sdl/SDL/src/core/linux/SDL_evdev_capabilities.h
vendored
Normal file
72
external/sdl/SDL/src/core/linux/SDL_evdev_capabilities.h
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_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_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_ */
|
819
external/sdl/SDL/src/core/linux/SDL_evdev_kbd.c
vendored
Normal file
819
external/sdl/SDL/src/core/linux/SDL_evdev_kbd.c
vendored
Normal file
@ -0,0 +1,819 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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;
|
||||
int old_kbd_mode;
|
||||
unsigned short **key_maps;
|
||||
unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */
|
||||
SDL_bool dead_key_next;
|
||||
int npadch; /* -1 or number assembled on pad */
|
||||
struct kbdiacrs *accents;
|
||||
unsigned int diacr;
|
||||
SDL_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;
|
||||
};
|
||||
|
||||
#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 == NULL) {
|
||||
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 != NULL) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 == NULL) {
|
||||
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);
|
||||
}
|
||||
|
||||
/* Allow inhibiting keyboard mute with env. variable for debugging etc. */
|
||||
if (SDL_getenv("SDL_INPUT_LINUX_KEEP_KBD") == NULL) {
|
||||
/* Mute the keyboard so keystrokes only generate evdev events
|
||||
* and do not leak through to the console
|
||||
*/
|
||||
ioctl(kbd->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, SDL_FALSE)) {
|
||||
kbd_register_emerg_cleanup(kbd);
|
||||
}
|
||||
}
|
||||
return kbd;
|
||||
}
|
||||
|
||||
void SDL_EVDEV_kbd_quit(SDL_EVDEV_keyboard_state *state)
|
||||
{
|
||||
if (state == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
kbd_unregister_emerg_cleanup();
|
||||
|
||||
if (state->console_fd >= 0) {
|
||||
/* Restore the original keyboard mode */
|
||||
ioctl(state->console_fd, KDSKBMODE, state->old_kbd_mode);
|
||||
|
||||
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 int 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 = SDL_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 = SDL_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 == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->rep = (down == 2);
|
||||
|
||||
shift_final = (state->shift_state | state->slockstate) ^ state->lockstate;
|
||||
key_map = state->key_maps[shift_final];
|
||||
if (key_map == NULL) {
|
||||
/* 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_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 */
|
32
external/sdl/SDL/src/core/linux/SDL_evdev_kbd.h
vendored
Normal file
32
external/sdl/SDL/src/core/linux/SDL_evdev_kbd.h
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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_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
external/sdl/SDL/src/core/linux/SDL_evdev_kbd_default_accents.h
vendored
Normal file
282
external/sdl/SDL/src/core/linux/SDL_evdev_kbd_default_accents.h
vendored
Normal file
@ -0,0 +1,282 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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
external/sdl/SDL/src/core/linux/SDL_evdev_kbd_default_keymap.h
vendored
Normal file
4765
external/sdl/SDL/src/core/linux/SDL_evdev_kbd_default_keymap.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
483
external/sdl/SDL/src/core/linux/SDL_fcitx.c
vendored
Normal file
483
external/sdl/SDL/src/core/linux/SDL_fcitx.c
vendored
Normal file
@ -0,0 +1,483 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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 "../../events/SDL_keyboard_c.h"
|
||||
#include "SDL_dbus.h"
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_X11
|
||||
#include "../../video/x11/SDL_x11video.h"
|
||||
#endif
|
||||
#include <SDL3/SDL_syswm.h>
|
||||
|
||||
#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(__LINUX__) || defined(__FREEBSD__)
|
||||
char *spot;
|
||||
char procfile[1024];
|
||||
char linkfile[1024];
|
||||
int linksize;
|
||||
|
||||
#ifdef __LINUX__
|
||||
(void)SDL_snprintf(procfile, sizeof(procfile), "/proc/%d/exe", getpid());
|
||||
#elif defined(__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 /* __LINUX__ || __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);
|
||||
|
||||
if (text && *text) {
|
||||
char buf[SDL_TEXTINPUTEVENT_TEXT_SIZE];
|
||||
size_t text_bytes = SDL_strlen(text), i = 0;
|
||||
|
||||
while (i < text_bytes) {
|
||||
size_t sz = SDL_utf8strlcpy(buf, text + i, sizeof(buf));
|
||||
SDL_SendKeyboardText(buf);
|
||||
|
||||
i += sz;
|
||||
}
|
||||
}
|
||||
|
||||
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 (SDL_GetHintBoolean(SDL_HINT_IME_SUPPORT_EXTENDED_TEXT, SDL_FALSE)) {
|
||||
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);
|
||||
} else {
|
||||
char buf[SDL_TEXTEDITINGEVENT_TEXT_SIZE];
|
||||
size_t i = 0;
|
||||
size_t cursor = 0;
|
||||
while (i < text_bytes) {
|
||||
const size_t sz = SDL_utf8strlcpy(buf, text + i, sizeof(buf));
|
||||
const size_t chars = SDL_utf8strlen(buf);
|
||||
|
||||
SDL_SendEditingText(buf, cursor, chars);
|
||||
|
||||
i += sz;
|
||||
cursor += chars;
|
||||
}
|
||||
}
|
||||
SDL_free(text);
|
||||
} else {
|
||||
SDL_SendEditingText("", 0, 0);
|
||||
}
|
||||
|
||||
SDL_Fcitx_UpdateTextRect(NULL);
|
||||
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 *internal_editing)
|
||||
{
|
||||
FcitxClient *client = (FcitxClient *)data;
|
||||
Uint64 caps = 0;
|
||||
if (!client->ic_path) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(internal_editing && *internal_editing == '1')) {
|
||||
caps |= (1 << 1); /* Preedit Flag */
|
||||
caps |= (1 << 4); /* Formatted Preedit Flag */
|
||||
}
|
||||
|
||||
SDL_DBus_CallVoidMethod(FCITX_DBUS_SERVICE, client->ic_path, FCITX_IC_DBUS_INTERFACE, "SetCapability", DBUS_TYPE_UINT64, &caps, DBUS_TYPE_INVALID);
|
||||
}
|
||||
|
||||
static SDL_bool FcitxCreateInputContext(SDL_DBusContext *dbus, const char *appname, char **ic_path)
|
||||
{
|
||||
const char *program = "program";
|
||||
SDL_bool retval = SDL_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)) {
|
||||
retval = SDL_TRUE;
|
||||
}
|
||||
dbus->message_unref(reply);
|
||||
}
|
||||
dbus->message_unref(msg);
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static SDL_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_INTERNAL_EDITING, Fcitx_SetCapabilities, client);
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
return SDL_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;
|
||||
}
|
||||
|
||||
SDL_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(SDL_bool focused)
|
||||
{
|
||||
if (focused) {
|
||||
FcitxClientICCallMethod(&fcitx_client, "FocusIn");
|
||||
} else {
|
||||
FcitxClientICCallMethod(&fcitx_client, "FocusOut");
|
||||
}
|
||||
}
|
||||
|
||||
void SDL_Fcitx_Reset(void)
|
||||
{
|
||||
FcitxClientICCallMethod(&fcitx_client, "Reset");
|
||||
}
|
||||
|
||||
SDL_bool SDL_Fcitx_ProcessKeyEvent(Uint32 keysym, Uint32 keycode, Uint8 state)
|
||||
{
|
||||
Uint32 mod_state = Fcitx_ModState();
|
||||
Uint32 handled = SDL_FALSE;
|
||||
Uint32 is_release = (state == SDL_RELEASED);
|
||||
Uint32 event_time = 0;
|
||||
|
||||
if (!fcitx_client.ic_path) {
|
||||
return SDL_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_UpdateTextRect(NULL);
|
||||
return SDL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
void SDL_Fcitx_UpdateTextRect(const SDL_Rect *rect)
|
||||
{
|
||||
SDL_Window *focused_win = NULL;
|
||||
SDL_SysWMinfo info;
|
||||
int x = 0, y = 0;
|
||||
SDL_Rect *cursor = &fcitx_client.cursor_rect;
|
||||
|
||||
if (rect) {
|
||||
SDL_copyp(cursor, rect);
|
||||
}
|
||||
|
||||
focused_win = SDL_GetKeyboardFocus();
|
||||
if (focused_win == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_GetWindowPosition(focused_win, &x, &y);
|
||||
|
||||
if (SDL_GetWindowWMInfo(focused_win, &info, SDL_SYSWM_CURRENT_VERSION) == 0) {
|
||||
#ifdef SDL_ENABLE_SYSWM_X11
|
||||
if (info.subsystem == SDL_SYSWM_X11) {
|
||||
Display *x_disp = info.info.x11.display;
|
||||
int x_screen = info.info.x11.screen;
|
||||
Window x_win = info.info.x11.window;
|
||||
Window unused;
|
||||
X11_XTranslateCoordinates(x_disp, x_win, RootWindow(x_disp, x_screen), 0, 0, &x, &y, &unused);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (cursor->x == -1 && cursor->y == -1 && cursor->w == 0 && cursor->h == 0) {
|
||||
/* move to bottom left */
|
||||
int w = 0, h = 0;
|
||||
SDL_GetWindowSize(focused_win, &w, &h);
|
||||
cursor->x = 0;
|
||||
cursor->y = h;
|
||||
}
|
||||
|
||||
x += cursor->x;
|
||||
y += cursor->y;
|
||||
|
||||
SDL_DBus_CallVoidMethod(FCITX_DBUS_SERVICE, fcitx_client.ic_path, FCITX_IC_DBUS_INTERFACE, "SetCursorRect",
|
||||
DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y, DBUS_TYPE_INT32, &cursor->w, DBUS_TYPE_INT32, &cursor->h, DBUS_TYPE_INVALID);
|
||||
}
|
||||
|
||||
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 */
|
||||
usleep(10);
|
||||
}
|
||||
}
|
35
external/sdl/SDL/src/core/linux/SDL_fcitx.h
vendored
Normal file
35
external/sdl/SDL/src/core/linux/SDL_fcitx.h
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2023 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 SDL_bool SDL_Fcitx_Init(void);
|
||||
extern void SDL_Fcitx_Quit(void);
|
||||
extern void SDL_Fcitx_SetFocus(SDL_bool focused);
|
||||
extern void SDL_Fcitx_Reset(void);
|
||||
extern SDL_bool SDL_Fcitx_ProcessKeyEvent(Uint32 keysym, Uint32 keycode, Uint8 state);
|
||||
extern void SDL_Fcitx_UpdateTextRect(const SDL_Rect *rect);
|
||||
extern void SDL_Fcitx_PumpEvents(void);
|
||||
|
||||
#endif /* SDL_fcitx_h_ */
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user