Skip to content

Commit

Permalink
Feature/monitor system commandline (#95)
Browse files Browse the repository at this point in the history
* Replace McMaster.Extensions.CommandLineUtils with System.CommandLine
for parsing and executing in machine code monitor.

* Scrollable SilkNet Native ImGUI monitor log
  • Loading branch information
highbyte authored Nov 27, 2023
1 parent f1dd285 commit e0ba1e5
Show file tree
Hide file tree
Showing 24 changed files with 1,043 additions and 922 deletions.
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// If <ImplicitUsings>enable</ImplicitUsings> is added to the .csproj file, a bunch of standard .NET namespaces are added by default (and not needed to be added here).
global using McMaster.Extensions.CommandLineUtils;
global using System.CommandLine;
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\libraries\Highbyte.DotNet6502\Highbyte.DotNet6502.csproj" />
<ProjectReference Include="..\..\libraries\Highbyte.DotNet6502.Monitor\Highbyte.DotNet6502.Monitor.csproj" />
<ProjectReference Include="..\..\libraries\Highbyte.DotNet6502.Systems\Highbyte.DotNet6502.Systems.csproj" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.0.2" />
<ProjectReference Include="..\..\libraries\Highbyte.DotNet6502\Highbyte.DotNet6502.csproj" />
<ProjectReference Include="..\..\libraries\Highbyte.DotNet6502.Monitor\Highbyte.DotNet6502.Monitor.csproj" />
<ProjectReference Include="..\..\libraries\Highbyte.DotNet6502.Systems\Highbyte.DotNet6502.Systems.csproj" />
</ItemGroup>

</Project>
12 changes: 7 additions & 5 deletions src/apps/Highbyte.DotNet6502.App.ConsoleMonitor/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,12 @@

string? PromptInput()
{
return Prompt.GetString(">",
promptColor: ConsoleColor.Gray,
promptBgColor: ConsoleColor.DarkBlue);
//return Prompt.GetString(">",
// promptColor: ConsoleColor.Gray,
// promptBgColor: ConsoleColor.DarkBlue);

// Console.Write(">");
// return Console.ReadLine();
Console.ForegroundColor = ConsoleColor.Gray;
//Console.BackgroundColor = ConsoleColor.DarkBlue;
Console.Write(">");
return Console.ReadLine();
}
1 change: 1 addition & 0 deletions src/apps/Highbyte.DotNet6502.App.SilkNetNative/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
DefaultDrawScale = 3.0f,
Monitor = new MonitorConfig
{
MaxLineLength = 100,
//DefaultDirectory = "../../../../../../samples/Assembler/C64/Build"

//DefaultDirectory = "../../../../../../samples/Assembler/Generic/Build"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Diagnostics;
using System.Numerics;
using Highbyte.DotNet6502.Monitor;
using Highbyte.DotNet6502.Systems;
using ImGuiNET;
using NativeFileDialogSharp;

namespace Highbyte.DotNet6502.App.SilkNetNative;
Expand All @@ -16,6 +18,7 @@ public class SilkNetImGuiMonitor : ISilkNetImGuiWindow

public bool Quit = false;

private bool _scrollToEnd = false;

private string _monitorCmdString = "";

Expand All @@ -25,8 +28,8 @@ public class SilkNetImGuiMonitor : ISilkNetImGuiWindow

private const int POS_X = 300;
private const int POS_Y = 2;
private const int WIDTH = 720;
private const int HEIGHT = 450;
private const int WIDTH = 750;
private const int HEIGHT = 642;
const int MONITOR_CMD_LINE_LENGTH = 200;

static Vector4 s_InformationColor = new Vector4(1.0f, 1.0f, 1.0f, 1.0f);
Expand All @@ -35,6 +38,8 @@ public class SilkNetImGuiMonitor : ISilkNetImGuiWindow

static Vector4 s_StatusColor = new Vector4(0.7f, 0.7f, 0.7f, 1.0f);

private bool _autoScroll = true;

public event EventHandler<bool> MonitorStateChange;
protected virtual void OnMonitorStateChange(bool monitorEnabled)
{
Expand Down Expand Up @@ -78,34 +83,61 @@ public void PostOnRender()
_hasBeenInitializedOnce = true;
}

ImGui.Begin($"6502 Monitor: {_silkNetNativeMonitor.System.Name}");
ImGui.Begin($"6502 Monitor: {_silkNetNativeMonitor.System.Name}", ImGuiWindowFlags.NoScrollbar);

if (ImGui.IsWindowFocused())
{
_setFocusOnInput = true;
//_setFocusOnInput = true; // TODO: This is not working ok when child window contains a scrollbar (cannot select scrollbar when clicking outside child window)
}

Vector4 textColor;
foreach (var cmd in _silkNetNativeMonitor.MonitorCmdHistory)
if (ImGui.BeginChild("##scrolling", Vector2.Zero, border: false, ImGuiWindowFlags.HorizontalScrollbar | ImGuiWindowFlags.AlwaysVerticalScrollbar))
{
textColor = cmd.Severity switch
//ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero);

Vector4 textColor;
foreach (var cmd in _silkNetNativeMonitor.MonitorCmdHistory)
{
MessageSeverity.Information => s_InformationColor,
MessageSeverity.Warning => s_WarningColor,
MessageSeverity.Error => s_ErrorColor,
_ => s_InformationColor
};
ImGui.PushStyleColor(ImGuiCol.Text, textColor);
ImGui.Text(cmd.Message);
ImGui.PopStyleColor();
textColor = cmd.Severity switch
{
MessageSeverity.Information => s_InformationColor,
MessageSeverity.Warning => s_WarningColor,
MessageSeverity.Error => s_ErrorColor,
_ => s_InformationColor
};
ImGui.PushStyleColor(ImGuiCol.Text, textColor);
ImGui.Text(cmd.Message);
ImGui.PopStyleColor();
}

//ImGui.PopStyleVar();

if (_autoScroll)
{
// If a command was entered, scroll to the bottom of the scroll region.
if (_scrollToEnd)
{
ImGui.SetScrollHereY(1.0f); // 0.0f:top, 0.5f:center, 1.0f:bottom
ImGui.SetScrollHereX(0.0f); // 0.0f:left, 0.5f:center, 1.0f:right
_scrollToEnd = false;
}

// Keep up at the bottom of the scroll region if we were already at the bottom at the beginning of the frame.
// Using a scrollbar or mouse-wheel will take away from the bottom edge.
if (ImGui.GetScrollY() >= ImGui.GetScrollMaxY())
ImGui.SetScrollHereY(1.0f); // 0.0f:top, 0.5f:center, 1.0f:bottom
}

}
ImGui.EndChild();



if (_setFocusOnInput)
{
ImGui.SetKeyboardFocusHere();
_setFocusOnInput = false;
}
ImGui.PushItemWidth(600);
ImGui.PushItemWidth(700);
if (ImGui.InputText("", ref _monitorCmdString, MONITOR_CMD_LINE_LENGTH, ImGuiInputTextFlags.EnterReturnsTrue))
{
_silkNetNativeMonitor.WriteOutput(_monitorCmdString, MessageSeverity.Information);
Expand All @@ -121,6 +153,7 @@ public void PostOnRender()
Disable();
}
_setFocusOnInput = true;
_scrollToEnd = true;
}

