summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/mac/impl/display-spritekit.hh1
-rw-r--r--app/mac/impl/display-spritekit.mm9
-rw-r--r--lib/nes/app/action.hh4
-rw-r--r--lib/nes/app/application.cc59
-rw-r--r--lib/nes/app/application.hh8
-rw-r--r--lib/nes/app/graphics/renderer.cc54
-rw-r--r--lib/nes/app/graphics/renderer.hh4
-rw-r--r--lib/nes/app/graphics/tiles/CMakeLists.txt14
-rw-r--r--lib/nes/app/graphics/tiles/border_bottom.pngbin0 -> 139 bytes
-rw-r--r--lib/nes/app/graphics/tiles/border_bottom_left.pngbin0 -> 151 bytes
-rw-r--r--lib/nes/app/graphics/tiles/border_bottom_right.pngbin0 -> 148 bytes
-rw-r--r--lib/nes/app/graphics/tiles/border_left.pngbin0 -> 140 bytes
-rw-r--r--lib/nes/app/graphics/tiles/border_right.pngbin0 -> 139 bytes
-rw-r--r--lib/nes/app/graphics/tiles/border_top.pngbin0 -> 136 bytes
-rw-r--r--lib/nes/app/graphics/tiles/border_top_left.pngbin0 -> 146 bytes
-rw-r--r--lib/nes/app/graphics/tiles/border_top_right.pngbin0 -> 146 bytes
-rw-r--r--lib/nes/app/ui/CMakeLists.txt6
-rw-r--r--lib/nes/app/ui/screen-confirm-quit.cc68
-rw-r--r--lib/nes/app/ui/screen-confirm-quit.hh24
-rw-r--r--lib/nes/app/ui/screen-error.cc7
-rw-r--r--lib/nes/app/ui/screen-error.hh7
-rw-r--r--lib/nes/app/ui/screen-freeze.cc33
-rw-r--r--lib/nes/app/ui/screen-freeze.hh25
-rw-r--r--lib/nes/common/display.hh2
-rw-r--r--lib/nes/common/rgb.hh2
25 files changed, 294 insertions, 33 deletions
diff --git a/app/mac/impl/display-spritekit.hh b/app/mac/impl/display-spritekit.hh
index 64e3b03..334d492 100644
--- a/app/mac/impl/display-spritekit.hh
+++ b/app/mac/impl/display-spritekit.hh
@@ -13,6 +13,7 @@ namespace nes::app::mac
NSMutableData* buffer_front_;
NSMutableData* buffer_back_;
u8* bytes_back_;
+ u8* bytes_front_;
public:
explicit display_spritekit(SKSpriteNode* node);
diff --git a/app/mac/impl/display-spritekit.mm b/app/mac/impl/display-spritekit.mm
index b6123d6..a5809c3 100644
--- a/app/mac/impl/display-spritekit.mm
+++ b/app/mac/impl/display-spritekit.mm
@@ -16,13 +16,14 @@ namespace nes::app::mac
, buffer_front_{ make_buffer() }
, buffer_back_{ make_buffer() }
, bytes_back_{ static_cast<u8*>([buffer_back_ mutableBytes]) }
+ , bytes_front_{ static_cast<u8*>([buffer_front_ mutableBytes]) }
{
}
auto display_spritekit::switch_buffers() -> void
{
std::swap(buffer_back_, buffer_front_);
- bytes_back_ = static_cast<u8*>([buffer_back_ mutableBytes]);
+ std::swap(bytes_back_, bytes_front_);
auto texture = [SKTexture textureWithData:buffer_front_ size:CGSizeMake(width, height) flipped:YES];
[texture setFilteringMode:SKTextureFilteringNearest];
@@ -42,9 +43,9 @@ namespace nes::app::mac
{
auto const offset = (y * width + x) * 4;
auto res = rgb{};
- res.r = bytes_back_[offset + 0];
- res.g = bytes_back_[offset + 1];
- res.b = bytes_back_[offset + 2];
+ res.r = bytes_front_[offset + 0];
+ res.g = bytes_front_[offset + 1];
+ res.b = bytes_front_[offset + 2];
return res;
}
} // namespace nes::app::mac \ No newline at end of file
diff --git a/lib/nes/app/action.hh b/lib/nes/app/action.hh
index f63c17d..cca7a35 100644
--- a/lib/nes/app/action.hh
+++ b/lib/nes/app/action.hh
@@ -17,6 +17,8 @@ namespace nes::app
go_to_settings,
go_to_help,
close_popup,
+ cancel_quit,
+ confirm_quit,
launch_game,
show_error,
};
@@ -35,6 +37,8 @@ namespace nes::app
static auto go_to_settings() -> action { return action{ type::go_to_settings }; }
static auto go_to_help() -> action { return action{ type::go_to_help }; }
static auto close_popup() -> action { return action{ type::close_popup }; }
+ static auto cancel_quit() -> action { return action{ type::cancel_quit }; }
+ static auto confirm_quit() -> action { return action{ type::confirm_quit }; }
static auto launch_game(std::string_view const file_name) -> action
{
diff --git a/lib/nes/app/application.cc b/lib/nes/app/application.cc
index ac81840..01d3f0b 100644
--- a/lib/nes/app/application.cc
+++ b/lib/nes/app/application.cc
@@ -4,7 +4,6 @@
#include "nes/app/ui/screen.hh"
#include "nes/app/graphics/renderer.hh"
#include "nes/app/action.hh"
-#include <iostream>
namespace nes::app
{
@@ -32,6 +31,7 @@ namespace nes::app
, screen_title_{ keyboard }
, screen_browser_{ keyboard, file_browser }
, screen_error_{ keyboard }
+ , screen_confirm_quit_{ keyboard }
{
display_.screen = &screen_title_;
}
@@ -49,8 +49,29 @@ namespace nes::app
handle_action(a);
}
- if (console_)
+ if (display_.screen)
{
+ // Directly let the scene render itself without rendering any gameplay (the scene is responsible for
+ // clearing the buffer).
+ display_.switch_buffers();
+ }
+ else if (console_)
+ {
+ // Handle escape key.
+ while (auto const event = input_manager_.get_keyboard().poll_event())
+ {
+ if (event == input_event::key_down(key::escape))
+ {
+ screen_freeze_.freeze(display_);
+ screen_confirm_quit_.set_confirm(false);
+ display_.screen = &screen_freeze_;
+ display_.popup = &screen_confirm_quit_;
+ display_.switch_buffers();
+ return;
+ }
+ }
+
+ // Forward current input state to the NES.
console_->ref_controller_1().set_pressed(input_manager_.get_input_1().read_buttons());
console_->ref_controller_2().set_pressed(input_manager_.get_input_2().read_buttons());
@@ -60,22 +81,15 @@ namespace nes::app
if (console_->get_status() != status::success)
{
- // TODO: Present as error popup
- std::cerr << "Invalid state: " << to_string(console_->get_status()) << std::endl;
- std::abort();
+ screen_freeze_.freeze(display_);
+ display_.screen = &screen_freeze_;
+ show_error("Runtime error", console_->get_status(), action::go_to_browser());
+ console_.clear();
}
-
- // TODO: Handle escape key
- }
- else
- {
- // Directly let the scene render itself without rendering any gameplay (the scene is responsible for
- // clearing the buffer).
- display_.switch_buffers();
}
}
- auto application::handle_action(action const a) -> void
+ auto application::handle_action(action const& a) -> void
{
switch (a.get_type())
{
@@ -112,6 +126,19 @@ namespace nes::app
display_.popup = nullptr;
break;
}
+ case action::type::cancel_quit:
+ {
+ display_.popup = nullptr;
+ display_.screen = nullptr;
+ break;
+ }
+ case action::type::confirm_quit:
+ {
+ display_.popup = nullptr;
+ display_.screen = &screen_browser_;
+ console_.clear();
+ break;
+ }
case action::type::launch_game:
{
u8 buffer[sys::cartridge::max_file_size];
@@ -142,11 +169,11 @@ namespace nes::app
}
}
- auto application::show_error(std::string_view const message, status const error) -> void
+ auto application::show_error(std::string_view const message, status const error, action const& action) -> void
{
screen_error_.set_message(message);
screen_error_.set_error(error);
+ screen_error_.set_action(action);
display_.popup = &screen_error_;
}
-
} // namespace nes::app \ No newline at end of file
diff --git a/lib/nes/app/application.hh b/lib/nes/app/application.hh
index 81e6516..ddeff85 100644
--- a/lib/nes/app/application.hh
+++ b/lib/nes/app/application.hh
@@ -3,7 +3,9 @@
#include "nes/app/input/input-manager.hh"
#include "nes/app/ui/screen-title.hh"
#include "nes/app/ui/screen-browser.hh"
+#include "nes/app/ui/screen-freeze.hh"
#include "nes/app/ui/screen-error.hh"
+#include "nes/app/ui/screen-confirm-quit.hh"
#include "nes/sys/nes.hh"
#include "nes/common/box.hh"
#include "nes/common/display.hh"
@@ -47,7 +49,9 @@ namespace nes::app
box<sys::nes> console_{};
screen_title screen_title_;
screen_browser screen_browser_;
+ screen_freeze screen_freeze_;
screen_error screen_error_;
+ screen_confirm_quit screen_confirm_quit_;
public:
explicit application(display&, input_device_keyboard&, file_browser& file_browser);
@@ -64,7 +68,7 @@ namespace nes::app
auto remove_controller(input_device_controller& c) -> void { input_manager_.remove_controller(c); }
private:
- auto handle_action(action) -> void;
- auto show_error(std::string_view message, status error) -> void;
+ auto handle_action(action const&) -> void;
+ auto show_error(std::string_view message, status error, action const& action = action::close_popup()) -> void;
};
} // namespace nes::app \ No newline at end of file
diff --git a/lib/nes/app/graphics/renderer.cc b/lib/nes/app/graphics/renderer.cc
index d512e71..564125b 100644
--- a/lib/nes/app/graphics/renderer.cc
+++ b/lib/nes/app/graphics/renderer.cc
@@ -5,6 +5,7 @@
#include "nes/app/graphics/image-view.hh"
#include "nes/app/graphics/text-attributes.hh"
#include "nes/app/graphics/tiles/characters.hh"
+#include "nes/app/graphics/tiles/borders.hh"
#include "nes/common/display.hh"
namespace nes::app
@@ -125,11 +126,9 @@ namespace nes::app
}
}
- auto renderer::render_rect(
- i32 const x, i32 const y, u32 const width, u32 const height, color const c) -> void
+ auto renderer::render_rect(i32 const x, i32 const y, u32 const width, u32 const height, color const c) -> void
{
if (c == color::transparent) { return; }
- if (x >= static_cast<i32>(renderer::width) || y >= static_cast<i32>(renderer::height)) { return; }
auto const resolved = resolve_color(c);
auto const x_stop = std::min(x + static_cast<i32>(width), static_cast<i32>(renderer::width));
@@ -145,6 +144,55 @@ namespace nes::app
}
}
+ auto renderer::render_border(i32 const x, i32 const y, u32 const width, u32 const height, color const c) -> void
+ {
+ if (width <= 1 || height <= 1) { return; }
+
+ auto const x_last = std::min(x + static_cast<i32>(width), static_cast<i32>(renderer::width)) - 1;
+ auto const y_last = std::min(y + static_cast<i32>(height), static_cast<i32>(renderer::height)) - 1;
+ auto const x_start = std::max(x, 0);
+ auto const y_start = std::max(y, 0);
+ for (auto border_y = i32{ y_start }; border_y <= y_last; ++border_y)
+ {
+ for (auto border_x = i32{ x_start }; border_x <= x_last; ++border_x)
+ {
+ if (border_x == x_start && border_y == y_start)
+ {
+ render_mask_tile(border_x, border_y, tiles::border_top_left, c);
+ }
+ else if (border_x == x_last && border_y == y_start)
+ {
+ render_mask_tile(border_x, border_y, tiles::border_top_right, c);
+ }
+ else if (border_x == x_last && border_y == y_last)
+ {
+ render_mask_tile(border_x, border_y, tiles::border_bottom_right, c);
+ }
+ else if (border_x == x_start && border_y == y_last)
+ {
+ render_mask_tile(border_x, border_y, tiles::border_bottom_left, c);
+ }
+ else if (border_x == x_start)
+ {
+ render_mask_tile(border_x, border_y, tiles::border_left, c);
+ }
+ else if (border_x == x_last)
+ {
+ render_mask_tile(border_x, border_y, tiles::border_right, c);
+ }
+ else if (border_y == y_start)
+ {
+ render_mask_tile(border_x, border_y, tiles::border_top, c);
+ }
+ else if (border_y == y_last)
+ {
+ render_mask_tile(border_x, border_y, tiles::border_bottom, c);
+ }
+ }
+ }
+ }
+
+
auto renderer::render_fill(color const c) -> void
{
render_rect(0, 0, width, height, c);
diff --git a/lib/nes/app/graphics/renderer.hh b/lib/nes/app/graphics/renderer.hh
index 9b764a6..83cafb3 100644
--- a/lib/nes/app/graphics/renderer.hh
+++ b/lib/nes/app/graphics/renderer.hh
@@ -37,6 +37,8 @@ namespace nes::app
auto operator=(renderer const&) -> renderer& = delete;
auto operator=(renderer&&) -> renderer& = delete;
+ auto get_display() const -> display& { return display_; }
+
/// Render a colored tile at the given tile coordinates.
auto render_image_tile(i32 x, i32 y, image_tile const&) -> void;
/// Render an alpha-only tile at the given tile coordinates with the given color.
@@ -66,6 +68,8 @@ namespace nes::app
/// Render a rectangle at the given tile coordinates with a given size (measured in tiles).
auto render_rect(i32 x, i32 y, u32 width, u32 height, color) -> void;
+ /// Render a border around a rectangle with the given size (measured in tiles).
+ auto render_border(i32 x, i32 y, u32 width, u32 height, color) -> void;
/// Completely fill the screen.
auto render_fill(color) -> void;
diff --git a/lib/nes/app/graphics/tiles/CMakeLists.txt b/lib/nes/app/graphics/tiles/CMakeLists.txt
index 144b9da..eced5ca 100644
--- a/lib/nes/app/graphics/tiles/CMakeLists.txt
+++ b/lib/nes/app/graphics/tiles/CMakeLists.txt
@@ -74,6 +74,20 @@ nes_generate_tiles(
nes_generate_tiles(
nes
+ borders
+ TYPE mask
+ FILES
+ border_top.png
+ border_top_left.png
+ border_top_right.png
+ border_left.png
+ border_right.png
+ border_bottom.png
+ border_bottom_left.png
+ border_bottom_right.png)
+
+nes_generate_tiles(
+ nes
banner
TYPE image
FILES banner.png) \ No newline at end of file
diff --git a/lib/nes/app/graphics/tiles/border_bottom.png b/lib/nes/app/graphics/tiles/border_bottom.png
new file mode 100644
index 0000000..05ac7a7
--- /dev/null
+++ b/lib/nes/app/graphics/tiles/border_bottom.png
Binary files differ
diff --git a/lib/nes/app/graphics/tiles/border_bottom_left.png b/lib/nes/app/graphics/tiles/border_bottom_left.png
new file mode 100644
index 0000000..974b1b1
--- /dev/null
+++ b/lib/nes/app/graphics/tiles/border_bottom_left.png
Binary files differ
diff --git a/lib/nes/app/graphics/tiles/border_bottom_right.png b/lib/nes/app/graphics/tiles/border_bottom_right.png
new file mode 100644
index 0000000..2fa6822
--- /dev/null
+++ b/lib/nes/app/graphics/tiles/border_bottom_right.png
Binary files differ
diff --git a/lib/nes/app/graphics/tiles/border_left.png b/lib/nes/app/graphics/tiles/border_left.png
new file mode 100644
index 0000000..ba5ecb6
--- /dev/null
+++ b/lib/nes/app/graphics/tiles/border_left.png
Binary files differ
diff --git a/lib/nes/app/graphics/tiles/border_right.png b/lib/nes/app/graphics/tiles/border_right.png
new file mode 100644
index 0000000..801bd7d
--- /dev/null
+++ b/lib/nes/app/graphics/tiles/border_right.png
Binary files differ
diff --git a/lib/nes/app/graphics/tiles/border_top.png b/lib/nes/app/graphics/tiles/border_top.png
new file mode 100644
index 0000000..94d5cf5
--- /dev/null
+++ b/lib/nes/app/graphics/tiles/border_top.png
Binary files differ
diff --git a/lib/nes/app/graphics/tiles/border_top_left.png b/lib/nes/app/graphics/tiles/border_top_left.png
new file mode 100644
index 0000000..f67b585
--- /dev/null
+++ b/lib/nes/app/graphics/tiles/border_top_left.png
Binary files differ
diff --git a/lib/nes/app/graphics/tiles/border_top_right.png b/lib/nes/app/graphics/tiles/border_top_right.png
new file mode 100644
index 0000000..0b8613c
--- /dev/null
+++ b/lib/nes/app/graphics/tiles/border_top_right.png
Binary files differ
diff --git a/lib/nes/app/ui/CMakeLists.txt b/lib/nes/app/ui/CMakeLists.txt
index de42744..37720c1 100644
--- a/lib/nes/app/ui/CMakeLists.txt
+++ b/lib/nes/app/ui/CMakeLists.txt
@@ -7,5 +7,9 @@ target_sources(
screen-title.cc
screen-browser.hh
screen-browser.cc
+ screen-freeze.hh
+ screen-freeze.cc
screen-error.hh
- screen-error.cc) \ No newline at end of file
+ screen-error.cc
+ screen-confirm-quit.hh
+ screen-confirm-quit.cc) \ No newline at end of file
diff --git a/lib/nes/app/ui/screen-confirm-quit.cc b/lib/nes/app/ui/screen-confirm-quit.cc
new file mode 100644
index 0000000..3c7a337
--- /dev/null
+++ b/lib/nes/app/ui/screen-confirm-quit.cc
@@ -0,0 +1,68 @@
+#include "nes/app/ui/screen-confirm-quit.hh"
+#include "nes/app/graphics/renderer.hh"
+#include "nes/app/graphics/color.hh"
+#include "nes/app/input/input-device-keyboard.hh"
+
+namespace nes::app
+{
+ screen_confirm_quit::screen_confirm_quit(input_device_keyboard& keyboard)
+ : keyboard_{ keyboard }
+ {
+ }
+
+ auto screen_confirm_quit::render(renderer& renderer) -> void
+ {
+ renderer.render_rect(2, 8, 28, 10, color::background_secondary);
+ renderer.render_border(2, 8, 28, 10, color::foreground_primary);
+
+ auto const attrs = text_attributes{}
+ .set_alignment(text_alignment::center)
+ .set_ellipsize_mode(ellipsize_mode::end)
+ .set_max_width(26);
+ renderer.render_text(16, 10, "Are you sure?", color::foreground_primary, attrs);
+ renderer.render_text(16, 12, "All progress is lost.", color::foreground_primary, attrs);
+ if (confirm_)
+ {
+ renderer.render_text(10, 15, "Cancel", color::foreground_secondary);
+ renderer.render_text(18, 15, "> OK <", color::accent_primary);
+ }
+ else
+ {
+ renderer.render_text(8, 15, "> Cancel <", color::accent_primary);
+ renderer.render_text(20, 15, "OK", color::foreground_secondary);
+ }
+ }
+
+ auto screen_confirm_quit::process_events() -> action
+ {
+ while (auto const event = keyboard_.poll_event())
+ {
+ // Use key-up so the enter key is not immediately captured by the game.
+ if (event == input_event::key_up(key::enter))
+ {
+ if (confirm_)
+ {
+ return action::confirm_quit();
+ }
+ else
+ {
+ return action::cancel_quit();
+ }
+ }
+ else if (event == input_event::key_down(key::space))
+ {
+ confirm_ = !confirm_;
+ }
+ else if (event == input_event::key_down(key::arrow_left))
+ {
+ confirm_ = false;
+ }
+ else if (event == input_event::key_down(key::arrow_right))
+ {
+ confirm_ = true;
+ }
+ }
+
+ return action{};
+ }
+} // namespace nes::app \ No newline at end of file
diff --git a/lib/nes/app/ui/screen-confirm-quit.hh b/lib/nes/app/ui/screen-confirm-quit.hh
new file mode 100644
index 0000000..c176f4b
--- /dev/null
+++ b/lib/nes/app/ui/screen-confirm-quit.hh
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "nes/app/ui/screen.hh"
+
+namespace nes::app
+{
+ class input_device_keyboard;
+
+ /// A popup asking the user if they really want to quit the game.
+ class screen_confirm_quit final : public screen
+ {
+ input_device_keyboard& keyboard_;
+ bool confirm_{ false };
+
+ public:
+ explicit screen_confirm_quit(input_device_keyboard&);
+
+ auto get_confirm() const -> bool { return confirm_; }
+ auto set_confirm(bool const v) -> void { confirm_ = v; }
+
+ auto render(renderer&) -> void override;
+ auto process_events() -> action override;
+ };
+} // namespace nes::app \ No newline at end of file
diff --git a/lib/nes/app/ui/screen-error.cc b/lib/nes/app/ui/screen-error.cc
index 67a3244..9375012 100644
--- a/lib/nes/app/ui/screen-error.cc
+++ b/lib/nes/app/ui/screen-error.cc
@@ -12,8 +12,8 @@ namespace nes::app
auto screen_error::render(renderer& renderer) -> void
{
- // TODO: Nicer box
- renderer.render_rect(2, 9, 28, 5, color::background_secondary);
+ renderer.render_rect(2, 8, 28, 10, color::background_secondary);
+ renderer.render_border(2, 8, 28, 10, color::foreground_primary);
auto const attrs = text_attributes{}
.set_alignment(text_alignment::center)
@@ -21,6 +21,7 @@ namespace nes::app
.set_max_width(26);
renderer.render_text_format(16, 10, color::foreground_primary, attrs, "{}:", message_);
renderer.render_text(16, 12, to_string(error_), color::foreground_primary, attrs);
+ renderer.render_text(16, 15, "> OK <", color::accent_primary, attrs);
}
auto screen_error::process_events() -> action
@@ -29,7 +30,7 @@ namespace nes::app
{
if (event == input_event::key_down(key::enter))
{
- return action::close_popup();
+ return action_;
}
}
diff --git a/lib/nes/app/ui/screen-error.hh b/lib/nes/app/ui/screen-error.hh
index ec877e1..4559942 100644
--- a/lib/nes/app/ui/screen-error.hh
+++ b/lib/nes/app/ui/screen-error.hh
@@ -12,14 +12,17 @@ namespace nes::app
input_device_keyboard& keyboard_;
std::string_view message_;
status error_{ status::success };
+ action action_{};
public:
explicit screen_error(input_device_keyboard&);
auto get_message() const -> std::string_view { return message_; }
auto get_error() const -> status { return error_; }
- auto set_message(std::string_view const v) { message_ = v; }
- auto set_error(status const v) { error_ = v; }
+ auto get_action() const -> action { return action_; }
+ auto set_message(std::string_view const v) -> void { message_ = v; }
+ auto set_error(status const v) -> void { error_ = v; }
+ auto set_action(action const& v) -> void { action_ = v; }
auto render(renderer&) -> void override;
auto process_events() -> action override;
diff --git a/lib/nes/app/ui/screen-freeze.cc b/lib/nes/app/ui/screen-freeze.cc
new file mode 100644
index 0000000..60410dd
--- /dev/null
+++ b/lib/nes/app/ui/screen-freeze.cc
@@ -0,0 +1,33 @@
+#include "nes/app/ui/screen-freeze.hh"
+#include "nes/app/graphics/renderer.hh"
+
+namespace nes::app
+{
+ auto screen_freeze::freeze(display const& disp) -> void
+ {
+ for (auto y = u32{ 0 }; y < display::height; ++y)
+ {
+ for (auto x = u32{ 0 }; x < display::width; ++x)
+ {
+ frozen_data_[y * display::width + x] = disp.get(x, y);
+ }
+ }
+ }
+
+ auto screen_freeze::render(renderer& renderer) -> void
+ {
+ auto& disp = renderer.get_display();
+ for (auto y = u32{ 0 }; y < display::height; ++y)
+ {
+ for (auto x = u32{ 0 }; x < display::width; ++x)
+ {
+ disp.set(x, y, frozen_data_[y * display::width + x]);
+ }
+ }
+ }
+
+ auto screen_freeze::process_events() -> action
+ {
+ return action{};
+ }
+} // namespace nes::app \ No newline at end of file
diff --git a/lib/nes/app/ui/screen-freeze.hh b/lib/nes/app/ui/screen-freeze.hh
new file mode 100644
index 0000000..fe67aad
--- /dev/null
+++ b/lib/nes/app/ui/screen-freeze.hh
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "nes/app/ui/screen.hh"
+#include "nes/common/rgb.hh"
+#include "nes/common/display.hh"
+
+namespace nes::app
+{
+ /// Freeze screen which preserves the display buffer at a certain state.
+ ///
+ /// This is usually used to preserve the image from the console when the game should not continue.
+ class screen_freeze final : public screen
+ {
+ rgb frozen_data_[display::width * display::height]{};
+
+ public:
+ explicit screen_freeze() = default;
+
+ auto render(renderer&) -> void override;
+ auto process_events() -> action override;
+
+ /// Update the image shown to the back buffer of the given display.
+ auto freeze(display const&) -> void;
+ };
+} // namespace nes::app \ No newline at end of file
diff --git a/lib/nes/common/display.hh b/lib/nes/common/display.hh
index eb35366..f7d53c1 100644
--- a/lib/nes/common/display.hh
+++ b/lib/nes/common/display.hh
@@ -25,7 +25,7 @@ namespace nes
virtual auto switch_buffers() -> void = 0;
/// Update the pixel at the given position in the back buffer.
virtual auto set(u32 x, u32 y, rgb value) -> void = 0;
- /// Load the pixel value at the given position in the back buffer.
+ /// Load the pixel value at the given position in the front buffer.
virtual auto get(u32 x, u32 y) const -> rgb = 0;
protected:
diff --git a/lib/nes/common/rgb.hh b/lib/nes/common/rgb.hh
index 0b8ec1c..b8cd0f3 100644
--- a/lib/nes/common/rgb.hh
+++ b/lib/nes/common/rgb.hh
@@ -8,7 +8,7 @@ namespace nes
class rgb
{
public:
- explicit rgb() = default;
+ rgb() = default;
explicit rgb(u8 const r, u8 const g, u8 const b)
: r{ r }