Add VM console output !
Some checks are pending
Build-StoryEditor / build_linux (push) Waiting to run
Build-StoryEditor / build_win32 (push) Waiting to run
Deploy-Documentation / deploy (push) Waiting to run

This commit is contained in:
anthony@rabine.fr 2025-10-03 18:12:29 +02:00
parent 07f4288748
commit 88b9bc6b3e
5 changed files with 157 additions and 41 deletions

View file

@ -829,16 +829,22 @@ uint8_t AppController::Syscall(chip32_ctx_t *ctx, uint8_t code)
{ {
std::string text = GetStringFromMemory(ctx->registers[R0]); std::string text = GetStringFromMemory(ctx->registers[R0]);
int arg_count = ctx->registers[R1]; int arg_count = ctx->registers[R1];
char working_buf[200] = {0}; char working_buf[400] = {0};
// Simplified printf logic for logging // Simplified printf logic for logging
switch(arg_count){ switch(arg_count){
case 0: m_logger.Log(text); break; case 0: strcpy(working_buf, text.c_str()); break;
case 1: snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2]); m_logger.Log(working_buf); break; case 1: snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2]); break;
case 2: snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2], ctx->registers[R3]); m_logger.Log(working_buf); break; case 2: snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2], ctx->registers[R3]); break;
case 3: snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2], ctx->registers[R3], ctx->registers[R4]); m_logger.Log(working_buf); break; case 3: snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2], ctx->registers[R3], ctx->registers[R4]); break;
default: m_logger.Log("Printf with unsupported arg_count: " + std::to_string(arg_count) + " text: " + text, true); break; default: m_logger.Log("Printf with unsupported arg_count: " + std::to_string(arg_count) + " text: " + text, true); break;
} }
// Send event to UI
auto evObj = std::make_shared<VmStateEvent>();
evObj->type = VmStateEvent::Type::PrintEvent;
evObj->printOutput = working_buf;
m_eventBus.Emit(evObj);
} }
else if (code == 5) // WAIT (sleep) else if (code == 5) // WAIT (sleep)
{ {

View file

@ -7,32 +7,35 @@ EmulatorDock::EmulatorDock(IStoryManager &proj)
: WindowBase("Emulator") : WindowBase("Emulator")
, m_story(proj) , m_story(proj)
{ {
} }
void EmulatorDock::Initialize() void EmulatorDock::Initialize()
{ {
}
void EmulatorDock::AddVmOutput(const std::string& text)
{
std::scoped_lock<std::mutex> lock(m_vmOutputMutex);
m_vmOutput.push_back({text, 0});
// Limiter à 1000 lignes
if (m_vmOutput.size() > 1000) {
m_vmOutput.erase(m_vmOutput.begin());
}
}
void EmulatorDock::ClearVmOutput()
{
std::scoped_lock<std::mutex> lock(m_vmOutputMutex);
m_vmOutput.clear();
} }
void EmulatorDock::Draw() void EmulatorDock::Draw()
{ {
// if (!IsVisible())
// {
// return;
// }
static ImVec2 size(320.0f, 240.0f);
WindowBase::BeginDraw(); WindowBase::BeginDraw();
ImGui::SetWindowSize(ImVec2(626, 744), ImGuiCond_FirstUseEver); ImGui::SetWindowSize(ImVec2(626, 744), ImGuiCond_FirstUseEver);
// ImGui::Image((void*)(intptr_t)my_image_texture, ImVec2(313, 367)); // ===== ÉCRAN DE L'ÉMULATEUR =====
// float sz = ImGui::GetTextLineHeight();
ImVec2 p = ImGui::GetCursorScreenPos(); ImVec2 p = ImGui::GetCursorScreenPos();
if (m_image.Valid()) if (m_image.Valid())
@ -41,44 +44,59 @@ void EmulatorDock::Draw()
} }
else else
{ {
ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + 320, p.y + 240), ImGui::GetColorU32(ImVec4(1.0, 1.0, 1.0, 1.0))); ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x + 320, p.y + 240),
ImGui::GetColorU32(ImVec4(0.1f, 0.1f, 0.1f, 1.0f)));
// Texte centré "No Image"
ImGui::SetCursorScreenPos(ImVec2(p.x + 110, p.y + 110));
ImGui::TextDisabled("No Image");
} }
ImGui::SetCursorScreenPos(ImVec2(p.x, p.y + 244)); ImGui::SetCursorScreenPos(ImVec2(p.x, p.y + 244));
// ===== BOUTONS DE CONTRÔLE =====
float old_size = ImGui::GetFont()->Scale; float old_size = ImGui::GetFont()->Scale;
ImGui::GetFont()->Scale *= 2; ImGui::GetFont()->Scale *= 2;
ImGui::PushFont(ImGui::GetFont()); ImGui::PushFont(ImGui::GetFont());
if (ImGui::Button(ICON_MDI_CHECK_CIRCLE_OUTLINE, ImVec2(50, 50))) if (ImGui::Button(ICON_MDI_CHECK_CIRCLE_OUTLINE, ImVec2(50, 50)))
{ {
m_story.Ok(); m_story.Ok();
} }
if (ImGui::IsItemHovered()) ImGui::SetTooltip("OK");
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button(ICON_MDI_PAUSE, ImVec2(50, 50))) if (ImGui::Button(ICON_MDI_PAUSE, ImVec2(50, 50)))
{ {
m_story.Pause(); m_story.Pause();
} }
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Pause");
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button(ICON_MDI_HOME, ImVec2(50, 50))) if (ImGui::Button(ICON_MDI_HOME, ImVec2(50, 50)))
{ {
m_story.Home(); m_story.Home();
} }
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Home");
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button(ICON_MDI_ARROW_LEFT_BOLD_CIRCLE_OUTLINE, ImVec2(50, 50))) if (ImGui::Button(ICON_MDI_ARROW_LEFT_BOLD_CIRCLE_OUTLINE, ImVec2(50, 50)))
{ {
m_story.Previous(); m_story.Previous();
} }
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Previous");
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button(ICON_MDI_ARROW_RIGHT_BOLD_CIRCLE_OUTLINE, ImVec2(50, 50))) if (ImGui::Button(ICON_MDI_ARROW_RIGHT_BOLD_CIRCLE_OUTLINE, ImVec2(50, 50)))
{ {
m_story.Next(); m_story.Next();
} }
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Next");
ImGui::GetFont()->Scale = old_size; ImGui::GetFont()->Scale = old_size;
ImGui::PopFont(); ImGui::PopFont();
// ===== CONTRÔLES DE SCRIPT ET DEBUG =====
ImGui::SeparatorText("Script control and debug"); ImGui::SeparatorText("Script control and debug");
if (ImGui::Button("Build nodes")) if (ImGui::Button("Build nodes"))
@ -96,19 +114,16 @@ void EmulatorDock::Draw()
m_story.Log("Play story"); m_story.Log("Play story");
m_story.Play(); m_story.Play();
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Stop")) if (ImGui::Button("Stop"))
{ {
m_story.Log("Stop story"); m_story.Log("Stop story");
m_story.Stop(); m_story.Stop();
} }
ImGui::SameLine(); ImGui::SameLine();
ImGui::Text("VM state: %s", m_story.VmState().c_str()); ImGui::Text("VM state: %s", m_story.VmState().c_str());
// ===== BOUTONS DE CHARGEMENT =====
if (ImGui::Button("Load binary story (.c32)")) if (ImGui::Button("Load binary story (.c32)"))
{ {
IGFD::FileDialogConfig config; IGFD::FileDialogConfig config;
@ -130,46 +145,108 @@ void EmulatorDock::Draw()
config.countSelectionMax = 1; config.countSelectionMax = 1;
config.flags = ImGuiFileDialogFlags_Modal; config.flags = ImGuiFileDialogFlags_Modal;
ImGuiFileDialog::Instance()->OpenDialog("SetSourceScriptDlgKey", ImGuiFileDialog::Instance()->OpenDialog("SetSourceScriptDlgKey",
"Choose a binary story", "Choose a source file",
".chip32", ".chip32",
config config
); );
} }
// ---------------- Load Binary story // ===== DIALOGS =====
// Load Binary story
if (ImGuiFileDialog::Instance()->Display("LoadBinaryStoryDlgKey")) if (ImGuiFileDialog::Instance()->Display("LoadBinaryStoryDlgKey"))
{ {
if (ImGuiFileDialog::Instance()->IsOk()) if (ImGuiFileDialog::Instance()->IsOk())
{ {
std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName(); std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
std::string filePath = ImGuiFileDialog::Instance()->GetCurrentPath();
std::string filter = ImGuiFileDialog::Instance()->GetCurrentFilter();
m_story.LoadBinaryStory(filePathName); m_story.LoadBinaryStory(filePathName);
} }
// close
ImGuiFileDialog::Instance()->Close(); ImGuiFileDialog::Instance()->Close();
} }
// ---------------- External source file // External source file
if (ImGuiFileDialog::Instance()->Display("SetSourceScriptDlgKey")) if (ImGuiFileDialog::Instance()->Display("SetSourceScriptDlgKey"))
{ {
if (ImGuiFileDialog::Instance()->IsOk()) if (ImGuiFileDialog::Instance()->IsOk())
{ {
std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName(); std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
std::string filePath = ImGuiFileDialog::Instance()->GetCurrentPath();
std::string filter = ImGuiFileDialog::Instance()->GetCurrentFilter();
m_story.SetExternalSourceFile(filePathName); m_story.SetExternalSourceFile(filePathName);
} }
// close
ImGuiFileDialog::Instance()->Close(); ImGuiFileDialog::Instance()->Close();
} }
// ===== CONSOLE VM (NOUVEAU) =====
DrawVmConsole();
WindowBase::EndDraw(); WindowBase::EndDraw();
} }
void EmulatorDock::DrawVmConsole()
{
ImGui::Separator();
// Header de la console
ImGui::Text(ICON_MDI_CONSOLE " VM Output");
ImGui::SameLine();
ImGui::Checkbox("Auto-scroll##vm", &m_autoScrollVm);
ImGui::SameLine();
if (ImGui::SmallButton("Clear##vm"))
{
ClearVmOutput();
}
ImGui::Separator();
// Zone de console scrollable
ImGui::BeginChild("VmConsoleRegion", ImVec2(0, 150), true,
ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysVerticalScrollbar);
{
std::scoped_lock<std::mutex> lock(m_vmOutputMutex);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 2));
ImGui::PushFont(ImGui::GetFont()); // Police monospace si disponible
if (m_vmOutput.empty())
{
ImGui::TextDisabled("(No VM output yet - run the program to see print statements)");
}
else
{
for (const auto& entry : m_vmOutput)
{
// Colorer les erreurs en rouge
if (entry.type == 1) {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.4f, 0.4f, 1.0f));
}
ImGui::TextUnformatted(entry.text.c_str());
if (entry.type == 1) {
ImGui::PopStyleColor();
}
}
}
ImGui::PopFont();
ImGui::PopStyleVar();
}
// Auto-scroll intelligent (comme la console principale)
float scrollY = ImGui::GetScrollY();
float scrollMaxY = ImGui::GetScrollMaxY();
const float BOTTOM_THRESHOLD = 5.0f;
bool wasAtBottom = (scrollMaxY == 0) || (scrollY >= scrollMaxY - BOTTOM_THRESHOLD);
if (m_autoScrollVm && wasAtBottom)
{
ImGui::SetScrollHereY(1.0f);
}
ImGui::EndChild();
}
void EmulatorDock::ClearImage() void EmulatorDock::ClearImage()
{ {
m_image.Clear(); m_image.Clear();
@ -179,4 +256,4 @@ void EmulatorDock::SetImage(const std::string &image)
{ {
m_imageFileName = image; m_imageFileName = image;
m_image.Load(m_story.BuildFullAssetsPath(image)); m_image.Load(m_story.BuildFullAssetsPath(image));
} }

View file

@ -3,6 +3,9 @@
#include "window_base.h" #include "window_base.h"
#include "i_story_manager.h" #include "i_story_manager.h"
#include "gui.h" #include "gui.h"
#include <vector>
#include <string>
#include <mutex>
class EmulatorDock : public WindowBase class EmulatorDock : public WindowBase
{ {
@ -14,10 +17,25 @@ public:
void ClearImage(); void ClearImage();
void SetImage(const std::string &image); void SetImage(const std::string &image);
// NEW: Console VM pour afficher la sortie des prints
void AddVmOutput(const std::string& text);
void ClearVmOutput();
private: private:
IStoryManager &m_story; IStoryManager &m_story;
Gui::Image m_image; Gui::Image m_image;
std::string m_imageFileName; std::string m_imageFileName;
};
// NEW: Buffer pour la console VM
struct VmOutputEntry {
std::string text;
uint32_t type; // 0 = normal, 1 = error
};
std::vector<VmOutputEntry> m_vmOutput;
std::mutex m_vmOutputMutex;
bool m_autoScrollVm = true;
void DrawVmConsole();
};

View file

@ -13,11 +13,15 @@ struct VmStateEvent : public Event
int currentLine; int currentLine;
chip32_result_t vmResult; chip32_result_t vmResult;
std::set<int> breakpoints; std::set<int> breakpoints;
std::string printOutput;
// Ajoutez d'autres données nécessaires, ex: std::vector<std::shared_ptr<Variable>> variables; // Ajoutez d'autres données nécessaires, ex: std::vector<std::shared_ptr<Variable>> variables;
// Constructeur pour faciliter la création de l'événement enum class Type
VmStateEvent(const chip32_ctx_t& ctx, int line, chip32_result_t result, const std::set<int>& bps) {
: vmContext(ctx), currentLine(line), vmResult(result), breakpoints(bps) {} PrintEvent,
};
Type type;
}; };
class OpenProjectEvent : public Event class OpenProjectEvent : public Event