// When reaching this line, we may have destroyed the ImGui controller if we did a Quit or Continue as monitor command.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class SilkNetNativeMonitor : MonitorBase
{
private readonly MonitorConfig _monitorConfig;

public const int MONITOR_CMD_HISTORY_VIEW_ROWS = 20;
public const int MONITOR_CMD_HISTORY_VIEW_ROWS = 200;
public List<(string Message, MessageSeverity Severity)> MonitorCmdHistory { get; private set; } = new();

public SilkNetNativeMonitor(
Expand Down
97 changes: 47 additions & 50 deletions src/libraries/Highbyte.DotNet6502.Monitor/CommandLineApp.cs
Original file line number Diff line number Diff line change
@@ -1,75 +1,72 @@
using System.ComponentModel.DataAnnotations;
using System.CommandLine;
using System.CommandLine.Builder;
using System.CommandLine.Parsing;
using Highbyte.DotNet6502.Monitor.Commands;
using Highbyte.DotNet6502.Monitor.SystemSpecific;
using McMaster.Extensions.CommandLineUtils;

namespace Highbyte.DotNet6502.Monitor;

/// <summary>
/// </summary>
public class CommandLineApp
public static class CommandLineApp
{
public static CommandLineApplication Build(MonitorBase monitor, MonitorVariables monitorVariables, MonitorConfig options)
public static Parser Build(MonitorBase monitor, MonitorVariables monitorVariables, MonitorConfig options, IConsole console)
{
//var app = new CommandLineApplication()
//var app = new CommandLineApplication(NullConsole.Singleton, monitor.Options.DefaultDirectory)
var app = new CommandLineApplication(MonitorConsole.BuildSingleton(monitor), monitor.Options.DefaultDirectory!)

Parser? parser = null;
//var root = new RootCommand()
//{
// Name = "DotNet6502Monitor",
// Description = "DotNet 6502 machine code monitor for the DotNet 6502 emulator library." + Environment.NewLine +
// "By Highbyte 2023" + Environment.NewLine +
// "Source at: https://github.com/highbyte/dotnet-6502"
//};
var root = new Command(
"DotNet6502Monitor",
"DotNet 6502 machine code monitor for the DotNet 6502 emulator library." + Environment.NewLine +
"By Highbyte 2023" + Environment.NewLine +
"Source at: https://github.com/highbyte/dotnet-6502")
{
Name = "",
Description = "DotNet 6502 machine code monitor for the DotNet 6502 emulator library." + Environment.NewLine +
"By Highbyte 2022" + Environment.NewLine +
"Source at: https://github.com/highbyte/dotnet-6502",
UnrecognizedArgumentHandling = UnrecognizedArgumentHandling.StopParsingAndCollect
};

// Fix: Use custom Help Text Generator to avoid name/description of the application to be shown each time help text is shown.
app.HelpTextGenerator = new CustomHelpTextGenerator(options.MaxLineLength);
// Fix: To avoid CommandLineUtils to the name of the application at the end of the help text: Don't use HelpOption on app-level, instead set it on each command below.
//app.HelpOption(inherited: true);

app.ConfigureRegisters(monitor, monitorVariables);
app.ConfigureMemory(monitor, monitorVariables);
app.ConfigureDisassembly(monitor, monitorVariables);
app.ConfigureExecution(monitor, monitorVariables);
app.ConfigureBreakpoints(monitor, monitorVariables);
app.ConfigureFiles(monitor, monitorVariables);
app.ConfigureReset(monitor, monitorVariables);
app.ConfigureOptions(monitor, monitorVariables);
root.ConfigureRegisters(monitor, monitorVariables);
root.ConfigureMemory(monitor, monitorVariables);
root.ConfigureDisassembly(monitor, monitorVariables);
root.ConfigureExecution(monitor, monitorVariables);
root.ConfigureBreakpoints(monitor, monitorVariables);
root.ConfigureFiles(monitor, monitorVariables);
root.ConfigureReset(monitor, monitorVariables);
root.ConfigureOptions(monitor, monitorVariables);

// Add any system-specific monitor commands if the system implements it.
if (monitor.SystemRunner.System is ISystemMonitor systemWithMonitor)
{
var monitorCommands = systemWithMonitor.GetSystemMonitorCommands();
monitorCommands.Configure(app, monitor);
monitorCommands.Configure(root, monitor);
}

app.Command("q", cmd =>
{
cmd.HelpOption(inherited: true);
cmd.Description = "Quit monitor.";
cmd.AddName("quit");
cmd.AddName("x");
cmd.AddName("exit");

cmd.OnValidationError((ValidationResult validationResult) =>
{
return monitor.WriteValidationError(validationResult);
});

cmd.OnExecute(() =>
{
return (int)CommandResult.Quit;
});
});
var quitCmd = new Command("q", "Quit monitor");
quitCmd.AddAlias("quit");
quitCmd.AddAlias("exit");
quitCmd.SetHandler(() => Task.FromResult((int)CommandResult.Quit));
root.AddCommand(quitCmd);

app.OnExecute(() =>
var helpCmd = new Command("?", "Help");
helpCmd.SetHandler(() =>
{
monitor.WriteOutput("Unknown command.", MessageSeverity.Error);
monitor.WriteOutput("Help: ?|help|-?|--help", MessageSeverity.Information);
monitor.WriteOutput("Help: command -?|-h|--help", MessageSeverity.Information);
return (int)CommandResult.Error;
parser?.Invoke($"{root.Name} -?", console);
});
root.AddCommand(helpCmd);

int maxWidth = options.MaxLineLength ?? int.MaxValue;
var cmdLineBuilder = new CommandLineBuilder(root)
.UseHelpBuilder(_ =>
{
return new CustomHelpBuilderWithourRootCommand(LocalizationResources.Instance, root.Name, maxWidth: maxWidth);
})
.UseHelp();

return app;
parser = cmdLineBuilder.Build();
return parser;
}
}
Loading

0 comments on commit e0ba1e5

Please sign in to comment.