combine with desktop project
Signed-off-by: Peter Siegmund <developer@mars3142.org>
This commit is contained in:
23
src/Common.cpp
Normal file
23
src/Common.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "Common.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
auto createWindow(const char *title, const int width, const int height) -> Window * {
|
||||
|
||||
constexpr uint32_t window_flag = SDL_WINDOW_HIDDEN;
|
||||
const auto window = SDL_CreateWindow(title, width, height, window_flag);
|
||||
if (window == nullptr) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create window", SDL_GetError(), nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const SDL_PropertiesID props = SDL_CreateProperties();
|
||||
SDL_SetPointerProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, window);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB_LINEAR);
|
||||
if (const auto renderer = SDL_CreateRendererWithProperties(props); renderer == nullptr) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create renderer", SDL_GetError(), nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new Window(window);
|
||||
}
|
42
src/ResourceManager.cpp
Normal file
42
src/ResourceManager.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "ResourceManager.h"
|
||||
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <SDL3_image/SDL_image.h>
|
||||
|
||||
ResourceManager& ResourceManager::getInstance() {
|
||||
static ResourceManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
ResourceManager::ResourceManager() {
|
||||
SDL_Log("ResourceManager instance created.");
|
||||
}
|
||||
|
||||
ResourceManager::~ResourceManager() {
|
||||
for(const auto& pair : m_textures) {
|
||||
if(pair.second != nullptr) {
|
||||
SDL_DestroyTexture(pair.second);
|
||||
}
|
||||
}
|
||||
m_textures.clear();
|
||||
}
|
||||
|
||||
SDL_Texture* ResourceManager::get_texture(SDL_Renderer* renderer, const std::string& path) {
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
if(const auto search = m_textures.find(path); search != m_textures.end()) {
|
||||
return search->second;
|
||||
}
|
||||
|
||||
SDL_Texture* texture = IMG_LoadTexture(renderer, path.c_str());
|
||||
|
||||
if(!texture) {
|
||||
std::cerr << "ResourceManager Fehler: Konnte Textur nicht laden '" << path << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
m_textures[path] = texture;
|
||||
return texture;
|
||||
}
|
||||
|
81
src/debug/DebugOverlay.cpp
Normal file
81
src/debug/DebugOverlay.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "debug/DebugOverlay.h"
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_sdl3.h"
|
||||
#include <imgui_impl_sdlrenderer3.h>
|
||||
|
||||
#include "Common.h"
|
||||
#include "ui/Matrix.h"
|
||||
#include "Version.h"
|
||||
|
||||
namespace DebugOverlay {
|
||||
void init(const AppContext *context) {
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO &io{ImGui::GetIO()};
|
||||
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||
|
||||
ImGui::StyleColorsDark();
|
||||
|
||||
ImGui_ImplSDL3_InitForSDLRenderer(context->window(), context->renderer());
|
||||
ImGui_ImplSDLRenderer3_Init(context->renderer());
|
||||
}
|
||||
|
||||
void update(AppContext *context, const SDL_Event *event) {
|
||||
ImGui_ImplSDL3_ProcessEvent(event);
|
||||
|
||||
if (show_led_matrix) {
|
||||
if (context->matrix_window() == nullptr) {
|
||||
const auto win = createWindow("LED Matrix", 32 * 50, 8 * 50);
|
||||
SDL_SetWindowFocusable(win->window(), false);
|
||||
SDL_SetRenderVSync(win->renderer(), SDL_RENDERER_VSYNC_ADAPTIVE);
|
||||
SDL_SetWindowPosition(win->window(), 0, 0);
|
||||
SDL_ShowWindow(win->window());
|
||||
|
||||
context->setMatrix(new Matrix(win));
|
||||
}
|
||||
} else {
|
||||
if (context->matrix_window() != nullptr) {
|
||||
SDL_DestroyWindow(context->matrix_window());
|
||||
|
||||
context->setMatrix(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void render(const AppContext *context) {
|
||||
ImGui_ImplSDLRenderer3_NewFrame();
|
||||
ImGui_ImplSDL3_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
if (show_debug_window && ImGui::BeginMainMenuBar()) {
|
||||
if (ImGui::BeginMenu("Config")) {
|
||||
ImGui::Checkbox("Show LED Matrix", &show_led_matrix);
|
||||
ImGui::Checkbox("Show Unhandled Events", &show_unhandled_events);
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("Help")) {
|
||||
ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate);
|
||||
ImGui::SeparatorText("App Info");
|
||||
ImGui::Text("Project: %s", MY_PROJECT);
|
||||
ImGui::Text("Version: %s", MY_VERSION);
|
||||
ImGui::Text("ImGui Version: %s", ImGui::GetVersion());
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
|
||||
// Rendering
|
||||
ImGui::Render();
|
||||
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), context->renderer());
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
ImGui_ImplSDLRenderer3_Shutdown();
|
||||
ImGui_ImplSDL3_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
}
|
||||
}
|
46
src/hal/u8x8_hal_sdl.cpp
Normal file
46
src/hal/u8x8_hal_sdl.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "hal/u8g2_hal_sdl.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <cstdint>
|
||||
|
||||
uint8_t u8x8_byte_sdl_hw_spi(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {
|
||||
switch(msg) {
|
||||
case U8X8_MSG_BYTE_SEND:
|
||||
case U8X8_MSG_BYTE_INIT:
|
||||
case U8X8_MSG_BYTE_SET_DC:
|
||||
case U8X8_MSG_BYTE_START_TRANSFER:
|
||||
case U8X8_MSG_BYTE_END_TRANSFER:
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t u8x8_gpio_and_delay_sdl(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {
|
||||
switch(msg) {
|
||||
case U8X8_MSG_DELAY_MILLI:
|
||||
SDL_Delay(arg_int);
|
||||
break;
|
||||
|
||||
case U8X8_MSG_DELAY_10MICRO:
|
||||
SDL_Delay((arg_int + 99) / 100);
|
||||
break;
|
||||
|
||||
case U8X8_MSG_DELAY_100NANO:
|
||||
case U8X8_MSG_DELAY_NANO:
|
||||
SDL_Delay(1);
|
||||
break;
|
||||
|
||||
case U8X8_MSG_GPIO_AND_DELAY_INIT:
|
||||
case U8X8_MSG_GPIO_RESET:
|
||||
case U8X8_MSG_GPIO_CS:
|
||||
case U8X8_MSG_GPIO_DC:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
154
src/main.cc
Normal file
154
src/main.cc
Normal file
@@ -0,0 +1,154 @@
|
||||
#include <imgui_impl_sdlrenderer3.h>
|
||||
#include <vector>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_main.h>
|
||||
|
||||
#include "Common.h"
|
||||
#include "debug/DebugOverlay.h"
|
||||
#include "hal/u8g2_hal_sdl.h"
|
||||
#include "model/AppContext.h"
|
||||
#include "ui/Device.h"
|
||||
#include "ui/UIWidget.h"
|
||||
|
||||
#include "u8g2.h"
|
||||
|
||||
#include "PushButton.h"
|
||||
|
||||
#include "ui/widgets/Button.h"
|
||||
#include "ui/widgets/D_Pad.h"
|
||||
|
||||
constexpr unsigned int WINDOW_WIDTH =
|
||||
(U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR + 3 * U8G2_SCREEN_PADDING + 2 * BUTTON_WIDTH + DPAD_WIDTH + 2 * U8G2_SCREEN_PADDING);
|
||||
constexpr unsigned int WINDOW_HEIGHT = (U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR + 50);
|
||||
|
||||
std::shared_ptr<Device> device;
|
||||
std::vector<std::shared_ptr<UIWidget>> widgets;
|
||||
|
||||
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
|
||||
if(SDL_Init(SDL_INIT_VIDEO) == false) {
|
||||
SDL_ShowSimpleMessageBox(
|
||||
SDL_MESSAGEBOX_ERROR, "Couldn't initialize SDL!", SDL_GetError(), nullptr);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
if(TTF_Init() == false) {
|
||||
SDL_ShowSimpleMessageBox(
|
||||
SDL_MESSAGEBOX_ERROR, "Couldn't initialize TTF", SDL_GetError(), nullptr);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
const auto win = createWindow("System Control (Simulator)", WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
if(!win) {
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
SDL_SetRenderVSync(win->renderer(), SDL_RENDERER_VSYNC_ADAPTIVE);
|
||||
SDL_SetWindowPosition(win->window(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
||||
SDL_ShowWindow(win->window());
|
||||
|
||||
const auto context = new AppContext(win);
|
||||
*appstate = context;
|
||||
|
||||
device = std::make_shared<Device>(context);
|
||||
widgets.push_back(device);
|
||||
|
||||
DebugOverlay::init(context);
|
||||
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
|
||||
const auto context = static_cast<AppContext*>(appstate);
|
||||
DebugOverlay::update(context, event);
|
||||
|
||||
switch(event->type) {
|
||||
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
||||
/// multi window
|
||||
if(SDL_GetWindowID(context->window()) == event->window.windowID) {
|
||||
return SDL_APP_SUCCESS;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_EVENT_QUIT:
|
||||
/// single window application
|
||||
return SDL_APP_SUCCESS;
|
||||
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
switch(event->key.key) {
|
||||
case SDLK_ESCAPE:
|
||||
return SDL_APP_SUCCESS;
|
||||
|
||||
case SDLK_UP:
|
||||
device->onButtonClicked(BUTTON_UP);
|
||||
break;
|
||||
|
||||
case SDLK_DOWN:
|
||||
device->onButtonClicked(BUTTON_DOWN);
|
||||
break;
|
||||
|
||||
case SDLK_LEFT:
|
||||
device->onButtonClicked(BUTTON_LEFT);
|
||||
break;
|
||||
|
||||
case SDLK_RIGHT:
|
||||
device->onButtonClicked(BUTTON_RIGHT);
|
||||
break;
|
||||
|
||||
case SDLK_RETURN:
|
||||
device->onButtonClicked(BUTTON_SELECT);
|
||||
break;
|
||||
|
||||
case SDLK_BACKSPACE:
|
||||
device->onButtonClicked(BUTTON_BACK);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_EVENT_KEY_UP:
|
||||
if(event->key.key == SDLK_LSHIFT) {
|
||||
DebugOverlay::show_debug_window = !DebugOverlay::show_debug_window;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
//device->hitTest(&event->motion);
|
||||
break;
|
||||
|
||||
default: {
|
||||
if(DebugOverlay::show_unhandled_events) {
|
||||
SDL_Log("Unused event: %d", event->type);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
// return continue to continue
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
SDL_AppResult SDL_AppIterate(void* appstate) {
|
||||
const auto context = static_cast<AppContext*>(appstate);
|
||||
|
||||
/// render main window
|
||||
SDL_SetRenderDrawColor(context->renderer(), 0, 0, 0, 255);
|
||||
SDL_RenderClear(context->renderer());
|
||||
|
||||
for(const auto& widget : widgets) {
|
||||
widget->render();
|
||||
}
|
||||
DebugOverlay::render(context);
|
||||
|
||||
SDL_RenderPresent(context->renderer());
|
||||
|
||||
/// render led matrix
|
||||
context->matrix_render();
|
||||
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
void SDL_AppQuit(void* appstate, SDL_AppResult result) {
|
||||
DebugOverlay::cleanup();
|
||||
|
||||
free(appstate);
|
||||
|
||||
// SDL will clean up the window/renderer for us.
|
||||
TTF_Quit();
|
||||
}
|
43
src/model/AppContext.cpp
Normal file
43
src/model/AppContext.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "model/AppContext.h"
|
||||
|
||||
#include "ui/Matrix.h"
|
||||
|
||||
auto AppContext::window() const -> SDL_Window * {
|
||||
return m_window->window();
|
||||
}
|
||||
|
||||
auto AppContext::renderer() const -> SDL_Renderer * {
|
||||
return m_window->renderer();
|
||||
}
|
||||
|
||||
auto AppContext::surface() const -> SDL_Surface * {
|
||||
return SDL_GetWindowSurface(m_window->window());
|
||||
}
|
||||
|
||||
void AppContext::setMatrix(Matrix *matrix) {
|
||||
m_matrix = matrix;
|
||||
}
|
||||
|
||||
auto AppContext::matrix() const -> Matrix * {
|
||||
return m_matrix;
|
||||
}
|
||||
|
||||
auto AppContext::matrix_window() const -> SDL_Window * {
|
||||
if (m_matrix && m_matrix->window()) {
|
||||
return m_matrix->window()->window();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto AppContext::matrix_renderer() const -> SDL_Renderer * {
|
||||
if (m_matrix && m_matrix->window()) {
|
||||
return m_matrix->window()->renderer();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AppContext::matrix_render() const {
|
||||
if (m_matrix && m_matrix->window()) {
|
||||
m_matrix->render();
|
||||
}
|
||||
}
|
9
src/model/Window.cpp
Normal file
9
src/model/Window.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "model/Window.h"
|
||||
|
||||
auto Window::window() const -> SDL_Window * {
|
||||
return m_window;
|
||||
}
|
||||
|
||||
auto Window::renderer() const -> SDL_Renderer * {
|
||||
return SDL_GetRenderer(m_window);
|
||||
}
|
200
src/ui/Device.cpp
Normal file
200
src/ui/Device.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
#include "ui/Device.h"
|
||||
|
||||
#include "SDL3_ttf/SDL_ttf.h"
|
||||
#include "ui/widgets/Button.h"
|
||||
#include "ui/widgets/D_Pad.h"
|
||||
|
||||
#include <u8g2.h>
|
||||
#include <hal/u8g2_hal_sdl.h>
|
||||
|
||||
#include "ui/SplashScreen.h"
|
||||
#include "MenuOptions.h"
|
||||
|
||||
u8g2_t u8g2;
|
||||
menu_options_t options;
|
||||
|
||||
static void set_pixel_rgba(
|
||||
const SDL_Surface* surface,
|
||||
const int x,
|
||||
const int y,
|
||||
const uint32_t pixel_color) {
|
||||
if(!surface || x < 0 || x >= surface->w || y < 0 || y >= surface->h) {
|
||||
return;
|
||||
}
|
||||
const auto p =
|
||||
static_cast<uint8_t*>(surface->pixels) + y * surface->pitch + x * sizeof(uint32_t);
|
||||
*reinterpret_cast<uint32_t*>(p) = pixel_color;
|
||||
}
|
||||
|
||||
Device::Device(void* appstate)
|
||||
: UIWidget(appstate) {
|
||||
m_children.push_back(
|
||||
std::make_shared<Button>(
|
||||
get_context(),
|
||||
U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR + 3 * U8G2_SCREEN_PADDING + DPAD_WIDTH,
|
||||
U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR + U8G2_SCREEN_PADDING - BUTTON_WIDTH,
|
||||
BUTTON_WIDTH,
|
||||
[]() { pushKey(SDLK_RETURN); }));
|
||||
m_children.push_back(
|
||||
std::make_shared<Button>(
|
||||
get_context(),
|
||||
U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR + 4 * U8G2_SCREEN_PADDING + DPAD_WIDTH +
|
||||
BUTTON_WIDTH,
|
||||
U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR + U8G2_SCREEN_PADDING - 2 * BUTTON_WIDTH,
|
||||
BUTTON_WIDTH,
|
||||
[]() { pushKey(SDLK_BACKSPACE); }));
|
||||
m_children.push_back(
|
||||
std::make_shared<D_Pad>(
|
||||
get_context(),
|
||||
U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR + 2 * U8G2_SCREEN_PADDING,
|
||||
U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR + U8G2_SCREEN_PADDING - DPAD_WIDTH,
|
||||
DPAD_WIDTH,
|
||||
[](int d) {}));
|
||||
|
||||
u8g2_Setup_sh1106_128x64_noname_f(
|
||||
&u8g2, U8G2_R0, u8x8_byte_sdl_hw_spi, u8x8_gpio_and_delay_sdl);
|
||||
u8x8_InitDisplay(u8g2_GetU8x8(&u8g2));
|
||||
|
||||
options = {
|
||||
.u8g2 = &u8g2,
|
||||
.setScreen = [this](const std::shared_ptr<Widget>& screen) { this->setScreen(screen); },
|
||||
.pushScreen = [this](const std::shared_ptr<Widget>& screen) { this->pushScreen(screen); },
|
||||
.popScreen = [this]() { this->popScreen(); },
|
||||
};
|
||||
widget = std::make_shared<SplashScreen>(&options);
|
||||
}
|
||||
|
||||
void Device::setScreen(const std::shared_ptr<Widget>& screen) {
|
||||
if(screen != nullptr) {
|
||||
widget = screen;
|
||||
history.clear();
|
||||
history.emplace_back(widget);
|
||||
}
|
||||
}
|
||||
|
||||
void Device::pushScreen(const std::shared_ptr<Widget>& screen) {
|
||||
if(screen != nullptr) {
|
||||
widget = screen;
|
||||
history.emplace_back(widget);
|
||||
}
|
||||
}
|
||||
|
||||
void Device::popScreen() {
|
||||
if(history.size() >= 2) {
|
||||
history.pop_back();
|
||||
widget = history.back();
|
||||
}
|
||||
}
|
||||
|
||||
void Device::pushKey(const SDL_Keycode key) {
|
||||
SDL_Event ev;
|
||||
ev.type = SDL_EVENT_KEY_DOWN;
|
||||
ev.key.key = key;
|
||||
SDL_PushEvent(&ev);
|
||||
}
|
||||
|
||||
void Device::render_u8g2() const {
|
||||
SDL_Surface* u8g2_surface = nullptr;
|
||||
SDL_Texture* u8g2_texture = nullptr;
|
||||
|
||||
u8g2_surface =
|
||||
SDL_CreateSurface(U8G2_SCREEN_WIDTH, U8G2_SCREEN_HEIGHT, SDL_PIXELFORMAT_RGBA8888);
|
||||
|
||||
if(!u8g2_surface) {
|
||||
SDL_Log("SDL_CreateSurfaceFrom Error: %s\n", SDL_GetError());
|
||||
} else {
|
||||
const auto color_black = SDL_MapSurfaceRGBA(u8g2_surface, 0, 0, 0, 255);
|
||||
const auto color_white = SDL_MapSurfaceRGBA(u8g2_surface, 255, 255, 255, 255);
|
||||
|
||||
if(!SDL_LockSurface(u8g2_surface)) {
|
||||
SDL_Log("SDL_LockSurface Error: %s\n", SDL_GetError());
|
||||
} else {
|
||||
const auto u8g2_buf = u8g2_GetBufferPtr(&u8g2);
|
||||
for(auto y = 0; y < U8G2_SCREEN_HEIGHT; ++y) {
|
||||
for(auto x = 0; x < U8G2_SCREEN_WIDTH; ++x) {
|
||||
const auto page = y / 8;
|
||||
const auto bit_index = y % 8;
|
||||
const auto byte_ptr = u8g2_buf + page * U8G2_SCREEN_WIDTH + x;
|
||||
const auto pixel_is_set = (*byte_ptr >> bit_index) & 0x01;
|
||||
set_pixel_rgba(u8g2_surface, x, y, pixel_is_set ? color_white : color_black);
|
||||
}
|
||||
}
|
||||
SDL_UnlockSurface(u8g2_surface);
|
||||
}
|
||||
|
||||
u8g2_texture = SDL_CreateTextureFromSurface(get_context()->renderer(), u8g2_surface);
|
||||
if(!u8g2_texture) {
|
||||
SDL_Log("SDL_CreateTextureFromSurface Error: %s\n", SDL_GetError());
|
||||
}
|
||||
if(!SDL_SetTextureScaleMode(u8g2_texture, SDL_SCALEMODE_NEAREST)) {
|
||||
SDL_Log("SDL_SetTextureScaleMode Error: %s\n", SDL_GetError());
|
||||
}
|
||||
SDL_DestroySurface(u8g2_surface);
|
||||
}
|
||||
|
||||
if(u8g2_texture) {
|
||||
SDL_FRect destRect;
|
||||
destRect.x = U8G2_SCREEN_PADDING;
|
||||
destRect.y = U8G2_SCREEN_PADDING;
|
||||
destRect.w = U8G2_SCREEN_WIDTH * U8G2_SCREEN_FACTOR;
|
||||
destRect.h = U8G2_SCREEN_HEIGHT * U8G2_SCREEN_FACTOR;
|
||||
|
||||
SDL_RenderTexture(get_context()->renderer(), u8g2_texture, nullptr, &destRect);
|
||||
}
|
||||
}
|
||||
|
||||
void Device::draw_background() const {
|
||||
int windowWidth = 0;
|
||||
int windowHeight = 0;
|
||||
|
||||
if(!SDL_GetWindowSize(get_context()->window(), &windowWidth, &windowHeight)) {
|
||||
SDL_Log("SDL_GetWindowSize Error: %s\n", SDL_GetError());
|
||||
}
|
||||
const auto rect =
|
||||
SDL_FRect{0.0f, 0.0f, static_cast<float>(windowWidth), static_cast<float>(windowHeight)};
|
||||
SDL_SetRenderDrawColor(get_context()->renderer(), 193, 46, 31, 255);
|
||||
SDL_RenderFillRect(get_context()->renderer(), &rect);
|
||||
}
|
||||
|
||||
void Device::draw_screen() const {
|
||||
u8g2_ClearBuffer(&u8g2);
|
||||
|
||||
if(widget != nullptr) {
|
||||
widget->update(SDL_GetTicks());
|
||||
widget->render();
|
||||
}
|
||||
|
||||
render_u8g2();
|
||||
}
|
||||
|
||||
void Device::draw_text() const {
|
||||
constexpr auto color = SDL_Color({0, 255, 200});
|
||||
const auto surface =
|
||||
TTF_RenderText_Blended(get_context()->m_font_default, "HelloWorld SDL3 TTF", 0, color);
|
||||
const auto texture = SDL_CreateTextureFromSurface(get_context()->renderer(), surface);
|
||||
SDL_DestroySurface(surface);
|
||||
const SDL_FRect dstRect{
|
||||
500, 100, static_cast<float>(texture->w), static_cast<float>(texture->h)};
|
||||
SDL_RenderTexture(get_context()->renderer(), texture, nullptr, &dstRect);
|
||||
SDL_DestroyTexture(texture); // ? Is this safe to do here ?
|
||||
}
|
||||
|
||||
void Device::render() const {
|
||||
draw_background();
|
||||
draw_screen();
|
||||
// draw_text();
|
||||
|
||||
for(const auto& child : m_children) {
|
||||
child->render();
|
||||
}
|
||||
}
|
||||
|
||||
void Device::hit_test(SDL_MouseMotionEvent* event) const {
|
||||
SDL_Log("x: %f", event->x);
|
||||
}
|
||||
|
||||
void Device::onButtonClicked(const uint8_t button) const {
|
||||
if(widget != nullptr) {
|
||||
widget->onButtonClicked(button);
|
||||
}
|
||||
}
|
47
src/ui/Matrix.cpp
Normal file
47
src/ui/Matrix.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "ui/Matrix.h"
|
||||
|
||||
Matrix::Matrix(Window *window): m_window(window) {
|
||||
}
|
||||
|
||||
Window *Matrix::window() const {
|
||||
return m_window;
|
||||
}
|
||||
|
||||
void Matrix::draw_colored_grid(const int rows, const int cols, const float cellSize, const float spacing) const {
|
||||
int i = 0;
|
||||
for (int w = 0; w < cols; w++) {
|
||||
const auto phase = w % (2 * rows);
|
||||
|
||||
for (int h_raw = 0; h_raw < rows; h_raw++) {
|
||||
int h;
|
||||
if (phase < rows) {
|
||||
h = h_raw;
|
||||
} else {
|
||||
h = rows - 1 - h_raw;
|
||||
}
|
||||
|
||||
const auto rectSize = cellSize - 2.0f * spacing;
|
||||
|
||||
const auto x = static_cast<float>(w) * cellSize + spacing;
|
||||
const auto y = static_cast<float>(h) * cellSize + spacing;
|
||||
|
||||
auto rect = SDL_FRect{x, y, rectSize, rectSize};
|
||||
|
||||
i++;
|
||||
const auto red = static_cast<Uint8>(static_cast<float>(i) * 255.0f);
|
||||
const auto green = static_cast<Uint8>(static_cast<float>(i) * 255.0f);
|
||||
const auto blue = static_cast<Uint8>(static_cast<float>(i) * 255.0f);
|
||||
SDL_SetRenderDrawColor(m_window->renderer(), red, green, blue, 255);
|
||||
SDL_RenderFillRect(m_window->renderer(), &rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Matrix::render() const {
|
||||
SDL_SetRenderDrawColor(m_window->renderer(), 0, 0, 0, 255);
|
||||
SDL_RenderClear(m_window->renderer());
|
||||
|
||||
draw_colored_grid(8, 32, 50.0f, 1.0f);
|
||||
|
||||
SDL_RenderPresent(m_window->renderer());
|
||||
}
|
10
src/ui/UIWidget.cpp
Normal file
10
src/ui/UIWidget.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include "ui/UIWidget.h"
|
||||
|
||||
UIWidget::UIWidget(void *appstate): m_context(static_cast<AppContext *>(appstate)) {
|
||||
}
|
||||
|
||||
UIWidget::~UIWidget() = default;
|
||||
|
||||
auto UIWidget::get_context() const -> AppContext* {
|
||||
return m_context;
|
||||
}
|
32
src/ui/widgets/Button.cpp
Normal file
32
src/ui/widgets/Button.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "ui/widgets/Button.h"
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <SDL3_image/SDL_image.h>
|
||||
#include "ResourceManager.h"
|
||||
|
||||
auto pressed = false;
|
||||
|
||||
Button::Button(
|
||||
void* appState,
|
||||
const float x,
|
||||
const float y,
|
||||
const float width,
|
||||
std::function<void()> callback)
|
||||
: UIWidget(appState)
|
||||
, m_x(x)
|
||||
, m_y(y)
|
||||
, m_width(width)
|
||||
, m_callback(std::move(callback)) {
|
||||
}
|
||||
|
||||
void Button::render() const {
|
||||
const auto button = ResourceManager::getInstance().get_texture(get_context()->renderer(), "assets/button_normal.png");
|
||||
const auto overlay =ResourceManager::getInstance().get_texture(get_context()->renderer(), "assets/button_pressed_overlay.png");
|
||||
|
||||
const auto dst = SDL_FRect(m_x, m_y, m_width, m_width);
|
||||
SDL_RenderTexture(get_context()->renderer(), button, nullptr, &dst);
|
||||
if(pressed) {
|
||||
SDL_RenderTexture(get_context()->renderer(), overlay, nullptr, &dst);
|
||||
}
|
||||
}
|
24
src/ui/widgets/D_Pad.cpp
Normal file
24
src/ui/widgets/D_Pad.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "ui/widgets/D_Pad.h"
|
||||
|
||||
#include <ResourceManager.h>
|
||||
|
||||
D_Pad::D_Pad(
|
||||
void* appState,
|
||||
const float x,
|
||||
const float y,
|
||||
const float width,
|
||||
std::function<void(int)> callback)
|
||||
: UIWidget(appState)
|
||||
, m_x(x)
|
||||
, m_y(y)
|
||||
, m_width(width)
|
||||
, m_callback(std::move(callback)) {
|
||||
}
|
||||
|
||||
void D_Pad::render() const {
|
||||
const auto dPad = ResourceManager::getInstance().get_texture(
|
||||
get_context()->renderer(), "assets/d-pad_normal.png");
|
||||
|
||||
const auto dst = SDL_FRect(m_x, m_y, m_width, m_width);
|
||||
SDL_RenderTexture(get_context()->renderer(), dPad, nullptr, &dst);
|
||||
}
|
Reference in New Issue
Block a user