Merge pull request #1319 from BDisp/clipboard-linux-fix

Fixes #1318. Ensures that the OS clipboard is always sets.
This commit is contained in:
Charlie Kindel
2021-05-30 07:32:45 -06:00
committed by GitHub
12 changed files with 726 additions and 107 deletions

View File

@@ -751,7 +751,11 @@ namespace Terminal.Gui {
if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
clipboard = new MacOSXClipboard ();
} else {
clipboard = new CursesClipboard ();
if (Is_WSL_Platform ()) {
clipboard = new WSLClipboard ();
} else {
clipboard = new CursesClipboard ();
}
}
Curses.raw ();
@@ -844,6 +848,15 @@ namespace Terminal.Gui {
}
}
public static bool Is_WSL_Platform ()
{
var result = BashRunner.Run ("uname -a");
if (result.Contains ("microsoft") && result.Contains ("WSL")) {
return true;
}
return false;
}
static int MapColor (Color color)
{
switch (color) {
@@ -1056,67 +1069,102 @@ namespace Terminal.Gui {
}
}
class CursesClipboard : IClipboard {
public string GetClipboardData ()
class CursesClipboard : ClipboardBase {
public override bool IsSupported => CheckSupport ();
bool CheckSupport ()
{
try {
var result = BashRunner.Run ("which xclip");
return BashRunner.FileExists (result);
} catch (Exception) {
// Permissions issue.
return false;
}
}
protected override string GetClipboardDataImpl ()
{
var tempFileName = System.IO.Path.GetTempFileName ();
try {
// BashRunner.Run ($"xsel -o --clipboard > {tempFileName}");
BashRunner.Run ($"xclip -o > {tempFileName}");
BashRunner.Run ($"xclip -selection clipboard -o > {tempFileName}");
return System.IO.File.ReadAllText (tempFileName);
} finally {
System.IO.File.Delete (tempFileName);
}
}
public void SetClipboardData (string text)
protected override void SetClipboardDataImpl (string text)
{
var tempFileName = System.IO.Path.GetTempFileName ();
System.IO.File.WriteAllText (tempFileName, text);
try {
// BashRunner.Run ($"cat {tempFileName} | xsel -i --clipboard");
BashRunner.Run ($"cat {tempFileName} | xclip -selection clipboard");
} finally {
System.IO.File.Delete (tempFileName);
}
// var tempFileName = System.IO.Path.GetTempFileName ();
// System.IO.File.WriteAllText (tempFileName, text);
// try {
// // BashRunner.Run ($"cat {tempFileName} | xsel -i --clipboard");
// BashRunner.Run ($"cat {tempFileName} | xclip -selection clipboard");
// } finally {
// System.IO.File.Delete (tempFileName);
// }
BashRunner.Run ("xclip -selection clipboard -i", false, text);
}
}
static class BashRunner {
public static string Run (string commandLine)
public static string Run (string commandLine, bool output = true, string inputText = "")
{
var errorBuilder = new System.Text.StringBuilder ();
var outputBuilder = new System.Text.StringBuilder ();
var arguments = $"-c \"{commandLine}\"";
using (var process = new System.Diagnostics.Process {
StartInfo = new System.Diagnostics.ProcessStartInfo {
FileName = "bash",
Arguments = arguments,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = false,
}
}) {
process.Start ();
process.OutputDataReceived += (sender, args) => { outputBuilder.AppendLine (args.Data); };
process.BeginOutputReadLine ();
process.ErrorDataReceived += (sender, args) => { errorBuilder.AppendLine (args.Data); };
process.BeginErrorReadLine ();
if (!process.DoubleWaitForExit ()) {
var timeoutError = $@"Process timed out. Command line: bash {arguments}.
if (output) {
var errorBuilder = new System.Text.StringBuilder ();
var outputBuilder = new System.Text.StringBuilder ();
using (var process = new System.Diagnostics.Process {
StartInfo = new System.Diagnostics.ProcessStartInfo {
FileName = "bash",
Arguments = arguments,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = false,
}
}) {
process.Start ();
process.OutputDataReceived += (sender, args) => { outputBuilder.AppendLine (args.Data); };
process.BeginOutputReadLine ();
process.ErrorDataReceived += (sender, args) => { errorBuilder.AppendLine (args.Data); };
process.BeginErrorReadLine ();
if (!process.DoubleWaitForExit ()) {
var timeoutError = $@"Process timed out. Command line: bash {arguments}.
Output: {outputBuilder}
Error: {errorBuilder}";
throw new Exception (timeoutError);
}
if (process.ExitCode == 0) {
return outputBuilder.ToString ();
}
var error = $@"Could not execute process. Command line: bash {arguments}.
Output: {outputBuilder}
Error: {errorBuilder}";
throw new Exception (timeoutError);
throw new Exception (error);
}
if (process.ExitCode == 0) {
return outputBuilder.ToString ();
} else {
using (var process = new System.Diagnostics.Process {
StartInfo = new System.Diagnostics.ProcessStartInfo {
FileName = "bash",
Arguments = arguments,
RedirectStandardInput = true,
UseShellExecute = false,
CreateNoWindow = false
}
}) {
process.Start ();
process.StandardInput.Write (inputText);
process.StandardInput.Close ();
process.WaitForExit ();
return inputText;
}
var error = $@"Could not execute process. Command line: bash {arguments}.
Output: {outputBuilder}
Error: {errorBuilder}";
throw new Exception (error);
}
}
@@ -1128,9 +1176,14 @@ namespace Terminal.Gui {
}
return result;
}
public static bool FileExists (string value)
{
return !string.IsNullOrEmpty (value) && !value.Contains ("not found");
}
}
class MacOSXClipboard : IClipboard {
class MacOSXClipboard : ClipboardBase {
IntPtr nsString = objc_getClass ("NSString");
IntPtr nsPasteboard = objc_getClass ("NSPasteboard");
IntPtr utfTextType;
@@ -1144,6 +1197,18 @@ namespace Terminal.Gui {
IntPtr generalPasteboardRegister = sel_registerName ("generalPasteboard");
IntPtr clearContentsRegister = sel_registerName ("clearContents");
public override bool IsSupported => CheckSupport ();
bool CheckSupport ()
{
var result = BashRunner.Run ("which pbcopy");
if (!BashRunner.FileExists (result)) {
return false;
}
result = BashRunner.Run ("which pbpaste");
return BashRunner.FileExists (result);
}
public MacOSXClipboard ()
{
utfTextType = objc_msgSend (objc_msgSend (nsString, allocRegister), initWithUtf8Register, "public.utf8-plain-text");
@@ -1151,14 +1216,14 @@ namespace Terminal.Gui {
generalPasteboard = objc_msgSend (nsPasteboard, generalPasteboardRegister);
}
public string GetClipboardData ()
protected override string GetClipboardDataImpl ()
{
var ptr = objc_msgSend (generalPasteboard, stringForTypeRegister, nsStringPboardType);
var charArray = objc_msgSend (ptr, utf8Register);
return Marshal.PtrToStringAnsi (charArray);
}
public void SetClipboardData (string text)
protected override void SetClipboardDataImpl (string text)
{
IntPtr str = default;
try {
@@ -1190,4 +1255,63 @@ namespace Terminal.Gui {
[DllImport ("/System/Library/Frameworks/AppKit.framework/AppKit")]
static extern IntPtr sel_registerName (string selectorName);
}
class WSLClipboard : ClipboardBase {
public override bool IsSupported => CheckSupport ();
bool CheckSupport ()
{
var result = BashRunner.Run ("which powershell.exe");
return BashRunner.FileExists (result);
//var result = BashRunner.Run ("which powershell.exe");
//if (!BashRunner.FileExists (result)) {
// return false;
//}
//result = BashRunner.Run ("which clip.exe");
//return BashRunner.FileExists (result);
}
protected override string GetClipboardDataImpl ()
{
using (var powershell = new System.Diagnostics.Process {
StartInfo = new System.Diagnostics.ProcessStartInfo {
RedirectStandardOutput = true,
FileName = "powershell.exe",
Arguments = "-noprofile -command \"Get-Clipboard\""
}
}) {
powershell.Start ();
var result = powershell.StandardOutput.ReadToEnd ();
powershell.StandardOutput.Close ();
powershell.WaitForExit ();
return result.TrimEnd ();
}
}
protected override void SetClipboardDataImpl (string text)
{
using (var powershell = new System.Diagnostics.Process {
StartInfo = new System.Diagnostics.ProcessStartInfo {
FileName = "powershell.exe",
Arguments = $"-noprofile -command \"Set-Clipboard -Value \\\"{text}\\\"\""
}
}) {
powershell.Start ();
powershell.WaitForExit ();
}
//using (var clipExe = new System.Diagnostics.Process {
// StartInfo = new System.Diagnostics.ProcessStartInfo {
// FileName = "clip.exe",
// RedirectStandardInput = true
// }
//}) {
// clipExe.Start ();
// clipExe.StandardInput.Write (text);
// clipExe.StandardInput.Close ();
// clipExe.WaitForExit ();
//}
}
}
}

View File

@@ -116,7 +116,7 @@ namespace Terminal.Gui {
this.mainLoop = mainLoop;
pipe (wakeupPipes);
AddWatch (wakeupPipes [0], Condition.PollIn, ml => {
read (wakeupPipes [0], ignore, (IntPtr)1);
read (wakeupPipes [0], ignore, (IntPtr)0);
return true;
});
}

View File

@@ -64,7 +64,11 @@ namespace Terminal.Gui {
} else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
Clipboard = new MacOSXClipboard ();
} else {
Clipboard = new CursesClipboard ();
if (CursesDriver.Is_WSL_Platform ()) {
Clipboard = new WSLClipboard ();
} else {
Clipboard = new CursesClipboard ();
}
}
}

View File

@@ -1102,7 +1102,11 @@ namespace Terminal.Gui {
} else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
Clipboard = new MacOSXClipboard ();
} else {
Clipboard = new CursesClipboard ();
if (CursesDriver.Is_WSL_Platform ()) {
Clipboard = new WSLClipboard ();
}else{
Clipboard = new CursesClipboard ();
}
}
}

View File

@@ -1640,11 +1640,13 @@ namespace Terminal.Gui {
}
}
class WindowsClipboard : IClipboard {
public string GetClipboardData ()
class WindowsClipboard : ClipboardBase {
public override bool IsSupported => IsClipboardFormatAvailable (cfUnicodeText) ? true : false;
protected override string GetClipboardDataImpl ()
{
if (!IsClipboardFormatAvailable (cfUnicodeText))
return null;
//if (!IsClipboardFormatAvailable (cfUnicodeText))
// return null;
try {
if (!OpenClipboard (IntPtr.Zero))
@@ -1678,7 +1680,7 @@ namespace Terminal.Gui {
}
}
public void SetClipboardData (string text)
protected override void SetClipboardDataImpl (string text)
{
OpenClipboard ();

View File

@@ -1,32 +0,0 @@
using NStack;
using System;
namespace Terminal.Gui {
/// <summary>
/// Provides cut, copy, and paste support for the clipboard with OS interaction.
/// </summary>
public static class Clipboard {
static ustring contents;
/// <summary>
/// Get or sets the operation system clipboard, otherwise the contents field.
/// </summary>
public static ustring Contents {
get {
try {
return Application.Driver.Clipboard.GetClipboardData ();
} catch (Exception) {
return contents;
}
}
set {
try {
Application.Driver.Clipboard.SetClipboardData (value.ToString ());
contents = value;
} catch (Exception) {
contents = value;
}
}
}
}
}

View File

@@ -0,0 +1,67 @@
using NStack;
using System;
namespace Terminal.Gui {
/// <summary>
/// Provides cut, copy, and paste support for the clipboard with OS interaction.
/// </summary>
public static class Clipboard {
static ustring contents;
/// <summary>
/// Get or sets the operation system clipboard, otherwise the contents field.
/// </summary>
public static ustring Contents {
get {
try {
return Application.Driver.Clipboard.GetClipboardData ();
} catch (Exception) {
return contents;
}
}
set {
try {
Application.Driver.Clipboard.SetClipboardData (value.ToString ());
contents = value;
} catch (Exception) {
contents = value;
}
}
}
/// <summary>
/// Returns true if the environmental dependencies are in place to interact with the OS clipboard.
/// </summary>
public static bool IsSupported { get; } = Application.Driver.Clipboard.IsSupported;
/// <summary>
/// Gets the operation system clipboard if possible.
/// </summary>
/// <param name="result">Clipboard contents read</param>
/// <returns>true if it was possible to read the OS clipboard.</returns>
public static bool TryGetClipboardData (out string result)
{
if (Application.Driver.Clipboard.TryGetClipboardData (out result)) {
if (contents != result) {
contents = result;
}
return true;
}
return false;
}
/// <summary>
/// Sets the operation system clipboard if possible.
/// </summary>
/// <param name="text"></param>
/// <returns>True if the clipboard content was set successfully.</returns>
public static bool TrySetClipboardData (string text)
{
if (Application.Driver.Clipboard.TrySetClipboardData (text)) {
contents = text;
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Terminal.Gui {
/// <summary>
/// Shared abstract class to enforce rules from the implementation of the <see cref="IClipboard"/> interface.
/// </summary>
public abstract class ClipboardBase : IClipboard {
/// <summary>
/// Returns true if the environmental dependencies are in place to interact with the OS clipboard
/// </summary>
public abstract bool IsSupported { get; }
/// <summary>
/// Get the operation system clipboard.
/// </summary>
/// <exception cref="NotSupportedException">Thrown if it was not possible to read the clipboard contents</exception>
public string GetClipboardData ()
{
try {
return GetClipboardDataImpl ();
} catch (Exception ex) {
throw new NotSupportedException ("Failed to read clipboard.", ex);
}
}
/// <summary>
/// Get the operation system clipboard.
/// </summary>
protected abstract string GetClipboardDataImpl ();
/// <summary>
/// Sets the operation system clipboard.
/// </summary>
/// <param name="text"></param>
/// <exception cref="NotSupportedException">Thrown if it was not possible to set the clipboard contents</exception>
public void SetClipboardData (string text)
{
try {
SetClipboardDataImpl (text);
} catch (Exception ex) {
throw new NotSupportedException ("Failed to write to clipboard.", ex);
}
}
/// <summary>
/// Sets the operation system clipboard.
/// </summary>
/// <param name="text"></param>
protected abstract void SetClipboardDataImpl (string text);
/// <summary>
/// Gets the operation system clipboard if possible.
/// </summary>
/// <param name="result">Clipboard contents read</param>
/// <returns>true if it was possible to read the OS clipboard.</returns>
public bool TryGetClipboardData (out string result)
{
// Don't even try to read because environment is not set up.
if (!IsSupported) {
result = null;
return false;
}
try {
result = GetClipboardDataImpl ();
while (result == null) {
result = GetClipboardDataImpl ();
}
return true;
} catch (Exception) {
result = null;
return false;
}
}
/// <summary>
/// Sets the operation system clipboard if possible.
/// </summary>
/// <param name="text"></param>
/// <returns>True if the clipboard content was set successfully</returns>
public bool TrySetClipboardData (string text)
{
// Don't even try to set because environment is not set up
if (!IsSupported) {
return false;
}
try {
SetClipboardDataImpl (text);
return true;
} catch (Exception) {
return false;
}
}
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Terminal.Gui {
/// <summary>
/// Definition to interact with the OS clipboard.
/// </summary>
public interface IClipboard {
/// <summary>
/// Returns true if the environmental dependencies are in place to interact with the OS clipboard.
/// </summary>
bool IsSupported { get; }
/// <summary>
/// Get the operation system clipboard.
/// </summary>
/// <exception cref="NotSupportedException">Thrown if it was not possible to read the clipboard contents.</exception>
string GetClipboardData ();
/// <summary>
/// Gets the operation system clipboard if possible.
/// </summary>
/// <param name="result">Clipboard contents read</param>
/// <returns>true if it was possible to read the OS clipboard.</returns>
bool TryGetClipboardData (out string result);
/// <summary>
/// Sets the operation system clipboard.
/// </summary>
/// <param name="text"></param>
/// <exception cref="NotSupportedException">Thrown if it was not possible to set the clipboard contents.</exception>
void SetClipboardData (string text);
/// <summary>
/// Sets the operation system clipboard if possible.
/// </summary>
/// <param name="text"></param>
/// <returns>True if the clipboard content was set successfully.</returns>
bool TrySetClipboardData (string text);
}
}

View File

@@ -1178,20 +1178,4 @@ namespace Terminal.Gui {
/// <returns>The current attribute.</returns>
public abstract Attribute GetAttribute ();
}
/// <summary>
/// Definition to interact with the OS clipboard.
/// </summary>
public interface IClipboard {
/// <summary>
/// Sets the operation system clipboard.
/// </summary>
/// <param name="text"></param>
void SetClipboardData (string text);
/// <summary>
/// Get the operation system clipboard.
/// </summary>
/// <returns></returns>
string GetClipboardData ();
}
}

View File

@@ -635,19 +635,27 @@ namespace UICatalog {
private static void OpenUrl (string url)
{
try {
Process.Start (url);
} catch {
// hack because of this: https://github.com/dotnet/corefx/issues/10361
if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) {
url = url.Replace ("&", "^&");
Process.Start (new ProcessStartInfo ("cmd", $"/c start {url}") { CreateNoWindow = true });
} else if (RuntimeInformation.IsOSPlatform (OSPlatform.Linux)) {
Process.Start ("xdg-open", url);
using (var process = new Process {
StartInfo = new ProcessStartInfo {
FileName = "xdg-open",
Arguments = url,
RedirectStandardError = true,
RedirectStandardOutput = true,
CreateNoWindow = true,
UseShellExecute = false
}
}) {
process.Start ();
}
} else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
Process.Start ("open", url);
} else {
throw;
}
} catch {
throw;
}
}
}

View File

@@ -1,4 +1,6 @@
using Xunit;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Xunit;
namespace Terminal.Gui.Core {
public class ClipboardTests {
@@ -9,9 +11,321 @@ namespace Terminal.Gui.Core {
var clipText = "This is a clipboard unit test.";
Clipboard.Contents = clipText;
Application.Iteration += () => Application.RequestStop ();
Application.Run ();
Assert.Equal (clipText, Clipboard.Contents);
Application.Shutdown ();
}
[Fact]
public void IsSupported_Get ()
{
Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
if (Clipboard.IsSupported) {
Assert.True (Clipboard.IsSupported);
} else {
Assert.False (Clipboard.IsSupported);
}
Application.Shutdown ();
}
[Fact]
public void TryGetClipboardData_Gets_From_OS_Clipboard ()
{
Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var clipText = "Trying to get from the OS clipboard.";
Clipboard.Contents = clipText;
Application.Iteration += () => Application.RequestStop ();
Application.Run ();
if (Clipboard.IsSupported) {
Assert.True (Clipboard.TryGetClipboardData (out string result));
Assert.Equal (clipText, result);
} else {
Assert.False (Clipboard.TryGetClipboardData (out string result));
Assert.NotEqual (clipText, result);
}
Application.Shutdown ();
}
[Fact]
public void TrySetClipboardData_Sets_The_OS_Clipboard ()
{
Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var clipText = "Trying to set the OS clipboard.";
if (Clipboard.IsSupported) {
Assert.True (Clipboard.TrySetClipboardData (clipText));
} else {
Assert.False (Clipboard.TrySetClipboardData (clipText));
}
Application.Iteration += () => Application.RequestStop ();
Application.Run ();
if (Clipboard.IsSupported) {
Assert.Equal (clipText, Clipboard.Contents);
} else {
Assert.NotEqual (clipText, Clipboard.Contents);
}
Application.Shutdown ();
}
[Fact]
public void Contents_Gets_From_OS_Clipboard ()
{
Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var clipText = "This is a clipboard unit test to get clipboard from OS.";
var exit = false;
Application.Iteration += () => {
if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) {
// using (Process clipExe = new Process {
// StartInfo = new ProcessStartInfo {
// RedirectStandardInput = true,
// FileName = "clip"
// }
// }) {
// clipExe.Start ();
// clipExe.StandardInput.Write (clipText);
// clipExe.StandardInput.Close ();
// var result = clipExe.WaitForExit (500);
// if (result) {
// clipExe.WaitForExit ();
// }
// }
using (Process pwsh = new Process {
StartInfo = new ProcessStartInfo {
FileName = "powershell",
Arguments = $"-command \"Set-Clipboard -Value \\\"{clipText}\\\"\""
}
}) {
pwsh.Start ();
pwsh.WaitForExit ();
}
} else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
using (Process copy = new Process {
StartInfo = new ProcessStartInfo {
RedirectStandardInput = true,
FileName = "pbcopy"
}
}) {
copy.Start ();
copy.StandardInput.Write (clipText);
copy.StandardInput.Close ();
copy.WaitForExit ();
}
} else if (RuntimeInformation.IsOSPlatform (OSPlatform.Linux)) {
if (Is_WSL_Platform ()) {
try {
using (Process bash = new Process {
StartInfo = new ProcessStartInfo {
FileName = "powershell.exe",
Arguments = $"-noprofile -command \"Set-Clipboard -Value \\\"{clipText}\\\"\""
}
}) {
bash.Start ();
bash.WaitForExit ();
}
//using (Process clipExe = new Process {
// StartInfo = new ProcessStartInfo {
// RedirectStandardInput = true,
// FileName = "clip.exe"
// }
//}) {
// clipExe.Start ();
// clipExe.StandardInput.Write (clipText);
// clipExe.StandardInput.Close ();
// clipExe.WaitForExit ();
// //var result = clipExe.WaitForExit (500);
// //if (result) {
// // clipExe.WaitForExit ();
// //}
//}
} catch {
exit = true;
}
Application.RequestStop ();
return;
}
if (exit = xclipExists () == false) {
// xclip doesn't exist then exit.
Application.RequestStop ();
return;
}
using (Process bash = new Process {
StartInfo = new ProcessStartInfo {
FileName = "bash",
Arguments = $"-c \"xclip -sel clip -i\"",
RedirectStandardInput = true,
}
}) {
bash.Start ();
bash.StandardInput.Write (clipText);
bash.StandardInput.Close ();
bash.WaitForExit ();
}
}
Application.RequestStop ();
};
Application.Run ();
if (!exit) {
Assert.Equal (clipText, Clipboard.Contents);
}
Application.Shutdown ();
}
[Fact]
public void Contents_Sets_The_OS_Clipboard ()
{
Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
var clipText = "This is a clipboard unit test to set the OS clipboard.";
var clipReadText = "";
var exit = false;
Application.Iteration += () => {
Clipboard.Contents = clipText;
if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) {
using (Process pwsh = new Process {
StartInfo = new ProcessStartInfo {
RedirectStandardOutput = true,
FileName = "powershell.exe",
Arguments = "-noprofile -command \"Get-Clipboard\""
}
}) {
pwsh.Start ();
clipReadText = pwsh.StandardOutput.ReadToEnd ().TrimEnd ();
pwsh.StandardOutput.Close ();
pwsh.WaitForExit ();
}
} else if (RuntimeInformation.IsOSPlatform (OSPlatform.OSX)) {
using (Process paste = new Process {
StartInfo = new ProcessStartInfo {
RedirectStandardOutput = true,
FileName = "pbpaste"
}
}) {
paste.Start ();
clipReadText = paste.StandardOutput.ReadToEnd ();
paste.StandardOutput.Close ();
paste.WaitForExit ();
}
} else if (RuntimeInformation.IsOSPlatform (OSPlatform.Linux)) {
if (Is_WSL_Platform ()) {
try {
using (Process bash = new Process {
StartInfo = new ProcessStartInfo {
RedirectStandardOutput = true,
FileName = "powershell.exe",
Arguments = "-noprofile -command \"Get-Clipboard\""
}
}) {
bash.Start ();
clipReadText = bash.StandardOutput.ReadToEnd ();
bash.StandardOutput.Close ();
bash.WaitForExit ();
}
} catch {
exit = true;
}
Application.RequestStop ();
}
if (exit = xclipExists () == false) {
// xclip doesn't exist then exit.
Application.RequestStop ();
}
using (Process bash = new Process {
StartInfo = new ProcessStartInfo {
RedirectStandardOutput = true,
FileName = "bash",
Arguments = $"-c \"xclip -sel clip -o\""
}
}) {
bash.Start ();
clipReadText = bash.StandardOutput.ReadToEnd ();
bash.StandardOutput.Close ();
bash.WaitForExit ();
}
}
Application.RequestStop ();
};
Application.Run ();
if (!exit) {
Assert.Equal (clipText, clipReadText);
}
Application.Shutdown ();
}
bool Is_WSL_Platform ()
{
using (Process bash = new Process {
StartInfo = new ProcessStartInfo {
FileName = "bash",
Arguments = $"-c \"uname -a\"",
RedirectStandardOutput = true,
}
}) {
bash.Start ();
var result = bash.StandardOutput.ReadToEnd ();
var isWSL = false;
if (result.Contains ("microsoft") && result.Contains ("WSL")) {
isWSL = true;
}
bash.StandardOutput.Close ();
bash.WaitForExit ();
return isWSL;
}
}
bool xclipExists ()
{
try {
using (Process bash = new Process {
StartInfo = new ProcessStartInfo {
FileName = "bash",
Arguments = $"-c \"which xclip\"",
RedirectStandardOutput = true,
}
}) {
bash.Start ();
bool exist = bash.StandardOutput.ReadToEnd ().TrimEnd () != "";
bash.StandardOutput.Close ();
bash.WaitForExit ();
if (exist) {
return true;
}
}
return false;
} catch (System.Exception) {
return false;
}
}
}
}