Browse Source

Implemented the ClipboardBase abstract class as suggested by @tznind.

BDisp 4 years ago
parent
commit
87271cc477

+ 99 - 8
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,8 +1069,19 @@ namespace Terminal.Gui {
 		}
 	}
 
-	class CursesClipboard : IClipboard {
-		public string GetClipboardData ()
+	class CursesClipboard : ClipboardBase {
+		public override bool IsSupported => CheckSupport ();
+
+		bool CheckSupport ()
+		{
+			var result = BashRunner.Run ("which xclip");
+			if (string.IsNullOrEmpty (result) || result.Contains ("not found")) {
+				return false;
+			}
+			return true;
+		}
+
+		protected override string GetClipboardDataImpl ()
 		{
 			var tempFileName = System.IO.Path.GetTempFileName ();
 			try {
@@ -1069,7 +1093,7 @@ namespace Terminal.Gui {
 			}
 		}
 
-		public void SetClipboardData (string text)
+		protected override void SetClipboardDataImpl (string text)
 		{
 			// var tempFileName = System.IO.Path.GetTempFileName ();
 			// System.IO.File.WriteAllText (tempFileName, text);
@@ -1083,7 +1107,7 @@ namespace Terminal.Gui {
 			try {
 				BashRunner.Run ("xclip -selection clipboard -i", false, text);
 			} catch (Exception ex) {
-				throw new Exception (ex.Message);
+				throw new NotSupportedException ("Write to clipboard failed", ex);
 			}
 		}
 	}
@@ -1156,7 +1180,7 @@ namespace Terminal.Gui {
 		}
 	}
 
-	class MacOSXClipboard : IClipboard {
+	class MacOSXClipboard : ClipboardBase {
 		IntPtr nsString = objc_getClass ("NSString");
 		IntPtr nsPasteboard = objc_getClass ("NSPasteboard");
 		IntPtr utfTextType;
@@ -1170,6 +1194,30 @@ 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 (!FileExists (result)) {
+				return false;
+			}
+			result = BashRunner.Run ("which pbpaste");
+			if (!FileExists (result)) {
+				return false;
+			}
+
+			bool FileExists (string value)
+			{
+				if (string.IsNullOrEmpty (value) || value.Contains ("not found")) {
+					return false;
+				}
+				return true;
+			}
+
+			return true;
+		}
+
 		public MacOSXClipboard ()
 		{
 			utfTextType = objc_msgSend (objc_msgSend (nsString, allocRegister), initWithUtf8Register, "public.utf8-plain-text");
@@ -1177,14 +1225,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 {
@@ -1216,4 +1264,47 @@ 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");
+			if (string.IsNullOrEmpty (result) || result.Contains ("not found")) {
+				return false;
+			}
+			return true;
+		}
+
+		protected override string GetClipboardDataImpl ()
+		{
+			using (var powershell = new System.Diagnostics.Process {
+				StartInfo = new System.Diagnostics.ProcessStartInfo {
+					RedirectStandardOutput = true,
+					FileName = "powershell.exe",
+					Arguments = "-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 = $"-command \"Set-Clipboard -Value \\\"{text}\\\"\""
+				}
+			}) {
+				powershell.Start ();
+				powershell.WaitForExit ();
+			}
+		}
+	}
 }

+ 5 - 1
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 ();
+				}
 			}
 		}
 

+ 5 - 1
Terminal.Gui/ConsoleDrivers/NetDriver.cs

@@ -1100,7 +1100,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 ();
+				}
 			}
 		}
 

+ 7 - 5
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 ();
 

+ 0 - 32
Terminal.Gui/Core/Clipboard.cs

@@ -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;
-				}
-			}
-		}
-	}
-}

+ 67 - 0
Terminal.Gui/Core/Clipboard/Clipboard.cs

@@ -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;
+		}
+	}
+}

+ 100 - 0
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 {
+	/// <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;
+			}
+		}
+	}
+}

+ 44 - 0
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 {
+	/// <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);
+	}
+}

+ 0 - 16
Terminal.Gui/Core/ConsoleDriver.cs

@@ -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 ();
-	}
 }

+ 108 - 0
UnitTests/ClipboardTests.cs

@@ -11,6 +11,56 @@ 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)));
+
+			Assert.True (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 ();
+
+			Assert.True (Clipboard.TryGetClipboardData (out string result));
+			Assert.Equal (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.";
+			Assert.True (Clipboard.TrySetClipboardData (clipText));
+
+			Application.Iteration += () => Application.RequestStop ();
+
+			Application.Run ();
+
 			Assert.Equal (clipText, Clipboard.Contents);
 
 			Application.Shutdown ();
@@ -63,9 +113,27 @@ namespace Terminal.Gui.Core {
 						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 = $"-command \"Set-Clipboard -Value \\\"{clipText}\\\"\""
+								}
+							}) {
+								bash.Start ();
+								bash.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 {
@@ -132,6 +200,25 @@ namespace Terminal.Gui.Core {
 						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 = "-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 ();
@@ -163,6 +250,27 @@ namespace Terminal.Gui.Core {
 			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 ()
 		{
 			using (Process bash = new Process {