summaryrefslogtreecommitdiff
path: root/app/sdl/impl
diff options
context:
space:
mode:
authorHannes Schulze <haschu0103@outlook.de>2025-02-07 22:15:57 +0100
committerHannes Schulze <haschu0103@outlook.de>2025-02-07 22:15:57 +0100
commit0de4957a22dd408793d0526351bcb7d61786fe86 (patch)
tree3160c72a45022b1e0b7e5ab928c690e85e20cded /app/sdl/impl
parenta6bfb14562982c8c0139566e5a9b3051543791c4 (diff)
add sdl implementation
Diffstat (limited to 'app/sdl/impl')
-rw-r--r--app/sdl/impl/CMakeLists.txt9
-rw-r--r--app/sdl/impl/display-sdl.cc16
-rw-r--r--app/sdl/impl/display-sdl.hh24
-rw-r--r--app/sdl/impl/file-browser-posix.cc139
-rw-r--r--app/sdl/impl/file-browser-posix.hh30
-rw-r--r--app/sdl/impl/input-device-keyboard-sdl.cc97
-rw-r--r--app/sdl/impl/input-device-keyboard-sdl.hh28
7 files changed, 343 insertions, 0 deletions
diff --git a/app/sdl/impl/CMakeLists.txt b/app/sdl/impl/CMakeLists.txt
new file mode 100644
index 0000000..6ba1824
--- /dev/null
+++ b/app/sdl/impl/CMakeLists.txt
@@ -0,0 +1,9 @@
+target_sources(
+ nes_app_sdl
+ PRIVATE
+ display-sdl.hh
+ display-sdl.cc
+ input-device-keyboard-sdl.hh
+ input-device-keyboard-sdl.cc
+ file-browser-posix.hh
+ file-browser-posix.cc)
diff --git a/app/sdl/impl/display-sdl.cc b/app/sdl/impl/display-sdl.cc
new file mode 100644
index 0000000..d8e22d1
--- /dev/null
+++ b/app/sdl/impl/display-sdl.cc
@@ -0,0 +1,16 @@
+#include "impl/display-sdl.hh"
+
+namespace nes::app::sdl
+{
+ auto display_sdl::set(u32 const x, u32 const y, rgb const color) -> void
+ {
+ buffer_back_[(y * width) + x] = (color.r << 24) | (color.g << 16) | (color.b << 8);
+ }
+
+ auto display_sdl::switch_buffers() -> void
+ {
+ if (!texture_) { return; }
+
+ SDL_UpdateTexture(texture_, nullptr, buffer_back_, width * 4);
+ }
+} // namespace nes::app::sdl
diff --git a/app/sdl/impl/display-sdl.hh b/app/sdl/impl/display-sdl.hh
new file mode 100644
index 0000000..9310eca
--- /dev/null
+++ b/app/sdl/impl/display-sdl.hh
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "nes/common/display.hh"
+
+#include <SDL3/SDL.h>
+
+namespace nes::app::sdl
+{
+ /// Display implementation rendering contents into an SDL texture.
+ class display_sdl final : public display
+ {
+ SDL_Texture* texture_{ nullptr };
+ u32 buffer_back_[width * height]{};
+
+ public:
+ explicit display_sdl() = default;
+
+ auto get_texture() const -> SDL_Texture* { return texture_; }
+ auto set_texture(SDL_Texture* v) -> void { texture_ = v; }
+
+ auto switch_buffers() -> void override;
+ auto set(u32 x, u32 y, rgb value) -> void override;
+ };
+} // namespace nes::app::sdl
diff --git a/app/sdl/impl/file-browser-posix.cc b/app/sdl/impl/file-browser-posix.cc
new file mode 100644
index 0000000..50de9e1
--- /dev/null
+++ b/app/sdl/impl/file-browser-posix.cc
@@ -0,0 +1,139 @@
+#include "impl/file-browser-posix.hh"
+#include <string>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+namespace nes::app::sdl
+{
+ file_browser_posix::file_browser_posix()
+ {
+ auto const initial_path = getenv("HOME");
+ if (initial_path)
+ {
+ for (auto const component : path_view{ initial_path })
+ {
+ if (path_.push(component) != status::success) { break; }
+ }
+ }
+
+ reopen_directory(path_.get_path());
+ }
+
+ file_browser_posix::~file_browser_posix()
+ {
+ if (it_) { closedir(it_); }
+ }
+
+ auto file_browser_posix::seek(u32 const pos) -> void
+ {
+ if (it_pos_ > pos)
+ {
+ reopen_directory(path_.get_path());
+ }
+
+ while (it_pos_ < pos)
+ {
+ if (!read_next(nullptr)) { break; }
+ }
+ }
+
+ auto file_browser_posix::read_next(entry* out_entry) -> bool
+ {
+ if (!it_) { return false; }
+
+ while (true)
+ {
+ errno = 0;
+ auto const ent = readdir(it_);
+ if (!ent)
+ {
+ if (errno) { perror("file_browser_posix: readdir"); }
+ return false;
+ }
+
+ auto const name = string_view{ ent->d_name };
+ if (name.get_length() > max_name_length) { continue; }
+ if (name[0] == '.') { continue; }
+
+ auto type = entry_type{};
+ if (ent->d_type == DT_DIR) { type = entry_type::directory; }
+ else if (ent->d_type == DT_REG) { type = entry_type::file; }
+ else { continue; }
+
+ it_pos_ += 1;
+ if (out_entry) { *out_entry = entry{ type, name }; }
+ return true;
+ }
+ }
+
+ auto file_browser_posix::navigate_up() -> status
+ {
+ auto new_path = path_;
+ if (auto const s = new_path.pop(); s != status::success) { return s; }
+ if (auto const s = reopen_directory(new_path.get_path()); s != status::success) { return s; }
+ path_ = new_path;
+ return status::success;
+ }
+
+ auto file_browser_posix::navigate(string_view const item) -> status
+ {
+ auto new_path = path_;
+ if (auto const s = new_path.push(item); s != status::success) { return s; }
+ if (auto const s = reopen_directory(new_path.get_path()); s != status::success) { return s; }
+ path_ = new_path;
+ return status::success;
+ }
+
+ auto file_browser_posix::load(string_view const item, span<u8> const buffer, u32* out_length) -> status
+ {
+ auto file_path = path_;
+ if (auto const s = file_path.push(item); s != status::success) { return s; }
+
+ auto const file_path_str = std::string{ file_path.get_path().get_data(), file_path.get_path().get_length() };
+ auto const fd = open(file_path_str.c_str(), O_RDONLY);
+ if (fd == -1)
+ {
+ perror("file_browser_posix: open");
+ return status::error_system_error;
+ }
+
+ auto const res = read(fd, buffer.get_data(), buffer.get_length());
+ if (res < 0)
+ {
+ perror("file_browser_posix: read");
+ close(fd);
+ return status::error_system_error;
+ }
+ if (out_length) { *out_length = static_cast<u32>(res); }
+
+ auto remaining = char{};
+ if (read(fd, &remaining, 1) != 0)
+ {
+ // File was too long.
+ close(fd);
+ return status::error_invalid_ines_data;
+ }
+
+ close(fd);
+
+ return status::success;
+ }
+
+ auto file_browser_posix::reopen_directory(string_view const path) -> status
+ {
+ auto const path_str = std::string{ path.get_data(), path.get_length() };
+ auto const new_it = opendir(path_str.c_str());
+ if (!new_it)
+ {
+ perror("file_browser_posix: opendir");
+ return status::error_system_error;
+ }
+
+ if (it_) { closedir(it_); }
+ it_ = new_it;
+ it_pos_ = 0;
+ return status::success;
+ }
+} // namespace nes::app::mac
diff --git a/app/sdl/impl/file-browser-posix.hh b/app/sdl/impl/file-browser-posix.hh
new file mode 100644
index 0000000..8d56575
--- /dev/null
+++ b/app/sdl/impl/file-browser-posix.hh
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "nes/app/file-browser.hh"
+#include "nes/common/containers/path-builder.hh"
+#include <dirent.h>
+
+namespace nes::app::sdl
+{
+ /// File browser implementation using the POSIX directory API.
+ class file_browser_posix final : public file_browser
+ {
+ path_buffer<1024> path_;
+ DIR* it_{ nullptr };
+ u32 it_pos_{ 0 };
+
+ public:
+ explicit file_browser_posix();
+ ~file_browser_posix() override;
+
+ auto get_path() const -> path_view override { return path_.get_components(); }
+ auto seek(u32) -> void override;
+ auto read_next(entry* out_entry) -> bool override;
+ auto navigate_up() -> status override;
+ auto navigate(string_view) -> status override;
+ auto load(string_view, span<u8>, u32* out_length) -> status override;
+
+ private:
+ auto reopen_directory(string_view path) -> status;
+ };
+} // namespace nes::app::sdl
diff --git a/app/sdl/impl/input-device-keyboard-sdl.cc b/app/sdl/impl/input-device-keyboard-sdl.cc
new file mode 100644
index 0000000..43f17ac
--- /dev/null
+++ b/app/sdl/impl/input-device-keyboard-sdl.cc
@@ -0,0 +1,97 @@
+#include "impl/input-device-keyboard-sdl.hh"
+#include <SDL3/SDL_keycode.h>
+
+namespace nes::app::sdl
+{
+ auto input_device_keyboard_sdl::handle_key_down(SDL_Event* event) -> void
+ {
+ if (auto key = convert_key(event->key.scancode)) { buffer_.key_down(*key); }
+ }
+
+ auto input_device_keyboard_sdl::handle_key_up(SDL_Event* event) -> void
+ {
+ if (auto key = convert_key(event->key.scancode)) { buffer_.key_up(*key); }
+ }
+
+ auto input_device_keyboard_sdl::convert_key(SDL_Scancode code) const -> std::optional<key>
+ {
+ switch (static_cast<int>(code))
+ {
+ case SDL_SCANCODE_0: return key::digit_0;
+ case SDL_SCANCODE_1: return key::digit_1;
+ case SDL_SCANCODE_2: return key::digit_2;
+ case SDL_SCANCODE_3: return key::digit_3;
+ case SDL_SCANCODE_4: return key::digit_4;
+ case SDL_SCANCODE_5: return key::digit_5;
+ case SDL_SCANCODE_6: return key::digit_6;
+ case SDL_SCANCODE_7: return key::digit_7;
+ case SDL_SCANCODE_8: return key::digit_8;
+ case SDL_SCANCODE_9: return key::digit_9;
+ case SDL_SCANCODE_A: return key::letter_a;
+ case SDL_SCANCODE_B: return key::letter_b;
+ case SDL_SCANCODE_C: return key::letter_c;
+ case SDL_SCANCODE_D: return key::letter_d;
+ case SDL_SCANCODE_E: return key::letter_e;
+ case SDL_SCANCODE_F: return key::letter_f;
+ case SDL_SCANCODE_G: return key::letter_g;
+ case SDL_SCANCODE_H: return key::letter_h;
+ case SDL_SCANCODE_I: return key::letter_i;
+ case SDL_SCANCODE_J: return key::letter_j;
+ case SDL_SCANCODE_K: return key::letter_k;
+ case SDL_SCANCODE_L: return key::letter_l;
+ case SDL_SCANCODE_M: return key::letter_m;
+ case SDL_SCANCODE_N: return key::letter_n;
+ case SDL_SCANCODE_O: return key::letter_o;
+ case SDL_SCANCODE_P: return key::letter_p;
+ case SDL_SCANCODE_Q: return key::letter_q;
+ case SDL_SCANCODE_R: return key::letter_r;
+ case SDL_SCANCODE_S: return key::letter_s;
+ case SDL_SCANCODE_T: return key::letter_t;
+ case SDL_SCANCODE_U: return key::letter_u;
+ case SDL_SCANCODE_V: return key::letter_v;
+ case SDL_SCANCODE_W: return key::letter_w;
+ case SDL_SCANCODE_X: return key::letter_x;
+ case SDL_SCANCODE_Y: return key::letter_y;
+ case SDL_SCANCODE_Z: return key::letter_z;
+ case SDL_SCANCODE_ESCAPE: return key::escape;
+ case SDL_SCANCODE_MINUS: return key::dash;
+ case SDL_SCANCODE_EQUALS: return key::equal;
+ case SDL_SCANCODE_BACKSPACE: return key::backspace;
+ case SDL_SCANCODE_TAB: return key::tab;
+ case SDL_SCANCODE_RETURN: return key::enter;
+ case SDL_SCANCODE_SEMICOLON: return key::semicolon;
+ case SDL_SCANCODE_APOSTROPHE: return key::apostrophe;
+ case SDL_SCANCODE_BACKSLASH: return key::backslash;
+ case SDL_SCANCODE_COMMA: return key::comma;
+ case SDL_SCANCODE_PERIOD: return key::period;
+ case SDL_SCANCODE_SLASH: return key::slash;
+ case SDL_SCANCODE_SPACE: return key::space;
+ case SDL_SCANCODE_CAPSLOCK: return key::caps_lock;
+ case SDL_SCANCODE_LEFTBRACKET: return key::bracket_open;
+ case SDL_SCANCODE_RIGHTBRACKET: return key::bracket_close;
+ case SDL_SCANCODE_LCTRL: return key::control_left;
+ case SDL_SCANCODE_RCTRL: return key::control_right;
+ case SDL_SCANCODE_LSHIFT: return key::shift_left;
+ case SDL_SCANCODE_RSHIFT: return key::shift_right;
+ case SDL_SCANCODE_LALT: return key::alt_left;
+ case SDL_SCANCODE_RALT: return key::alt_right;
+ case SDL_SCANCODE_LEFT: return key::arrow_left;
+ case SDL_SCANCODE_RIGHT: return key::arrow_right;
+ case SDL_SCANCODE_UP: return key::arrow_up;
+ case SDL_SCANCODE_DOWN: return key::arrow_down;
+ case SDL_SCANCODE_F1: return key::f1;
+ case SDL_SCANCODE_F2: return key::f2;
+ case SDL_SCANCODE_F3: return key::f3;
+ case SDL_SCANCODE_F4: return key::f4;
+ case SDL_SCANCODE_F5: return key::f5;
+ case SDL_SCANCODE_F6: return key::f6;
+ case SDL_SCANCODE_F7: return key::f7;
+ case SDL_SCANCODE_F8: return key::f8;
+ case SDL_SCANCODE_F9: return key::f9;
+ case SDL_SCANCODE_F10: return key::f10;
+ case SDL_SCANCODE_F11: return key::f11;
+ case SDL_SCANCODE_F12: return key::f12;
+ default: return std::nullopt;
+ }
+ }
+} // namespace nes::app::sdl
diff --git a/app/sdl/impl/input-device-keyboard-sdl.hh b/app/sdl/impl/input-device-keyboard-sdl.hh
new file mode 100644
index 0000000..1c38065
--- /dev/null
+++ b/app/sdl/impl/input-device-keyboard-sdl.hh
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "nes/app/input/input-device-keyboard.hh"
+#include "nes/app/input/input-buffer.hh"
+#include <optional>
+
+#include <SDL3/SDL.h>
+
+namespace nes::app::sdl
+{
+ /// Keyboard implementation using SDL.
+ class input_device_keyboard_sdl final : public input_device_keyboard
+ {
+ input_buffer buffer_;
+
+ public:
+ explicit input_device_keyboard_sdl() = default;
+
+ auto handle_key_down(SDL_Event*) -> void;
+ auto handle_key_up(SDL_Event*) -> void;
+
+ auto read_key(key const key) -> bool override { return buffer_.read_key(key); }
+ auto poll_event() -> input_event override { return buffer_.poll_event(); }
+
+ private:
+ auto convert_key(SDL_Scancode) const -> std::optional<key>;
+ };
+} // namespace nes::app::sdl