diff --git a/build/toolchain.emscripten.mak b/build/toolchain.emscripten.mak index 5ccc9b7b9..ac6116fe5 100644 --- a/build/toolchain.emscripten.mak +++ b/build/toolchain.emscripten.mak @@ -77,6 +77,7 @@ _closure_call \ _do_load \ _do_load_from_lexer \ _emscripten_sleep \ +_emscripten_sleep_with_yield \ _fun_bc_call \ _fun_builtin_1_call \ _fun_builtin_var_call \ @@ -103,6 +104,14 @@ _mp_execute_bytecode \ _mp_hal_input \ _mp_import_name \ _mp_parse_compile_execute \ +_get_clipboard_text \ +__ZN3Ion9Clipboard4readEPcm \ +__ZN9Clipboard10storedTextEv \ +__ZN11LayoutField18privateHandleEventEN3Ion6Events5EventE \ +__ZN11LayoutField11handleEventEN3Ion6Events5EventE \ +__ZN8TextArea18privateHandleEventEN3Ion6Events5EventE \ +__ZN8TextArea11handleEventEN3Ion6Events5EventE \ +__ZN15ExpressionField11handleEventEN3Ion6Events5EventE \ _msleep EMTERPRETIFY_WHITELIST = $(foreach sym,$(EMSCRIPTEN_ASYNC_SYMBOLS),"$(sym)",)END diff --git a/ion/src/simulator/Makefile b/ion/src/simulator/Makefile index 473c0c7b3..10f10fb1e 100644 --- a/ion/src/simulator/Makefile +++ b/ion/src/simulator/Makefile @@ -8,7 +8,6 @@ ion_src += $(addprefix ion/src/simulator/shared/, \ dummy/serial_number.cpp \ dummy/stack.cpp \ dummy/usb.cpp \ - clipboard.cpp \ console_stdio.cpp:-consoledisplay \ crc32.cpp \ display.cpp:-headless \ diff --git a/ion/src/simulator/android/Makefile b/ion/src/simulator/android/Makefile index 2fb97edbb..210130d8a 100644 --- a/ion/src/simulator/android/Makefile +++ b/ion/src/simulator/android/Makefile @@ -6,6 +6,7 @@ ion_src += $(addprefix ion/src/simulator/android/src/cpp/, \ ion_src += $(addprefix ion/src/simulator/shared/, \ dummy/callback.cpp \ dummy/language.cpp \ + clipboard.cpp \ haptics.cpp \ ) diff --git a/ion/src/simulator/ios/Makefile b/ion/src/simulator/ios/Makefile index e3496ac80..7a52d972b 100644 --- a/ion/src/simulator/ios/Makefile +++ b/ion/src/simulator/ios/Makefile @@ -6,6 +6,7 @@ ion_src += $(addprefix ion/src/simulator/shared/, \ apple/language.m \ dummy/callback.cpp \ dummy/haptics_enabled.cpp \ + clipboard.cpp \ haptics.cpp \ ) diff --git a/ion/src/simulator/linux/Makefile b/ion/src/simulator/linux/Makefile index 256685745..304068aad 100644 --- a/ion/src/simulator/linux/Makefile +++ b/ion/src/simulator/linux/Makefile @@ -16,6 +16,7 @@ ion_src += $(addprefix ion/src/simulator/linux/, \ ion_src += $(addprefix ion/src/simulator/shared/, \ dummy/callback.cpp \ dummy/haptics_enabled.cpp \ + clipboard.cpp \ collect_registers_x86_64.s \ collect_registers.cpp \ haptics.cpp \ diff --git a/ion/src/simulator/macos/Makefile b/ion/src/simulator/macos/Makefile index 8171ffff2..7b90d0525 100644 --- a/ion/src/simulator/macos/Makefile +++ b/ion/src/simulator/macos/Makefile @@ -6,6 +6,7 @@ ion_src += $(addprefix ion/src/simulator/shared/, \ apple/language.m \ dummy/callback.cpp \ dummy/haptics_enabled.cpp \ + clipboard.cpp \ collect_registers_x86_64.s \ collect_registers.cpp \ haptics.cpp \ diff --git a/ion/src/simulator/shared/clipboard.cpp b/ion/src/simulator/shared/clipboard.cpp index e18546833..550b18d9d 100644 --- a/ion/src/simulator/shared/clipboard.cpp +++ b/ion/src/simulator/shared/clipboard.cpp @@ -2,6 +2,9 @@ #include #include +/* This file implements the methods to access the system clipboard on all + * targets but the web simulator. */ + namespace Ion { void Clipboard::write(const char * text) { diff --git a/ion/src/simulator/web/Makefile b/ion/src/simulator/web/Makefile index e8c729371..dcfcb44d8 100644 --- a/ion/src/simulator/web/Makefile +++ b/ion/src/simulator/web/Makefile @@ -15,6 +15,7 @@ LDFLAGS += --pre-js ion/src/simulator/web/preamble_env.js ion_src += $(addprefix ion/src/simulator/web/, \ callback.cpp \ + clipboard.cpp \ helpers.cpp \ ) diff --git a/ion/src/simulator/web/clipboard.cpp b/ion/src/simulator/web/clipboard.cpp new file mode 100644 index 000000000..1775e76ed --- /dev/null +++ b/ion/src/simulator/web/clipboard.cpp @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include +#include + +enum class AsyncStatus : uint32_t { + Pending, + Success, + Failure +}; + +/* When using emscripten, the SDL_Clipboard methods do not interact with the + * system clipboard, but only with an internal buffer. We thus need to + * implement our own methods via the JavaScript API. + * However, we still call the SDL_Clipboard methods as a fallback to preserve + * the copy-paste feature in case of calls to set_clipboard_text and + * get_clipboard_text failing. */ + +EM_JS(void, set_clipboard_text, (const char * text, AsyncStatus * status, AsyncStatus failure, AsyncStatus success), { + try { + navigator.clipboard.writeText(UTF8ToString(text)).then( + function () { HEAP32[status>>2] = success; }, + function () { HEAP32[status>>2] = failure; } + ); + } catch (error) { + console.error(error); + HEAP32[status>>2] = failure; + } +}); + +EM_JS(void, get_clipboard_text, (char * buffer, uint32_t bufferSize, AsyncStatus * status, AsyncStatus failure, AsyncStatus success), { + try { + navigator.clipboard.readText().then( + function(text) { + var lenghtBytes = Math.min(lengthBytesUTF8(text) + 1, bufferSize); + stringToUTF8(text, buffer, lenghtBytes); + HEAP32[status>>2] = success; + }, + function(text) { HEAP32[status>>2] = failure; } + ); + } catch (error) { + console.error(error); + HEAP32[status>>2] = failure; + } +}); + +namespace Ion { + +void Clipboard::write(const char * text) { + /* FIXME : Handle the error if need be. */ + /* As the rest of the execution does not depend on the system clipboard being + * properly filled at this point, the call to set_clipboard_text does not + * need to be made synchronous. */ + AsyncStatus lock; + set_clipboard_text(text, &lock, AsyncStatus::Failure, AsyncStatus::Success); + /* Store a local copy of the text in case the browser does not grant access + * to the clipboard. */ + SDL_SetClipboardText(text); +} + +void Clipboard::read(char * buffer, size_t bufferSize) { + AsyncStatus lock = AsyncStatus::Pending; + static_assert(sizeof(size_t) <= sizeof(uint32_t), "Cast from size_t to uint32_t may overflow."); + get_clipboard_text(buffer, static_cast(bufferSize), &lock, AsyncStatus::Failure, AsyncStatus::Success); + while (lock == AsyncStatus::Pending) { + emscripten_sleep_with_yield(10); + } + if (lock == AsyncStatus::Success) { + return; + } + /* If the browser does not grant access to the clipboard, read from a local + * copy to at least maintain the basic copy-paste functionnality. */ + if (!SDL_HasClipboardText()) { + buffer[0] = '\0'; + return; + } + char * text = SDL_GetClipboardText(); + if (text) { + strlcpy(buffer, text, bufferSize); + SDL_free(text); + } +} + +} diff --git a/ion/src/simulator/windows/Makefile b/ion/src/simulator/windows/Makefile index a057924f5..5c23860f9 100644 --- a/ion/src/simulator/windows/Makefile +++ b/ion/src/simulator/windows/Makefile @@ -7,6 +7,7 @@ ion_src += $(addprefix ion/src/simulator/windows/, \ ion_src += $(addprefix ion/src/simulator/shared/, \ dummy/callback.cpp \ dummy/haptics_enabled.cpp \ + clipboard.cpp \ haptics.cpp \ )