diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index df5c3871f..88b876eba 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -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 (); + //} + } + } } diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs index feccf6126..96087cce8 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/UnixMainLoop.cs @@ -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; }); } diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 246f5af5d..0aa48539f 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -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 (); + } } } diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 3cf2b1765..e70151fd1 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -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 (); + } } } diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index bddf89f14..c08a18768 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -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 (); diff --git a/Terminal.Gui/Core/Clipboard.cs b/Terminal.Gui/Core/Clipboard.cs deleted file mode 100644 index 5e029fe29..000000000 --- a/Terminal.Gui/Core/Clipboard.cs +++ /dev/null @@ -1,32 +0,0 @@ -using NStack; -using System; - -namespace Terminal.Gui { - /// - /// Provides cut, copy, and paste support for the clipboard with OS interaction. - /// - public static class Clipboard { - static ustring contents; - - /// - /// Get or sets the operation system clipboard, otherwise the contents field. - /// - 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; - } - } - } - } -} diff --git a/Terminal.Gui/Core/Clipboard/Clipboard.cs b/Terminal.Gui/Core/Clipboard/Clipboard.cs new file mode 100644 index 000000000..e7d570074 --- /dev/null +++ b/Terminal.Gui/Core/Clipboard/Clipboard.cs @@ -0,0 +1,67 @@ +using NStack; +using System; + +namespace Terminal.Gui { + /// + /// Provides cut, copy, and paste support for the clipboard with OS interaction. + /// + public static class Clipboard { + static ustring contents; + + /// + /// Get or sets the operation system clipboard, otherwise the contents field. + /// + 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; + } + } + } + + /// + /// Returns true if the environmental dependencies are in place to interact with the OS clipboard. + /// + public static bool IsSupported { get; } = Application.Driver.Clipboard.IsSupported; + + /// + /// Gets the operation system clipboard if possible. + /// + /// Clipboard contents read + /// true if it was possible to read the OS clipboard. + public static bool TryGetClipboardData (out string result) + { + if (Application.Driver.Clipboard.TryGetClipboardData (out result)) { + if (contents != result) { + contents = result; + } + return true; + } + return false; + } + + /// + /// Sets the operation system clipboard if possible. + /// + /// + /// True if the clipboard content was set successfully. + public static bool TrySetClipboardData (string text) + { + if (Application.Driver.Clipboard.TrySetClipboardData (text)) { + contents = text; + return true; + } + return false; + } + } +} diff --git a/Terminal.Gui/Core/Clipboard/ClipboardBase.cs b/Terminal.Gui/Core/Clipboard/ClipboardBase.cs new file mode 100644 index 000000000..db61af80f --- /dev/null +++ b/Terminal.Gui/Core/Clipboard/ClipboardBase.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Terminal.Gui { + /// + /// Shared abstract class to enforce rules from the implementation of the interface. + /// + public abstract class ClipboardBase : IClipboard { + /// + /// Returns true if the environmental dependencies are in place to interact with the OS clipboard + /// + public abstract bool IsSupported { get; } + + /// + /// Get the operation system clipboard. + /// + /// Thrown if it was not possible to read the clipboard contents + public string GetClipboardData () + { + try { + return GetClipboardDataImpl (); + } catch (Exception ex) { + throw new NotSupportedException ("Failed to read clipboard.", ex); + } + } + + /// + /// Get the operation system clipboard. + /// + protected abstract string GetClipboardDataImpl (); + + /// + /// Sets the operation system clipboard. + /// + /// + /// Thrown if it was not possible to set the clipboard contents + public void SetClipboardData (string text) + { + try { + SetClipboardDataImpl (text); + } catch (Exception ex) { + throw new NotSupportedException ("Failed to write to clipboard.", ex); + } + } + + /// + /// Sets the operation system clipboard. + /// + /// + protected abstract void SetClipboardDataImpl (string text); + + /// + /// Gets the operation system clipboard if possible. + /// + /// Clipboard contents read + /// true if it was possible to read the OS clipboard. + 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; + } + } + + /// + /// Sets the operation system clipboard if possible. + /// + /// + /// True if the clipboard content was set successfully + 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; + } + } + } +} diff --git a/Terminal.Gui/Core/Clipboard/IClipboard.cs b/Terminal.Gui/Core/Clipboard/IClipboard.cs new file mode 100644 index 000000000..9619d8125 --- /dev/null +++ b/Terminal.Gui/Core/Clipboard/IClipboard.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Terminal.Gui { + /// + /// Definition to interact with the OS clipboard. + /// + public interface IClipboard { + /// + /// Returns true if the environmental dependencies are in place to interact with the OS clipboard. + /// + bool IsSupported { get; } + + /// + /// Get the operation system clipboard. + /// + /// Thrown if it was not possible to read the clipboard contents. + string GetClipboardData (); + + /// + /// Gets the operation system clipboard if possible. + /// + /// Clipboard contents read + /// true if it was possible to read the OS clipboard. + bool TryGetClipboardData (out string result); + + /// + /// Sets the operation system clipboard. + /// + /// + /// Thrown if it was not possible to set the clipboard contents. + void SetClipboardData (string text); + + /// + /// Sets the operation system clipboard if possible. + /// + /// + /// True if the clipboard content was set successfully. + bool TrySetClipboardData (string text); + } +} diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index 9f7594443..86fc62a6f 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -1178,20 +1178,4 @@ namespace Terminal.Gui { /// The current attribute. public abstract Attribute GetAttribute (); } - - /// - /// Definition to interact with the OS clipboard. - /// - public interface IClipboard { - /// - /// Sets the operation system clipboard. - /// - /// - void SetClipboardData (string text); - /// - /// Get the operation system clipboard. - /// - /// - string GetClipboardData (); - } } diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs index 33a1fc725..66f20e687 100644 --- a/UICatalog/UICatalog.cs +++ b/UICatalog/UICatalog.cs @@ -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; } } } diff --git a/UnitTests/ClipboardTests.cs b/UnitTests/ClipboardTests.cs index 3ee4fc3c9..d12aa852a 100644 --- a/UnitTests/ClipboardTests.cs +++ b/UnitTests/ClipboardTests.cs @@ -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; + } + } } }