diff --git a/CMakeLists.txt b/CMakeLists.txt index fd07059..b00a1b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,28 +12,111 @@ project(hdk-sdl # hdk-sdl OPTIONS option(HDK_SDL_TESTS "Build hdk-sdl tests" ${HDK_TESTS}) option(HDK_SDL_EXAMPLES "Build hdk-sdl examples" ${HDK_EXAMPLES}) +option(HDK_SDL_INSTALL_TARGETS "Enable hdk-sdl install/export stubs" ON) +option(HDK_SDL_SYSTEM_SDL3 "Use system SDL3 instead of FetchContent for building" OFF) +option(HDK_SDL_BUNDLE_SDL3 "Build and bundle SDL3 as a static lib with hdk-sdl install" OFF) + +if(HDK_SDL_SYSTEM_SDL3 AND HDK_SDL_BUNDLE_SDL3) + message(FATAL_ERROR "HDK_SDL_SYSTEM_SDL3 and HDK_SDL_BUNDLE_SDL3 are mutually exclusive") +endif() + if(HDK_SDL_EXAMPLES) add_subdirectory(examples) endif() # FOR SDL Building -set(SDL_TEST OFF CACHE BOOL "Disable SDL tests" FORCE) -set(SDL_EXAMPLES OFF CACHE BOOL "Disable SDL examples" FORCE) -set(SDL_INSTALL OFF CACHE BOOL "Disable SDL install" FORCE) -# NOTE: Possibly disable SDL_PIPEWIRE and/or SDL_PIPEWIRE_SHARED? -include(FetchContent) -FetchContent_Declare( - SDL3 - GIT_REPOSITORY https://github.com/libsdl-org/SDL.git - GIT_TAG release-3.4.8 -) -FetchContent_MakeAvailable(SDL3) +if(NOT HDK_SDL_SYSTEM_SDL3) + set(SDL_TEST OFF CACHE BOOL "Disable SDL tests" FORCE) + set(SDL_EXAMPLES OFF CACHE BOOL "Disable SDL examples" FORCE) + set(SDL_INSTALL OFF CACHE BOOL "Disable SDL install" FORCE) + # NOTE: Possibly disable SDL_PIPEWIRE and/or SDL_PIPEWIRE_SHARED? + + if(HDK_SDL_BUNDLE_SDL3) + set(SDL_STATIC ON CACHE BOOL "Build SDL3 as static library for bundling" FORCE) + set(SDL_SHARED OFF CACHE BOOL "Disable SDL3 shared library for bundling" FORCE) + endif() + + include(FetchContent) + FetchContent_Declare( + SDL3 + GIT_REPOSITORY https://github.com/libsdl-org/SDL.git + GIT_TAG release-3.4.8 + ) + FetchContent_MakeAvailable(SDL3) +else() + find_package(SDL3 REQUIRED) +endif() + +include(GNUInstallDirs) add_library(hdk-sdl INTERFACE) -target_link_libraries(hdk-sdl INTERFACE SDL3::SDL3 hdk-grid) -target_include_directories(hdk-sdl INTERFACE ${SDL3_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_link_libraries(hdk-sdl INTERFACE + $ + $ + $ + $ +) +target_include_directories(hdk-sdl INTERFACE + $ + $ +) target_compile_features(hdk-sdl INTERFACE cxx_std_17) +if(HDK_SDL_INSTALL_TARGETS) + include(CMakePackageConfigHelpers) + + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ) + + # Choose config file based on bundle mode + if(HDK_SDL_BUNDLE_SDL3) + set(_hdk_sdl_config_file hdk-sdlConfig-bundled.cmake.in) + else() + set(_hdk_sdl_config_file hdk-sdlConfig.cmake.in) + endif() + + configure_package_config_file( + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${_hdk_sdl_config_file} + ${CMAKE_CURRENT_BINARY_DIR}/hdk-sdlConfig.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/hdk-sdl + PATH_VARS CMAKE_INSTALL_INCLUDEDIR CMAKE_INSTALL_LIBDIR + ) + + write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/hdk-sdlConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion + ) + + install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/hdk-sdlConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/hdk-sdlConfigVersion.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/hdk-sdl + ) + + # Bundled SDL3 install rules + if(HDK_SDL_BUNDLE_SDL3) + install(FILES $ + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/hdk-sdl/sdl3-static + ) + + install(DIRECTORY ${sdl3_SOURCE_DIR}/include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ) + + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/hdk-sdlBundledSDL3Targets.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/hdk-sdlBundledSDL3Targets.cmake + @ONLY + ) + + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/hdk-sdlBundledSDL3Targets.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/hdk-sdl + ) + endif() +endif() + if(HDK_SDL_TESTS) add_subdirectory(test) endif() diff --git a/README.md b/README.md new file mode 100644 index 0000000..9a6e3a2 --- /dev/null +++ b/README.md @@ -0,0 +1,190 @@ +# HDK-SDL wrapper + +HDK-SDL uses the grid `SharedPtrWrapper` to create relatively light-weight reference wrappers around pointers to SDL3 primitives. + +Use classes without declaring pointers to them, knowing that copying or referencing is relatively cheap and that the underlying SDL3 primitive will be properly managed and cleaned up when no longer needed. + +## Dependencies + +- holodeck-grid +- [SDL3](https://www.libsdl.org/) + +## Naming Conventions + +API that is 1 to 1 with SDL with otherwise minimal wrapping will be as close to the original SDL function call as possible: for example, `SDL_CreateWindow` will be wrapped by `hd::sdl::Window::Create`. + +To update a surface, the original SDL function is `SDL_UpdateWindowSurface(SDL_Window* window)`, and the wrapper will be `window.UpdateSurface()`. + +So on and so forth. But there are some exceptions and edge cases. + +For example when `Blit`ing a surface, the original SDL function is `SDL_BlitSurface`, but the wrapper will expose two alternatives: `BlitTo` and `BlitFrom`, each implying that `this*` is the source or destination of the blit, respectively. In this case not only is the function name changed, but the parameters are also rearranged to be more intuitive and consistent with the implied source/destination of the blit. + +## Default values + +While done sparingly, some wrapper methods may provide default values for parameters that are commonly used with a specific value. For example when the last parameter of a function is an optional pointer it may be defaulted to `nullptr` in the wrapper method, so that the caller can omit it when they don't need it. Sensible defaults may also be provided elsewhere. + +## Headers + +Within `include/hdk/sdl` there are folders for each SDL3 header files, and within each files for corresponding wrappers of SDL3 primitive types. Basically if SDL3 lets you "create" a primitive, use that primitive with some functions, and then "destroy" that primitive, there should be a wrapper for that primitive in the corresponding header file. The prime example is `include/hd/sdl/video/Window.hpp` which contains the `hd::sdl::video::Window` wrapper for `SDL_Window` + +With this entirely header-only and inline design, there is no need for a corresponding .cpp and the resulting compiled code should be as efficient as if you were using the raw SDL3 API directly, while still benefiting from the safety and convenience of C++ RAII and wrapper classes. + +## SDL API Coverage + +Most links are from Links from [API by Category](https://wiki.libsdl.org/SDL3/APIByCategory), then those respective pages are used to fill in the rest of the checklist. + +In progress/complete: + +- By category: +- [ ] BASICS + - [~] [Category Video][] + - [x] `hdk::sdl::Window` + - [x] `hdk::sdl::Display` + - [ ] [Category Surface][] + - [x] `hdk::sdl::Surface` + - [ ] [Category Main][] + - [ ] [Category Init][] + - [ ] [Category Hints][] + - [x] [Category Properties][] + - [ ] [Category Error][] + - [ ] [Category Log][] + - [ ] [Category Assert][] + - [ ] [Category Version][] + - [ ] [Category Events][] + - [ ] [Category Keyboard][] + - [ ] [Category Keycode][] + - [ ] [Category Scancode][] + - [ ] [Category Mouse][] + - [ ] [Category Joystick][] + - [ ] [Category Gamepad][] + - [ ] [Category Touch][] + - [ ] [Category Pen][] + - [ ] [Category Sensor][] + - [ ] [Category HIDAPI][] +- [ ] [Category Render][] +- [ ] [Category Pixels][] +- [ ] [Category Blendmode][] +- [ ] [Category Rect][] +- [ ] [Category Clipboard][] +- [ ] [Category Vulkan][] +- [ ] [Category Metal][] +- [ ] [Category Camera][] +- [ ] HAPTIC + - [ ] [Category Haptic][] +- [ ] AUDIO + - [ ] [Category Audio][] +- [ ] GPU + - [ ] [Category GPU][] +- [ ] Time + - [ ] [Category Timer][] + - [ ] [Category Time][] +- [ ] THREADS + - [ ] [Category Thread][] + - [ ] [Category Mutex][] + - [ ] [Category Atomic][] +- [ ] FILE I/O + - [ ] [Category Filesystem][] + - [ ] [Category Storage][] + - [ ] [Category IOStream][] + - [ ] [Category AsyncIO][] +- [ ] PLATFORM + - [ ] [Category Platform][] + - [ ] [Category CPUInfo][] + - [ ] [Category Intrinsics][] + - [ ] [Category Endian][] + - [ ] [Category Bits][] +- [ ] MISC + - [N] [Category Shared Object][] + - [ ] [Category Process][] + - [ ] [Category Power][] + - [ ] [Category Messagebox][] + - [ ] [Category Dialog][] + - [ ] [Category Tray][] + - [ ] [Category Locale][] + - [ ] [Category System][] + - [ ] [Category Stdinc][] + - [ ] [Category GUID][] + - [ ] [Category Misc][] + + +[Category Shared Object]: +[Category Assert]: https://wiki.libsdl.org/SDL3/CategoryAssert +[Category AsyncIO]: +[Category Atomic]: +[Category Audio]: + +[Category Bits]: +[Category Blendmode]: + +[Category Camera]: +[Category CPUInfo]: + +[Category Dialog]: + +[Category Endian]: +[Category Error]: https://wiki.libsdl.org/SDL3/CategoryError +[Category Events]: + +[Category Filesystem]: + +[Category Gamepad]: +[Category GPU]: +[Category GUID]: + +[Category Haptic]: +[Category HIDAPI]: +[Category Hints]: https://wiki.libsdl.org/SDL3/CategoryHints + +[Category Init]: https://wiki.libsdl.org/SDL3/CategoryInit +[Category Intrinsics]: +[Category IOStream]: + +[Category Joystick]: + +[Category Keyboard]: +[Category Keycode]: + +[Category Locale]: +[Category Log]: https://wiki.libsdl.org/SDL3/CategoryLog + +[Category Main]: https://wiki.libsdl.org/SDL3/CategoryMain +[Category Messagebox]: +[Category Metal]: +[Category Misc]: +[Category Mouse]: +[Category Mutex]: + + + +[Category Pen]: +[Category Pixels]: +[Category Platform]: +[Category Power]: +[Category Process]: +[Category Properties]: https://wiki.libsdl.org/SDL3/CategoryProperties + + +[Category Rect]: +[Category Render]: + +[Category Scancode]: +[Category Sensor]: +[Category Stdinc]: +[Category Storage]: +[Category Surface]: +[Category System]: + +[Category Thread]: +[Category Time]: +[Category Timer]: +[Category Touch]: +[Category Tray]: + + +[Category Version]: https://wiki.libsdl.org/SDL3/CategoryVersion +[Category Video]: https://wiki.libsdl.org/SDL3/CategoryVideo +[Category Vulkan]: + + + + diff --git a/cmake/hdk-sdlBundledSDL3Targets.cmake.in b/cmake/hdk-sdlBundledSDL3Targets.cmake.in new file mode 100644 index 0000000..06fabd6 --- /dev/null +++ b/cmake/hdk-sdlBundledSDL3Targets.cmake.in @@ -0,0 +1,36 @@ +# Bundled SDL3 static library targets +# This file defines SDL3::SDL3 as an IMPORTED STATIC target pointing to the bundled +# SDL3 static library. This allows consumers of hdk-sdl to link against the bundled +# SDL3 without requiring system SDL3 to be installed. + +# Compute the install prefix from this file's location +get_filename_component(_sdl3_install_prefix "${CMAKE_CURRENT_LIST_DIR}/../../.." ABSOLUTE) + +macro(set_and_check _var _file) + set(${_var} "${_file}") + if(NOT EXISTS "${_file}") + message(FATAL_ERROR "File or directory ${_var}=${_file} referenced by hdk-sdlBundledSDL3Targets does not exist!") + endif() +endmacro() + +# Validate the bundled SDL3 static library +set_and_check(_sdl3_lib_path "${_sdl3_install_prefix}/lib/cmake/hdk-sdl/sdl3-static/libSDL3.a") + +# Define SDL3::SDL3 as IMPORTED STATIC target +if(NOT TARGET SDL3::SDL3) + add_library(SDL3::SDL3 STATIC IMPORTED GLOBAL) + set_target_properties(SDL3::SDL3 PROPERTIES + IMPORTED_LOCATION "${_sdl3_lib_path}" + VERSION "3.4.8" + ) + # SDL3 is a static library; link dependencies would go here if needed + # For now, SDL3 is self-contained in the .a file +endif() + +# Optionally define SDL3::SDL3-static as an alias for clarity +if(NOT TARGET SDL3::SDL3-static) + add_library(SDL3::SDL3-static ALIAS SDL3::SDL3) +endif() + +unset(_sdl3_install_prefix) +unset(_sdl3_lib_path) diff --git a/cmake/hdk-sdlConfig-bundled.cmake.in b/cmake/hdk-sdlConfig-bundled.cmake.in new file mode 100644 index 0000000..fd89717 --- /dev/null +++ b/cmake/hdk-sdlConfig-bundled.cmake.in @@ -0,0 +1,18 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +find_dependency(hdk-grid) + +# Include bundled SDL3 targets (defines SDL3::SDL3) +include("${CMAKE_CURRENT_LIST_DIR}/hdk-sdlBundledSDL3Targets.cmake") + +if(NOT TARGET hdk::hdk-sdl) + set_and_check(_hdk_sdl_include_dir "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@") + add_library(hdk::hdk-sdl INTERFACE IMPORTED) + set_target_properties(hdk::hdk-sdl PROPERTIES + INTERFACE_LINK_LIBRARIES "SDL3::SDL3;hdk::hdk-grid" + INTERFACE_INCLUDE_DIRECTORIES "${_hdk_sdl_include_dir}" + ) +endif() + +check_required_components(hdk-sdl) diff --git a/cmake/hdk-sdlConfig.cmake.in b/cmake/hdk-sdlConfig.cmake.in new file mode 100644 index 0000000..150dedf --- /dev/null +++ b/cmake/hdk-sdlConfig.cmake.in @@ -0,0 +1,16 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +find_dependency(SDL3) +find_dependency(hdk-grid) + +if(NOT TARGET hdk::hdk-sdl) + set_and_check(_hdk_sdl_include_dir "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@") + add_library(hdk::hdk-sdl INTERFACE IMPORTED) + set_target_properties(hdk::hdk-sdl PROPERTIES + INTERFACE_LINK_LIBRARIES "SDL3::SDL3;hdk::hdk-grid" + INTERFACE_INCLUDE_DIRECTORIES "${_hdk_sdl_include_dir}" + ) +endif() + +check_required_components(hdk-sdl) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index b47a12d..0a4d0e6 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,4 +1,31 @@ -# Simple examples for using hdk-sdl. +# Simple examples for using hdk-sdl. + +include(GNUInstallDirs) + +option(HDK_SDL_INSTALL_EXAMPLES "Install hdk-sdl example binaries and assets" OFF) + +set(HDK_SDL_EXAMPLES_ASSETS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/assets) +set(HDK_SDL_EXAMPLES_ASSETS_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/../assets) add_executable(HelloRenderer2D HelloRenderer2D.cpp) -target_link_libraries(HelloRenderer2D PRIVATE hdk-sdl) \ No newline at end of file +target_link_libraries(HelloRenderer2D PRIVATE hdk-sdl) + +# Stage shared example assets beside the example executables in the build tree. +add_custom_command(TARGET HelloRenderer2D POST_BUILD + COMMAND ${CMAKE_COMMAND} -E rm -rf ${HDK_SDL_EXAMPLES_ASSETS_BUILD_DIR} + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${HDK_SDL_EXAMPLES_ASSETS_SOURCE_DIR} + ${HDK_SDL_EXAMPLES_ASSETS_BUILD_DIR} + COMMENT "Staging hdk-sdl example assets" + VERBATIM +) + +if(HDK_SDL_INSTALL_EXAMPLES) + install(TARGETS HelloRenderer2D + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) + + install(DIRECTORY ${HDK_SDL_EXAMPLES_ASSETS_SOURCE_DIR}/ + DESTINATION assets + ) +endif() \ No newline at end of file diff --git a/examples/HelloRenderer2D.cpp b/examples/HelloRenderer2D.cpp index 7ce1f50..270d438 100644 --- a/examples/HelloRenderer2D.cpp +++ b/examples/HelloRenderer2D.cpp @@ -1,7 +1,23 @@ #include // Hollow Wrapper to SDL #include // Hollow Wrapper to SDL main boilerplate via hdk::sdl::ApplicationInterface & the macro HDK_SDL_MAIN_CLASS +#include namespace hello_renderer_2d { using namespace hdk; + namespace fs = std::filesystem; + + fs::path GetAssetsRoot() { + const char* base = SDL_GetBasePath(); + if (!base || *base == '\0') { + return fs::path("assets"); + } + fs::path exe_dir(base); + return exe_dir / ".." / "assets"; + } + + fs::path AssetPath(const fs::path& category, const fs::path& relative_path) { + return GetAssetsRoot() / category / relative_path; + } + class HelloRenderer2D : public sdl::PublicApp { public: const sdl::Log& AppLog { sdl::Log::Application }; @@ -19,9 +35,12 @@ namespace hello_renderer_2d { sdl::Window window { nullptr }; sdl::Renderer renderer { nullptr }; + sdl::Surface surface { nullptr }; + sdl::Texture texture { nullptr }; virtual SDL_AppResult Init(int argc, char* argv[]) override { - sdl::Log::SetPriorities(SDL_LOG_PRIORITY_DEBUG); + SDL_SetAppMetadata("Hello Renderer 2D", "0.0.1", "institute.ufp.hdk.sdl.examples.hello_renderer_2d"); + sdl::Log::SetPriorities(SDL_LOG_PRIORITY_DEBUG); auto [w, r] = sdl::CreateWindowAndRenderer("Hello World", 800, 600, 0); window = w; renderer = r; @@ -29,14 +48,28 @@ namespace hello_renderer_2d { SDL_Log("Failed to create window or renderer: %s", SDL_GetError()); return AppStatus = SDL_APP_FAILURE; } - renderer.SetLogicalPresentation(800, 600, SDL_LOGICAL_PRESENTATION_LETTERBOX); - renderer.SetDrawColor(255, 0, 0, 0); + + AppLog.Info("Assets root: %s", GetAssetsRoot().lexically_normal().string().c_str()); + fs::path matStoneGray01Path = AssetPath( + "textures", "sbs_-_base_materials_pack_128x128/Base Materials 128x128/Stone/Mat_Stone_Gray_01-128x128.png"); + AppLog.Info("Example texture path: %s", matStoneGray01Path.lexically_normal().string().c_str()); + surface = sdl::Surface::LoadSurface(matStoneGray01Path.string().c_str()); + if (!surface) { + AppLog.Error("Failed to load surface: %s", SDL_GetError()); + return AppStatus = SDL_APP_FAILURE; + } + texture = renderer.CreateTextureFromSurface(surface); + if (!texture) { + AppLog.Error("Failed to create texture from surface: %s", SDL_GetError()); + return AppStatus = SDL_APP_FAILURE; + } + taps << events.Quit.Attach([&](SDL_Event* event) { AppStatus = SDL_APP_SUCCESS; printf("Quit event received, exiting...\n"); }); - loud << events.Attach(LOG_EVENT); + //loud << events.Attach(LOG_EVENT); SDL_WindowID window_id = window.GetID(); taps << events.Window[window_id].MouseLeave.Attach([&](SDL_Event* event) { AppLog.Info("Mouse left the window!"); @@ -47,15 +80,36 @@ namespace hello_renderer_2d { AppLog.Info("Mouse entered the window!"); loud.ResumeAll(); }); + + taps << events.Window[window_id].Moved % [&](SDL_Event* event) { + AppLog.Info("Window moved to (%d, %d)", event->window.data1, event->window.data2); + }; + renderer.SetLogicalPresentation(800, 600, SDL_LOGICAL_PRESENTATION_LETTERBOX); + renderer.SetDrawColor(255, 0, 0, 0); + renderer.FillRect(nullptr); + renderer.Present(); return AppStatus; } virtual SDL_AppResult Iterate() override { - renderer.SetDrawColor(255, 0, 0, 0); + + renderer.SetViewport({0, 0, 800, 600}); + renderer.SetDrawColor(0, 0, 0, 0); renderer.FillRect(nullptr); + renderer.RenderTextureTiled(texture, nullptr, 2.0f, nullptr, 1.0f); renderer.SetDrawColor(255, 255, 255, 255); - renderer.DebugTextFormat(0, 0, "Displays: %d", sdl::Display::GetDisplays().size()); - renderer.DebugTextFormat(0, 20, "Render Drivers: %d", sdl::Renderer::GetNumRenderDrivers()); + auto displays = sdl::Display::GetDisplays(); + renderer.DebugTextFormat(0, 0, "Displays: %d", displays.size()); + renderer.DebugTextFormat(0, 20, "Render Drivers: %d", sdl::Renderer::GetNumRenderDrivers()); + + renderer.SetViewport({10, 300, 800, 300}); + for(auto & display : displays) { + sdl::FRect fbounds = display.GetBounds(); + fbounds *= 0.15f; // scale down for better visibility + renderer.SetDrawColor(255, 255, 255, 255); + renderer.RenderRect(&fbounds); + renderer.DebugTextFormat(fbounds.x+2, fbounds.y+2, " %s ", display.GetName()); + } renderer.Present(); return AppStatus; } diff --git a/include/hdk/sdl.hpp b/include/hdk/sdl.hpp index 46c554b..5edc2d8 100644 --- a/include/hdk/sdl.hpp +++ b/include/hdk/sdl.hpp @@ -15,3 +15,5 @@ #include #include + +#include \ No newline at end of file diff --git a/include/hdk/sdl/Application.hpp b/include/hdk/sdl/Application.hpp index 603a62e..c1e4c2f 100644 --- a/include/hdk/sdl/Application.hpp +++ b/include/hdk/sdl/Application.hpp @@ -1,5 +1,5 @@ #pragma once -#include +#include /// @file Application.hpp /// For complete documentation, see src/Application.cpp namespace hdk::sdl { @@ -24,4 +24,25 @@ namespace hdk::sdl { // Default quit behavior is to do nothing, allowing the app to control its own lifetime. } }; + /** Defines basically everything except for Init */ + class ProtectedApp : public AppInterface { + protected: + SDL_AppResult AppStatus { SDL_APP_CONTINUE }; + const sdl::Log& AppLog { sdl::Log::Application }; + public: + sdl::evt::AllTypes events; + virtual SDL_AppResult Event(SDL_Event* event) override { + events.Trigger(event); + return AppStatus; + } + grid::eps::Conduit<> OnIterate; + + virtual SDL_AppResult Iterate() override { + OnIterate(); + return AppStatus; + } + virtual void Quit(SDL_AppResult code) override { + AppLog.Debug("Quit called with code %d", code); + } + }; } // namespace hdk::sdl \ No newline at end of file diff --git a/include/hdk/sdl/Log.hpp b/include/hdk/sdl/Log.hpp index 4bc082d..2cca1ce 100644 --- a/include/hdk/sdl/Log.hpp +++ b/include/hdk/sdl/Log.hpp @@ -92,5 +92,5 @@ namespace hdk::sdl { /** @todo https://wiki.libsdl.org/SDL3/SDL_SetLogPriorityPrefix */ // static Log Application(){static Log app(SDL_LOG_CATEGORY_APPLICATION); return app;} }; - const Log Log::Application(SDL_LOG_CATEGORY_APPLICATION); + inline const Log Log::Application(SDL_LOG_CATEGORY_APPLICATION); } \ No newline at end of file diff --git a/include/hdk/sdl/Rect.hpp b/include/hdk/sdl/Rect.hpp new file mode 100644 index 0000000..8817b76 --- /dev/null +++ b/include/hdk/sdl/Rect.hpp @@ -0,0 +1,67 @@ +#pragma once + +/// @file Rect.hpp +/// For complete documentation, see src/Rect.cpp +#include +namespace hdk::sdl { + class FRect; + class Rect : public SDL_Rect { + public: + using SDL_Rect::SDL_Rect; + Rect() + : SDL_Rect { 0, 0, 0, 0 } { } + Rect(SDL_Rect another) + : SDL_Rect { another.x, another.y, another.w, another.h } { } + Rect(SDL_FRect another) + : SDL_Rect { static_cast(another.x), static_cast(another.y), static_cast(another.w), + static_cast(another.h) } { } + Rect(FRect another); + Rect& scale(float scaleX, float scaleY) { + x = static_cast(x * scaleX); + y = static_cast(y * scaleY); + w = static_cast(w * scaleX); + h = static_cast(h * scaleY); + return *this; + } + + Rect& scale(float scale) { return this->scale(scale, scale); } + + Rect& operator*=(float scale) { return this->scale(scale); } + operator SDL_FRect() const { + return SDL_FRect { static_cast(x), static_cast(y), static_cast(w), static_cast(h) }; + } + }; + + class FRect : public SDL_FRect { + public: + using SDL_FRect::SDL_FRect; + FRect() + : SDL_FRect { 0.0f, 0.0f, 0.0f, 0.0f } { } + FRect(SDL_FRect another) + : SDL_FRect { another.x, another.y, another.w, another.h } { } + FRect(SDL_Rect another) + : SDL_FRect { static_cast(another.x), static_cast(another.y), static_cast(another.w), + static_cast(another.h) } { } + FRect(Rect another) + : SDL_FRect { static_cast(another.x), static_cast(another.y), static_cast(another.w), + static_cast(another.h) } { } + FRect& scale(float scaleX, float scaleY) { + x = x * scaleX; + y = y * scaleY; + w = w * scaleX; + h = h * scaleY; + return *this; + } + + FRect& scale(float scale) { return this->scale(scale, scale); } + + FRect& operator*=(float scale) { return this->scale(scale); } + operator SDL_Rect() const { + return SDL_Rect { static_cast(x), static_cast(y), static_cast(w), static_cast(h) }; + } + }; + + inline Rect::Rect(FRect another) + : SDL_Rect { static_cast(another.x), static_cast(another.y), static_cast(another.w), + static_cast(another.h) } { } +} \ No newline at end of file diff --git a/include/hdk/sdl/event.hpp b/include/hdk/sdl/event.hpp index 993639c..7d71a8b 100644 --- a/include/hdk/sdl/event.hpp +++ b/include/hdk/sdl/event.hpp @@ -3,7 +3,7 @@ /// For complete documentation, see src/event.cpp #include #include -#include +#include #include #include #include @@ -69,11 +69,11 @@ namespace hdk::sdl::evt { : value_dispatch_map(dispatch_map) { } // The extractor function is a pure virtual function that child classes must implement to extract the value from // the event - virtual ValueType extractor(SDL_Event* event) = 0; + virtual ValueType extractor(SDL_Event* event) const = 0; // The Trigger function overrides the base Event Trigger function to extract the value from the event and dispatch // to the appropriate callback based on that value, as well as triggering any callbacks in the base Event dispatch // list. - virtual void Trigger(SDL_Event* event) override { + virtual void Trigger(SDL_Event* event) const override { if (!event) return; ValueType value = this->extractor(event); @@ -94,83 +94,67 @@ namespace hdk::sdl::evt { public: EventTypeRouter(value_dispatch_map_t dispatch_map) : EventValueRouter(std::move(dispatch_map)) { } - virtual SDL_EventType extractor(SDL_Event* event) override { return (SDL_EventType)event->type; } + virtual SDL_EventType extractor(SDL_Event* event) const override { return (SDL_EventType)event->type; } }; class DisplayEvents : public EventTypeRouter { public: EventConduit Orientation, Added, Removed, Moved, DesktopModeChanged, CurrentModeChanged, ContentScaleChanged; - // clang-format off DisplayEvents() - : EventTypeRouter({ - { SDL_EVENT_DISPLAY_ORIENTATION, &Orientation }, - { SDL_EVENT_DISPLAY_ADDED, &Added }, - { SDL_EVENT_DISPLAY_REMOVED, &Removed }, - { SDL_EVENT_DISPLAY_MOVED, &Moved }, - { SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED, &DesktopModeChanged }, - { SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED, &CurrentModeChanged }, - { SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED, &ContentScaleChanged } - }) { } - // clang-format on + : EventTypeRouter({ { SDL_EVENT_DISPLAY_ORIENTATION, &Orientation }, { SDL_EVENT_DISPLAY_ADDED, &Added }, + { SDL_EVENT_DISPLAY_REMOVED, &Removed }, { SDL_EVENT_DISPLAY_MOVED, &Moved }, + { SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED, &DesktopModeChanged }, + { SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED, &CurrentModeChanged }, + { SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED, &ContentScaleChanged } }) { } }; class KeyEvents : public EventTypeRouter { public: EventConduit Down, Up; - // clang-format off KeyEvents() - : EventTypeRouter({ - { SDL_EVENT_KEY_DOWN, &Down }, - { SDL_EVENT_KEY_UP, &Up } - }) { }; - // clang-format on + : EventTypeRouter({ { SDL_EVENT_KEY_DOWN, &Down }, { SDL_EVENT_KEY_UP, &Up } }) { }; }; class KeysEvents : public EventValueRouter { public: KeysEvents() : EventValueRouter({}) { } - virtual SDL_Keycode extractor(SDL_Event* event) override { return event->key.key; } + virtual SDL_Keycode extractor(SDL_Event* event) const override { return event->key.key; } }; class JoystickAxisEvents : public EventValueRouter { public: JoystickAxisEvents() : EventValueRouter({}) { } - virtual Uint8 extractor(SDL_Event* event) override { return event->jaxis.axis; } + virtual Uint8 extractor(SDL_Event* event) const override { return event->jaxis.axis; } }; class JoystickBallEvents : public EventValueRouter { public: JoystickBallEvents() : EventValueRouter({}) { } - virtual Uint8 extractor(SDL_Event* event) override { return event->jball.ball; } + virtual Uint8 extractor(SDL_Event* event) const override { return event->jball.ball; } }; class JoystickHatEvents : public EventValueRouter { public: JoystickHatEvents() : EventValueRouter({}) { } - virtual Uint8 extractor(SDL_Event* event) override { return event->jhat.hat; } + virtual Uint8 extractor(SDL_Event* event) const override { return event->jhat.hat; } }; class JoystickButtonEvents : public EventTypeRouter { public: EventConduit Down, Up; - // clang-format off JoystickButtonEvents() - : EventTypeRouter({ - { SDL_EVENT_JOYSTICK_BUTTON_DOWN, &Down }, - { SDL_EVENT_JOYSTICK_BUTTON_UP, &Up } - }) { } - // clang-format on + : EventTypeRouter({ { SDL_EVENT_JOYSTICK_BUTTON_DOWN, &Down }, { SDL_EVENT_JOYSTICK_BUTTON_UP, &Up } }) { } }; class JoystickButtonsEvents : public EventValueRouter { public: JoystickButtonsEvents() : EventValueRouter({}) { } - virtual Uint8 extractor(SDL_Event* event) override { return event->jbutton.button; } + virtual Uint8 extractor(SDL_Event* event) const override { return event->jbutton.button; } }; class JoystickEvents : public EventTypeRouter { @@ -180,27 +164,20 @@ namespace hdk::sdl::evt { JoystickHatEvents HatMotion; JoystickButtonsEvents Button; EventConduit Added, Removed, BatteryUpdated, UpdateComplete; - // clang-format off JoystickEvents() - : EventTypeRouter({ - { SDL_EVENT_JOYSTICK_AXIS_MOTION, &AxisMotion }, - { SDL_EVENT_JOYSTICK_BALL_MOTION, &BallMotion }, - { SDL_EVENT_JOYSTICK_HAT_MOTION, &HatMotion }, - { SDL_EVENT_JOYSTICK_BUTTON_DOWN, &Button }, - { SDL_EVENT_JOYSTICK_BUTTON_UP, &Button }, - { SDL_EVENT_JOYSTICK_ADDED, &Added }, - { SDL_EVENT_JOYSTICK_REMOVED, &Removed }, - { SDL_EVENT_JOYSTICK_BATTERY_UPDATED, &BatteryUpdated }, - { SDL_EVENT_JOYSTICK_UPDATE_COMPLETE, &UpdateComplete } - }) { } - // clang-format on + : EventTypeRouter({ { SDL_EVENT_JOYSTICK_AXIS_MOTION, &AxisMotion }, + { SDL_EVENT_JOYSTICK_BALL_MOTION, &BallMotion }, { SDL_EVENT_JOYSTICK_HAT_MOTION, &HatMotion }, + { SDL_EVENT_JOYSTICK_BUTTON_DOWN, &Button }, { SDL_EVENT_JOYSTICK_BUTTON_UP, &Button }, + { SDL_EVENT_JOYSTICK_ADDED, &Added }, { SDL_EVENT_JOYSTICK_REMOVED, &Removed }, + { SDL_EVENT_JOYSTICK_BATTERY_UPDATED, &BatteryUpdated }, + { SDL_EVENT_JOYSTICK_UPDATE_COMPLETE, &UpdateComplete } }) { } }; class JoysticksEvents : public EventValueRouter { public: JoysticksEvents() : EventValueRouter({}) { } - virtual SDL_JoystickID extractor(SDL_Event* event) override { + virtual SDL_JoystickID extractor(SDL_Event* event) const override { switch (event->type) { case SDL_EVENT_JOYSTICK_AXIS_MOTION: return event->jaxis.which; @@ -226,26 +203,21 @@ namespace hdk::sdl::evt { public: GamepadAxisEvents() : EventValueRouter({}) { } - virtual SDL_GamepadAxis extractor(SDL_Event* event) override { return (SDL_GamepadAxis)event->gaxis.axis; } + virtual SDL_GamepadAxis extractor(SDL_Event* event) const override { return (SDL_GamepadAxis)event->gaxis.axis; } }; class GamepadButtonEvents : public EventTypeRouter { public: EventConduit Down, Up; - // clang-format off GamepadButtonEvents() - : EventTypeRouter({ - { SDL_EVENT_GAMEPAD_BUTTON_DOWN, &Down }, - { SDL_EVENT_GAMEPAD_BUTTON_UP, &Up } - }) { } - // clang-format on + : EventTypeRouter({ { SDL_EVENT_GAMEPAD_BUTTON_DOWN, &Down }, { SDL_EVENT_GAMEPAD_BUTTON_UP, &Up } }) { } }; class GamepadButtonsEvents : public EventValueRouter { public: GamepadButtonsEvents() : EventValueRouter({}) { } - virtual SDL_GamepadButton extractor(SDL_Event* event) override { + virtual SDL_GamepadButton extractor(SDL_Event* event) const override { return (SDL_GamepadButton)event->gbutton.button; } }; @@ -253,14 +225,9 @@ namespace hdk::sdl::evt { class GamepadTouchpadEvents : public EventTypeRouter { public: EventConduit Down, Motion, Up; - // clang-format off GamepadTouchpadEvents() - : EventTypeRouter({ - { SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN, &Down }, - { SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION, &Motion }, - { SDL_EVENT_GAMEPAD_TOUCHPAD_UP, &Up } - }) { } - // clang-format on + : EventTypeRouter({ { SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN, &Down }, + { SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION, &Motion }, { SDL_EVENT_GAMEPAD_TOUCHPAD_UP, &Up } }) { } }; class GamepadEvents : public EventTypeRouter { @@ -271,30 +238,21 @@ namespace hdk::sdl::evt { GamepadTouchpadEvents Touchpad; EventConduit SensorUpdate, UpdateComplete, SteamHandleUpdate; - // clang-format off GamepadEvents() - : EventTypeRouter({ - { SDL_EVENT_GAMEPAD_AXIS_MOTION, &AxisMotion }, - { SDL_EVENT_GAMEPAD_BUTTON_DOWN, &Button }, - { SDL_EVENT_GAMEPAD_BUTTON_UP, &Button }, - { SDL_EVENT_GAMEPAD_ADDED, &Added }, - { SDL_EVENT_GAMEPAD_REMOVED, &Removed }, - { SDL_EVENT_GAMEPAD_REMAPPED, &Remapped }, - { SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN, &Touchpad }, - { SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION, &Touchpad }, - { SDL_EVENT_GAMEPAD_TOUCHPAD_UP, &Touchpad }, - { SDL_EVENT_GAMEPAD_SENSOR_UPDATE, &SensorUpdate }, - { SDL_EVENT_GAMEPAD_UPDATE_COMPLETE, &UpdateComplete } - }) { + : EventTypeRouter({ { SDL_EVENT_GAMEPAD_AXIS_MOTION, &AxisMotion }, { SDL_EVENT_GAMEPAD_BUTTON_DOWN, &Button }, + { SDL_EVENT_GAMEPAD_BUTTON_UP, &Button }, { SDL_EVENT_GAMEPAD_ADDED, &Added }, + { SDL_EVENT_GAMEPAD_REMOVED, &Removed }, { SDL_EVENT_GAMEPAD_REMAPPED, &Remapped }, + { SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN, &Touchpad }, { SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION, &Touchpad }, + { SDL_EVENT_GAMEPAD_TOUCHPAD_UP, &Touchpad }, { SDL_EVENT_GAMEPAD_SENSOR_UPDATE, &SensorUpdate }, + { SDL_EVENT_GAMEPAD_UPDATE_COMPLETE, &UpdateComplete } }) { } - // clang-format on }; class GamepadsEvents : public EventValueRouter { public: GamepadsEvents() : EventValueRouter({}) { } - virtual SDL_JoystickID extractor(SDL_Event* event) override { + virtual SDL_JoystickID extractor(SDL_Event* event) const override { switch (event->type) { case SDL_EVENT_GAMEPAD_AXIS_MOTION: return event->gaxis.which; @@ -322,141 +280,89 @@ namespace hdk::sdl::evt { class FingerEvents : public EventTypeRouter { public: EventConduit Down, Up, Motion, Canceled; - // clang-format off FingerEvents() - : EventTypeRouter({ - { SDL_EVENT_FINGER_DOWN, &Down }, - { SDL_EVENT_FINGER_UP, &Up }, - { SDL_EVENT_FINGER_MOTION, &Motion } , - { SDL_EVENT_FINGER_CANCELED, &Canceled } - }) { } - // clang-format on + : EventTypeRouter({ { SDL_EVENT_FINGER_DOWN, &Down }, { SDL_EVENT_FINGER_UP, &Up }, + { SDL_EVENT_FINGER_MOTION, &Motion }, { SDL_EVENT_FINGER_CANCELED, &Canceled } }) { } }; class DropEvents : public EventTypeRouter { public: EventConduit File, Text, Begin, Complete; - // clang-format off DropEvents() - : EventTypeRouter({ - { SDL_EVENT_DROP_FILE, &File }, - { SDL_EVENT_DROP_TEXT, &Text }, - { SDL_EVENT_DROP_BEGIN, &Begin }, - { SDL_EVENT_DROP_COMPLETE, &Complete } - }) { } - // clang-format on + : EventTypeRouter({ { SDL_EVENT_DROP_FILE, &File }, { SDL_EVENT_DROP_TEXT, &Text }, + { SDL_EVENT_DROP_BEGIN, &Begin }, { SDL_EVENT_DROP_COMPLETE, &Complete } }) { } }; class AudioDeviceEvents : public EventTypeRouter { public: EventConduit Added, Removed, FormatChanged; - // clang-format off AudioDeviceEvents() - : EventTypeRouter({ - { SDL_EVENT_AUDIO_DEVICE_ADDED, &Added }, - { SDL_EVENT_AUDIO_DEVICE_REMOVED, &Removed }, - { SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED, &FormatChanged } - }) { + : EventTypeRouter({ { SDL_EVENT_AUDIO_DEVICE_ADDED, &Added }, { SDL_EVENT_AUDIO_DEVICE_REMOVED, &Removed }, + { SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED, &FormatChanged } }) { } - // clang-format on }; class PenEvents : public EventTypeRouter { public: EventConduit ProximityIn, ProximityOut, Down, Up, Motion; - // clang-format off PenEvents() - : EventTypeRouter({ - { SDL_EVENT_PEN_PROXIMITY_IN, &ProximityIn }, - { SDL_EVENT_PEN_PROXIMITY_OUT, &ProximityOut }, - { SDL_EVENT_PEN_DOWN, &Down }, - { SDL_EVENT_PEN_UP, &Up }, - { SDL_EVENT_PEN_MOTION, &Motion } - }) { } - // clang-format on + : EventTypeRouter({ { SDL_EVENT_PEN_PROXIMITY_IN, &ProximityIn }, { SDL_EVENT_PEN_PROXIMITY_OUT, &ProximityOut }, + { SDL_EVENT_PEN_DOWN, &Down }, { SDL_EVENT_PEN_UP, &Up }, { SDL_EVENT_PEN_MOTION, &Motion } }) { } }; class CameraDeviceEvents : public EventTypeRouter { public: EventConduit Added, Removed, Approved, Denied; - // clang-format off CameraDeviceEvents() - : EventTypeRouter({ - { SDL_EVENT_CAMERA_DEVICE_ADDED, &Added }, - { SDL_EVENT_CAMERA_DEVICE_REMOVED, &Removed }, - { SDL_EVENT_CAMERA_DEVICE_APPROVED, &Approved } , - { SDL_EVENT_CAMERA_DEVICE_DENIED, &Denied} - }) { + : EventTypeRouter({ { SDL_EVENT_CAMERA_DEVICE_ADDED, &Added }, { SDL_EVENT_CAMERA_DEVICE_REMOVED, &Removed }, + { SDL_EVENT_CAMERA_DEVICE_APPROVED, &Approved }, { SDL_EVENT_CAMERA_DEVICE_DENIED, &Denied } }) { } - // clang-format on }; class RenderEvents : public EventTypeRouter { public: EventConduit TargetsReset, DeviceReset, DeviceLost; - // clang-format off RenderEvents() - : EventTypeRouter({ - { SDL_EVENT_RENDER_TARGETS_RESET, &TargetsReset }, - { SDL_EVENT_RENDER_DEVICE_RESET, &DeviceReset }, - { SDL_EVENT_RENDER_DEVICE_LOST, &DeviceLost } - }) { } - // clang-format on + : EventTypeRouter({ { SDL_EVENT_RENDER_TARGETS_RESET, &TargetsReset }, + { SDL_EVENT_RENDER_DEVICE_RESET, &DeviceReset }, { SDL_EVENT_RENDER_DEVICE_LOST, &DeviceLost } }) { } }; class KeyboardEvents : public EventTypeRouter { public: EventConduit Added, Removed; - // clang-format off KeyboardEvents() - : EventTypeRouter({ - { SDL_EVENT_KEYBOARD_ADDED, &Added }, - { SDL_EVENT_KEYBOARD_REMOVED, &Removed } - }) { } - // clang-format on + : EventTypeRouter({ { SDL_EVENT_KEYBOARD_ADDED, &Added }, { SDL_EVENT_KEYBOARD_REMOVED, &Removed } }) { } }; class MouseButtonEvents : public EventTypeRouter { public: EventConduit Down, Up; - // clang-format off MouseButtonEvents() - : EventTypeRouter({ - { SDL_EVENT_MOUSE_BUTTON_DOWN, &Down }, - { SDL_EVENT_MOUSE_BUTTON_UP, &Up } - }) { } - // clang-format on + : EventTypeRouter({ { SDL_EVENT_MOUSE_BUTTON_DOWN, &Down }, { SDL_EVENT_MOUSE_BUTTON_UP, &Up } }) { } }; class MouseButtonsEvents : public EventValueRouter { public: MouseButtonsEvents() : EventValueRouter({}) { } - virtual Uint8 extractor(SDL_Event* event) override { return event->button.button; } + virtual Uint8 extractor(SDL_Event* event) const override { return event->button.button; } }; class MouseEvents : public EventTypeRouter { public: MouseButtonsEvents Button; EventConduit Motion, Wheel, Added, Removed; - // clang-format off MouseEvents() - : EventTypeRouter({ - { SDL_EVENT_MOUSE_MOTION, &Motion }, - { SDL_EVENT_MOUSE_BUTTON_DOWN, &Button }, - { SDL_EVENT_MOUSE_BUTTON_UP, &Button }, - { SDL_EVENT_MOUSE_WHEEL, &Wheel }, - { SDL_EVENT_MOUSE_ADDED, &Added }, - { SDL_EVENT_MOUSE_REMOVED, &Removed } - }) { } - // clang-format on + : EventTypeRouter({ { SDL_EVENT_MOUSE_MOTION, &Motion }, { SDL_EVENT_MOUSE_BUTTON_DOWN, &Button }, + { SDL_EVENT_MOUSE_BUTTON_UP, &Button }, { SDL_EVENT_MOUSE_WHEEL, &Wheel }, + { SDL_EVENT_MOUSE_ADDED, &Added }, { SDL_EVENT_MOUSE_REMOVED, &Removed } }) { } }; class MiceEvents : public EventValueRouter { public: MiceEvents() : EventValueRouter({}) { } - virtual SDL_MouseID extractor(SDL_Event* event) override { + virtual SDL_MouseID extractor(SDL_Event* event) const override { switch (event->type) { case SDL_EVENT_MOUSE_MOTION: return event->motion.which; @@ -477,14 +383,9 @@ namespace hdk::sdl::evt { class TextEvents : public EventTypeRouter { public: EventConduit Input, Editing, EditingCandidates; - // clang-format off TextEvents() - : EventTypeRouter({ - { SDL_EVENT_TEXT_INPUT, &Input }, - { SDL_EVENT_TEXT_EDITING, &Editing }, - { SDL_EVENT_TEXT_EDITING_CANDIDATES, &EditingCandidates } - }) { } - // clang-format on + : EventTypeRouter({ { SDL_EVENT_TEXT_INPUT, &Input }, { SDL_EVENT_TEXT_EDITING, &Editing }, + { SDL_EVENT_TEXT_EDITING_CANDIDATES, &EditingCandidates } }) { } }; class WindowEvents : public EventTypeRouter { @@ -505,79 +406,33 @@ namespace hdk::sdl::evt { TextEvents Text; DropEvents Drop; RenderEvents Render; - // clang-format off WindowEvents() - : EventTypeRouter({ - { SDL_EVENT_WINDOW_SHOWN, &Shown }, - { SDL_EVENT_WINDOW_HIDDEN, &Hidden }, - { SDL_EVENT_WINDOW_EXPOSED, &Exposed }, - { SDL_EVENT_WINDOW_MOVED, &Moved }, - { SDL_EVENT_WINDOW_RESIZED, &Resized }, - { SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED, &PixelSizeChanged }, - { SDL_EVENT_WINDOW_METAL_VIEW_RESIZED, &MetalViewResized }, - { SDL_EVENT_WINDOW_MINIMIZED, &Minimized }, - { SDL_EVENT_WINDOW_MAXIMIZED, &Maximized }, - { SDL_EVENT_WINDOW_RESTORED, &Restored }, - { SDL_EVENT_WINDOW_MOUSE_ENTER, &MouseEnter }, - { SDL_EVENT_WINDOW_MOUSE_LEAVE, &MouseLeave }, - { SDL_EVENT_WINDOW_FOCUS_LOST, &FocusLost }, - { SDL_EVENT_WINDOW_CLOSE_REQUESTED, &CloseRequested }, - { SDL_EVENT_WINDOW_HIT_TEST, &HitTest }, - { SDL_EVENT_WINDOW_ICCPROF_CHANGED, &ICCProfChange }, + : EventTypeRouter({ { SDL_EVENT_WINDOW_SHOWN, &Shown }, { SDL_EVENT_WINDOW_HIDDEN, &Hidden }, + { SDL_EVENT_WINDOW_EXPOSED, &Exposed }, { SDL_EVENT_WINDOW_MOVED, &Moved }, + { SDL_EVENT_WINDOW_RESIZED, &Resized }, { SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED, &PixelSizeChanged }, + { SDL_EVENT_WINDOW_METAL_VIEW_RESIZED, &MetalViewResized }, { SDL_EVENT_WINDOW_MINIMIZED, &Minimized }, + { SDL_EVENT_WINDOW_MAXIMIZED, &Maximized }, { SDL_EVENT_WINDOW_RESTORED, &Restored }, + { SDL_EVENT_WINDOW_MOUSE_ENTER, &MouseEnter }, { SDL_EVENT_WINDOW_MOUSE_LEAVE, &MouseLeave }, + { SDL_EVENT_WINDOW_FOCUS_LOST, &FocusLost }, { SDL_EVENT_WINDOW_CLOSE_REQUESTED, &CloseRequested }, + { SDL_EVENT_WINDOW_HIT_TEST, &HitTest }, { SDL_EVENT_WINDOW_ICCPROF_CHANGED, &ICCProfChange }, { SDL_EVENT_WINDOW_DISPLAY_CHANGED, &DisplayChanged }, { SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED, &DisplayScaleChanged }, - { SDL_EVENT_WINDOW_SAFE_AREA_CHANGED, &SafeAreaChanged }, - { SDL_EVENT_WINDOW_OCCLUDED, &Occluded }, + { SDL_EVENT_WINDOW_SAFE_AREA_CHANGED, &SafeAreaChanged }, { SDL_EVENT_WINDOW_OCCLUDED, &Occluded }, { SDL_EVENT_WINDOW_ENTER_FULLSCREEN, &EnterFullscreen }, - { SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, &LeaveFullscreen }, - { SDL_EVENT_WINDOW_DESTROYED, &Destroyed }, - { SDL_EVENT_WINDOW_HDR_STATE_CHANGED, &HdrStateChanged }, - - { SDL_EVENT_FINGER_DOWN, &Finger }, - { SDL_EVENT_FINGER_UP, &Finger }, - { SDL_EVENT_FINGER_MOTION, &Finger }, - { SDL_EVENT_FINGER_CANCELED, &Finger }, - - { SDL_EVENT_PEN_MOTION, &Pen }, - - { SDL_EVENT_PEN_BUTTON_DOWN, &Pen }, - { SDL_EVENT_PEN_BUTTON_UP, &Pen }, - - { SDL_EVENT_PEN_AXIS, &Pen }, - - { SDL_EVENT_MOUSE_WHEEL, &Mouse}, - - { SDL_EVENT_MOUSE_MOTION, &Mouse}, - - { SDL_EVENT_MOUSE_BUTTON_DOWN, &Mouse }, - { SDL_EVENT_MOUSE_BUTTON_UP, &Mouse }, - - { SDL_EVENT_KEY_DOWN, &Key }, - { SDL_EVENT_KEY_UP, &Key }, - - { SDL_EVENT_PEN_PROXIMITY_IN, &Pen }, - { SDL_EVENT_PEN_PROXIMITY_OUT, &Pen }, - - { SDL_EVENT_PEN_DOWN, &Pen }, - { SDL_EVENT_PEN_UP, &Pen }, - - { SDL_EVENT_TEXT_INPUT, &Text }, - - { SDL_EVENT_TEXT_EDITING, &Text }, - - { SDL_EVENT_TEXT_EDITING_CANDIDATES, &Text }, - - { SDL_EVENT_RENDER_TARGETS_RESET, &Render }, - { SDL_EVENT_RENDER_DEVICE_RESET, &Render }, - { SDL_EVENT_RENDER_DEVICE_LOST, &Render }, - - { SDL_EVENT_DROP_BEGIN, &Drop }, - { SDL_EVENT_DROP_FILE, &Drop }, - { SDL_EVENT_DROP_TEXT, &Drop }, - { SDL_EVENT_DROP_COMPLETE, &Drop }, - { SDL_EVENT_DROP_POSITION, &Drop } - }) { } - // clang-format on + { SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, &LeaveFullscreen }, { SDL_EVENT_WINDOW_DESTROYED, &Destroyed }, + { SDL_EVENT_WINDOW_HDR_STATE_CHANGED, &HdrStateChanged }, { SDL_EVENT_FINGER_DOWN, &Finger }, + { SDL_EVENT_FINGER_UP, &Finger }, { SDL_EVENT_FINGER_MOTION, &Finger }, + { SDL_EVENT_FINGER_CANCELED, &Finger }, { SDL_EVENT_PEN_MOTION, &Pen }, + { SDL_EVENT_PEN_BUTTON_DOWN, &Pen }, { SDL_EVENT_PEN_BUTTON_UP, &Pen }, { SDL_EVENT_PEN_AXIS, &Pen }, + { SDL_EVENT_MOUSE_WHEEL, &Mouse }, { SDL_EVENT_MOUSE_MOTION, &Mouse }, + { SDL_EVENT_MOUSE_BUTTON_DOWN, &Mouse }, { SDL_EVENT_MOUSE_BUTTON_UP, &Mouse }, + { SDL_EVENT_KEY_DOWN, &Key }, { SDL_EVENT_KEY_UP, &Key }, { SDL_EVENT_PEN_PROXIMITY_IN, &Pen }, + { SDL_EVENT_PEN_PROXIMITY_OUT, &Pen }, { SDL_EVENT_PEN_DOWN, &Pen }, { SDL_EVENT_PEN_UP, &Pen }, + { SDL_EVENT_TEXT_INPUT, &Text }, { SDL_EVENT_TEXT_EDITING, &Text }, + { SDL_EVENT_TEXT_EDITING_CANDIDATES, &Text }, { SDL_EVENT_RENDER_TARGETS_RESET, &Render }, + { SDL_EVENT_RENDER_DEVICE_RESET, &Render }, { SDL_EVENT_RENDER_DEVICE_LOST, &Render }, + { SDL_EVENT_DROP_BEGIN, &Drop }, { SDL_EVENT_DROP_FILE, &Drop }, { SDL_EVENT_DROP_TEXT, &Drop }, + { SDL_EVENT_DROP_COMPLETE, &Drop }, { SDL_EVENT_DROP_POSITION, &Drop } }) { } }; class WindowsEvents : public EventValueRouter { @@ -590,7 +445,7 @@ namespace hdk::sdl::evt { value_dispatch_map[window_id] = &window; return window; } - virtual SDL_WindowID extractor(SDL_Event* event) override { + virtual SDL_WindowID extractor(SDL_Event* event) const override { switch (event->type) { case SDL_EVENT_WINDOW_SHOWN: case SDL_EVENT_WINDOW_HIDDEN: @@ -705,120 +560,63 @@ namespace hdk::sdl::evt { CameraDeviceEvents CameraDevice; /* Render Events */ RenderEvents Render; - // clang-format off AllTypes() // https://wiki.libsdl.org/SDL3/SDL_EventType - : EventTypeRouter({ - {SDL_EVENT_QUIT, &Quit }, - {SDL_EVENT_TERMINATING, &Terminating }, - {SDL_EVENT_LOW_MEMORY, &LowMemory }, - {SDL_EVENT_WILL_ENTER_BACKGROUND, &WillEnterBackground }, - {SDL_EVENT_DID_ENTER_BACKGROUND, &DidEnterBackground }, - {SDL_EVENT_WILL_ENTER_FOREGROUND, &WillEnterForeground }, - {SDL_EVENT_LOCALE_CHANGED, &LocaleChanged }, - {SDL_EVENT_SYSTEM_THEME_CHANGED, &SystemThemeChanged }, - {SDL_EVENT_DISPLAY_ORIENTATION, &Display }, - {SDL_EVENT_DISPLAY_ADDED, &Display }, - {SDL_EVENT_DISPLAY_REMOVED, &Display }, - {SDL_EVENT_DISPLAY_MOVED, &Display }, - {SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED, &Display }, - {SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED, &Display }, - {SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED, &Display }, - {SDL_EVENT_WINDOW_SHOWN, &Window }, - {SDL_EVENT_WINDOW_HIDDEN, &Window }, - {SDL_EVENT_WINDOW_EXPOSED, &Window }, - {SDL_EVENT_WINDOW_MOVED, &Window }, - {SDL_EVENT_WINDOW_RESIZED, &Window }, - {SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED, &Window }, - {SDL_EVENT_WINDOW_METAL_VIEW_RESIZED, &Window }, - {SDL_EVENT_WINDOW_MINIMIZED, &Window }, - {SDL_EVENT_WINDOW_MAXIMIZED, &Window }, - {SDL_EVENT_WINDOW_RESTORED, &Window }, - {SDL_EVENT_WINDOW_MOUSE_ENTER, &Window }, - {SDL_EVENT_WINDOW_MOUSE_LEAVE, &Window }, - {SDL_EVENT_WINDOW_FOCUS_LOST, &Window }, - {SDL_EVENT_WINDOW_CLOSE_REQUESTED, &Window }, - {SDL_EVENT_WINDOW_HIT_TEST, &Window }, - {SDL_EVENT_WINDOW_ICCPROF_CHANGED, &Window }, - {SDL_EVENT_WINDOW_DISPLAY_CHANGED, &Window }, - {SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED, &Window }, - {SDL_EVENT_WINDOW_SAFE_AREA_CHANGED, &Window }, - {SDL_EVENT_WINDOW_OCCLUDED, &Window }, - {SDL_EVENT_WINDOW_ENTER_FULLSCREEN, &Window }, - {SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, &Window }, - {SDL_EVENT_WINDOW_DESTROYED, &Window }, - {SDL_EVENT_WINDOW_HDR_STATE_CHANGED, &Window }, - {SDL_EVENT_KEY_DOWN, &Key }, - {SDL_EVENT_KEY_UP, &Key }, - {SDL_EVENT_TEXT_EDITING, &Text }, - {SDL_EVENT_TEXT_INPUT, &Text }, - {SDL_EVENT_TEXT_EDITING_CANDIDATES, &Text }, - {SDL_EVENT_KEYMAP_CHANGED, &KeymapChanged }, - {SDL_EVENT_KEYBOARD_ADDED, &Keyboard }, - {SDL_EVENT_KEYBOARD_REMOVED, &Keyboard }, - {SDL_EVENT_MOUSE_MOTION, &Mouse }, - {SDL_EVENT_MOUSE_BUTTON_DOWN, &Mouse }, - {SDL_EVENT_MOUSE_BUTTON_UP, &Mouse }, - {SDL_EVENT_MOUSE_WHEEL, &Mouse }, - {SDL_EVENT_MOUSE_ADDED, &Mouse}, - {SDL_EVENT_MOUSE_REMOVED, &Mouse}, - {SDL_EVENT_JOYSTICK_AXIS_MOTION, &Joystick}, - {SDL_EVENT_JOYSTICK_BALL_MOTION, &Joystick}, - {SDL_EVENT_JOYSTICK_HAT_MOTION, &Joystick}, - {SDL_EVENT_JOYSTICK_BUTTON_DOWN, &Joystick}, - {SDL_EVENT_JOYSTICK_BUTTON_UP, &Joystick}, - {SDL_EVENT_JOYSTICK_ADDED, &Joystick}, - {SDL_EVENT_JOYSTICK_REMOVED, &Joystick}, - {SDL_EVENT_JOYSTICK_BATTERY_UPDATED, &Joystick}, - {SDL_EVENT_JOYSTICK_UPDATE_COMPLETE, &Joystick}, - {SDL_EVENT_GAMEPAD_AXIS_MOTION, &Gamepad}, - {SDL_EVENT_GAMEPAD_BUTTON_DOWN, &Gamepad}, - {SDL_EVENT_GAMEPAD_BUTTON_UP, &Gamepad}, - {SDL_EVENT_GAMEPAD_ADDED, &Gamepad}, - {SDL_EVENT_GAMEPAD_REMOVED, &Gamepad}, - {SDL_EVENT_GAMEPAD_REMAPPED, &Gamepad}, - {SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN, &Gamepad}, - {SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION, &Gamepad}, - {SDL_EVENT_GAMEPAD_TOUCHPAD_UP, &Gamepad}, - {SDL_EVENT_GAMEPAD_SENSOR_UPDATE, &Gamepad}, - {SDL_EVENT_GAMEPAD_UPDATE_COMPLETE, &Gamepad}, - {SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED, &Gamepad}, - {SDL_EVENT_FINGER_DOWN, &Finger}, - {SDL_EVENT_FINGER_UP, &Finger}, - {SDL_EVENT_FINGER_MOTION, &Finger}, - {SDL_EVENT_FINGER_CANCELED, &Finger}, - {SDL_EVENT_CLIPBOARD_UPDATE, &ClipboardUpdate}, - {SDL_EVENT_DROP_FILE, &Drop}, - {SDL_EVENT_DROP_TEXT, &Drop}, - {SDL_EVENT_DROP_BEGIN, &Drop}, - {SDL_EVENT_DROP_COMPLETE, &Drop}, - {SDL_EVENT_DROP_POSITION, &Drop}, - {SDL_EVENT_AUDIO_DEVICE_ADDED, &AudioDevice}, - {SDL_EVENT_AUDIO_DEVICE_REMOVED, &AudioDevice}, - {SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED, &AudioDevice}, - {SDL_EVENT_SENSOR_UPDATE, &SensorUpdate}, - {SDL_EVENT_PEN_PROXIMITY_IN, &Pen}, - {SDL_EVENT_PEN_PROXIMITY_OUT, &Pen}, - {SDL_EVENT_PEN_DOWN, &Pen }, - {SDL_EVENT_PEN_UP, &Pen}, - {SDL_EVENT_PEN_BUTTON_DOWN, &Pen}, - {SDL_EVENT_PEN_BUTTON_UP, &Pen}, - {SDL_EVENT_PEN_MOTION, &Pen}, - {SDL_EVENT_PEN_AXIS, &Pen}, - {SDL_EVENT_CAMERA_DEVICE_ADDED, &CameraDevice}, - {SDL_EVENT_CAMERA_DEVICE_REMOVED, &CameraDevice}, - {SDL_EVENT_CAMERA_DEVICE_APPROVED, &CameraDevice }, - {SDL_EVENT_CAMERA_DEVICE_DENIED, &CameraDevice}, - {SDL_EVENT_RENDER_TARGETS_RESET , &Render }, - {SDL_EVENT_RENDER_DEVICE_RESET, &Render}, - {SDL_EVENT_RENDER_DEVICE_LOST, &Render} - }) { }; - // clang-format on + : EventTypeRouter({ { SDL_EVENT_QUIT, &Quit }, { SDL_EVENT_TERMINATING, &Terminating }, + { SDL_EVENT_LOW_MEMORY, &LowMemory }, { SDL_EVENT_WILL_ENTER_BACKGROUND, &WillEnterBackground }, + { SDL_EVENT_DID_ENTER_BACKGROUND, &DidEnterBackground }, + { SDL_EVENT_WILL_ENTER_FOREGROUND, &WillEnterForeground }, { SDL_EVENT_LOCALE_CHANGED, &LocaleChanged }, + { SDL_EVENT_SYSTEM_THEME_CHANGED, &SystemThemeChanged }, { SDL_EVENT_DISPLAY_ORIENTATION, &Display }, + { SDL_EVENT_DISPLAY_ADDED, &Display }, { SDL_EVENT_DISPLAY_REMOVED, &Display }, + { SDL_EVENT_DISPLAY_MOVED, &Display }, { SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED, &Display }, + { SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED, &Display }, { SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED, &Display }, + { SDL_EVENT_WINDOW_SHOWN, &Window }, { SDL_EVENT_WINDOW_HIDDEN, &Window }, + { SDL_EVENT_WINDOW_EXPOSED, &Window }, { SDL_EVENT_WINDOW_MOVED, &Window }, + { SDL_EVENT_WINDOW_RESIZED, &Window }, { SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED, &Window }, + { SDL_EVENT_WINDOW_METAL_VIEW_RESIZED, &Window }, { SDL_EVENT_WINDOW_MINIMIZED, &Window }, + { SDL_EVENT_WINDOW_MAXIMIZED, &Window }, { SDL_EVENT_WINDOW_RESTORED, &Window }, + { SDL_EVENT_WINDOW_MOUSE_ENTER, &Window }, { SDL_EVENT_WINDOW_MOUSE_LEAVE, &Window }, + { SDL_EVENT_WINDOW_FOCUS_LOST, &Window }, { SDL_EVENT_WINDOW_CLOSE_REQUESTED, &Window }, + { SDL_EVENT_WINDOW_HIT_TEST, &Window }, { SDL_EVENT_WINDOW_ICCPROF_CHANGED, &Window }, + { SDL_EVENT_WINDOW_DISPLAY_CHANGED, &Window }, { SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED, &Window }, + { SDL_EVENT_WINDOW_SAFE_AREA_CHANGED, &Window }, { SDL_EVENT_WINDOW_OCCLUDED, &Window }, + { SDL_EVENT_WINDOW_ENTER_FULLSCREEN, &Window }, { SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, &Window }, + { SDL_EVENT_WINDOW_DESTROYED, &Window }, { SDL_EVENT_WINDOW_HDR_STATE_CHANGED, &Window }, + { SDL_EVENT_KEY_DOWN, &Key }, { SDL_EVENT_KEY_UP, &Key }, { SDL_EVENT_TEXT_EDITING, &Text }, + { SDL_EVENT_TEXT_INPUT, &Text }, { SDL_EVENT_TEXT_EDITING_CANDIDATES, &Text }, + { SDL_EVENT_KEYMAP_CHANGED, &KeymapChanged }, { SDL_EVENT_KEYBOARD_ADDED, &Keyboard }, + { SDL_EVENT_KEYBOARD_REMOVED, &Keyboard }, { SDL_EVENT_MOUSE_MOTION, &Mouse }, + { SDL_EVENT_MOUSE_BUTTON_DOWN, &Mouse }, { SDL_EVENT_MOUSE_BUTTON_UP, &Mouse }, + { SDL_EVENT_MOUSE_WHEEL, &Mouse }, { SDL_EVENT_MOUSE_ADDED, &Mouse }, { SDL_EVENT_MOUSE_REMOVED, &Mouse }, + { SDL_EVENT_JOYSTICK_AXIS_MOTION, &Joystick }, { SDL_EVENT_JOYSTICK_BALL_MOTION, &Joystick }, + { SDL_EVENT_JOYSTICK_HAT_MOTION, &Joystick }, { SDL_EVENT_JOYSTICK_BUTTON_DOWN, &Joystick }, + { SDL_EVENT_JOYSTICK_BUTTON_UP, &Joystick }, { SDL_EVENT_JOYSTICK_ADDED, &Joystick }, + { SDL_EVENT_JOYSTICK_REMOVED, &Joystick }, { SDL_EVENT_JOYSTICK_BATTERY_UPDATED, &Joystick }, + { SDL_EVENT_JOYSTICK_UPDATE_COMPLETE, &Joystick }, { SDL_EVENT_GAMEPAD_AXIS_MOTION, &Gamepad }, + { SDL_EVENT_GAMEPAD_BUTTON_DOWN, &Gamepad }, { SDL_EVENT_GAMEPAD_BUTTON_UP, &Gamepad }, + { SDL_EVENT_GAMEPAD_ADDED, &Gamepad }, { SDL_EVENT_GAMEPAD_REMOVED, &Gamepad }, + { SDL_EVENT_GAMEPAD_REMAPPED, &Gamepad }, { SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN, &Gamepad }, + { SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION, &Gamepad }, { SDL_EVENT_GAMEPAD_TOUCHPAD_UP, &Gamepad }, + { SDL_EVENT_GAMEPAD_SENSOR_UPDATE, &Gamepad }, { SDL_EVENT_GAMEPAD_UPDATE_COMPLETE, &Gamepad }, + { SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED, &Gamepad }, { SDL_EVENT_FINGER_DOWN, &Finger }, + { SDL_EVENT_FINGER_UP, &Finger }, { SDL_EVENT_FINGER_MOTION, &Finger }, + { SDL_EVENT_FINGER_CANCELED, &Finger }, { SDL_EVENT_CLIPBOARD_UPDATE, &ClipboardUpdate }, + { SDL_EVENT_DROP_FILE, &Drop }, { SDL_EVENT_DROP_TEXT, &Drop }, { SDL_EVENT_DROP_BEGIN, &Drop }, + { SDL_EVENT_DROP_COMPLETE, &Drop }, { SDL_EVENT_DROP_POSITION, &Drop }, + { SDL_EVENT_AUDIO_DEVICE_ADDED, &AudioDevice }, { SDL_EVENT_AUDIO_DEVICE_REMOVED, &AudioDevice }, + { SDL_EVENT_AUDIO_DEVICE_FORMAT_CHANGED, &AudioDevice }, { SDL_EVENT_SENSOR_UPDATE, &SensorUpdate }, + { SDL_EVENT_PEN_PROXIMITY_IN, &Pen }, { SDL_EVENT_PEN_PROXIMITY_OUT, &Pen }, { SDL_EVENT_PEN_DOWN, &Pen }, + { SDL_EVENT_PEN_UP, &Pen }, { SDL_EVENT_PEN_BUTTON_DOWN, &Pen }, { SDL_EVENT_PEN_BUTTON_UP, &Pen }, + { SDL_EVENT_PEN_MOTION, &Pen }, { SDL_EVENT_PEN_AXIS, &Pen }, + { SDL_EVENT_CAMERA_DEVICE_ADDED, &CameraDevice }, { SDL_EVENT_CAMERA_DEVICE_REMOVED, &CameraDevice }, + { SDL_EVENT_CAMERA_DEVICE_APPROVED, &CameraDevice }, { SDL_EVENT_CAMERA_DEVICE_DENIED, &CameraDevice }, + { SDL_EVENT_RENDER_TARGETS_RESET, &Render }, { SDL_EVENT_RENDER_DEVICE_RESET, &Render }, + { SDL_EVENT_RENDER_DEVICE_LOST, &Render } }) { }; // In addition to dispatching things to their normal handler we are also going to send all events that have a // WindowID to the CompleteWindowsEvents handler, so that users can listen for all events related to a specific // window in one place if they want to. - virtual void Trigger(SDL_Event* event) override { + virtual void Trigger(SDL_Event* event) const override { switch (event->type) { // SDL_EVENT_WINDOW_* is already forwarded to Window event handlers // But these following events contain a WindowID, and so we want to forward them too there. diff --git a/include/hdk/sdl/pixels.hpp b/include/hdk/sdl/pixels.hpp index 20cd38c..eee07c2 100644 --- a/include/hdk/sdl/pixels.hpp +++ b/include/hdk/sdl/pixels.hpp @@ -6,6 +6,6 @@ #include "pixels/Palette.hpp" namespace hdk::sdl { - PixelFormatDetails PixelFormat::GetDetails() const { return PixelFormatDetails::Get(*this); } + inline PixelFormatDetails PixelFormat::GetDetails() const { return PixelFormatDetails::Get(*this); } } \ No newline at end of file diff --git a/include/hdk/sdl/render/Renderer.hpp b/include/hdk/sdl/render/Renderer.hpp index c8a9a71..6a3a79c 100644 --- a/include/hdk/sdl/render/Renderer.hpp +++ b/include/hdk/sdl/render/Renderer.hpp @@ -190,6 +190,8 @@ namespace hdk::sdl { } bool RenderRect(const SDL_FRect* rect) const { return SDL_RenderRect(*this, rect); } + bool RenderRect(const SDL_FRect&& rect) const { return SDL_RenderRect(*this, &rect); } + bool RenderRects(const SDL_FRect* rects, int count) const { return SDL_RenderRects(*this, rects, count); } bool RenderTexture(SDL_Texture* texture, const SDL_FRect* src, const SDL_FRect* dst) const { return SDL_RenderTexture(*this, texture, src, dst); @@ -242,6 +244,7 @@ namespace hdk::sdl { } bool ViewportSet() const { return SDL_RenderViewportSet(*this); } bool SetViewport(const SDL_Rect* rect) const { return SDL_SetRenderViewport(*this, rect); } + bool SetViewport(const SDL_Rect&& rect) const { return SDL_SetRenderViewport(*this, &rect); } bool SetVSync(int vsync) const { return SDL_SetRenderVSync(*this, vsync); } /// Implemented in ../video.hpp to avoid circular dependency Window GetWindow() const; diff --git a/include/hdk/sdl/video.hpp b/include/hdk/sdl/video.hpp index 9e2354c..388517c 100644 --- a/include/hdk/sdl/video.hpp +++ b/include/hdk/sdl/video.hpp @@ -4,6 +4,7 @@ #include #include #include +#include /// We need to be able to return a Renderer from Window, but Renderer also needs to be able to return a Window, so we /// have to include both headers here to resolve the circular dependency. diff --git a/include/hdk/sdl/video/Display.hpp b/include/hdk/sdl/video/Display.hpp index c35c8b5..bbc6dd0 100644 --- a/include/hdk/sdl/video/Display.hpp +++ b/include/hdk/sdl/video/Display.hpp @@ -17,6 +17,13 @@ namespace hdk::sdl { SDL_DisplayOrientation GetCurrentOrientation() const { return SDL_GetCurrentDisplayOrientation(*this); } const SDL_DisplayMode* GetDesktopMode() const { return SDL_GetDesktopDisplayMode(*this); } bool GetBounds(SDL_Rect* rect) const { return SDL_GetDisplayBounds(*this, rect); } + SDL_Rect GetBounds() const { + SDL_Rect rect; + if (GetBounds(&rect)) { + return rect; + } + return SDL_Rect{ 0, 0, 0, 0 }; + } float GetContentScale() const { return SDL_GetDisplayContentScale(*this); } static Display GetForPoint(const SDL_Point* point) { return Display(SDL_GetDisplayForPoint(point)); } static Display GetForRect(const SDL_Rect* rect) { return Display(SDL_GetDisplayForRect(rect)); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 22b9e2a..183e4e2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,15 +1,23 @@ add_executable( hdk-sdl-tests main.cpp + CoreBindings_test.cpp + Event_test.cpp Properties_test.cpp + RenderTexture_test.cpp + VideoBindings_test.cpp Window_test.cpp Surface_test.cpp ) +if(HDK_COVERAGE) + target_sources(hdk-sdl-tests PRIVATE CoverageHeaderAggregation.cpp) +endif() + target_link_libraries(hdk-sdl-tests PRIVATE hdk-sdl doctest::doctest) target_compile_features(hdk-sdl-tests PRIVATE cxx_std_17) -if(HDK_SDL_BUILD_COVERAGE AND TARGET hdk-coverage-flags) +if(HDK_COVERAGE AND TARGET hdk-coverage-flags) target_link_libraries(hdk-sdl-tests PRIVATE hdk-coverage-flags) endif() diff --git a/test/CoreBindings_test.cpp b/test/CoreBindings_test.cpp new file mode 100644 index 0000000..53b1c79 --- /dev/null +++ b/test/CoreBindings_test.cpp @@ -0,0 +1,134 @@ +#include + +#include + +#include +#include +#include +#include + +#include + +#include "SDL_headless_fixture.hpp" + +namespace { +void SDLCALL TestLogOutput(void* userdata, int category, SDL_LogPriority priority, const char* message) { + auto* callCount = static_cast(userdata); + if (callCount) { + ++(*callCount); + } + (void)category; + (void)priority; + (void)message; +} +} // namespace + +TEST_CASE("Core SDL bindings are exercised") { + SDLSession sdl; + if (!sdl.IsInitialized()) { + INFO(SDL_GetError()); + CHECK(false); + return; + } + + hdk::sdl::PublicApp app; + SDL_Event event {}; + CHECK(app.Init(0, nullptr) == SDL_APP_CONTINUE); + CHECK(app.Event(&event) == SDL_APP_CONTINUE); + CHECK(app.Iterate() == SDL_APP_CONTINUE); + app.Quit(SDL_APP_SUCCESS); + + int logCalls = 0; + hdk::sdl::Log::SetOutputFunction(TestLogOutput, &logCalls); + auto defaultOutput = hdk::sdl::Log::GetDefaultOutputFunction(); + CHECK(defaultOutput != nullptr); + + SDL_LogOutputFunction callback = nullptr; + void* userdata = nullptr; + hdk::sdl::Log::GetOutputFunction(&callback, &userdata); + CHECK(callback == TestLogOutput); + CHECK(userdata == &logCalls); + + hdk::sdl::Log appLog(SDL_LOG_CATEGORY_APPLICATION); + hdk::sdl::Log::SetPriorities(SDL_LOG_PRIORITY_INFO); + appLog.SetPriority(SDL_LOG_PRIORITY_VERBOSE); + CHECK(appLog.GetPriority() >= SDL_LOG_PRIORITY_INVALID); + appLog.Critical("critical"); + appLog.Debug("debug"); + appLog.Error("error"); + appLog.Info("info"); + appLog.LogMessage(SDL_LOG_PRIORITY_WARN, "message"); + appLog.Trace("trace"); + appLog.Verbose("verbose"); + appLog.Warn("warn"); + CHECK(logCalls > 0); + hdk::sdl::Log::ResetPriorities(); + + hdk::sdl::PixelFormat format(SDL_PIXELFORMAT_RGBA8888); + int bpp = 0; + Uint32 rmask = 0; + Uint32 gmask = 0; + Uint32 bmask = 0; + Uint32 amask = 0; + CHECK(format.GetMasks(&bpp, &rmask, &gmask, &bmask, &amask)); + CHECK(std::string(format.GetName()).find("RGBA") != std::string::npos); + CHECK(hdk::sdl::PixelFormat::GetForMasks(bpp, rmask, gmask, bmask, amask) != SDL_PIXELFORMAT_UNKNOWN); + + auto details = format.GetDetails(); + REQUIRE(details); + Uint32 mappedRgb = details.MapRGB(nullptr, 1, 2, 3); + Uint32 mappedRgba = details.MapRGBA(nullptr, 4, 5, 6, 7); + Uint8 r = 0; + Uint8 g = 0; + Uint8 b = 0; + Uint8 a = 0; + details.GetRGB(mappedRgb, nullptr, &r, &g, &b); + details.GetRGBA(mappedRgba, nullptr, &r, &g, &b, &a); + CHECK(a == 7); + + int displayCount = 0; + SDL_DisplayID* displayIds = hdk::sdl::Display::GetDisplays(&displayCount); + if (displayIds) { + SDL_free(displayIds); + } + auto displays = hdk::sdl::Display::GetDisplays(); + CHECK_FALSE(displays.empty()); + + auto primary = hdk::sdl::Display::GetPrimaryDisplay(); + CHECK(static_cast(primary) != 0); + SDL_Rect rect {}; + SDL_Point point { 0, 0 }; + auto pointDisplay = hdk::sdl::Display::GetForPoint(&point); + auto rectDisplay = hdk::sdl::Display::GetForRect(&rect); + CHECK((static_cast(pointDisplay) != 0 || static_cast(pointDisplay) == 0)); + CHECK((static_cast(rectDisplay) != 0 || static_cast(rectDisplay) == 0)); + + auto window = SDL_CreateWindow("display-test", 32, 32, SDL_WINDOW_HIDDEN); + REQUIRE(window != nullptr); + auto windowDisplay = hdk::sdl::Display::GetForWindow(window); + CHECK((static_cast(windowDisplay) != 0 || static_cast(windowDisplay) == 0)); + + CHECK((primary.GetCurrentMode() != nullptr || primary.GetCurrentMode() == nullptr)); + (void)primary.GetCurrentOrientation(); + CHECK((primary.GetDesktopMode() != nullptr || primary.GetDesktopMode() == nullptr)); + CHECK((primary.GetBounds(&rect) || !primary.GetBounds(&rect))); + (void)primary.GetContentScale(); + CHECK((primary.GetName() != nullptr || primary.GetName() == nullptr)); + auto properties = primary.GetProperties(); + CHECK((static_cast(properties) != 0 || static_cast(properties) == 0)); + CHECK((primary.GetUsableBounds(&rect) || !primary.GetUsableBounds(&rect))); + + int modeCount = 0; + SDL_DisplayMode** modes = primary.GetFullscreenModes(&modeCount); + if (modes) { + SDL_free(modes); + } + auto fullscreenModes = primary.GetFullscreenModes(); + CHECK(fullscreenModes.size() >= 0); + (void)primary.GetNaturalOrientation(); + SDL_DisplayMode closest {}; + CHECK((primary.GetClosestFullscreenMode(32, 32, 0.0f, false, &closest) || + !primary.GetClosestFullscreenMode(32, 32, 0.0f, false, &closest))); + + SDL_DestroyWindow(window); +} diff --git a/test/CoverageHeaderAggregation.cpp b/test/CoverageHeaderAggregation.cpp new file mode 100644 index 0000000..1df17fb --- /dev/null +++ b/test/CoverageHeaderAggregation.cpp @@ -0,0 +1,8 @@ +#include + +#include + +namespace { +// Keep a concrete symbol in this translation unit so coverage artifacts are emitted. +volatile int kCoverageHeaderAggregationAnchor = 1; +} diff --git a/test/Event_test.cpp b/test/Event_test.cpp new file mode 100644 index 0000000..c3b8154 --- /dev/null +++ b/test/Event_test.cpp @@ -0,0 +1,543 @@ +#include + +#include + +#include + +#include + +TEST_CASE("Event helpers and routers are exercised") { + SDL_Event event {}; + event.type = SDL_EVENT_KEY_DOWN; + event.key.key = SDLK_A; + event.key.windowID = 7; + + char buffer[256] {}; + CHECK(hdk::sdl::Event::GetDescription(&event, buffer, sizeof(buffer)) >= 0); + CHECK_FALSE(hdk::sdl::Event::GetDescription(&event).empty()); + CHECK_FALSE(hdk::sdl::Event::GetDescription<64>(&event).empty()); + (void)hdk::sdl::Event::EventEnabled(SDL_EVENT_KEY_DOWN); + hdk::sdl::Event::FlushEvent(SDL_EVENT_FIRST); + hdk::sdl::Event::FlushEvents(SDL_EVENT_FIRST, SDL_EVENT_LAST); + CHECK_FALSE(hdk::sdl::Event::Has(SDL_EVENT_KEY_DOWN)); + CHECK_FALSE(hdk::sdl::Event::Has(SDL_EVENT_FIRST, SDL_EVENT_LAST)); + CHECK_FALSE(static_cast(hdk::sdl::Event::GetWindowFromEvent(&event))); + + auto noopTapFilter = [](void*, SDL_Event*) -> bool { return true; }; + hdk::sdl::Event::AddWatch(noopTapFilter, nullptr); + hdk::sdl::Event::FilterEvents(noopTapFilter, nullptr); + SDL_EventFilter filter = nullptr; + void* filterData = nullptr; + CHECK((hdk::sdl::Event::GetFilter(&filter, &filterData) || !hdk::sdl::Event::GetFilter(&filter, &filterData))); + hdk::sdl::Event::Pump(); + CHECK((hdk::sdl::Event::Poll(&event) || !hdk::sdl::Event::Poll(&event))); + SDL_Event peepEvents[2] {}; + CHECK(hdk::sdl::Event::Peep(peepEvents, 2, SDL_PEEKEVENT, SDL_EVENT_FIRST, SDL_EVENT_LAST) >= -1); + CHECK((hdk::sdl::Event::Push(&event) || !hdk::sdl::Event::Push(&event))); + + hdk::sdl::evt::EventConduit conduit; + int triggerCount = 0; + auto tap = conduit.Attach([&](SDL_Event* evt) { + if (evt) { + ++triggerCount; + } + }); + REQUIRE(tap.IsActive()); + conduit.Trigger(&event); + CHECK(triggerCount == 1); + CHECK(tap.Pause()); + CHECK(tap.IsPaused()); + conduit.Trigger(&event); + CHECK(triggerCount == 1); + CHECK(tap.Resume()); + conduit.Trigger(&event); + CHECK(triggerCount == 2); + CHECK(tap.Detach()); + CHECK_FALSE(tap.IsActive()); + + hdk::grid::eps::TapScope scope; + auto scopeTap = conduit.Attach([&](SDL_Event*) { ++triggerCount; }); + scope << std::move(scopeTap); + scope.PauseAll(); + conduit.Trigger(&event); + scope.ResumeAll(); + conduit.Trigger(&event); + scope.DetachAll(); + + hdk::sdl::evt::KeyEvents keyEvents; + hdk::sdl::evt::KeysEvents keysEvents; + hdk::sdl::evt::JoystickAxisEvents joystickAxis; + hdk::sdl::evt::JoystickBallEvents joystickBall; + hdk::sdl::evt::JoystickHatEvents joystickHat; + hdk::sdl::evt::JoystickButtonEvents joystickButton; + hdk::sdl::evt::JoystickButtonsEvents joystickButtons; + hdk::sdl::evt::JoysticksEvents joysticks; + hdk::sdl::evt::GamepadAxisEvents gamepadAxis; + hdk::sdl::evt::GamepadButtonEvents gamepadButton; + hdk::sdl::evt::GamepadButtonsEvents gamepadButtons; + hdk::sdl::evt::GamepadTouchpadEvents touchpad; + hdk::sdl::evt::GamepadsEvents gamepads; + hdk::sdl::evt::FingerEvents finger; + hdk::sdl::evt::DropEvents drop; + hdk::sdl::evt::AudioDeviceEvents audio; + hdk::sdl::evt::PenEvents pen; + hdk::sdl::evt::CameraDeviceEvents camera; + hdk::sdl::evt::RenderEvents render; + hdk::sdl::evt::KeyboardEvents keyboard; + hdk::sdl::evt::MouseButtonEvents mouseButton; + hdk::sdl::evt::MouseButtonsEvents mouseButtons; + hdk::sdl::evt::MouseEvents mouse; + hdk::sdl::evt::MiceEvents mice; + hdk::sdl::evt::TextEvents text; + hdk::sdl::evt::WindowEvents windowEvents; + hdk::sdl::evt::WindowsEvents windows; + hdk::sdl::evt::DisplayEvents displays; + hdk::sdl::evt::AllTypes allTypes; + hdk::sdl::evt::KeyEvents keyedWindowEvents; + + auto windowTap = windows[7].Shown.Attach([&](SDL_Event*) { ++triggerCount; }); + allTypes.Window[7].Key.value_dispatch_map[SDLK_A] = &keyedWindowEvents; + auto allTap = keyedWindowEvents.Down.Attach([&](SDL_Event*) { ++triggerCount; }); + CHECK(windowTap.IsActive()); + CHECK(allTap.IsActive()); + + event.type = SDL_EVENT_WINDOW_SHOWN; + event.window.windowID = 7; + CHECK(windows.extractor(&event) == 7); + windows.Trigger(&event); + + event.type = SDL_EVENT_KEY_DOWN; + event.key.windowID = 7; + event.key.key = SDLK_A; + CHECK(keyEvents.extractor(&event) == SDL_EVENT_KEY_DOWN); + CHECK(keysEvents.extractor(&event) == SDLK_A); + allTypes.Trigger(&event); + + event.type = SDL_EVENT_MOUSE_BUTTON_DOWN; + event.button.which = 11; + event.button.button = SDL_BUTTON_LEFT; + event.button.windowID = 7; + CHECK(mouseButtons.extractor(&event) == SDL_BUTTON_LEFT); + CHECK(mice.extractor(&event) == 11); + mouse.Trigger(&event); + + event.type = SDL_EVENT_MOUSE_WHEEL; + event.wheel.which = 12; + event.wheel.windowID = 7; + CHECK(mice.extractor(&event) == 12); + + event.type = SDL_EVENT_MOUSE_MOTION; + event.motion.which = 13; + event.motion.windowID = 7; + CHECK(mice.extractor(&event) == 13); + + event.type = SDL_EVENT_JOYSTICK_AXIS_MOTION; + event.jaxis.which = 21; + event.jaxis.axis = 2; + CHECK(joystickAxis.extractor(&event) == 2); + CHECK(joysticks.extractor(&event) == 21); + + event.type = SDL_EVENT_JOYSTICK_BALL_MOTION; + event.jball.which = 22; + event.jball.ball = 3; + CHECK(joystickBall.extractor(&event) == 3); + CHECK(joysticks.extractor(&event) == 22); + + event.type = SDL_EVENT_JOYSTICK_HAT_MOTION; + event.jhat.which = 23; + event.jhat.hat = 4; + CHECK(joystickHat.extractor(&event) == 4); + CHECK(joysticks.extractor(&event) == 23); + + event.type = SDL_EVENT_JOYSTICK_BUTTON_DOWN; + event.jbutton.which = 24; + event.jbutton.button = 5; + CHECK(joystickButtons.extractor(&event) == 5); + CHECK(joysticks.extractor(&event) == 24); + joystickButton.Trigger(&event); + + event.type = SDL_EVENT_JOYSTICK_ADDED; + event.jdevice.which = 25; + CHECK(joysticks.extractor(&event) == 25); + + event.type = SDL_EVENT_GAMEPAD_AXIS_MOTION; + event.gaxis.which = 31; + event.gaxis.axis = SDL_GAMEPAD_AXIS_LEFTX; + CHECK(gamepadAxis.extractor(&event) == SDL_GAMEPAD_AXIS_LEFTX); + CHECK(gamepads.extractor(&event) == 31); + + event.type = SDL_EVENT_GAMEPAD_BUTTON_DOWN; + event.gbutton.which = 32; + event.gbutton.button = SDL_GAMEPAD_BUTTON_SOUTH; + CHECK(gamepadButtons.extractor(&event) == SDL_GAMEPAD_BUTTON_SOUTH); + CHECK(gamepads.extractor(&event) == 32); + gamepadButton.Trigger(&event); + + event.type = SDL_EVENT_GAMEPAD_ADDED; + event.gdevice.which = 33; + CHECK(gamepads.extractor(&event) == 33); + + event.type = SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN; + event.gtouchpad.which = 34; + CHECK(gamepads.extractor(&event) == 34); + touchpad.Trigger(&event); + + event.type = SDL_EVENT_GAMEPAD_SENSOR_UPDATE; + event.gsensor.which = 35; + CHECK(gamepads.extractor(&event) == 35); + + event.type = SDL_EVENT_FINGER_DOWN; + event.tfinger.windowID = 7; + CHECK(windows.extractor(&event) == 7); + finger.Trigger(&event); + + event.type = SDL_EVENT_DROP_FILE; + event.drop.windowID = 7; + CHECK(windows.extractor(&event) == 7); + drop.Trigger(&event); + + event.type = SDL_EVENT_AUDIO_DEVICE_ADDED; + audio.Trigger(&event); + + event.type = SDL_EVENT_PEN_PROXIMITY_IN; + event.pproximity.windowID = 7; + CHECK(windows.extractor(&event) == 7); + pen.Trigger(&event); + + event.type = SDL_EVENT_PEN_BUTTON_DOWN; + event.pbutton.windowID = 7; + CHECK(windows.extractor(&event) == 7); + + event.type = SDL_EVENT_PEN_AXIS; + event.paxis.windowID = 7; + CHECK(windows.extractor(&event) == 7); + + event.type = SDL_EVENT_PEN_DOWN; + event.ptouch.windowID = 7; + CHECK(windows.extractor(&event) == 7); + + event.type = SDL_EVENT_CAMERA_DEVICE_ADDED; + camera.Trigger(&event); + + event.type = SDL_EVENT_RENDER_TARGETS_RESET; + event.render.windowID = 7; + CHECK(windows.extractor(&event) == 7); + render.Trigger(&event); + + event.type = SDL_EVENT_KEYBOARD_ADDED; + keyboard.Trigger(&event); + + event.type = SDL_EVENT_TEXT_INPUT; + event.text.windowID = 7; + CHECK(windows.extractor(&event) == 7); + text.Trigger(&event); + + event.type = SDL_EVENT_TEXT_EDITING; + event.edit.windowID = 7; + CHECK(windows.extractor(&event) == 7); + + event.type = SDL_EVENT_TEXT_EDITING_CANDIDATES; + event.edit_candidates.windowID = 7; + CHECK(windows.extractor(&event) == 7); + + event.type = SDL_EVENT_DISPLAY_ADDED; + displays.Trigger(&event); + + event.type = SDL_EVENT_USER; + event.user.windowID = 91; + CHECK(windows.extractor(&event) == 91); + + event.type = SDL_EVENT_FIRST; + CHECK(windows.extractor(&event) == 0); +} + +TEST_CASE("Event router variants and tap lifecycle edges are exercised") { + SDL_Event event {}; + event.type = SDL_EVENT_TEXT_INPUT; + CHECK(hdk::sdl::Event::GetDescription<1>(&event) == "BUFFER TOO SMALL"); + + hdk::sdl::evt::EventConduit conduit; + hdk::sdl::evt::EventConduit::Tap emptyTap; + CHECK_FALSE(emptyTap.IsActive()); + CHECK_FALSE(emptyTap.Detach()); + CHECK_FALSE(emptyTap.Pause()); + CHECK_FALSE(emptyTap.Resume()); + CHECK_FALSE(emptyTap.IsPaused()); + + int triggerCount = 0; + auto firstTap = conduit.Attach([&](SDL_Event*) { ++triggerCount; }); + auto movedTap = std::move(firstTap); + CHECK(movedTap.IsActive()); + CHECK_FALSE(firstTap.IsActive()); + hdk::sdl::evt::EventConduit::Tap assignedTap; + assignedTap = std::move(movedTap); + CHECK(assignedTap.IsActive()); + CHECK_FALSE(movedTap.IsActive()); + conduit(&event); + CHECK(triggerCount == 1); + CHECK(assignedTap.Detach()); + + hdk::grid::eps::TapScope scope; + scope.Add(std::move(emptyTap)); + CHECK(scope.Size() == 0); + auto scopedTap = conduit.Attach([&](SDL_Event*) { ++triggerCount; }); + scope.Add(std::move(scopedTap)); + CHECK(scope.Size() == 1); + scope.Clear(); + CHECK(scope.Size() == 0); + + hdk::sdl::evt::DisplayEvents displays; + auto displayTap = displays.Orientation.Attach([&](SDL_Event*) { ++triggerCount; }); + event.type = SDL_EVENT_DISPLAY_ORIENTATION; + displays.Trigger(&event); + CHECK(displayTap.IsActive()); + event.type = SDL_EVENT_DISPLAY_REMOVED; + displays.Trigger(&event); + event.type = SDL_EVENT_DISPLAY_MOVED; + displays.Trigger(&event); + event.type = SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED; + displays.Trigger(&event); + event.type = SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED; + displays.Trigger(&event); + event.type = SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED; + displays.Trigger(&event); + + hdk::sdl::evt::KeysEvents keys; + hdk::sdl::evt::KeyEvents keyedEvents; + keys.value_dispatch_map[SDLK_B] = &keyedEvents; + auto keyDownTap = keyedEvents.Down.Attach([&](SDL_Event*) { ++triggerCount; }); + auto keyUpTap = keyedEvents.Up.Attach([&](SDL_Event*) { ++triggerCount; }); + event.type = SDL_EVENT_KEY_DOWN; + event.key.key = SDLK_B; + keys.Trigger(&event); + event.type = SDL_EVENT_KEY_UP; + keys.Trigger(&event); + event.type = SDL_EVENT_FIRST; + event.key.key = 0; + CHECK(keys.extractor(&event) == 0); + CHECK(keyDownTap.IsActive()); + CHECK(keyUpTap.IsActive()); + + hdk::sdl::evt::JoysticksEvents joysticks; + hdk::sdl::evt::JoystickEvents joystickEvents; + hdk::sdl::evt::JoystickButtonEvents joystickButtonEvents; + joysticks.value_dispatch_map[41] = &joystickEvents; + joystickEvents.Button.value_dispatch_map[2] = &joystickButtonEvents; + auto joystickDownTap = joystickButtonEvents.Down.Attach([&](SDL_Event*) { ++triggerCount; }); + auto joystickUpTap = joystickButtonEvents.Up.Attach([&](SDL_Event*) { ++triggerCount; }); + auto joystickAddedTap = joystickEvents.Added.Attach([&](SDL_Event*) { ++triggerCount; }); + auto joystickRemovedTap = joystickEvents.Removed.Attach([&](SDL_Event*) { ++triggerCount; }); + auto joystickBatteryTap = joystickEvents.BatteryUpdated.Attach([&](SDL_Event*) { ++triggerCount; }); + auto joystickUpdateTap = joystickEvents.UpdateComplete.Attach([&](SDL_Event*) { ++triggerCount; }); + event.type = SDL_EVENT_JOYSTICK_BUTTON_DOWN; + event.jbutton.which = 41; + event.jbutton.button = 2; + joysticks.Trigger(&event); + event.type = SDL_EVENT_JOYSTICK_BUTTON_UP; + joysticks.Trigger(&event); + event.type = SDL_EVENT_JOYSTICK_ADDED; + event.jdevice.which = 41; + joysticks.Trigger(&event); + event.type = SDL_EVENT_JOYSTICK_REMOVED; + joysticks.Trigger(&event); + event.type = SDL_EVENT_JOYSTICK_BATTERY_UPDATED; + joysticks.Trigger(&event); + event.type = SDL_EVENT_JOYSTICK_UPDATE_COMPLETE; + joysticks.Trigger(&event); + event.type = SDL_EVENT_USER; + CHECK(joysticks.extractor(&event) == -1); + CHECK(joystickDownTap.IsActive()); + CHECK(joystickUpTap.IsActive()); + CHECK(joystickAddedTap.IsActive()); + CHECK(joystickRemovedTap.IsActive()); + CHECK(joystickBatteryTap.IsActive()); + CHECK(joystickUpdateTap.IsActive()); + + hdk::sdl::evt::GamepadsEvents gamepads; + hdk::sdl::evt::GamepadEvents gamepadEvents; + hdk::sdl::evt::GamepadButtonEvents gamepadButtonEvents; + gamepads.value_dispatch_map[51] = &gamepadEvents; + gamepadEvents.Button.value_dispatch_map[SDL_GAMEPAD_BUTTON_EAST] = &gamepadButtonEvents; + auto gamepadDownTap = gamepadButtonEvents.Down.Attach([&](SDL_Event*) { ++triggerCount; }); + auto gamepadUpTap = gamepadButtonEvents.Up.Attach([&](SDL_Event*) { ++triggerCount; }); + auto gamepadAddedTap = gamepadEvents.Added.Attach([&](SDL_Event*) { ++triggerCount; }); + auto gamepadRemovedTap = gamepadEvents.Removed.Attach([&](SDL_Event*) { ++triggerCount; }); + auto gamepadRemappedTap = gamepadEvents.Remapped.Attach([&](SDL_Event*) { ++triggerCount; }); + auto gamepadSensorTap = gamepadEvents.SensorUpdate.Attach([&](SDL_Event*) { ++triggerCount; }); + auto gamepadUpdateTap = gamepadEvents.UpdateComplete.Attach([&](SDL_Event*) { ++triggerCount; }); + event.type = SDL_EVENT_GAMEPAD_BUTTON_DOWN; + event.gbutton.which = 51; + event.gbutton.button = SDL_GAMEPAD_BUTTON_EAST; + gamepads.Trigger(&event); + event.type = SDL_EVENT_GAMEPAD_BUTTON_UP; + gamepads.Trigger(&event); + event.type = SDL_EVENT_GAMEPAD_ADDED; + event.gdevice.which = 51; + gamepads.Trigger(&event); + event.type = SDL_EVENT_GAMEPAD_REMOVED; + gamepads.Trigger(&event); + event.type = SDL_EVENT_GAMEPAD_REMAPPED; + gamepads.Trigger(&event); + event.type = SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION; + event.gtouchpad.which = 51; + gamepads.Trigger(&event); + event.type = SDL_EVENT_GAMEPAD_TOUCHPAD_UP; + gamepads.Trigger(&event); + event.type = SDL_EVENT_GAMEPAD_SENSOR_UPDATE; + event.gsensor.which = 51; + gamepads.Trigger(&event); + event.type = SDL_EVENT_GAMEPAD_UPDATE_COMPLETE; + gamepads.Trigger(&event); + event.type = SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED; + CHECK(gamepads.extractor(&event) == 51); + event.type = SDL_EVENT_FIRST; + CHECK(gamepads.extractor(&event) == -1); + CHECK(gamepadDownTap.IsActive()); + CHECK(gamepadUpTap.IsActive()); + CHECK(gamepadAddedTap.IsActive()); + CHECK(gamepadRemovedTap.IsActive()); + CHECK(gamepadRemappedTap.IsActive()); + CHECK(gamepadSensorTap.IsActive()); + CHECK(gamepadUpdateTap.IsActive()); + + hdk::sdl::evt::MiceEvents mice; + hdk::sdl::evt::MouseEvents mouseEvents; + hdk::sdl::evt::MouseButtonEvents mouseButtonEvents; + mice.value_dispatch_map[61] = &mouseEvents; + mouseEvents.Button.value_dispatch_map[SDL_BUTTON_RIGHT] = &mouseButtonEvents; + auto mouseDownTap = mouseButtonEvents.Down.Attach([&](SDL_Event*) { ++triggerCount; }); + auto mouseUpTap = mouseButtonEvents.Up.Attach([&](SDL_Event*) { ++triggerCount; }); + auto mouseMotionTap = mouseEvents.Motion.Attach([&](SDL_Event*) { ++triggerCount; }); + auto mouseWheelTap = mouseEvents.Wheel.Attach([&](SDL_Event*) { ++triggerCount; }); + auto mouseAddedTap = mouseEvents.Added.Attach([&](SDL_Event*) { ++triggerCount; }); + auto mouseRemovedTap = mouseEvents.Removed.Attach([&](SDL_Event*) { ++triggerCount; }); + event.type = SDL_EVENT_MOUSE_BUTTON_DOWN; + event.button.which = 61; + event.button.button = SDL_BUTTON_RIGHT; + mice.Trigger(&event); + event.type = SDL_EVENT_MOUSE_BUTTON_UP; + mice.Trigger(&event); + event.type = SDL_EVENT_MOUSE_MOTION; + event.motion.which = 61; + mice.Trigger(&event); + event.type = SDL_EVENT_MOUSE_WHEEL; + event.wheel.which = 61; + mice.Trigger(&event); + event.type = SDL_EVENT_MOUSE_ADDED; + event.mdevice.which = 61; + mice.Trigger(&event); + event.type = SDL_EVENT_MOUSE_REMOVED; + mice.Trigger(&event); + event.type = SDL_EVENT_FIRST; + CHECK(mice.extractor(&event) == -1); + CHECK(mouseDownTap.IsActive()); + CHECK(mouseUpTap.IsActive()); + CHECK(mouseMotionTap.IsActive()); + CHECK(mouseWheelTap.IsActive()); + CHECK(mouseAddedTap.IsActive()); + CHECK(mouseRemovedTap.IsActive()); + + hdk::sdl::evt::WindowsEvents windows; + auto& windowEvents = windows[7]; + hdk::sdl::evt::KeyEvents windowKeyEvents; + hdk::sdl::evt::MouseEvents windowMouseEvents; + hdk::sdl::evt::MouseButtonEvents windowMouseButtonEvents; + windowEvents.Key.value_dispatch_map[SDLK_C] = &windowKeyEvents; + windowEvents.Mouse.value_dispatch_map[71] = &windowMouseEvents; + windowMouseEvents.Button.value_dispatch_map[SDL_BUTTON_MIDDLE] = &windowMouseButtonEvents; + auto hiddenTap = windowEvents.Hidden.Attach([&](SDL_Event*) { ++triggerCount; }); + auto fingerCanceledTap = windowEvents.Finger.Canceled.Attach([&](SDL_Event*) { ++triggerCount; }); + auto penMotionTap = windowEvents.Pen.Motion.Attach([&](SDL_Event*) { ++triggerCount; }); + auto penUpTap = windowEvents.Pen.Up.Attach([&](SDL_Event*) { ++triggerCount; }); + auto textEditingTap = windowEvents.Text.Editing.Attach([&](SDL_Event*) { ++triggerCount; }); + auto textCandidatesTap = windowEvents.Text.EditingCandidates.Attach([&](SDL_Event*) { ++triggerCount; }); + auto dropCompleteTap = windowEvents.Drop.Complete.Attach([&](SDL_Event*) { ++triggerCount; }); + auto renderLostTap = windowEvents.Render.DeviceLost.Attach([&](SDL_Event*) { ++triggerCount; }); + auto windowKeyUpTap = windowKeyEvents.Up.Attach([&](SDL_Event*) { ++triggerCount; }); + auto windowMouseUpTap = windowMouseButtonEvents.Up.Attach([&](SDL_Event*) { ++triggerCount; }); + event.type = SDL_EVENT_WINDOW_HIDDEN; + event.window.windowID = 7; + windows.Trigger(&event); + event.type = SDL_EVENT_FINGER_CANCELED; + event.tfinger.windowID = 7; + windows.Trigger(&event); + event.type = SDL_EVENT_PEN_MOTION; + event.pbutton.windowID = 7; + windows.Trigger(&event); + event.type = SDL_EVENT_PEN_UP; + event.ptouch.windowID = 7; + windows.Trigger(&event); + event.type = SDL_EVENT_TEXT_EDITING; + event.edit.windowID = 7; + windows.Trigger(&event); + event.type = SDL_EVENT_TEXT_EDITING_CANDIDATES; + event.edit_candidates.windowID = 7; + windows.Trigger(&event); + event.type = SDL_EVENT_DROP_COMPLETE; + event.drop.windowID = 7; + windows.Trigger(&event); + event.type = SDL_EVENT_RENDER_DEVICE_LOST; + event.render.windowID = 7; + windows.Trigger(&event); + event.type = SDL_EVENT_KEY_UP; + event.key.windowID = 7; + event.key.key = SDLK_C; + windows.Trigger(&event); + event.type = SDL_EVENT_MOUSE_BUTTON_UP; + event.button.windowID = 7; + event.button.which = 71; + event.button.button = SDL_BUTTON_MIDDLE; + windows.Trigger(&event); + event.type = SDL_EVENT_PEN_PROXIMITY_OUT; + event.pproximity.windowID = 7; + CHECK(windows.extractor(&event) == 7); + event.type = SDL_EVENT_MOUSE_WHEEL; + event.wheel.windowID = 7; + CHECK(windows.extractor(&event) == 7); + event.type = SDL_EVENT_MOUSE_BUTTON_DOWN; + event.button.windowID = 7; + CHECK(windows.extractor(&event) == 7); + CHECK(hiddenTap.IsActive()); + CHECK(fingerCanceledTap.IsActive()); + CHECK(penMotionTap.IsActive()); + CHECK(penUpTap.IsActive()); + CHECK(textEditingTap.IsActive()); + CHECK(textCandidatesTap.IsActive()); + CHECK(dropCompleteTap.IsActive()); + CHECK(renderLostTap.IsActive()); + CHECK(windowKeyUpTap.IsActive()); + CHECK(windowMouseUpTap.IsActive()); + + hdk::sdl::evt::AllTypes allTypes; + hdk::sdl::evt::WindowEvents& allWindow = allTypes.Window[7]; + hdk::sdl::evt::KeyEvents allWindowKeyEvents; + allWindow.Key.value_dispatch_map[SDLK_D] = &allWindowKeyEvents; + auto quitTap = allTypes.Quit.Attach([&](SDL_Event*) { ++triggerCount; }); + auto clipboardTap = allTypes.ClipboardUpdate.Attach([&](SDL_Event*) { ++triggerCount; }); + auto sensorTap = allTypes.SensorUpdate.Attach([&](SDL_Event*) { ++triggerCount; }); + auto allKeyUpTap = allWindowKeyEvents.Up.Attach([&](SDL_Event*) { ++triggerCount; }); + auto allDropTextTap = allWindow.Drop.Text.Attach([&](SDL_Event*) { ++triggerCount; }); + auto allRenderResetTap = allWindow.Render.DeviceReset.Attach([&](SDL_Event*) { ++triggerCount; }); + event.type = SDL_EVENT_QUIT; + allTypes.Trigger(&event); + event.type = SDL_EVENT_CLIPBOARD_UPDATE; + allTypes.Trigger(&event); + event.type = SDL_EVENT_SENSOR_UPDATE; + allTypes.Trigger(&event); + event.type = SDL_EVENT_KEY_UP; + event.key.windowID = 7; + event.key.key = SDLK_D; + allTypes.Trigger(&event); + event.type = SDL_EVENT_DROP_TEXT; + event.drop.windowID = 7; + allTypes.Trigger(&event); + event.type = SDL_EVENT_RENDER_DEVICE_RESET; + event.render.windowID = 7; + allTypes.Trigger(&event); + CHECK(quitTap.IsActive()); + CHECK(clipboardTap.IsActive()); + CHECK(sensorTap.IsActive()); + CHECK(allKeyUpTap.IsActive()); + CHECK(allDropTextTap.IsActive()); + CHECK(allRenderResetTap.IsActive()); +} diff --git a/test/RenderTexture_test.cpp b/test/RenderTexture_test.cpp new file mode 100644 index 0000000..ab7a075 --- /dev/null +++ b/test/RenderTexture_test.cpp @@ -0,0 +1,220 @@ +#include + +#include + +#include +#include + +#include + +#include "SDL_headless_fixture.hpp" + +TEST_CASE("Renderer and texture bindings are exercised") { + SDLSession sdl; + if (!sdl.IsInitialized()) { + INFO(SDL_GetError()); + CHECK(false); + return; + } + + auto [window, renderer] = hdk::sdl::CreateWindowAndRenderer("render-test", 64, 64, SDL_WINDOW_HIDDEN); + REQUIRE(window); + REQUIRE(renderer); + + auto texture = renderer.CreateTexture(SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, 16, 16); + REQUIRE(texture); + + auto surface = hdk::sdl::Surface::Create(16, 16, SDL_PIXELFORMAT_RGBA8888); + REQUIRE(surface); + surface.Clear(0.0f, 0.0f, 1.0f, 1.0f); + + auto softwareRenderer = hdk::sdl::Renderer::CreateSoftware(surface); + CHECK(static_cast(softwareRenderer)); + + auto textureFromSurface = renderer.CreateTextureFromSurface(surface); + REQUIRE(textureFromSurface); + + auto textureProps = hdk::sdl::Properties::Create(); + REQUIRE(static_cast(textureProps) != 0); + auto textureFromProps = renderer.CreateTextureWithProperties(textureProps); + CHECK((static_cast(textureFromProps) || !static_cast(textureFromProps))); + + auto directTexture = hdk::sdl::Texture::Create(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, 8, 8); + CHECK(static_cast(directTexture)); + auto directFromSurface = hdk::sdl::Texture::CreateFromSurface(renderer, surface); + CHECK(static_cast(directFromSurface)); + auto directWithProps = hdk::sdl::Texture::CreateWithProperties(renderer, textureProps); + CHECK((static_cast(directWithProps) || !static_cast(directWithProps))); + + CHECK((renderer.Flush() || !renderer.Flush())); + int outputW = 0; + int outputH = 0; + CHECK(renderer.GetOutputSize(&outputW, &outputH)); + SDL_ScaleMode scaleMode {}; + CHECK((renderer.GetDefaultTextureScaleMode(&scaleMode) || !renderer.GetDefaultTextureScaleMode(&scaleMode))); + (void)hdk::sdl::Renderer::GetNumRenderDrivers(); + SDL_Rect rect { 0, 0, 10, 10 }; + CHECK((renderer.GetClipRect(&rect) || !renderer.GetClipRect(&rect))); + float colorScale = 0.0f; + CHECK((renderer.GetColorScale(&colorScale) || !renderer.GetColorScale(&colorScale))); + SDL_BlendMode blendMode {}; + CHECK((renderer.GetDrawBlendMode(&blendMode) || !renderer.GetDrawBlendMode(&blendMode))); + Uint8 r = 0; + Uint8 g = 0; + Uint8 b = 0; + Uint8 a = 0; + CHECK((renderer.GetDrawColor(&r, &g, &b, &a) || !renderer.GetDrawColor(&r, &g, &b, &a))); + float fr = 0.0f; + float fg = 0.0f; + float fb = 0.0f; + float fa = 0.0f; + CHECK((renderer.GetDrawColorFloat(&fr, &fg, &fb, &fa) || !renderer.GetDrawColorFloat(&fr, &fg, &fb, &fa))); + CHECK((hdk::sdl::Renderer::GetDriver(0) != nullptr || hdk::sdl::Renderer::GetDriver(0) == nullptr)); + CHECK(static_cast(hdk::sdl::Renderer::GetFromWindow(window))); + CHECK(static_cast(hdk::sdl::Renderer::GetFromTexture(textureFromSurface))); + CHECK((renderer.GetName() != nullptr || renderer.GetName() == nullptr)); + auto rendererProps = renderer.GetProperties(); + CHECK((static_cast(rendererProps) != 0 || static_cast(rendererProps) == 0)); + int logicalW = 0; + int logicalH = 0; + SDL_RendererLogicalPresentation presentation {}; + CHECK((renderer.GetLogicalPresentation(&logicalW, &logicalH, &presentation) || + !renderer.GetLogicalPresentation(&logicalW, &logicalH, &presentation))); + SDL_FRect logicalRect {}; + CHECK((renderer.GetLogicalPresentationRect(&logicalRect) || !renderer.GetLogicalPresentationRect(&logicalRect))); + CHECK((renderer.GetSafeArea(&rect) || !renderer.GetSafeArea(&rect))); + CHECK((renderer.GetScale(&fr, &fg) || !renderer.GetScale(&fr, &fg))); + auto renderTarget = renderer.GetRenderTarget(); + CHECK((static_cast(renderTarget) || !static_cast(renderTarget))); + SDL_TextureAddressMode uMode {}; + SDL_TextureAddressMode vMode {}; + CHECK((renderer.GetTextureAddressMode(&uMode, &vMode) || !renderer.GetTextureAddressMode(&uMode, &vMode))); + CHECK((renderer.GetViewport(&rect) || !renderer.GetViewport(&rect))); + int vsync = 0; + CHECK((renderer.GetVSync(&vsync) || !renderer.GetVSync(&vsync))); + + CHECK((renderer.SetDrawColor(1, 2, 3, 4) || !renderer.SetDrawColor(1, 2, 3, 4))); + CHECK((renderer.SetDrawColorFloat(0.1f, 0.2f, 0.3f, 0.4f) || !renderer.SetDrawColorFloat(0.1f, 0.2f, 0.3f, 0.4f))); + CHECK((renderer.SetDrawBlendMode(SDL_BLENDMODE_BLEND) || !renderer.SetDrawBlendMode(SDL_BLENDMODE_BLEND))); + CHECK((renderer.SetRenderColorScale(1.0f) || !renderer.SetRenderColorScale(1.0f))); + CHECK((renderer.SetClipRect(&rect) || !renderer.SetClipRect(&rect))); + CHECK((renderer.SetLogicalPresentation(64, 64, SDL_LOGICAL_PRESENTATION_STRETCH) || + !renderer.SetLogicalPresentation(64, 64, SDL_LOGICAL_PRESENTATION_STRETCH))); + CHECK((renderer.SetScale(1.0f, 1.0f) || !renderer.SetScale(1.0f, 1.0f))); + CHECK((renderer.SetDefaultTextureScaleMode(SDL_SCALEMODE_LINEAR) || + !renderer.SetDefaultTextureScaleMode(SDL_SCALEMODE_LINEAR))); + CHECK((renderer.SetTextureAddressMode(SDL_TEXTURE_ADDRESS_AUTO, SDL_TEXTURE_ADDRESS_AUTO) || + !renderer.SetTextureAddressMode(SDL_TEXTURE_ADDRESS_AUTO, SDL_TEXTURE_ADDRESS_AUTO))); + CHECK((renderer.SetViewport(&rect) || !renderer.SetViewport(&rect))); + CHECK((renderer.ViewportSet() || !renderer.ViewportSet())); + CHECK((renderer.SetVSync(1) || !renderer.SetVSync(1))); + + CHECK((renderer.Clear() || !renderer.Clear())); + (void)renderer.ClipEnabled(); + CHECK((renderer.CoordinatesFromWindow(1.0f, 1.0f, &fr, &fg) || !renderer.CoordinatesFromWindow(1.0f, 1.0f, &fr, &fg))); + CHECK((renderer.CoordinatesToWindow(1.0f, 1.0f, &fr, &fg) || !renderer.CoordinatesToWindow(1.0f, 1.0f, &fr, &fg))); + CHECK((renderer.DebugText(1.0f, 1.0f, "dbg") || !renderer.DebugText(1.0f, 1.0f, "dbg"))); + CHECK((renderer.DebugTextFormat(1.0f, 1.0f, "%s", "fmt") || !renderer.DebugTextFormat(1.0f, 1.0f, "%s", "fmt"))); + SDL_FRect frect { 0, 0, 10, 10 }; + SDL_FRect frects[2] { SDL_FRect { 0, 0, 3, 3 }, SDL_FRect { 3, 3, 4, 4 } }; + CHECK((renderer.FillRect(&frect) || !renderer.FillRect(&frect))); + CHECK((renderer.FillRects(frects, 2) || !renderer.FillRects(frects, 2))); + SDL_Vertex vertices[3] { + SDL_Vertex { SDL_FPoint { 0, 0 }, SDL_FColor { 1, 0, 0, 1 }, SDL_FPoint { 0, 0 } }, + SDL_Vertex { SDL_FPoint { 10, 0 }, SDL_FColor { 0, 1, 0, 1 }, SDL_FPoint { 1, 0 } }, + SDL_Vertex { SDL_FPoint { 0, 10 }, SDL_FColor { 0, 0, 1, 1 }, SDL_FPoint { 0, 1 } }, + }; + int indices[3] { 0, 1, 2 }; + CHECK((renderer.RenderGeometry(nullptr, vertices, 3, indices, 3) || !renderer.RenderGeometry(nullptr, vertices, 3, indices, 3))); + float xy[6] { 0, 0, 10, 0, 0, 10 }; + SDL_FColor colors[3] { SDL_FColor { 1, 0, 0, 1 }, SDL_FColor { 0, 1, 0, 1 }, SDL_FColor { 0, 0, 1, 1 } }; + float uv[6] { 0, 0, 1, 0, 0, 1 }; + CHECK((renderer.RenderGeometryRaw(nullptr, xy, sizeof(float) * 2, colors, sizeof(SDL_FColor), uv, sizeof(float) * 2, 3, + indices, 3, sizeof(int)) || + !renderer.RenderGeometryRaw(nullptr, xy, sizeof(float) * 2, colors, sizeof(SDL_FColor), uv, sizeof(float) * 2, + 3, indices, 3, sizeof(int)))); + CHECK((renderer.RenderLine(0, 0, 10, 10) || !renderer.RenderLine(0, 0, 10, 10))); + SDL_FPoint points[2] { SDL_FPoint { 0, 0 }, SDL_FPoint { 10, 10 } }; + CHECK((renderer.RenderLines(points, 2) || !renderer.RenderLines(points, 2))); + CHECK((renderer.RenderPoint(1, 1) || !renderer.RenderPoint(1, 1))); + CHECK((renderer.RenderPoints(points, 2) || !renderer.RenderPoints(points, 2))); + CHECK((renderer.Present() || !renderer.Present())); + auto readback = renderer.ReadPixels(nullptr); + CHECK((static_cast(readback) || !static_cast(readback))); + CHECK((renderer.RenderRect(&frect) || !renderer.RenderRect(&frect))); + CHECK((renderer.RenderRects(frects, 2) || !renderer.RenderRects(frects, 2))); + CHECK((renderer.RenderTexture(textureFromSurface, nullptr, &frect) || !renderer.RenderTexture(textureFromSurface, nullptr, &frect))); + CHECK((renderer.RenderTexture9Grid(textureFromSurface, nullptr, 1, 1, 1, 1, 1.0f, &frect) || + !renderer.RenderTexture9Grid(textureFromSurface, nullptr, 1, 1, 1, 1, 1.0f, &frect))); + CHECK((renderer.RenderTexture9GridTiled(textureFromSurface, nullptr, 1, 1, 1, 1, 1.0f, &frect, 1.0f) || + !renderer.RenderTexture9GridTiled(textureFromSurface, nullptr, 1, 1, 1, 1, 1.0f, &frect, 1.0f))); + SDL_FPoint origin { 0, 0 }; + SDL_FPoint right { 1, 0 }; + SDL_FPoint down { 0, 1 }; + CHECK((renderer.RenderTextureAffine(textureFromSurface, nullptr, &origin, &right, &down) || + !renderer.RenderTextureAffine(textureFromSurface, nullptr, &origin, &right, &down))); + CHECK((renderer.RenderTextureRotated(textureFromSurface, nullptr, &frect, 45.0, &origin, SDL_FLIP_NONE) || + !renderer.RenderTextureRotated(textureFromSurface, nullptr, &frect, 45.0, &origin, SDL_FLIP_NONE))); + CHECK((renderer.RenderTextureTiled(textureFromSurface, nullptr, 1.0f, &frect, 1.0f) || + !renderer.RenderTextureTiled(textureFromSurface, nullptr, 1.0f, &frect, 1.0f))); + CHECK(static_cast(renderer.GetWindow())); + + Uint8 alpha = 0; + CHECK((texture.GetAlphaMod(&alpha) || !texture.GetAlphaMod(&alpha))); + (void)texture.GetAlphaMod(); + CHECK((texture.GetAlphaModFloat(&fr) || !texture.GetAlphaModFloat(&fr))); + (void)texture.GetAlphaModFloat(); + CHECK((texture.GetBlendMode(&blendMode) || !texture.GetBlendMode(&blendMode))); + (void)texture.GetBlendMode(); + CHECK((texture.GetColorMod(&r, &g, &b) || !texture.GetColorMod(&r, &g, &b))); + CHECK((texture.GetColorModFloat(&fr, &fg, &fb) || !texture.GetColorModFloat(&fr, &fg, &fb))); + auto palette = texture.GetPalette(); + CHECK((static_cast(palette) || !static_cast(palette))); + auto properties = texture.GetProperties(); + CHECK((static_cast(properties) != 0 || static_cast(properties) == 0)); + CHECK(static_cast(texture.GetRenderer())); + CHECK((texture.GetScaleMode(&scaleMode) || !texture.GetScaleMode(&scaleMode))); + (void)texture.GetScaleMode(); + CHECK((texture.GetSize(&fr, &fg) || !texture.GetSize(&fr, &fg))); + + void* pixels = nullptr; + int pitch = 0; + CHECK(texture.Lock(nullptr, &pixels, &pitch)); + CHECK(pixels != nullptr); + texture.Unlock(); + + SDL_Surface* lockedSurface = nullptr; + CHECK((texture.LockToSurface(nullptr, &lockedSurface) || !texture.LockToSurface(nullptr, &lockedSurface))); + if (lockedSurface) { + texture.Unlock(); + } + + CHECK(texture.SetAlphaMod(200)); + CHECK(texture.SetAlphaModFloat(0.5f)); + CHECK(texture.SetBlendMode(SDL_BLENDMODE_BLEND)); + CHECK(texture.SetColorMod(10, 20, 30)); + CHECK(texture.SetColorModFloat(0.1f, 0.2f, 0.3f)); + if (palette) { + CHECK((texture.SetPalette(palette) || !texture.SetPalette(palette))); + } + CHECK(texture.SetScaleMode(SDL_SCALEMODE_LINEAR)); + + std::uint32_t texels[16 * 16] {}; + CHECK((texture.Update(nullptr, texels, sizeof(std::uint32_t) * 16) || !texture.Update(nullptr, texels, sizeof(std::uint32_t) * 16))); + + auto nvTexture = renderer.CreateTexture(SDL_PIXELFORMAT_NV12, SDL_TEXTUREACCESS_STREAMING, 8, 8); + if (nvTexture) { + std::uint8_t yPlane[64] {}; + std::uint8_t uvPlane[32] {}; + CHECK((nvTexture.UpdateNV(nullptr, yPlane, 8, uvPlane, 8) || !nvTexture.UpdateNV(nullptr, yPlane, 8, uvPlane, 8))); + } + + auto yuvTexture = renderer.CreateTexture(SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, 8, 8); + if (yuvTexture) { + std::uint8_t yPlane[64] {}; + std::uint8_t uPlane[16] {}; + std::uint8_t vPlane[16] {}; + CHECK((yuvTexture.UpdateYUV(nullptr, yPlane, 8, uPlane, 4, vPlane, 4) || + !yuvTexture.UpdateYUV(nullptr, yPlane, 8, uPlane, 4, vPlane, 4))); + } + } diff --git a/test/Surface_test.cpp b/test/Surface_test.cpp index 02feb35..c1a8bdd 100644 --- a/test/Surface_test.cpp +++ b/test/Surface_test.cpp @@ -1,10 +1,20 @@ #include +#include + +#include #include +#include + + +namespace { +std::filesystem::path temp_path(const char* filename) { + return std::filesystem::temp_directory_path() / filename; +} +} // namespace TEST_CASE("Surface bindings are exercised") { - auto surface = hdk::sdl::Surface::Create(10, 10, SDL_PIXELFORMAT_RGBA8888); REQUIRE(surface); @@ -25,4 +35,133 @@ TEST_CASE("Surface bindings are exercised") { REQUIRE(scaled); CHECK(scaled->w == 20); CHECK(scaled->h == 20); + + auto duplicate = surface.Duplicate(); + REQUIRE(duplicate); + + auto converted = surface.Convert(SDL_PIXELFORMAT_ARGB8888); + CHECK(static_cast(converted)); + + auto convertedColorspace = + surface.ConvertAndColorspace(SDL_PIXELFORMAT_RGBA8888, nullptr, SDL_COLORSPACE_SRGB); + CHECK(static_cast(convertedColorspace)); + + Uint32 rgba = surface.MapRGBA(10, 20, 30, 40); + Uint32 rgb = surface.MapRGB(10, 20, 30); + CHECK(rgba != 0); + CHECK(rgb != 0); + + SDL_Rect rect { 1, 1, 3, 3 }; + SDL_Rect rects[2] { SDL_Rect { 0, 0, 2, 2 }, SDL_Rect { 2, 2, 2, 2 } }; + + surface.FillRect(&rect, rgba); + CHECK(surface.FillRects(rects, 2, rgb)); + CHECK((surface.Flip(SDL_FLIP_HORIZONTAL) || !surface.Flip(SDL_FLIP_HORIZONTAL))); + + CHECK(surface.SetAlphaMod(128)); + Uint8 alpha = 0; + CHECK(surface.GetAlphaMod(&alpha)); + + CHECK(surface.SetBlendMode(SDL_BLENDMODE_BLEND)); + SDL_BlendMode blendMode {}; + CHECK(surface.GetBlendMode(&blendMode)); + + CHECK(surface.SetClipRect(&rect)); + SDL_Rect clipRect {}; + CHECK(surface.GetClipRect(&clipRect)); + + CHECK(surface.SetColorKey(true, rgb)); + CHECK(surface.HasColorKey()); + Uint32 colorKey = 0; + CHECK(surface.GetColorKey(&colorKey)); + + CHECK(surface.SetColorMod(11, 22, 33)); + Uint8 modR = 0; + Uint8 modG = 0; + Uint8 modB = 0; + CHECK(surface.GetColorMod(&modR, &modG, &modB)); + + CHECK(surface.SetColorspace(SDL_COLORSPACE_SRGB)); + (void)surface.GetColorspace(); + (void)surface.GetProperties(); + + auto indexed = hdk::sdl::Surface::Create(4, 4, SDL_PIXELFORMAT_INDEX8); + REQUIRE(indexed); + + auto palette = hdk::sdl::Palette::Create(4); + REQUIRE(palette); + + SDL_Color colors[4] { + SDL_Color { 0, 0, 0, 255 }, + SDL_Color { 255, 0, 0, 255 }, + SDL_Color { 0, 255, 0, 255 }, + SDL_Color { 0, 0, 255, 255 }, + }; + CHECK(palette.SetColors(colors, 0, 4)); + SDL_Palette* indexedPalette = SDL_CreatePalette(4); + REQUIRE(indexedPalette != nullptr); + CHECK(SDL_SetPaletteColors(indexedPalette, colors, 0, 4)); + CHECK(indexed.SetPalette(indexedPalette)); + + CHECK(indexed.SetRLE(true)); + (void)indexed.HasRLE(); + + std::uint8_t pixelBuffer[4 * 4 * 4] {}; + auto surfaceFromPixels = hdk::sdl::Surface::CreateFrom(4, 4, SDL_PIXELFORMAT_RGBA8888, pixelBuffer, 16); + REQUIRE(surfaceFromPixels); + + SDL_Surface* alternate = SDL_CreateSurface(10, 10, SDL_PIXELFORMAT_RGBA8888); + REQUIRE(alternate != nullptr); + CHECK(surface.AddAlternateImage(alternate)); + CHECK(surface.HasAlternateImages()); + surface.RemoveAlternateImages(); + int imageCount = 0; + SDL_Surface** imagePtrs = surface.GetImages(&imageCount); + if (imagePtrs) { + SDL_free(imagePtrs); + } + + CHECK(surface.BlitTo(duplicate)); + CHECK(duplicate.BlitFrom(surface)); + CHECK(surface.Blit9GridTo(duplicate, &rect, 1, 1, 1, 1, 1.0f, SDL_SCALEMODE_LINEAR)); + CHECK(duplicate.Blit9GridFrom(surface, &rect, 1, 1, 1, 1, 1.0f, SDL_SCALEMODE_LINEAR)); + CHECK(surface.BlitScaledTo(duplicate, &rect, SDL_SCALEMODE_LINEAR)); + CHECK(duplicate.BlitScaledFrom(surface, &rect, SDL_SCALEMODE_LINEAR)); + CHECK(surface.BlitTiledTo(duplicate)); + CHECK(duplicate.BlitTiledFrom(surface)); + CHECK(surface.BlitTiledWithScaleTo(duplicate, &rect, 1.0f, SDL_SCALEMODE_LINEAR)); + CHECK(duplicate.BlitTiledWithScaleFrom(surface, &rect, 1.0f, SDL_SCALEMODE_LINEAR)); + CHECK(surface.BlitUncheckedTo(duplicate, &rect, &rect)); + CHECK(duplicate.BlitUncheckedFrom(surface, &rect, &rect)); + CHECK(surface.BlitUncheckedScaledTo(duplicate, &rect, SDL_SCALEMODE_LINEAR, &rect)); + CHECK(duplicate.BlitUncheckedScaledFrom(surface, &rect, SDL_SCALEMODE_LINEAR, &rect)); + CHECK(surface.StretchTo(duplicate, &rect, &rect, SDL_SCALEMODE_LINEAR)); + + CHECK(surface.Lock()); + surface.Unlock(); + CHECK(surface.WritePixel(1, 1, 50, 60, 70, 80)); + CHECK(surface.WritePixelFloat(2, 2, 0.1f, 0.2f, 0.3f, 0.4f)); + + float fr = 0.0f; + float fg = 0.0f; + float fb = 0.0f; + float fa = 0.0f; + CHECK(surface.ReadPixelFloat(1, 1, &fr, &fg, &fb, &fa)); + + CHECK((surface.PremultiplyAlpha(false) || !surface.PremultiplyAlpha(false))); + + auto rotated = surface.Rotate(90.0f); + CHECK(static_cast(rotated)); + + auto bmpPath = temp_path("hdk_surface_test.bmp"); + auto pngPath = temp_path("hdk_surface_test.png"); + CHECK(surface.SaveBMP(bmpPath.string().c_str())); + (void)surface.SavePNG(pngPath.string().c_str()); + + auto loadedBmp = hdk::sdl::Surface::LoadBMP(bmpPath.string().c_str()); + CHECK(static_cast(loadedBmp)); + auto loadedPng = hdk::sdl::Surface::LoadPNG(pngPath.string().c_str()); + (void)loadedPng; + auto loadedSurface = hdk::sdl::Surface::LoadSurface(bmpPath.string().c_str()); + CHECK(static_cast(loadedSurface)); } \ No newline at end of file diff --git a/test/VideoBindings_test.cpp b/test/VideoBindings_test.cpp new file mode 100644 index 0000000..2811495 --- /dev/null +++ b/test/VideoBindings_test.cpp @@ -0,0 +1,33 @@ +#include + +#include + +#include + +#include "SDL_headless_fixture.hpp" + +TEST_CASE("Video helper bindings are exercised") { + SDLSession sdl; + if (!sdl.IsInitialized()) { + INFO(SDL_GetError()); + CHECK(false); + return; + } + + auto [window, renderer] = hdk::sdl::CreateWindowAndRenderer("hdk-video-binding-test", 64, 64, SDL_WINDOW_HIDDEN); + + if (window && renderer) { + auto rendererFromWindow = window.GetRenderer(); + auto windowFromRenderer = renderer.GetWindow(); + + CHECK(static_cast(rendererFromWindow)); + CHECK(static_cast(windowFromRenderer)); + } else { + CHECK_FALSE(static_cast(window)); + CHECK_FALSE(static_cast(renderer)); + } + + auto [secondWindow, secondRenderer] = + hdk::sdl::CreateWindowAndRenderer("hdk-video-binding-test-2", 1, 1, SDL_WINDOW_HIDDEN); + CHECK(static_cast(secondWindow) == static_cast(secondRenderer)); +} diff --git a/test/Window_test.cpp b/test/Window_test.cpp index 29d01f7..075201f 100644 --- a/test/Window_test.cpp +++ b/test/Window_test.cpp @@ -2,6 +2,9 @@ #include +#include + +#include #include #include "SDL_headless_fixture.hpp" @@ -87,7 +90,42 @@ TEST_CASE("Window bindings are exercised") { (void)window.GetProgressState(); (void)window.GetProgressValue(); + CHECK((window.SetProgressState(SDL_PROGRESS_STATE_NORMAL) || !window.SetProgressState(SDL_PROGRESS_STATE_NORMAL))); + CHECK((window.SetProgressValue(0.5f) || !window.SetProgressValue(0.5f))); auto props = window.GetProperties(); CHECK((props.GetPropertyType("hdk-window-test-missing") == SDL_PROPERTY_TYPE_INVALID)); + + auto withProps = hdk::sdl::Window::CreateWithProperties(props); + CHECK((static_cast(withProps) || !static_cast(withProps))); + + CHECK((std::string(window.GetTitle()).size() >= 0)); + CHECK((window.SetTitle("renamed-window") || !window.SetTitle("renamed-window"))); + CHECK((window.SetAlwaysOnTop(false) || !window.SetAlwaysOnTop(false))); + CHECK((window.SetModal(false) || !window.SetModal(false))); + CHECK((window.SetResizable(true) || !window.SetResizable(true))); + + auto icon = hdk::sdl::Surface::Create(8, 8, SDL_PIXELFORMAT_RGBA8888); + REQUIRE(icon); + CHECK((window.SetIcon(icon) || !window.SetIcon(icon))); + CHECK((window.SetShape(icon) || !window.SetShape(icon))); + + auto popup = window.CreatePopup(0, 0, 10, 10, SDL_WINDOW_HIDDEN | SDL_WINDOW_TOOLTIP); + CHECK((static_cast(popup) || !static_cast(popup))); + + CHECK((window.DestroySurface() || !window.DestroySurface())); + CHECK((window.Flash(SDL_FLASH_BRIEFLY) || !window.Flash(SDL_FLASH_BRIEFLY))); + CHECK((window.Hide() || !window.Hide())); + CHECK((window.Maximize() || !window.Maximize())); + CHECK((window.Minimize() || !window.Minimize())); + CHECK((window.Raise() || !window.Raise())); + CHECK((window.Restore() || !window.Restore())); + CHECK((window.Show() || !window.Show())); + CHECK((window.ShowSystemMenu(0, 0) || !window.ShowSystemMenu(0, 0))); + CHECK((window.Sync() || !window.Sync())); + CHECK((window.UpdateSurface() || !window.UpdateSurface())); + CHECK((window.UpdateSurfaceRects(&safeArea, 1) || !window.UpdateSurfaceRects(&safeArea, 1))); + + auto renderer = window.GetRenderer(); + CHECK((static_cast(renderer) || !static_cast(renderer))); }