mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
Add VM console output !
This commit is contained in:
parent
07f4288748
commit
88b9bc6b3e
5 changed files with 157 additions and 41 deletions
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
@ -15,9 +18,24 @@ 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();
|
||||||
|
};
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()) {
|
||||||
|
|
@ -103,6 +112,8 @@ MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appCo
|
||||||
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()) {
|
||||||
// You can also open a popup if desired
|
// You can also open a popup if desired
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue