diff options
Diffstat (limited to 'app/sdl/impl')
| -rw-r--r-- | app/sdl/impl/CMakeLists.txt | 9 | ||||
| -rw-r--r-- | app/sdl/impl/display-sdl.cc | 16 | ||||
| -rw-r--r-- | app/sdl/impl/display-sdl.hh | 24 | ||||
| -rw-r--r-- | app/sdl/impl/file-browser-posix.cc | 139 | ||||
| -rw-r--r-- | app/sdl/impl/file-browser-posix.hh | 30 | ||||
| -rw-r--r-- | app/sdl/impl/input-device-keyboard-sdl.cc | 97 | ||||
| -rw-r--r-- | app/sdl/impl/input-device-keyboard-sdl.hh | 28 |
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 |
