Skip to content

Commit

Permalink
Add SadConsole Stats console
Browse files Browse the repository at this point in the history
  • Loading branch information
highbyte committed Jul 27, 2024
1 parent 06dd560 commit 0cf1826
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 22 deletions.
14 changes: 13 additions & 1 deletion src/apps/Highbyte.DotNet6502.App.SadConsole/MenuConsole.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,16 @@ private void DrawUIItems()
Controls.Add(monitorButton);


var fontSizeLabel = CreateLabel("Font size:", 1, monitorButton.Bounds.MaxExtentY + 2);
var statsButton = new Button("Stats (F11)")
{
Name = "statsButton",
Position = (1, monitorButton.Position.Y + 2),
};
statsButton.Click += (s, e) => { _sadConsoleHostApp.ToggleStats(); IsDirty = true; };
Controls.Add(statsButton);


var fontSizeLabel = CreateLabel("Font size:", 1, statsButton.Bounds.MaxExtentY + 2);
ComboBox selectFontSizeBox = new ComboBox(9, 9, 5, Enum.GetValues<IFont.Sizes>().Select(x => (object)x).ToArray())
{
Position = (fontSizeLabel.Bounds.MaxExtentX + 2, fontSizeLabel.Position.Y),
Expand Down Expand Up @@ -161,6 +170,9 @@ private void SetControlStates()
var monitorButton = Controls["monitorButton"];
monitorButton.IsEnabled = _sadConsoleHostApp.EmulatorState != Systems.EmulatorState.Uninitialized;

var statsButton = Controls["statsButton"];
statsButton.IsEnabled = _sadConsoleHostApp.EmulatorState != Systems.EmulatorState.Uninitialized;

var selectFontSizeComboBox = Controls["selectFontSizeComboBox"];
selectFontSizeComboBox.IsEnabled = _sadConsoleHostApp.EmulatorState == Systems.EmulatorState.Uninitialized;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ internal class MonitorStatusConsole : ControlsConsole

private Label _processorStatusLabel;
private List<Label> _sysInfoLabels;
private string _emptyInfoRow = new string(' ', USABLE_WIDTH);


/// <summary>
/// Console to display the monitor
Expand All @@ -28,7 +30,6 @@ public MonitorStatusConsole(SadConsoleHostApp sadConsoleHostApp)
{
_sadConsoleHostApp = sadConsoleHostApp;

// Initially not visible. Call Init() to initialize with the current system, then Enable() to show it.
IsVisible = false;
FocusedMode = FocusBehavior.None;

Expand All @@ -43,11 +44,11 @@ public MonitorStatusConsole(SadConsoleHostApp sadConsoleHostApp)

private void CreateUIControls()
{
_processorStatusLabel = CreateLabel(new string(' ', USABLE_WIDTH), 1, 1, "processorStatusLabel");
_processorStatusLabel = CreateLabel(_emptyInfoRow, 1, 1, "processorStatusLabel");
_sysInfoLabels = new List<Label>();
for (int i = 0; i < 2; i++)
{
var sysInfoLabel = CreateLabel(new string(' ', USABLE_WIDTH), 1, _processorStatusLabel.Position.Y + 1 + i, $"sysInfoLabel{i}");
var sysInfoLabel = CreateLabel(_emptyInfoRow, 1, _processorStatusLabel.Position.Y + 1 + i, $"sysInfoLabel{i}");
_sysInfoLabels.Add(sysInfoLabel);
}

Expand Down Expand Up @@ -91,9 +92,10 @@ private void DisplayCPUStatus()
{

if (i < system.SystemInfo.Count)
// TODO: Is a new string every time needed here? If system.SystemInfo items does not change, then a list of pre-created string can be initialized once and then reused..
_sysInfoLabels[i].DisplayText = $"SYS: {system.SystemInfo[i]}";
else
_sysInfoLabels[i].DisplayText = new string(' ', _sysInfoLabels[i].DisplayText.Length);
_sysInfoLabels[i].DisplayText = _emptyInfoRow;
}
}
}
Expand Down
79 changes: 62 additions & 17 deletions src/apps/Highbyte.DotNet6502.App.SadConsole/SadConsoleHostApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ protected virtual void OnMonitorStateChange(bool monitorEnabled)
private SadConsoleRenderContext _renderContext = default!;
private SadConsoleInputHandlerContext _inputHandlerContext = default!;
private NullAudioHandlerContext _audioHandlerContext = default!;
private StatsConsole _statsConsole;
private const int MENU_POSITION_X = 0;
private const int MENU_POSITION_Y = 0;

Expand Down Expand Up @@ -148,20 +149,25 @@ private IScreenObject CreateMainSadConsoleScreen(GameHost gameHost)
_menuConsole.Position = (MENU_POSITION_X, MENU_POSITION_Y);
_sadConsoleScreen.Children.Add(_menuConsole);

// Stats console
_statsConsole = new StatsConsole(this);
_statsConsole.Position = (15, 30); // Temporary position while invisible. Will be moved after a system is started and stats is enabled.
_sadConsoleScreen.Children.Add(_statsConsole);

// Monitor status console
_monitorStatusConsole = new MonitorStatusConsole(this);
_monitorStatusConsole.Position = (_menuConsole.Position.X + _menuConsole.Width, _menuConsole.Position.Y + _menuConsole.Height + 1); // Temporary position while invisible. Will be moved after a system is started.
_monitorStatusConsole.Position = (_menuConsole.Position.X + _menuConsole.Width, _menuConsole.Position.Y + _menuConsole.Height + 1); // Temporary position while invisible. Will be moved after a system is started and monitor is enabled.
_sadConsoleScreen.Children.Add(_monitorStatusConsole);

// Monitor console
_monitorConsole = new MonitorConsole(this, _emulatorConfig.Monitor, _monitorStatusConsole.Refresh);
_monitorConsole.IsVisible = false;
_monitorConsole.Position = (_menuConsole.Position.X + _menuConsole.Width, _menuConsole.Position.Y); // Temporary position while invisible. Will be moved after a system is started.
_monitorConsole.Position = (_menuConsole.Position.X + _menuConsole.Width, _menuConsole.Position.Y); // Temporary position while invisible. Will be moved after a system is started and monitor is enabled.
MonitorStateChange += (s, monitorEnabled) =>
{
//_inputHandlerContext.ListenForKeyboardInput(enabled: !monitorEnabled);
//if (monitorEnabled)
// statsPanel.Disable();
// DisableStats();

if (monitorEnabled)
{
Expand Down Expand Up @@ -272,11 +278,9 @@ public override void OnAfterStop()
{
// Disable monitor if it is visible
if (_monitorConsole.IsVisible)
{
_monitorConsole.Disable();
// Resize window to that monitor console is not shown.
Game.Instance.ResizeWindow(CalculateWindowWidthPixels(), CalculateWindowHeightPixels());
}
DisableMonitor();
if (_statsConsole.IsVisible)
DisableStats();

// Remove the console containing the running system
if (_sadConsoleEmulatorConsole != null)
Expand All @@ -286,7 +290,6 @@ public override void OnAfterStop()
_sadConsoleEmulatorConsole.Dispose();
_sadConsoleEmulatorConsole = null;
}

}

public override void OnAfterClose()
Expand Down Expand Up @@ -319,7 +322,7 @@ public override void OnBeforeRunEmulatorOneFrame(out bool shouldRun, out bool sh
shouldRun = false;
shouldReceiveInput = false;

// Don't update emulator state when monitor is visible
// Don't update emulator state when monitor is enabled/visible
if (_monitorConsole.IsVisible)
return;

Expand Down Expand Up @@ -356,6 +359,12 @@ public override void OnBeforeDrawFrame(bool emulatorWillBeRendered)
}
public override void OnAfterDrawFrame(bool emulatorRendered)
{
if (emulatorRendered)
{
// Render stats if enabled and emulator was rendered
if (_statsConsole.IsVisible)
_statsConsole.Refresh();
}
}


Expand Down Expand Up @@ -390,7 +399,8 @@ private int CalculateWindowWidthPixels()
}

var menuConsoleWidthPixels = _menuConsole.WidthPixels;
var emulatorConsoleWidthPixels = (_sadConsoleEmulatorConsole != null ? _sadConsoleEmulatorConsole.WidthPixels + emulatorConsoleFontSizeAdjustment : 0);
var emulatorConsoleWidthPixels = Math.Max((_sadConsoleEmulatorConsole != null ? _sadConsoleEmulatorConsole.WidthPixels + emulatorConsoleFontSizeAdjustment : 0)
, (_statsConsole!= null && _statsConsole.IsVisible ? _statsConsole.WidthPixels : 0));
var monitorConsoleWidthPixels = (_monitorConsole != null && _monitorConsole.IsVisible ? _monitorConsole.WidthPixels : 0);
var widthPixels = menuConsoleWidthPixels + emulatorConsoleWidthPixels + monitorConsoleWidthPixels;
return widthPixels;
Expand All @@ -399,14 +409,14 @@ private int CalculateWindowWidthPixels()
private int CalculateWindowHeightPixels()
{
var menuConsoleHeightPixels = _menuConsole.HeightPixels + (_systemMenuConsole != null ? _systemMenuConsole.HeightPixels : 0);
var emulatorConsoleHeightPixels = (_sadConsoleEmulatorConsole != null ? _sadConsoleEmulatorConsole.HeightPixels : 0);
var emulatorConsoleHeightPixels = (_sadConsoleEmulatorConsole != null ? _sadConsoleEmulatorConsole.HeightPixels + (_statsConsole.IsVisible ? _statsConsole.HeightPixels : 0) : 0);
var monitorConsoleHeightPixels = (_monitorConsole != null && _monitorConsole.IsVisible ? _monitorConsole.HeightPixels + _monitorStatusConsole.HeightPixels : 0);

var heightPixels = Math.Max(menuConsoleHeightPixels, Math.Max(emulatorConsoleHeightPixels, monitorConsoleHeightPixels));
// Calculate Max of the variables above
var heightPixels = new int[] { menuConsoleHeightPixels, emulatorConsoleHeightPixels, monitorConsoleHeightPixels }.Max();
return heightPixels;
}


public void ToggleMonitor()
{
// Only be able to toggle monitor if emulator is running or paused
Expand Down Expand Up @@ -436,16 +446,51 @@ public void EnableMonitor(ExecEvaluatorTriggerResult? execEvaluatorTriggerResult
OnMonitorStateChange(monitorEnabled: true);
}

public void ToggleStats()
{
// Only be able to toggle stats if emulator is running or paused
if (EmulatorState == EmulatorState.Uninitialized)
return;

if (_statsConsole!.IsVisible)
{
DisableStats();
}
else
{
EnableStats();
}
}
public void DisableStats()
{
_statsConsole.Disable();
// Resize main window to fit menu, emulator, monitor and other visible consoles
Game.Instance.ResizeWindow(CalculateWindowWidthPixels(), CalculateWindowHeightPixels());
//OnStatsStateChange(statsEnabled: false);
}
public void EnableStats()
{
_statsConsole.Enable();
// Assume _sadConsoleEmulatorConsole has enabled pixel positioning
_statsConsole.UsePixelPositioning = true;
//_statsConsole.Position = (_sadConsoleEmulatorConsole.Position.X, _sadConsoleEmulatorConsole.Position.Y + (_sadConsoleEmulatorConsole.Height * (int)(_sadConsoleEmulatorConsole.Font.GlyphHeight * _emulatorConfig.FontSizeScaleFactor)));
_statsConsole.Position = (_sadConsoleEmulatorConsole.Position.X, _sadConsoleEmulatorConsole.Position.Y + _sadConsoleEmulatorConsole.HeightPixels);
// Resize main window to fit menu, emulator, monitor and other visible consoles
Game.Instance.ResizeWindow(CalculateWindowWidthPixels(), CalculateWindowHeightPixels());

//OnStatsStateChange(statsEnabled: true);
}

private void HandleUIKeyboardInput()
{
var keyboard = GameHost.Instance.Keyboard;
//if (keyboard.IsKeyPressed(Keys.F10))
// ToggleLogsPanel();
// ToggleLogs();

if (EmulatorState == EmulatorState.Running || EmulatorState == EmulatorState.Paused)
{
//if (keyboard.IsKeyPressed(Keys.F11))
// ToggleStatsPanel();
if (keyboard.IsKeyPressed(Keys.F11))
ToggleStats();
if (keyboard.IsKeyPressed(Keys.F12))
ToggleMonitor();
}
Expand Down
109 changes: 109 additions & 0 deletions src/apps/Highbyte.DotNet6502.App.SadConsole/StatsConsole.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
using Highbyte.DotNet6502.Instrumentation.Stats;
using Highbyte.DotNet6502.Systems;
using SadConsole.UI;
using SadConsole.UI.Controls;
using SadRogue.Primitives;

namespace Highbyte.DotNet6502.App.SadConsole;
internal class StatsConsole : ControlsConsole
{
public const int CONSOLE_WIDTH = USABLE_WIDTH + (SadConsoleUISettings.UI_USE_CONSOLE_BORDER ? 2 : 0);
public const int CONSOLE_HEIGHT = USABLE_HEIGHT + (SadConsoleUISettings.UI_USE_CONSOLE_BORDER ? 2 : 0);
private const int USABLE_WIDTH = 52 * 2 + 1; // To roughly match C64 emulator console width. Will not look if another system with different width is used.
private const int USABLE_HEIGHT = 16;

private readonly SadConsoleHostApp _sadConsoleHostApp;

public event EventHandler<bool>? MonitorStateChange;

private List<Label> _statsLabels;

private string _emptyStatsRow = new string(' ', USABLE_WIDTH);

/// <summary>
/// Console to display the monitor
/// </summary>
/// <param name="sadConsoleHostApp"></param>
/// <param name="monitorConfig"></param>
public StatsConsole(SadConsoleHostApp sadConsoleHostApp)
: base(CONSOLE_WIDTH, CONSOLE_HEIGHT)
{
_sadConsoleHostApp = sadConsoleHostApp;

// Initially not visible. Call Init() to initialize with the current system, then Enable() to show it.
IsVisible = false;
FocusedMode = FocusBehavior.None;

Surface.DefaultForeground = SadConsoleUISettings.UIConsoleForegroundColor;
Surface.DefaultBackground = SadConsoleUISettings.UIConsoleBackgroundColor;

if (SadConsoleUISettings.UI_USE_CONSOLE_BORDER)
Surface.DrawBox(new Rectangle(0, 0, Width, Height), SadConsoleUISettings.ConsoleDrawBoxBorderParameters);

CreateUIControls();
}

private void CreateUIControls()
{
_statsLabels = new List<Label>();
for (int i = 0; i < 2; i++)
{
var statsLabel = CreateLabel(_emptyStatsRow, 1, 1 + i, $"statsLabel{i}");
_statsLabels.Add(statsLabel);
}

//Helper function to create a label and add it to the console
Label CreateLabel(string text, int col, int row, string? name = null)
{
var labelTemp = new Label(text) { Position = new Point(col, row), Name = name };
Controls.Add(labelTemp);
return labelTemp;
}
}

protected override void OnIsDirtyChanged()
{
if (IsDirty)
DisplayStats();
}

public void Refresh()
{
DisplayStats();
}

private void DisplayStats()
{
var system = _sadConsoleHostApp.CurrentRunningSystem!;

var statsStrings = new List<string>();
foreach ((string name, IStat stat) in _sadConsoleHostApp.GetStats().OrderBy(i => i.name))
{
if (stat.ShouldShow())
{
string line = name + ": " + stat.GetDescription();
statsStrings.Add(line);
}
};

// TODO: If there are more stats rows than can be displayed (i.e. not enough items in _statsLabels), then they are not displayed. Fix it?
for (int i = 0; i < _statsLabels.Count; ++i)
{
if (i < statsStrings.Count)
_statsLabels[i].DisplayText = statsStrings[i];
else
_statsLabels[i].DisplayText = _emptyStatsRow;
}
}

public void Enable()
{
IsVisible = true;
IsDirty = true; // Trigger draw of stats
}

public void Disable()
{
IsVisible = false;
}
}

0 comments on commit 0cf1826

Please sign in to comment.