using System.Text;
using System;
using System.Diagnostics;
using System.Threading.Tasks;
namespace Terminal.Gui;
///
/// Provides cut, copy, and paste support for the OS clipboard.
///
///
///
/// On Windows, the class uses the Windows Clipboard APIs via P/Invoke.
///
///
/// On Linux, when not running under Windows Subsystem for Linux (WSL),
/// the class uses the xclip command line tool. If xclip is not installed,
/// the clipboard will not work.
///
///
/// On Linux, when running under Windows Subsystem for Linux (WSL),
/// the class launches Windows' powershell.exe via WSL interop and uses the
/// "Set-Clipboard" and "Get-Clipboard" Powershell CmdLets.
///
///
/// On the Mac, the class uses the MacO OS X pbcopy and pbpaste command line tools
/// and the Mac clipboard APIs vai P/Invoke.
///
///
public static class Clipboard {
static string _contents = string.Empty;
///
/// Gets (copies from) or sets (pastes to) the contents of the OS clipboard.
///
public static string Contents {
get {
try {
if (IsSupported) {
var clipData = Application.Driver.Clipboard.GetClipboardData ();
if (clipData == null) {
// throw new InvalidOperationException ($"{Application.Driver.GetType ().Name}.GetClipboardData returned null instead of string.Empty");
clipData = string.Empty;
}
_contents = clipData;
}
} catch (Exception) {
_contents = string.Empty;
}
return _contents;
}
set {
try {
if (IsSupported) {
if (value == null) {
value = string.Empty;
}
Application.Driver.Clipboard.SetClipboardData (value);
}
_contents = value;
} catch (NotSupportedException e) {
throw e;
} catch (Exception) {
_contents = value;
}
}
}
///
/// Returns true if the environmental dependencies are in place to interact with the OS clipboard.
///
///
///
public static bool IsSupported { get => Application.Driver.Clipboard.IsSupported; }
///
/// Copies the _contents of the OS clipboard to if possible.
///
/// The _contents of the OS clipboard if successful, if not.
/// the OS clipboard was retrieved, otherwise.
public static bool TryGetClipboardData (out string result)
{
if (IsSupported && Application.Driver.Clipboard.TryGetClipboardData (out result)) {
if (_contents != result) {
_contents = result;
}
return true;
}
result = string.Empty;
return false;
}
///
/// Pastes the to the OS clipboard if possible.
///
/// The text to paste to the OS clipboard.
/// the OS clipboard was set, otherwise.
public static bool TrySetClipboardData (string text)
{
if (IsSupported && Application.Driver.Clipboard.TrySetClipboardData (text)) {
_contents = text;
return true;
}
return false;
}
}
///
/// Helper class for console drivers to invoke shell commands to interact with the clipboard.
/// Used primarily by CursesDriver, but also used in Unit tests which is why it is in
/// ConsoleDriver.cs.
///
internal static class ClipboardProcessRunner {
public static (int exitCode, string result) Bash (string commandLine, string inputText = "", bool waitForOutput = false)
{
var arguments = $"-c \"{commandLine}\"";
var (exitCode, result) = Process ("bash", arguments, inputText, waitForOutput);
return (exitCode, result.TrimEnd ());
}
public static (int exitCode, string result) Process (string cmd, string arguments, string input = null, bool waitForOutput = true)
{
var output = string.Empty;
using (Process process = new Process {
StartInfo = new ProcessStartInfo {
FileName = cmd,
Arguments = arguments,
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true,
UseShellExecute = false,
CreateNoWindow = true,
}
}) {
var eventHandled = new TaskCompletionSource ();
process.Start ();
if (!string.IsNullOrEmpty (input)) {
process.StandardInput.Write (input);
process.StandardInput.Close ();
}
if (!process.WaitForExit (5000)) {
var timeoutError = $@"Process timed out. Command line: {process.StartInfo.FileName} {process.StartInfo.Arguments}.";
throw new TimeoutException (timeoutError);
}
if (waitForOutput && process.StandardOutput.Peek () != -1) {
output = process.StandardOutput.ReadToEnd ();
}
if (process.ExitCode > 0) {
output = $@"Process failed to run. Command line: {cmd} {arguments}.
Output: {output}
Error: {process.StandardError.ReadToEnd ()}";
}
return (process.ExitCode, output);
}
}
public static bool DoubleWaitForExit (this System.Diagnostics.Process process)
{
var result = process.WaitForExit (500);
if (result) {
process.WaitForExit ();
}
return result;
}
public static bool FileExists (this string value)
{
return !string.IsNullOrEmpty (value) && !value.Contains ("not found");
}
}