diff --git a/story-editor/src/app/app_controller.cpp b/story-editor/src/app/app_controller.cpp index fb2c607..5679db8 100644 --- a/story-editor/src/app/app_controller.cpp +++ b/story-editor/src/app/app_controller.cpp @@ -829,16 +829,22 @@ uint8_t AppController::Syscall(chip32_ctx_t *ctx, uint8_t code) { std::string text = GetStringFromMemory(ctx->registers[R0]); int arg_count = ctx->registers[R1]; - char working_buf[200] = {0}; + char working_buf[400] = {0}; // Simplified printf logic for logging switch(arg_count){ - case 0: m_logger.Log(text); break; - case 1: snprintf(working_buf, sizeof(working_buf), text.c_str(), ctx->registers[R2]); m_logger.Log(working_buf); 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 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 0: strcpy(working_buf, text.c_str()); 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]); 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; } + + // Send event to UI + auto evObj = std::make_shared(); + evObj->type = VmStateEvent::Type::PrintEvent; + evObj->printOutput = working_buf; + m_eventBus.Emit(evObj); } else if (code == 5) // WAIT (sleep) { diff --git a/story-editor/src/docks/emulator_dock.cpp b/story-editor/src/docks/emulator_dock.cpp index 631243e..94696fb 100644 --- a/story-editor/src/docks/emulator_dock.cpp +++ b/story-editor/src/docks/emulator_dock.cpp @@ -7,32 +7,35 @@ EmulatorDock::EmulatorDock(IStoryManager &proj) : WindowBase("Emulator") , m_story(proj) { - } void EmulatorDock::Initialize() { +} +void EmulatorDock::AddVmOutput(const std::string& text) +{ + std::scoped_lock 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 lock(m_vmOutputMutex); + m_vmOutput.clear(); } void EmulatorDock::Draw() { -// if (!IsVisible()) -// { -// return; -// } - - static ImVec2 size(320.0f, 240.0f); - WindowBase::BeginDraw(); ImGui::SetWindowSize(ImVec2(626, 744), ImGuiCond_FirstUseEver); -// ImGui::Image((void*)(intptr_t)my_image_texture, ImVec2(313, 367)); - -// float sz = ImGui::GetTextLineHeight(); - + // ===== ÉCRAN DE L'ÉMULATEUR ===== ImVec2 p = ImGui::GetCursorScreenPos(); if (m_image.Valid()) @@ -41,44 +44,59 @@ void EmulatorDock::Draw() } 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)); + // ===== BOUTONS DE CONTRÔLE ===== float old_size = ImGui::GetFont()->Scale; ImGui::GetFont()->Scale *= 2; - ImGui::PushFont(ImGui::GetFont()); if (ImGui::Button(ICON_MDI_CHECK_CIRCLE_OUTLINE, ImVec2(50, 50))) { m_story.Ok(); } + if (ImGui::IsItemHovered()) ImGui::SetTooltip("OK"); + ImGui::SameLine(); if (ImGui::Button(ICON_MDI_PAUSE, ImVec2(50, 50))) { m_story.Pause(); } + if (ImGui::IsItemHovered()) ImGui::SetTooltip("Pause"); + ImGui::SameLine(); if (ImGui::Button(ICON_MDI_HOME, ImVec2(50, 50))) { m_story.Home(); } + if (ImGui::IsItemHovered()) ImGui::SetTooltip("Home"); + ImGui::SameLine(); if (ImGui::Button(ICON_MDI_ARROW_LEFT_BOLD_CIRCLE_OUTLINE, ImVec2(50, 50))) { m_story.Previous(); } + if (ImGui::IsItemHovered()) ImGui::SetTooltip("Previous"); + ImGui::SameLine(); if (ImGui::Button(ICON_MDI_ARROW_RIGHT_BOLD_CIRCLE_OUTLINE, ImVec2(50, 50))) { m_story.Next(); } + if (ImGui::IsItemHovered()) ImGui::SetTooltip("Next"); ImGui::GetFont()->Scale = old_size; ImGui::PopFont(); + // ===== CONTRÔLES DE SCRIPT ET DEBUG ===== ImGui::SeparatorText("Script control and debug"); if (ImGui::Button("Build nodes")) @@ -96,19 +114,16 @@ void EmulatorDock::Draw() m_story.Log("Play story"); m_story.Play(); } - ImGui::SameLine(); if (ImGui::Button("Stop")) { m_story.Log("Stop story"); m_story.Stop(); } - ImGui::SameLine(); - ImGui::Text("VM state: %s", m_story.VmState().c_str()); - + // ===== BOUTONS DE CHARGEMENT ===== if (ImGui::Button("Load binary story (.c32)")) { IGFD::FileDialogConfig config; @@ -130,46 +145,108 @@ void EmulatorDock::Draw() config.countSelectionMax = 1; config.flags = ImGuiFileDialogFlags_Modal; ImGuiFileDialog::Instance()->OpenDialog("SetSourceScriptDlgKey", - "Choose a binary story", + "Choose a source file", ".chip32", config ); } - // ---------------- Load Binary story + // ===== DIALOGS ===== + // Load Binary story if (ImGuiFileDialog::Instance()->Display("LoadBinaryStoryDlgKey")) { if (ImGuiFileDialog::Instance()->IsOk()) { std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName(); - std::string filePath = ImGuiFileDialog::Instance()->GetCurrentPath(); - std::string filter = ImGuiFileDialog::Instance()->GetCurrentFilter(); - m_story.LoadBinaryStory(filePathName); } - // close ImGuiFileDialog::Instance()->Close(); } - // ---------------- External source file + // External source file if (ImGuiFileDialog::Instance()->Display("SetSourceScriptDlgKey")) { if (ImGuiFileDialog::Instance()->IsOk()) { std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName(); - std::string filePath = ImGuiFileDialog::Instance()->GetCurrentPath(); - std::string filter = ImGuiFileDialog::Instance()->GetCurrentFilter(); - m_story.SetExternalSourceFile(filePathName); } - // close ImGuiFileDialog::Instance()->Close(); } + // ===== CONSOLE VM (NOUVEAU) ===== + DrawVmConsole(); 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 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() { m_image.Clear(); @@ -179,4 +256,4 @@ void EmulatorDock::SetImage(const std::string &image) { m_imageFileName = image; m_image.Load(m_story.BuildFullAssetsPath(image)); -} +} \ No newline at end of file diff --git a/story-editor/src/docks/emulator_dock.h b/story-editor/src/docks/emulator_dock.h index 4564870..d81337c 100644 --- a/story-editor/src/docks/emulator_dock.h +++ b/story-editor/src/docks/emulator_dock.h @@ -3,6 +3,9 @@ #include "window_base.h" #include "i_story_manager.h" #include "gui.h" +#include +#include +#include class EmulatorDock : public WindowBase { @@ -14,10 +17,25 @@ public: void ClearImage(); void SetImage(const std::string &image); + + // NEW: Console VM pour afficher la sortie des prints + void AddVmOutput(const std::string& text); + void ClearVmOutput(); private: IStoryManager &m_story; Gui::Image m_image; std::string m_imageFileName; -}; - + + // NEW: Buffer pour la console VM + struct VmOutputEntry { + std::string text; + uint32_t type; // 0 = normal, 1 = error + }; + + std::vector m_vmOutput; + std::mutex m_vmOutputMutex; + bool m_autoScrollVm = true; + + void DrawVmConsole(); +}; \ No newline at end of file diff --git a/story-editor/src/events/all_events.h b/story-editor/src/events/all_events.h index 48e4383..19c5ca9 100644 --- a/story-editor/src/events/all_events.h +++ b/story-editor/src/events/all_events.h @@ -13,11 +13,15 @@ struct VmStateEvent : public Event int currentLine; chip32_result_t vmResult; std::set breakpoints; + std::string printOutput; // Ajoutez d'autres données nécessaires, ex: std::vector> variables; - // Constructeur pour faciliter la création de l'événement - VmStateEvent(const chip32_ctx_t& ctx, int line, chip32_result_t result, const std::set& bps) - : vmContext(ctx), currentLine(line), vmResult(result), breakpoints(bps) {} + enum class Type + { + PrintEvent, + }; + + Type type; }; class OpenProjectEvent : public Event diff --git a/story-editor/src/main_window.cpp b/story-editor/src/main_window.cpp index d9c6bbb..d8c900b 100644 --- a/story-editor/src/main_window.cpp +++ b/story-editor/src/main_window.cpp @@ -83,6 +83,15 @@ MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appCo OpenFunction(event.GetUuid(), event.GetName()); }); + m_eventBus.Subscribe([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([this](const GenericResultEvent &event) { if (event.IsSuccess()) { @@ -102,6 +111,8 @@ MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appCo } else if (event.GetType() == ModuleEvent::Type::BuildSuccess) { m_toastNotifier.addToast("Module", "Module built successfully! Binary ready for testing.", ToastType::Success); m_debuggerWindow.SetScript(m_appController.GetModuleAssembly()); + + m_emulatorDock.ClearVmOutput(); // Show success message if no errors if (!m_errorListDock.HasErrors()) {