View file

@ -83,6 +83,15 @@ MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appCo
OpenFunction(event.GetUuid(), event.GetName()); OpenFunction(event.GetUuid(), event.GetName());
}); });
m_eventBus.Subscribe<VmStateEvent>([this](const VmStateEvent &event) {
if (event.type == VmStateEvent::Type::PrintEvent)
{
m_logger.Log("Print from VM: " + event.printOutput);
m_emulatorDock.AddVmOutput(event.printOutput);
}
});
m_eventBus.Subscribe<GenericResultEvent>([this](const GenericResultEvent &event) { m_eventBus.Subscribe<GenericResultEvent>([this](const GenericResultEvent &event) {
if (event.IsSuccess()) { if (event.IsSuccess()) {
@ -102,6 +111,8 @@ MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appCo
} else if (event.GetType() == ModuleEvent::Type::BuildSuccess) { } else if (event.GetType() == ModuleEvent::Type::BuildSuccess) {
m_toastNotifier.addToast("Module", "Module built successfully! Binary ready for testing.", ToastType::Success); m_toastNotifier.addToast("Module", "Module built successfully! Binary ready for testing.", ToastType::Success);
m_debuggerWindow.SetScript(m_appController.GetModuleAssembly()); m_debuggerWindow.SetScript(m_appController.GetModuleAssembly());
m_emulatorDock.ClearVmOutput();
// Show success message if no errors // Show success message if no errors
if (!m_errorListDock.HasErrors()) { if (!m_errorListDock.HasErrors()) {