Skip to content

Fix ascii scrap on sdl2 backend, docs/tests fixes #3473

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 8 additions & 32 deletions docs/reST/ref/scrap.rst
Original file line number Diff line number Diff line change
Expand Up @@ -77,43 +77,21 @@ data (MIME) types are defined and registered:

::

pygame string
constant value description
--------------------------------------------------
SCRAP_TEXT "text/plain" plain text
SCRAP_BMP "image/bmp" BMP encoded image data
SCRAP_PBM "image/pbm" PBM encoded image data
SCRAP_PPM "image/ppm" PPM encoded image data

``pygame.SCRAP_PPM``, ``pygame.SCRAP_PBM`` and ``pygame.SCRAP_BMP`` are
suitable for surface buffers to be shared with other applications.
``pygame.SCRAP_TEXT`` is an alias for the plain text clipboard type.

Depending on the platform, additional types are automatically registered when
data is placed into the clipboard to guarantee a consistent sharing behaviour
with other applications. The following listed types can be used as strings to
be passed to the respective :mod:`pygame.scrap` module functions.
"text/plain" Plain text (also accessible via the SCRAP_TEXT constant)
"text/plain;charset=utf-8" UTF-8 encoded text

For **Windows** platforms, these additional types are supported automatically
and resolve to their internal definitions:

::

"text/plain;charset=utf-8" UTF-8 encoded text
"image/bmp" BMP encoded image data (also accessible via the SCRAP_BMP constant)
"audio/wav" WAV encoded audio
"image/tiff" TIFF encoded image data

For **X11** platforms, these additional types are supported automatically and
resolve to their internal definitions:

::

"text/plain;charset=utf-8" UTF-8 encoded text
"UTF8_STRING" UTF-8 encoded text
"COMPOUND_TEXT" COMPOUND text

User defined types can be used, but the data might not be accessible by other
applications unless they know what data type to look for.
User defined types can be used on **Windows**, but the data might not be
accessible by other applications unless they know what data type to look for.
Example: Data placed into the clipboard by
``pygame.scrap.put("my_data_type", byte_data)`` can only be accessed by
applications which query the clipboard for the ``"my_data_type"`` data type.
Expand All @@ -125,9 +103,7 @@ For an example of how the scrap module works refer to the examples page
.. versionaddedold:: 1.8

.. note::
The scrap module is currently only supported for Windows, X11 and Mac OS X.
On Mac OS X only text works at the moment - other types may be supported in
future releases.
Non-text data is only supported on Windows. On other platforms only text is supported.

.. function:: init

Expand Down Expand Up @@ -221,8 +197,8 @@ For an example of how the scrap module works refer to the examples page
Places data for a given clipboard type into the clipboard. The data must
be a string buffer. The type is a string identifying the type of data to be
placed into the clipboard. This can be one of the predefined
``pygame.SCRAP_PBM``, ``pygame.SCRAP_PPM``, ``pygame.SCRAP_BMP`` or
``pygame.SCRAP_TEXT`` values or a user defined string identifier.
``pygame.SCRAP_BMP`` or ``pygame.SCRAP_TEXT`` values or a user defined
string identifier.

:param string type: type identifier of the data to be placed into the
clipboard
Expand Down
39 changes: 31 additions & 8 deletions src_c/scrap_sdl2.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,30 @@

#define PYGAME_SCRAP_FREE_STRING 1

char *pygame_scrap_plaintext_type = "text/plain;charset=utf-8";
char *pygame_scrap_plaintext_type = "text/plain";
char *pygame_scrap_utf8text_type = "text/plain;charset=utf-8";
char **pygame_scrap_types;

int
pygame_scrap_contains(char *type)
{
return (strcmp(type, pygame_scrap_plaintext_type) == 0) &&
return (strcmp(type, pygame_scrap_plaintext_type) == 0 ||
strcmp(type, pygame_scrap_utf8text_type) == 0) &&
SDL_HasClipboardText();
}

bool
_pg_is_ascii_str(const char *str)
{
while (*str) {
if ((unsigned char)*str > 127) {
return false;
}
str++;
}
return true;
}

char *
pygame_scrap_get(char *type, size_t *count)
{
Expand All @@ -28,11 +42,18 @@ pygame_scrap_get(char *type, size_t *count)
return RAISE(pgExc_SDLError, "scrap system not initialized.");
}

