diff options
-rw-r--r-- | bindings/cpp/include/pugl/pugl.hpp | 12 | ||||
-rw-r--r-- | doc/c/clipboards.rst | 14 | ||||
-rw-r--r-- | examples/pugl_clipboard_demo.c | 20 | ||||
-rw-r--r-- | include/pugl/pugl.h | 44 | ||||
-rw-r--r-- | src/mac.m | 61 | ||||
-rw-r--r-- | src/win.c | 38 | ||||
-rw-r--r-- | src/x11.c | 80 | ||||
-rw-r--r-- | src/x11.h | 1 | ||||
-rw-r--r-- | test/test_local_copy_paste.c | 27 | ||||
-rw-r--r-- | test/test_remote_copy_paste.c | 12 |
10 files changed, 207 insertions, 102 deletions
diff --git a/bindings/cpp/include/pugl/pugl.hpp b/bindings/cpp/include/pugl/pugl.hpp index b7f9fcf..18dd015 100644 --- a/bindings/cpp/include/pugl/pugl.hpp +++ b/bindings/cpp/include/pugl/pugl.hpp @@ -138,6 +138,9 @@ using ViewStyleFlag = PuglViewStyleFlag; /// @copydoc PuglViewStyleFlags using ViewStyleFlags = PuglViewStyleFlags; +/// @copydoc PuglClipboard +using Clipboard = PuglClipboard; + /// @copydoc PuglRealizeEvent using RealizeEvent = Event<PUGL_REALIZE, PuglRealizeEvent>; @@ -636,15 +639,16 @@ public: } /// @copydoc puglGetNumClipboardTypes - uint32_t numClipboardTypes() const + uint32_t numClipboardTypes(const Clipboard clipboard) const { - return puglGetNumClipboardTypes(cobj()); + return puglGetNumClipboardTypes(cobj(), clipboard); } /// @copydoc puglGetClipboardType - const char* clipboardType(const uint32_t typeIndex) const + const char* clipboardType(const Clipboard clipboard, + const uint32_t typeIndex) const { - return puglGetClipboardType(cobj(), typeIndex); + return puglGetClipboardType(cobj(), clipboard, typeIndex); } /** diff --git a/doc/c/clipboards.rst b/doc/c/clipboards.rst index 44f2960..c7b6ef4 100644 --- a/doc/c/clipboards.rst +++ b/doc/c/clipboards.rst @@ -66,10 +66,11 @@ When handling this event, static void onDataOffer(PuglView* view, const PuglEventDataOffer* event) { - size_t numTypes = puglGetNumClipboardTypes(view, clipboard); + PuglClipboard clipboard = event->clipboard; + size_t numTypes = puglGetNumClipboardTypes(view, clipboard); for (uint32_t t = 0; t < numTypes; ++t) { - const char* type = puglGetClipboardType(view, t); + const char* type = puglGetClipboardType(view, clipboard, t); printf("Offered type: %s\n", type); } } @@ -80,7 +81,7 @@ it can accept the offer with :func:`puglAcceptOffer`: .. code-block:: c for (uint32_t t = 0; t < numTypes; ++t) { - const char* type = puglGetClipboardType(view, t); + const char* type = puglGetClipboardType(view, clipboard, t); if (!strcmp(type, "text/uri-list")) { puglAcceptOffer(view, event, @@ -109,15 +110,16 @@ the data can be fetched with :func:`puglGetClipboard`: static void onData(PuglView* view, const PuglEventData* event) { - uint32_t typeIndex = event->typeIndex; + PuglClipboard clipboard = event->clipboard; + uint32_t typeIndex = event->typeIndex; - const char* type = puglGetClipboardType(view, typeIndex); + const char* type = puglGetClipboardType(view, clipboard, typeIndex); fprintf(stderr, "Received data type: %s\n", type); if (!strcmp(type, "text/plain")) { size_t len = 0; - const void* data = puglGetClipboard(view, typeIndex, &len); + const void* data = puglGetClipboard(view, clipboard, typeIndex, &len); printf("Dropped: %s\n", (const char*)data); } diff --git a/examples/pugl_clipboard_demo.c b/examples/pugl_clipboard_demo.c index f43d729..ba691cf 100644 --- a/examples/pugl_clipboard_demo.c +++ b/examples/pugl_clipboard_demo.c @@ -65,7 +65,11 @@ onKeyPress(PuglView* const view, const PuglKeyEvent* const event) if (event->key == 'q' || event->key == PUGL_KEY_ESCAPE) { app->quit = 1; } else if ((event->state & PUGL_MOD_CTRL) && event->key == 'c') { - puglSetClipboard(view, "text/plain", copyString, strlen(copyString)); + puglSetClipboard(view, + PUGL_CLIPBOARD_GENERAL, + "text/plain", + copyString, + strlen(copyString)); fprintf(stderr, "Copy \"%s\"\n", copyString); } else if ((event->state & PUGL_MOD_CTRL) && event->key == 'v') { @@ -76,18 +80,19 @@ onKeyPress(PuglView* const view, const PuglKeyEvent* const event) static void onDataOffer(PuglView* view, const PuglDataOfferEvent* event) { - const uint32_t numTypes = puglGetNumClipboardTypes(view); + const PuglClipboard clipboard = event->clipboard; + const uint32_t numTypes = puglGetNumClipboardTypes(view, clipboard); // Print all offered types to be useful as a testing program fprintf(stderr, "Offered %u types:\n", numTypes); for (uint32_t t = 0; t < numTypes; ++t) { - const char* type = puglGetClipboardType(view, t); + const char* type = puglGetClipboardType(view, clipboard, t); fprintf(stderr, "\t%s\n", type); } // Accept the first type found that we support (namely text) for (uint32_t t = 0; t < numTypes; ++t) { - const char* type = puglGetClipboardType(view, t); + const char* type = puglGetClipboardType(view, clipboard, t); if (!strncmp(type, "text/", 5)) { puglAcceptOffer(view, event, t, puglGetFrame(view)); return; @@ -98,15 +103,16 @@ onDataOffer(PuglView* view, const PuglDataOfferEvent* event) static void onData(PuglView* view, const PuglDataEvent* event) { - const uint32_t typeIndex = event->typeIndex; + const PuglClipboard clipboard = event->clipboard; + const uint32_t typeIndex = event->typeIndex; - const char* const type = puglGetClipboardType(view, typeIndex); + const char* const type = puglGetClipboardType(view, clipboard, typeIndex); fprintf(stderr, "Received data type: %s\n", type); if (!strncmp(type, "text/", 5)) { // Accept any text type size_t len = 0; - const void* data = puglGetClipboard(view, typeIndex, &len); + const void* data = puglGetClipboard(view, clipboard, typeIndex, &len); fprintf(stderr, "Data:\n%s\n", (const char*)data); } diff --git a/include/pugl/pugl.h b/include/pugl/pugl.h index 329acba..852003f 100644 --- a/include/pugl/pugl.h +++ b/include/pugl/pugl.h @@ -148,6 +148,16 @@ typedef enum { PUGL_CROSSING_UNGRAB ///< Crossing due to a grab release } PuglCrossingMode; +/** + A system clipboard. + + A clipboard provides a mechanism for transferring data between views, + including views in different processes. +*/ +typedef enum { + PUGL_CLIPBOARD_GENERAL, ///< General clipboard for copy/pasted data +} PuglClipboard; + /// Common header for all event structs typedef struct { PuglEventType type; ///< Event type @@ -649,11 +659,12 @@ typedef struct { puglAcceptOffer(). */ typedef struct { - PuglEventType type; ///< #PUGL_DATA_OFFER - PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values - double time; ///< Time in seconds - double x; ///< View-relative X coordinate - double y; ///< View-relative Y coordinate + PuglEventType type; ///< #PUGL_DATA_OFFER + PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values + double time; ///< Time in seconds + double x; ///< View-relative X coordinate + double y; ///< View-relative Y coordinate + PuglClipboard clipboard; ///< Clipboard with available data } PuglDataOfferEvent; /** @@ -669,6 +680,7 @@ typedef struct { double time; ///< Time in seconds double x; ///< View-relative X coordinate double y; ///< View-relative Y coordinate + PuglClipboard clipboard; ///< Clipboard with available data uint32_t typeIndex; ///< Index of datatype } PuglDataEvent; @@ -1495,7 +1507,7 @@ puglPaste(PuglView* view); */ PUGL_API uint32_t -puglGetNumClipboardTypes(const PuglView* view); +puglGetNumClipboardTypes(const PuglView* view, PuglClipboard clipboard); /** Return the identifier of a type available in a clipboard. @@ -1508,7 +1520,9 @@ puglGetNumClipboardTypes(const PuglView* view); */ PUGL_API const char* -puglGetClipboardType(const PuglView* view, uint32_t typeIndex); +puglGetClipboardType(const PuglView* view, + PuglClipboard clipboard, + uint32_t typeIndex); /** Accept data offered from a clipboard. @@ -1543,16 +1557,18 @@ puglAcceptOffer(PuglView* view, puglGetClipboard() or pasted into other applications. @param view The view. + @param clipboard Clipboard to set data for. @param type The MIME type of the data, "text/plain" is assumed if `NULL`. @param data The data to copy to the clipboard. @param len The length of data in bytes (including terminator if necessary). */ PUGL_API PuglStatus -puglSetClipboard(PuglView* view, - const char* type, - const void* data, - size_t len); +puglSetClipboard(PuglView* view, + PuglClipboard clipboard, + const char* type, + const void* data, + size_t len); /** Get the clipboard contents. @@ -1561,13 +1577,17 @@ puglSetClipboard(PuglView* view, puglSetClipboard() or copied from another application. @param view The view. + @param clipboard Clipboard to get data from. @param typeIndex Index of the data type to get the item as. @param[out] len Set to the length of the data in bytes. @return The clipboard contents, or null. */ PUGL_API const void* -puglGetClipboard(PuglView* view, uint32_t typeIndex, size_t* len); +puglGetClipboard(PuglView* view, + PuglClipboard clipboard, + uint32_t typeIndex, + size_t* len); /** Set the mouse cursor. @@ -1867,6 +1867,19 @@ puglSetTransientParent(PuglView* view, PuglNativeView parent) return PUGL_FAILURE; } +static NSPasteboard* +getPasteboard(const PuglView* const view, const PuglClipboard clipboard) +{ + (void)view; + + switch (clipboard) { + case PUGL_CLIPBOARD_GENERAL: + return [NSPasteboard generalPasteboard]; + } + + return NULL; +} + PuglStatus puglPaste(PuglView* const view) { @@ -1883,18 +1896,20 @@ puglPaste(PuglView* const view) } uint32_t -puglGetNumClipboardTypes(const PuglView* PUGL_UNUSED(view)) +puglGetNumClipboardTypes(const PuglView* const view, + const PuglClipboard clipboard) { - NSPasteboard* const pasteboard = [NSPasteboard generalPasteboard]; + NSPasteboard* const pasteboard = getPasteboard(view, clipboard); return pasteboard ? (uint32_t)[[pasteboard types] count] : 0; } const char* -puglGetClipboardType(const PuglView* PUGL_UNUSED(view), - const uint32_t typeIndex) +puglGetClipboardType(const PuglView* const view, + const PuglClipboard clipboard, + const uint32_t typeIndex) { - NSPasteboard* const pasteboard = [NSPasteboard generalPasteboard]; + NSPasteboard* const pasteboard = getPasteboard(view, clipboard); if (!pasteboard) { return NULL; } @@ -1918,7 +1933,7 @@ puglAcceptOffer(PuglView* const view, const PuglRect region) { PuglWrapperView* const wrapper = view->impl->wrapperView; - NSPasteboard* const pasteboard = [NSPasteboard generalPasteboard]; + NSPasteboard* const pasteboard = getPasteboard(view, offer->clipboard); if (!pasteboard) { return PUGL_BAD_PARAMETER; } @@ -1945,13 +1960,14 @@ puglAcceptOffer(PuglView* const view, } const void* -puglGetClipboard(PuglView* const view, - const uint32_t typeIndex, - size_t* const len) +puglGetClipboard(PuglView* const view, + const PuglClipboard clipboard, + const uint32_t typeIndex, + size_t* const len) { *len = 0; - NSPasteboard* const pasteboard = [NSPasteboard generalPasteboard]; + NSPasteboard* const pasteboard = getPasteboard(view, clipboard); if (!pasteboard) { return NULL; } @@ -2035,20 +2051,23 @@ puglSetCursor(PuglView* view, PuglCursor cursor) } PuglStatus -puglSetClipboard(PuglView* PUGL_UNUSED(view), - const char* const type, - const void* const data, - const size_t len) +puglSetClipboard(PuglView* PUGL_UNUSED(view), + const PuglClipboard clipboard, + const char* const type, + const void* const data, + const size_t len) { - NSPasteboard* const pasteboard = [NSPasteboard generalPasteboard]; - NSString* const mimeType = [NSString stringWithUTF8String:type]; - NSString* const uti = utiForMimeType(mimeType); - NSData* const blob = [NSData dataWithBytes:data length:len]; + if (clipboard == PUGL_CLIPBOARD_GENERAL) { + NSPasteboard* const pasteboard = [NSPasteboard generalPasteboard]; + NSString* const mimeType = [NSString stringWithUTF8String:type]; + NSString* const uti = utiForMimeType(mimeType); + NSData* const blob = [NSData dataWithBytes:data length:len]; - [pasteboard declareTypes:[NSArray arrayWithObjects:uti, nil] owner:nil]; + [pasteboard declareTypes:[NSArray arrayWithObjects:uti, nil] owner:nil]; - if ([pasteboard setData:blob forType:uti]) { - return PUGL_SUCCESS; + if ([pasteboard setData:blob forType:uti]) { + return PUGL_SUCCESS; + } } return PUGL_FAILURE; @@ -1348,16 +1348,22 @@ puglSetTransientParent(PuglView* view, PuglNativeView parent) } uint32_t -puglGetNumClipboardTypes(const PuglView* const PUGL_UNUSED(view)) +puglGetNumClipboardTypes(const PuglView* const PUGL_UNUSED(view), + const PuglClipboard clipboard) { - return IsClipboardFormatAvailable(CF_UNICODETEXT) ? 1U : 0U; + return (clipboard == PUGL_CLIPBOARD_GENERAL && + IsClipboardFormatAvailable(CF_UNICODETEXT)) + ? 1U + : 0U; } const char* puglGetClipboardType(const PuglView* const PUGL_UNUSED(view), - const uint32_t typeIndex) + const PuglClipboard clipboard, + const uint32_t PUGL_UNUSED(typeIndex)) { - return (typeIndex == 0 && IsClipboardFormatAvailable(CF_UNICODETEXT)) + return (clipboard == PUGL_CLIPBOARD_GENERAL && typeIndex == 0 && + IsClipboardFormatAvailable(CF_UNICODETEXT)) ? "text/plain" : NULL; } @@ -1388,12 +1394,17 @@ puglAcceptOffer(PuglView* const view, } const void* -puglGetClipboard(PuglView* const view, - const uint32_t typeIndex, - size_t* const len) +puglGetClipboard(PuglView* const view, + const PuglClipboard clipboard, + const uint32_t typeIndex, + size_t* const len) { PuglInternals* const impl = view->impl; + if (clipboard != PUGL_CLIPBOARD_GENERAL) { + return NULL; + } + if (typeIndex > 0U || !IsClipboardFormatAvailable(CF_UNICODETEXT) || !OpenClipboard(impl->hwnd)) { return NULL; @@ -1418,13 +1429,18 @@ puglGetClipboard(PuglView* const view, } PuglStatus -puglSetClipboard(PuglView* const view, - const char* const type, - const void* const data, - const size_t len) +puglSetClipboard(PuglView* const view, + const PuglClipboard clipboard, + const char* const type, + const void* const data, + const size_t len) { PuglInternals* const impl = view->impl; + if (clipboard != PUGL_CLIPBOARD_GENERAL) { + return PUGL_FAILURE; + } + PuglStatus st = puglSetBlob(&view->impl->clipboard, data, len); if (st) { return st; @@ -257,6 +257,7 @@ puglInitViewInternals(PuglWorld* const world) { PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals)); + impl->clipboard.clipboard = PUGL_CLIPBOARD_GENERAL; impl->clipboard.selection = world->impl->atoms.CLIPBOARD; impl->clipboard.property = XA_PRIMARY; @@ -951,10 +952,22 @@ getAtomProperty(PuglView* const view, } static PuglX11Clipboard* +getX11Clipboard(PuglView* const view, const PuglClipboard clipboard) +{ + return clipboard == PUGL_CLIPBOARD_GENERAL ? &view->impl->clipboard : NULL; +} + +static const PuglX11Clipboard* +getConstX11Clipboard(const PuglView* const view, const PuglClipboard clipboard) +{ + return clipboard == PUGL_CLIPBOARD_GENERAL ? &view->impl->clipboard : NULL; +} + +static PuglX11Clipboard* getX11SelectionClipboard(PuglView* const view, const Atom selection) { return (selection == view->world->impl->atoms.CLIPBOARD) - ? &view->impl->clipboard + ? getX11Clipboard(view, PUGL_CLIPBOARD_GENERAL) : NULL; } @@ -1617,8 +1630,12 @@ handleSelectionNotify(const PuglWorld* const world, if (!getAtomProperty( view, event->requestor, event->property, &numFormats, &formats) && !setClipboardFormats(view, board, numFormats, formats)) { - const PuglDataOfferEvent offer = { - PUGL_DATA_OFFER, 0, (double)event->time / 1e3, 0.0, 0.0}; + const PuglDataOfferEvent offer = {PUGL_DATA_OFFER, + 0, + (double)event->time / 1e3, + 0.0, + 0.0, + board->clipboard}; puglEvent.offer = offer; board->acceptedFormatIndex = UINT32_MAX; @@ -1640,6 +1657,7 @@ handleSelectionNotify(const PuglWorld* const world, (double)event->time / 1e3, 0.0, 0.0, + board->clipboard, board->acceptedFormatIndex}; puglEvent.data = data; @@ -2072,12 +2090,13 @@ puglSetTransientParent(PuglView* const view, const PuglNativeView parent) } const void* -puglGetClipboard(PuglView* const view, - const uint32_t typeIndex, - size_t* const len) +puglGetClipboard(PuglView* const view, + const PuglClipboard clipboard, + const uint32_t typeIndex, + size_t* const len) { Display* const display = view->world->impl->display; - PuglX11Clipboard* const board = &view->impl->clipboard; + PuglX11Clipboard* const board = getX11Clipboard(view, clipboard); if (typeIndex != board->acceptedFormatIndex) { return NULL; @@ -2099,25 +2118,28 @@ puglAcceptOffer(PuglView* const view, const uint32_t typeIndex, const PuglRect region) { - (void)offer; (void)region; PuglInternals* const impl = view->impl; Display* const display = view->world->impl->display; - PuglX11Clipboard* const board = &view->impl->clipboard; + PuglX11Clipboard* const board = getX11Clipboard(view, offer->clipboard); board->acceptedFormatIndex = typeIndex; board->acceptedFormat = board->formats[typeIndex]; - // Request the data in the specified type from the general clipboard - XConvertSelection(display, - board->selection, - board->acceptedFormat, - board->property, - impl->win, - CurrentTime); + if (offer->clipboard == PUGL_CLIPBOARD_GENERAL) { + // Request the data in the specified type from the general clipboard + XConvertSelection(display, + board->selection, + board->acceptedFormat, + board->property, + impl->win, + CurrentTime); - return PUGL_SUCCESS; + return PUGL_SUCCESS; + } + + return PUGL_FAILURE; } PuglStatus @@ -2125,7 +2147,7 @@ puglPaste(PuglView* const view) { Display* const display = view->world->impl->display; const PuglX11Atoms* atoms = &view->world->impl->atoms; - const PuglX11Clipboard* board = &view->impl->clipboard; + const PuglX11Clipboard* board = getX11Clipboard(view, PUGL_CLIPBOARD_GENERAL); // Request a SelectionNotify for TARGETS (available datatypes) XConvertSelection(display, @@ -2139,28 +2161,32 @@ puglPaste(PuglView* const view) } uint32_t -puglGetNumClipboardTypes(const PuglView* const view) +puglGetNumClipboardTypes(const PuglView* const view, + const PuglClipboard clipboard) { - return (uint32_t)view->impl->clipboard.numFormats; + return (uint32_t)getConstX11Clipboard(view, clipboard)->numFormats; } const char* -puglGetClipboardType(const PuglView* const view, const uint32_t typeIndex) +puglGetClipboardType(const PuglView* const view, + const PuglClipboard clipboard, + const uint32_t typeIndex) { - const PuglX11Clipboard* const board = &view->impl->clipboard; + const PuglX11Clipboard* const board = getConstX11Clipboard(view, clipboard); return typeIndex < board->numFormats ? board->formatStrings[typeIndex] : NULL; } PuglStatus -puglSetClipboard(PuglView* const view, - const char* const type, - const void* const data, - const size_t len) +puglSetClipboard(PuglView* const view, + const PuglClipboard clipboard, + const char* const type, + const void* const data, + const size_t len) { PuglInternals* const impl = view->impl; Display* const display = view->world->impl->display; - PuglX11Clipboard* const board = &view->impl->clipboard; + PuglX11Clipboard* const board = getX11Clipboard(view, clipboard); PuglStatus st = puglSetBlob(&board->data, data, len); if (!st) { @@ -54,6 +54,7 @@ typedef struct { } PuglTimer; typedef struct { + PuglClipboard clipboard; Atom selection; Atom property; Window source; diff --git a/test/test_local_copy_paste.c b/test/test_local_copy_paste.c index 1d0d3fc..9aa4cba 100644 --- a/test/test_local_copy_paste.c +++ b/test/test_local_copy_paste.c @@ -57,23 +57,29 @@ onEvent(PuglView* view, const PuglEvent* event) assert(event->timer.id == timerId); if (test->iteration == 0) { - puglSetClipboard( - view, "text/plain", "Copied Text", strlen("Copied Text") + 1); + puglSetClipboard(view, + PUGL_CLIPBOARD_GENERAL, + "text/plain", + "Copied Text", + strlen("Copied Text") + 1); // Check that the new type is available immediately - assert(puglGetNumClipboardTypes(view) >= 1); - assert(!strcmp(puglGetClipboardType(view, 0), "text/plain")); + assert(puglGetNumClipboardTypes(view, PUGL_CLIPBOARD_GENERAL) == 1); + assert(!strcmp(puglGetClipboardType(view, PUGL_CLIPBOARD_GENERAL, 0), + "text/plain")); - size_t len = 0; - const char* text = (const char*)puglGetClipboard(view, 0, &len); + size_t len = 0; + const char* text = + (const char*)puglGetClipboard(view, PUGL_CLIPBOARD_GENERAL, 0, &len); // Check that the new contents are available immediately assert(text); assert(!strcmp(text, "Copied Text")); } else if (test->iteration == 1) { - size_t len = 0; - const char* text = (const char*)puglGetClipboard(view, 0, &len); + size_t len = 0; + const char* text = + (const char*)puglGetClipboard(view, PUGL_CLIPBOARD_GENERAL, 0, &len); // Check that the contents we pasted last iteration are still there assert(text); @@ -98,8 +104,9 @@ onEvent(PuglView* view, const PuglEvent* event) case PUGL_DATA: if (test->state == RECEIVED_OFFER) { - size_t len = 0; - const char* text = (const char*)puglGetClipboard(view, 0, &len); + size_t len = 0; + const char* text = + (const char*)puglGetClipboard(view, PUGL_CLIPBOARD_GENERAL, 0, &len); // Check that the offered data is what we copied earlier assert(text); diff --git a/test/test_remote_copy_paste.c b/test/test_remote_copy_paste.c index 8e2b752..de5a61b 100644 --- a/test/test_remote_copy_paste.c +++ b/test/test_remote_copy_paste.c @@ -60,8 +60,11 @@ onCopierEvent(PuglView* const view, const PuglEvent* const event) assert(event->timer.id == copierTimerId); if (test->state < COPIED) { - puglSetClipboard( - view, "text/plain", "Copied Text", strlen("Copied Text") + 1); + puglSetClipboard(view, + PUGL_CLIPBOARD_GENERAL, + "text/plain", + "Copied Text", + strlen("Copied Text") + 1); test->state = COPIED; } @@ -111,8 +114,9 @@ onPasterEvent(PuglView* const view, const PuglEvent* const event) case PUGL_DATA: if (test->state == RECEIVED_OFFER) { - size_t len = 0; - const char* text = (const char*)puglGetClipboard(view, 0, &len); + size_t len = 0; + const char* text = + (const char*)puglGetClipboard(view, PUGL_CLIPBOARD_GENERAL, 0, &len); // Check that the offered data is what we copied earlier assert(text); |