This repository has been archived on 2025-01-01. You can view files and clone it, but cannot push or open issues or pull requests.
ShiftOS_TheReturn/ShiftOS.Frontend/Apps/Terminal.cs

477 lines
16 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
2017-07-04 09:52:49 -04:00
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
2017-07-04 09:52:49 -04:00
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
2017-07-04 09:52:49 -04:00
using Microsoft.Xna.Framework.Input;
using ShiftOS.Engine;
using ShiftOS.Frontend.GraphicsSubsystem;
using static ShiftOS.Engine.SkinEngine;
namespace ShiftOS.Frontend.Apps
{
[FileHandler("Shell script", ".trm", "fileicontrm")]
[Launcher("{TITLE_TERMINAL}", false, null, "{AL_UTILITIES}")]
[WinOpen("{WO_TERMINAL}")]
[DefaultTitle("{TITLE_TERMINAL}")]
[DefaultIcon("iconTerminal")]
public class Terminal : GUI.Control, IShiftOSWindow
{
private TerminalControl _terminal = null;
2017-07-28 12:04:16 -04:00
2017-07-31 22:48:17 -04:00
public TerminalControl TerminalControl
{
get
{
return _terminal;
}
}
public Terminal()
{
Width = 493;
Height = 295;
}
public void OnLoad()
{
_terminal = new Apps.TerminalControl();
_terminal.Dock = GUI.DockStyle.Fill;
AddControl(_terminal);
AppearanceManager.ConsoleOut = _terminal;
AppearanceManager.StartConsoleOut();
TerminalBackend.PrintPrompt();
SaveSystem.GameReady += () =>
{
Console.WriteLine("[sessionmgr] Starting system UI...");
AppearanceManager.SetupWindow(new SystemStatus());
TerminalBackend.PrintPrompt();
};
}
protected override void OnLayout(GameTime gameTime)
{
if (ContainsFocusedControl || IsFocusedControl)
AppearanceManager.ConsoleOut = _terminal;
}
public void OnSkinLoad()
{
}
public bool OnUnload()
{
return true;
}
public void OnUpgrade()
{
}
}
public class TerminalControl : GUI.TextInput, ITerminalWidget
{
2017-07-28 12:04:16 -04:00
private int _zoomFactor = 1;
2017-07-04 09:52:49 -04:00
public TerminalControl()
{
Dock = GUI.DockStyle.Fill;
}
private static readonly string[] delimiters = { Environment.NewLine };
2017-07-04 09:52:49 -04:00
public string[] Lines
{
get
{
return Text.Split(delimiters, StringSplitOptions.None);
}
}
public void Clear()
{
Text = "";
Index = 0;
_vertOffset = 0;
Invalidate();
}
public void SelectBottom()
{
Index = Text.Length - 1;
RecalculateLayout();
InvalidateTopLevel();
}
public void Write(string text)
{
Engine.Desktop.InvokeOnWorkerThread(() =>
{
Text += Localization.Parse(text);
SelectBottom();
Index += text.Length;
RecalculateLayout();
InvalidateTopLevel();
});
}
public void WriteLine(string text)
{
Write(text + Environment.NewLine);
}
private static readonly Regex regexNl = new Regex(Regex.Escape(Environment.NewLine));
public int GetCurrentLine()
{
return regexNl.Matches(Text.Substring(0, Index)).Count;
}
float _vertOffset = 0.0f;
protected void RecalculateLayout()
{
2017-08-09 14:17:51 -04:00
var cloc = GetPointAtIndex();
var csize = GraphicsContext.MeasureString("#", new Font(LoadedSkin.TerminalFont.Name, LoadedSkin.TerminalFont.Size * _zoomFactor, LoadedSkin.TerminalFont.Style));
if (cloc.Y - _vertOffset < 0)
2017-07-31 22:48:17 -04:00
{
2017-08-09 14:17:51 -04:00
_vertOffset += cloc.Y - _vertOffset;
}
while ((cloc.Y + csize.Y) - _vertOffset > Height)
{
_vertOffset += csize.Y;
2017-07-31 22:48:17 -04:00
}
}
private bool blinkStatus = false;
private double blinkTime = 0.0;
protected override void OnLayout(GameTime gameTime)
2017-07-04 09:52:49 -04:00
{
blinkTime += gameTime.ElapsedGameTime.TotalMilliseconds;
if (blinkTime > 500.0)
blinkTime = 0;
bool prev = blinkStatus;
blinkStatus = blinkTime > 250.0;
if (prev != blinkStatus)
Invalidate();
2017-07-04 09:52:49 -04:00
}
public bool ReadOnly = false;
/// <summary>
/// Gets the X and Y coordinates (in pixels) of the caret.
/// </summary>
/// <param name="gfx">A <see cref="System.Drawing.Graphics"/> object used for font measurements</param>
/// <returns>the correct position of the d*ng caret. yw</returns>
2017-08-09 14:17:51 -04:00
public System.Drawing.Point GetPointAtIndex()
{
if (string.IsNullOrEmpty(Text))
return new System.Drawing.Point(2, 2);
2017-07-30 12:12:26 -04:00
var font = new Font(LoadedSkin.TerminalFont.Name, LoadedSkin.TerminalFont.Size * _zoomFactor, LoadedSkin.TerminalFont.Style);
int currline = GetCurrentLine();
string substring = String.Join(Environment.NewLine, Lines.Take(currline + 1));
2017-08-09 14:17:51 -04:00
int h = (int)Math.Round(GraphicsContext.MeasureString(substring, font, Width).Y - font.Height);
int linestart = String.Join(Environment.NewLine, Lines.Take(GetCurrentLine())).Length;
2017-08-09 14:17:51 -04:00
var lineMeasure = GraphicsContext.MeasureString(Text.Substring(linestart, Index - linestart), font);
int w = (int)Math.Floor(lineMeasure.X);
while (w > Width)
{
w -= Width;
2017-08-09 14:17:51 -04:00
h += (int)lineMeasure.Y;
}
return new System.Drawing.Point(w, h);
}
private PointF CaretPosition = new PointF(2, 2);
private Size CaretSize = new Size(2, 15);
2017-08-08 11:29:03 -04:00
private bool doEnter = true;
2017-07-04 09:52:49 -04:00
protected override void OnKeyEvent(KeyEvent a)
{
2017-08-08 11:29:03 -04:00
if (a.Key != Keys.Enter)
doEnter = true;
2017-07-29 11:01:32 -04:00
if (a.ControlDown && (a.Key == Keys.OemPlus || a.Key == Keys.Add))
2017-07-28 12:04:16 -04:00
{
_zoomFactor *= 2;
RecalculateLayout();
Invalidate();
return;
}
if (a.ControlDown && (a.Key == Keys.OemMinus || a.Key == Keys.Subtract))
{
2017-07-29 11:01:32 -04:00
_zoomFactor = Math.Max(1, _zoomFactor / 2);
2017-07-28 12:04:16 -04:00
RecalculateLayout();
Invalidate();
return;
}
if (a.Key == Keys.Enter && !ReadOnly)
{
2017-08-08 11:29:03 -04:00
if (doEnter == true)
{
2017-08-08 11:29:03 -04:00
if (!PerformTerminalBehaviours)
2017-07-04 09:52:49 -04:00
{
2017-08-08 11:29:03 -04:00
Text = Text.Insert(Index, Environment.NewLine);
Index += 2;
RecalculateLayout();
Invalidate();
2017-07-04 09:52:49 -04:00
return;
}
2017-08-08 11:29:03 -04:00
try
2017-07-29 11:01:32 -04:00
{
2017-08-08 11:29:03 -04:00
if (!TerminalBackend.PrefixEnabled)
2017-07-04 09:52:49 -04:00
{
2017-08-08 11:29:03 -04:00
string textraw = Lines[Lines.Length - 1];
TerminalBackend.SendText(textraw);
return;
}
var text = Lines;
var text2 = text[text.Length - 1];
var text3 = "";
var text4 = Regex.Replace(text2, @"\t|\n|\r", "");
WriteLine("");
2017-07-04 09:52:49 -04:00
2017-08-08 11:29:03 -04:00
if (TerminalBackend.PrefixEnabled)
{
text3 = text4.Remove(0, TerminalBackend.ShellOverride.Length);
}
if (!string.IsNullOrWhiteSpace(text3))
{
TerminalBackend.LastCommand = text3;
TerminalBackend.SendText(text4);
if (TerminalBackend.InStory == false)
{
2017-07-04 09:52:49 -04:00
{
2017-08-08 11:29:03 -04:00
var result = SkinEngine.LoadedSkin.CurrentParser.ParseCommand(text3);
if (result.Equals(default(KeyValuePair<string, Dictionary<string, string>>)))
{
Console.WriteLine("{ERR_SYNTAXERROR}");
}
else
{
TerminalBackend.InvokeCommand(result.Key, result.Value);
}
2017-07-04 09:52:49 -04:00
2017-08-08 11:29:03 -04:00
}
2017-07-04 09:52:49 -04:00
}
}
}
2017-08-08 11:29:03 -04:00
catch
{
}
2017-08-08 11:29:03 -04:00
finally
{
if (TerminalBackend.PrefixEnabled)
{
TerminalBackend.PrintPrompt();
}
AppearanceManager.CurrentPosition = 0;
2017-08-08 11:29:03 -04:00
}
doEnter = false;
}
2017-07-04 09:52:49 -04:00
}
else if (a.Key == Keys.Back && !ReadOnly)
2017-07-04 09:52:49 -04:00
{
try
{
if (PerformTerminalBehaviours)
{
var tostring3 = Lines[Lines.Length - 1];
var tostringlen = tostring3.Length + 1;
2017-07-29 11:01:32 -04:00
var workaround = TerminalBackend.ShellOverride;
var derp = workaround.Length + 1;
if (tostringlen != derp)
{
AppearanceManager.CurrentPosition--;
base.OnKeyEvent(a);
RecalculateLayout();
InvalidateTopLevel();
}
}
else
2017-07-04 09:52:49 -04:00
{
base.OnKeyEvent(a);
RecalculateLayout();
InvalidateTopLevel();
}
}
catch
{
Debug.WriteLine("Drunky alert in terminal.");
}
}
2017-07-29 11:01:32 -04:00
else if (a.Key == Keys.Right)
{
2017-07-29 11:01:32 -04:00
if (Index < Text.Length)
{
Index++;
AppearanceManager.CurrentPosition++;
RecalculateLayout();
InvalidateTopLevel();
}
}
2017-07-04 09:52:49 -04:00
else if (a.Key == Keys.Left)
{
if (SaveSystem.CurrentSave != null)
{
var getstring = Lines[Lines.Length - 1];
var stringlen = getstring.Length + 1;
2017-07-29 11:01:32 -04:00
var header = TerminalBackend.ShellOverride;
2017-07-04 09:52:49 -04:00
var headerlen = header.Length + 1;
var selstart = Index;
var remstrlen = Text.Length - stringlen;
var finalnum = selstart - remstrlen;
if (!PerformTerminalBehaviours)
headerlen = 0;
if (finalnum > headerlen)
2017-07-04 09:52:49 -04:00
{
AppearanceManager.CurrentPosition--;
base.OnKeyEvent(a);
}
}
}
else if (a.Key == Keys.Up && PerformTerminalBehaviours)
2017-07-04 09:52:49 -04:00
{
var tostring3 = Lines[Lines.Length - 1];
2017-07-29 11:01:32 -04:00
if (tostring3 == TerminalBackend.ShellOverride)
2017-07-04 09:52:49 -04:00
Console.Write(TerminalBackend.LastCommand);
ConsoleEx.OnFlush?.Invoke();
return;
}
else
{
if ((PerformTerminalBehaviours && TerminalBackend.InStory) || ReadOnly)
2017-07-04 09:52:49 -04:00
{
return;
}
if (a.KeyChar != '\0')
{
Text = Text.Insert(Index, a.KeyChar.ToString());
Index++;
2017-07-04 09:52:49 -04:00
AppearanceManager.CurrentPosition++;
2017-07-29 11:01:32 -04:00
// RecalculateLayout();
2017-07-04 09:52:49 -04:00
InvalidateTopLevel();
}
}
blinkStatus = true;
blinkTime = 250;
}
public bool PerformTerminalBehaviours = true;
protected override void OnPaint(GraphicsContext gfx)
2017-07-28 12:04:16 -04:00
{
var font = new System.Drawing.Font(LoadedSkin.TerminalFont.Name, LoadedSkin.TerminalFont.Size * _zoomFactor, LoadedSkin.TerminalFont.Style);
gfx.Clear(LoadedSkin.TerminalBackColorCC.ToColor().ToMonoColor());
if (!string.IsNullOrEmpty(Text))
{
//Draw the caret.
if (blinkStatus == true)
{
2017-08-09 14:17:51 -04:00
PointF cursorPos = GetPointAtIndex();
string caret = (Index < Text.Length) ? Text[Index].ToString() : " ";
var cursorSize = GraphicsContext.MeasureString(caret, font);
2017-08-09 14:17:51 -04:00
var lineMeasure = GraphicsContext.MeasureString(Lines[GetCurrentLine()], font);
if (cursorPos.X > lineMeasure.X)
{
cursorPos.X = lineMeasure.X;
}
2017-07-19 13:06:39 -04:00
2017-07-31 22:48:17 -04:00
gfx.DrawRectangle((int)cursorPos.X, (int)(cursorPos.Y - _vertOffset), (int)cursorSize.X, (int)cursorSize.Y, LoadedSkin.TerminalForeColorCC.ToColor().ToMonoColor());
2017-07-19 13:06:39 -04:00
}
//Draw the text
2017-08-07 13:46:04 -04:00
int textloc = 0 - (int)_vertOffset;
foreach (var line in Lines)
{
if(!(textloc < 0 || textloc - font.Height >= Height))
gfx.DrawString(line, 0, textloc, LoadedSkin.TerminalForeColorCC.ToColor().ToMonoColor(), font, Width - 4);
textloc += font.Height;
}
}
}
}
public static class ConsoleColorExtensions
{
public static System.Drawing.Color ToColor(this ConsoleColor cc)
{
switch (cc)
{
case ConsoleColor.Black:
return System.Drawing.Color.Black;
case ConsoleColor.Blue:
return System.Drawing.Color.Blue;
case ConsoleColor.Cyan:
return System.Drawing.Color.Cyan;
case ConsoleColor.DarkBlue:
return System.Drawing.Color.DarkBlue;
case ConsoleColor.DarkCyan:
return System.Drawing.Color.DarkCyan;
case ConsoleColor.DarkGray:
return System.Drawing.Color.DarkGray;
case ConsoleColor.DarkGreen:
return System.Drawing.Color.DarkGreen;
case ConsoleColor.DarkMagenta:
return System.Drawing.Color.DarkMagenta;
case ConsoleColor.DarkRed:
return System.Drawing.Color.DarkRed;
case ConsoleColor.DarkYellow:
return System.Drawing.Color.Orange;
case ConsoleColor.Gray:
return System.Drawing.Color.Gray;
case ConsoleColor.Green:
return System.Drawing.Color.Green;
case ConsoleColor.Magenta:
return System.Drawing.Color.Magenta;
case ConsoleColor.Red:
return System.Drawing.Color.Red;
case ConsoleColor.White:
return System.Drawing.Color.White;
case ConsoleColor.Yellow:
return System.Drawing.Color.Yellow;
}
return System.Drawing.Color.Empty;
}
}
public static class GraphicsExtensions
{
2017-08-09 14:17:51 -04:00
[Obsolete("Use GraphicsContext.MeasureString instead")]
public static SizeF SmartMeasureString(this Graphics gfx, string s, Font font, int width)
{
2017-08-07 13:46:04 -04:00
var measure = System.Windows.Forms.TextRenderer.MeasureText(s, font, new Size(width, int.MaxValue));
return measure;
}
2017-08-09 14:17:51 -04:00
[Obsolete("Use GraphicsContext.MeasureString instead")]
public static SizeF SmartMeasureString(this Graphics gfx, string s, Font font)
{
2017-07-05 08:43:35 -04:00
return SmartMeasureString(gfx, s, font, int.MaxValue);
}
}
}