diff -ruN SDL-release-2.30.11/CMakeLists.txt SDL-release-2.30.11-banan_os/CMakeLists.txt --- SDL-release-2.30.11/CMakeLists.txt 2025-01-01 19:09:38.000000000 +0200 +++ SDL-release-2.30.11-banan_os/CMakeLists.txt 2025-06-25 16:53:26.163363407 +0300 @@ -1452,7 +1452,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) @@ -2422,6 +2422,49 @@ 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_VIDEO) + set(SDL_VIDEO_DRIVER_BANAN_OS 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) + + if(SDL_OPENGL) + set(SDL_VIDEO_OPENGL 1) + set(SDL_VIDEO_OPENGL_BANAN_OS 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 SDL-release-2.30.11/include/SDL_config.h.cmake SDL-release-2.30.11-banan_os/include/SDL_config.h.cmake --- SDL-release-2.30.11/include/SDL_config.h.cmake 2025-01-01 19:09:38.000000000 +0200 +++ SDL-release-2.30.11-banan_os/include/SDL_config.h.cmake 2025-06-24 19:53:27.963249565 +0300 @@ -406,6 +406,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_BANAN_OS @SDL_VIDEO_DRIVER_BANAN_OS@ #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 SDL-release-2.30.11/include/SDL_platform.h SDL-release-2.30.11-banan_os/include/SDL_platform.h --- SDL-release-2.30.11/include/SDL_platform.h 2025-01-01 19:09:38.000000000 +0200 +++ SDL-release-2.30.11-banan_os/include/SDL_platform.h 2025-06-24 17:54:20.094530618 +0300 @@ -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 SDL-release-2.30.11/src/misc/banan_os/SDL_sysurl.cpp SDL-release-2.30.11-banan_os/src/misc/banan_os/SDL_sysurl.cpp --- SDL-release-2.30.11/src/misc/banan_os/SDL_sysurl.cpp 1970-01-01 02:00:00.000000000 +0200 +++ SDL-release-2.30.11-banan_os/src/misc/banan_os/SDL_sysurl.cpp 2025-06-24 18:51:56.695953622 +0300 @@ -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 SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_message_box.cpp SDL-release-2.30.11-banan_os/src/video/banan_os/SDL_banan_os_message_box.cpp --- SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_message_box.cpp 1970-01-01 02:00:00.000000000 +0200 +++ SDL-release-2.30.11-banan_os/src/video/banan_os/SDL_banan_os_message_box.cpp 2025-06-27 16:29:59.012277213 +0300 @@ -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_BANAN_OS + +#include "SDL_messagebox.h" + +#include "SDL_banan_os_message_box.h" + +#include +#include + +int BANAN_OS_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_BANAN_OS */ + +/* vi: set ts=4 sw=4 expandtab: */ diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_message_box.h SDL-release-2.30.11-banan_os/src/video/banan_os/SDL_banan_os_message_box.h --- SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_message_box.h 1970-01-01 02:00:00.000000000 +0200 +++ SDL-release-2.30.11-banan_os/src/video/banan_os/SDL_banan_os_message_box.h 2025-06-24 19:49:44.390183027 +0300 @@ -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_BANAN_OS_MESSAGEBOX_H +#define SDL_BANAN_OS_MESSAGEBOX_H + +#include "../../SDL_internal.h" + +#ifdef SDL_VIDEO_DRIVER_BANAN_OS + +#ifdef __cplusplus +extern "C" { +#endif + +extern int +BANAN_OS_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid); + +#ifdef __cplusplus +} +#endif + +#endif /* SDL_BANAN_OS_MESSAGEBOX_H */ + +#endif + +/* vi: set ts=4 sw=4 expandtab: */ diff -ruN SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_video.cpp SDL-release-2.30.11-banan_os/src/video/banan_os/SDL_banan_os_video.cpp --- SDL-release-2.30.11/src/video/banan_os/SDL_banan_os_video.cpp 1970-01-01 02:00:00.000000000 +0200 +++ SDL-release-2.30.11-banan_os/src/video/banan_os/SDL_banan_os_video.cpp 2025-06-28 20:49:41.452498550 +0300 @@ -0,0 +1,770 @@ +/* + 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_BANAN_OS + +extern "C" { +#include "../SDL_sysvideo.h" +#include "../../events/SDL_events_c.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; + LibGUI::Texture framebuffer; + OSMesaContext gl_context; +}; + +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 BANAN_OS_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* BANAN_OS_CreateSystemCursor(SDL_SystemCursor id) +{ + DUMP_FUNCTION(); + + auto* cursor = static_cast(SDL_calloc(1, sizeof(SDL_Cursor))); + if (cursor == nullptr) { + SDL_OutOfMemory(); + return nullptr; + } + + return cursor; +} + +static void BANAN_OS_FreeCursor(SDL_Cursor* cursor) +{ + DUMP_FUNCTION(); + + if (cursor != nullptr) + SDL_free(cursor); +} + +static int BANAN_OS_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 BANAN_OS_CaptureMouse(SDL_Window* window) +{ + DUMP_FUNCTION(); + + auto& ban_window = *static_cast(window->driverdata); + ban_window.window->set_mouse_capture(SDL_GetMouse()->capture_window == window); + + return 0; +} + +static void BANAN_OS_InitMouse(_THIS) +{ + DUMP_FUNCTION(); + + auto* mouse = SDL_GetMouse(); + mouse->ShowCursor = BANAN_OS_ShowCursor; + mouse->CaptureMouse = BANAN_OS_CaptureMouse; + mouse->CreateSystemCursor = BANAN_OS_CreateSystemCursor; + mouse->FreeCursor = BANAN_OS_FreeCursor; +} + +static int BANAN_OS_VideoInit(_THIS) +{ + DUMP_FUNCTION(); + + BANAN_OS_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 BANAN_OS_VideoQuit(_THIS) +{ + DUMP_FUNCTION(); + + if (s_video_device == _this) + s_video_device = nullptr; +} + +static void BANAN_OS_free(_THIS) +{ + DUMP_FUNCTION(); + delete static_cast(_this->driverdata); + SDL_free(_this); +} + +static int BANAN_OS_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](LibGUI::EventPacket::MouseButtonEvent::event_t event) { + const int state = event.pressed ? SDL_PRESSED : SDL_RELEASED; + SDL_SendMouseMotion(window, 0, SDL_FALSE, event.x, event.y); + SDL_SendMouseButton(window, 0, state, BANAN_OS_mouse_button_to_sdl(event.button)); + } + ); + + ban_window->window->set_mouse_move_event_callback( + [window](LibGUI::EventPacket::MouseMoveEvent::event_t event) { + SDL_SendMouseMotion(window, 0, SDL_FALSE, 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) { + ban_window->framebuffer = MUST(LibGUI::Texture::create(width, height, 0x000000)); + OSMesaMakeCurrent( + ban_window->gl_context, + ban_window->framebuffer.pixels().data(), + GL_UNSIGNED_BYTE, + width, height + ); + } + + 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_close_window_event_callback( + [window]() { + SDL_SendWindowEvent(window, SDL_WINDOWEVENT_CLOSE, 0, 0); + } + ); + + return 0; +} + +static void BANAN_OS_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 BANAN_OS_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 BANAN_OS_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 BANAN_OS_SetWindowTitle(_THIS, SDL_Window* window) +{ + DUMP_FUNCTION(); + + auto& ban_window = *static_cast(window->driverdata); + ban_window.window->set_title(window->title); +} + +static void BANAN_OS_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 BANAN_OS_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 BANAN_OS_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 BANAN_OS_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 BANAN_OS_SetWindowBordered(_THIS, SDL_Window * window, SDL_bool bordered) +{ + DUMP_FUNCTION(); +} + +static void BANAN_OS_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 BANAN_OS_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 BANAN_OS_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; + } + + ban_window.framebuffer = framebuffer_or_error.release_value(); + + *format = SDL_PIXELFORMAT_BGR888; + *pixels = ban_window.framebuffer.pixels().data(); + *pitch = ban_window.framebuffer.width() * sizeof(uint32_t); + + return 0; +} + +static int BANAN_OS_UpdateWindowFramebuffer(_THIS, SDL_Window* window, const SDL_Rect* rects, int numrects) +{ + DUMP_FUNCTION(); + + auto& ban_window = *static_cast(window->driverdata); + + int min_x = ban_window.window->width(); + int min_y = ban_window.window->height(); + int max_x = 0; + int max_y = 0; + for (int i = 0; i < numrects; i++) { + ban_window.window->texture().copy_texture(ban_window.framebuffer, + rects[i].x, rects[i].y, + rects[i].x, rects[i].y, + rects[i].w, rects[i].h + ); + + min_x = BAN::Math::min(min_x, rects[i].x); + min_y = BAN::Math::min(min_y, rects[i].y); + max_x = BAN::Math::max(max_x, rects[i].x + rects[i].w); + max_y = BAN::Math::max(max_y, rects[i].y + rects[i].h); + } + + if (min_x < max_x && min_y < max_y) + ban_window.window->invalidate(min_x, min_y, max_x - min_x, max_y - min_y); + + return 0; +} + +static void BANAN_OS_DestroyWindowFramebuffer(_THIS, SDL_Window* window) +{ + DUMP_FUNCTION(); + + auto& ban_window = *static_cast(window->driverdata); + ban_window.framebuffer = {}; +} + +static int BANAN_OS_WaitEventTimeout(_THIS, int timeout) +{ + DUMP_FUNCTION(); + + auto& ban_video_device_data = *static_cast(_this->driverdata); + + fd_set fds; + FD_ZERO(&fds); + + int max_fd = 0; + for (auto* window : ban_video_device_data.windows) + { + max_fd = BAN::Math::max(max_fd, window->window->server_fd()); + FD_SET(window->window->server_fd(), &fds); + } + + timeval tv { + .tv_sec = static_cast(timeout / 1000), + .tv_usec = static_cast((timeout % 1000) * 1000), + }; + return select(max_fd + 1, &fds, nullptr, nullptr, &tv); +} + +static void BANAN_OS_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 BANAN_OS_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* BANAN_OS_GL_GetProcAddress(_THIS, const char* proc) +{ + DUMP_FUNCTION(); + + return reinterpret_cast(OSMesaGetProcAddress(proc)); +} + +static SDL_GLContext BANAN_OS_GL_CreateContext(_THIS, SDL_Window* window) +{ + DUMP_FUNCTION(); + + auto& ban_window = *static_cast(window->driverdata); + + 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 nullptr; + } + + auto gl_context = OSMesaCreateContext(OSMESA_BGRA, NULL); + if (gl_context == nullptr) { + dwarnln("OSMesaCreateContext"); + return nullptr; + } + + auto fb = framebuffer_or_error.release_value(); + if (!OSMesaMakeCurrent(gl_context, fb.pixels().data(), GL_UNSIGNED_BYTE, fb.width(), fb.height())) { + OSMesaDestroyContext(gl_context); + return nullptr; + } + + ban_window.framebuffer = BAN::move(fb); + ban_window.gl_context = gl_context; + + return gl_context; +} + +static void BANAN_OS_GL_DeleteContext(_THIS, SDL_GLContext context) +{ + DUMP_FUNCTION(); + + auto gl_context = static_cast(context); + OSMesaDestroyContext(gl_context); +} + +static int BANAN_OS_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.framebuffer; + if (!OSMesaMakeCurrent(gl_context, fb.pixels().data(), GL_UNSIGNED_BYTE, fb.width(), fb.height())) + return -1; + + return 0; +} + +static int BANAN_OS_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); + + auto* src = ban_window.framebuffer.pixels().data(); + auto* dst = ban_window.window->texture().pixels().data(); + + const size_t width = ban_window.window->width(); + const size_t height = ban_window.window->height(); + for (size_t y = 0; y < height; y++) + memcpy(&dst[(height - y - 1) * width], &src[y * width], width * 4); + + ban_window.window->invalidate(); + + return 0; +} + +static SDL_VideoDevice* BANAN_OS_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 = BANAN_OS_VideoInit; + device->VideoQuit = BANAN_OS_VideoQuit; + + device->CreateSDLWindow = BANAN_OS_CreateSDLWindow; + device->DestroyWindow = BANAN_OS_DestroyWindow; + device->ShowWindow = BANAN_OS_ShowWindow; + device->HideWindow = BANAN_OS_HideWindow; + + device->SetWindowTitle = BANAN_OS_SetWindowTitle; + device->SetWindowPosition = BANAN_OS_SetWindowPosition; + device->SetWindowSize = BANAN_OS_SetWindowSize; + device->SetWindowMinimumSize = BANAN_OS_SetWindowMinimumSize; + device->SetWindowMaximumSize = BANAN_OS_SetWindowMaximumSize; + device->SetWindowBordered = BANAN_OS_SetWindowBordered; + device->SetWindowResizable = BANAN_OS_SetWindowResizable; + device->SetWindowFullscreen = BANAN_OS_SetWindowFullscreen; + + device->CreateWindowFramebuffer = BANAN_OS_CreateWindowFramebuffer; + device->UpdateWindowFramebuffer = BANAN_OS_UpdateWindowFramebuffer; + device->DestroyWindowFramebuffer = BANAN_OS_DestroyWindowFramebuffer; + + device->WaitEventTimeout = BANAN_OS_WaitEventTimeout; + device->PumpEvents = BANAN_OS_PumpEvents; + + device->GL_LoadLibrary = BANAN_OS_GL_LoadLibrary; + device->GL_GetProcAddress = BANAN_OS_GL_GetProcAddress; + device->GL_CreateContext = BANAN_OS_GL_CreateContext; + device->GL_DeleteContext = BANAN_OS_GL_DeleteContext; + device->GL_MakeCurrent = BANAN_OS_GL_MakeCurrent; + device->GL_SwapWindow = BANAN_OS_GL_SwapWindow; + + device->free = BANAN_OS_free; + + return device; +} + +VideoBootStrap BANAN_OS_bootstrap = { + "banan-os", "banan-os graphics", + BANAN_OS_CreateDevice, + BANAN_OS_ShowMessageBox +}; + +#endif /* SDL_VIDEO_DRIVER_BANAN_OS */ + +/* vi: set ts=4 sw=4 expandtab: */ diff -ruN SDL-release-2.30.11/src/video/SDL_sysvideo.h SDL-release-2.30.11-banan_os/src/video/SDL_sysvideo.h --- SDL-release-2.30.11/src/video/SDL_sysvideo.h 2025-01-01 19:09:38.000000000 +0200 +++ SDL-release-2.30.11-banan_os/src/video/SDL_sysvideo.h 2025-06-24 18:55:08.546768590 +0300 @@ -462,6 +462,7 @@ extern VideoBootStrap WINDOWS_bootstrap; extern VideoBootStrap WINRT_bootstrap; extern VideoBootStrap HAIKU_bootstrap; +extern VideoBootStrap BANAN_OS_bootstrap; extern VideoBootStrap PND_bootstrap; extern VideoBootStrap UIKIT_bootstrap; extern VideoBootStrap Android_bootstrap; diff -ruN SDL-release-2.30.11/src/video/SDL_video.c SDL-release-2.30.11-banan_os/src/video/SDL_video.c --- SDL-release-2.30.11/src/video/SDL_video.c 2025-01-01 19:09:38.000000000 +0200 +++ SDL-release-2.30.11-banan_os/src/video/SDL_video.c 2025-06-24 19:37:36.342677687 +0300 @@ -94,6 +94,9 @@ #ifdef SDL_VIDEO_DRIVER_HAIKU &HAIKU_bootstrap, #endif +#ifdef SDL_VIDEO_DRIVER_BANAN_OS + &BANAN_OS_bootstrap, +#endif #ifdef SDL_VIDEO_DRIVER_PANDORA &PND_bootstrap, #endif