blob: 39c57e910c825d7c0c2f73ef9d1d4d7ed3bbeb4d [file] [log] [blame]
//
// Copyright (c) 2010-2022 Antmicro
//
// This file is licensed under the MIT License.
// Full license text is available in 'licenses/MIT.txt'.
//
using System;
using Xwt;
using Antmicro.Renode.Peripherals.UART;
using AntShell.Terminal;
using Antmicro.Renode.Utilities;
using Antmicro.Renode.Logging;
using TermSharp;
using System.Collections.Generic;
using TermSharp.Rows;
using Xwt.Drawing;
namespace Antmicro.Renode.UI
{
public partial class TerminalWidget : Widget
{
public TerminalWidget(Func<bool> focusProvider, bool isMonitorWindow)
{
this.isMonitorWindow = isMonitorWindow;
var shortcutDictionary = new Dictionary<KeyEventArgs, Action>
{
{CreateKey(Key.C, ModifierKeys.Shift | ModifierKeys.Control), CopyMarkedField},
{CreateKey(Key.V, ModifierKeys.Shift | ModifierKeys.Control), PasteMarkedField},
{CreateKey(Key.Insert, ModifierKeys.Shift), PasteMarkedField},
{CreateKey(Key.Home, ModifierKeys.Shift), () => terminal.MoveScrollbarToBeginning() },
{CreateKey(Key.End, ModifierKeys.Shift), () => terminal.MoveScrollbarToEnd() },
{CreateKey(Key.PageUp, ModifierKeys.Shift), () => terminal.PageUp() },
{CreateKey(Key.PageDown, ModifierKeys.Shift), () => terminal.PageDown() },
{CreateKey(Key.Up, ModifierKeys.Shift), () => terminal.LineUp() },
{CreateKey(Key.Down, ModifierKeys.Shift), () => terminal.LineDown() },
{CreateKey(Key.Plus, ModifierKeys.Shift | ModifierKeys.Control), FontSizeUp},
{CreateKey(Key.Minus, ModifierKeys.Control), FontSizeDown},
{CreateKey(Key.NumPadAdd, ModifierKeys.Control), FontSizeUp},
{CreateKey(Key.NumPadSubtract, ModifierKeys.Control), FontSizeDown},
{CreateKey(Key.K0, ModifierKeys.Control), SetDefaultFontSize},
{CreateKey(Key.NumPad0, ModifierKeys.Control), SetDefaultFontSize}
};
modifyLineEndings = ConfigurationManager.Instance.Get("termsharp", "append-CR-to-LF", true);
terminal = new Terminal(focusProvider);
IOSource = new TerminalIOSource(terminal);
IOSource.BeforeWrite += b =>
{
// we do not check if previous byte was '\r', because it should not cause any problem to
// send it twice
if(modifyLineEndings && b == '\n')
{
IOSource.Write((byte)'\r');
}
};
terminal.InnerMargin = new WidgetSpacing(5, 5, 5, 5);
terminal.Cursor.Enabled = true;
terminal.ContextMenu = CreatePopupMenu();
// We set the default font as a fall-back option.
terminal.CurrentFont = Xwt.Drawing.Font.SystemMonospaceFont;
#if !PLATFORM_OSX
// Here we try to load the robot font; unfortunately it doesn't work on OSX. Moreover, on some versions of
// OSX it passes with no error (and no effect), on the others - it hangs. That's why we try to set the font and then
// we check if we succeeded.
var fontFile = typeof(TerminalWidget).Assembly.FromResourceToTemporaryFile("RobotoMono-Regular.ttf");
Xwt.Drawing.Font.RegisterFontFromFile(fontFile);
#endif
var fontFace = ConfigurationManager.Instance.Get("termsharp", "font-face", "Roboto Mono");
defaultFontSize = ConfigurationManager.Instance.Get("termsharp", "font-size", (int)PredefinedFontSize, x => x >= MinFontSize);
var font = Xwt.Drawing.Font.FromName(fontFace);
if(!font.Family.Contains(fontFace))
{
Logger.Log(LogLevel.Warning, "The font '{0}' defined in the config file cannot be loaded.", fontFace);
font = terminal.CurrentFont;
}
terminal.CurrentFont = font.WithSize(defaultFontSize);
if(isMonitorWindow)
{
terminal.AppendRow(new ImageRow(Image.FromResource("logo.png"), 3), true);
}
// this empty dummy row is required as this is where first
// characters will be displayed
terminal.AppendRow(new MonospaceTextRow(""));
var encoder = new TermSharp.Vt100.Encoder(x =>
{
terminal.ClearSelection();
terminal.MoveScrollbarToEnd();
IOSource.HandleInput(x);
});
terminal.ButtonPressed += (s, a) =>
{
if(a.Button == PointerButton.Middle)
{
a.Handled = true;
PastePrimarySelection();
}
};
terminal.KeyPressed += (s, a) =>
{
a.Handled = true;
var modifiers = a.Modifiers;
if(!Misc.IsOnOsX)
{
modifiers &= ~(ModifierKeys.Command);
}
foreach(var entry in shortcutDictionary)
{
if(modifiers == entry.Key.Modifiers)
{
if(a.Key == entry.Key.Key)
{
entry.Value();
return;
}
}
}
encoder.Feed(a.Key, modifiers);
};
Content = terminal;
}
public void Clear()
{
terminal.Clear();
}
public TerminalIOSource IOSource
{
get;
set;
}
public event Action Initialized
{
add
{
terminal.Initialized += value;
}
remove
{
terminal.Initialized -= value;
}
}
public void Close()
{
base.Dispose(true);
if(IOSource != null)
{
IOSource.Dispose();
IOSource = null;
}
}
protected override void OnBoundsChanged()
{
if(!isMonitorWindow)
{
var availableScreenSize = terminal.ScreenSize + terminal.InnerMarginBottom - MinimalBottomMargin;
var rowHeight = terminal.GetScreenRow(0).LineHeight;
var fullLinesCount = Math.Floor(availableScreenSize / rowHeight);
var desiredScreenSize = rowHeight * fullLinesCount;
terminal.InnerMarginBottom = Math.Floor(availableScreenSize - desiredScreenSize + MinimalBottomMargin);
}
base.OnBoundsChanged();
}
private Menu CreatePopupMenu()
{
var popup = new Menu();
var copyItem = new MenuItem("Copy");
copyItem.Clicked += delegate
{
CopyMarkedField();
};
popup.Items.Add(copyItem);
var pasteItem = new MenuItem("Paste");
pasteItem.Clicked += delegate
{
PasteMarkedField();
};
popup.Items.Add(pasteItem);
var lineEndingsItem = new MenuItem(lineEndingsDictionary[!modifyLineEndings]);
lineEndingsItem.Clicked += delegate
{
modifyLineEndings = !modifyLineEndings;
lineEndingsItem.Label = lineEndingsDictionary[!modifyLineEndings];
ConfigurationManager.Instance.Set("termsharp", "append-CR-to-LF", modifyLineEndings);
};
popup.Items.Add(lineEndingsItem);
return popup;
}
private KeyEventArgs CreateKey(Key key, ModifierKeys modifierKeys)
{
return new KeyEventArgs(key, modifierKeys, false, 0);
}
private Dictionary<bool, string> lineEndingsDictionary = new Dictionary<bool, string>
{
{true, "Append '\\r' to line ending"},
{false, "Do not append '\\r' to line ending"}
};
private bool modifyLineEndings;
private bool isMonitorWindow;
private double defaultFontSize;
private Terminal terminal;
private const int MinimalBottomMargin = 2;
#if PLATFORM_LINUX
private const double PredefinedFontSize = 10.0;
#else
// Default font size on OSX and Windows is slightly larger than on generic Linux system.
private const double PredefinedFontSize = 12.0;
#endif
private const double MinFontSize = 1.0;
}
}