Initial Video, Render, and Properties implementations.

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
BadQuanta
2026-04-26 14:39:01 +00:00
parent 6d0de7e621
commit 85dbe33a77
17 changed files with 2461 additions and 50 deletions
+17
View File
@@ -0,0 +1,17 @@
add_executable(
hdk-sdl-tests
main.cpp
Properties_test.cpp
Window_test.cpp
Surface_test.cpp
)
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)
target_link_libraries(hdk-sdl-tests PRIVATE hdk-coverage-flags)
endif()
add_test(NAME hdk-sdl-tests COMMAND hdk-sdl-tests)
set_tests_properties(hdk-sdl-tests PROPERTIES ENVIRONMENT "SDL_VIDEODRIVER=dummy")
+95
View File
@@ -0,0 +1,95 @@
#include <doctest/doctest.h>
#include <SDL3/SDL.h>
#include <hdk/sdl/Properties.hpp>
#include <string>
#include <vector>
#include "SDL_headless_fixture.hpp"
namespace {
struct CleanupState {
int calls = 0;
};
void SDLCALL CleanupPointerProperty(void* userdata, void* value) {
auto* state = static_cast<CleanupState*>(userdata);
if (state) {
++state->calls;
}
delete static_cast<int*>(value);
}
void SDLCALL CollectPropertyNames(void* userdata, SDL_PropertiesID, const char* name) {
auto* names = static_cast<std::vector<std::string>*>(userdata);
if (names && name) {
names->emplace_back(name);
}
}
} // namespace
TEST_CASE("Properties bindings are exercised") {
SDLSession sdl;
if (!sdl.IsInitialized()) {
INFO(SDL_GetError());
CHECK(false);
return;
}
auto props = hdk::sdl::Properties::Create();
auto second = hdk::sdl::Properties::Create();
REQUIRE(static_cast<SDL_PropertiesID>(props) != 0);
REQUIRE(static_cast<SDL_PropertiesID>(second) != 0);
CHECK(props.SetBooleanProperty("hdk.bool", true));
CHECK(props.GetBooleanProperty("hdk.bool", false));
CHECK(props.SetFloatProperty("hdk.float", 1.5f));
CHECK(props.GetFloatProperty("hdk.float", 0.0f) == doctest::Approx(1.5f));
CHECK(props.SetNumberProperty("hdk.number", 44));
CHECK(props.GetNumberProperty("hdk.number", 0) == 44);
int pointerValue = 77;
CHECK(props.SetPointerProperty("hdk.pointer", &pointerValue));
CHECK(props.GetPointerProperty("hdk.pointer", nullptr) == &pointerValue);
CHECK(props.SetStringProperty("hdk.string", "value"));
CHECK(std::string(props.GetStringProperty("hdk.string", "missing")) == "value");
CHECK(props.HasProperty("hdk.string"));
CHECK(props.GetPropertyType("hdk.string") == SDL_PROPERTY_TYPE_STRING);
CleanupState cleanupState;
CHECK(props.SetPointerPropertyWithCleanup("hdk.cleanup", new int(9), CleanupPointerProperty, &cleanupState));
CHECK(props.GetPropertyType("hdk.cleanup") == SDL_PROPERTY_TYPE_POINTER);
CHECK(props.CopyAllTo(second));
CHECK(std::string(second.GetStringProperty("hdk.string", "missing")) == "value");
CHECK(second.ClearProperty("hdk.string"));
CHECK_FALSE(second.HasProperty("hdk.string"));
CHECK(second.CopyAllFrom(props));
CHECK(second.HasProperty("hdk.string"));
std::vector<std::string> names;
CHECK(props.EnumerateProperties(CollectPropertyNames, &names));
CHECK_FALSE(names.empty());
CHECK(props.LockProperties());
props.UnlockProperties();
auto global = hdk::sdl::Properties::GetGlobalProperties();
CHECK(static_cast<SDL_PropertiesID>(global) != 0);
CHECK(props.ClearProperty("hdk.cleanup"));
CHECK(cleanupState.calls >= 1);
props.Destroy();
CHECK(static_cast<SDL_PropertiesID>(props) == 0);
second.Destroy();
}
+32
View File
@@ -0,0 +1,32 @@
#pragma once
#include <SDL3/SDL.h>
#include <cstdlib>
inline void hdk_set_headless_video_driver() {
#if defined(_WIN32)
_putenv_s("SDL_VIDEODRIVER", "dummy");
#else
setenv("SDL_VIDEODRIVER", "dummy", 1);
#endif
}
class SDLSession {
public:
SDLSession() {
hdk_set_headless_video_driver();
initialized = SDL_Init(SDL_INIT_VIDEO);
}
~SDLSession() {
if (initialized) {
SDL_Quit();
}
}
bool IsInitialized() const { return initialized; }
private:
bool initialized = false;
};
+28
View File
@@ -0,0 +1,28 @@
#include <doctest/doctest.h>
#include <hdk/sdl/video/Surface.hpp>
TEST_CASE("Surface bindings are exercised") {
auto surface = hdk::sdl::Surface::Create(10, 10, SDL_PIXELFORMAT_RGBA8888);
REQUIRE(surface);
CHECK(surface->w == 10);
CHECK(surface->h == 10);
CHECK(surface->format == SDL_PIXELFORMAT_RGBA8888);
surface.Clear(1.0f, 0.0f, 0.0f, 1.0f);
Uint8 r, g, b, a;
CHECK(surface.ReadPixel(0, 0, &r, &g, &b, &a));
CHECK(r == 255);
CHECK(g == 0);
CHECK(b == 0);
CHECK(a == 255);
auto scaled = surface.Scale(20, 20, SDL_ScaleMode::SDL_SCALEMODE_LINEAR);
REQUIRE(scaled);
CHECK(scaled->w == 20);
CHECK(scaled->h == 20);
}
+93
View File
@@ -0,0 +1,93 @@
#include <doctest/doctest.h>
#include <SDL3/SDL.h>
#include <hdk/sdl/video/Window.hpp>
#include "SDL_headless_fixture.hpp"
TEST_CASE("Window bindings are exercised") {
SDLSession sdl;
if (!sdl.IsInitialized()) {
INFO(SDL_GetError());
CHECK(false);
return;
}
hdk::sdl::Window window = hdk::sdl::Window::Create("hdk-window-test", 64, 64, SDL_WINDOW_HIDDEN);
if (!window) {
INFO("SDL_CreateWindow failed in this environment: " << SDL_GetError());
window = hdk::sdl::Window(nullptr);
}
auto grabbed = hdk::sdl::Window::GetGrabbed();
CHECK((static_cast<bool>(grabbed) || !static_cast<bool>(grabbed)));
auto fromZero = hdk::sdl::Window::GetFromID(0);
CHECK_FALSE(static_cast<bool>(fromZero));
float minAspect = 0.0f;
float maxAspect = 0.0f;
CHECK((window.GetAspectRatio(&minAspect, &maxAspect) || !window.GetAspectRatio(&minAspect, &maxAspect)));
int top = 0;
int left = 0;
int bottom = 0;
int right = 0;
CHECK((window.GetBordersSize(&top, &left, &bottom, &right) || !window.GetBordersSize(&top, &left, &bottom, &right)));
(void)window.GetDisplayScale();
(void)window.GetFlags();
(void)window.GetFullscreenMode();
(void)window.GetID();
(void)window.GetKeyboardGrab();
CHECK((window.SetKeyboardGrab(false) || !window.SetKeyboardGrab(false)));
(void)window.GetMouseGrab();
CHECK((window.SetMouseGrab(false) || !window.SetMouseGrab(false)));
(void)window.GetMouseRect();
CHECK((window.SetMouseRect(nullptr) || !window.SetMouseRect(nullptr)));
int maxW = 0;
int maxH = 0;
CHECK((window.GetMaximumSize(&maxW, &maxH) || !window.GetMaximumSize(&maxW, &maxH)));
CHECK((window.SetMaximumSize(1280, 720) || !window.SetMaximumSize(1280, 720)));
int minW = 0;
int minH = 0;
CHECK((window.GetMinimumSize(&minW, &minH) || !window.GetMinimumSize(&minW, &minH)));
CHECK((window.SetMinimumSize(32, 32) || !window.SetMinimumSize(32, 32)));
CHECK((window.SetSize(96, 96) || !window.SetSize(96, 96)));
int width = 0;
int height = 0;
CHECK((window.GetSize(&width, &height) || !window.GetSize(&width, &height)));
SDL_Rect safeArea{};
CHECK((window.GetSafeArea(&safeArea) || !window.GetSafeArea(&safeArea)));
(void)window.GetOpacity();
CHECK((window.SetOpacity(0.8f) || !window.SetOpacity(0.8f)));
auto parent = window.GetParent();
CHECK((static_cast<bool>(parent) || !static_cast<bool>(parent)));
CHECK((window.SetParent(nullptr) || !window.SetParent(nullptr)));
(void)window.GetPixelDensity();
int vsync = 0;
CHECK((window.GetSurfaceVSync(&vsync) || !window.GetSurfaceVSync(&vsync)));
CHECK((window.SetSurfaceVSync(1) || !window.SetSurfaceVSync(1)));
(void)window.GetPixelFormat();
int x = 0;
int y = 0;
CHECK((window.GetPosition(&x, &y) || !window.GetPosition(&x, &y)));
CHECK((window.SetPosition(10, 10) || !window.SetPosition(10, 10)));
(void)window.GetProgressState();
(void)window.GetProgressValue();
auto props = window.GetProperties();
CHECK((props.GetPropertyType("hdk-window-test-missing") == SDL_PROPERTY_TYPE_INVALID));
}
+2
View File
@@ -0,0 +1,2 @@
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest/doctest.h>