5#include "absl/strings/str_format.h"
10#include "imgui/imgui.h"
22constexpr float kStandardSpacing = 8.0f;
24constexpr float kButtonHeight = 30.0f;
33void AddSectionSpacing() {
45 const auto& theme = theme_manager.GetCurrentTheme();
50 if (ImGui::Shortcut(ImGuiKey_Space, ImGuiInputFlags_RouteGlobal)) {
55 if (ImGui::Shortcut(ImGuiKey_F10, ImGuiInputFlags_RouteGlobal)) {
57 emu->
snes().RunFrame();
67 bool is_running = emu->
running();
69 if (ImGui::Button(
ICON_MD_PAUSE " Pause", ImVec2(100, kButtonHeight))) {
72 if (ImGui::IsItemHovered()) {
73 ImGui::SetTooltip(
"Pause emulation (Space)");
79 if (ImGui::IsItemHovered()) {
80 ImGui::SetTooltip(
"Start emulation (Space)");
89 emu->
snes().RunFrame();
92 if (ImGui::IsItemHovered()) {
93 ImGui::SetTooltip(
"Step one frame (F10)");
101 LOG_INFO(
"Emulator",
"System reset");
103 if (ImGui::IsItemHovered()) {
104 ImGui::SetTooltip(
"Reset SNES (Ctrl+R)");
112 if (!rom_path.empty()) {
115 if (ext ==
".sfc" || ext ==
".smc" || ext ==
".SFC" || ext ==
".SMC") {
118 std::ifstream rom_file(rom_path, std::ios::binary);
119 if (rom_file.good()) {
120 std::vector<uint8_t> rom_data(
121 (std::istreambuf_iterator<char>(rom_file)),
122 std::istreambuf_iterator<char>()
127 if (!rom_data.empty()) {
129 LOG_INFO(
"Emulator",
"Loaded ROM: %s (%zu bytes)",
132 LOG_ERROR(
"Emulator",
"ROM file is empty: %s", rom_path.c_str());
135 LOG_ERROR(
"Emulator",
"Failed to open ROM file: %s", rom_path.c_str());
137 }
catch (
const std::exception& e) {
138 LOG_ERROR(
"Emulator",
"Error loading ROM: %s", e.what());
141 LOG_WARN(
"Emulator",
"Invalid ROM file extension: %s (expected .sfc or .smc)",
146 if (ImGui::IsItemHovered()) {
147 ImGui::SetTooltip(
"Load a different ROM file\n"
148 "Allows testing hacks with assembly patches applied");
160 if (ImGui::IsItemHovered()) {
161 ImGui::SetTooltip(
"Enable debugger features");
183 if (ImGui::IsItemHovered()) {
184 ImGui::SetTooltip(
"Fast forward (shortcut: hold Tab)");
196 }
else if (fps >= 45.0) {
202 ImGui::TextColored(fps_color,
ICON_MD_SPEED " %.1f FPS", fps);
209 ImVec4 audio_color = audio_status.
is_playing ?
214 audio_status.queued_frames);
216 if (ImGui::IsItemHovered()) {
217 ImGui::SetTooltip(
"Audio Backend: %s\nQueued: %u frames\nPlaying: %s",
219 audio_status.queued_frames,
220 audio_status.is_playing ?
"YES" :
"NO");
232 ImGuiIO& io = ImGui::GetIO();
233 if (io.WantCaptureKeyboard) {
237 if (ImGui::IsItemHovered()) {
238 ImGui::SetTooltip(
"Keyboard captured by UI\nGame input disabled");
244 if (ImGui::IsItemHovered()) {
245 ImGui::SetTooltip(
"Game input active\nPress F1 for controls");
249 ImGui::PopStyleColor(3);
256 const auto& theme = theme_manager.GetCurrentTheme();
259 ImGui::BeginChild(
"##SNES_PPU", ImVec2(0, 0),
true,
260 ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
262 ImVec2 canvas_size = ImGui::GetContentRegionAvail();
263 ImVec2 snes_size = ImVec2(512, 480);
267 float aspect = snes_size.x / snes_size.y;
268 float display_w = canvas_size.x;
269 float display_h = display_w / aspect;
271 if (display_h > canvas_size.y) {
272 display_h = canvas_size.y;
273 display_w = display_h * aspect;
276 float pos_x = (canvas_size.x - display_w) * 0.5f;
277 float pos_y = (canvas_size.y - display_h) * 0.5f;
279 ImGui::SetCursorPos(ImVec2(pos_x, pos_y));
282 ImGui::Image((ImTextureID)(intptr_t)emu->
ppu_texture(),
283 ImVec2(display_w, display_h),
284 ImVec2(0, 0), ImVec2(1, 1));
288 if (ImGui::IsItemHovered()) {
292 ImDrawList* draw_list = ImGui::GetWindowDrawList();
293 ImVec2 screen_pos = ImGui::GetItemRectMin();
294 ImVec2 screen_size = ImGui::GetItemRectMax();
295 draw_list->AddRect(screen_pos, screen_size,
301 ImVec2 text_size = ImGui::CalcTextSize(
"Load a ROM to start emulation");
302 ImGui::SetCursorPos(ImVec2((canvas_size.x - text_size.x) * 0.5f,
303 (canvas_size.y - text_size.y) * 0.5f - 20));
306 ImGui::SetCursorPosX((canvas_size.x - text_size.x) * 0.5f);
308 "Load a ROM to start emulation");
309 ImGui::SetCursorPosX((canvas_size.x - ImGui::CalcTextSize(
"512x480 SNES output").x) * 0.5f);
311 "512x480 SNES output");
315 ImGui::PopStyleColor();
322 const auto& theme = theme_manager.GetCurrentTheme();
325 ImGui::BeginChild(
"##Performance", ImVec2(0, 0),
true);
334 if (ImGui::CollapsingHeader(
ICON_MD_SHOW_CHART " Frame Rate", ImGuiTreeNodeFlags_DefaultOpen)) {
335 ImGui::Text(
"Current: %.2f FPS", metrics.fps);
336 ImGui::Text(
"Target: %.2f FPS", emu->
snes().memory().pal_timing() ? 50.0 : 60.0);
342 if (ImGui::CollapsingHeader(
ICON_MD_MEMORY " CPU Status", ImGuiTreeNodeFlags_DefaultOpen)) {
343 ImGui::Text(
"PC: $%02X:%04X", metrics.cpu_pb, metrics.cpu_pc);
344 ImGui::Text(
"Cycles: %llu", metrics.cycles);
348 if (ImGui::CollapsingHeader(
ICON_MD_AUDIOTRACK " Audio Status", ImGuiTreeNodeFlags_DefaultOpen)) {
352 ImGui::Text(
"Queued: %u frames", audio_status.queued_frames);
353 ImGui::Text(
"Playing: %s", audio_status.is_playing ?
"YES" :
"NO");
360 ImGui::PopStyleColor();
364 if (!show || !*show)
return;
367 const auto& theme = theme_manager.GetCurrentTheme();
370 ImVec2 center = ImGui::GetMainViewport()->GetCenter();
371 ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
372 ImGui::SetNextWindowSize(ImVec2(550, 600), ImGuiCond_Appearing);
378 ImGuiWindowFlags_NoCollapse)) {
385 if (ImGui::BeginTable(
"EmulatorControls", 2, ImGuiTableFlags_Borders)) {
386 ImGui::TableSetupColumn(
"Key", ImGuiTableColumnFlags_WidthFixed, 120);
387 ImGui::TableSetupColumn(
"Action", ImGuiTableColumnFlags_WidthStretch);
388 ImGui::TableHeadersRow();
390 auto AddRow = [](
const char* key,
const char* action) {
391 ImGui::TableNextRow();
392 ImGui::TableNextColumn();
393 ImGui::Text(
"%s", key);
394 ImGui::TableNextColumn();
395 ImGui::Text(
"%s", action);
398 AddRow(
"Space",
"Play/Pause emulation");
399 AddRow(
"F10",
"Step one frame");
400 AddRow(
"Ctrl+R",
"Reset SNES");
401 AddRow(
"Tab (hold)",
"Turbo mode (fast forward)");
402 AddRow(
"F1",
"Show/hide this help");
416 if (ImGui::BeginTable(
"GameControls", 2, ImGuiTableFlags_Borders)) {
417 ImGui::TableSetupColumn(
"Key", ImGuiTableColumnFlags_WidthFixed, 120);
418 ImGui::TableSetupColumn(
"Button", ImGuiTableColumnFlags_WidthStretch);
419 ImGui::TableHeadersRow();
421 auto AddRow = [](
const char* key,
const char* button) {
422 ImGui::TableNextRow();
423 ImGui::TableNextColumn();
424 ImGui::Text(
"%s", key);
425 ImGui::TableNextColumn();
426 ImGui::Text(
"%s", button);
429 AddRow(
"Arrow Keys",
"D-Pad (Up/Down/Left/Right)");
430 AddRow(
"X",
"A Button");
431 AddRow(
"Z",
"B Button");
432 AddRow(
"S",
"X Button");
433 AddRow(
"A",
"Y Button");
434 AddRow(
"D",
"L Shoulder");
435 AddRow(
"C",
"R Shoulder");
436 AddRow(
"Enter",
"Start");
437 AddRow(
"RShift",
"Select");
451 ImGui::BulletText(
"Input is disabled when typing in UI fields");
452 ImGui::BulletText(
"Check the status bar for input capture state");
453 ImGui::BulletText(
"Click the game screen to ensure focus");
454 ImGui::BulletText(
"The emulator continues running in background");
459 if (ImGui::Button(
"Close", ImVec2(-1, 30))) {
465 ImGui::PopStyleColor(2);
472 const auto& theme = theme_manager.GetCurrentTheme();
485 static bool show_shortcuts =
false;
486 if (ImGui::IsKeyPressed(ImGuiKey_F1)) {
487 show_shortcuts = !show_shortcuts;
491 ImGui::PopStyleColor();
A class for emulating and debugging SNES games.
gfx::IRenderer * renderer()
void Initialize(gfx::IRenderer *renderer, const std::vector< uint8_t > &rom_data)
bool is_debugging() const
void set_turbo_mode(bool turbo)
double GetCurrentFPS() const
void set_debugging(bool debugging)
bool is_turbo_mode() const
void set_running(bool running)
bool is_snes_initialized() const
audio::IAudioBackend * audio_backend()
auto running() const -> bool
EmulatorMetrics GetMetrics()
virtual std::string GetBackendName() const =0
virtual AudioStatus GetStatus() const =0
static ThemeManager & Get()
static std::string ShowOpenFileDialog()
ShowOpenFileDialog opens a file dialog and returns the selected filepath. Uses global feature flag to...
#define ICON_MD_FOLDER_OPEN
#define ICON_MD_VOLUME_UP
#define ICON_MD_FAST_FORWARD
#define ICON_MD_PLAY_ARROW
#define ICON_MD_VIDEOGAME_ASSET
#define ICON_MD_BUG_REPORT
#define ICON_MD_SHOW_CHART
#define ICON_MD_AUDIOTRACK
#define ICON_MD_SKIP_NEXT
#define ICON_MD_SPORTS_ESPORTS
#define ICON_MD_VOLUME_OFF
#define ICON_MD_RESTART_ALT
#define LOG_ERROR(category, format,...)
#define LOG_WARN(category, format,...)
#define LOG_INFO(category, format,...)
constexpr float kIconSize
constexpr float kSectionSpacing
void RenderPerformanceMonitor(Emulator *emu)
Performance metrics (FPS, frame time, audio status)
void RenderKeyboardShortcuts(bool *show)
Keyboard shortcuts help overlay (F1 in modern emulators)
void RenderSnesPpu(Emulator *emu)
SNES PPU output display.
void RenderNavBar(Emulator *emu)
Navigation bar with play/pause, step, reset controls.
void RenderEmulatorInterface(Emulator *emu)
Main emulator UI interface - renders the emulator window.
Graphical User Interface (GUI) components for the application.
ImVec4 ConvertColorToImVec4(const Color &color)
std::string GetFileName(const std::string &filename)
Gets the filename from a full path.
std::string GetFileExtension(const std::string &filename)
Gets the file extension from a filename.
Main namespace for the application.