Implement clipboard on Windows

This commit is contained in:
PJB3005
2019-03-23 20:08:44 +01:00
parent a74f8c9787
commit ae21b98d9a
5 changed files with 230 additions and 5 deletions

View File

@@ -172,11 +172,13 @@ namespace SS14.Client
}
else
{
#if LINUX
#if LINUX
IoCManager.Register<IClipboardManager, ClipboardManagerLinux>();
#else
#elif WINDOWS
IoCManager.Register<IClipboardManager, ClipboardManagerWindows>();
#else
IoCManager.Register<IClipboardManager, ClipboardManagerUnsupported>();
#endif
#endif
}
IoCManager.BuildGraph();

View File

@@ -509,6 +509,11 @@ namespace SS14.Client.Graphics.Clyde
_shutdownAudio();
}
public IntPtr GetNativeWindowHandle()
{
return _window.WindowInfo.Handle;
}
[StructLayout(LayoutKind.Sequential)]
[PublicAPI]
private readonly struct Vertex2D

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using SixLabors.ImageSharp;
@@ -40,6 +40,12 @@ namespace SS14.Client.Interfaces.Graphics
AudioStream LoadAudioWav(Stream stream);
IClydeAudioSource CreateAudioSource(AudioStream stream);
/// <summary>
/// Gets the platform specific window handle exposed by OpenTK.
/// Seriously please avoid using this unless absolutely necessary.
/// </summary>
IntPtr GetNativeWindowHandle();
}
internal interface IClydeAudioSource : IDisposable

View File

@@ -214,6 +214,7 @@
<Compile Include="UserInterface\ClipboardManagerGodot.cs" />
<Compile Include="UserInterface\ClipboardManagerLinux.cs" />
<Compile Include="UserInterface\ClipboardManagerUnsupported.cs" />
<Compile Include="UserInterface\ClipboardManagerWindows.cs" />
<Compile Include="UserInterface\Control.Styling.cs" />
<Compile Include="UserInterface\Controls\BaseButton.cs" />
<Compile Include="UserInterface\Controls\Button.cs" />
@@ -381,4 +382,4 @@
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\MSBuild\SS14.Engine.targets" />
<Target Name="AfterBuild" DependsOnTargets="CopySS14Noise;CopyMiscDependencies" />
</Project>
</Project>

View File

@@ -0,0 +1,211 @@
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using SS14.Client.Interfaces.Graphics;
using SS14.Client.Interfaces.UserInterface;
using SS14.Shared.IoC;
namespace SS14.Client.UserInterface
{
// Yay Windows API!
sealed class ClipboardManagerWindows : IClipboardManager
{
[Dependency]
private readonly IClyde _clyde;
public bool Available => true;
public string NotAvailableReason => "";
public string GetText()
{
var windowHandle = _clyde.GetNativeWindowHandle();
if (!OpenClipboard(windowHandle))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
var count = CountClipboardFormats();
try
{
if (IsClipboardFormatAvailable(CF_UNICODETEXT))
{
var dataHandle = GetClipboardData(CF_UNICODETEXT);
if (dataHandle == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
var ptr = GlobalLock(dataHandle);
if (ptr == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
try
{
var str = Marshal.PtrToStringUni(ptr);
return str;
}
finally
{
GlobalUnlock(dataHandle);
}
}
if (IsClipboardFormatAvailable(CF_TEXT))
{
var dataHandle = GetClipboardData(CF_TEXT);
if (dataHandle == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
var ptr = GlobalLock(dataHandle);
if (ptr == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
try
{
var str = Marshal.PtrToStringAnsi(ptr);
return str;
}
finally
{
GlobalUnlock(dataHandle);
}
}
// Clipboard data isn't available as string, guess we just say it's empty.
return "";
}
finally
{
CloseClipboard();
}
}
public void SetText(string text)
{
void DoSetData(int allocLength, uint format, Func<string, IntPtr> allocHGlobal)
{
// Allocate global data for the clipboard contents.
var alloc = GlobalAlloc(GMEM_MOVEABLE, (UIntPtr)allocLength);
var clipboardSet = false;
try
{
// Copy data into global allocation.
var ptr = GlobalLock(alloc);
if (ptr == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
try
{
var uniPtr = allocHGlobal(text);
try
{
unsafe
{
Buffer.MemoryCopy((void*)uniPtr, (void*)ptr, allocLength, allocLength);
}
}
finally
{
Marshal.FreeHGlobal(uniPtr);
}
}
finally
{
GlobalUnlock(alloc);
}
// Set clipboard to global allocation.
var clip = SetClipboardData(format, alloc);
if (clip == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
clipboardSet = true;
}
finally
{
if (!clipboardSet)
{
// If clipboardSet is false we didn't hand the data off to the clipboard and an error occured.
// In that case, try to avoid a memory leak by freeing the data.
GlobalFree(alloc);
}
}
}
var windowHandle = _clyde.GetNativeWindowHandle();
if (!OpenClipboard(windowHandle))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
EmptyClipboard();
try
{
DoSetData(text.Length * 2 + 1, CF_UNICODETEXT, Marshal.StringToHGlobalUni);
DoSetData(text.Length + 1, CF_TEXT, Marshal.StringToHGlobalAnsi);
}
finally
{
CloseClipboard();
}
}
private const uint CF_UNICODETEXT = 13;
private const uint CF_TEXT = 1;
private const uint GMEM_MOVEABLE = 2;
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GlobalAlloc(uint uFlags, UIntPtr dwBytes);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GlobalFree(IntPtr hMem);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GlobalLock(IntPtr hMem);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GlobalUnlock(IntPtr hMem);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool OpenClipboard(IntPtr hWndNewOwner);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsClipboardFormatAvailable(uint format);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseClipboard();
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EmptyClipboard();
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetClipboardData(uint uFormat, IntPtr hMem);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr GetClipboardData(uint uFormat);
[DllImport("user32.dll", SetLastError = true)]
private static extern int CountClipboardFormats();
}
}