mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
Merge pull request #1319 from BDisp/clipboard-linux-fix
Fixes #1318. Ensures that the OS clipboard is always sets.
This commit is contained in:
@@ -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 ();
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ();
|
||||
|
||||
|
||||
@@ -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
Terminal.Gui/Core/Clipboard/Clipboard.cs
Normal file
67
Terminal.Gui/Core/Clipboard/Clipboard.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
100
Terminal.Gui/Core/Clipboard/ClipboardBase.cs
Normal file
100
Terminal.Gui/Core/Clipboard/ClipboardBase.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
44
Terminal.Gui/Core/Clipboard/IClipboard.cs
Normal file
44
Terminal.Gui/Core/Clipboard/IClipboard.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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 ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user