mirror of
https://github.com/pound-emu/pound.git
synced 2025-12-11 16:36:59 +00:00
feat(gui): Extract and modularize GUI system from main.cpp
- Create modular GUI architecture with base Panel class - Implement GUIManager to handle window lifecycle and panel management - Add Window wrapper class for SDL3/OpenGL context management - Create specialized panels: - ConsolePanel: Colored log output with timestamps - CPUPanel: CPU debugging with tabs for registers, memory, and disassembly - PerformancePanel: Real-time FPS and frame time monitoring - Apply modern dark theme with purple accents - Add comprehensive menu bar (File, Emulation, View, Tools, Help) - Update CMakeLists.txt to include new gui/ directory structure - Refactor main.cpp to use the new GUI system - Cutom theme on switch colors
This commit is contained in:
parent
22418185f8
commit
2bb666a088
15 changed files with 1099 additions and 121 deletions
|
|
@ -71,3 +71,5 @@ target_include_directories(Pound PRIVATE
|
|||
find_package(OpenGL REQUIRED)
|
||||
target_link_libraries(Pound PRIVATE OpenGL::GL)
|
||||
|
||||
# add ./gui directory
|
||||
add_subdirectory(gui)
|
||||
169
core/main.cpp
169
core/main.cpp
|
|
@ -1,68 +1,20 @@
|
|||
// Copyright 2025 Pound Emulator Project. All rights reserved.
|
||||
|
||||
#include <thread>
|
||||
#include <imgui.h>
|
||||
#include <imgui_impl_sdl3.h>
|
||||
#include <imgui_impl_opengl3.h>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
|
||||
#include "Base/Logging/Backend.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_opengl.h>
|
||||
|
||||
#include "ARM/cpu.h"
|
||||
#include "Base/Config.h"
|
||||
#include "ARM/cpu.h"
|
||||
#include "JIT/jit.h"
|
||||
|
||||
SDL_Window* Window{};
|
||||
SDL_GLContext gl_context{};
|
||||
SDL_Event windowEvent;
|
||||
|
||||
void initSDL3() {
|
||||
|
||||
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
||||
LOG_ERROR(Render, "Error while creating SDL3 Context!");
|
||||
}
|
||||
|
||||
SDL_PropertiesID props = SDL_CreateProperties();
|
||||
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, "Pound Emulator");
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_CENTERED);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_CENTERED);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, Config::windowWidth());
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, Config::windowHeight());
|
||||
// For a new Vulkan support, don't forget to change 'SDL_WINDOW_OPENGL' by 'SDL_WINDOW_VULKAN'.
|
||||
SDL_SetNumberProperty(props, "flags", SDL_WINDOW_OPENGL);
|
||||
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN, true);
|
||||
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true);
|
||||
Window = SDL_CreateWindowWithProperties(props);
|
||||
SDL_DestroyProperties(props);
|
||||
|
||||
SDL_SetWindowMinimumSize(Window, 640, 480);
|
||||
|
||||
gl_context = SDL_GL_CreateContext(Window);
|
||||
if (!gl_context) {
|
||||
LOG_ERROR(Render, "Failed to create OpenGL context: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
SDL_GL_MakeCurrent(Window, gl_context);
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
|
||||
ImGui::CreateContext();
|
||||
ImGui::StyleColorsDark();
|
||||
ImGui_ImplSDL3_InitForOpenGL(Window, gl_context);
|
||||
ImGui_ImplOpenGL3_Init("#version 330");
|
||||
|
||||
}
|
||||
|
||||
void deinitializeSDL3() {
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplSDL3_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
SDL_DestroyWindow(Window);
|
||||
SDL_Quit();
|
||||
}
|
||||
#include "gui/GUIManager.h"
|
||||
#include "gui/panels/ConsolePanel.h"
|
||||
#include "gui/panels/CPUPanel.h"
|
||||
#include "gui/panels/PerformancePanel.h"
|
||||
|
||||
// CPU test function
|
||||
void cpuTest() {
|
||||
CPU cpu;
|
||||
cpu.pc = 0;
|
||||
|
|
@ -90,73 +42,48 @@ int main() {
|
|||
const auto config_dir = Base::FS::GetUserPath(Base::FS::PathType::BinaryDir);
|
||||
Config::Load(config_dir / "config.toml");
|
||||
|
||||
initSDL3();
|
||||
|
||||
bool rendering = true;
|
||||
bool show_popup = false; // Flag to control popup visibility
|
||||
|
||||
while (rendering) {
|
||||
// Process events.
|
||||
while (SDL_PollEvent(&windowEvent)) {
|
||||
ImGui_ImplSDL3_ProcessEvent(&windowEvent);
|
||||
|
||||
switch (windowEvent.type) {
|
||||
case SDL_EVENT_QUIT:
|
||||
rendering = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplSDL3_NewFrame();
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(50.0f, 50.0f), ImVec2(FLT_MAX, FLT_MAX));
|
||||
ImGui::NewFrame();
|
||||
|
||||
ImGui::Begin("Pound Emulator");
|
||||
|
||||
if (ImGui::Button("Run CPU Test")) {
|
||||
cpuTest();
|
||||
show_popup = true; // Trigger the popup after running the test
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
// Popup logic
|
||||
if (show_popup) {
|
||||
ImGui::OpenPopup("CPU Test Info");
|
||||
}
|
||||
|
||||
// Define the modal popup
|
||||
if (ImGui::BeginPopupModal("CPU Test Info", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("The CPU test has ran in the terminal.\nKeep in mind that Pound is still in pre-alpha state.");
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::Button("OK", ImVec2(120, 0))) {
|
||||
show_popup = false; // Close the popup
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::Render();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
|
||||
|
||||
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
|
||||
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
SDL_GL_SwapWindow(Window);
|
||||
// Create GUI manager
|
||||
auto gui_manager = std::make_unique<Pound::GUI::GUIManager>();
|
||||
|
||||
// Initialize GUI
|
||||
if (!gui_manager->Initialize("Pound Emulator", Config::windowWidth(), Config::windowHeight())) {
|
||||
LOG_ERROR(Render, "Failed to initialize GUI");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create and add panels
|
||||
auto console_panel = std::make_shared<Pound::GUI::ConsolePanel>();
|
||||
auto cpu_panel = std::make_shared<Pound::GUI::CPUPanel>();
|
||||
auto performance_panel = std::make_shared<Pound::GUI::PerformancePanel>();
|
||||
|
||||
gui_manager->AddPanel(console_panel);
|
||||
gui_manager->AddPanel(cpu_panel);
|
||||
gui_manager->AddPanel(performance_panel);
|
||||
|
||||
// Set up callbacks
|
||||
auto cpu_test_callback = [console_panel]() {
|
||||
console_panel->AddLog("[INFO] Running CPU test...");
|
||||
cpuTest();
|
||||
console_panel->AddLog("[INFO] CPU test completed. Check terminal for details.");
|
||||
};
|
||||
|
||||
gui_manager->SetCPUTestCallback(cpu_test_callback);
|
||||
cpu_panel->SetCPUTestCallback(cpu_test_callback);
|
||||
|
||||
// Add initial console message
|
||||
console_panel->AddLog("[INFO] Pound Emulator started");
|
||||
console_panel->AddLog("[INFO] Version: Pre-Alpha");
|
||||
|
||||
// Main loop
|
||||
while (gui_manager->IsRunning()) {
|
||||
gui_manager->RunFrame();
|
||||
|
||||
// Small delay to prevent excessive CPU usage
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
}
|
||||
|
||||
deinitializeSDL3();
|
||||
// Cleanup
|
||||
gui_manager->Shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
39
gui/CMakeLists.txt
Normal file
39
gui/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# Copyright 2025 Pound Emulator Project. All rights reserved.
|
||||
|
||||
# GUI sources
|
||||
set(GUI_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Window.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GUIManager.cpp
|
||||
)
|
||||
|
||||
set(GUI_HEADERS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Window.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/GUIManager.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Panel.h
|
||||
)
|
||||
|
||||
# Panel sources
|
||||
set(PANEL_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/panels/ConsolePanel.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/panels/CPUPanel.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/panels/PerformancePanel.cpp
|
||||
)
|
||||
|
||||
set(PANEL_HEADERS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/panels/ConsolePanel.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/panels/CPUPanel.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/panels/PerformancePanel.h
|
||||
)
|
||||
|
||||
# Add all GUI sources to the main target
|
||||
target_sources(Pound PRIVATE
|
||||
${GUI_SOURCES}
|
||||
${GUI_HEADERS}
|
||||
${PANEL_SOURCES}
|
||||
${PANEL_HEADERS}
|
||||
)
|
||||
|
||||
# Include directories for GUI
|
||||
target_include_directories(Pound PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/..
|
||||
)
|
||||
73
gui/Colors.h
Normal file
73
gui/Colors.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright 2025 Pound Emulator Project. All rights reserved.
|
||||
#pragma once
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
namespace Pound::GUI {
|
||||
|
||||
class Colors {
|
||||
public:
|
||||
// Primary colors
|
||||
static constexpr ImVec4 Primary = ImVec4(0.0f, 0.765f, 0.890f, 1.0f); // #00c3e3
|
||||
static constexpr ImVec4 PrimaryHover = ImVec4(0.0f, 0.865f, 0.990f, 1.0f); // Lighter
|
||||
static constexpr ImVec4 PrimaryActive = ImVec4(0.0f, 0.665f, 0.790f, 1.0f); // Darker
|
||||
|
||||
// Secondary colors
|
||||
static constexpr ImVec4 Secondary = ImVec4(1.0f, 0.271f, 0.329f, 1.0f); // #ff4554
|
||||
static constexpr ImVec4 SecondaryHover = ImVec4(1.0f, 0.371f, 0.429f, 1.0f); // Lighter
|
||||
static constexpr ImVec4 SecondaryActive = ImVec4(0.9f, 0.171f, 0.229f, 1.0f); // Darker
|
||||
|
||||
// Background colors
|
||||
static constexpr ImVec4 Background = ImVec4(0.255f, 0.271f, 0.282f, 1.0f); // #414548
|
||||
static constexpr ImVec4 BackgroundDark = ImVec4(0.155f, 0.171f, 0.182f, 1.0f);
|
||||
static constexpr ImVec4 BackgroundLight = ImVec4(0.355f, 0.371f, 0.382f, 1.0f);
|
||||
|
||||
// Text colors
|
||||
static constexpr ImVec4 Text = ImVec4(0.95f, 0.96f, 0.98f, 1.0f);
|
||||
static constexpr ImVec4 TextDisabled = ImVec4(0.60f, 0.60f, 0.60f, 1.0f);
|
||||
|
||||
// UI element colors
|
||||
static constexpr ImVec4 Border = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);
|
||||
static constexpr ImVec4 Frame = ImVec4(0.16f, 0.29f, 0.48f, 0.54f);
|
||||
static constexpr ImVec4 FrameHover = ImVec4(0.26f, 0.59f, 0.98f, 0.40f);
|
||||
static constexpr ImVec4 FrameActive = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
|
||||
|
||||
// Special colors
|
||||
static constexpr ImVec4 Success = ImVec4(0.0f, 0.8f, 0.0f, 1.0f);
|
||||
static constexpr ImVec4 Warning = ImVec4(1.0f, 0.8f, 0.0f, 1.0f);
|
||||
static constexpr ImVec4 Error = ImVec4(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
static constexpr ImVec4 Info = ImVec4(0.0f, 0.765f, 0.890f, 1.0f);
|
||||
|
||||
// Utility functions
|
||||
static ImVec4 WithAlpha(const ImVec4& color, float alpha) {
|
||||
return ImVec4(color.x, color.y, color.z, alpha);
|
||||
}
|
||||
|
||||
static ImVec4 Lighten(const ImVec4& color, float amount = 0.1f) {
|
||||
return ImVec4(
|
||||
std::min(1.0f, color.x + amount),
|
||||
std::min(1.0f, color.y + amount),
|
||||
std::min(1.0f, color.z + amount),
|
||||
color.w
|
||||
);
|
||||
}
|
||||
|
||||
static ImVec4 Darken(const ImVec4& color, float amount = 0.1f) {
|
||||
return ImVec4(
|
||||
std::max(0.0f, color.x - amount),
|
||||
std::max(0.0f, color.y - amount),
|
||||
std::max(0.0f, color.z - amount),
|
||||
color.w
|
||||
);
|
||||
}
|
||||
|
||||
// Convert hex to ImVec4 (utility for future use)
|
||||
static ImVec4 FromHex(uint32_t hex, float alpha = 1.0f) {
|
||||
float r = ((hex >> 16) & 0xFF) / 255.0f;
|
||||
float g = ((hex >> 8) & 0xFF) / 255.0f;
|
||||
float b = (hex & 0xFF) / 255.0f;
|
||||
return ImVec4(r, g, b, alpha);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Pound::GUI
|
||||
275
gui/GUIManager.cpp
Normal file
275
gui/GUIManager.cpp
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
// Copyright 2025 Pound Emulator Project. All rights reserved.
|
||||
|
||||
#include "GUIManager.h"
|
||||
#include "Colors.h"
|
||||
#include "Base/Logging/Log.h"
|
||||
#include <imgui.h>
|
||||
#include <imgui_impl_sdl3.h>
|
||||
#include <imgui_impl_opengl3.h>
|
||||
#include <SDL3/SDL_opengl.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Pound::GUI
|
||||
{
|
||||
|
||||
GUIManager::GUIManager() = default;
|
||||
|
||||
GUIManager::~GUIManager()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
bool GUIManager::Initialize(const std::string &title, int width, int height)
|
||||
{
|
||||
window = std::make_unique<Window>();
|
||||
|
||||
if (!window->Initialize(title, width, height))
|
||||
{
|
||||
LOG_ERROR(Render, "Failed to initialize window");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize ImGui
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||
|
||||
// Setup style
|
||||
ApplyTheme();
|
||||
|
||||
// Setup platform/renderer backends
|
||||
ImGui_ImplSDL3_InitForOpenGL(window->GetSDLWindow(), window->GetGLContext());
|
||||
ImGui_ImplOpenGL3_Init("#version 330");
|
||||
|
||||
running = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GUIManager::Shutdown()
|
||||
{
|
||||
if (!running)
|
||||
return;
|
||||
|
||||
panels.clear();
|
||||
|
||||
ImGui_ImplOpenGL3_Shutdown();
|
||||
ImGui_ImplSDL3_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
window.reset();
|
||||
running = false;
|
||||
}
|
||||
|
||||
void GUIManager::RunFrame()
|
||||
{
|
||||
if (!running)
|
||||
return;
|
||||
|
||||
window->ProcessEvents();
|
||||
|
||||
BeginFrame();
|
||||
|
||||
// Render menu bar
|
||||
RenderMainMenuBar();
|
||||
|
||||
// Render all panels
|
||||
for (auto &panel : panels)
|
||||
{
|
||||
if (panel->IsVisible())
|
||||
{
|
||||
panel->Render();
|
||||
}
|
||||
}
|
||||
|
||||
// Demo window for debugging
|
||||
if (show_demo_window)
|
||||
{
|
||||
ImGui::ShowDemoWindow(&show_demo_window);
|
||||
}
|
||||
|
||||
EndFrame();
|
||||
}
|
||||
|
||||
void GUIManager::BeginFrame()
|
||||
{
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplSDL3_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
}
|
||||
|
||||
void GUIManager::EndFrame()
|
||||
{
|
||||
ImGui::Render();
|
||||
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
|
||||
glClearColor(0.08f, 0.08f, 0.10f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
window->SwapBuffers();
|
||||
}
|
||||
|
||||
void GUIManager::RenderMainMenuBar()
|
||||
{
|
||||
if (ImGui::BeginMainMenuBar())
|
||||
{
|
||||
if (ImGui::BeginMenu("File"))
|
||||
{
|
||||
if (ImGui::MenuItem("Load ROM...", "Ctrl+O"))
|
||||
{
|
||||
// TODO: Implement ROM loading
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Exit", "Alt+F4"))
|
||||
{
|
||||
window->SetShouldClose(true);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Emulation"))
|
||||
{
|
||||
if (ImGui::MenuItem("Run CPU Test"))
|
||||
{
|
||||
if (cpu_test_callback)
|
||||
{
|
||||
cpu_test_callback();
|
||||
}
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Pause", "F5"))
|
||||
{
|
||||
// TODO: Implement pause
|
||||
}
|
||||
if (ImGui::MenuItem("Reset", "F6"))
|
||||
{
|
||||
// TODO: Implement reset
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("View"))
|
||||
{
|
||||
for (auto &panel : panels)
|
||||
{
|
||||
bool visible = panel->IsVisible();
|
||||
if (ImGui::MenuItem(panel->GetName().c_str(), nullptr, &visible))
|
||||
{
|
||||
panel->SetVisible(visible);
|
||||
}
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("ImGui Demo", nullptr, &show_demo_window))
|
||||
{
|
||||
// Toggle handled by flag
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Tools"))
|
||||
{
|
||||
if (ImGui::MenuItem("Settings", "Ctrl+,"))
|
||||
{
|
||||
// TODO: Open settings panel
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Help"))
|
||||
{
|
||||
if (ImGui::MenuItem("About"))
|
||||
{
|
||||
// TODO: Show about dialog
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
}
|
||||
|
||||
void GUIManager::ApplyTheme()
|
||||
{
|
||||
ImGuiStyle &style = ImGui::GetStyle();
|
||||
|
||||
// Modern theme with custom colors
|
||||
style.WindowRounding = 8.0f;
|
||||
style.FrameRounding = 4.0f;
|
||||
style.PopupRounding = 4.0f;
|
||||
style.ScrollbarRounding = 6.0f;
|
||||
style.GrabRounding = 4.0f;
|
||||
style.TabRounding = 4.0f;
|
||||
|
||||
style.WindowTitleAlign = ImVec2(0.5f, 0.5f);
|
||||
style.WindowMenuButtonPosition = ImGuiDir_Right;
|
||||
|
||||
// Apply custom color scheme
|
||||
style.Colors[ImGuiCol_Text] = Colors::Text;
|
||||
style.Colors[ImGuiCol_TextDisabled] = Colors::TextDisabled;
|
||||
style.Colors[ImGuiCol_WindowBg] = Colors::WithAlpha(Colors::Background, 0.95f);
|
||||
style.Colors[ImGuiCol_ChildBg] = Colors::BackgroundDark;
|
||||
style.Colors[ImGuiCol_PopupBg] = Colors::WithAlpha(Colors::Background, 0.94f);
|
||||
style.Colors[ImGuiCol_Border] = Colors::Border;
|
||||
style.Colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||
style.Colors[ImGuiCol_FrameBg] = Colors::BackgroundLight;
|
||||
style.Colors[ImGuiCol_FrameBgHovered] = Colors::Lighten(Colors::BackgroundLight, 0.1f);
|
||||
style.Colors[ImGuiCol_FrameBgActive] = Colors::Lighten(Colors::BackgroundLight, 0.2f);
|
||||
style.Colors[ImGuiCol_TitleBg] = Colors::BackgroundDark;
|
||||
style.Colors[ImGuiCol_TitleBgActive] = Colors::Background;
|
||||
style.Colors[ImGuiCol_TitleBgCollapsed] = Colors::WithAlpha(Colors::BackgroundDark, 0.51f);
|
||||
style.Colors[ImGuiCol_MenuBarBg] = Colors::BackgroundDark;
|
||||
style.Colors[ImGuiCol_ScrollbarBg] = Colors::WithAlpha(Colors::BackgroundDark, 0.53f);
|
||||
style.Colors[ImGuiCol_ScrollbarGrab] = Colors::BackgroundLight;
|
||||
style.Colors[ImGuiCol_ScrollbarGrabHovered] = Colors::Lighten(Colors::BackgroundLight, 0.1f);
|
||||
style.Colors[ImGuiCol_ScrollbarGrabActive] = Colors::Lighten(Colors::BackgroundLight, 0.2f);
|
||||
style.Colors[ImGuiCol_CheckMark] = Colors::Primary;
|
||||
style.Colors[ImGuiCol_SliderGrab] = Colors::Primary;
|
||||
style.Colors[ImGuiCol_SliderGrabActive] = Colors::PrimaryActive;
|
||||
style.Colors[ImGuiCol_Button] = Colors::WithAlpha(Colors::Primary, 0.40f);
|
||||
style.Colors[ImGuiCol_ButtonHovered] = Colors::PrimaryHover;
|
||||
style.Colors[ImGuiCol_ButtonActive] = Colors::PrimaryActive;
|
||||
style.Colors[ImGuiCol_Header] = Colors::WithAlpha(Colors::Primary, 0.31f);
|
||||
style.Colors[ImGuiCol_HeaderHovered] = Colors::WithAlpha(Colors::Primary, 0.80f);
|
||||
style.Colors[ImGuiCol_HeaderActive] = Colors::Primary;
|
||||
style.Colors[ImGuiCol_Separator] = Colors::Border;
|
||||
style.Colors[ImGuiCol_SeparatorHovered] = Colors::WithAlpha(Colors::Primary, 0.78f);
|
||||
style.Colors[ImGuiCol_SeparatorActive] = Colors::Primary;
|
||||
style.Colors[ImGuiCol_ResizeGrip] = Colors::WithAlpha(Colors::Primary, 0.25f);
|
||||
style.Colors[ImGuiCol_ResizeGripHovered] = Colors::WithAlpha(Colors::Primary, 0.67f);
|
||||
style.Colors[ImGuiCol_ResizeGripActive] = Colors::WithAlpha(Colors::Primary, 0.95f);
|
||||
style.Colors[ImGuiCol_Tab] = Colors::BackgroundLight;
|
||||
style.Colors[ImGuiCol_TabHovered] = Colors::WithAlpha(Colors::Primary, 0.80f);
|
||||
style.Colors[ImGuiCol_TabActive] = Colors::Primary;
|
||||
style.Colors[ImGuiCol_TabUnfocused] = Colors::Background;
|
||||
style.Colors[ImGuiCol_TabUnfocusedActive] = Colors::Lighten(Colors::Background, 0.1f);
|
||||
style.Colors[ImGuiCol_PlotLines] = Colors::Primary;
|
||||
style.Colors[ImGuiCol_PlotLinesHovered] = Colors::PrimaryHover;
|
||||
style.Colors[ImGuiCol_PlotHistogram] = Colors::Secondary;
|
||||
style.Colors[ImGuiCol_PlotHistogramHovered] = Colors::SecondaryHover;
|
||||
style.Colors[ImGuiCol_TextSelectedBg] = Colors::WithAlpha(Colors::Primary, 0.35f);
|
||||
style.Colors[ImGuiCol_DragDropTarget] = Colors::WithAlpha(Colors::Secondary, 0.90f);
|
||||
style.Colors[ImGuiCol_NavHighlight] = Colors::Primary;
|
||||
style.Colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
|
||||
style.Colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
|
||||
style.Colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
|
||||
}
|
||||
|
||||
void GUIManager::AddPanel(std::shared_ptr<Panel> panel)
|
||||
{
|
||||
panels.push_back(panel);
|
||||
}
|
||||
|
||||
void GUIManager::RemovePanel(const std::string &name)
|
||||
{
|
||||
panels.erase(
|
||||
std::remove_if(panels.begin(), panels.end(),
|
||||
[&name](const std::shared_ptr<Panel> &panel)
|
||||
{
|
||||
return panel->GetName() == name;
|
||||
}),
|
||||
panels.end());
|
||||
}
|
||||
}
|
||||
44
gui/GUIManager.h
Normal file
44
gui/GUIManager.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2025 Pound Emulator Project. All rights reserved.
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include "Window.h"
|
||||
#include "Panel.h"
|
||||
|
||||
namespace Pound::GUI {
|
||||
|
||||
class GUIManager {
|
||||
public:
|
||||
GUIManager();
|
||||
~GUIManager();
|
||||
|
||||
bool Initialize(const std::string& title, int width, int height);
|
||||
void Shutdown();
|
||||
|
||||
void RunFrame();
|
||||
bool IsRunning() const { return running && window && !window->ShouldClose(); }
|
||||
|
||||
void AddPanel(std::shared_ptr<Panel> panel);
|
||||
void RemovePanel(const std::string& name);
|
||||
|
||||
// Callback for external systems
|
||||
void SetCPUTestCallback(std::function<void()> callback) { cpu_test_callback = callback; }
|
||||
|
||||
private:
|
||||
void BeginFrame();
|
||||
void EndFrame();
|
||||
void RenderMainMenuBar();
|
||||
void ApplyTheme();
|
||||
|
||||
std::unique_ptr<Window> window;
|
||||
std::vector<std::shared_ptr<Panel>> panels;
|
||||
bool running = false;
|
||||
bool show_demo_window = false;
|
||||
|
||||
// Callbacks
|
||||
std::function<void()> cpu_test_callback;
|
||||
};
|
||||
|
||||
} // namespace Pound::GUI
|
||||
27
gui/Panel.h
Normal file
27
gui/Panel.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2025 Pound Emulator Project. All rights reserved.
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <imgui.h>
|
||||
|
||||
namespace Pound::GUI
|
||||
{
|
||||
|
||||
class Panel
|
||||
{
|
||||
public:
|
||||
Panel(const std::string &name) : name(name) {}
|
||||
virtual ~Panel() = default;
|
||||
|
||||
virtual void Render() = 0;
|
||||
|
||||
const std::string &GetName() const { return name; }
|
||||
bool IsVisible() const { return visible; }
|
||||
void SetVisible(bool vis) { visible = vis; }
|
||||
|
||||
protected:
|
||||
std::string name;
|
||||
bool visible = true;
|
||||
};
|
||||
|
||||
} // namespace Pound::GUI
|
||||
97
gui/Window.cpp
Normal file
97
gui/Window.cpp
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright 2025 Pound Emulator Project. All rights reserved.
|
||||
|
||||
#include "Window.h"
|
||||
#include "Base/Logging/Log.h"
|
||||
#include <imgui.h>
|
||||
#include <imgui_impl_sdl3.h>
|
||||
#include <imgui_impl_opengl3.h>
|
||||
|
||||
namespace Pound::GUI
|
||||
{
|
||||
|
||||
Window::Window() = default;
|
||||
|
||||
Window::~Window()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
bool Window::Initialize(const std::string &title, int width, int height)
|
||||
{
|
||||
if (!SDL_Init(SDL_INIT_VIDEO))
|
||||
{
|
||||
LOG_ERROR(Render, "Error while creating SDL3 Context!");
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_PropertiesID props = SDL_CreateProperties();
|
||||
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, title.c_str());
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_CENTERED);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_CENTERED);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, width);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, height);
|
||||
SDL_SetNumberProperty(props, "flags", SDL_WINDOW_OPENGL);
|
||||
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN, true);
|
||||
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true);
|
||||
|
||||
window = SDL_CreateWindowWithProperties(props);
|
||||
SDL_DestroyProperties(props);
|
||||
|
||||
if (!window)
|
||||
{
|
||||
LOG_ERROR(Render, "Failed to create SDL window: {}", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_SetWindowMinimumSize(window, 640, 480);
|
||||
|
||||
gl_context = SDL_GL_CreateContext(window);
|
||||
if (!gl_context)
|
||||
{
|
||||
LOG_ERROR(Render, "Failed to create OpenGL context: {}", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_GL_MakeCurrent(window, gl_context);
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Window::Shutdown()
|
||||
{
|
||||
if (gl_context)
|
||||
{
|
||||
SDL_GL_DestroyContext(gl_context);
|
||||
gl_context = nullptr;
|
||||
}
|
||||
|
||||
if (window)
|
||||
{
|
||||
SDL_DestroyWindow(window);
|
||||
window = nullptr;
|
||||
}
|
||||
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void Window::ProcessEvents()
|
||||
{
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
ImGui_ImplSDL3_ProcessEvent(&event);
|
||||
|
||||
if (event.type == SDL_EVENT_QUIT)
|
||||
{
|
||||
should_close = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Window::SwapBuffers()
|
||||
{
|
||||
SDL_GL_SwapWindow(window);
|
||||
}
|
||||
|
||||
} // namespace Pound::GUI
|
||||
36
gui/Window.h
Normal file
36
gui/Window.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2025 Pound Emulator Project. All rights reserved.
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_opengl.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace Pound::GUI
|
||||
{
|
||||
|
||||
class Window
|
||||
{
|
||||
public:
|
||||
Window();
|
||||
~Window();
|
||||
|
||||
bool Initialize(const std::string &title, int width, int height);
|
||||
void Shutdown();
|
||||
|
||||
SDL_Window *GetSDLWindow() const { return window; }
|
||||
SDL_GLContext GetGLContext() const { return gl_context; }
|
||||
|
||||
bool ShouldClose() const { return should_close; }
|
||||
void SetShouldClose(bool close) { should_close = close; }
|
||||
|
||||
void ProcessEvents();
|
||||
void SwapBuffers();
|
||||
|
||||
private:
|
||||
SDL_Window *window = nullptr;
|
||||
SDL_GLContext gl_context = nullptr;
|
||||
bool should_close = false;
|
||||
};
|
||||
|
||||
} // namespace Pound::GUI
|
||||
143
gui/panels/CPUPanel.cpp
Normal file
143
gui/panels/CPUPanel.cpp
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
// Copyright 2025 Pound Emulator Project. All rights reserved.
|
||||
|
||||
#include "CPUPanel.h"
|
||||
#include "../Colors.h"
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
namespace Pound::GUI {
|
||||
|
||||
CPUPanel::CPUPanel() : Panel("CPU Debug") {}
|
||||
|
||||
void CPUPanel::Render() {
|
||||
if (!ImGui::Begin(name.c_str(), &visible, ImGuiWindowFlags_NoCollapse)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
// Control buttons with custom colors
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, Colors::WithAlpha(Colors::Primary, 0.40f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, Colors::PrimaryHover);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, Colors::PrimaryActive);
|
||||
|
||||
if (ImGui::Button("Run CPU Test", ImVec2(120, 0))) {
|
||||
if (cpu_test_callback) {
|
||||
cpu_test_callback();
|
||||
show_test_result = true;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Step", ImVec2(60, 0))) {
|
||||
// TODO: Implement step functionality
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
// Reset button with secondary color
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, Colors::WithAlpha(Colors::Secondary, 0.40f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, Colors::SecondaryHover);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, Colors::SecondaryActive);
|
||||
|
||||
if (ImGui::Button("Reset", ImVec2(60, 0))) {
|
||||
// TODO: Implement reset functionality
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor(6); // Pop all 6 color changes
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// Tabs for different views
|
||||
if (ImGui::BeginTabBar("CPUTabBar")) {
|
||||
if (ImGui::BeginTabItem("Registers")) {
|
||||
// General purpose registers
|
||||
ImGui::Text("General Purpose Registers:");
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::BeginTable("RegisterTable", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("Register", ImGuiTableColumnFlags_WidthFixed, 60.0f);
|
||||
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, 120.0f);
|
||||
ImGui::TableSetupColumn("Register", ImGuiTableColumnFlags_WidthFixed, 60.0f);
|
||||
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, 120.0f);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
ImGui::TableNextRow();
|
||||
|
||||
// Left column
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::Text("X%d", i);
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::Text("0x%016llX", cpu_state.registers[i]);
|
||||
|
||||
// Right column
|
||||
if (i + 16 < 32) {
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
ImGui::Text("X%d", i + 16);
|
||||
ImGui::TableSetColumnIndex(3);
|
||||
ImGui::Text("0x%016llX", cpu_state.registers[i + 16]);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Text("Program Counter: 0x%016llX", cpu_state.pc);
|
||||
ImGui::Text("Flags: 0x%08X", cpu_state.flags);
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Memory")) {
|
||||
static char addr_input[17] = "0000000000000000";
|
||||
ImGui::Text("Memory Viewer");
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::InputText("Address", addr_input, sizeof(addr_input),
|
||||
ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase);
|
||||
|
||||
// TODO: Implement memory viewer
|
||||
ImGui::Text("Memory viewer will be implemented here");
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTabItem("Disassembly")) {
|
||||
ImGui::Text("Disassembly View");
|
||||
ImGui::Separator();
|
||||
|
||||
// TODO: Implement disassembly view
|
||||
ImGui::Text("Disassembly will be shown here");
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
// Test result popup
|
||||
if (show_test_result) {
|
||||
ImGui::OpenPopup("CPU Test Result");
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopupModal("CPU Test Result", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("The CPU test has been executed successfully!");
|
||||
ImGui::Text("Check the console for detailed output.");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Note: Pound is still in pre-alpha state.");
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
if (ImGui::Button("OK", ImVec2(120, 0))) {
|
||||
show_test_result = false;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
} // namespace Pound::GUI
|
||||
32
gui/panels/CPUPanel.h
Normal file
32
gui/panels/CPUPanel.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright 2025 Pound Emulator Project. All rights reserved.
|
||||
#pragma once
|
||||
|
||||
#include "../Panel.h"
|
||||
#include <functional>
|
||||
|
||||
namespace Pound::GUI
|
||||
{
|
||||
|
||||
class CPUPanel : public Panel
|
||||
{
|
||||
public:
|
||||
CPUPanel();
|
||||
|
||||
void Render() override;
|
||||
void SetCPUTestCallback(std::function<void()> callback) { cpu_test_callback = callback; }
|
||||
void ShowTestResult(bool show = true) { show_test_result = show; }
|
||||
|
||||
private:
|
||||
std::function<void()> cpu_test_callback;
|
||||
bool show_test_result = false;
|
||||
|
||||
// CPU state display (placeholder for future integration)
|
||||
struct CPUState
|
||||
{
|
||||
uint64_t registers[32] = {0};
|
||||
uint64_t pc = 0;
|
||||
uint32_t flags = 0;
|
||||
} cpu_state;
|
||||
};
|
||||
|
||||
} // namespace Pound::GUI
|
||||
110
gui/panels/ConsolePanel.cpp
Normal file
110
gui/panels/ConsolePanel.cpp
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
// Copyright 2025 Pound Emulator Project. All rights reserved.
|
||||
|
||||
#include "ConsolePanel.h"
|
||||
#include "../Colors.h"
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
namespace Pound::GUI
|
||||
{
|
||||
|
||||
ConsolePanel::ConsolePanel() : Panel("Console") {}
|
||||
|
||||
void ConsolePanel::Render()
|
||||
{
|
||||
if (!ImGui::Begin(name.c_str(), &visible))
|
||||
{
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
// Options
|
||||
if (ImGui::BeginPopup("Options"))
|
||||
{
|
||||
ImGui::Checkbox("Auto-scroll", &auto_scroll);
|
||||
ImGui::Checkbox("Show timestamps", &show_timestamps);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
// Buttons
|
||||
if (ImGui::Button("Options"))
|
||||
ImGui::OpenPopup("Options");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Clear"))
|
||||
Clear();
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Log entries: %zu", log_buffer.size());
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// Log display
|
||||
ImGui::BeginChild("ScrollingRegion", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||
|
||||
for (const auto &entry : log_buffer)
|
||||
{
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, entry.color);
|
||||
ImGui::TextUnformatted(entry.text.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
if (auto_scroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
|
||||
ImGui::SetScrollHereY(1.0f);
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void ConsolePanel::AddLog(const std::string &text)
|
||||
{
|
||||
std::string final_text = text;
|
||||
|
||||
if (show_timestamps)
|
||||
{
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto time_t = std::chrono::system_clock::to_time_t(now);
|
||||
std::stringstream ss;
|
||||
ss << "[" << std::put_time(std::localtime(&time_t), "%H:%M:%S") << "] ";
|
||||
final_text = ss.str() + text;
|
||||
}
|
||||
|
||||
log_buffer.push_back({final_text, GetLogColor(text)});
|
||||
|
||||
// Keep buffer size limited
|
||||
while (log_buffer.size() > MAX_LOG_ENTRIES)
|
||||
{
|
||||
log_buffer.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void ConsolePanel::Clear()
|
||||
{
|
||||
log_buffer.clear();
|
||||
}
|
||||
|
||||
ImVec4 ConsolePanel::GetLogColor(const std::string &text) const
|
||||
{
|
||||
if (text.find("[ERROR]") != std::string::npos)
|
||||
{
|
||||
return Colors::Error;
|
||||
}
|
||||
else if (text.find("[WARN]") != std::string::npos)
|
||||
{
|
||||
return Colors::Warning;
|
||||
}
|
||||
else if (text.find("[INFO]") != std::string::npos)
|
||||
{
|
||||
return Colors::Info;
|
||||
}
|
||||
else if (text.find("[DEBUG]") != std::string::npos)
|
||||
{
|
||||
return Colors::TextDisabled;
|
||||
}
|
||||
return Colors::Text;
|
||||
}
|
||||
}
|
||||
36
gui/panels/ConsolePanel.h
Normal file
36
gui/panels/ConsolePanel.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2025 Pound Emulator Project. All rights reserved.
|
||||
#pragma once
|
||||
|
||||
#include "../Panel.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <deque>
|
||||
|
||||
namespace Pound::GUI
|
||||
{
|
||||
|
||||
class ConsolePanel : public Panel
|
||||
{
|
||||
public:
|
||||
ConsolePanel();
|
||||
|
||||
void Render() override;
|
||||
void AddLog(const std::string &text);
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
struct LogEntry
|
||||
{
|
||||
std::string text;
|
||||
ImVec4 color;
|
||||
};
|
||||
|
||||
std::deque<LogEntry> log_buffer;
|
||||
bool auto_scroll = true;
|
||||
bool show_timestamps = true;
|
||||
static constexpr size_t MAX_LOG_ENTRIES = 1000;
|
||||
|
||||
ImVec4 GetLogColor(const std::string &text) const;
|
||||
};
|
||||
|
||||
} // namespace Pound::GUI
|
||||
100
gui/panels/PerformancePanel.cpp
Normal file
100
gui/panels/PerformancePanel.cpp
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
// Copyright 2025 Pound Emulator Project. All rights reserved.
|
||||
|
||||
#include "PerformancePanel.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace Pound::GUI
|
||||
{
|
||||
|
||||
PerformancePanel::PerformancePanel() : Panel("Performance")
|
||||
{
|
||||
last_update = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
void PerformancePanel::Render()
|
||||
{
|
||||
if (!ImGui::Begin(name.c_str(), &visible))
|
||||
{
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
Update();
|
||||
|
||||
// Performance metrics
|
||||
ImGui::Text("FPS: %.1f", current_data.fps);
|
||||
ImGui::Text("Frame Time: %.2f ms", current_data.frame_time);
|
||||
ImGui::Separator();
|
||||
|
||||
// FPS Graph
|
||||
if (!fps_history.empty())
|
||||
{
|
||||
float fps_array[HISTORY_SIZE];
|
||||
std::copy(fps_history.begin(), fps_history.end(), fps_array);
|
||||
|
||||
ImGui::Text("FPS History:");
|
||||
ImGui::PlotLines("##FPS", fps_array, (int)fps_history.size(), 0, nullptr,
|
||||
0.0f, 144.0f, ImVec2(0, 80));
|
||||
}
|
||||
|
||||
// Frame Time Graph
|
||||
if (!frame_time_history.empty())
|
||||
{
|
||||
float frame_time_array[HISTORY_SIZE];
|
||||
std::copy(frame_time_history.begin(), frame_time_history.end(), frame_time_array);
|
||||
|
||||
ImGui::Text("Frame Time History (ms):");
|
||||
ImGui::PlotLines("##FrameTime", frame_time_array, (int)frame_time_history.size(), 0, nullptr,
|
||||
0.0f, 33.33f, ImVec2(0, 80));
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// System info (placeholder)
|
||||
ImGui::Text("CPU Usage: %.1f%%", current_data.cpu_usage);
|
||||
ImGui::Text("Memory Usage: %.1f MB", current_data.memory_usage);
|
||||
|
||||
// Emulation stats
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Emulation Statistics:");
|
||||
ImGui::Text("Instructions/sec: N/A");
|
||||
ImGui::Text("JIT Cache Usage: N/A");
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void PerformancePanel::Update()
|
||||
{
|
||||
frame_count++;
|
||||
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update);
|
||||
|
||||
if (duration.count() >= 100)
|
||||
{ // Update every 100ms
|
||||
current_data.fps = frame_count * 1000.0f / duration.count();
|
||||
current_data.frame_time = duration.count() / (float)frame_count;
|
||||
|
||||
fps_history.push_back(current_data.fps);
|
||||
frame_time_history.push_back(current_data.frame_time);
|
||||
|
||||
// Keep history size limited
|
||||
while (fps_history.size() > HISTORY_SIZE)
|
||||
{
|
||||
fps_history.pop_front();
|
||||
}
|
||||
while (frame_time_history.size() > HISTORY_SIZE)
|
||||
{
|
||||
frame_time_history.pop_front();
|
||||
}
|
||||
|
||||
frame_count = 0;
|
||||
last_update = now;
|
||||
|
||||
// TODO: Get actual CPU and memory usage
|
||||
current_data.cpu_usage = 0.0f;
|
||||
current_data.memory_usage = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Pound::GUI
|
||||
37
gui/panels/PerformancePanel.h
Normal file
37
gui/panels/PerformancePanel.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2025 Pound Emulator Project. All rights reserved.
|
||||
#pragma once
|
||||
|
||||
#include "../Panel.h"
|
||||
#include <deque>
|
||||
#include <chrono>
|
||||
|
||||
namespace Pound::GUI
|
||||
{
|
||||
|
||||
class PerformancePanel : public Panel
|
||||
{
|
||||
public:
|
||||
PerformancePanel();
|
||||
|
||||
void Render() override;
|
||||
void Update();
|
||||
|
||||
private:
|
||||
struct PerformanceData
|
||||
{
|
||||
float fps = 0.0f;
|
||||
float frame_time = 0.0f;
|
||||
float cpu_usage = 0.0f;
|
||||
float memory_usage = 0.0f;
|
||||
};
|
||||
|
||||
PerformanceData current_data;
|
||||
std::deque<float> fps_history;
|
||||
std::deque<float> frame_time_history;
|
||||
static constexpr size_t HISTORY_SIZE = 120;
|
||||
|
||||
std::chrono::steady_clock::time_point last_update;
|
||||
int frame_count = 0;
|
||||
};
|
||||
|
||||
} // namespace Pound::GUI
|
||||
Loading…
Add table
Add a link
Reference in a new issue