diff --git a/.gitignore b/.gitignore index e92ae95..51269c9 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,3 @@ __pycache__/ /build /build.ninja /shuriken.override.yaml - -# Dependencies -/vendor -!/vendor/README.md diff --git a/assets/fonts/LatoLatin-Regular.ttf b/assets/fonts/LatoLatin-Regular.ttf deleted file mode 100644 index bcc5778..0000000 Binary files a/assets/fonts/LatoLatin-Regular.ttf and /dev/null differ diff --git a/assets/neofox.png b/assets/neofox.png deleted file mode 100644 index 64dd563..0000000 Binary files a/assets/neofox.png and /dev/null differ diff --git a/assets/sprites/neocat_64.png b/assets/sprites/neocat_64.png deleted file mode 100644 index b8c0957..0000000 Binary files a/assets/sprites/neocat_64.png and /dev/null differ diff --git a/assets/sprites/neofox_64.png b/assets/sprites/neofox_64.png deleted file mode 100644 index b00da05..0000000 Binary files a/assets/sprites/neofox_64.png and /dev/null differ diff --git a/assets/tilesets/terrain.png b/assets/tilesets/terrain.png deleted file mode 100644 index d14db67..0000000 Binary files a/assets/tilesets/terrain.png and /dev/null differ diff --git a/assets/ui/base.rcss b/assets/ui/base.rcss deleted file mode 100644 index b325039..0000000 --- a/assets/ui/base.rcss +++ /dev/null @@ -1,88 +0,0 @@ -/* Base style sheet based on: https://mikke89.github.io/RmlUiDoc/pages/rml/html4_style_sheet.html */ - -body, div, h1, h2, h3, h4, h5, h6, p, hr, pre { - display: block; -} - -h1 { - font-size: 2em; - margin: .67em 0; -} - -h2 { - font-size: 1.5em; - margin: .75em 0; -} - -h3 { - font-size: 1.17em; - margin: .83em 0; -} - -h4 { - margin: 1.12em 0; -} - -h5 { - font-size: .83em; - margin: 1.5em 0; -} - -h6 { - font-size: .75em; - margin: 1.67em 0; -} - -h1, h2, h3, h4, h5, h6, strong { - font-weight: bold; -} - -em { - font-style: italic; -} - -pre { - white-space: pre; -} - -hr { - border-width: 1px; -} - -table { - box-sizing: border-box; - display: table; -} - -tr { - box-sizing: border-box; - display: table-row; -} - -td { - box-sizing: border-box; - display: table-cell; -} - -col { - box-sizing: border-box; - display: table-column; -} - -colgroup { - display: table-column-group; -} - -thead, tbody, tfoot { - display: table-row-group; -} - -tabset tabs { - display: block; -} - -input { - background-color: white; - color: black; - cursor: text; -} diff --git a/assets/ui/main_ui.rml b/assets/ui/main_ui.rml deleted file mode 100644 index 9dc12f1..0000000 --- a/assets/ui/main_ui.rml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - -

Meow!

