diff -ruN SDL2-2.32.8/cmake/sdlplatform.cmake SDL2-2.32.8-banan_os/cmake/sdlplatform.cmake --- SDL2-2.32.8/cmake/sdlplatform.cmake 2024-08-14 13:35:43.000000000 +0300 +++ SDL2-2.32.8-banan_os/cmake/sdlplatform.cmake 2026-01-03 19:50:45.777185459 +0200 @@ -28,6 +28,8 @@ set(SDL_CMAKE_PLATFORM AIX) elseif(CMAKE_SYSTEM_NAME MATCHES "Minix.*") set(SDL_CMAKE_PLATFORM MINIX) + elseif(CMAKE_SYSTEM_NAME MATCHES "banan-os") + set(SDL_CMAKE_PLATFORM BANAN_OS) elseif(CMAKE_SYSTEM_NAME MATCHES "QNX") set(SDL_CMAKE_PLATFORM QNX) endif() diff -ruN SDL2-2.32.8/CMakeLists.txt SDL2-2.32.8-banan_os/CMakeLists.txt --- SDL2-2.32.8/CMakeLists.txt 2025-06-03 02:00:39.000000000 +0300 +++ SDL2-2.32.8-banan_os/CMakeLists.txt 2026-01-03 19:51:02.494165302 +0200 @@ -14,7 +14,7 @@ set(SDL2_SUBPROJECT ON) endif() -if (HAIKU) +if (HAIKU OR BANAN_OS) enable_language(CXX) set(LINKER_LANGUAGE CXX) endif() @@ -1462,7 +1462,7 @@ CheckPTHREAD() CheckLibUnwind() -elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) +elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU AND NOT BANAN_OS) if(SDL_AUDIO) if(SYSV5 OR SOLARIS OR HPUX) set(SDL_AUDIO_DRIVER_SUNAUDIO 1) @@ -2459,6 +2459,64 @@ CheckPTHREAD() list(APPEND EXTRA_LIBS root be media game device textencoding) +elseif(BANAN_OS) + if(SDL_MISC) + file(GLOB MISC_SOURCES ${SDL2_SOURCE_DIR}/src/misc/banan_os/*.cpp) + list(APPEND SOURCE_FILES ${MISC_SOURCES}) + set(HAVE_SDL_MISC TRUE) + endif() + + if(SDL_AUDIO) + set(SDL_AUDIO_DRIVER_BANANOS 1) + file(GLOB AUDIO_SOURCES ${SDL2_SOURCE_DIR}/src/audio/banan_os/*.cpp) + list(APPEND SOURCE_FILES ${AUDIO_SOURCES}) + set(HAVE_SDL_AUDIO TRUE) + list(APPEND EXTRA_LIBS audio) + endif() + + if(SDL_JOYSTICK) + set(SDL_JOYSTICK_BANANOS 1) + file(GLOB JOYSTICK_SOURCES ${SDL2_SOURCE_DIR}/src/joystick/banan_os/*.cpp) + list(APPEND SOURCE_FILES ${JOYSTICK_SOURCES}) + set(HAVE_SDL_JOYSTICK TRUE) + endif() + + if(SDL_VIDEO) + set(SDL_VIDEO_DRIVER_BANANOS 1) + file(GLOB VIDEO_SOURCES ${SDL2_SOURCE_DIR}/src/video/banan_os/*.cpp) + list(APPEND SOURCE_FILES ${VIDEO_SOURCES}) + set(HAVE_SDL_VIDEO TRUE) + list(APPEND EXTRA_LIBS gui input clipboard) + + if(SDL_OPENGL) + set(SDL_VIDEO_OPENGL 1) + set(SDL_VIDEO_OPENGL_BANANOS 1) + set(SDL_VIDEO_RENDER_OGL 1) + list(APPEND EXTRA_LIBS OSMesa) + set(HAVE_OPENGL TRUE) + endif() + endif() + + if(SDL_FILESYSTEM) + set(SDL_FILESYSTEM_UNIX 1) + file(GLOB FILESYSTEM_SOURCES ${SDL2_SOURCE_DIR}/src/filesystem/unix/*.c) + list(APPEND SOURCE_FILES ${FILESYSTEM_SOURCES}) + set(HAVE_SDL_FILESYSTEM TRUE) + endif() + + if(SDL_TIMERS) + set(SDL_TIMER_UNIX 1) + file(GLOB TIMER_SOURCES ${SDL2_SOURCE_DIR}/src/timer/unix/*.c) + list(APPEND SOURCE_FILES ${TIMER_SOURCES}) + set(HAVE_SDL_TIMERS TRUE) + + if(SDL_CLOCK_GETTIME) + set(HAVE_CLOCK_GETTIME 1) + endif() + endif() + + CheckPTHREAD() + elseif(RISCOS) if(SDL_MISC) file(GLOB MISC_SOURCES ${SDL2_SOURCE_DIR}/src/misc/riscos/*.c) diff -ruN SDL2-2.32.8/include/SDL_config.h.cmake SDL2-2.32.8-banan_os/include/SDL_config.h.cmake --- SDL2-2.32.8/include/SDL_config.h.cmake 2025-01-01 17:47:53.000000000 +0200 +++ SDL2-2.32.8-banan_os/include/SDL_config.h.cmake 2026-01-03 19:55:22.679890924 +0200 @@ -307,6 +307,7 @@ #cmakedefine SDL_AUDIO_DRIVER_FUSIONSOUND @SDL_AUDIO_DRIVER_FUSIONSOUND@ #cmakedefine SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC @SDL_AUDIO_DRIVER_FUSIONSOUND_DYNAMIC@ #cmakedefine SDL_AUDIO_DRIVER_HAIKU @SDL_AUDIO_DRIVER_HAIKU@ +#cmakedefine SDL_AUDIO_DRIVER_BANANOS @SDL_AUDIO_DRIVER_BANANOS@ #cmakedefine SDL_AUDIO_DRIVER_JACK @SDL_AUDIO_DRIVER_JACK@ #cmakedefine SDL_AUDIO_DRIVER_JACK_DYNAMIC @SDL_AUDIO_DRIVER_JACK_DYNAMIC@ #cmakedefine SDL_AUDIO_DRIVER_NAS @SDL_AUDIO_DRIVER_NAS@ @@ -337,6 +338,7 @@ #cmakedefine SDL_INPUT_WSCONS @SDL_INPUT_WSCONS@ #cmakedefine SDL_JOYSTICK_ANDROID @SDL_JOYSTICK_ANDROID@ #cmakedefine SDL_JOYSTICK_HAIKU @SDL_JOYSTICK_HAIKU@ +#cmakedefine SDL_JOYSTICK_BANANOS @SDL_JOYSTICK_BANANOS@ #cmakedefine SDL_JOYSTICK_WGI @SDL_JOYSTICK_WGI@ #cmakedefine SDL_JOYSTICK_DINPUT @SDL_JOYSTICK_DINPUT@ #cmakedefine SDL_JOYSTICK_XINPUT @SDL_JOYSTICK_XINPUT@ @@ -406,6 +408,7 @@ #cmakedefine SDL_VIDEO_DRIVER_ANDROID @SDL_VIDEO_DRIVER_ANDROID@ #cmakedefine SDL_VIDEO_DRIVER_EMSCRIPTEN @SDL_VIDEO_DRIVER_EMSCRIPTEN@ #cmakedefine SDL_VIDEO_DRIVER_HAIKU @SDL_VIDEO_DRIVER_HAIKU@ +#cmakedefine SDL_VIDEO_DRIVER_BANANOS @SDL_VIDEO_DRIVER_BANANOS@ #cmakedefine SDL_VIDEO_DRIVER_COCOA @SDL_VIDEO_DRIVER_COCOA@ #cmakedefine SDL_VIDEO_DRIVER_UIKIT @SDL_VIDEO_DRIVER_UIKIT@ #cmakedefine SDL_VIDEO_DRIVER_DIRECTFB @SDL_VIDEO_DRIVER_DIRECTFB@ diff -ruN SDL2-2.32.8/include/SDL_platform.h SDL2-2.32.8-banan_os/include/SDL_platform.h --- SDL2-2.32.8/include/SDL_platform.h 2025-01-01 17:47:53.000000000 +0200 +++ SDL2-2.32.8-banan_os/include/SDL_platform.h 2026-01-03 19:50:45.778818009 +0200 @@ -36,6 +36,10 @@ #undef __HAIKU__ #define __HAIKU__ 1 #endif +#if defined(__banan_os__) +#undef __banan_os__ +#define __banan_os__ 1 +#endif #if defined(bsdi) || defined(__bsdi) || defined(__bsdi__) #undef __BSDI__ #define __BSDI__ 1 diff -ruN SDL2-2.32.8/src/audio/banan_os/SDL_banan_os_audio.cpp SDL2-2.32.8-banan_os/src/audio/banan_os/SDL_banan_os_audio.cpp --- SDL2-2.32.8/src/audio/banan_os/SDL_banan_os_audio.cpp 1970-01-01 02:00:00.000000000 +0200 +++ SDL2-2.32.8-banan_os/src/audio/banan_os/SDL_banan_os_audio.cpp 2026-01-03 19:50:45.779146754 +0200 @@ -0,0 +1,150 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2019 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as 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 SDL_AUDIO_DRIVER_BANANOS + +extern "C" { +#include "SDL_audio.h" +#include "../SDL_audio_c.h" +#include "../SDL_sysaudio.h" +} + +#include "SDL_banan_os_audio.h" + +#include + +#define DUMP_FUNCTIONS 0 + +#if DUMP_FUNCTIONS +# define DUMP_FUNCTION() dprintln(__FUNCTION__) +#else +# define DUMP_FUNCTION() +#endif + +static void BANANOS_CloseDevice(_THIS) +{ + DUMP_FUNCTION(); + + if (!_this->hidden) + return; + + if (_this->hidden->mixbuf) + SDL_free(_this->hidden->mixbuf); + + delete _this->hidden; +} + +static int BANANOS_OpenDevice(_THIS, char const*) +{ + DUMP_FUNCTION(); + + // TODO: try to accept already existing spec + _this->spec.freq = 44100; + _this->spec.format = AUDIO_S16LSB; + _this->spec.channels = 2; + _this->spec.samples = 2048; + SDL_CalculateAudioSpec(&_this->spec); + + auto audio_or_error = LibAudio::Audio::create(_this->spec.channels, _this->spec.freq, 0x1000); + if (audio_or_error.is_error()) + return SDL_SetError("failed to create audio device: %s", audio_or_error.error().get_message()); + + void* mixbuf = SDL_malloc(_this->spec.size); + if (mixbuf == nullptr) + return SDL_OutOfMemory(); + + _this->hidden = new SDL_PrivateAudioData(audio_or_error.release_value(), mixbuf); + if (!_this->hidden) + return SDL_OutOfMemory(); + MUST(_this->hidden->audio.start()); + + return 0; +} + +static void BANANOS_PlayDevice(_THIS) +{ + DUMP_FUNCTION(); + + const bool should_play = SDL_AtomicGet(&_this->enabled) && !SDL_AtomicGet(&_this->paused); + _this->hidden->audio.set_paused(!should_play); + if (!should_play) + { + usleep(100); + return; + } + + static_assert(BAN::is_same_v); + + const auto convert_sample = [](int16_t input) { + return (static_cast(input) - BAN::numeric_limits::min()) / BAN::numeric_limits::max() * 2.0 - 1.0; + }; + + const size_t input_samples = _this->spec.size / sizeof(int16_t); + size_t samples_queued = 0; + + const int16_t* mixbuf = static_cast(_this->hidden->mixbuf); + while (samples_queued < input_samples) + { + const size_t to_convert = BAN::Math::min(_this->hidden->conversion.size(), input_samples - samples_queued); + for (size_t i = 0; i < to_convert; i++) + _this->hidden->conversion[i] = convert_sample(mixbuf[samples_queued + i]); + + auto sample_span = _this->hidden->conversion.span(); + while (!sample_span.empty()) + { + const size_t queued = _this->hidden->audio.queue_samples(sample_span); + if (queued == 0) + usleep(100); + sample_span = sample_span.slice(queued); + } + + samples_queued += to_convert; + } +} + +static Uint8* BANANOS_GetDeviceBuf(_THIS) +{ + DUMP_FUNCTION(); + + return static_cast(_this->hidden->mixbuf); +} + +static SDL_bool BANANOS_Init(SDL_AudioDriverImpl* impl) +{ + impl->OpenDevice = BANANOS_OpenDevice; + impl->CloseDevice = BANANOS_CloseDevice; + impl->PlayDevice = BANANOS_PlayDevice; + impl->GetDeviceBuf = BANANOS_GetDeviceBuf; + + impl->ProvidesOwnCallbackThread = SDL_FALSE; + impl->HasCaptureSupport = SDL_FALSE; + impl->OnlyHasDefaultOutputDevice = SDL_TRUE; + impl->SupportsNonPow2Samples = SDL_TRUE; + + return SDL_TRUE; +} + +AudioBootStrap BANANOSAUDIO_bootstrap = { + "banan-os", "banan-os AudioServer", BANANOS_Init, SDL_FALSE +}; + +#endif diff -ruN SDL2-2.32.8/src/audio/banan_os/SDL_banan_os_audio.h SDL2-2.32.8-banan_os/src/audio/banan_os/SDL_banan_os_audio.h --- SDL2-2.32.8/src/audio/banan_os/SDL_banan_os_audio.h 1970-01-01 02:00:00.000000000 +0200 +++ SDL2-2.32.8-banan_os/src/audio/banan_os/SDL_banan_os_audio.h 2026-01-03 19:50:45.779253053 +0200 @@ -0,0 +1,34 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2019 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as 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" + +#pragma once + +#include +#include + +#define _THIS SDL_AudioDevice* _this + +struct SDL_PrivateAudioData { + LibAudio::Audio audio; + void* mixbuf { nullptr }; + BAN::Array conversion; +}; diff -ruN SDL2-2.32.8/src/audio/SDL_audio.c SDL2-2.32.8-banan_os/src/audio/SDL_audio.c --- SDL2-2.32.8/src/audio/SDL_audio.c 2025-01-01 17:47:53.000000000 +0200 +++ SDL2-2.32.8-banan_os/src/audio/SDL_audio.c 2026-01-03 19:50:45.779381073 +0200 @@ -87,6 +87,9 @@ #ifdef SDL_AUDIO_DRIVER_HAIKU &HAIKUAUDIO_bootstrap, #endif +#ifdef SDL_AUDIO_DRIVER_BANANOS + &BANANOSAUDIO_bootstrap, +#endif #ifdef SDL_AUDIO_DRIVER_COREAUDIO &COREAUDIO_bootstrap, #endif diff -ruN SDL2-2.32.8/src/audio/SDL_sysaudio.h SDL2-2.32.8-banan_os/src/audio/SDL_sysaudio.h --- SDL2-2.32.8/src/audio/SDL_sysaudio.h 2025-01-01 17:47:53.000000000 +0200 +++ SDL2-2.32.8-banan_os/src/audio/SDL_sysaudio.h 2026-01-03 19:50:45.779975705 +0200 @@ -196,6 +196,7 @@ extern AudioBootStrap WINMM_bootstrap; extern AudioBootStrap PAUDIO_bootstrap; extern AudioBootStrap HAIKUAUDIO_bootstrap; +extern AudioBootStrap BANANOSAUDIO_bootstrap; extern AudioBootStrap COREAUDIO_bootstrap; extern AudioBootStrap DISKAUDIO_bootstrap; extern AudioBootStrap DUMMYAUDIO_bootstrap; diff -ruN SDL2-2.32.8/src/joystick/banan_os/SDL_banan_os_joystick.cpp SDL2-2.32.8-banan_os/src/joystick/banan_os/SDL_banan_os_joystick.cpp --- SDL2-2.32.8/src/joystick/banan_os/SDL_banan_os_joystick.cpp 1970-01-01 02:00:00.000000000 +0200 +++ SDL2-2.32.8-banan_os/src/joystick/banan_os/SDL_banan_os_joystick.cpp 2026-01-03 19:51:21.437329388 +0200 @@ -0,0 +1,308 @@ +/* +Simple DirectMedia Layer +Copyright (C) 1997-2025 Sam Lantinga + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as 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_JOYSTICK_BANANOS + +#include + +#include + +extern "C" +{ + +#include "SDL_joystick.h" +#include "../SDL_sysjoystick.h" +#include "../SDL_joystick_c.h" + +#include +#include +#include +#include +#include + +#define MAX_JOYSTICKS 16 + +struct joystick_hwdata +{ + int fd; +}; + +static int s_joystick_count = 0; +static char* s_joystick_path[MAX_JOYSTICKS]; + +static void BANANOS_JoystickDetect(void); + +static int BANANOS_JoystickInit(void) +{ + s_joystick_count = 0; + + for (int i = 0; i < MAX_JOYSTICKS; i++) + { + char path[PATH_MAX]; + sprintf(path, "/dev/joystick%d", i); + + struct stat st; + if (stat(path, &st) == -1) + continue; + + s_joystick_path[i] = SDL_strdup(path); + if (s_joystick_path[i] == NULL) + continue; + s_joystick_count++; + } + + return 0; +} + +static int BANANOS_JoystickGetCount(void) +{ + return s_joystick_count; +} + +static void BANANOS_JoystickDetect(void) +{ +} + +static const char* BANANOS_JoystickGetDeviceName(int device_index) +{ + return s_joystick_path[device_index] + strlen("/dev/"); +} + +static const char* BANANOS_JoystickGetDevicePath(int device_index) +{ + return s_joystick_path[device_index]; +} + +static int BANANOS_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index) +{ + (void)device_index; + return -1; +} + +static int BANANOS_JoystickGetDevicePlayerIndex(int device_index) +{ + (void)device_index; + return -1; +} + +static void BANANOS_JoystickSetDevicePlayerIndex(int device_index, int player_index) +{ + (void)device_index; + (void)player_index; +} + +static SDL_JoystickID BANANOS_JoystickGetDeviceInstanceID(int device_index) +{ + return device_index; +} + +static void BANANOS_JoystickClose(SDL_Joystick* joystick) +{ + if (joystick->hwdata == NULL) + return; + + if (joystick->hwdata->fd != -1) + close(joystick->hwdata->fd); + SDL_free(joystick->hwdata); +} + +static int BANANOS_JoystickOpen(SDL_Joystick* joystick, int device_index) +{ + joystick->instance_id = device_index; + joystick->hwdata = static_cast(SDL_calloc(1, sizeof(joystick_hwdata))); + if (joystick->hwdata == NULL) + return SDL_OutOfMemory(); + + joystick->hwdata->fd = open(s_joystick_path[device_index], O_RDONLY); + if (joystick->hwdata->fd == -1) + { + BANANOS_JoystickClose(joystick); + return SDL_SetError("Unable to open joystick"); + } + + joystick->nbuttons = sizeof(LibInput::JoystickState::buttons) / sizeof(*LibInput::JoystickState::buttons); + joystick->naxes = sizeof(LibInput::JoystickState::axis) / sizeof(*LibInput::JoystickState::axis); + joystick->nhats = 0; + + return 0; +} + +static void BANANOS_JoystickUpdate(SDL_Joystick *joystick) +{ + LibInput::JoystickState state; + if (read(joystick->hwdata->fd, &state, sizeof(state)) < static_cast(sizeof(state))) + return; + + const auto map_joystick_value = + [](const LibInput::JoystickState::Axis& axis) -> Sint16 + { + if (axis.min == axis.max) + return 0; + const float mapped = (axis.value - axis.min) * 65534.0f / (axis.max - axis.min) - 32767.0f; + return BAN::Math::clamp(mapped, -32767, 32767); + }; + + for (int i = 0; i < joystick->naxes; i++) + SDL_PrivateJoystickAxis(joystick, i, map_joystick_value(state.axis[i])); + + for (int i = 0; i < joystick->nbuttons; i++) + SDL_PrivateJoystickButton(joystick, i, state.buttons[i]); +} + +static void BANANOS_JoystickQuit(void) +{ + for (int i = 0; i < s_joystick_count; i++) + SDL_free(s_joystick_path[i]); +} + +static SDL_JoystickGUID BANANOS_JoystickGetDeviceGUID(int device_index) +{ + return SDL_CreateJoystickGUIDForName(BANANOS_JoystickGetDeviceName(device_index)); +} + +static int BANANOS_JoystickRumble(SDL_Joystick* joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) +{ + (void)joystick; + (void)low_frequency_rumble; + (void)high_frequency_rumble; + return SDL_Unsupported(); +} + +static int BANANOS_JoystickRumbleTriggers(SDL_Joystick* joystick, Uint16 left_rumble, Uint16 right_rumble) +{ + (void)joystick; + (void)left_rumble; + (void)right_rumble; + return SDL_Unsupported(); +} + +static SDL_bool BANANOS_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping* out) +{ + (void)device_index; + + // FIXME: this is hardcoded to a PS3 controller layout :D + + memset(out, 0, sizeof(SDL_GamepadMapping)); + +#define BANANOS_MAP_BUTTON(name, button) \ + out->name.kind = EMappingKind_Button; \ + out->name.target = button + +#define BANANOS_MAP_AXIS(name, axis) \ + out->name.kind = EMappingKind_Axis; \ + out->name.target = axis + + BANANOS_MAP_BUTTON(a, 14); + BANANOS_MAP_BUTTON(b, 13); + BANANOS_MAP_BUTTON(x, 15); + BANANOS_MAP_BUTTON(y, 12); + + BANANOS_MAP_BUTTON(back, 0); + BANANOS_MAP_BUTTON(guide, 16); + BANANOS_MAP_BUTTON(start, 3); + + BANANOS_MAP_BUTTON(leftstick, 1); + BANANOS_MAP_BUTTON(rightstick, 2); + + BANANOS_MAP_BUTTON(leftshoulder, 10); + BANANOS_MAP_BUTTON(rightshoulder, 11); + + BANANOS_MAP_BUTTON(dpup, 4); + BANANOS_MAP_BUTTON(dpdown, 6); + BANANOS_MAP_BUTTON(dpleft, 7); + BANANOS_MAP_BUTTON(dpright, 5); + + BANANOS_MAP_AXIS(leftx, 0); + BANANOS_MAP_AXIS(lefty, 1); + BANANOS_MAP_AXIS(rightx, 2); + BANANOS_MAP_AXIS(righty, 3); + + // TODO: these should probably be axis + BANANOS_MAP_BUTTON(lefttrigger, 8); + BANANOS_MAP_BUTTON(righttrigger, 9); + +#undef BANANOS_MAP_BUTTON +#undef BANANOS_MAP_AXIS + + return SDL_TRUE; +} + +static Uint32 BANANOS_JoystickGetCapabilities(SDL_Joystick* joystick) +{ + (void)joystick; + return 0; +} + +static int BANANOS_JoystickSetLED(SDL_Joystick* joystick, Uint8 red, Uint8 green, Uint8 blue) +{ + (void)joystick; + (void)red; + (void)green; + (void)blue; + return SDL_Unsupported(); +} + +static int BANANOS_JoystickSendEffect(SDL_Joystick* joystick, const void* data, int size) +{ + (void)joystick; + (void)data; + (void)size; + return SDL_Unsupported(); +} + +static int BANANOS_JoystickSetSensorsEnabled(SDL_Joystick* joystick, SDL_bool enabled) +{ + (void)joystick; + (void)enabled; + return SDL_Unsupported(); +} + +SDL_JoystickDriver SDL_BANANOS_JoystickDriver = +{ + BANANOS_JoystickInit, + BANANOS_JoystickGetCount, + BANANOS_JoystickDetect, + BANANOS_JoystickGetDeviceName, + BANANOS_JoystickGetDevicePath, + BANANOS_JoystickGetDeviceSteamVirtualGamepadSlot, + BANANOS_JoystickGetDevicePlayerIndex, + BANANOS_JoystickSetDevicePlayerIndex, + BANANOS_JoystickGetDeviceGUID, + BANANOS_JoystickGetDeviceInstanceID, + BANANOS_JoystickOpen, + BANANOS_JoystickRumble, + BANANOS_JoystickRumbleTriggers, + BANANOS_JoystickGetCapabilities, + BANANOS_JoystickSetLED, + BANANOS_JoystickSendEffect, + BANANOS_JoystickSetSensorsEnabled, + BANANOS_JoystickUpdate, + BANANOS_JoystickClose, + BANANOS_JoystickQuit, + BANANOS_JoystickGetGamepadMapping +}; + +} // extern "C" + +#endif /* SDL_JOYSTICK_BANANOS */ + +/* vi: set ts=4 sw=4 expandtab: */ diff -ruN SDL2-2.32.8/src/joystick/SDL_joystick.c SDL2-2.32.8-banan_os/src/joystick/SDL_joystick.c --- SDL2-2.32.8/src/joystick/SDL_joystick.c 2025-01-01 17:47:53.000000000 +0200 +++ SDL2-2.32.8-banan_os/src/joystick/SDL_joystick.c 2026-01-03 19:52:38.740921854 +0200 @@ -85,6 +85,9 @@ #ifdef SDL_JOYSTICK_HAIKU &SDL_HAIKU_JoystickDriver, #endif +#ifdef SDL_JOYSTICK_BANANOS + &SDL_BANANOS_JoystickDriver, +#endif #ifdef SDL_JOYSTICK_USBHID /* !!! FIXME: "USBHID" is a generic name, and doubly-confusing with HIDAPI next to it. This is the *BSD interface, rename this. */ &SDL_BSD_JoystickDriver, #endif diff -ruN SDL2-2.32.8/src/joystick/SDL_sysjoystick.h SDL2-2.32.8-banan_os/src/joystick/SDL_sysjoystick.h --- SDL2-2.32.8/src/joystick/SDL_sysjoystick.h 2025-01-01 17:47:53.000000000 +0200 +++ SDL2-2.32.8-banan_os/src/joystick/SDL_sysjoystick.h 2026-01-03 19:52:50.906765671 +0200 @@ -235,6 +235,7 @@ /* The available joystick drivers */ extern SDL_JoystickDriver SDL_ANDROID_JoystickDriver; +extern SDL_JoystickDriver SDL_BANANOS_JoystickDriver; extern SDL_JoystickDriver SDL_BSD_JoystickDriver; extern SDL_JoystickDriver SDL_DARWIN_JoystickDriver; extern SDL_JoystickDriver SDL_DUMMY_JoystickDriver; diff -ruN SDL2-2.32.8/src/misc/banan_os/SDL_sysurl.cpp SDL2-2.32.8-banan_os/src/misc/banan_os/SDL_sysurl.cpp --- SDL2-2.32.8/src/misc/banan_os/SDL_sysurl.cpp 1970-01-01 02:00:00.000000000 +0200 +++ SDL2-2.32.8-banan_os/src/misc/banan_os/SDL_sysurl.cpp 2026-01-03 19:50:45.780251300 +0200 @@ -0,0 +1,30 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as 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_sysurl.h" + +int SDL_SYS_OpenURL(const char *url) +{ + return SDL_SetError("SDL_SYS_OpenURL not supported"); +} + +/* vi: set ts=4 sw=4 expandtab: */ + diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_clipboard.cpp SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_clipboard.cpp --- SDL2-2.32.8/src/video/banan_os/SDL_banan_os_clipboard.cpp 1970-01-01 02:00:00.000000000 +0200 +++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_clipboard.cpp 2026-01-03 19:50:45.780447206 +0200 @@ -0,0 +1,51 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as 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_VIDEO_DRIVER_BANANOS + +#include "SDL_banan_os_clipboard.h" + +#include + +int BANANOS_SetClipboardText(_THIS, const char *text) { + if (LibClipboard::Clipboard::set_clipboard_text(text).is_error()) + return -1; + return 0; +} + +char *BANANOS_GetClipboardText(_THIS) { + auto text_or_error = LibClipboard::Clipboard::get_clipboard_text(); + if (text_or_error.is_error()) + return NULL; + return SDL_strdup(text_or_error.value().data()); +} + +SDL_bool BANANOS_HasClipboardText(_THIS) { + auto text_or_error = LibClipboard::Clipboard::get_clipboard_text(); + if (text_or_error.is_error()) + return SDL_FALSE; + return text_or_error.value().empty() ? SDL_FALSE : SDL_TRUE; +} + +#endif /* SDL_VIDEO_DRIVER_BANANOS */ + +/* vi: set ts=4 sw=4 expandtab: */ diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_clipboard.h SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_clipboard.h --- SDL2-2.32.8/src/video/banan_os/SDL_banan_os_clipboard.h 1970-01-01 02:00:00.000000000 +0200 +++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_clipboard.h 2026-01-03 19:50:45.780567334 +0200 @@ -0,0 +1,43 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as 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_BANANOS_CLIPBOARD_H +#define SDL_BANANOS_CLIPBOARD_H + +#include "../../SDL_internal.h" + +#include "../SDL_sysvideo.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern int BANANOS_SetClipboardText(_THIS, const char *text); +extern char *BANANOS_GetClipboardText(_THIS); +extern SDL_bool BANANOS_HasClipboardText(_THIS); + +#ifdef __cplusplus +} +#endif + +#endif + +/* vi: set ts=4 sw=4 expandtab: */ diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_message_box.cpp SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_message_box.cpp --- SDL2-2.32.8/src/video/banan_os/SDL_banan_os_message_box.cpp 1970-01-01 02:00:00.000000000 +0200 +++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_message_box.cpp 2026-01-03 19:50:45.780690325 +0200 @@ -0,0 +1,60 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 2018-2019 EXL + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as 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_VIDEO_DRIVER_BANANOS + +#include "SDL_messagebox.h" + +#include "SDL_banan_os_message_box.h" + +#include +#include + +int BANANOS_ShowMessageBox(const SDL_MessageBoxData* messageboxdata, int* buttonid) +{ + BAN::Vector buttons; + for (int i = 0; i < messageboxdata->numbuttons; i++) { + if (buttons.push_back(messageboxdata->buttons[i].text).is_error()) { + SDL_OutOfMemory(); + return -1; + } + } + + if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) + buttons.reverse(); + + auto result = LibGUI::MessageBox::create(messageboxdata->message, messageboxdata->title, buttons.span()); + if (result.is_error()) { + dwarnln("LibGUI::MessageBox::create: {}", result.error()); + return -1; + } + + *buttonid = messageboxdata->buttons[result.value()].buttonid; + + return 0; +} + +#endif /* SDL_VIDEO_DRIVER_BANANOS */ + +/* vi: set ts=4 sw=4 expandtab: */ diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_message_box.h SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_message_box.h --- SDL2-2.32.8/src/video/banan_os/SDL_banan_os_message_box.h 1970-01-01 02:00:00.000000000 +0200 +++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_message_box.h 2026-01-03 19:50:45.780815551 +0200 @@ -0,0 +1,45 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + Copyright (C) 2018-2019 EXL + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as 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_BANANOS_MESSAGEBOX_H +#define SDL_BANANOS_MESSAGEBOX_H + +#include "../../SDL_internal.h" + +#ifdef SDL_VIDEO_DRIVER_BANANOS + +#ifdef __cplusplus +extern "C" { +#endif + +extern int +BANANOS_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid); + +#ifdef __cplusplus +} +#endif + +#endif /* SDL_BANANOS_MESSAGEBOX_H */ + +#endif + +/* vi: set ts=4 sw=4 expandtab: */ diff -ruN SDL2-2.32.8/src/video/banan_os/SDL_banan_os_video.cpp SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_video.cpp --- SDL2-2.32.8/src/video/banan_os/SDL_banan_os_video.cpp 1970-01-01 02:00:00.000000000 +0200 +++ SDL2-2.32.8-banan_os/src/video/banan_os/SDL_banan_os_video.cpp 2026-01-03 19:50:45.780934771 +0200 @@ -0,0 +1,729 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as 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_VIDEO_DRIVER_BANANOS + +extern "C" { +#include "../SDL_sysvideo.h" +#include "../../events/SDL_events_c.h" +} + +#include "SDL_banan_os_clipboard.h" +#include "SDL_banan_os_message_box.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define DUMP_FUNCTIONS 0 + +#if DUMP_FUNCTIONS +# define DUMP_FUNCTION() dprintln(__FUNCTION__) +#else +# define DUMP_FUNCTION() +#endif + +struct banan_os_window +{ + BAN::UniqPtr window; + OSMesaContext gl_context { nullptr }; + SDL_bool relative { SDL_FALSE }; +}; + +struct banan_os_video_device_data +{ + BAN::LinkedList windows; +}; + +struct Keymap +{ + consteval Keymap() + { + for (auto& scancode : map) + scancode = SDL_SCANCODE_UNKNOWN; + + using LibInput::keycode_normal; + using LibInput::keycode_function; + using LibInput::keycode_numpad; + + map[keycode_normal(0, 0)] = SDL_SCANCODE_GRAVE; + map[keycode_normal(0, 1)] = SDL_SCANCODE_1; + map[keycode_normal(0, 2)] = SDL_SCANCODE_2; + map[keycode_normal(0, 3)] = SDL_SCANCODE_3; + map[keycode_normal(0, 4)] = SDL_SCANCODE_4; + map[keycode_normal(0, 5)] = SDL_SCANCODE_5; + map[keycode_normal(0, 6)] = SDL_SCANCODE_6; + map[keycode_normal(0, 7)] = SDL_SCANCODE_7; + map[keycode_normal(0, 8)] = SDL_SCANCODE_8; + map[keycode_normal(0, 9)] = SDL_SCANCODE_9; + map[keycode_normal(0, 10)] = SDL_SCANCODE_0; + map[keycode_normal(0, 11)] = SDL_SCANCODE_MINUS; + map[keycode_normal(0, 12)] = SDL_SCANCODE_EQUALS; + map[keycode_normal(0, 13)] = SDL_SCANCODE_BACKSPACE; + map[keycode_normal(1, 0)] = SDL_SCANCODE_TAB; + map[keycode_normal(1, 1)] = SDL_SCANCODE_Q; + map[keycode_normal(1, 2)] = SDL_SCANCODE_W; + map[keycode_normal(1, 3)] = SDL_SCANCODE_E; + map[keycode_normal(1, 4)] = SDL_SCANCODE_R; + map[keycode_normal(1, 5)] = SDL_SCANCODE_T; + map[keycode_normal(1, 6)] = SDL_SCANCODE_Y; + map[keycode_normal(1, 7)] = SDL_SCANCODE_U; + map[keycode_normal(1, 8)] = SDL_SCANCODE_I; + map[keycode_normal(1, 9)] = SDL_SCANCODE_O; + map[keycode_normal(1, 10)] = SDL_SCANCODE_P; + map[keycode_normal(1, 11)] = SDL_SCANCODE_LEFTBRACKET; + map[keycode_normal(1, 12)] = SDL_SCANCODE_RIGHTBRACKET; + map[keycode_normal(2, 0)] = SDL_SCANCODE_CAPSLOCK; + map[keycode_normal(2, 1)] = SDL_SCANCODE_A; + map[keycode_normal(2, 2)] = SDL_SCANCODE_S; + map[keycode_normal(2, 3)] = SDL_SCANCODE_D; + map[keycode_normal(2, 4)] = SDL_SCANCODE_F; + map[keycode_normal(2, 5)] = SDL_SCANCODE_G; + map[keycode_normal(2, 6)] = SDL_SCANCODE_H; + map[keycode_normal(2, 7)] = SDL_SCANCODE_J; + map[keycode_normal(2, 8)] = SDL_SCANCODE_K; + map[keycode_normal(2, 9)] = SDL_SCANCODE_L; + map[keycode_normal(2, 10)] = SDL_SCANCODE_SEMICOLON; + map[keycode_normal(2, 11)] = SDL_SCANCODE_APOSTROPHE; + map[keycode_normal(2, 12)] = SDL_SCANCODE_BACKSLASH; + map[keycode_normal(2, 13)] = SDL_SCANCODE_RETURN; + map[keycode_normal(3, 0)] = SDL_SCANCODE_LSHIFT; + map[keycode_normal(3, 1)] = SDL_SCANCODE_NONUSBACKSLASH; + map[keycode_normal(3, 2)] = SDL_SCANCODE_Z; + map[keycode_normal(3, 3)] = SDL_SCANCODE_X; + map[keycode_normal(3, 4)] = SDL_SCANCODE_C; + map[keycode_normal(3, 5)] = SDL_SCANCODE_V; + map[keycode_normal(3, 6)] = SDL_SCANCODE_B; + map[keycode_normal(3, 7)] = SDL_SCANCODE_N; + map[keycode_normal(3, 8)] = SDL_SCANCODE_M; + map[keycode_normal(3, 9)] = SDL_SCANCODE_COMMA; + map[keycode_normal(3, 10)] = SDL_SCANCODE_PERIOD; + map[keycode_normal(3, 11)] = SDL_SCANCODE_SLASH; + map[keycode_normal(3, 12)] = SDL_SCANCODE_RSHIFT; + map[keycode_normal(4, 0)] = SDL_SCANCODE_LCTRL; + map[keycode_normal(4, 1)] = SDL_SCANCODE_LGUI; + map[keycode_normal(4, 2)] = SDL_SCANCODE_LALT; + map[keycode_normal(4, 3)] = SDL_SCANCODE_SPACE; + map[keycode_normal(4, 5)] = SDL_SCANCODE_RALT; + map[keycode_normal(4, 6)] = SDL_SCANCODE_RCTRL; + + map[keycode_normal(5, 0)] = SDL_SCANCODE_UP; + map[keycode_normal(5, 1)] = SDL_SCANCODE_LEFT; + map[keycode_normal(5, 2)] = SDL_SCANCODE_DOWN; + map[keycode_normal(5, 3)] = SDL_SCANCODE_RIGHT; + + map[keycode_function(0)] = SDL_SCANCODE_ESCAPE; + map[keycode_function(1)] = SDL_SCANCODE_F1; + map[keycode_function(2)] = SDL_SCANCODE_F2; + map[keycode_function(3)] = SDL_SCANCODE_F3; + map[keycode_function(4)] = SDL_SCANCODE_F4; + map[keycode_function(5)] = SDL_SCANCODE_F5; + map[keycode_function(6)] = SDL_SCANCODE_F6; + map[keycode_function(7)] = SDL_SCANCODE_F7; + map[keycode_function(8)] = SDL_SCANCODE_F8; + map[keycode_function(9)] = SDL_SCANCODE_F9; + map[keycode_function(10)] = SDL_SCANCODE_F10; + map[keycode_function(11)] = SDL_SCANCODE_F11; + map[keycode_function(12)] = SDL_SCANCODE_F12; + map[keycode_function(13)] = SDL_SCANCODE_INSERT; + map[keycode_function(14)] = SDL_SCANCODE_PRINTSCREEN; + map[keycode_function(15)] = SDL_SCANCODE_DELETE; + map[keycode_function(16)] = SDL_SCANCODE_HOME; + map[keycode_function(17)] = SDL_SCANCODE_END; + map[keycode_function(18)] = SDL_SCANCODE_PAGEUP; + map[keycode_function(19)] = SDL_SCANCODE_PAGEDOWN; + map[keycode_function(20)] = SDL_SCANCODE_SCROLLLOCK; + + map[keycode_numpad(0, 0)] = SDL_SCANCODE_NUMLOCKCLEAR; + map[keycode_numpad(0, 1)] = SDL_SCANCODE_KP_DIVIDE; + map[keycode_numpad(0, 2)] = SDL_SCANCODE_KP_MULTIPLY; + map[keycode_numpad(0, 3)] = SDL_SCANCODE_KP_MINUS; + map[keycode_numpad(1, 0)] = SDL_SCANCODE_KP_7; + map[keycode_numpad(1, 1)] = SDL_SCANCODE_KP_8; + map[keycode_numpad(1, 2)] = SDL_SCANCODE_KP_9; + map[keycode_numpad(1, 3)] = SDL_SCANCODE_KP_PLUS; + map[keycode_numpad(2, 0)] = SDL_SCANCODE_KP_4; + map[keycode_numpad(2, 1)] = SDL_SCANCODE_KP_5; + map[keycode_numpad(2, 2)] = SDL_SCANCODE_KP_6; + map[keycode_numpad(3, 0)] = SDL_SCANCODE_KP_1; + map[keycode_numpad(3, 1)] = SDL_SCANCODE_KP_2; + map[keycode_numpad(3, 2)] = SDL_SCANCODE_KP_3; + map[keycode_numpad(3, 3)] = SDL_SCANCODE_KP_ENTER; + map[keycode_numpad(4, 0)] = SDL_SCANCODE_KP_0; + map[keycode_numpad(4, 1)] = SDL_SCANCODE_KP_COMMA; + }; + + SDL_Scancode map[0x100]; +}; +static Keymap s_keymap; + +static int BANANOS_mouse_button_to_sdl(LibInput::MouseButton button) +{ + switch (button) { +#define BUTTON_CASE(my, sdl) case LibInput::MouseButton::my: return SDL_BUTTON_##sdl + BUTTON_CASE(Left, LEFT); + BUTTON_CASE(Right, RIGHT); + BUTTON_CASE(Middle, MIDDLE); + BUTTON_CASE(Extra1, X1); + BUTTON_CASE(Extra2, X2); +#undef BUTTON_CASE + } + return 0; +} + +static SDL_VideoDevice* s_video_device = nullptr; + +static SDL_Cursor* BANANOS_CreateSystemCursor(SDL_SystemCursor id) +{ + DUMP_FUNCTION(); + + // FIXME: support system cursors :) + + auto* cursor = static_cast(SDL_calloc(1, sizeof(SDL_Cursor))); + if (cursor == nullptr) { + SDL_OutOfMemory(); + return nullptr; + } + + return cursor; +} + +static void BANANOS_FreeCursor(SDL_Cursor* cursor) +{ + DUMP_FUNCTION(); + + if (cursor != nullptr) + SDL_free(cursor); +} + +static int BANANOS_ShowCursor(SDL_Cursor* cursor) +{ + DUMP_FUNCTION(); + + if (s_video_device == nullptr) + return 0; + + auto& ban_video_device_data = *static_cast(s_video_device->driverdata); + for (auto it = ban_video_device_data.windows.begin(); it != ban_video_device_data.windows.end(); it++) + (*it)->window->set_cursor_visible(!!cursor); + + return 0; +} + +static int BANANOS_SetRelativeMouseMode(SDL_bool enabled) +{ + DUMP_FUNCTION(); + + auto* window = SDL_GetMouseFocus(); + if (window == nullptr) + return 0; + + auto& ban_window = *static_cast(window->driverdata); + ban_window.window->set_mouse_relative(enabled); + ban_window.relative = enabled; + + return 0; +} + +static void BANANOS_InitMouse(_THIS) +{ + DUMP_FUNCTION(); + + auto* mouse = SDL_GetMouse(); + mouse->ShowCursor = BANANOS_ShowCursor; + mouse->SetRelativeMouseMode = BANANOS_SetRelativeMouseMode; + mouse->CreateSystemCursor = BANANOS_CreateSystemCursor; + mouse->FreeCursor = BANANOS_FreeCursor; + + SDL_SetDefaultCursor(BANANOS_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW)); +} + +static int BANANOS_VideoInit(_THIS) +{ + DUMP_FUNCTION(); + + BANANOS_InitMouse(_this); + + int fb_fd = open("/dev/fb0", O_RDONLY); + if (fb_fd == -1) { + dwarnln("Failed to open framebuffer: {}", strerror(errno)); + return -1; + } + + framebuffer_info_t fb_info; + const ssize_t nread = pread(fb_fd, &fb_info, sizeof(fb_info), -1); + close(fb_fd); + + if (nread != sizeof(fb_info)) { + dwarnln("Failed to get framebuffer info"); + return -1; + } + + SDL_DisplayMode mode { + .format = SDL_PIXELFORMAT_RGB888, + .w = static_cast(fb_info.width), + .h = static_cast(fb_info.height), + .refresh_rate = 60, + .driverdata = nullptr, + }; + + if (SDL_AddBasicVideoDisplay(&mode) < 0) + return -1; + + SDL_AddDisplayMode(&_this->displays[0], &mode); + + if (s_video_device == nullptr) + s_video_device = _this; + + return 0; +} + +static void BANANOS_VideoQuit(_THIS) +{ + DUMP_FUNCTION(); + + if (s_video_device == _this) + s_video_device = nullptr; +} + +static void BANANOS_free(_THIS) +{ + DUMP_FUNCTION(); + delete static_cast(_this->driverdata); + SDL_free(_this); +} + +static int BANANOS_CreateSDLWindow(_THIS, SDL_Window* window) +{ + DUMP_FUNCTION(); + + auto attributes = LibGUI::Window::default_attributes; + attributes.shown = false; + attributes.resizable = false; + + auto window_or_error = LibGUI::Window::create(window->w, window->h, ""_sv, attributes); + if (window_or_error.is_error()) { + dwarnln("LibGUI::Window::create: {}", window_or_error.error()); + return -1; + } + + auto& ban_video_device_data = *static_cast(_this->driverdata); + + auto* ban_window = new banan_os_window(window_or_error.release_value()); + + window->driverdata = ban_window; + if (window->driverdata == nullptr) + return -1; + + if (ban_video_device_data.windows.push_back(ban_window).is_error()) { + delete ban_window; + return -1; + } + + ban_window->window->set_key_event_callback( + [](LibGUI::EventPacket::KeyEvent::event_t event) { + SDL_SendKeyboardKey(event.pressed() ? SDL_PRESSED : SDL_RELEASED, s_keymap.map[event.scancode]); + + if (event.pressed()) { + if (const char* utf8 = LibInput::key_to_utf8(event.key, event.modifier)) + SDL_SendKeyboardText(utf8); + } + } + ); + + ban_window->window->set_mouse_button_event_callback( + [window, ban_window](LibGUI::EventPacket::MouseButtonEvent::event_t event) { + const int state = event.pressed ? SDL_PRESSED : SDL_RELEASED; + SDL_SendMouseMotion(window, 0, ban_window->relative, event.x, event.y); + SDL_SendMouseButton(window, 0, state, BANANOS_mouse_button_to_sdl(event.button)); + } + ); + + ban_window->window->set_mouse_move_event_callback( + [window, ban_window](LibGUI::EventPacket::MouseMoveEvent::event_t event) { + SDL_SendMouseMotion(window, 0, ban_window->relative, event.x, event.y); + } + ); + + ban_window->window->set_mouse_scroll_event_callback( + [window](LibGUI::EventPacket::MouseScrollEvent::event_t event) { + SDL_SendMouseWheel(window, 0, 0.0f, event.scroll, SDL_MOUSEWHEEL_NORMAL); + } + ); + + ban_window->window->set_resize_window_event_callback( + [window, ban_window]() { + const size_t width = ban_window->window->width(); + const size_t height = ban_window->window->height(); + + if (ban_window->gl_context) { + OSMesaMakeCurrent( + ban_window->gl_context, + ban_window->window->texture().pixels().data(), + GL_UNSIGNED_BYTE, + width, height + ); + OSMesaPixelStore(OSMESA_Y_UP, 0); + } + + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, width, height); + } + ); + + ban_window->window->set_window_shown_event_callback( + [window](LibGUI::EventPacket::WindowShownEvent::event_t event) { + const int state = event.shown ? SDL_WINDOWEVENT_SHOWN : SDL_WINDOWEVENT_HIDDEN; + SDL_SendWindowEvent(window, state, 0, 0); + } + ); + + ban_window->window->set_window_focus_event_callback( + [window](LibGUI::EventPacket::WindowFocusEvent::event_t event) { + SDL_SetKeyboardFocus(event.focused ? window : nullptr); + } + ); + + ban_window->window->set_close_window_event_callback( + [window]() { + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_CLOSE, 0, 0); + } + ); + + return 0; +} + +static void BANANOS_DestroyWindow(_THIS, SDL_Window* window) +{ + DUMP_FUNCTION(); + + auto& ban_video_device_data = *static_cast(_this->driverdata); + for (auto it = ban_video_device_data.windows.begin(); it != ban_video_device_data.windows.end(); it++) { + if (*it != static_cast(window->driverdata)) + continue; + ban_video_device_data.windows.remove(it); + break; + } + + delete static_cast(window->driverdata); + window->driverdata = nullptr; +} + +static void BANANOS_ShowWindow(_THIS, SDL_Window* window) +{ + DUMP_FUNCTION(); + + auto& ban_window = *static_cast(window->driverdata); + + auto attributes = ban_window.window->get_attributes(); + if (attributes.shown) + return; + attributes.shown = true; + + ban_window.window->set_attributes(attributes); +} + +static void BANANOS_HideWindow(_THIS, SDL_Window* window) +{ + DUMP_FUNCTION(); + + auto& ban_window = *static_cast(window->driverdata); + + auto attributes = ban_window.window->get_attributes(); + if (!attributes.shown) + return; + attributes.shown = false; + + ban_window.window->set_attributes(attributes); +} + +static void BANANOS_SetWindowTitle(_THIS, SDL_Window* window) +{ + DUMP_FUNCTION(); + + auto& ban_window = *static_cast(window->driverdata); + ban_window.window->set_title(window->title); +} + +static void BANANOS_SetWindowPosition(_THIS, SDL_Window* window) +{ + DUMP_FUNCTION(); + + auto& ban_window = *static_cast(window->driverdata); + ban_window.window->set_position(window->x, window->y); +} + +static void BANANOS_SetWindowSize(_THIS, SDL_Window* window) +{ + DUMP_FUNCTION(); + + auto& ban_window = *static_cast(window->driverdata); + ban_window.window->request_resize(window->w, window->h); +} + +static void BANANOS_SetWindowMinimumSize(_THIS, SDL_Window* window) +{ + DUMP_FUNCTION(); + + auto& ban_window = *static_cast(window->driverdata); + ban_window.window->set_min_size(window->min_w, window->min_h); +} + +static void BANANOS_SetWindowMaximumSize(_THIS, SDL_Window* window) +{ + DUMP_FUNCTION(); + + auto& ban_window = *static_cast(window->driverdata); + ban_window.window->set_max_size(window->min_w, window->min_h); +} + +static void BANANOS_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered) +{ + DUMP_FUNCTION(); +} + +static void BANANOS_SetWindowResizable(_THIS, SDL_Window * window, SDL_bool resizable) +{ + DUMP_FUNCTION(); + + auto& ban_window = *static_cast(window->driverdata); + if (auto attributes = ban_window.window->get_attributes(); attributes.resizable != resizable) { + attributes.resizable = resizable; + ban_window.window->set_attributes(attributes); + } +} + +static void BANANOS_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen) +{ + DUMP_FUNCTION(); + + auto& ban_window = *static_cast(window->driverdata); + ban_window.window->set_fullscreen(fullscreen); +} + +static int BANANOS_CreateWindowFramebuffer(_THIS, SDL_Window* window, Uint32* format, void** pixels, int* pitch) +{ + DUMP_FUNCTION(); + + auto& ban_window = *static_cast(window->driverdata); + + if (ban_window.gl_context) { + dwarnln("CreateWindowFramebuffer with OpenGL context"); + return -1; + } + + auto framebuffer_or_error = LibGUI::Texture::create(ban_window.window->width(), ban_window.window->width(), 0x000000); + if (framebuffer_or_error.is_error()) { + dwarnln("LibGUI::Texture::create: {}", framebuffer_or_error.error()); + return -1; + } + + *format = SDL_PIXELFORMAT_BGR888; + *pixels = ban_window.window->texture().pixels().data(); + *pitch = ban_window.window->texture().width() * sizeof(uint32_t); + + return 0; +} + +static int BANANOS_UpdateWindowFramebuffer(_THIS, SDL_Window* window, const SDL_Rect* rects, int numrects) +{ + DUMP_FUNCTION(); + + auto& ban_window = *static_cast(window->driverdata); + + for (int i = 0; i < numrects; i++) + ban_window.window->invalidate(rects[i].x, rects[i].y, rects[i].w, rects[i].h); + + return 0; +} + +static void BANANOS_DestroyWindowFramebuffer(_THIS, SDL_Window* window) +{ + DUMP_FUNCTION(); +} + +static void BANANOS_PumpEvents(_THIS) +{ + DUMP_FUNCTION(); + + auto& ban_video_device_data = *static_cast(_this->driverdata); + for (auto* window : ban_video_device_data.windows) + window->window->poll_events(); +} + +static int BANANOS_GL_LoadLibrary(_THIS, const char* path) +{ + DUMP_FUNCTION(); + + if (_this->gl_config.driver_loaded) { + SDL_SetError("OpenGL library is already loaded"); + return -1; + } + + _this->gl_config.driver_loaded = SDL_TRUE; + return 0; +} + +static void* BANANOS_GL_GetProcAddress(_THIS, const char* proc) +{ + DUMP_FUNCTION(); + + return reinterpret_cast(OSMesaGetProcAddress(proc)); +} + +static SDL_GLContext BANANOS_GL_CreateContext(_THIS, SDL_Window* window) +{ + DUMP_FUNCTION(); + + auto& ban_window = *static_cast(window->driverdata); + + auto gl_context = OSMesaCreateContext(OSMESA_BGRA, NULL); + if (gl_context == nullptr) { + dwarnln("OSMesaCreateContext"); + return nullptr; + } + + auto& fb = ban_window.window->texture(); + if (!OSMesaMakeCurrent(gl_context, fb.pixels().data(), GL_UNSIGNED_BYTE, fb.width(), fb.height())) { + OSMesaDestroyContext(gl_context); + return nullptr; + } + OSMesaPixelStore(OSMESA_Y_UP, 0); + + ban_window.gl_context = gl_context; + + return gl_context; +} + +static void BANANOS_GL_DeleteContext(_THIS, SDL_GLContext context) +{ + DUMP_FUNCTION(); + + auto gl_context = static_cast(context); + OSMesaDestroyContext(gl_context); +} + +static int BANANOS_GL_MakeCurrent(_THIS, SDL_Window* window, SDL_GLContext context) +{ + DUMP_FUNCTION(); + + if (window == nullptr || context == nullptr) + return 0; + + auto& ban_window = *static_cast(window->driverdata); + + auto gl_context = static_cast(context); + + auto& fb = ban_window.window->texture(); + if (!OSMesaMakeCurrent(gl_context, fb.pixels().data(), GL_UNSIGNED_BYTE, fb.width(), fb.height())) + return -1; + OSMesaPixelStore(OSMESA_Y_UP, 0); + + return 0; +} + +static int BANANOS_GL_SwapWindow(_THIS, SDL_Window* window) +{ + DUMP_FUNCTION(); + + static void (*glFinish)() = nullptr; + if (glFinish == nullptr) + glFinish = OSMesaGetProcAddress("glFinish"); + glFinish(); + + auto& ban_window = *static_cast(window->driverdata); + ban_window.window->invalidate(); + + return 0; +} + +static SDL_VideoDevice* BANANOS_CreateDevice(void) +{ + DUMP_FUNCTION(); + + auto* device = static_cast(SDL_calloc(1, sizeof(SDL_VideoDevice))); + if (device == nullptr) { + SDL_OutOfMemory(); + return nullptr; + } + + device->driverdata = new banan_os_video_device_data(); + if (device->driverdata == nullptr) { + SDL_OutOfMemory(); + return nullptr; + } + + device->VideoInit = BANANOS_VideoInit; + device->VideoQuit = BANANOS_VideoQuit; + + device->CreateSDLWindow = BANANOS_CreateSDLWindow; + device->DestroyWindow = BANANOS_DestroyWindow; + device->ShowWindow = BANANOS_ShowWindow; + device->HideWindow = BANANOS_HideWindow; + + device->SetWindowTitle = BANANOS_SetWindowTitle; + device->SetWindowPosition = BANANOS_SetWindowPosition; + device->SetWindowSize = BANANOS_SetWindowSize; + device->SetWindowMinimumSize = BANANOS_SetWindowMinimumSize; + device->SetWindowMaximumSize = BANANOS_SetWindowMaximumSize; + device->SetWindowBordered = BANANOS_SetWindowBordered; + device->SetWindowResizable = BANANOS_SetWindowResizable; + device->SetWindowFullscreen = BANANOS_SetWindowFullscreen; + + device->CreateWindowFramebuffer = BANANOS_CreateWindowFramebuffer; + device->UpdateWindowFramebuffer = BANANOS_UpdateWindowFramebuffer; + device->DestroyWindowFramebuffer = BANANOS_DestroyWindowFramebuffer; + + device->PumpEvents = BANANOS_PumpEvents; + + device->GL_LoadLibrary = BANANOS_GL_LoadLibrary; + device->GL_GetProcAddress = BANANOS_GL_GetProcAddress; + device->GL_CreateContext = BANANOS_GL_CreateContext; + device->GL_DeleteContext = BANANOS_GL_DeleteContext; + device->GL_MakeCurrent = BANANOS_GL_MakeCurrent; + device->GL_SwapWindow = BANANOS_GL_SwapWindow; + + device->SetClipboardText = BANANOS_SetClipboardText; + device->GetClipboardText = BANANOS_GetClipboardText; + device->HasClipboardText = BANANOS_HasClipboardText; + + device->free = BANANOS_free; + + return device; +} + +VideoBootStrap BANANOS_bootstrap = { + "banan-os", "banan-os graphics", + BANANOS_CreateDevice, + BANANOS_ShowMessageBox +}; + +#endif /* SDL_VIDEO_DRIVER_BANANOS */ + +/* vi: set ts=4 sw=4 expandtab: */ diff -ruN SDL2-2.32.8/src/video/SDL_sysvideo.h SDL2-2.32.8-banan_os/src/video/SDL_sysvideo.h --- SDL2-2.32.8/src/video/SDL_sysvideo.h 2025-05-20 00:24:41.000000000 +0300 +++ SDL2-2.32.8-banan_os/src/video/SDL_sysvideo.h 2026-01-03 19:50:45.781358361 +0200 @@ -462,6 +462,7 @@ extern VideoBootStrap WINDOWS_bootstrap; extern VideoBootStrap WINRT_bootstrap; extern VideoBootStrap HAIKU_bootstrap; +extern VideoBootStrap BANANOS_bootstrap; extern VideoBootStrap PND_bootstrap; extern VideoBootStrap UIKIT_bootstrap; extern VideoBootStrap Android_bootstrap; diff -ruN SDL2-2.32.8/src/video/SDL_video.c SDL2-2.32.8-banan_os/src/video/SDL_video.c --- SDL2-2.32.8/src/video/SDL_video.c 2025-05-20 00:24:41.000000000 +0300 +++ SDL2-2.32.8-banan_os/src/video/SDL_video.c 2026-01-03 19:50:45.782037083 +0200 @@ -96,6 +96,9 @@ #ifdef SDL_VIDEO_DRIVER_HAIKU &HAIKU_bootstrap, #endif +#ifdef SDL_VIDEO_DRIVER_BANANOS + &BANANOS_bootstrap, +#endif #ifdef SDL_VIDEO_DRIVER_PANDORA &PND_bootstrap, #endif