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/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..5983465 100644 --- a/shuriken.yaml +++ b/shuriken.yaml @@ -1,18 +1,7 @@ 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 diff --git a/src/rutile/app.cppm b/src/app.cppm similarity index 59% rename from src/rutile/app.cppm rename to src/app.cppm index 37deff0..7b59948 100644 --- a/src/rutile/app.cppm +++ b/src/app.cppm @@ -1,21 +1,22 @@ module; -#include +#include +#include +#include -export module rutile.app; +export module app; -import std; -import rutile.config; -import rutile.core.engine; -import rutile.game.game; +import config; +import core.engine; +import game.game; import wrappers.sdl; -export namespace rutile +export { class App { - std::unique_ptr engine_{nullptr}; - std::unique_ptr game_{nullptr}; + std::unique_ptr engine_{nullptr}; + std::unique_ptr game_{nullptr}; public: App() = default; @@ -24,20 +25,20 @@ export namespace rutile { 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"); + sdl::SetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING, config::app_name); + sdl::SetAppMetadataProperty(SDL_PROP_APP_METADATA_VERSION_STRING, config::app_version); + sdl::SetAppMetadataProperty(SDL_PROP_APP_METADATA_IDENTIFIER_STRING, config::app_identifier); + sdl::SetAppMetadataProperty(SDL_PROP_APP_METADATA_CREATOR_STRING, config::app_creator); + sdl::SetAppMetadataProperty(SDL_PROP_APP_METADATA_COPYRIGHT_STRING, config::app_copyright); + sdl::SetAppMetadataProperty(SDL_PROP_APP_METADATA_URL_STRING, config::app_url); + sdl::SetAppMetadataProperty(SDL_PROP_APP_METADATA_TYPE_STRING, "game"); // Initialize SDL subsystems - sdl::initialize_sdl(sdl::InitFlags::Video | sdl::InitFlags::Events); + sdl::Init(sdl::InitFlags::Video | sdl::InitFlags::Events); // Create engine (includes window and renderer) and game state - engine_ = Engine::create(); - game_ = Game::create(*engine_); + engine_ = core::Engine::create(); + game_ = game::Game::create(*engine_); } catch (const std::runtime_error& e) { std::cerr << "Unhandled exception during initialization: " << e.what() << '\n'; @@ -47,7 +48,7 @@ export namespace rutile return sdl::AppResult::Continue; } - sdl::AppResult handle_event(const sdl::Event& event) + sdl::AppResult handle_event(const sdl::Event* event) { try { if (!engine_->handle_event(event)) { diff --git a/src/rutile/config.cppm b/src/config.cppm similarity index 91% rename from src/rutile/config.cppm rename to src/config.cppm index 2e36783..0ed9a38 100644 --- a/src/rutile/config.cppm +++ b/src/config.cppm @@ -1,16 +1,16 @@ module; +#include + #ifdef NDEBUG #define DEBUG_BOOL false #else #define DEBUG_BOOL true #endif -export module rutile.config; +export module config; -import std; - -export namespace rutile::config +export namespace config { constexpr auto debug = DEBUG_BOOL; diff --git a/src/rutile/core/drawing/sprite.cppm b/src/core/drawing/sprite.cppm similarity index 95% rename from src/rutile/core/drawing/sprite.cppm rename to src/core/drawing/sprite.cppm index 1158bde..31224cd 100644 --- a/src/rutile/core/drawing/sprite.cppm +++ b/src/core/drawing/sprite.cppm @@ -1,14 +1,15 @@ module; #include +#include +#include -export module rutile.core.drawing.sprite; +export module core.drawing.sprite; -import std; -import rutile.core.render_server; +import core.render_server; import wrappers.sdl; -export namespace rutile +export namespace core { class Sprite { diff --git a/src/rutile/core/drawing/tile_map.cppm b/src/core/drawing/tile_map.cppm similarity index 94% rename from src/rutile/core/drawing/tile_map.cppm rename to src/core/drawing/tile_map.cppm index de66b2c..31bfbfa 100644 --- a/src/rutile/core/drawing/tile_map.cppm +++ b/src/core/drawing/tile_map.cppm @@ -1,18 +1,19 @@ module; #include +#include +#include -export module rutile.core.drawing.tile_map; +export module core.drawing.tile_map; -import std; -import rutile.core.drawing.tile_set; -import rutile.core.render_server; +import core.drawing.tile_set; +import core.render_server; import wrappers.sdl; -export namespace rutile +export namespace core { // Restrict map size to 16 bit per dimension, so that width*height still fits within size_t - using MapCoord = std::uint16_t; + using MapCoord = uint16_t; class TileMap { diff --git a/src/rutile/core/drawing/tile_set.cppm b/src/core/drawing/tile_set.cppm similarity index 95% rename from src/rutile/core/drawing/tile_set.cppm rename to src/core/drawing/tile_set.cppm index ddad86c..39ba81d 100644 --- a/src/rutile/core/drawing/tile_set.cppm +++ b/src/core/drawing/tile_set.cppm @@ -1,14 +1,16 @@ module; #include +#include +#include +#include -export module rutile.core.drawing.tile_set; +export module core.drawing.tile_set; -import std; -import rutile.core.render_server; +import core.render_server; import wrappers.sdl; -export namespace rutile +export namespace core { using TileSetID = unsigned int; using TileID = unsigned int; diff --git a/src/rutile/core/engine.cppm b/src/core/engine.cppm similarity index 73% rename from src/rutile/core/engine.cppm rename to src/core/engine.cppm index 984f323..d9bd5ad 100644 --- a/src/rutile/core/engine.cppm +++ b/src/core/engine.cppm @@ -1,16 +1,15 @@ module; #include +#include -export module rutile.core.engine; +export module core.engine; -import std; -import rutile.config; -import rutile.core.render_server; -import rutile.core.ui.ui_server; +import config; +import core.render_server; import wrappers.sdl; -export namespace rutile +export namespace core { class Engine { @@ -22,13 +21,11 @@ export namespace rutile 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_} + render_server_{std::move(renderer)} { instantiated_ = true; } @@ -52,21 +49,19 @@ export namespace rutile // Prevent the class from being instantiated multiple times assert(!instantiated_); - auto [sdl_window, sdl_renderer] = sdl::create_window_and_renderer( + auto [sdl_window, sdl_renderer] = sdl::CreateWindowAndRenderer( 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 @@ -84,24 +79,15 @@ export namespace rutile 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) + bool handle_event(const sdl::Event* event) { - if (event.type == sdl::EventTypes::Quit) { + if (event->type == sdl::EventType::Quit) { // Exit the application keep_running_ = false; return true; } - if (ui_server_.handle_event(event)) { - return true; - } - return false; } diff --git a/src/rutile/core/render_server.cppm b/src/core/render_server.cppm similarity index 91% rename from src/rutile/core/render_server.cppm rename to src/core/render_server.cppm index 7366b13..bcb4c9f 100644 --- a/src/rutile/core/render_server.cppm +++ b/src/core/render_server.cppm @@ -1,13 +1,15 @@ module; -export module rutile.core.render_server; +#include +#include -import std; -import rutile.core.resource_manager; -import rutile.core.texture_loader; +export module core.render_server; + +import core.resource_manager; +import core.texture_loader; import wrappers.sdl; -export namespace rutile +export namespace core { using TextureID = unsigned int; using TextureManager = ResourceManager; @@ -25,9 +27,7 @@ export namespace rutile : renderer_{std::move(renderer)}, texture_loader_{renderer_}, texture_manager_{texture_loader_} - { - renderer_.set_vsync(1); - } + {} // No copy or move operations RenderServer(const RenderServer&) = delete; diff --git a/src/rutile/core/resource_manager.cppm b/src/core/resource_manager.cppm similarity index 96% rename from src/rutile/core/resource_manager.cppm rename to src/core/resource_manager.cppm index b73f184..2e947f5 100644 --- a/src/rutile/core/resource_manager.cppm +++ b/src/core/resource_manager.cppm @@ -1,12 +1,14 @@ module; #include +#include +#include +#include +#include -export module rutile.core.resource_manager; +export module core.resource_manager; -import std; - -export namespace rutile +export namespace core { template concept IsResourceLoader = requires(ResourceLoaderType loader, const std::string& name) diff --git a/src/rutile/core/texture_loader.cppm b/src/core/texture_loader.cppm similarity index 79% rename from src/rutile/core/texture_loader.cppm rename to src/core/texture_loader.cppm index 78cb3df..32548c4 100644 --- a/src/rutile/core/texture_loader.cppm +++ b/src/core/texture_loader.cppm @@ -1,13 +1,14 @@ module; -export module rutile.core.texture_loader; +#include -import std; -import rutile.core.resource_manager; +export module core.texture_loader; + +import core.resource_manager; import wrappers.sdl; import wrappers.sdl_image; -export namespace rutile +export namespace core { class TextureLoader { @@ -28,7 +29,7 @@ export namespace rutile sdl::Texture load_resource(const std::string& filename) const { - return sdl_image::load_texture(renderer_, filename); + return sdl_image::LoadTexture(renderer_, filename); } }; } diff --git a/src/rutile/game/game.cppm b/src/game/game.cppm similarity index 68% rename from src/rutile/game/game.cppm rename to src/game/game.cppm index 8b7a1ff..a18cc6f 100644 --- a/src/rutile/game/game.cppm +++ b/src/game/game.cppm @@ -1,20 +1,21 @@ module; #include -#include +#include +#include +#include -export module rutile.game.game; +export module 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 core.drawing.sprite; +import core.drawing.tile_map; +import core.drawing.tile_set; +import core.engine; +import core.render_server; import wrappers.sdl; import wrappers.sdl_image; -export namespace rutile +export namespace game { class Game { @@ -22,21 +23,21 @@ export namespace rutile static bool instantiated_; // Reference to the engine - Engine& engine_; + core::Engine& engine_; // Sprites for testing - Sprite player_sprite_; - std::vector sprites_; + core::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_; + core::TileSet tile_set_; + core::TileMap tile_map_; // Private constructor - explicit Game(Engine& engine) + explicit Game(core::Engine& engine) : engine_(engine), player_sprite_{ - Sprite::create_from_texture( + core::Sprite::create_from_texture( engine_.get_render_server(), "assets/sprites/neocat_64.png", std::nullopt, @@ -45,7 +46,7 @@ export namespace rutile ) }, tile_set_{ - TileSet::create_from_texture( + core::TileSet::create_from_texture( engine_.get_render_server(), "assets/tilesets/terrain.png", 32, @@ -62,12 +63,6 @@ export namespace rutile }, 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++) { @@ -75,8 +70,6 @@ export namespace rutile tile_map_.set_tile(x, y, tile_index); } } - - instantiated_ = true; } public: @@ -93,7 +86,7 @@ export namespace rutile instantiated_ = false; } - static std::unique_ptr create(Engine& engine) + static std::unique_ptr create(core::Engine& engine) { // Prevent the class from being instantiated multiple times assert(!instantiated_); @@ -103,20 +96,20 @@ export namespace rutile } // Handles an SDL event. Returns true if the event has been handled. - bool handle_event(const sdl::Event& event) + bool handle_event(const sdl::Event* event) { - if (event.type == sdl::EventTypes::MouseMotion) { - player_sprite_.set_position({event.motion.x, event.motion.y}); + if (event->type == sdl::EventType::MouseMotion) { + player_sprite_.set_position({event->motion.x, event->motion.y}); return true; } - if (event.type == sdl::EventTypes::MouseButtonUp) { + if (event->type == sdl::EventType::MouseButtonUp) { sprites_.push_back( - Sprite::create_from_texture( + core::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{event->motion.x, event->motion.y}, sdl::FPoint{-32, -32} ) ); @@ -128,8 +121,6 @@ export namespace rutile void update() { - // TODO: Move this to Engine - engine_.get_ui_server().update(); } void render() const @@ -141,14 +132,11 @@ export namespace rutile tile_map_.draw(render_server, tile_set_); // Render sprites - for (const Sprite& sprite : sprites_) { + for (const core::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(); } diff --git a/src/main.cpp b/src/main.cpp index a6e0353..4a0a4fc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,30 +1,30 @@ -import rutile.app; +import app; #define SDL_MAIN_USE_CALLBACKS #include SDL_AppResult SDL_AppInit(void** appstate, const int /*argc*/, char** /*argv*/) { - auto* app = new rutile::App; + auto* app = new 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)); + auto* app = static_cast(appstate); + return static_cast(app->handle_event(event)); } SDL_AppResult SDL_AppIterate(void* appstate) { - auto* app = static_cast(appstate); + auto* app = static_cast(appstate); return static_cast(app->iterate()); } void SDL_AppQuit(void* appstate, const SDL_AppResult /*result*/) { - const auto* app = static_cast(appstate); + const auto* app = static_cast(appstate); app->shutdown(); delete app; } 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 41622ff..0000000 --- a/src/rutile/core/ui/backend/rmlui_input_handler.cppm +++ /dev/null @@ -1,278 +0,0 @@ -module; - -#include -#include -#include - -export module rutile.core.ui.backend.rmlui_input_handler; - -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 f64528f..0000000 --- a/src/rutile/core/ui/backend/rmlui_render_interface.cppm +++ /dev/null @@ -1,200 +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 -#include -#include -#include - -export module rutile.core.ui.backend.rmlui_render_interface; - -import std; -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 - { - RMLUI_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 6c74193..0000000 --- a/src/rutile/core/ui/backend/rmlui_system_interface.cppm +++ /dev/null @@ -1,118 +0,0 @@ -// RmlUi SystemInterface implementation for SDL. -// Based on RmlUi example code: vendor/RmlUi/Backends/RmlUi_Platform_SDL.{cpp,h} - -module; - -#include -#include -#include -#include - -export module rutile.core.ui.backend.rmlui_system_interface; - -import utils.memory; -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 66eb4c9..0000000 --- a/src/rutile/core/ui/ui_server.cppm +++ /dev/null @@ -1,124 +0,0 @@ -module; - -#include -#include -#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.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/wrappers/sdl.cppm b/src/wrappers/sdl.cppm index 78f7c72..8e8088d 100644 --- a/src/wrappers/sdl.cppm +++ b/src/wrappers/sdl.cppm @@ -3,14 +3,9 @@ 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 index 0b12725..ceaedf0 100644 --- a/src/wrappers/sdl/error.cppm +++ b/src/wrappers/sdl/error.cppm @@ -1,11 +1,11 @@ module; -#include +#include +#include +#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). diff --git a/src/wrappers/sdl/events.cppm b/src/wrappers/sdl/events.cppm index 8ef341a..7bb2e61 100644 --- a/src/wrappers/sdl/events.cppm +++ b/src/wrappers/sdl/events.cppm @@ -1,6 +1,6 @@ module; -#include +#include export module wrappers.sdl.events; @@ -10,7 +10,7 @@ export namespace sdl using Event = SDL_Event; // Alias for EventType enum - using EventType = SDL_EventType; + using EventType_t = SDL_EventType; /** * Wrapper for the SDL_EventType enum. @@ -18,30 +18,13 @@ export namespace sdl * 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 + namespace EventType { - // 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; + constexpr EventType_t Quit = SDL_EVENT_QUIT; + constexpr EventType_t KeyDown = SDL_EVENT_KEY_DOWN; + constexpr EventType_t KeyUp = SDL_EVENT_KEY_UP; + constexpr EventType_t MouseMotion = SDL_EVENT_MOUSE_MOTION; + constexpr EventType_t MouseButtonUp = SDL_EVENT_MOUSE_BUTTON_UP; + constexpr EventType_t MouseButtonDown = SDL_EVENT_MOUSE_BUTTON_DOWN; } } diff --git a/src/wrappers/sdl/init.cppm b/src/wrappers/sdl/init.cppm index f673011..9044776 100644 --- a/src/wrappers/sdl/init.cppm +++ b/src/wrappers/sdl/init.cppm @@ -1,10 +1,10 @@ module; -#include +#include +#include export module wrappers.sdl.init; -import std; import wrappers.sdl.error; export namespace sdl @@ -31,7 +31,7 @@ export namespace sdl } /** - * Wrapper for the SDL_AppResult enum. + * Wrapper around the SDL_AppResult enum. */ enum class AppResult : std::underlying_type_t { @@ -41,9 +41,9 @@ export namespace sdl }; /** - * Wrapper for SDL_Init(). + * Wrapper around SDL_Init(). */ - constexpr void initialize_sdl(const InitFlags_t flags) + constexpr void Init(const InitFlags_t flags) { if (!SDL_Init(flags)) { throw SDLException("SDL_Init"); @@ -51,9 +51,9 @@ export namespace sdl } /** - * Wrapper for SDL_SetAppMetadataProperty(). + * Wrapper around SDL_SetAppMetadataProperty(). */ - constexpr void set_app_metadata_property(const char* property_name, const char* value) + constexpr void SetAppMetadataProperty(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 index 6e5dd32..a42648a 100644 --- a/src/wrappers/sdl/rect.cppm +++ b/src/wrappers/sdl/rect.cppm @@ -1,6 +1,6 @@ module; -#include +#include export module wrappers.sdl.rect; @@ -11,30 +11,30 @@ export namespace sdl using Point = SDL_Point; /** - * Wrapper for SDL_FRect using class inheritance. + * Wrapper around SDL_FRect using class inheritance. */ class FRect : public SDL_FRect { /** - * Wrapper for SDL_RectEmptyFloat(). + * Wrapper around SDL_RectEmptyFloat(). * @return True if the floating point rectangle takes no space. */ - bool is_empty() const + constexpr bool is_empty() const { return SDL_RectEmptyFloat(this); } }; /** - * Wrapper for SDL_Rect using class inheritance. + * Wrapper around SDL_Rect using class inheritance. */ class Rect : public SDL_Rect { /** - * Wrapper for SDL_RectEmpty(). + * Wrapper around SDL_RectEmpty(). * @return True if the rectangle takes no space. */ - bool is_empty() const + constexpr bool is_empty() const { return SDL_RectEmpty(this); } diff --git a/src/wrappers/sdl/render.cppm b/src/wrappers/sdl/render.cppm index a556c3f..0cf34e1 100644 --- a/src/wrappers/sdl/render.cppm +++ b/src/wrappers/sdl/render.cppm @@ -1,11 +1,11 @@ module; #include +#include #include export module wrappers.sdl.render; -import std; import utils.memory; import wrappers.sdl.error; import wrappers.sdl.rect; @@ -13,10 +13,8 @@ import wrappers.sdl.video; export namespace sdl { - using Vertex = SDL_Vertex; - /** - * Wrapper for SDL_Texture that manages its lifecycle with a unique_ptr. + * Wrapper around SDL_Texture that manages its lifecycle with a unique_ptr. */ class Texture { @@ -55,7 +53,7 @@ export namespace sdl }; /** - * Wrapper for SDL_Renderer that manages its lifecycle with a unique_ptr. + * Wrapper around SDL_Renderer that manages its lifecycle with a unique_ptr. */ class Renderer { @@ -78,41 +76,7 @@ export namespace sdl } /** - * 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(). + * Wrapper around SDL_RenderClear(). * Uses assert to verify the function returned true. */ constexpr void clear() const @@ -124,7 +88,7 @@ export namespace sdl } /** - * Wrapper for SDL_RenderPresent(). + * Wrapper around SDL_RenderPresent(). * Uses assert to verify the function returned true. */ constexpr void present() const @@ -135,7 +99,7 @@ export namespace sdl } /** - * Wrapper for SDL_RenderTexture(). + * Wrapper around SDL_RenderTexture(). * Uses assert to verify the function returned true. */ constexpr void render_texture( @@ -148,32 +112,14 @@ export namespace sdl 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(). + * Wrapper around SDL_CreateWindowAndRenderer(). * Returns a pair of a Window and a Renderer object. * Throws an SDLException on failure. */ - std::pair create_window_and_renderer( + std::pair CreateWindowAndRenderer( const std::string& title, const int width, const int height, 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 index f393a43..f80c93f 100644 --- a/src/wrappers/sdl/video.cppm +++ b/src/wrappers/sdl/video.cppm @@ -1,19 +1,17 @@ module; #include +#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. + * Wrapper around SDL_Window that manages its lifecycle with a unique_ptr. */ class Window { @@ -34,68 +32,5 @@ export namespace sdl 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 index 7312e9c..a434564 100644 --- a/src/wrappers/sdl_image.cppm +++ b/src/wrappers/sdl_image.cppm @@ -1,43 +1,28 @@ module; +#include #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(). + * Wrapper around IMG_LoadTexture(). */ - sdl::Surface load(const std::string& filename) + sdl::Texture LoadTexture(const sdl::Renderer& renderer, const std::string& filename) { - SDL_Surface* raw_surface = IMG_Load(filename.c_str()); + SDL_Texture* sdl_texture = IMG_LoadTexture(renderer.get_raw(), 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) { + if (sdl_texture == nullptr) { throw sdl::SDLException("IMG_LoadTexture"); } - return sdl::Texture{raw_texture}; + return sdl::Texture{sdl_texture}; } } diff --git a/tools/shuriken.py b/tools/shuriken.py index e5a1943..e1ae8aa 100755 --- a/tools/shuriken.py +++ b/tools/shuriken.py @@ -3,7 +3,7 @@ ############################################ # 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 @@ -64,7 +64,6 @@ class TargetConfig: 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') @@ -77,8 +76,6 @@ class ShurikenConfig: linker_args='', )) - enable_import_std: bool = False - default_target: str = '' targets: dict[str, TargetConfig] = field(default_factory=dict) @@ -89,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] == '.': @@ -108,11 +105,8 @@ 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)) @@ -192,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( @@ -202,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', @@ -217,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', ) @@ -311,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') @@ -429,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) @@ -490,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: @@ -507,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): @@ -525,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'): @@ -538,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 @@ -582,18 +530,12 @@ 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 command = {target_config.cpp_compiler} $cpp_flags -c $in -o $out @@ -602,17 +544,13 @@ rule cpp rule cppm command = {target_config.cpp_compiler} $cpp_flags -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 = {target_config.cpp_compiler} $cpp_std_module_flags --precompile $in -o $out - # Rule to link several .o files to an executable rule link 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 @@ -622,7 +560,7 @@ rule generate_dyndeps # -- STATIC BUILD TARGETS # Generate compilation database from default target Ninja file -build $compdb_file: generate_compdb {self.config.get_ninja_file_path(config.default_target)} | 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 @@ -637,23 +575,15 @@ build $dyndep_file: generate_dyndeps $compdb_file 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 {self.list_generated_source_files()} - {self.render_compile_targets()} # End of build.{target}.ninja """.strip() - def list_generated_source_files(self) -> str: - generated_files = [target.generated_src for target in self.compile_targets_std_module] - return ' '.join(generated_files) - 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: @@ -670,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: @@ -705,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) @@ -725,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 -```