diff --git a/ClassicalSharp/2D/Widgets/Chat/TextInputWidget.Handlers.cs b/ClassicalSharp/2D/Widgets/Chat/TextInputWidget.Handlers.cs index 2707c21f5..f271c8a86 100644 --- a/ClassicalSharp/2D/Widgets/Chat/TextInputWidget.Handlers.cs +++ b/ClassicalSharp/2D/Widgets/Chat/TextInputWidget.Handlers.cs @@ -53,7 +53,7 @@ namespace ClassicalSharp.Gui { string part = new String( value, start, pos + 1 - start ); List matches = new List(); - game.Chat.Add( null, MessageType.ClientStatus5 ); + game.Chat.Add( null, MessageType.ClientStatus5 ); bool extList = game.Network.UsingExtPlayerList; CpeListInfo[] info = game.CpePlayersList; @@ -222,23 +222,37 @@ namespace ClassicalSharp.Gui { bool OtherKey( Key key ) { if( key == Key.V && chatInputText.Length < TotalChars ) { - string text = game.window.ClipboardText; + string text = null; + try { + text = game.window.ClipboardText; + } catch( Exception ex ) { + ErrorHandler.LogError( "Paste from clipboard", ex ); + const string warning = "&cError while trying to paste from clipboard."; + game.Chat.Add( warning, MessageType.ClientStatus4 ); + return true; + } + if( String.IsNullOrEmpty( text ) ) return true; game.Chat.Add( null, MessageType.ClientStatus4 ); for( int i = 0; i < text.Length; i++ ) { - if( !IsValidInputChar( text[i] ) ) { - const string warning = "&eClipboard contained some characters that can't be sent."; - game.Chat.Add( warning, MessageType.ClientStatus4 ); - text = RemoveInvalidChars( text ); - break; - } + if( IsValidInputChar( text[i] ) ) continue; + const string warning = "&eClipboard contained some characters that can't be sent."; + game.Chat.Add( warning, MessageType.ClientStatus4 ); + text = RemoveInvalidChars( text ); + break; } AppendText( text ); return true; } else if( key == Key.C ) { - if( !chatInputText.Empty ) + if( chatInputText.Empty ) return true; + try { game.window.ClipboardText = chatInputText.ToString(); + } catch( Exception ex ) { + ErrorHandler.LogError( "Copy to clipboard", ex ); + const string warning = "&cError while trying to copy to clipboard."; + game.Chat.Add( warning, MessageType.ClientStatus4 ); + } return true; } return false; diff --git a/ClassicalSharp/Platform/IPlatformWindow.cs b/ClassicalSharp/Platform/IPlatformWindow.cs index 7aac1172e..21f6063a9 100644 --- a/ClassicalSharp/Platform/IPlatformWindow.cs +++ b/ClassicalSharp/Platform/IPlatformWindow.cs @@ -82,8 +82,18 @@ namespace ClassicalSharp { // TODO: retry when clipboard returns null. public string ClipboardText { - get { return Clipboard.GetText(); } - set { Clipboard.SetText( value ); } + get { + if ( OpenTK.Configuration.RunningOnMacOS ) + return GetClipboardText(); + else + return Clipboard.GetText(); + } + set { + if ( OpenTK.Configuration.RunningOnMacOS ) + SetClipboardText( value ); + else + Clipboard.SetText( value ); + } } } } diff --git a/OpenTK/Platform/MacOS/CarbonBindings/CarbonAPI.cs b/OpenTK/Platform/MacOS/CarbonBindings/CarbonAPI.cs index fe8258cd0..e6940e6f6 100644 --- a/OpenTK/Platform/MacOS/CarbonBindings/CarbonAPI.cs +++ b/OpenTK/Platform/MacOS/CarbonBindings/CarbonAPI.cs @@ -779,6 +779,29 @@ namespace OpenTK.Platform.MacOS.Carbon public extern static OSStatus QDBeginCGContext( IntPtr port, ref IntPtr context ); [DllImport(carbon)] public extern static OSStatus QDEndCGContext( IntPtr port, ref IntPtr context ); + #endregion + #region --- Clipboard --- + + [DllImport (carbon)] + public static extern IntPtr CFDataCreate(IntPtr allocator, IntPtr buf, Int32 length); + [DllImport (carbon)] + public static extern IntPtr CFDataGetBytePtr(IntPtr data); + [DllImport (carbon)] + public static extern int PasteboardSynchronize(IntPtr pbref); + + [DllImport (carbon)] + public static extern OSStatus PasteboardClear(IntPtr pbref); + [DllImport (carbon)] + public static extern OSStatus PasteboardCreate(IntPtr str, out IntPtr pbref); + [DllImport (carbon)] + public static extern OSStatus PasteboardCopyItemFlavorData(IntPtr pbref, UInt32 itemid, IntPtr key, out IntPtr data); + [DllImport (carbon)] + public static extern OSStatus PasteboardGetItemCount(IntPtr pbref, out UInt32 count); + [DllImport (carbon)] + public static extern OSStatus PasteboardGetItemIdentifier(IntPtr pbref, UInt32 itemindex, out UInt32 itemid); + [DllImport (carbon)] + public static extern OSStatus PasteboardPutItemFlavor(IntPtr pbref, UInt32 itemid, IntPtr key, IntPtr data, UInt32 flags); + #endregion [DllImport(carbon)] diff --git a/OpenTK/Platform/MacOS/CarbonGLNative.cs b/OpenTK/Platform/MacOS/CarbonGLNative.cs index 038325f4c..c33c90ef4 100644 --- a/OpenTK/Platform/MacOS/CarbonGLNative.cs +++ b/OpenTK/Platform/MacOS/CarbonGLNative.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; +using System.Runtime.InteropServices; using OpenTK.Graphics; using OpenTK.Platform.MacOS.Carbon; using OpenTK.Input; @@ -556,9 +557,63 @@ namespace OpenTK.Platform.MacOS #region INativeWindow Members - public string GetClipboardText() { return ""; } + IntPtr pbStr, utf16, utf8; + public string GetClipboardText() { + IntPtr pbRef = GetPasteboard(); + API.PasteboardSynchronize( pbRef ); + + uint itemCount; + OSStatus err = API.PasteboardGetItemCount( pbRef, out itemCount ); + if( err != OSStatus.NoError ) + throw new MacOSException( err, "Getting item count from Pasteboard." ); + if( itemCount < 1 ) return ""; + + uint itemID; + err = API.PasteboardGetItemIdentifier( pbRef, 1, out itemID ); + if( err != OSStatus.NoError ) + throw new MacOSException( err, "Getting item identifier from Pasteboard." ); + + IntPtr outData; + if ( (err = API.PasteboardCopyItemFlavorData( pbRef, itemID, utf16, out outData )) == OSStatus.NoError ) { + IntPtr ptr = API.CFDataGetBytePtr( outData ); + if( ptr == IntPtr.Zero ) + throw new InvalidOperationException( "CFDataGetBytePtr() returned null pointer." ); + return Marshal.PtrToStringUni( ptr ); + } + // TODO: UTF 8 + return ""; + } - public void SetClipboardText( string value ) { } + public void SetClipboardText( string value ) { + IntPtr pbRef = GetPasteboard(); + OSStatus err = API.PasteboardClear( pbRef ); + if( err != OSStatus.NoError ) + throw new MacOSException( err, "Cleaing Pasteboard." ); + API.PasteboardSynchronize( pbRef ); + + IntPtr ptr = Marshal.StringToHGlobalUni( value ); + IntPtr cfData = API.CFDataCreate( IntPtr.Zero, ptr, (value.Length + 1) * 2 ); + if( cfData == IntPtr.Zero ) + throw new InvalidOperationException( "CFDataCreate() returned null pointer." ); + + API.PasteboardPutItemFlavor( pbRef, 1, utf16, cfData, 0 ); + Marshal.FreeHGlobal( ptr ); + } + + IntPtr GetPasteboard() { + if( pbStr == IntPtr.Zero ) { + pbStr = CF.CFSTR( "com.apple.pasteboard.clipboard" ); + utf16 = CF.CFSTR( "public.utf16-plain-text" ); + utf8 = CF.CFSTR( "public.utf8-plain-text" ); + } + + IntPtr pbRef; + OSStatus err = API.PasteboardCreate( pbStr, out pbRef ); + if( err != OSStatus.NoError ) + throw new MacOSException( err, "Creating Pasteboard reference." ); + API.PasteboardSynchronize( pbRef ); + return pbRef; + } public void ProcessEvents() {