Repository: kivy/kivy

Issue:
Clipboard (under Windows) fails to copy special symbols (affects for example TextInput)
* Python: 3.10.9
* OS: Windows 10
* Kivy: 2.1.0 & 2.2.1
* Kivy installation method: pip install

The `Clipboard` (at least under Windows) fails to copy strings that have certain symbols, such as 💜 and 💠 and will, if attempting to copy those, result in an empty clipboard instead. Of course text, even with such symbols, should copy into the system clipboard properly. Other processing of such symbols including pasting and reading out work as expected.

**To Reproduce**
```python
from kivy.core.clipboard import Clipboard
text="Hello World"
Clipboard.copy(text)
print(Clipboard.paste()==text)#True

text="Hello 💠 World"
Clipboard.copy(text)
print(Clipboard.paste()==text)#False
```

I'm fairly confident that the exact issue resides within clipboard_winctypes.py in the put method. The `text` variable arrives here well-formed as it should be, but never actually makes it to the system clipboard.
I have verified this by simply replacing the entire put method somewhat lazily with:
```python
import win32clipboard
#...
def put(self, text, mimetype='text/plain'):
	win32clipboard.OpenClipboard()
	win32clipboard.EmptyClipboard()
	win32clipboard.SetClipboardData(win32clipboard.CF_UNICODETEXT, text)
	win32clipboard.CloseClipboard()
```
Which has indeed allowed me to run my Kivy app with text fields that can now copy any of their contents as opposed to just any content without such symbols.

I have not submitted this as a fix and pull request since I'm not aware why exactly the `put` method looks like it does. Someone in the know should be able to make a general fix relatively easily with the provided information though.


Discussion:
Try using sdl2 clipboard. Before any `kivy` imports do:
```python
import os
os.environ['KIVY_CLIPBOARD'] = 'sdl2'
```
> Try using sdl2 clipboard.

...Yeah I could've had the idea to instead try a different provider like that myself. And yes, this works. While I still consider the `ctypes` implementation bugged, at least for me this additional line of code is as good as a fix.
@Xovaryu I agree that `ClipboardWindows` has a bug when special characters are used, but until this bug gets fixed using sdl2 clipboard is an option for your app.
Is anyone working on this? if not I'll try to fix
@p0lygun go for it.

Resolving patch:
diff --git a/kivy/core/clipboard/clipboard_winctypes.py b/kivy/core/clipboard/clipboard_winctypes.py
--- a/kivy/core/clipboard/clipboard_winctypes.py
+++ b/kivy/core/clipboard/clipboard_winctypes.py
@@ -40,8 +40,6 @@ def get(self, mimetype='text/plain'):
         return data
 
     def put(self, text, mimetype='text/plain'):
-        text = text.decode(self._encoding)  # auto converted later
-        text += u'\x00'
 
         SetClipboardData = user32.SetClipboardData
         SetClipboardData.argtypes = [wintypes.UINT, wintypes.HANDLE]
@@ -51,14 +49,23 @@ def put(self, text, mimetype='text/plain'):
         GlobalAlloc.argtypes = [wintypes.UINT, ctypes.c_size_t]
         GlobalAlloc.restype = wintypes.HGLOBAL
 
-        CF_UNICODETEXT = 13
-
         user32.OpenClipboard(user32.GetActiveWindow())
         user32.EmptyClipboard()
-        hCd = GlobalAlloc(0, len(text) * ctypes.sizeof(ctypes.c_wchar))
 
-        # ignore null character for strSource pointer
-        msvcrt.wcscpy_s(c_wchar_p(hCd), len(text), c_wchar_p(text[:-1]))
+        # this allocates memory for the string and returns a handle to it
+        # allocates fixed memory, len + 2 is for the null character
+        # no need to  call GlobalFree here as SetClipboardData will do for you
+        # noqa: E501 see: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setclipboarddata#parameters
+        GMEM_FIXED = 0x0000
+        hCd = GlobalAlloc(GMEM_FIXED, len(text) + 2)
+
+        # copy the string into the allocated memory
+        msvcrt.wcscpy(c_wchar_p(hCd), text)
+
+        # standard clipboard format for unicode text
+        # noqa: E501 see https://learn.microsoft.com/en-us/windows/win32/dataxchg/standard-clipboard-formats#constants
+        CF_UNICODETEXT = 13
+        # set the clipboard data, later used by GetClipboardData()
         SetClipboardData(CF_UNICODETEXT, hCd)
         user32.CloseClipboard()
 


Test patch:
diff --git a/kivy/tests/test_clipboard.py b/kivy/tests/test_clipboard.py
--- a/kivy/tests/test_clipboard.py
+++ b/kivy/tests/test_clipboard.py
@@ -32,7 +32,7 @@ def test_clipboard_copy(self):
             clippy.copy(u"Hello World")
         except:
             self.fail(
-                'Can not get put data to clipboard')
+                'Can not put data to clipboard')
 
     def test_clipboard_copy_paste(self):
         clippy = self._clippy
@@ -40,3 +40,9 @@ def test_clipboard_copy_paste(self):
         clippy.copy(txt1)
         ret = clippy.paste()
         self.assertEqual(txt1, ret)
+
+    def test_clipboard_copy_paste_with_emoji(self):
+        clippy = self._clippy
+        test_emoji_str = 'kivy 😀 😁 🤣 😃 😄 😅 😆 😉 😊 😋 😎 😍 😘 😗'
+        clippy.copy(test_emoji_str)
+        self.assertEqual(test_emoji_str, clippy.paste())