diff --git a/CMakeLists.txt b/CMakeLists.txt index f16e457..c545de7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,11 +7,23 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) # Find SDL2 find_package(SDL2 REQUIRED) +# Collect all source files +set(SOURCES + src/main.cpp + src/core/Application.cpp + src/window/Window.cpp + src/renderer/Renderer.cpp + src/events/EventManager.cpp +) + # Add executable -add_executable(${PROJECT_NAME} src/main.cpp) +add_executable(${PROJECT_NAME} ${SOURCES}) # Link SDL2 target_link_libraries(${PROJECT_NAME} SDL2::SDL2) # Include directories for SDL2 target_include_directories(${PROJECT_NAME} PRIVATE ${SDL2_INCLUDE_DIRS}) + +# Set include directories for our source files +target_include_directories(${PROJECT_NAME} PRIVATE src) diff --git a/src/core/Application.cpp b/src/core/Application.cpp new file mode 100644 index 0000000..87f5b7c --- /dev/null +++ b/src/core/Application.cpp @@ -0,0 +1,121 @@ +#include "Application.h" +#include "Config.h" +#include "../utils/Utils.h" +#include + +Application::Application(const std::string& title, int width, int height) + : initialized_(false) { + + if (!initialize()) { + Utils::logError("Failed to initialize application"); + return; + } + + // Create window + window_ = std::make_unique(title, width, height); + if (!window_ || !window_->isValid()) { + Utils::logError("Failed to create window"); + return; + } + + // Create renderer + renderer_ = std::make_unique(window_->getHandle()); + if (!renderer_ || !renderer_->isValid()) { + Utils::logError("Failed to create renderer"); + return; + } + + // Create event manager + eventManager_ = std::make_unique(); + + // Register event callbacks + eventManager_->registerCallback(EventType::Quit, + [this](const EventData& event) { onQuit(event); }); + eventManager_->registerCallback(EventType::KeyDown, + [this](const EventData& event) { onKeyDown(event); }); + eventManager_->registerCallback(EventType::KeyUp, + [this](const EventData& event) { onKeyUp(event); }); + + initialized_ = true; +} + +Application::~Application() { + cleanup(); +} + +bool Application::initialize() { + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + std::cerr << "SDL2 initialization failed: " << SDL_GetError() << std::endl; + return false; + } + return true; +} + +void Application::cleanup() { + eventManager_.reset(); + renderer_.reset(); + window_.reset(); + SDL_Quit(); +} + +int Application::run() { + if (!initialized_) { + Utils::logError("Application not properly initialized"); + return 1; + } + + Utils::log("Starting application..."); + + // Main application loop + while (isRunning()) { + // Handle events + eventManager_->pollEvents(); + + // Update game logic + update(); + + // Render frame + render(); + } + + Utils::log("Application finished"); + return 0; +} + +void Application::update() { + // Update game logic here + // This is where you would update game objects, physics, etc. +} + +void Application::render() { + if (!renderer_) return; + + // Clear screen with black background + renderer_->setDrawColor(0, 0, 0, 255); + renderer_->clear(); + + // Render game objects here + // This is where you would draw sprites, shapes, etc. + + // Present the frame + renderer_->present(); +} + +void Application::onQuit(const EventData& event) { + stop(); +} + +void Application::onKeyDown(const EventData& event) { + const SDL_KeyboardEvent& keyEvent = event.sdlEvent.key; + + switch (keyEvent.keysym.sym) { + case SDLK_ESCAPE: + stop(); + break; + // Add more key handling here + } +} + +void Application::onKeyUp(const EventData& event) { + // Handle key up events here +} diff --git a/src/core/Application.h b/src/core/Application.h new file mode 100644 index 0000000..a6a6762 --- /dev/null +++ b/src/core/Application.h @@ -0,0 +1,48 @@ +#pragma once + +#include "Common.h" +#include "../window/Window.h" +#include "../renderer/Renderer.h" +#include "../events/EventManager.h" + +class Application { +public: + Application(const std::string& title, int width, int height); + ~Application(); + + // Disable copying + Application(const Application&) = delete; + Application& operator=(const Application&) = delete; + + // Main application loop + int run(); + + // Getters + Window* getWindow() const { return window_.get(); } + Renderer* getRenderer() const { return renderer_.get(); } + EventManager* getEventManager() const { return eventManager_.get(); } + + // Application state + bool isRunning() const { return eventManager_ && eventManager_->isRunning(); } + void stop() { if (eventManager_) eventManager_->stop(); } + +private: + // Core components + WindowPtr window_; + RendererPtr renderer_; + EventManagerPtr eventManager_; + + // Application state + bool initialized_; + + // Private methods + bool initialize(); + void cleanup(); + void update(); + void render(); + + // Event callbacks + void onQuit(const EventData& event); + void onKeyDown(const EventData& event); + void onKeyUp(const EventData& event); +}; diff --git a/src/core/Common.h b/src/core/Common.h new file mode 100644 index 0000000..c55abfe --- /dev/null +++ b/src/core/Common.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include + +// Forward declarations +class Window; +class Renderer; +class EventManager; +class Application; + +// Type aliases +using WindowPtr = std::unique_ptr; +using RendererPtr = std::unique_ptr; +using EventManagerPtr = std::unique_ptr; +using ApplicationPtr = std::unique_ptr; diff --git a/src/core/Config.h b/src/core/Config.h new file mode 100644 index 0000000..286ae0f --- /dev/null +++ b/src/core/Config.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace Config { + // Window settings + constexpr int DEFAULT_WINDOW_WIDTH = 800; + constexpr int DEFAULT_WINDOW_HEIGHT = 600; + constexpr const char* DEFAULT_WINDOW_TITLE = "Observations on the Sublime Dynamics of Eroding Matter"; + + // Rendering settings + constexpr bool VSYNC_ENABLED = true; + constexpr int TARGET_FPS = 60; + constexpr float TARGET_FRAME_TIME = 1.0f / TARGET_FPS; + + // Application settings + constexpr bool DEBUG_MODE = true; + constexpr bool LOGGING_ENABLED = true; + + // File paths + constexpr const char* ASSETS_PATH = "assets/"; + constexpr const char* CONFIG_PATH = "config/"; + constexpr const char* LOG_PATH = "logs/"; +} diff --git a/src/events/EventManager.cpp b/src/events/EventManager.cpp new file mode 100644 index 0000000..3c5ddfc --- /dev/null +++ b/src/events/EventManager.cpp @@ -0,0 +1,93 @@ +#include "EventManager.h" +#include + +EventManager::EventManager() : running_(true) { +} + +bool EventManager::pollEvents() { + SDL_Event event; + while (SDL_PollEvent(&event)) { + processEvent(event); + } + return running_; +} + +void EventManager::registerCallback(EventType type, EventCallback callback) { + callbacks_[type] = callback; +} + +void EventManager::unregisterCallback(EventType type) { + callbacks_.erase(type); +} + +void EventManager::processEvent(const SDL_Event& event) { + switch (event.type) { + case SDL_QUIT: + handleQuitEvent(); + break; + case SDL_KEYDOWN: + case SDL_KEYUP: + handleKeyEvent(event); + break; + case SDL_MOUSEMOTION: + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + handleMouseEvent(event); + break; + case SDL_WINDOWEVENT: + handleWindowEvent(event); + break; + } +} + +void EventManager::handleQuitEvent() { + running_ = false; + + // Call registered callback if any + auto it = callbacks_.find(EventType::Quit); + if (it != callbacks_.end()) { + EventData data{EventType::Quit, {}}; + it->second(data); + } +} + +void EventManager::handleKeyEvent(const SDL_Event& event) { + EventType type = (event.type == SDL_KEYDOWN) ? EventType::KeyDown : EventType::KeyUp; + + auto it = callbacks_.find(type); + if (it != callbacks_.end()) { + EventData data{type, event}; + it->second(data); + } +} + +void EventManager::handleMouseEvent(const SDL_Event& event) { + EventType type; + switch (event.type) { + case SDL_MOUSEMOTION: + type = EventType::MouseMove; + break; + case SDL_MOUSEBUTTONDOWN: + type = EventType::MouseButtonDown; + break; + case SDL_MOUSEBUTTONUP: + type = EventType::MouseButtonUp; + break; + default: + return; + } + + auto it = callbacks_.find(type); + if (it != callbacks_.end()) { + EventData data{type, event}; + it->second(data); + } +} + +void EventManager::handleWindowEvent(const SDL_Event& event) { + auto it = callbacks_.find(EventType::WindowEvent); + if (it != callbacks_.end()) { + EventData data{EventType::WindowEvent, event}; + it->second(data); + } +} diff --git a/src/events/EventManager.h b/src/events/EventManager.h new file mode 100644 index 0000000..f1c9b76 --- /dev/null +++ b/src/events/EventManager.h @@ -0,0 +1,48 @@ +#pragma once + +#include "../core/Common.h" +#include +#include + +enum class EventType { + Quit, + KeyDown, + KeyUp, + MouseMove, + MouseButtonDown, + MouseButtonUp, + WindowEvent +}; + +struct EventData { + EventType type; + SDL_Event sdlEvent; +}; + +class EventManager { +public: + EventManager(); + ~EventManager() = default; + + // Event handling + bool pollEvents(); + bool isRunning() const { return running_; } + void stop() { running_ = false; } + + // Event callbacks + using EventCallback = std::function; + void registerCallback(EventType type, EventCallback callback); + void unregisterCallback(EventType type); + + // Event processing + void processEvent(const SDL_Event& event); + +private: + bool running_; + std::unordered_map callbacks_; + + void handleQuitEvent(); + void handleKeyEvent(const SDL_Event& event); + void handleMouseEvent(const SDL_Event& event); + void handleWindowEvent(const SDL_Event& event); +}; diff --git a/src/main.cpp b/src/main.cpp index 231a050..d0f7df3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,61 +1,16 @@ -#include +#include "core/Application.h" +#include "core/Config.h" #include int main(int argc, char* argv[]) { - // Initialize SDL2 - if (SDL_Init(SDL_INIT_VIDEO) < 0) { - std::cerr << "SDL2 initialization failed: " << SDL_GetError() << std::endl; + try { + Application app(Config::DEFAULT_WINDOW_TITLE, Config::DEFAULT_WINDOW_WIDTH, Config::DEFAULT_WINDOW_HEIGHT); + return app.run(); + } catch (const std::exception& e) { + std::cerr << "Fatal error: " << e.what() << std::endl; + return 1; + } catch (...) { + std::cerr << "Unknown fatal error occurred" << std::endl; return 1; } - - // Create window - SDL_Window* window = SDL_CreateWindow( - "Observations on the Sublime Dynamics of Eroding Matter", - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - 800, 600, - SDL_WINDOW_SHOWN - ); - - if (!window) { - std::cerr << "Window creation failed: " << SDL_GetError() << std::endl; - SDL_Quit(); - return 1; - } - - // Create renderer - SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); - if (!renderer) { - std::cerr << "Renderer creation failed: " << SDL_GetError() << std::endl; - SDL_DestroyWindow(window); - SDL_Quit(); - return 1; - } - - // Main event loop - bool running = true; - SDL_Event event; - - while (running) { - // Handle events - while (SDL_PollEvent(&event)) { - if (event.type == SDL_QUIT) { - running = false; - } - } - - // Clear screen with black background - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); - SDL_RenderClear(renderer); - - // Present the renderer - SDL_RenderPresent(renderer); - } - - // Cleanup - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - SDL_Quit(); - - return 0; } diff --git a/src/renderer/Renderer.cpp b/src/renderer/Renderer.cpp new file mode 100644 index 0000000..ffbde71 --- /dev/null +++ b/src/renderer/Renderer.cpp @@ -0,0 +1,73 @@ +#include "Renderer.h" +#include + +Renderer::Renderer(SDL_Window* window) + : renderer_(nullptr) { + + if (window) { + renderer_ = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + if (!renderer_) { + std::cerr << "Renderer creation failed: " << SDL_GetError() << std::endl; + } + } +} + +Renderer::~Renderer() { + if (renderer_) { + SDL_DestroyRenderer(renderer_); + renderer_ = nullptr; + } +} + +Renderer::Renderer(Renderer&& other) noexcept + : renderer_(other.renderer_) { + other.renderer_ = nullptr; +} + +Renderer& Renderer::operator=(Renderer&& other) noexcept { + if (this != &other) { + if (renderer_) { + SDL_DestroyRenderer(renderer_); + } + + renderer_ = other.renderer_; + other.renderer_ = nullptr; + } + return *this; +} + +void Renderer::clear() { + if (renderer_) { + SDL_RenderClear(renderer_); + } +} + +void Renderer::setDrawColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a) { + if (renderer_) { + SDL_SetRenderDrawColor(renderer_, r, g, b, a); + } +} + +void Renderer::present() { + if (renderer_) { + SDL_RenderPresent(renderer_); + } +} + +void Renderer::drawLine(int x1, int y1, int x2, int y2) { + if (renderer_) { + SDL_RenderDrawLine(renderer_, x1, y1, x2, y2); + } +} + +void Renderer::drawRect(const SDL_Rect& rect) { + if (renderer_) { + SDL_RenderDrawRect(renderer_, &rect); + } +} + +void Renderer::fillRect(const SDL_Rect& rect) { + if (renderer_) { + SDL_RenderFillRect(renderer_, &rect); + } +} diff --git a/src/renderer/Renderer.h b/src/renderer/Renderer.h new file mode 100644 index 0000000..acf4490 --- /dev/null +++ b/src/renderer/Renderer.h @@ -0,0 +1,35 @@ +#pragma once + +#include "../core/Common.h" +#include + +class Renderer { +public: + explicit Renderer(SDL_Window* window); + ~Renderer(); + + // Disable copying + Renderer(const Renderer&) = delete; + Renderer& operator=(const Renderer&) = delete; + + // Allow moving + Renderer(Renderer&& other) noexcept; + Renderer& operator=(Renderer&& other) noexcept; + + // Getters + SDL_Renderer* getHandle() const { return renderer_; } + bool isValid() const { return renderer_ != nullptr; } + + // Rendering operations + void clear(); + void setDrawColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a = 255); + void present(); + + // Drawing primitives + void drawLine(int x1, int y1, int x2, int y2); + void drawRect(const SDL_Rect& rect); + void fillRect(const SDL_Rect& rect); + +private: + SDL_Renderer* renderer_; +}; diff --git a/src/utils/Utils.h b/src/utils/Utils.h new file mode 100644 index 0000000..8f4fa59 --- /dev/null +++ b/src/utils/Utils.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +namespace Utils { + // Logging utilities + inline void log(const std::string& message) { + std::cout << "[INFO] " << message << std::endl; + } + + inline void logError(const std::string& message) { + std::cerr << "[ERROR] " << message << std::endl; + } + + inline void logWarning(const std::string& message) { + std::cout << "[WARNING] " << message << std::endl; + } + + // String utilities + inline std::string formatString(const std::string& format, ...) { + // Simple string formatting - can be expanded later + return format; + } + + // Math utilities + template + inline T clamp(T value, T min, T max) { + if (value < min) return min; + if (value > max) return max; + return value; + } + + template + inline T lerp(T a, T b, float t) { + return a + (b - a) * t; + } +} diff --git a/src/window/Window.cpp b/src/window/Window.cpp new file mode 100644 index 0000000..23126e1 --- /dev/null +++ b/src/window/Window.cpp @@ -0,0 +1,70 @@ +#include "Window.h" +#include + +Window::Window(const std::string& title, int width, int height) + : window_(nullptr), width_(width), height_(height), title_(title) { + + window_ = SDL_CreateWindow( + title.c_str(), + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + width, + height, + SDL_WINDOW_SHOWN + ); + + if (!window_) { + std::cerr << "Window creation failed: " << SDL_GetError() << std::endl; + } +} + +Window::~Window() { + if (window_) { + SDL_DestroyWindow(window_); + window_ = nullptr; + } +} + +Window::Window(Window&& other) noexcept + : window_(other.window_), width_(other.width_), height_(other.height_), title_(std::move(other.title_)) { + other.window_ = nullptr; + other.width_ = 0; + other.height_ = 0; +} + +Window& Window::operator=(Window&& other) noexcept { + if (this != &other) { + if (window_) { + SDL_DestroyWindow(window_); + } + + window_ = other.window_; + width_ = other.width_; + height_ = other.height_; + title_ = std::move(other.title_); + + other.window_ = nullptr; + other.width_ = 0; + other.height_ = 0; + } + return *this; +} + +void Window::show() { + if (window_) { + SDL_ShowWindow(window_); + } +} + +void Window::hide() { + if (window_) { + SDL_HideWindow(window_); + } +} + +void Window::setTitle(const std::string& title) { + title_ = title; + if (window_) { + SDL_SetWindowTitle(window_, title.c_str()); + } +} diff --git a/src/window/Window.h b/src/window/Window.h new file mode 100644 index 0000000..fbefa97 --- /dev/null +++ b/src/window/Window.h @@ -0,0 +1,35 @@ +#pragma once + +#include "../core/Common.h" +#include + +class Window { +public: + Window(const std::string& title, int width, int height); + ~Window(); + + // Disable copying + Window(const Window&) = delete; + Window& operator=(const Window&) = delete; + + // Allow moving + Window(Window&& other) noexcept; + Window& operator=(Window&& other) noexcept; + + // Getters + SDL_Window* getHandle() const { return window_; } + int getWidth() const { return width_; } + int getHeight() const { return height_; } + bool isValid() const { return window_ != nullptr; } + + // Window operations + void show(); + void hide(); + void setTitle(const std::string& title); + +private: + SDL_Window* window_; + int width_; + int height_; + std::string title_; +};