- -
diff --git a/shuriken.yaml b/shuriken.yaml index 0a07b3f..8dd0a71 100644 --- a/shuriken.yaml +++ b/shuriken.yaml @@ -1,22 +1,10 @@ defaults: cpp_standard: c++23 - cpp_flags: >- - -Wall - -Wextra - -pedantic - -Ivendor/RmlUi/Include - linker_args: >- - -lSDL3 - -lSDL3_image - -lfreetype - vendor/RmlUi/Build/librmlui_debugger.a - vendor/RmlUi/Build/librmlui.a - -enable_import_std: true + cpp_flags: -Wall -Wextra -Werror -pedantic + linker_args: -lSDL3 -lSDL3_image default_target: linux targets: linux: output_file: rutile_game - # TODO: In a release build, set -DNDEBUG diff --git a/src/main.cpp b/src/main.cpp index a6e0353..cdfe69e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,30 +1,27 @@ -import rutile.app; +import sdl_app; #define SDL_MAIN_USE_CALLBACKS #include -SDL_AppResult SDL_AppInit(void** appstate, const int /*argc*/, char** /*argv*/) +SDL_AppResult SDL_AppInit(void** appstate, int /*argc*/, char** /*argv*/) { - auto* app = new rutile::App; - *appstate = app; - return static_cast(app->initialize()); -} - -SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) -{ - auto* app = static_cast(appstate); - return static_cast(app->handle_event(*event)); + *appstate = new AppState; + return sdl_app_init(static_cast(*appstate)); } SDL_AppResult SDL_AppIterate(void* appstate) { - auto* app = static_cast(appstate); - return static_cast(app->iterate()); + return sdl_app_iterate(static_cast(appstate)); } -void SDL_AppQuit(void* appstate, const SDL_AppResult /*result*/) +SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) { - const auto* app = static_cast(appstate); - app->shutdown(); - delete app; + return sdl_app_event(static_cast(appstate), event); +} + +void SDL_AppQuit(void* appstate, SDL_AppResult /*result*/) +{ + const auto* game_app_state = static_cast(appstate); + sdl_app_shutdown(game_app_state); + delete game_app_state; } diff --git a/src/rutile/app.cppm b/src/rutile/app.cppm deleted file mode 100644 index 37deff0..0000000 --- a/src/rutile/app.cppm +++ /dev/null @@ -1,86 +0,0 @@ -module; - -#include - -export module rutile.app; - -import std; -import rutile.config; -import rutile.core.engine; -import rutile.game.game; -import wrappers.sdl; - -export namespace rutile -{ - class App - { - std::unique_ptr engine_{nullptr}; - std::unique_ptr game_{nullptr}; - - public: - App() = default; - - sdl::AppResult initialize() - { - try { - // Set SDL application metadata - sdl::set_app_metadata_property(SDL_PROP_APP_METADATA_NAME_STRING, config::app_name); - sdl::set_app_metadata_property(SDL_PROP_APP_METADATA_VERSION_STRING, config::app_version); - sdl::set_app_metadata_property(SDL_PROP_APP_METADATA_IDENTIFIER_STRING, config::app_identifier); - sdl::set_app_metadata_property(SDL_PROP_APP_METADATA_CREATOR_STRING, config::app_creator); - sdl::set_app_metadata_property(SDL_PROP_APP_METADATA_COPYRIGHT_STRING, config::app_copyright); - sdl::set_app_metadata_property(SDL_PROP_APP_METADATA_URL_STRING, config::app_url); - sdl::set_app_metadata_property(SDL_PROP_APP_METADATA_TYPE_STRING, "game"); - - // Initialize SDL subsystems - sdl::initialize_sdl(sdl::InitFlags::Video | sdl::InitFlags::Events); - - // Create engine (includes window and renderer) and game state - engine_ = Engine::create(); - game_ = Game::create(*engine_); - } - catch (const std::runtime_error& e) { - std::cerr << "Unhandled exception during initialization: " << e.what() << '\n'; - return sdl::AppResult::Failure; - } - - return sdl::AppResult::Continue; - } - - sdl::AppResult handle_event(const sdl::Event& event) - { - try { - if (!engine_->handle_event(event)) { - game_->handle_event(event); - } - } - catch (const std::runtime_error& e) { - std::cerr << "Unhandled exception during event handling: " << e.what() << '\n'; - return sdl::AppResult::Failure; - } - - return engine_->keep_running() ? sdl::AppResult::Continue : sdl::AppResult::Success; - } - - sdl::AppResult iterate() - { - try { - engine_->update(); - game_->update(); - game_->render(); - } - catch (const std::runtime_error& e) { - std::cerr << "Unhandled exception during updating: " << e.what() << '\n'; - return sdl::AppResult::Failure; - } - - return engine_->keep_running() ? sdl::AppResult::Continue : sdl::AppResult::Success; - } - - void shutdown() const - { - engine_->shutdown(); - game_->shutdown(); - } - }; -} diff --git a/src/rutile/config.cppm b/src/rutile/config.cppm deleted file mode 100644 index 2e36783..0000000 --- a/src/rutile/config.cppm +++ /dev/null @@ -1,36 +0,0 @@ -module; - -#ifdef NDEBUG -#define DEBUG_BOOL false -#else -#define DEBUG_BOOL true -#endif - -export module rutile.config; - -import std; - -export namespace rutile::config -{ - constexpr auto debug = DEBUG_BOOL; - - constexpr auto app_name = "Rutile Game Prototype"; - constexpr auto app_version = "0.0.1-dev"; - constexpr auto app_identifier = "dev.binarydiv.rutile_game"; - constexpr auto app_creator = "binaryDiv"; - constexpr auto app_copyright = "Copyright (c) 2025 binaryDiv"; - constexpr auto app_url = "https://git.0xbd.space/binaryDiv/rutile-game"; - - constexpr auto window_width = 640; - constexpr auto window_height = 480; - - constexpr auto get_window_title() - { - if constexpr (debug) { - return std::format("{} ({}) [DEBUG]", app_name, app_version); - } - else { - return std::format("{} ({})", app_name, app_version); - } - } -} diff --git a/src/rutile/core/drawing/sprite.cppm b/src/rutile/core/drawing/sprite.cppm deleted file mode 100644 index 1158bde..0000000 --- a/src/rutile/core/drawing/sprite.cppm +++ /dev/null @@ -1,82 +0,0 @@ -module; - -#include - -export module rutile.core.drawing.sprite; - -import std; -import rutile.core.render_server; -import wrappers.sdl; - -export namespace rutile -{ - class Sprite - { - // Texture ID (managed by TextureManager in RenderServer) - TextureID texture_id_; - - // Texture source boundaries: which part of the texture to render - sdl::FRect texture_boundaries_; - - // Texture offset: static offset that is added to the render position (BEFORE scale is applied) - sdl::FPoint texture_offset_{0, 0}; - - // Texture scale: factors applied to width/height (AFTER offset is applied) - // TODO: Proper vector class? - sdl::FPoint texture_scale_{1, 1}; - - // Sprite position: where to render the sprite (world coordinates) - sdl::FPoint position_{0, 0}; - - public: - explicit Sprite( - const TextureID texture_id, - const sdl::FRect texture_boundaries, - const sdl::FPoint position, - const sdl::FPoint texture_offset = {0, 0}, - const sdl::FPoint texture_scale = {1, 1} - ) - : texture_id_{texture_id}, - texture_boundaries_{texture_boundaries}, - texture_offset_{texture_offset}, - texture_scale_{texture_scale}, - position_{position} - { - assert(texture_boundaries_.w > 0 && texture_boundaries_.h > 0); - } - - static Sprite create_from_texture( - RenderServer& render_server, - const std::string& filename, - std::optional texture_boundaries, - auto... args - ) - { - const TextureID texture_id = render_server.load_texture(filename); - - if (!texture_boundaries.has_value()) { - const sdl::Texture& texture = render_server.get_texture(texture_id); - texture_boundaries = texture.get_boundaries(); - } - - return Sprite(texture_id, texture_boundaries.value(), args...); - } - - void set_position(const sdl::FPoint new_position) - { - position_ = new_position; - } - - void draw(const RenderServer& render_server) const - { - const sdl::FRect dest_rect{ - position_.x + (texture_scale_.x * texture_offset_.x), - position_.y + (texture_scale_.y * texture_offset_.y), - texture_scale_.x * texture_boundaries_.w, - texture_scale_.y * texture_boundaries_.h, - }; - - render_server.render_texture(texture_id_, &texture_boundaries_, &dest_rect); - } - }; -} diff --git a/src/rutile/core/drawing/tile_map.cppm b/src/rutile/core/drawing/tile_map.cppm deleted file mode 100644 index de66b2c..0000000 --- a/src/rutile/core/drawing/tile_map.cppm +++ /dev/null @@ -1,108 +0,0 @@ -module; - -#include - -export module rutile.core.drawing.tile_map; - -import std; -import rutile.core.drawing.tile_set; -import rutile.core.render_server; -import wrappers.sdl; - -export namespace rutile -{ - // Restrict map size to 16 bit per dimension, so that width*height still fits within size_t - using MapCoord = std::uint16_t; - - class TileMap - { - // Tile set ID - // TODO: Needed later when we centralize tile set management using a ResourceManager - [[maybe_unused]] - TileSetID tile_set_id_; - - // Tile size - int tile_size_; - - // Map boundaries - MapCoord width_; - MapCoord height_; - - // Tile map origin in world coordinates - sdl::FPoint position_{0, 0}; - - // Tile map data: Tiles in a continuous array - std::unique_ptr tiles_{nullptr}; - - public: - explicit TileMap( - const TileSetID tile_set_id, - const int tile_size, - const MapCoord width, - const MapCoord height, - const sdl::FPoint position = {0, 0} - ) - : tile_set_id_(tile_set_id), - tile_size_(tile_size), - width_(width), - height_(height), - position_{position} - { - // TODO: This maybe shouldn't be an assert... - assert(width_ > 0 && height_ > 0); - tiles_ = std::make_unique( - static_cast(width * height) - ); - } - - constexpr std::size_t map_coords_to_index(const MapCoord x, const MapCoord y) const - { - // TODO: Properly handle x<0, y<0, x>=width, y>=height - assert(x < width_ && y < height_); - return y * width_ + x; - } - - constexpr TileID get_tile(const MapCoord x, const MapCoord y) const - { - return tiles_[map_coords_to_index(x, y)]; - } - - constexpr void set_tile(const MapCoord x, const MapCoord y, const TileID tile_id) - { - tiles_[map_coords_to_index(x, y)] = tile_id; - } - - // TODO: Should we get the TileSet from the RenderServer, similar to how we get textures? - void draw(const RenderServer& render_server, const TileSet& tile_set) const - { - // Get tile set texture - const sdl::Texture& tile_set_texture = render_server.get_texture(tile_set.get_texture_id()); - - // TODO: Implement more efficiently (we could just iterate over tiles_ directly) - for (MapCoord y = 0; y < height_; y++) { - for (MapCoord x = 0; x < width_; x++) { - const int tile_index = get_tile(x, y); - - // Ignore tiles with ID 0 - if (tile_index == 0) { - continue; - } - - const auto [tile_atlas_src_rect] = tile_set.get_tile_definition(tile_index); - const sdl::FRect tile_dest_rect{ - position_.x + x * tile_size_, - position_.y + y * tile_size_, - static_cast(tile_size_), - static_cast(tile_size_), - }; - - render_server.render_texture( - tile_set_texture, - &tile_atlas_src_rect, - &tile_dest_rect - ); - } - } - } - }; -} diff --git a/src/rutile/core/drawing/tile_set.cppm b/src/rutile/core/drawing/tile_set.cppm deleted file mode 100644 index ddad86c..0000000 --- a/src/rutile/core/drawing/tile_set.cppm +++ /dev/null @@ -1,92 +0,0 @@ -module; - -#include - -export module rutile.core.drawing.tile_set; - -import std; -import rutile.core.render_server; -import wrappers.sdl; - -export namespace rutile -{ - using TileSetID = unsigned int; - using TileID = unsigned int; - - struct TileDefinition - { - sdl::FRect atlas_src_rect; - }; - - class TileSet - { - // Texture ID of tileset atlas texture - TextureID texture_id_; - - // Size of each tile in pixels - // TODO: Do we need this? - // int tile_size_; - - // Tile definitions: Maps tile ID to a struct describing the tile, e.g. the texture source boundaries, - // collision data, etc. - std::vector tile_definitions_; - - public: - TileSet() = delete; - - explicit TileSet( - const TextureID texture_id, - std::vector tile_definitions - ) - : texture_id_{texture_id}, - tile_definitions_{std::move(tile_definitions)} - {} - - static TileSet create_from_texture( - RenderServer& render_server, - const std::string& filename, - const int tile_size, - const std::vector& tile_atlas_coords - ) - { - const TextureID texture_id = render_server.load_texture(filename); - const sdl::Texture& texture = render_server.get_texture(texture_id); - - assert(tile_size > 0); - // TODO: These probably shouldn't be asserts... - assert(texture.get_width() % tile_size == 0); - assert(texture.get_height() % tile_size == 0); - - std::vector tile_definitions; - - // TODO: Should we automatically create tile 0 (no tile) instead of listing it in tile_atlas_coords? - for (const auto& [x, y] : tile_atlas_coords) { - const TileDefinition tile{ - { - static_cast(x * tile_size), - static_cast(y * tile_size), - static_cast(tile_size), - static_cast(tile_size), - } - }; - - // TODO: These probably shouldn't be asserts... - assert(tile.atlas_src_rect.x + tile.atlas_src_rect.w <= texture.get_width()); - assert(tile.atlas_src_rect.y + tile.atlas_src_rect.h <= texture.get_height()); - tile_definitions.push_back(tile); - } - - return TileSet(texture_id, tile_definitions); - } - - constexpr TextureID get_texture_id() const - { - return texture_id_; - } - - constexpr const TileDefinition& get_tile_definition(const TileID tile_id) const - { - return tile_definitions_.at(tile_id); - } - }; -} diff --git a/src/rutile/core/engine.cppm b/src/rutile/core/engine.cppm deleted file mode 100644 index 984f323..0000000 --- a/src/rutile/core/engine.cppm +++ /dev/null @@ -1,118 +0,0 @@ -module; - -#include - -export module rutile.core.engine; - -import std; -import rutile.config; -import rutile.core.render_server; -import rutile.core.ui.ui_server; -import wrappers.sdl; - -export namespace rutile -{ - class Engine - { - // Whether this class is currently instantiated (to prevent multiple instances) - static bool instantiated_; - - // If this is set to false, the application will exit - bool keep_running_ = true; - - sdl::Window window_; - RenderServer render_server_; - UIServer ui_server_; - - // Private constructor - Engine(sdl::Window&& window, sdl::Renderer&& renderer) - : window_{std::move(window)}, - render_server_{std::move(renderer)}, - ui_server_{render_server_.get_renderer(), window_} - { - instantiated_ = true; - } - - public: - Engine() = delete; - - // No copy or move operations - Engine(const Engine&) = delete; - Engine& operator=(const Engine&) = delete; - Engine(Engine&&) = delete; - Engine& operator=(Engine&&) = delete; - - ~Engine() - { - instantiated_ = false; - } - - static std::unique_ptr create() - { - // Prevent the class from being instantiated multiple times - assert(!instantiated_); - - auto [sdl_window, sdl_renderer] = sdl::create_window_and_renderer( - config::get_window_title(), - config::window_width, - config::window_height, - 0 - ); - - // NOLINTBEGIN: "Allocated memory is leaked" - return std::unique_ptr{ - new Engine{ - std::move(sdl_window), - std::move(sdl_renderer) - } - }; - // NOLINTEND - } - - bool keep_running() const - { - return keep_running_; - } - - sdl::Window& get_window() - { - return window_; - } - - RenderServer& get_render_server() - { - return render_server_; - } - - UIServer& get_ui_server() - { - return ui_server_; - } - - // Handles an SDL event. Returns true if the event has been handled. - bool handle_event(const sdl::Event& event) - { - if (event.type == sdl::EventTypes::Quit) { - // Exit the application - keep_running_ = false; - return true; - } - - if (ui_server_.handle_event(event)) { - return true; - } - - return false; - } - - void update() - { - } - - void shutdown() - { - } - }; - - bool Engine::instantiated_ = false; -} diff --git a/src/rutile/core/render_server.cppm b/src/rutile/core/render_server.cppm deleted file mode 100644 index 7366b13..0000000 --- a/src/rutile/core/render_server.cppm +++ /dev/null @@ -1,89 +0,0 @@ -module; - -export module rutile.core.render_server; - -import std; -import rutile.core.resource_manager; -import rutile.core.texture_loader; -import wrappers.sdl; - -export namespace rutile -{ - using TextureID = unsigned int; - using TextureManager = ResourceManager; - - class RenderServer - { - sdl::Renderer renderer_; - TextureLoader texture_loader_; - TextureManager texture_manager_; - - public: - RenderServer() = delete; - - explicit RenderServer(sdl::Renderer&& renderer) - : renderer_{std::move(renderer)}, - texture_loader_{renderer_}, - texture_manager_{texture_loader_} - { - renderer_.set_vsync(1); - } - - // No copy or move operations - RenderServer(const RenderServer&) = delete; - RenderServer& operator=(const RenderServer&) = delete; - RenderServer(RenderServer&&) = delete; - RenderServer& operator=(RenderServer&&) = delete; - - ~RenderServer() = default; - - constexpr sdl::Renderer& get_renderer() - { - return renderer_; - } - - /** - * Load a texture from a file (if not loaded yet) and return its texture ID. - */ - constexpr TextureID load_texture(const std::string& filename) - { - return texture_manager_.load_resource_by_name(filename); - } - - /** - * Get a reference to a texture by its texture ID. - */ - constexpr const sdl::Texture& get_texture(const TextureID texture_id) const - { - return texture_manager_.get_resource(texture_id); - } - - void start_frame() const - { - renderer_.clear(); - } - - void finish_frame() const - { - renderer_.present(); - } - - void render_texture( - const TextureID texture_id, - const sdl::FRect* src_rect, - const sdl::FRect* dest_rect - ) const - { - render_texture(get_texture(texture_id), src_rect, dest_rect); - } - - constexpr void render_texture( - const sdl::Texture& texture, - const sdl::FRect* src_rect, - const sdl::FRect* dest_rect - ) const - { - renderer_.render_texture(texture, src_rect, dest_rect); - } - }; -} diff --git a/src/rutile/core/resource_manager.cppm b/src/rutile/core/resource_manager.cppm deleted file mode 100644 index b73f184..0000000 --- a/src/rutile/core/resource_manager.cppm +++ /dev/null @@ -1,120 +0,0 @@ -module; - -#include - -export module rutile.core.resource_manager; - -import std; - -export namespace rutile -{ - template - concept IsResourceLoader = requires(ResourceLoaderType loader, const std::string& name) - { - { loader.load_resource(name) } -> std::same_as; - }; - - template < - typename ResourceIDType, - typename ResourceType, - IsResourceLoader ResourceLoaderType - > - class ResourceManager - { - // Resource loader - ResourceLoaderType& resource_loader_; - - // Registry of loaded resources, array index is resource ID - std::vector resource_registry_; - - // Mapping of all loaded resources from (file) name to resource ID - std::map resource_name_map_; - - public: - ResourceManager() = delete; - - explicit ResourceManager(ResourceLoaderType& resource_loader) - : resource_loader_{resource_loader} - {} - - // No copy or move operations - ResourceManager(const ResourceManager&) = delete; - ResourceManager& operator=(const ResourceManager&) = delete; - ResourceManager(ResourceManager&&) = delete; - ResourceManager& operator=(ResourceManager&&) = delete; - - ~ResourceManager() = default; - - /** - * Adds a new resource to the registry and returns its ID. - */ - ResourceIDType add_resource(ResourceType&& resource) - { - // Add resource to end of vector - resource_registry_.push_back(std::move(resource)); - - // Return the index of the newly added resource - return static_cast(resource_registry_.size() - 1); - } - - /** - * Adds a new resource to the registry, associates the name with it and returns its ID. - */ - ResourceIDType add_resource(ResourceType&& resource, const std::string& name) - { - // Add resource - ResourceIDType resource_id = add_resource(std::move(resource)); - - // Associate name with resource ID for future access - resource_name_map_.emplace(name, resource_id); - - return resource_id; - } - - /** - * Gets the resource ID for a given resource name, or `std::nullopt` if the resource was not found. - */ - std::optional get_resource_id_by_name(const std::string& name) const - { - // Check if resource is already loaded - if ( - const auto search = resource_name_map_.find(name); - search != resource_name_map_.end() - ) { - // Return resource ID - return search->second; - } - - return std::nullopt; - } - - /** - * Loads a resource (e.g. from a file) and adds it to the registry if it isn't loaded yet. - * Returns the resource ID. - */ - ResourceIDType load_resource_by_name(const std::string& name) - { - // Check if resource is already loaded - if ( - auto resource_id = get_resource_id_by_name(name); - resource_id.has_value() - ) { - return resource_id.value(); - } - - // Load resource and add it to the registry - return add_resource(resource_loader_.load_resource(name), name); - } - - /** - * Returns a reference to the resource with the given ID. - * The reference is not guaranteed to be valid after adding new resources to the registry. - * Assumes that the resource ID is valid. - */ - const ResourceType& get_resource(const ResourceIDType resource_id) const - { - assert(resource_id < resource_registry_.size()); - return resource_registry_[resource_id]; - } - }; -} diff --git a/src/rutile/core/texture_loader.cppm b/src/rutile/core/texture_loader.cppm deleted file mode 100644 index 78cb3df..0000000 --- a/src/rutile/core/texture_loader.cppm +++ /dev/null @@ -1,34 +0,0 @@ -module; - -export module rutile.core.texture_loader; - -import std; -import rutile.core.resource_manager; -import wrappers.sdl; -import wrappers.sdl_image; - -export namespace rutile -{ - class TextureLoader - { - sdl::Renderer& renderer_; - - public: - explicit TextureLoader(sdl::Renderer& renderer) - : renderer_{renderer} - {} - - // No copy or move operations (reference) - TextureLoader(const TextureLoader&) = delete; - TextureLoader& operator=(const TextureLoader&) = delete; - TextureLoader(TextureLoader&&) = delete; - TextureLoader& operator=(TextureLoader&&) = delete; - - ~TextureLoader() = default; - - sdl::Texture load_resource(const std::string& filename) const - { - return sdl_image::load_texture(renderer_, filename); - } - }; -} diff --git a/src/rutile/core/ui/backend/rmlui_input_handler.cppm b/src/rutile/core/ui/backend/rmlui_input_handler.cppm deleted file mode 100644 index 2dedff2..0000000 --- a/src/rutile/core/ui/backend/rmlui_input_handler.cppm +++ /dev/null @@ -1,275 +0,0 @@ -module; - -export module rutile.core.ui.backend.rmlui_input_handler; - -import wrappers.rmlui.core; -import wrappers.sdl; - -export namespace rutile -{ - class RmlUiInputHandler - { - sdl::Window& window_; - - public: - explicit RmlUiInputHandler(sdl::Window& window) - : window_{window} - {} - - static Rml::Input::KeyIdentifier convert_key(const int sdl_key) - { - namespace Key = sdl::Key; - - // clang-format off - switch (sdl_key) { - case Key::Unknown: return Rml::Input::KI_UNKNOWN; - case Key::Escape: return Rml::Input::KI_ESCAPE; - case Key::Space: return Rml::Input::KI_SPACE; - case Key::Num0: return Rml::Input::KI_0; - case Key::Num1: return Rml::Input::KI_1; - case Key::Num2: return Rml::Input::KI_2; - case Key::Num3: return Rml::Input::KI_3; - case Key::Num4: return Rml::Input::KI_4; - case Key::Num5: return Rml::Input::KI_5; - case Key::Num6: return Rml::Input::KI_6; - case Key::Num7: return Rml::Input::KI_7; - case Key::Num8: return Rml::Input::KI_8; - case Key::Num9: return Rml::Input::KI_9; - case Key::A: return Rml::Input::KI_A; - case Key::B: return Rml::Input::KI_B; - case Key::C: return Rml::Input::KI_C; - case Key::D: return Rml::Input::KI_D; - case Key::E: return Rml::Input::KI_E; - case Key::F: return Rml::Input::KI_F; - case Key::G: return Rml::Input::KI_G; - case Key::H: return Rml::Input::KI_H; - case Key::I: return Rml::Input::KI_I; - case Key::J: return Rml::Input::KI_J; - case Key::K: return Rml::Input::KI_K; - case Key::L: return Rml::Input::KI_L; - case Key::M: return Rml::Input::KI_M; - case Key::N: return Rml::Input::KI_N; - case Key::O: return Rml::Input::KI_O; - case Key::P: return Rml::Input::KI_P; - case Key::Q: return Rml::Input::KI_Q; - case Key::R: return Rml::Input::KI_R; - case Key::S: return Rml::Input::KI_S; - case Key::T: return Rml::Input::KI_T; - case Key::U: return Rml::Input::KI_U; - case Key::V: return Rml::Input::KI_V; - case Key::W: return Rml::Input::KI_W; - case Key::X: return Rml::Input::KI_X; - case Key::Y: return Rml::Input::KI_Y; - case Key::Z: return Rml::Input::KI_Z; - case Key::Semicolon: return Rml::Input::KI_OEM_1; - case Key::Plus: return Rml::Input::KI_OEM_PLUS; - case Key::Comma: return Rml::Input::KI_OEM_COMMA; - case Key::Minus: return Rml::Input::KI_OEM_MINUS; - case Key::Period: return Rml::Input::KI_OEM_PERIOD; - case Key::Slash: return Rml::Input::KI_OEM_2; - case Key::Grave: return Rml::Input::KI_OEM_3; - case Key::LeftBracket: return Rml::Input::KI_OEM_4; - case Key::Backslash: return Rml::Input::KI_OEM_5; - case Key::RightBracket: return Rml::Input::KI_OEM_6; - case Key::DblApostrophe: return Rml::Input::KI_OEM_7; - case Key::Numpad0: return Rml::Input::KI_NUMPAD0; - case Key::Numpad1: return Rml::Input::KI_NUMPAD1; - case Key::Numpad2: return Rml::Input::KI_NUMPAD2; - case Key::Numpad3: return Rml::Input::KI_NUMPAD3; - case Key::Numpad4: return Rml::Input::KI_NUMPAD4; - case Key::Numpad5: return Rml::Input::KI_NUMPAD5; - case Key::Numpad6: return Rml::Input::KI_NUMPAD6; - case Key::Numpad7: return Rml::Input::KI_NUMPAD7; - case Key::Numpad8: return Rml::Input::KI_NUMPAD8; - case Key::Numpad9: return Rml::Input::KI_NUMPAD9; - case Key::NumpadEnter: return Rml::Input::KI_NUMPADENTER; - case Key::NumpadMultiply: return Rml::Input::KI_MULTIPLY; - case Key::NumpadPlus: return Rml::Input::KI_ADD; - case Key::NumpadMinus: return Rml::Input::KI_SUBTRACT; - case Key::NumpadPeriod: return Rml::Input::KI_DECIMAL; - case Key::NumpadDivide: return Rml::Input::KI_DIVIDE; - case Key::NumpadEquals: return Rml::Input::KI_OEM_NEC_EQUAL; - case Key::Backspace: return Rml::Input::KI_BACK; - case Key::Tab: return Rml::Input::KI_TAB; - case Key::Clear: return Rml::Input::KI_CLEAR; - case Key::Return: return Rml::Input::KI_RETURN; - case Key::Pause: return Rml::Input::KI_PAUSE; - case Key::CapsLock: return Rml::Input::KI_CAPITAL; - case Key::PageUp: return Rml::Input::KI_PRIOR; - case Key::PageDown: return Rml::Input::KI_NEXT; - case Key::End: return Rml::Input::KI_END; - case Key::Home: return Rml::Input::KI_HOME; - case Key::Left: return Rml::Input::KI_LEFT; - case Key::Up: return Rml::Input::KI_UP; - case Key::Right: return Rml::Input::KI_RIGHT; - case Key::Down: return Rml::Input::KI_DOWN; - case Key::Insert: return Rml::Input::KI_INSERT; - case Key::Delete: return Rml::Input::KI_DELETE; - case Key::Help: return Rml::Input::KI_HELP; - case Key::F1: return Rml::Input::KI_F1; - case Key::F2: return Rml::Input::KI_F2; - case Key::F3: return Rml::Input::KI_F3; - case Key::F4: return Rml::Input::KI_F4; - case Key::F5: return Rml::Input::KI_F5; - case Key::F6: return Rml::Input::KI_F6; - case Key::F7: return Rml::Input::KI_F7; - case Key::F8: return Rml::Input::KI_F8; - case Key::F9: return Rml::Input::KI_F9; - case Key::F10: return Rml::Input::KI_F10; - case Key::F11: return Rml::Input::KI_F11; - case Key::F12: return Rml::Input::KI_F12; - case Key::F13: return Rml::Input::KI_F13; - case Key::F14: return Rml::Input::KI_F14; - case Key::F15: return Rml::Input::KI_F15; - case Key::NumLockClear: return Rml::Input::KI_NUMLOCK; - case Key::ScrollLock: return Rml::Input::KI_SCROLL; - case Key::LShift: return Rml::Input::KI_LSHIFT; - case Key::RShift: return Rml::Input::KI_RSHIFT; - case Key::LCtrl: return Rml::Input::KI_LCONTROL; - case Key::RCtrl: return Rml::Input::KI_RCONTROL; - case Key::LAlt: return Rml::Input::KI_LMENU; - case Key::RAlt: return Rml::Input::KI_RMENU; - case Key::LGui: return Rml::Input::KI_LMETA; - case Key::RGui: return Rml::Input::KI_RMETA; - default: return Rml::Input::KI_UNKNOWN; - } - // clang-format on - } - - static int convert_mouse_button(const sdl::MouseButtonIndex sdl_mouse_button) - { - switch (sdl_mouse_button) { - case sdl::MouseButtons::Left: return 0; - case sdl::MouseButtons::Right: return 1; - case sdl::MouseButtons::Middle: return 2; - default: return 3; - } - } - - static int get_key_modifiers() - { - const sdl::KeyModState sdl_mods = sdl::get_key_mod_state(); - int retval = 0; - - if (sdl_mods & sdl::KeyMods::Ctrl) { - retval |= Rml::Input::KM_CTRL; - } - if (sdl_mods & sdl::KeyMods::Shift) { - retval |= Rml::Input::KM_SHIFT; - } - if (sdl_mods & sdl::KeyMods::Alt) { - retval |= Rml::Input::KM_ALT; - } - if (sdl_mods & sdl::KeyMods::NumLock) { - retval |= Rml::Input::KM_NUMLOCK; - } - if (sdl_mods & sdl::KeyMods::CapsLock) { - retval |= Rml::Input::KM_CAPSLOCK; - } - - return retval; - } - - static Rml::TouchList convert_touch_event(const Rml::Context& context, const sdl::Event& ev) - { - const Rml::Vector2f position = Rml::Vector2f{ev.tfinger.x, ev.tfinger.y} - * Rml::Vector2f{context.GetDimensions()}; - return {Rml::Touch{static_cast(ev.tfinger.fingerID), position}}; - } - - /** - * Handle input events and forward them to RmlUi if relevant. - * Returns true if the event was consumed (inverting the return values of RmlUi, where true means the event - * was NOT consumed). - */ - bool handle_input_event(Rml::Context& context, const sdl::Event& ev) const - { - // Return value of RmlUi event handlers, true if the event was NOT consumed. - // Result will be inverted at the end of this method to return true if the event WAS consumed. - bool result = true; - - switch (ev.type) { - case sdl::EventTypes::MouseMotion: { - const float pixel_density = window_.get_pixel_density(); - result = context.ProcessMouseMove( - static_cast(ev.motion.x * pixel_density), - static_cast(ev.motion.y * pixel_density), - get_key_modifiers() - ); - break; - } - - case sdl::EventTypes::MouseButtonDown: - result = context.ProcessMouseButtonDown( - convert_mouse_button(ev.button.button), - get_key_modifiers() - ); - sdl::capture_mouse(true); - break; - - case sdl::EventTypes::MouseButtonUp: - sdl::capture_mouse(false); - result = context.ProcessMouseButtonUp( - convert_mouse_button(ev.button.button), get_key_modifiers() - ); - break; - - case sdl::EventTypes::MouseWheel: - result = context.ProcessMouseWheel( - Rml::Vector2f{ev.wheel.x, -ev.wheel.y}, - get_key_modifiers() - ); - break; - - case sdl::EventTypes::KeyDown: - result = context.ProcessKeyDown(convert_key(ev.key.key), get_key_modifiers()); - - if (ev.key.key == sdl::Key::Return || ev.key.key == sdl::Key::NumpadEnter) { - result &= context.ProcessTextInput('\n'); - } - break; - - case sdl::EventTypes::KeyUp: - result = context.ProcessKeyUp(convert_key(ev.key.key), get_key_modifiers()); - break; - - case sdl::EventTypes::TextInput: - result = context.ProcessTextInput(Rml::String(&ev.text.text[0])); - break; - - case sdl::EventTypes::FingerDown: - result = context.ProcessTouchStart(convert_touch_event(context, ev), get_key_modifiers()); - break; - - case sdl::EventTypes::FingerMotion: - result = context.ProcessTouchMove(convert_touch_event(context, ev), get_key_modifiers()); - break; - - case sdl::EventTypes::FingerUp: - result = context.ProcessTouchEnd(convert_touch_event(context, ev), get_key_modifiers()); - break; - - case sdl::EventTypes::WindowMouseLeave: - context.ProcessMouseLeave(); - break; - - case sdl::EventTypes::WindowPixelSizeChanged: - context.SetDimensions( - Rml::Vector2i{ev.window.data1, ev.window.data2} - ); - break; - - case sdl::EventTypes::WindowDisplayScaleChanged: - context.SetDensityIndependentPixelRatio(window_.get_display_scale()); - break; - - default: - break; - } - - // Invert result to be consistent with the event handling of this app. Return true if the event was - // consumed, i.e. no further event handling is necessary. - return !result; - } - }; -} diff --git a/src/rutile/core/ui/backend/rmlui_render_interface.cppm b/src/rutile/core/ui/backend/rmlui_render_interface.cppm deleted file mode 100644 index 28724c9..0000000 --- a/src/rutile/core/ui/backend/rmlui_render_interface.cppm +++ /dev/null @@ -1,198 +0,0 @@ -// RmlUi RenderInterface implementation for SDL using SDLrenderer. -// Based on RmlUi example code: vendor/RmlUi/Backends/RmlUi_Renderer_SDL.{cpp,h} - -module; - -#include -#include - -export module rutile.core.ui.backend.rmlui_render_interface; - -import std; -import wrappers.rmlui.core; -import wrappers.sdl; -import wrappers.sdl_image; - -export namespace rutile -{ - class RmlUiRenderInterface final : public Rml::RenderInterface - { - struct GeometryView - { - Rml::Span vertices; - Rml::Span indices; - }; - - sdl::Renderer& renderer_; - SDL_BlendMode blend_mode_ = {}; - sdl::Rect rect_scissor_ = {}; - bool scissor_region_enabled_ = false; - - public: - explicit RmlUiRenderInterface(sdl::Renderer& renderer) - : renderer_{renderer} - { - // RmlUi serves vertex colors and textures with premultiplied alpha, set the blend mode accordingly. - // Equivalent to glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA). - blend_mode_ = SDL_ComposeCustomBlendMode( - SDL_BLENDFACTOR_ONE, - SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, - SDL_BLENDOPERATION_ADD, - SDL_BLENDFACTOR_ONE, - SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, - SDL_BLENDOPERATION_ADD - ); - } - - /** - * Sets up OpenGL states for taking rendering commands from RmlUi. - */ - void prepare_render() const - { - renderer_.set_draw_blend_mode(blend_mode_); - } - - // -- Inherited from Rml::RenderInterface - - Rml::CompiledGeometryHandle CompileGeometry( - const Rml::Span vertices, - const Rml::Span indices - ) override - { - // RmlUi handles lifetime and calls ReleaseGeometry to delete the object - GeometryView* data = new GeometryView{vertices, indices}; // NOLINT: "Allocated memory is leaked" - return reinterpret_cast(data); - } - - void ReleaseGeometry(const Rml::CompiledGeometryHandle geometry) override - { - delete reinterpret_cast(geometry); - } - - void RenderGeometry( - const Rml::CompiledGeometryHandle handle, - const Rml::Vector2f translation, - const Rml::TextureHandle texture - ) override - { - const GeometryView* geometry = reinterpret_cast(handle); - const Rml::Vertex* vertices = geometry->vertices.data(); - const std::size_t num_vertices = geometry->vertices.size(); - const int* indices = geometry->indices.data(); - const std::size_t num_indices = geometry->indices.size(); - - // Convert RmlUi vertices to SDL vertices - auto sdl_vertices = std::make_unique(num_vertices); - - for (size_t i = 0; i < num_vertices; i++) { - const auto& [position, color, tex_coord] = vertices[i]; - - sdl_vertices[i].position = { - position.x + translation.x, - position.y + translation.y, - }; - sdl_vertices[i].tex_coord = { - tex_coord.x, - tex_coord.y, - }; - sdl_vertices[i].color = { - color.red / 255.f, - color.green / 255.f, - color.blue / 255.f, - color.alpha / 255.f, - }; - } - - renderer_.render_geometry( - reinterpret_cast(texture), - sdl_vertices.get(), - static_cast(num_vertices), - indices, - static_cast(num_indices) - ); - } - - Rml::TextureHandle LoadTexture( - Rml::Vector2i& texture_dimensions, - const Rml::String& source - ) override - { - sdl::Surface surface = sdl_image::load(source.c_str()); - - if (surface.get_format() != SDL_PIXELFORMAT_RGBA32 && surface.get_format() != SDL_PIXELFORMAT_BGRA32) { - surface = surface.convert_format(SDL_PIXELFORMAT_RGBA32); - } - - // Convert colors to premultiplied alpha, which is necessary for correct alpha compositing. - const std::size_t pixels_byte_size = surface.get_width() * surface.get_height() * 4; - Rml::byte* pixels = static_cast(surface.get_raw_pixels()); - - for (size_t i = 0; i < pixels_byte_size; i += 4) { - const Rml::byte alpha = pixels[i + 3]; - for (size_t j = 0; j < 3; ++j) { - pixels[i + j] = static_cast( - static_cast(pixels[i + j]) * static_cast(alpha) / 255 - ); - } - } - - SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer_.get_raw(), surface.get_raw()); - if (texture) { - SDL_SetTextureBlendMode(texture, blend_mode_); - } - - texture_dimensions = {surface.get_width(), surface.get_height()}; - return reinterpret_cast(texture); - } - - Rml::TextureHandle GenerateTexture( - const Rml::Span source, - const Rml::Vector2i source_dimensions - ) override - { - assert( - source.data() && source.size() == static_cast(source_dimensions.x * source_dimensions.y * 4) - ); - - const sdl::Surface surface = sdl::Surface::create_from( - source_dimensions.x, - source_dimensions.y, - SDL_PIXELFORMAT_RGBA32, - // Cast away the const because SDL_CreateSurfaceFrom() expects a void*. - // This is fine as long as we don't modify surface.get_raw_pixels(). - const_cast(source.data()), - source_dimensions.x * 4 - ); - - SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer_.get_raw(), surface.get_raw()); - if (texture) { - SDL_SetTextureBlendMode(texture, blend_mode_); - } - - return reinterpret_cast(texture); - } - - void ReleaseTexture(const Rml::TextureHandle texture_handle) override - { - SDL_DestroyTexture(reinterpret_cast(texture_handle)); - } - - void EnableScissorRegion(const bool enable) override - { - renderer_.set_clip_rect(enable ? &rect_scissor_ : nullptr); - scissor_region_enabled_ = enable; - } - - void SetScissorRegion(const Rml::Rectanglei region) override - { - rect_scissor_.x = region.Left(); - rect_scissor_.y = region.Top(); - rect_scissor_.w = region.Width(); - rect_scissor_.h = region.Height(); - - if (scissor_region_enabled_) { - renderer_.set_clip_rect(&rect_scissor_); - } - } - }; -} diff --git a/src/rutile/core/ui/backend/rmlui_system_interface.cppm b/src/rutile/core/ui/backend/rmlui_system_interface.cppm deleted file mode 100644 index 3f2ac72..0000000 --- a/src/rutile/core/ui/backend/rmlui_system_interface.cppm +++ /dev/null @@ -1,117 +0,0 @@ -// RmlUi SystemInterface implementation for SDL. -// Based on RmlUi example code: vendor/RmlUi/Backends/RmlUi_Platform_SDL.{cpp,h} - -module; - -#include - -export module rutile.core.ui.backend.rmlui_system_interface; - -import std; -import utils.memory; -import wrappers.rmlui.core; -import wrappers.sdl; - -// Shorthand for a unique pointer to an SDL_Cursor -// TODO: Build actual wrapper sdl::Cursor at some point? -using SDL_CursorPtr = std::unique_ptr>; - -export namespace rutile -{ - class RmlUiSystemInterface final : public Rml::SystemInterface - { - sdl::Window& window_; - - SDL_CursorPtr cursor_default; - SDL_CursorPtr cursor_move; - SDL_CursorPtr cursor_pointer; - SDL_CursorPtr cursor_resize; - SDL_CursorPtr cursor_cross; - SDL_CursorPtr cursor_text; - SDL_CursorPtr cursor_unavailable; - - public: - explicit RmlUiSystemInterface(sdl::Window& window) - : window_{window} - { - cursor_default = SDL_CursorPtr{SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT)}; - cursor_move = SDL_CursorPtr{SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_MOVE)}; - cursor_pointer = SDL_CursorPtr{SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_POINTER)}; - cursor_resize = SDL_CursorPtr{SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NWSE_RESIZE)}; - cursor_cross = SDL_CursorPtr{SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR)}; - cursor_text = SDL_CursorPtr{SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_TEXT)}; - cursor_unavailable = SDL_CursorPtr{SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NOT_ALLOWED)}; - } - - // -- Inherited from Rml::SystemInterface - - double GetElapsedTime() override - { - // TODO: Add a shared timer to the engine and use that here. - static const Uint64 start = SDL_GetPerformanceCounter(); - static const double frequency = static_cast(SDL_GetPerformanceFrequency()); - return static_cast(SDL_GetPerformanceCounter() - start) / frequency; - } - - void SetMouseCursor(const Rml::String& cursor_name) override - { - SDL_Cursor* cursor = nullptr; - - if (cursor_name.empty() || cursor_name == "arrow") { - cursor = cursor_default.get(); - } - else if (cursor_name == "move") { - cursor = cursor_move.get(); - } - else if (cursor_name == "pointer") { - cursor = cursor_pointer.get(); - } - else if (cursor_name == "resize") { - cursor = cursor_resize.get(); - } - else if (cursor_name == "cross") { - cursor = cursor_cross.get(); - } - else if (cursor_name == "text") { - cursor = cursor_text.get(); - } - else if (cursor_name == "unavailable") { - cursor = cursor_unavailable.get(); - } - else if (Rml::StringUtilities::StartsWith(cursor_name, "rmlui-scroll")) { - cursor = cursor_move.get(); - } - - if (cursor) { - SDL_SetCursor(cursor); - } - } - - void SetClipboardText(const Rml::String& text) override - { - sdl::set_clipboard_text(text); - } - - void GetClipboardText(Rml::String& text) override - { - text = sdl::get_clipboard_text(); - } - - void ActivateKeyboard(const Rml::Vector2f caret_position, const float line_height) override - { - const sdl::Rect rect{ - static_cast(caret_position.x), - static_cast(caret_position.y), - 1, - static_cast(line_height), - }; - window_.set_text_input_area(&rect, 0); - window_.start_text_input(); - } - - void DeactivateKeyboard() override - { - window_.stop_text_input(); - } - }; -} diff --git a/src/rutile/core/ui/ui_server.cppm b/src/rutile/core/ui/ui_server.cppm deleted file mode 100644 index 3e5d3ab..0000000 --- a/src/rutile/core/ui/ui_server.cppm +++ /dev/null @@ -1,124 +0,0 @@ -module; - -#include - -export module rutile.core.ui.ui_server; - -import std; -import rutile.core.ui.backend.rmlui_render_interface; -import rutile.core.ui.backend.rmlui_system_interface; -import rutile.core.ui.backend.rmlui_input_handler; -import wrappers.rmlui.core; -import wrappers.rmlui.debugger; -import wrappers.sdl; - -export namespace rutile -{ - class UIServer - { - // Whether this class is currently instantiated (to prevent multiple instances because we need to do - // Rml::Initialise() and Rml::Shutdown()). - static bool instantiated_; - - RmlUiSystemInterface system_interface_; - RmlUiRenderInterface render_interface_; - RmlUiInputHandler input_handler_; - - // RmlUi context (non-owning pointer, lifetime managed by library) - Rml::Context* context_; - - public: - explicit UIServer(sdl::Renderer& renderer, sdl::Window& window) - : system_interface_{window}, - render_interface_{renderer}, - input_handler_{window} - { - // Prevent the class from being instantiated multiple times - assert(!instantiated_); - - Rml::SetSystemInterface(&system_interface_); - Rml::SetRenderInterface(&render_interface_); - Rml::Initialise(); - - // Create the main RmlUi context - auto [window_width, window_height] = window.get_size(); - context_ = Rml::CreateContext("main", Rml::Vector2i(window_width, window_height)); - if (!context_) { - throw std::runtime_error("RmlUi: Couldn't create context"); - } - - // TODO: Implement some kind of switch to disable debugger (just use config::debug?) - Rml::Debugger::Initialise(context_); - - instantiated_ = true; - } - - // No copy or move operations - UIServer(const UIServer&) = delete; - UIServer& operator=(const UIServer&) = delete; - UIServer(UIServer&&) = delete; - UIServer& operator=(UIServer&&) = delete; - - ~UIServer() - { - Rml::Shutdown(); - instantiated_ = false; - } - - constexpr Rml::Context& get_context() const - { - // TODO: Can we assume context_ is always non-nullptr? - assert(context_); - return *context_; - } - - // Handles an SDL event. Returns true if the event has been handled. - bool handle_event(const sdl::Event& event) const - { - // Toggle RmlUi debugger with F12 - if (event.type == sdl::EventTypes::KeyDown && event.key.key == sdl::Key::F12) { - Rml::Debugger::SetVisible(!Rml::Debugger::IsVisible()); - return true; - } - - // Let RmlUi handle relevant events - if (input_handler_.handle_input_event(get_context(), event)) { - return true; - } - - return false; - } - - void update() const - { - get_context().Update(); - } - - void render() const - { - render_interface_.prepare_render(); - get_context().Render(); - } - - static void load_font_face(const std::string& file_path) - { - if (!Rml::LoadFontFace(file_path)) { - throw std::runtime_error(std::format("RmlUi: Couldn't load font face \"{}\"", file_path)); - } - } - - // TODO: Can/should we avoid the (non-owning) raw pointers here? - Rml::ElementDocument* load_document(const std::string& file_path) const - { - Rml::ElementDocument* document = get_context().LoadDocument(file_path); - - if (!document) { - throw std::runtime_error(std::format("RmlUi: Couldn't load document \"{}\"", file_path)); - } - - return document; - } - }; - - bool UIServer::instantiated_ = false; -} diff --git a/src/rutile/game/game.cppm b/src/rutile/game/game.cppm deleted file mode 100644 index 9f8ca08..0000000 --- a/src/rutile/game/game.cppm +++ /dev/null @@ -1,161 +0,0 @@ -module; - -#include - -export module rutile.game.game; - -import std; -import rutile.core.drawing.sprite; -import rutile.core.drawing.tile_map; -import rutile.core.drawing.tile_set; -import rutile.core.engine; -import rutile.core.render_server; -import wrappers.rmlui.core; -import wrappers.sdl; -import wrappers.sdl_image; - -export namespace rutile -{ - class Game - { - // Whether this class is currently instantiated (to prevent multiple instances) - static bool instantiated_; - - // Reference to the engine - Engine& engine_; - - // Sprites for testing - Sprite player_sprite_; - std::vector sprites_; - - // Tile set and tile map (TODO: tile set should be moved to resource manager in engine) - TileSet tile_set_; - TileMap tile_map_; - - // Private constructor - explicit Game(Engine& engine) - : engine_(engine), - player_sprite_{ - Sprite::create_from_texture( - engine_.get_render_server(), - "assets/sprites/neocat_64.png", - std::nullopt, - sdl::FPoint{0, 0}, - sdl::FPoint{-32, -32} - ) - }, - tile_set_{ - TileSet::create_from_texture( - engine_.get_render_server(), - "assets/tilesets/terrain.png", - 32, - { - // Tile 0: No tile - {0, 0}, - // Tile 1: Actual first tile in the texture - {0, 0}, - {1, 0}, - {2, 0}, - {3, 0}, - } - ) - }, - tile_map_(0, 32, 16, 12, {50, 50}) - { - // Initialize UI - const auto& ui_server = engine_.get_ui_server(); - ui_server.load_font_face("assets/fonts/LatoLatin-Regular.ttf"); - Rml::ElementDocument* document = ui_server.load_document("assets/ui/main_ui.rml"); - document->Show(); - - // Initialize tile map with example data - for (auto x = 0; x < 10; x++) { - for (auto y = 0; y < 10; y++) { - const auto tile_index = 2 + (x + y) % 2; - tile_map_.set_tile(x, y, tile_index); - } - } - - instantiated_ = true; - } - - public: - Game() = delete; - - // No copy or move operations because we have a reference to the engine - Game(const Game&) = delete; - Game& operator=(const Game&) = delete; - Game(Game&&) = delete; - Game& operator=(Game&&) = delete; - - ~Game() - { - instantiated_ = false; - } - - static std::unique_ptr create(Engine& engine) - { - // Prevent the class from being instantiated multiple times - assert(!instantiated_); - return std::unique_ptr{ - new Game(engine) - }; - } - - // Handles an SDL event. Returns true if the event has been handled. - bool handle_event(const sdl::Event& event) - { - if (event.type == sdl::EventTypes::MouseMotion) { - player_sprite_.set_position({event.motion.x, event.motion.y}); - return true; - } - - if (event.type == sdl::EventTypes::MouseButtonUp) { - sprites_.push_back( - Sprite::create_from_texture( - engine_.get_render_server(), - "assets/sprites/neofox_64.png", - std::nullopt, - sdl::FPoint{event.motion.x, event.motion.y}, - sdl::FPoint{-32, -32} - ) - ); - return true; - } - - return false; - } - - void update() - { - // TODO: Move this to Engine - engine_.get_ui_server().update(); - } - - void render() const - { - const auto& render_server = engine_.get_render_server(); - render_server.start_frame(); - - // Render tilemap - tile_map_.draw(render_server, tile_set_); - - // Render sprites - for (const Sprite& sprite : sprites_) { - sprite.draw(render_server); - } - player_sprite_.draw(render_server); - - // TODO: Move this to Engine - engine_.get_ui_server().render(); - - render_server.finish_frame(); - } - - void shutdown() - { - } - }; - - bool Game::instantiated_ = false; -} diff --git a/src/sdl_app.cppm b/src/sdl_app.cppm new file mode 100644 index 0000000..7e14111 --- /dev/null +++ b/src/sdl_app.cppm @@ -0,0 +1,95 @@ +module; + +#include +#include +#include + +export module sdl_app; + +import sprite; + +export { + struct AppState + { + SDL_Window* window = nullptr; + SDL_Renderer* renderer = nullptr; + Sprite* sprite = nullptr; + }; + + SDL_AppResult sdl_panic( + const std::string& error_prefix, + SDL_Window* window = nullptr, + SDL_Renderer* renderer = nullptr + ) + { + std::cerr << error_prefix << ": " << SDL_GetError() << '\n'; + + if (renderer != nullptr) { + SDL_DestroyRenderer(renderer); + } + if (window != nullptr) { + SDL_DestroyWindow(window); + } + + SDL_Quit(); + return SDL_APP_FAILURE; + } + + SDL_AppResult sdl_app_init(AppState* app_state) + { + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) { + return sdl_panic("SDL_Init error"); + } + + app_state->window = SDL_CreateWindow("BuildSystemTest", 640, 480, 0); + if (app_state->window == nullptr) { + return sdl_panic("SDL_CreateWindow error"); + } + + app_state->renderer = SDL_CreateRenderer(app_state->window, nullptr); + if (app_state->renderer == nullptr) { + return sdl_panic("SDL_CreateRenderer error", app_state->window); + } + + try { + app_state->sprite = new Sprite(app_state->renderer, "assets/neocat.png", 100, 100); + } + catch (const std::runtime_error& e) { + return sdl_panic(e.what(), app_state->window, app_state->renderer); + } + + return SDL_APP_CONTINUE; + } + + SDL_AppResult sdl_app_event(const AppState* app_state, const SDL_Event* event) + { + if (event->type == SDL_EVENT_QUIT) { + return SDL_APP_SUCCESS; + } + + if (event->type == SDL_EVENT_MOUSE_MOTION) { + app_state->sprite->move( + event->motion.x - 50, + event->motion.y - 50 + ); + } + + return SDL_APP_CONTINUE; + } + + SDL_AppResult sdl_app_iterate(const AppState* app_state) + { + SDL_RenderClear(app_state->renderer); + app_state->sprite->render(app_state->renderer); + SDL_RenderPresent(app_state->renderer); + + return SDL_APP_CONTINUE; + } + + void sdl_app_shutdown(const AppState* app_state) + { + delete app_state->sprite; + SDL_DestroyRenderer(app_state->renderer); + SDL_DestroyWindow(app_state->window); + } +} diff --git a/src/sprite.cppm b/src/sprite.cppm new file mode 100644 index 0000000..3318180 --- /dev/null +++ b/src/sprite.cppm @@ -0,0 +1,81 @@ +module; + +#include +#include +#include +#include + +export module sprite; + +export class Sprite +{ + SDL_Texture* sdl_texture; + SDL_FRect dest_rect{0, 0, 0, 0}; + + public: + explicit Sprite( + SDL_Renderer* renderer, + const std::string& filename, + const int width, + const int height + ) + { + SDL_Surface* texture_surface = IMG_Load(filename.c_str()); + if (texture_surface == nullptr) { + throw std::runtime_error("IMG_Load error"); + } + + sdl_texture = SDL_CreateTextureFromSurface(renderer, texture_surface); + SDL_DestroySurface(texture_surface); + + if (sdl_texture == nullptr) { + throw std::runtime_error("SDL_CreateTextureFromSurface error"); + } + + dest_rect.w = static_cast(width); + dest_rect.h = static_cast(height); + } + + // Don't allow copy operations + Sprite(const Sprite&) = delete; + Sprite& operator=(const Sprite&) = delete; + + // Move constructor + Sprite(Sprite&& other) noexcept + : sdl_texture(other.sdl_texture), + dest_rect(other.dest_rect) + { + other.sdl_texture = nullptr; + } + + // Move assignment + Sprite& operator=(Sprite&& other) noexcept + { + // Move inner resources from other + sdl_texture = other.sdl_texture; + dest_rect = other.dest_rect; + + // Reset other to make it safe for deletion + other.sdl_texture = nullptr; + + return *this; + } + + ~Sprite() + { + if (sdl_texture != nullptr) { + SDL_DestroyTexture(sdl_texture); + } + } + + void move(const float x, const float y) + { + dest_rect.x = x; + dest_rect.y = y; + } + + void render(SDL_Renderer* renderer) const + { + SDL_RenderTexture(renderer, sdl_texture, nullptr, &dest_rect); + } +}; diff --git a/src/utils/memory.cppm b/src/utils/memory.cppm deleted file mode 100644 index d1e16ad..0000000 --- a/src/utils/memory.cppm +++ /dev/null @@ -1,21 +0,0 @@ -module; - -export module utils.memory; - -export namespace utils -{ - /** - * Template to generate deleters for `std::unique_ptr` from functions, e.g. to free SDL resources. - * - * @tparam delete_func Function that takes a pointer to a resource and deletes the resource. - */ - template - struct FuncDeleter - { - template - constexpr void operator()(T* ptr) const noexcept - { - delete_func(ptr); - } - }; -} diff --git a/src/wrappers/rmlui/core.cppm b/src/wrappers/rmlui/core.cppm deleted file mode 100644 index fa209d4..0000000 --- a/src/wrappers/rmlui/core.cppm +++ /dev/null @@ -1,530 +0,0 @@ -module; - -#include - -export module wrappers.rmlui.core; - -export -{ - namespace Rml - { - using Rml::Animation; - using Rml::AnimationList; - using Rml::Any; - using Rml::Array; - using Rml::ArrayDefinition; - using Rml::Assert; - using Rml::AttributeNameList; - using Rml::BasePointerDefinition; - using Rml::BaseXMLParser; - using Rml::BlendMode; - using Rml::Box; - using Rml::BoxArea; - using Rml::BoxDirection; - using Rml::BoxEdge; - using Rml::BoxShadow; - using Rml::BoxShadowList; - using Rml::CallbackTexture; - using Rml::CallbackTextureFunction; - using Rml::CallbackTextureInterface; - using Rml::CallbackTextureSource; - using Rml::Character; - using Rml::ClipMaskGeometry; - using Rml::ClipMaskGeometryList; - using Rml::ClipMaskOperation; - using Rml::ColorFormat; - using Rml::ColorStop; - using Rml::ColorStopList; - using Rml::Colour; - using Rml::Colourb; - using Rml::ColourbPremultiplied; - using Rml::Colourf; - using Rml::ColumnMajorMatrix4f; - using Rml::ColumnMajorStorage; - using Rml::CompiledFilter; - using Rml::CompiledFilterHandle; - using Rml::CompiledGeometryHandle; - using Rml::CompiledShader; - using Rml::CompiledShaderHandle; - using Rml::ComputedValues; - using Rml::ContainerBox; - using Rml::Context; - using Rml::ContextInstancer; - using Rml::ContextPtr; - using Rml::ConvolutionFilter; - using Rml::CornerSizes; - using Rml::CreateContext; - using Rml::CreateString; - using Rml::DataAddress; - using Rml::DataAddressEntry; - using Rml::DataController; - using Rml::DataControllerInstancer; - using Rml::DataControllerPtr; - using Rml::DataEventFunc; - using Rml::DataGetFunc; - using Rml::DataModel; - using Rml::DataModelConstructor; - using Rml::DataModelHandle; - using Rml::DataSetFunc; - using Rml::DataTransformFunc; - using Rml::DataTypeGetFunc; - using Rml::DataTypeRegister; - using Rml::DataTypeSetFunc; - using Rml::DataVariable; - using Rml::DataVariableType; - using Rml::DataView; - using Rml::DataViewInstancer; - using Rml::DataViewPtr; - using Rml::Decorator; - using Rml::DecoratorDataHandle; - using Rml::DecoratorDeclaration; - using Rml::DecoratorDeclarationList; - using Rml::DecoratorInstancer; - using Rml::DecoratorInstancerInterface; - using Rml::DecoratorPtrList; - using Rml::DecoratorsPtr; - using Rml::DefaultActionPhase; - using Rml::DefaultStyleSheetParsers; - using Rml::Dictionary; - using Rml::DirtyVariables; - using Rml::DocumentHeader; - using Rml::EdgeSizes; - using Rml::EffectSpecification; - using Rml::Element; - using Rml::ElementAnimation; - using Rml::ElementAnimationList; - using Rml::ElementAttributes; - using Rml::ElementBackgroundBorder; - using Rml::ElementDefinition; - using Rml::ElementDocument; - using Rml::ElementForm; - using Rml::ElementFormControl; - using Rml::ElementFormControlInput; - using Rml::ElementFormControlSelect; - using Rml::ElementFormControlTextArea; - using Rml::ElementInstancer; - using Rml::ElementInstancerElement; - using Rml::ElementInstancerGeneric; - using Rml::ElementInstancerText; - using Rml::ElementList; - using Rml::ElementMeta; - using Rml::ElementProgress; - using Rml::ElementPtr; - using Rml::ElementScroll; - using Rml::ElementStyle; - using Rml::ElementTabSet; - using Rml::ElementText; - using Rml::ElementUtilities; - using Rml::EnableObserverPtr; - using Rml::Event; - using Rml::EventDispatcher; - using Rml::EventId; - using Rml::EventInstancer; - using Rml::EventListener; - using Rml::EventListenerInstancer; - using Rml::EventPhase; - using Rml::EventPtr; - using Rml::EventSpecification; - using Rml::Factory; - using Rml::Family; - using Rml::FamilyBase; - using Rml::FamilyId; - using Rml::FileHandle; - using Rml::FileInterface; - using Rml::Filter; - using Rml::FilterDeclaration; - using Rml::FilterDeclarationList; - using Rml::FilterHandleList; - using Rml::FilterInstancer; - using Rml::FilterOperation; - using Rml::FiltersPtr; - using Rml::FocusFlag; - using Rml::FontEffect; - using Rml::FontEffectInstancer; - using Rml::FontEffectList; - using Rml::FontEffects; - using Rml::FontEffectsHandle; - using Rml::FontEffectsPtr; - using Rml::FontEngineInterface; - using Rml::FontFaceHandle; - using Rml::FontGlyph; - using Rml::FontGlyphMap; - using Rml::FontMetrics; - using Rml::FormatString; - using Rml::FromString; - using Rml::FuncDefinition; - using Rml::Function; - using Rml::Geometry; - using Rml::Get; - using Rml::GetContext; - using Rml::GetFileInterface; - using Rml::GetFontEngineInterface; - using Rml::GetIf; - using Rml::GetNumContexts; - using Rml::GetRenderInterface; - using Rml::GetSystemInterface; - using Rml::GetTextInputHandler; - using Rml::GetTextureSourceList; - using Rml::GetVersion; - using Rml::Hash; - using Rml::Initialise; - using Rml::InlineLevelBox; - using Rml::InputType; - using Rml::IsVoidMemberFunc; - using Rml::KeyframeBlock; - using Rml::Keyframes; - using Rml::KeyframesMap; - using Rml::LayerHandle; - using Rml::LayoutEngine; - using Rml::List; - using Rml::LoadFontFace; - using Rml::Log; - using Rml::MakeLiteralIntVariable; - using Rml::MakeMoveIterator; - using Rml::MakeShared; - using Rml::MakeUnique; - using Rml::Matrix4; - using Rml::Matrix4f; - using Rml::MatrixStorageBase; - using Rml::MediaBlock; - using Rml::MediaBlockList; - using Rml::MediaQueryId; - using Rml::MediaQueryModifier; - using Rml::MemberGetFunc; - using Rml::MemberGetFuncDefinition; - using Rml::MemberGetterFunc; - using Rml::MemberObjectDefinition; - using Rml::MemberScalarGetSetFuncDefinition; - using Rml::MemberSetFunc; - using Rml::MemberSetterFunc; - using Rml::Mesh; - using Rml::MeshUtilities; - using Rml::ModalFlag; - using Rml::NamedDecorator; - using Rml::NamedDecoratorMap; - using Rml::NavigationSearchDirection; - using Rml::NonCopyMoveable; - using Rml::NumericValue; - using Rml::ObserverPtr; - using Rml::OwnedElementList; - using Rml::Pair; - using Rml::ParameterMap; - using Rml::Plugin; - using Rml::PointerDefinition; - using Rml::PointerTraits; - using Rml::PropertiesIterator; - using Rml::PropertiesIteratorView; - using Rml::Property; - using Rml::PropertyDefinition; - using Rml::PropertyDictionary; - using Rml::PropertyId; - using Rml::PropertyIdNameMap; - using Rml::PropertyIdSet; - using Rml::PropertyIdSetIterator; - using Rml::PropertyMap; - using Rml::PropertyParser; - using Rml::PropertySource; - using Rml::PropertySpecification; - using Rml::Queue; - using Rml::Rectangle; - using Rml::Rectanglef; - using Rml::Rectanglei; - using Rml::RegisterEventType; - using Rml::RegisterPlugin; - using Rml::RelativeTarget; - using Rml::Releasable; - using Rml::ReleaseCompiledGeometry; - using Rml::ReleaseFontResources; - using Rml::ReleaseRenderManagers; - using Rml::ReleaseTexture; - using Rml::ReleaseTextures; - using Rml::Releaser; - using Rml::ReleaserBase; - using Rml::RemoveContext; - using Rml::RenderBox; - using Rml::RenderInterface; - using Rml::RenderManager; - using Rml::RenderManagerAccess; - using Rml::RenderState; - using Rml::ReplacedBox; - using Rml::ResolveValue; - using Rml::ResolveValueOr; - using Rml::RowMajorMatrix4f; - using Rml::RowMajorStorage; - using Rml::ScalarDefinition; - using Rml::ScalarFuncDefinition; - using Rml::ScriptInterface; - using Rml::ScriptInterfacePtr; - using Rml::ScriptObject; - using Rml::ScrollAlignment; - using Rml::ScrollBehavior; - using Rml::ScrollController; - using Rml::ScrollIntoViewOptions; - using Rml::ScrollParentage; - using Rml::SetFileInterface; - using Rml::SetFontEngineInterface; - using Rml::SetRenderInterface; - using Rml::SetSystemInterface; - using Rml::SetTextInputHandler; - using Rml::SharedPtr; - using Rml::ShorthandDefinition; - using Rml::ShorthandId; - using Rml::ShorthandIdNameMap; - using Rml::ShorthandType; - using Rml::Shutdown; - using Rml::SmallOrderedMap; - using Rml::SmallOrderedSet; - using Rml::SmallUnorderedMap; - using Rml::SmallUnorderedSet; - using Rml::Span; - using Rml::Sprite; - using Rml::SpriteDefinitionList; - using Rml::SpriteMap; - using Rml::Spritesheet; - using Rml::SpritesheetList; - using Rml::StableMap; - using Rml::StableUnorderedMap; - using Rml::StableVector; - using Rml::StableVectorIndex; - using Rml::Stack; - using Rml::StackingContextChild; - using Rml::Stream; - using Rml::String; - using Rml::StringIteratorU8; - using Rml::StringList; - using Rml::StringView; - using Rml::StructDefinition; - using Rml::StructHandle; - using Rml::StyleSheet; - using Rml::StyleSheetContainer; - using Rml::StyleSheetIndex; - using Rml::StyleSheetNode; - using Rml::StyleSheetParser; - using Rml::StyleSheetSpecification; - using Rml::SystemInterface; - using Rml::TextInputHandler; - using Rml::TextShapingContext; - using Rml::Texture; - using Rml::TextureDatabase; - using Rml::TextureFileIndex; - using Rml::TextureHandle; - using Rml::TextureSource; - using Rml::TexturedMesh; - using Rml::TexturedMeshList; - using Rml::ToString; - using Rml::Touch; - using Rml::TouchId; - using Rml::TouchList; - using Rml::Transform; - using Rml::TransformFuncRegister; - using Rml::TransformPrimitive; - using Rml::TransformPtr; - using Rml::TransformState; - using Rml::Transition; - using Rml::TransitionList; - using Rml::Tween; - using Rml::TypeConverter; - using Rml::URL; - using Rml::UniquePtr; - using Rml::UniqueReleaserPtr; - using Rml::UniqueRenderResource; - using Rml::Unit; - using Rml::Units; - using Rml::UnorderedMap; - using Rml::UnorderedMultimap; - using Rml::UnorderedSet; - using Rml::UnregisterPlugin; - using Rml::VariableDefinition; - using Rml::Variant; - using Rml::VariantList; - using Rml::Vector2; - using Rml::Vector2f; - using Rml::Vector2i; - using Rml::Vector3; - using Rml::Vector3f; - using Rml::Vector3i; - using Rml::Vector4; - using Rml::Vector4f; - using Rml::Vector4i; - using Rml::Vector; - using Rml::Vertex; - using Rml::VoidMemberFunc; - using Rml::WeakPtr; - using Rml::WidgetDropDown; - using Rml::WidgetScroll; - using Rml::WidgetTextInputMultiLine; - using Rml::XMLAttributes; - using Rml::XMLDataType; - using Rml::XMLNodeHandler; - using Rml::XMLParser; - using Rml::byte; - using Rml::is_builtin_data_scalar; - using Rml::voidPtr; - - namespace Input - { - using Rml::Input::KeyIdentifier; - using Rml::Input::KeyModifier; - } - - namespace Math - { - using Rml::Math::ACos; - using Rml::Math::ASin; - using Rml::Math::ATan2; - using Rml::Math::Absolute; - using Rml::Math::Clamp; - using Rml::Math::Cos; - using Rml::Math::DecomposeFractionalIntegral; - using Rml::Math::DegreesToRadians; - using Rml::Math::Exp; - using Rml::Math::ExpandToPixelGrid; - using Rml::Math::HexToDecimal; - using Rml::Math::IsCloseToZero; - using Rml::Math::Lerp; - using Rml::Math::Log2; - using Rml::Math::Max; - using Rml::Math::Min; - using Rml::Math::NormaliseAngle; - using Rml::Math::RadiansToDegrees; - using Rml::Math::RandomBool; - using Rml::Math::RandomInteger; - using Rml::Math::RandomReal; - using Rml::Math::Round; - using Rml::Math::RoundDown; - using Rml::Math::RoundDownToInteger; - using Rml::Math::RoundToInteger; - using Rml::Math::RoundUp; - using Rml::Math::RoundUpToInteger; - using Rml::Math::RoundedLerp; - using Rml::Math::Sin; - using Rml::Math::SnapToPixelGrid; - using Rml::Math::SquareRoot; - using Rml::Math::Tan; - using Rml::Math::ToPowerOfTwo; - } - - namespace StringUtilities - { - using Rml::StringUtilities::BytesUTF8; - using Rml::StringUtilities::ConvertByteOffsetToCharacterOffset; - using Rml::StringUtilities::ConvertCharacterOffsetToByteOffset; - using Rml::StringUtilities::DecodeRml; - using Rml::StringUtilities::EncodeRml; - using Rml::StringUtilities::EndsWith; - using Rml::StringUtilities::ExpandString; - using Rml::StringUtilities::IsWhitespace; - using Rml::StringUtilities::JoinString; - using Rml::StringUtilities::LengthUTF8; - using Rml::StringUtilities::Replace; - using Rml::StringUtilities::SeekBackwardUTF8; - using Rml::StringUtilities::SeekForwardUTF8; - using Rml::StringUtilities::StartsWith; - using Rml::StringUtilities::StringCompareCaseInsensitive; - using Rml::StringUtilities::StripWhitespace; - using Rml::StringUtilities::ToCharacter; - using Rml::StringUtilities::ToLower; - using Rml::StringUtilities::ToUTF8; - using Rml::StringUtilities::ToUpper; - using Rml::StringUtilities::TrimTrailingDotZeros; - } - - namespace Style - { - using Rml::Style::AlignContent; - using Rml::Style::AlignItems; - using Rml::Style::AlignSelf; - using Rml::Style::Bottom; - using Rml::Style::BoxSizing; - using Rml::Style::Clear; - using Rml::Style::Clip; - using Rml::Style::CommonValues; - using Rml::Style::ComputedValues; - using Rml::Style::Direction; - using Rml::Style::Display; - using Rml::Style::Drag; - using Rml::Style::FlexBasis; - using Rml::Style::FlexDirection; - using Rml::Style::FlexWrap; - using Rml::Style::Float; - using Rml::Style::Focus; - using Rml::Style::FontKerning; - using Rml::Style::FontStyle; - using Rml::Style::FontWeight; - using Rml::Style::Height; - using Rml::Style::InheritedValues; - using Rml::Style::JustifyContent; - using Rml::Style::Left; - using Rml::Style::LengthPercentage; - using Rml::Style::LengthPercentageAuto; - using Rml::Style::LineHeight; - using Rml::Style::Margin; - using Rml::Style::MaxHeight; - using Rml::Style::MaxWidth; - using Rml::Style::MinHeight; - using Rml::Style::MinWidth; - using Rml::Style::Nav; - using Rml::Style::NumberAuto; - using Rml::Style::OriginX; - using Rml::Style::OriginY; - using Rml::Style::Overflow; - using Rml::Style::OverscrollBehavior; - using Rml::Style::Padding; - using Rml::Style::PerspectiveOrigin; - using Rml::Style::PointerEvents; - using Rml::Style::Position; - using Rml::Style::RareValues; - using Rml::Style::Right; - using Rml::Style::TabIndex; - using Rml::Style::TextAlign; - using Rml::Style::TextDecoration; - using Rml::Style::TextOverflow; - using Rml::Style::TextTransform; - using Rml::Style::Top; - using Rml::Style::TransformOrigin; - using Rml::Style::VerticalAlign; - using Rml::Style::Visibility; - using Rml::Style::WhiteSpace; - using Rml::Style::Width; - using Rml::Style::WordBreak; - using Rml::Style::ZIndex; - } - - namespace Transforms - { - using Rml::Transforms::DecomposedMatrix4; - using Rml::Transforms::Matrix2D; - using Rml::Transforms::Matrix3D; - using Rml::Transforms::Perspective; - using Rml::Transforms::ResolvedPrimitive; - using Rml::Transforms::Rotate2D; - using Rml::Transforms::Rotate3D; - using Rml::Transforms::RotateX; - using Rml::Transforms::RotateY; - using Rml::Transforms::RotateZ; - using Rml::Transforms::Scale2D; - using Rml::Transforms::Scale3D; - using Rml::Transforms::ScaleX; - using Rml::Transforms::ScaleY; - using Rml::Transforms::ScaleZ; - using Rml::Transforms::Skew2D; - using Rml::Transforms::SkewX; - using Rml::Transforms::SkewY; - using Rml::Transforms::Translate2D; - using Rml::Transforms::Translate3D; - using Rml::Transforms::TranslateX; - using Rml::Transforms::TranslateY; - using Rml::Transforms::TranslateZ; - using Rml::Transforms::UnresolvedPrimitive; - } - - namespace Utilities - { - using Rml::Utilities::HashCombine; - } - } - - using ::rmlui_dynamic_cast; - using ::rmlui_static_cast; - using ::rmlui_type_name; -} diff --git a/src/wrappers/rmlui/debugger.cppm b/src/wrappers/rmlui/debugger.cppm deleted file mode 100644 index 7dd319a..0000000 --- a/src/wrappers/rmlui/debugger.cppm +++ /dev/null @@ -1,14 +0,0 @@ -module; - -#include - -export module wrappers.rmlui.debugger; - -export namespace Rml::Debugger -{ - using Rml::Debugger::Initialise; - using Rml::Debugger::IsVisible; - using Rml::Debugger::SetContext; - using Rml::Debugger::SetVisible; - using Rml::Debugger::Shutdown; -} diff --git a/src/wrappers/sdl.cppm b/src/wrappers/sdl.cppm deleted file mode 100644 index 78f7c72..0000000 --- a/src/wrappers/sdl.cppm +++ /dev/null @@ -1,16 +0,0 @@ -module; - -export module wrappers.sdl; - -// Export submodules -export import wrappers.sdl.clipboard; -export import wrappers.sdl.error; -export import wrappers.sdl.events; -export import wrappers.sdl.init; -export import wrappers.sdl.keyboard; -export import wrappers.sdl.mouse; -export import wrappers.sdl.rect; -export import wrappers.sdl.render; -export import wrappers.sdl.surface; -export import wrappers.sdl.utils; -export import wrappers.sdl.video; diff --git a/src/wrappers/sdl/clipboard.cppm b/src/wrappers/sdl/clipboard.cppm deleted file mode 100644 index 8ab1278..0000000 --- a/src/wrappers/sdl/clipboard.cppm +++ /dev/null @@ -1,38 +0,0 @@ -module; - -#include - -export module wrappers.sdl.clipboard; - -import std; -import wrappers.sdl.error; -import wrappers.sdl.utils; - -export namespace sdl -{ - /** - * Put UTF-8 text into the clipboard. - * - * Wrapper for SDL_SetClipboardText(). - * - * Throws an SDLException on failure. - */ - void set_clipboard_text(const std::string& text) - { - if (!SDL_SetClipboardText(text.c_str())) { - throw SDLException("SDL_SetClipboardText"); - } - } - - /** - * Get UTF-8 text from the clipboard. - * - * Wrapper for SDL_GetClipboardText(). - * - * Returns an empty string on failure. - */ - std::string get_clipboard_text() - { - return wrap_string(SDL_GetClipboardText()); - } -} diff --git a/src/wrappers/sdl/error.cppm b/src/wrappers/sdl/error.cppm deleted file mode 100644 index 0b12725..0000000 --- a/src/wrappers/sdl/error.cppm +++ /dev/null @@ -1,24 +0,0 @@ -module; - -#include - -export module wrappers.sdl.error; - -import std; - -export namespace sdl -{ - // Exception that wraps an SDL error (which SDL function caused the error, what's the error). - // SDL_GetError() is used in the constructor to get the error message unless specified explicitly. - class SDLException final : public std::runtime_error - { - public: - explicit SDLException(const std::string& sdl_func, const std::string& sdl_error) - : runtime_error(std::format("Error in SDL function {}: {}", sdl_func, sdl_error)) - {} - - explicit SDLException(const std::string& sdl_func) - : SDLException(sdl_func, SDL_GetError()) - {} - }; -} diff --git a/src/wrappers/sdl/events.cppm b/src/wrappers/sdl/events.cppm deleted file mode 100644 index 8ef341a..0000000 --- a/src/wrappers/sdl/events.cppm +++ /dev/null @@ -1,47 +0,0 @@ -module; - -#include - -export module wrappers.sdl.events; - -export namespace sdl -{ - // Simple alias for SDL_Event union - using Event = SDL_Event; - - // Alias for EventType enum - using EventType = SDL_EventType; - - /** - * Wrapper for the SDL_EventType enum. - * - * We're using a namespace here to emulate an enum-like interface, without having to copy the entire enum. - * More constants can be added on demand. - */ - namespace EventTypes - { - // Application events - constexpr EventType Quit = SDL_EVENT_QUIT; - - // Window events - constexpr EventType WindowMouseLeave = SDL_EVENT_WINDOW_MOUSE_LEAVE; - constexpr EventType WindowDisplayScaleChanged = SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED; - constexpr EventType WindowPixelSizeChanged = SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED; - - // Keyboard events - constexpr EventType KeyDown = SDL_EVENT_KEY_DOWN; - constexpr EventType KeyUp = SDL_EVENT_KEY_UP; - constexpr EventType TextInput = SDL_EVENT_TEXT_INPUT; - - // Mouse events - constexpr EventType MouseMotion = SDL_EVENT_MOUSE_MOTION; - constexpr EventType MouseButtonDown = SDL_EVENT_MOUSE_BUTTON_DOWN; - constexpr EventType MouseButtonUp = SDL_EVENT_MOUSE_BUTTON_UP; - constexpr EventType MouseWheel = SDL_EVENT_MOUSE_WHEEL; - - // Touch events - constexpr EventType FingerDown = SDL_EVENT_FINGER_DOWN; - constexpr EventType FingerUp = SDL_EVENT_FINGER_UP; - constexpr EventType FingerMotion = SDL_EVENT_FINGER_MOTION; - } -} diff --git a/src/wrappers/sdl/init.cppm b/src/wrappers/sdl/init.cppm deleted file mode 100644 index f673011..0000000 --- a/src/wrappers/sdl/init.cppm +++ /dev/null @@ -1,62 +0,0 @@ -module; - -#include - -export module wrappers.sdl.init; - -import std; -import wrappers.sdl.error; - -export namespace sdl -{ - using InitFlags_t = SDL_InitFlags; - - /** - * Wrapper for the SDL_InitFlags enum/constants. - * - * We're using a namespace here to emulate an enum-like interface. If we used an actual enum, we would have to - * explicitly define bit-wise operations for it (as well as for every other kind of bit flag enum. - * (Maybe in the future this can be replaced with a more elegant template-based solution or something.) - */ - namespace InitFlags - { - constexpr InitFlags_t Audio = SDL_INIT_AUDIO; - constexpr InitFlags_t Video = SDL_INIT_VIDEO; - constexpr InitFlags_t Joystick = SDL_INIT_JOYSTICK; - constexpr InitFlags_t Haptic = SDL_INIT_HAPTIC; - constexpr InitFlags_t Gamepad = SDL_INIT_GAMEPAD; - constexpr InitFlags_t Events = SDL_INIT_EVENTS; - constexpr InitFlags_t Sensor = SDL_INIT_SENSOR; - constexpr InitFlags_t Camera = SDL_INIT_CAMERA; - } - - /** - * Wrapper for the SDL_AppResult enum. - */ - enum class AppResult : std::underlying_type_t - { - Continue = SDL_APP_CONTINUE, - Success = SDL_APP_SUCCESS, - Failure = SDL_APP_FAILURE, - }; - - /** - * Wrapper for SDL_Init(). - */ - constexpr void initialize_sdl(const InitFlags_t flags) - { - if (!SDL_Init(flags)) { - throw SDLException("SDL_Init"); - } - } - - /** - * Wrapper for SDL_SetAppMetadataProperty(). - */ - constexpr void set_app_metadata_property(const char* property_name, const char* value) - { - if (!SDL_SetAppMetadataProperty(property_name, value)) { - throw SDLException("SDL_SetAppMetadataProperty"); - } - } -} diff --git a/src/wrappers/sdl/keyboard.cppm b/src/wrappers/sdl/keyboard.cppm deleted file mode 100644 index bed6d25..0000000 --- a/src/wrappers/sdl/keyboard.cppm +++ /dev/null @@ -1,177 +0,0 @@ -module; - -#include -#include - -export module wrappers.sdl.keyboard; - -export namespace sdl -{ - using Keycode = SDL_Keycode; - - /** - * SDL keycode constants (SDLK_...). - * This is not a complete collection of all keycodes, only those we need. Add more when needed. - * Namespace is just called "Key" instead of "Keycodes" to keep it short. - */ - namespace Key - { - // clang-format off - constexpr Keycode Unknown = SDLK_UNKNOWN; - constexpr Keycode Return = SDLK_RETURN; - constexpr Keycode Escape = SDLK_ESCAPE; - constexpr Keycode Backspace = SDLK_BACKSPACE; - constexpr Keycode Tab = SDLK_TAB; - constexpr Keycode Space = SDLK_SPACE; - constexpr Keycode DblApostrophe = SDLK_DBLAPOSTROPHE; - constexpr Keycode Plus = SDLK_PLUS; - constexpr Keycode Comma = SDLK_COMMA; - constexpr Keycode Minus = SDLK_MINUS; - constexpr Keycode Period = SDLK_PERIOD; - constexpr Keycode Slash = SDLK_SLASH; - constexpr Keycode Num0 = SDLK_0; - constexpr Keycode Num1 = SDLK_1; - constexpr Keycode Num2 = SDLK_2; - constexpr Keycode Num3 = SDLK_3; - constexpr Keycode Num4 = SDLK_4; - constexpr Keycode Num5 = SDLK_5; - constexpr Keycode Num6 = SDLK_6; - constexpr Keycode Num7 = SDLK_7; - constexpr Keycode Num8 = SDLK_8; - constexpr Keycode Num9 = SDLK_9; - constexpr Keycode Semicolon = SDLK_SEMICOLON; - constexpr Keycode LeftBracket = SDLK_LEFTBRACKET; - constexpr Keycode Backslash = SDLK_BACKSLASH; - constexpr Keycode RightBracket = SDLK_RIGHTBRACKET; - constexpr Keycode Grave = SDLK_GRAVE; - constexpr Keycode A = SDLK_A; - constexpr Keycode B = SDLK_B; - constexpr Keycode C = SDLK_C; - constexpr Keycode D = SDLK_D; - constexpr Keycode E = SDLK_E; - constexpr Keycode F = SDLK_F; - constexpr Keycode G = SDLK_G; - constexpr Keycode H = SDLK_H; - constexpr Keycode I = SDLK_I; - constexpr Keycode J = SDLK_J; - constexpr Keycode K = SDLK_K; - constexpr Keycode L = SDLK_L; - constexpr Keycode M = SDLK_M; - constexpr Keycode N = SDLK_N; - constexpr Keycode O = SDLK_O; - constexpr Keycode P = SDLK_P; - constexpr Keycode Q = SDLK_Q; - constexpr Keycode R = SDLK_R; - constexpr Keycode S = SDLK_S; - constexpr Keycode T = SDLK_T; - constexpr Keycode U = SDLK_U; - constexpr Keycode V = SDLK_V; - constexpr Keycode W = SDLK_W; - constexpr Keycode X = SDLK_X; - constexpr Keycode Y = SDLK_Y; - constexpr Keycode Z = SDLK_Z; - constexpr Keycode Delete = SDLK_DELETE; - constexpr Keycode CapsLock = SDLK_CAPSLOCK; - constexpr Keycode F1 = SDLK_F1; - constexpr Keycode F2 = SDLK_F2; - constexpr Keycode F3 = SDLK_F3; - constexpr Keycode F4 = SDLK_F4; - constexpr Keycode F5 = SDLK_F5; - constexpr Keycode F6 = SDLK_F6; - constexpr Keycode F7 = SDLK_F7; - constexpr Keycode F8 = SDLK_F8; - constexpr Keycode F9 = SDLK_F9; - constexpr Keycode F10 = SDLK_F10; - constexpr Keycode F11 = SDLK_F11; - constexpr Keycode F12 = SDLK_F12; - constexpr Keycode PrintScreen = SDLK_PRINTSCREEN; - constexpr Keycode ScrollLock = SDLK_SCROLLLOCK; - constexpr Keycode Pause = SDLK_PAUSE; - constexpr Keycode Insert = SDLK_INSERT; - constexpr Keycode Home = SDLK_HOME; - constexpr Keycode PageUp = SDLK_PAGEUP; - constexpr Keycode End = SDLK_END; - constexpr Keycode PageDown = SDLK_PAGEDOWN; - constexpr Keycode Right = SDLK_RIGHT; - constexpr Keycode Left = SDLK_LEFT; - constexpr Keycode Down = SDLK_DOWN; - constexpr Keycode Up = SDLK_UP; - constexpr Keycode NumLockClear = SDLK_NUMLOCKCLEAR; - constexpr Keycode NumpadDivide = SDLK_KP_DIVIDE; - constexpr Keycode NumpadMultiply = SDLK_KP_MULTIPLY; - constexpr Keycode NumpadMinus = SDLK_KP_MINUS; - constexpr Keycode NumpadPlus = SDLK_KP_PLUS; - constexpr Keycode NumpadEnter = SDLK_KP_ENTER; - constexpr Keycode Numpad1 = SDLK_KP_1; - constexpr Keycode Numpad2 = SDLK_KP_2; - constexpr Keycode Numpad3 = SDLK_KP_3; - constexpr Keycode Numpad4 = SDLK_KP_4; - constexpr Keycode Numpad5 = SDLK_KP_5; - constexpr Keycode Numpad6 = SDLK_KP_6; - constexpr Keycode Numpad7 = SDLK_KP_7; - constexpr Keycode Numpad8 = SDLK_KP_8; - constexpr Keycode Numpad9 = SDLK_KP_9; - constexpr Keycode Numpad0 = SDLK_KP_0; - constexpr Keycode NumpadPeriod = SDLK_KP_PERIOD; - constexpr Keycode NumpadEquals = SDLK_KP_EQUALS; - constexpr Keycode F13 = SDLK_F13; - constexpr Keycode F14 = SDLK_F14; - constexpr Keycode F15 = SDLK_F15; - constexpr Keycode Help = SDLK_HELP; - constexpr Keycode Clear = SDLK_CLEAR; - constexpr Keycode LCtrl = SDLK_LCTRL; - constexpr Keycode LShift = SDLK_LSHIFT; - constexpr Keycode LAlt = SDLK_LALT; - constexpr Keycode LGui = SDLK_LGUI; - constexpr Keycode RCtrl = SDLK_RCTRL; - constexpr Keycode RShift = SDLK_RSHIFT; - constexpr Keycode RAlt = SDLK_RALT; - constexpr Keycode RGui = SDLK_RGUI; - // clang-format on - } - - // Key modifier (e.g. left shift, left ctrl, ...) - using KeyMod = SDL_Keymod; - - // Key modifier state: Bitmask of key modifiers (just an alias for KeyMod for slightly better semantics) - using KeyModState = KeyMod; - - /** - * SDL key modifier constants (SDL_KMOD_...). - * This is not a complete collection of all key modifiers, only those we need. Add more when needed. - */ - namespace KeyMods - { - // clang-format off - - // Single modifiers - constexpr KeyMod None = SDL_KMOD_NONE; - constexpr KeyMod LShift = SDL_KMOD_LSHIFT; - constexpr KeyMod RShift = SDL_KMOD_RSHIFT; - constexpr KeyMod LCtrl = SDL_KMOD_LCTRL; - constexpr KeyMod RCtrl = SDL_KMOD_RCTRL; - constexpr KeyMod LAlt = SDL_KMOD_LALT; - constexpr KeyMod RAlt = SDL_KMOD_RALT; - constexpr KeyMod LGui = SDL_KMOD_LGUI; - constexpr KeyMod RGui = SDL_KMOD_RGUI; - constexpr KeyMod NumLock = SDL_KMOD_NUM; - constexpr KeyMod CapsLock = SDL_KMOD_CAPS; - - // Combined modifiers - constexpr KeyModState Ctrl = SDL_KMOD_CTRL; // Any Ctrl key - constexpr KeyModState Shift = SDL_KMOD_SHIFT; // Any Shift key - constexpr KeyModState Alt = SDL_KMOD_ALT; // Any Alt key - constexpr KeyModState Gui = SDL_KMOD_GUI; // Any GUI key - - // clang-format on - } - - /** - * Get the current key modifier state for the keyboard. - * Wrapper for SDL_GetModState(). - */ - constexpr KeyModState get_key_mod_state() - { - return SDL_GetModState(); - } -} diff --git a/src/wrappers/sdl/mouse.cppm b/src/wrappers/sdl/mouse.cppm deleted file mode 100644 index e612c00..0000000 --- a/src/wrappers/sdl/mouse.cppm +++ /dev/null @@ -1,29 +0,0 @@ -module; - -#include -#include - -export module wrappers.sdl.mouse; - -import std; - -export namespace sdl -{ - using MouseButtonIndex = std::uint8_t; - - namespace MouseButtons - { - constexpr MouseButtonIndex Left = SDL_BUTTON_LEFT; - constexpr MouseButtonIndex Middle = SDL_BUTTON_MIDDLE; - constexpr MouseButtonIndex Right = SDL_BUTTON_RIGHT; - } - - /** - * Wrapper for SDL_CaptureMouse(). - */ - constexpr void capture_mouse(const bool enabled) - { - // Ignore errors (probably means it's not supported on this platform) - SDL_CaptureMouse(enabled); - } -} diff --git a/src/wrappers/sdl/rect.cppm b/src/wrappers/sdl/rect.cppm deleted file mode 100644 index 6e5dd32..0000000 --- a/src/wrappers/sdl/rect.cppm +++ /dev/null @@ -1,42 +0,0 @@ -module; - -#include - -export module wrappers.sdl.rect; - -export namespace sdl -{ - // Wrappers around SDL_Point and SDL_FPoint (change to wrapper classes when needed) - using FPoint = SDL_FPoint; - using Point = SDL_Point; - - /** - * Wrapper for SDL_FRect using class inheritance. - */ - class FRect : public SDL_FRect - { - /** - * Wrapper for SDL_RectEmptyFloat(). - * @return True if the floating point rectangle takes no space. - */ - bool is_empty() const - { - return SDL_RectEmptyFloat(this); - } - }; - - /** - * Wrapper for SDL_Rect using class inheritance. - */ - class Rect : public SDL_Rect - { - /** - * Wrapper for SDL_RectEmpty(). - * @return True if the rectangle takes no space. - */ - bool is_empty() const - { - return SDL_RectEmpty(this); - } - }; -} diff --git a/src/wrappers/sdl/render.cppm b/src/wrappers/sdl/render.cppm deleted file mode 100644 index a556c3f..0000000 --- a/src/wrappers/sdl/render.cppm +++ /dev/null @@ -1,194 +0,0 @@ -module; - -#include -#include - -export module wrappers.sdl.render; - -import std; -import utils.memory; -import wrappers.sdl.error; -import wrappers.sdl.rect; -import wrappers.sdl.video; - -export namespace sdl -{ - using Vertex = SDL_Vertex; - - /** - * Wrapper for SDL_Texture that manages its lifecycle with a unique_ptr. - */ - class Texture - { - std::unique_ptr< - SDL_Texture, - utils::FuncDeleter - > raw_texture_; - - public: - Texture() = delete; - - explicit Texture(SDL_Texture* raw_texture) - : raw_texture_{raw_texture} - {} - - constexpr SDL_Texture* get_raw() const - { - assert(raw_texture_); - return raw_texture_.get(); - } - - constexpr int get_width() const - { - return raw_texture_->w; - } - - constexpr int get_height() const - { - return raw_texture_->h; - } - - constexpr FRect get_boundaries() const - { - return {0, 0, static_cast(raw_texture_->w), static_cast(raw_texture_->h)}; - } - }; - - /** - * Wrapper for SDL_Renderer that manages its lifecycle with a unique_ptr. - */ - class Renderer - { - std::unique_ptr< - SDL_Renderer, - utils::FuncDeleter - > raw_renderer_; - - public: - Renderer() = delete; - - explicit Renderer(SDL_Renderer* raw_renderer) - : raw_renderer_{raw_renderer} - {} - - constexpr SDL_Renderer* get_raw() const - { - assert(raw_renderer_); - return raw_renderer_.get(); - } - - /** - * Wrapper for SDL_SetRenderClipRect(). - * Uses assert to verify the function returned true. - */ - constexpr void set_clip_rect(const Rect* rect) const - { - if (!SDL_SetRenderClipRect(get_raw(), rect)) { - assert(!"SDL_SetRenderClipRect returned false"); - } - } - - /** - * Wrapper for SDL_SetRenderDrawBlendMode(). - * Uses assert to verify the function returned true. - */ - constexpr void set_draw_blend_mode(SDL_BlendMode blend_mode) const - { - if (!SDL_SetRenderDrawBlendMode(get_raw(), blend_mode)) { - assert(!"SDL_SetRenderDrawBlendMode returned false"); - } - } - - /** - * Wrapper for SDL_SetRenderVSync(). - * Uses assert to verify the function returned true. - */ - constexpr void set_vsync(const int vsync) const - { - // TODO: These functions probably should never fail? Change this to a runtime exception if necessary. - if (!SDL_SetRenderVSync(get_raw(), vsync)) { - assert(!"SDL_SetRenderVSync returned false"); - } - } - - /** - * Wrapper for SDL_RenderClear(). - * Uses assert to verify the function returned true. - */ - constexpr void clear() const - { - // TODO: These functions probably should never fail? Change this to a runtime exception if necessary. - if (!SDL_RenderClear(get_raw())) { - assert(!"SDL_RenderClear returned false"); - } - } - - /** - * Wrapper for SDL_RenderPresent(). - * Uses assert to verify the function returned true. - */ - constexpr void present() const - { - if (!SDL_RenderPresent(get_raw())) { - assert(!"SDL_RenderPresent returned false"); - } - } - - /** - * Wrapper for SDL_RenderTexture(). - * Uses assert to verify the function returned true. - */ - constexpr void render_texture( - const Texture& texture, - const FRect* src_rect, - const FRect* dest_rect - ) const - { - if (!SDL_RenderTexture(get_raw(), texture.get_raw(), src_rect, dest_rect)) { - assert(!"SDL_RenderTexture returned false"); - } - } - - /** - * Wrapper for SDL_RenderGeometry(). - * Uses assert to verify the function returned true. - */ - // TODO: Function uses raw SDL_Texture pointer. Add overload for sdl::Texture& when needed. - constexpr void render_geometry( - SDL_Texture* texture, - const Vertex* vertices, - const int num_vertices, - const int* indices, - const int num_indices - ) const - { - if (!SDL_RenderGeometry(get_raw(), texture, vertices, num_vertices, indices, num_indices)) { - assert(!"SDL_RenderGeometry returned false"); - } - } - }; - - /** - * Wrapper for SDL_CreateWindowAndRenderer(). - * Returns a pair of a Window and a Renderer object. - * Throws an SDLException on failure. - */ - std::pair create_window_and_renderer( - const std::string& title, - const int width, - const int height, - const SDL_WindowFlags window_flags - ) - { - SDL_Window* raw_window = nullptr; - SDL_Renderer* raw_renderer = nullptr; - - if (!SDL_CreateWindowAndRenderer( - title.c_str(), width, height, window_flags, &raw_window, &raw_renderer - )) { - throw SDLException("SDL_CreateWindowAndRenderer"); - } - - return {Window{raw_window}, Renderer{raw_renderer}}; - } -} diff --git a/src/wrappers/sdl/surface.cppm b/src/wrappers/sdl/surface.cppm deleted file mode 100644 index 88cba61..0000000 --- a/src/wrappers/sdl/surface.cppm +++ /dev/null @@ -1,98 +0,0 @@ -module; - -#include -#include - -export module wrappers.sdl.surface; - -import std; -import utils.memory; -import wrappers.sdl.error; - -export namespace sdl -{ - /** - * Wrapper for SDL_Texture that manages its lifecycle with a unique_ptr. - */ - class Surface - { - std::unique_ptr< - SDL_Surface, - utils::FuncDeleter - > raw_surface_; - - public: - Surface() = delete; - - explicit Surface(SDL_Surface* raw_surface) - : raw_surface_{raw_surface} - {} - - /** - * Allocate a new surface with a specific pixel format and existing pixel data. - * - * NOTE: No copy is made of the pixel data. Pixel data is not managed automatically; you must free the - * surface before you free the pixel data. - * - * Wrapper for SDL_CreateSurfaceFrom(). - */ - static Surface create_from( - const int width, - const int height, - const SDL_PixelFormat format, - void* pixels, - const int pitch - ) - { - SDL_Surface* raw_surface = SDL_CreateSurfaceFrom(width, height, format, pixels, pitch); - - if (raw_surface == nullptr) { - throw SDLException("SDL_CreateSurfaceFrom"); - } - - return Surface(raw_surface); - } - - constexpr SDL_Surface* get_raw() const - { - assert(raw_surface_); - return raw_surface_.get(); - } - - // TODO: Wrap SDL_PixelFormat? - constexpr SDL_PixelFormat get_format() const - { - return raw_surface_->format; - } - - constexpr int get_width() const - { - return raw_surface_->w; - } - - constexpr int get_height() const - { - return raw_surface_->h; - } - - constexpr void* get_raw_pixels() const - { - return raw_surface_->pixels; - } - - /** - * Wrapper for SDL_ConvertSurface(). - */ - Surface convert_format(const SDL_PixelFormat new_format) const - { - SDL_Surface* new_raw_surface = SDL_ConvertSurface(get_raw(), new_format); - - // TODO: When does this function fail? - if (new_raw_surface == nullptr) { - throw SDLException("SDL_ConvertSurface"); - } - - return Surface(new_raw_surface); - } - }; -} diff --git a/src/wrappers/sdl/utils.cppm b/src/wrappers/sdl/utils.cppm deleted file mode 100644 index 96dd45e..0000000 --- a/src/wrappers/sdl/utils.cppm +++ /dev/null @@ -1,23 +0,0 @@ -module; - -#include - -export module wrappers.sdl.utils; - -import std; - -export namespace sdl -{ - /** - * Convert a string returned by an SDL function to std::string and free the original memory using SDL_free(). - * - * Usage: - * std::string foo = wrap_string(SDL_FunctionThatReturnsAString()); - */ - constexpr std::string wrap_string(char* raw_str) - { - std::string str{raw_str}; - SDL_free(raw_str); - return str; - } -} diff --git a/src/wrappers/sdl/video.cppm b/src/wrappers/sdl/video.cppm deleted file mode 100644 index f393a43..0000000 --- a/src/wrappers/sdl/video.cppm +++ /dev/null @@ -1,101 +0,0 @@ -module; - -#include -#include - -export module wrappers.sdl.video; - -import std; -import utils.memory; -import wrappers.sdl.error; -import wrappers.sdl.rect; - -export namespace sdl -{ - /** - * Wrapper for SDL_Window that manages its lifecycle with a unique_ptr. - */ - class Window - { - std::unique_ptr< - SDL_Window, - utils::FuncDeleter - > raw_window_; - - public: - Window() = delete; - - explicit Window(SDL_Window* raw_window) - : raw_window_{raw_window} - {} - - constexpr SDL_Window* get_raw() const - { - assert(raw_window_); - return raw_window_.get(); - } - - /** - * Wrapper for SDL_GetWindowPixelDensity(). - */ - constexpr float get_pixel_density() const - { - return SDL_GetWindowPixelDensity(get_raw()); - } - - /** - * Wrapper for SDL_GetWindowDisplayScale(). - */ - constexpr float get_display_scale() const - { - return SDL_GetWindowDisplayScale(get_raw()); - } - - /** - * Wrapper for SDL_GetWindowSize(). - */ - std::pair get_size() const - { - int width, height; - - if (!SDL_GetWindowSize(get_raw(), &width, &height)) { - throw SDLException("SDL_GetWindowSize"); - } - - return {width, height}; - } - - /** - * Wrapper for SDL_SetTextInputArea(). - * Uses assert to verify the function returned true. - */ - constexpr void set_text_input_area(const Rect* rect, const int cursor) const - { - if (!SDL_SetTextInputArea(get_raw(), rect, cursor)) { - assert(!"SDL_SetTextInputArea returned false"); - } - } - - /** - * Wrapper for SDL_StartTextInput(). - * Uses assert to verify the function returned true. - */ - constexpr void start_text_input() const - { - if (!SDL_StartTextInput(get_raw())) { - assert(!"SDL_StartTextInput returned false"); - } - } - - /** - * Wrapper for SDL_StopTextInput(). - * Uses assert to verify the function returned true. - */ - constexpr void stop_text_input() const - { - if (!SDL_StopTextInput(get_raw())) { - assert(!"SDL_StopTextInput returned false"); - } - } - }; -} diff --git a/src/wrappers/sdl_image.cppm b/src/wrappers/sdl_image.cppm deleted file mode 100644 index 7312e9c..0000000 --- a/src/wrappers/sdl_image.cppm +++ /dev/null @@ -1,43 +0,0 @@ -module; - -#include -#include - -export module wrappers.sdl_image; - -import std; -import utils.memory; -import wrappers.sdl.error; -import wrappers.sdl.render; -import wrappers.sdl.surface; - -export namespace sdl_image -{ - /** - * Wrapper for IMG_Load(). - */ - sdl::Surface load(const std::string& filename) - { - SDL_Surface* raw_surface = IMG_Load(filename.c_str()); - - if (raw_surface == nullptr) { - throw sdl::SDLException("IMG_Load"); - } - - return sdl::Surface{raw_surface}; - } - - /** - * Wrapper for IMG_LoadTexture(). - */ - sdl::Texture load_texture(const sdl::Renderer& renderer, const std::string& filename) - { - SDL_Texture* raw_texture = IMG_LoadTexture(renderer.get_raw(), filename.c_str()); - - if (raw_texture == nullptr) { - throw sdl::SDLException("IMG_LoadTexture"); - } - - return sdl::Texture{raw_texture}; - } -} diff --git a/tools/shuriken.py b/tools/shuriken.py index b7f38a7..e1ae8aa 100755 --- a/tools/shuriken.py +++ b/tools/shuriken.py @@ -3,13 +3,14 @@ ############################################ # shuriken - A ninja-based C++ build tool # ------------------------------------------ -# Version: 0.1.0 (modified) +# Version: 0.1.0 # Author: binaryDiv # License: MIT License # https://git.0xbd.space/binaryDiv/shuriken ############################################ import argparse +import copy import json import shlex import subprocess @@ -17,7 +18,7 @@ import sys from collections import namedtuple from dataclasses import asdict, dataclass, field from pathlib import Path -from typing import Any, Self +from typing import Any import yaml @@ -59,43 +60,10 @@ class TargetConfig: return f"{self.linker_args or ''} {self.linker_args_extra or ''}".strip() -@dataclass(kw_only=True) -class MergedTargetConfig(TargetConfig): - cpp_compiler: str = '' - cpp_standard: str = '' - cpp_flags: str = '' - cpp_flags_extra: str = '' - - linker_flags: str = '' - linker_flags_extra: str = '' - linker_args: str = '' - linker_args_extra: str = '' - - output_file: str = '' - run_command: str = '' - - @classmethod - def create_from(cls, target_defaults: TargetConfig, target_config: TargetConfig) -> Self: - merged_config = cls() - merged_config.update(asdict(target_defaults)) - merged_config.update(asdict(target_config)) - return merged_config - - def validate(self) -> None: - # Validate that all required fields are set - if not self.cpp_compiler: - raise ConfigValidationException('cpp_compiler must be set') - if not self.cpp_standard: - raise ConfigValidationException('cpp_standard must be set') - if not self.output_file: - raise ConfigValidationException('output_file must be set') - - @dataclass(kw_only=True) class ShurikenConfig: __path_fields = ['source_dir', 'build_dir'] __list_fields = [] - __bool_fields = ['enable_import_std'] source_dir: Path = Path('src') build_dir: Path = Path('build') @@ -103,16 +71,14 @@ class ShurikenConfig: defaults: TargetConfig = field(default_factory=lambda: TargetConfig( cpp_compiler='clang++', cpp_standard='c++20', + cpp_flags='', + linker_flags='', + linker_args='', )) - enable_import_std: bool = False - default_target: str = '' targets: dict[str, TargetConfig] = field(default_factory=dict) - # Merged target configs, generated from defaults and targets - _merged_target_configs = {} - def update(self, data: dict) -> None: for key, value in data.items(): # Ignore unknown config keys @@ -120,13 +86,13 @@ class ShurikenConfig: continue if key == 'defaults': - assert type(value) is dict + assert (type(value) is dict) self.defaults.update(value) elif key == 'targets': - assert type(value) is dict + assert (type(value) is dict) for target_name, target_config in value.items(): - assert type(target_name) is str and len(target_name) > 0 - assert type(target_config) is dict or target_config is None + assert (type(target_name) is str and len(target_name) > 0) + assert (type(target_config) is dict or target_config is None) # Ignore "hidden" targets starting with a dot (can be used for YAML anchors) if target_name[0] == '.': @@ -139,18 +105,11 @@ class ShurikenConfig: elif key in self.__path_fields: setattr(self, key, Path(value)) elif key in self.__list_fields: - assert type(value) is list + assert (type(value) is list) setattr(self, key, list(str(item) for item in value)) - elif key in self.__bool_fields: - assert type(value) is bool - setattr(self, key, value) else: setattr(self, key, str(value)) - # Generate merged target configs (we generate all of them so that we can validate the full config) - for target_name, target_config in self.targets.items(): - self._merged_target_configs[target_name] = MergedTargetConfig.create_from(self.defaults, target_config) - def update_from_yaml(self, file_path: Path) -> None: with file_path.open('r') as file: self.update(yaml.safe_load(file)) @@ -172,18 +131,23 @@ class ShurikenConfig: raise ConfigValidationException('default_target is not defined in targets') # Validate individual build targets - for target_name, merged_target_config in self._merged_target_configs.items(): - try: - merged_target_config.validate() - except ConfigValidationException as exc: - raise ConfigValidationException(f'Target "{target_name}": {str(exc)}') + for target_name, target_config in self.targets.items(): + if not target_config.cpp_compiler and not self.defaults.cpp_compiler: + raise ConfigValidationException(f'Target "{target_name}": cpp_compiler must be set') + if not target_config.cpp_standard and not self.defaults.cpp_standard: + raise ConfigValidationException(f'Target "{target_name}": cpp_standard must be set') + if not target_config.output_file and not self.defaults.output_file: + raise ConfigValidationException(f'Target "{target_name}": output_file must be set') def get_ninja_file_path(self, target: str) -> Path: return self.build_dir / f'build.{target}.ninja' - def get_merged_target_config(self, target: str) -> MergedTargetConfig: + def get_merged_target_config(self, target: str) -> TargetConfig: assert target in self.targets, f'Target "{target}" not defined!' - return self._merged_target_configs[target] + + merged_config = copy.deepcopy(self.defaults) + merged_config.update(asdict(self.targets[target])) + return merged_config class ShurikenArgumentParser: @@ -222,8 +186,8 @@ class ShurikenArgumentParser: help='Print generated Ninja file to stdout instead of writing to a file', ) - _subparser_build = subparsers.add_parser('build', help='Build project (default command)') - _subparser_compdb = subparsers.add_parser('compdb', help='Generate compilation database') + subparser_build = subparsers.add_parser('build', help='Build project (default command)') + subparser_compdb = subparsers.add_parser('compdb', help='Generate compilation database') subparser_run = subparsers.add_parser('run', help='Build and run project') subparser_run.add_argument( @@ -232,13 +196,6 @@ class ShurikenArgumentParser: help='Arguments that are passed to the application', ) - subparser_lint = subparsers.add_parser('lint', help='Lint the project') - subparser_lint.add_argument( - 'lint_files', - nargs='*', - help='Files to lint (defaults to all source files)', - ) - subparser_clean = subparsers.add_parser('clean', help='Remove all build files of the current target') subparser_clean.add_argument( '--all', @@ -247,11 +204,11 @@ class ShurikenArgumentParser: help='Remove all generated files from all targets', ) - _subparser_dump_config = subparsers.add_parser( + subparser_dump_config = subparsers.add_parser( 'dump-config', help='Dumps the parsed config as well as the effective build target config (for debugging)', ) - _subparser_dyndep = subparsers.add_parser( + subparser_dyndep = subparsers.add_parser( 'p1689-to-dyndeps', help='(Internal command) Parse p1689 file and generate Ninja dyndeps', ) @@ -341,9 +298,6 @@ class ShurikenCli: case 'run': self.run_project(selected_target) - case 'lint': - self.lint_project(selected_target) - case 'clean': if self.args.clean_all: self.log_info('Cleaning up *all* build targets') @@ -459,26 +413,6 @@ class ShurikenCli: except KeyboardInterrupt: self.log_info('Keyboard interrupt') - def lint_project(self, target: str) -> None: - # Build project using Ninja (necessary before linting) - self.build_project(target) - - # Collect files to lint - if self.args.lint_files: - files_args = shlex.join(self.args.lint_files) - else: - files_args = f"$(find {self.config.source_dir} -name '*.cpp' -or -name '*.cppm')" - - self.log_info('Running clangd-tidy') - run_result = subprocess.run( - f'clangd-tidy -p {self.config.build_dir} --allow-extensions cpp,cppm {files_args}', - shell=True, - ) - - if run_result.returncode > 0: - self.log_error(f'clangd-tidy exited with return code {run_result.returncode}') - exit(1) - def clean_project(self, target: str) -> None: ninja_file_path = self.config.get_ninja_file_path(target) @@ -520,14 +454,13 @@ class ShurikenCli: print('ninja_dyndep_version = 1\n') for out, deps in build_dependencies.items(): if deps: - print(f'build {out}: dyndep | {" ".join(str(dep) for dep in deps)}') + print(f'build {out}: dyndep | {" ".join(deps)}') else: print(f'build {out}: dyndep') CompileTargetCpp = namedtuple('CompileTargetCpp', ['src', 'obj']) CompileTargetCppm = namedtuple('CompileTargetCppm', ['src', 'obj', 'pcm']) -CompileTargetStdModule = namedtuple('CompileTargetStdModule', ['original_src', 'generated_src', 'pcm']) class NinjaFileGenerator: @@ -537,7 +470,6 @@ class NinjaFileGenerator: compile_targets_cpp: list[CompileTargetCpp] compile_targets_cppm: list[CompileTargetCppm] - compile_targets_std_module: list[CompileTargetStdModule] link_target_obj_files: list[str] def __init__(self, *, config: ShurikenConfig, target: str): @@ -555,7 +487,6 @@ class NinjaFileGenerator: def collect_compile_targets(self) -> None: self.compile_targets_cpp = [] self.compile_targets_cppm = [] - self.compile_targets_std_module = [] # Compile targets for .cpp files for src_file in self.find_source_files('**/*.cpp'): @@ -568,25 +499,12 @@ class NinjaFileGenerator: # Compile targets for .cppm files for src_file in self.find_source_files('**/*.cppm'): relative_name = src_file.relative_to(self.config.source_dir) - module_name = '.'.join(relative_name.with_suffix('.pcm').parts) self.compile_targets_cppm.append(CompileTargetCppm( src=str(src_file), obj=f"$builddir/obj/{relative_name.with_suffix('.o')}", - pcm=f"$builddir/pcm/{module_name}", + pcm=f"$builddir/pcm/{'.'.join(relative_name.with_suffix('.pcm').parts)}", )) - # Compile targets for standard library modules ("import std") - if self.config.enable_import_std: - # NOTE: Add std.compat to the list (here and a few other places) if we ever need it. For now, std is enough. - std_module_source_finder = StdModuleSourceFinder(config=self.config, target=self.target) - std_module_sources = std_module_source_finder.find_module_sources(['std']) - for module_name, module_src_path in std_module_sources.items(): - self.compile_targets_std_module.append(CompileTargetStdModule( - original_src=str(module_src_path), - generated_src=f"$builddir/generated/stdlib/{module_name}.cppm", - pcm=f"$builddir/pcm/{module_name}.pcm", - )) - # Link target for output file (executable): gather all object files self.link_target_obj_files = sorted( target.obj for target in self.compile_targets_cpp + self.compile_targets_cppm @@ -600,25 +518,7 @@ class NinjaFileGenerator: config = self.config target = self.target target_config = self.target_config - build_dir = config.build_dir - ninja_file_path = config.get_ninja_file_path(config.default_target) - cpp_compiler = target_config.cpp_compiler - - # Generate list of all source input files to be used as implicit dependencies for the dyndep file - dyndep_file_implicit_inputs = ' '.join(sorted( - t.src for t in self.compile_targets_cpp + self.compile_targets_cppm - )) - - # Generate list of all .dep files that are generated implicitly during dyndep file generation. - # This is mostly necessary so that Ninja can create the directories for these files beforehand. - dyndep_file_implicit_outputs = ' '.join(sorted( - f'{t.obj}.dep' for t in self.compile_targets_cpp + self.compile_targets_cppm - )) - - # Generate list of generated source files (currently just the copied std module file) - generated_source_files = ' '.join(sorted( - t.generated_src for t in self.compile_targets_std_module - )) + build_dir = self.config.build_dir # Generate Ninja file return f""" @@ -630,39 +530,27 @@ compdb_file = {build_dir}/compile_commands.json dyndep_file = $builddir/.ninja_dyndep # Compiler flags -cpp_base_flags = -std={target_config.cpp_standard} -fprebuilt-module-path=$builddir/pcm/ -cpp_flags = $cpp_base_flags {target_config.merged_cpp_flags} -cpp_std_module_flags = $cpp_base_flags -Wno-reserved-module-identifier -x c++-module +cpp_flags = -std={target_config.cpp_standard} -fprebuilt-module-path=$builddir/pcm/ {target_config.merged_cpp_flags} linker_flags = {target_config.merged_linker_flags} linker_args = {target_config.merged_linker_args} # -- RULES -# Rule to copy an arbitrary file (or multiple) from in to out -rule copy - command = cp $in $out - # Rule to compile a .cpp file to a .o file rule cpp - depfile = $out.dep - command = {cpp_compiler} $cpp_flags -MMD -MF $out.dep -c $in -o $out + command = {target_config.cpp_compiler} $cpp_flags -c $in -o $out # Rule to compile a .cppm file (C++20 module) to a .o file ($out) and a .pcm file ($pcm_out) rule cppm - depfile = $out.dep - command = {cpp_compiler} $cpp_flags -MMD -MF $out.dep -c $in -o $out -fmodule-output=$pcm_out - -# Rule to compile a standard library module to a .pcm file (e.g. "import std") -rule std_module - command = {cpp_compiler} $cpp_std_module_flags --precompile $in -o $out + command = {target_config.cpp_compiler} $cpp_flags -c $in -o $out -fmodule-output=$pcm_out # Rule to link several .o files to an executable rule link - command = {cpp_compiler} $linker_flags -o $out $in $linker_args + command = {target_config.cpp_compiler} $linker_flags -o $out $in $linker_args # Rule to generate a compilation database (JSON file) from the build targets defined in a Ninja file rule generate_compdb - command = ninja -f $in -t compdb cpp cppm std_module > $out + command = ninja -f $in -t compdb cpp cppm > $out # Rule to generate a Ninja dyndep file for C++ module dependencies based on a compilation database rule generate_dyndeps @@ -672,13 +560,13 @@ rule generate_dyndeps # -- STATIC BUILD TARGETS # Generate compilation database from default target Ninja file -build $compdb_file: generate_compdb {ninja_file_path} | generate_source_files +build $compdb_file: generate_compdb {self.config.get_ninja_file_path(config.default_target)} # Shortcut alias to generate compilation database build compdb: phony $compdb_file # Generate Ninja dyndep file from compilation database -build $dyndep_file | {dyndep_file_implicit_outputs}: generate_dyndeps $compdb_file | {dyndep_file_implicit_inputs} +build $dyndep_file: generate_dyndeps $compdb_file # -- GENERATED BUILD TARGETS @@ -687,19 +575,15 @@ build $dyndep_file | {dyndep_file_implicit_outputs}: generate_dyndeps $compdb_fi build $builddir/{target_config.output_file}: link {' '.join(self.link_target_obj_files)} default $builddir/{target_config.output_file} -# Phony target that depends on all generated source files -build generate_source_files: phony {generated_source_files} - {self.render_compile_targets()} # End of build.{target}.ninja """.strip() def render_compile_targets(self) -> str: - rendered_targets = list(map(self.render_compile_target_std_module, self.compile_targets_std_module)) - rendered_targets += list(map(self.render_compile_target_cpp, self.compile_targets_cpp)) - rendered_targets += list(map(self.render_compile_target_cppm, self.compile_targets_cppm)) - return '\n\n'.join(rendered_targets) + rendered_targets_cpp = [self.render_compile_target_cpp(target) for target in self.compile_targets_cpp] + rendered_targets_cppm = [self.render_compile_target_cppm(target) for target in self.compile_targets_cppm] + return '\n\n'.join(rendered_targets_cpp + rendered_targets_cppm) @staticmethod def render_compile_target_cpp(target: CompileTargetCpp) -> str: @@ -716,34 +600,23 @@ build {target.obj} | {target.pcm}: cppm {target.src} || $dyndep_file pcm_out = {target.pcm} """.strip() - @staticmethod - def render_compile_target_std_module(target: CompileTargetStdModule) -> str: - return f""" -build {target.generated_src}: copy {target.original_src} - -build {target.pcm}: std_module {target.generated_src} || $dyndep_file - dyndep = $dyndep_file -""".strip() - class BuildDependencyParser: config: ShurikenConfig target: str - def __init__(self, *, config: ShurikenConfig, target: str): + def __init__(self, config: ShurikenConfig, target: str): self.config = config self.target = target - def parse_build_dependencies(self, parsed_p1689: dict[str, Any]) -> dict[Path, list[Path]]: + def parse_build_dependencies(self, parsed_p1689: dict[str, Any]) -> dict[str, list[str]]: # Shortcut variables source_dir = self.config.source_dir target_build_dir = self.config.build_dir / self.target default_target_build_dir = self.config.build_dir / self.config.default_target - # Map of module names to PCM file paths - module_map: dict[str, Path] = {} - # Validate module name and path convention, construct map of module names to PCM file paths + module_map = {} for rule in parsed_p1689['rules']: provides = rule.get('provides', []) if provides: @@ -751,19 +624,15 @@ class BuildDependencyParser: assert provides[0]['is-interface'] is True, f"Rule provides non-interface module: {rule}" module_name: str = provides[0]['logical-name'] - assert module_name not in module_map, f'Module "{module_name}" is provided more than once: {rule}' + assert module_name not in module_map, f"Module {module_name} is provided more than once: {rule}" - if module_name != 'std': - expected_source_path = str(Path(source_dir, module_name.replace('.', '/') + '.cppm')) - assert provides[0]['source-path'] == expected_source_path, \ - f"Module name does not match source path: {rule}" + expected_source_path = str(Path(source_dir, module_name.replace('.', '/') + '.cppm')) + assert provides[0]['source-path'] == expected_source_path, f"Module name does not match source path: {rule}" - module_map[module_name] = Path(target_build_dir, 'pcm', module_name + '.pcm') - - # Collect all module dependencies to generate dyndeps - build_dependencies: dict[Path, list[Path]] = {} + module_map[module_name] = str(Path(target_build_dir, 'pcm', module_name + '.pcm')) # Parse rules and get module dependencies + build_dependencies = {} for rule in parsed_p1689['rules']: # Compilation database contains "build/{default_target}" paths, we need to map them to "build/{target}" out = target_build_dir / Path(rule['primary-output']).relative_to(default_target_build_dir) @@ -771,65 +640,11 @@ class BuildDependencyParser: build_dependencies[out] = [] for require in rule.get('requires', []): module_name = require['logical-name'] - assert module_name in module_map, f'Module "{module_name}" not found in module map' + assert module_name in module_map, f'Module {module_name} not found in module map' build_dependencies[out].append(module_map[module_name]) return build_dependencies -class StdModuleSourceFinder: - cpp_compiler: str - - def __init__(self, *, config: ShurikenConfig, target: str): - self.cpp_compiler = config.get_merged_target_config(target).cpp_compiler - - def find_module_sources(self, module_names: list[str]) -> dict[str, Path]: - # Get normalized path of standard library "module manifest" (JSON file containing source paths for std modules) - module_manifest_path = self.get_module_manifest_path() - module_manifest_base_dir = module_manifest_path.parent - - # Parse module manifest JSON file - with module_manifest_path.open('r') as f: - module_manifest_json = json.load(f) - - # Collect the module source paths - std_module_sources: dict[str, Path] = {} - for modules_item in module_manifest_json['modules']: - # Ignore modules we don't need (probably just std.compat) - module_name = modules_item['logical-name'] - if module_name not in module_names: - continue - - # The source path is usually relative to the module manifest file, convert to absolute path - module_src_path = Path(modules_item['source-path']) - if not module_src_path.is_absolute(): - module_src_path = module_manifest_base_dir / module_src_path - if not module_src_path.is_file(): - raise Exception(f'Source path for module "{module_name}" is not a file: {module_src_path}') - - # Resolve symlinks and normalize path - std_module_sources[module_name] = module_src_path.resolve() - - return std_module_sources - - def get_module_manifest_path(self) -> Path: - # Use compiler to get the path to the module manifest JSON file - clang_cmd = [self.cpp_compiler, '-print-library-module-manifest-path'] - clang_cmd_result = subprocess.run(clang_cmd, capture_output=True) - - if clang_cmd_result.returncode != 0: - cmd_stderr = clang_cmd_result.stderr.decode().strip() - raise Exception(f'Subprocess "{" ".join(clang_cmd)}" failed. stderr:\n{cmd_stderr}') - - # Read path from subprocess stdout - module_manifest_path = Path(clang_cmd_result.stdout.decode().strip()) - - if not module_manifest_path.is_file(): - raise Exception(f'Could not locate stdlib module manifest: {module_manifest_path}') - - # Resolve symlinks and normalize path - return module_manifest_path.resolve() - - if __name__ == '__main__': ShurikenCli().run() diff --git a/vendor/README.md b/vendor/README.md deleted file mode 100644 index c729343..0000000 --- a/vendor/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# Vendor dependencies - -TODO: This should be automated with shuriken at some point. For now, manual instructions. - - -## System dependencies - -For now, SDL3 and SDL3_image need to be installed on the system. They should be vendored at some point. - -Same for freetype, which is required by RmlUi. - - -## RmlUi - -[RmlUi](https://github.com/mikke89/RmlUi) is a UI library. - -Within the `vendor` directory, run: - -```shell -# Either clone git repository or download and unpack archive (version 6.2) -git clone git@github.com:mikke89/RmlUi.git RmlUi --branch 6.2 -cd RmlUi - -# Configure and build with cmake (use samples or standalone preset) -cmake -G Ninja -B Build --preset samples -DBUILD_SHARED_LIBS=false -DRMLUI_BACKEND=SDL_SDLrenderer -cmake --build Build - -# Optionally test by running a sample -./Build/rmlui_sample_demo -```