if (strcmp(type, pygame_scrap_plaintext_type) == 0) {
int is_ascii = strcmp(type, pygame_scrap_plaintext_type) == 0;
if (is_ascii || strcmp(type, pygame_scrap_utf8text_type) == 0) {
clipboard = SDL_GetClipboardText();
if (clipboard != NULL) {
*count = strlen(clipboard);
retval = strdup(clipboard);
if (is_ascii && !_pg_is_ascii_str(clipboard)) {
*count = 0;
retval = NULL;
}
else {
*count = strlen(clipboard);
retval = strdup(clipboard);
}
SDL_free(clipboard);
return retval;
}
Expand All @@ -56,13 +77,14 @@ pygame_scrap_init(void)
{
SDL_Init(SDL_INIT_VIDEO);

pygame_scrap_types = malloc(sizeof(char *) * 2);
pygame_scrap_types = malloc(sizeof(char *) * 3);
if (!pygame_scrap_types) {
return 0;
}

pygame_scrap_types[0] = pygame_scrap_plaintext_type;
pygame_scrap_types[1] = NULL;
pygame_scrap_types[1] = pygame_scrap_utf8text_type;
pygame_scrap_types[2] = NULL;

_scrapinitialized = 1;
return _scrapinitialized;
Expand All @@ -82,7 +104,8 @@ pygame_scrap_put(char *type, Py_ssize_t srclen, char *src)
return 0;
}

if (strcmp(type, pygame_scrap_plaintext_type) == 0) {
if (strcmp(type, pygame_scrap_plaintext_type) == 0 ||
strcmp(type, pygame_scrap_utf8text_type) == 0) {
if (SDL_SetClipboardText(src) == 0) {
return 1;
}
Expand Down
67 changes: 13 additions & 54 deletions test/scrap_test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import platform
import sys
import unittest

Expand Down Expand Up @@ -51,6 +52,10 @@ def todo_test_get(self):
"""Ensures get works as expected."""
self.fail()

@unittest.skipIf(
platform.system() != "Windows",
"scrap features are broken on non windows platforms",
)
def test_get__owned_empty_type(self):
"""Ensures get works when there is no data of the requested type
in the clipboard and the clipboard is owned by the pygame application.
Expand Down Expand Up @@ -95,7 +100,10 @@ def test_put__text(self):

self.assertEqual(scrap.get(pygame.SCRAP_TEXT), b"Another String")

@unittest.skipIf("pygame.image" not in sys.modules, "requires pygame.image module")
@unittest.skipIf(
platform.system() != "Windows",
"scrap features are broken on non windows platforms",
)
def test_put__bmp_image(self):
"""Ensures put can place a BMP image into the clipboard."""
sf = pygame.image.load(trunk_relative_path("examples/data/asprite.bmp"))
Expand All @@ -104,6 +112,10 @@ def test_put__bmp_image(self):

self.assertEqual(scrap.get(pygame.SCRAP_BMP), expected_bytes)

@unittest.skipIf(
platform.system() != "Windows",
"scrap features are broken on non windows platforms",
)
def test_put(self):
"""Ensures put can place data into the clipboard
when using a user defined type identifier.
Expand Down Expand Up @@ -205,59 +217,6 @@ def test_lost__not_owned(self):
self.assertTrue(lost)


class X11InteractiveTest(unittest.TestCase):
__tags__ = ["ignore", "subprocess_ignore"]
try:
pygame.display.init()
except Exception:
pass
else:
if pygame.display.get_driver() == "x11":
__tags__ = ["interactive"]
pygame.display.quit()

def test_issue_223(self):
"""PATCH: pygame.scrap on X11, fix copying into PRIMARY selection

Copying into theX11 PRIMARY selection (mouse copy/paste) would not
work due to a confusion between content type and clipboard type.

"""

from pygame import display, event, freetype
from pygame.locals import KEYDOWN, QUIT, SCRAP_SELECTION, SCRAP_TEXT, K_y

success = False
freetype.init()
font = freetype.Font(None, 24)
display.init()
display.set_caption("Interactive X11 Paste Test")
screen = display.set_mode((600, 200))
screen.fill(pygame.Color("white"))
text = "Scrap put() succeeded."
msg = (
"Some text has been placed into the X11 clipboard."
" Please click the center mouse button in an open"
" text window to retrieve it."
'\n\nDid you get "{}"? (y/n)'
).format(text)
word_wrap(screen, msg, font, 6)
display.flip()
event.pump()
scrap.init()
scrap.set_mode(SCRAP_SELECTION)
scrap.put(SCRAP_TEXT, text.encode("UTF-8"))
while True:
e = event.wait()
if e.type == QUIT:
break
if e.type == KEYDOWN:
success = e.key == K_y
break
pygame.display.quit()
self.assertTrue(success)


def word_wrap(surf, text, font, margin=0, color=(0, 0, 0)):
font.origin = True
surf_width, surf_height = surf.get_size()
Expand Down
Loading