From 88926f2b731792a6f50b98b3ed2029352e106ec7 Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Fri, 22 Nov 2024 03:25:12 +0100 Subject: [PATCH] emscripten: send drag and drop events --- src/video/emscripten/SDL_emscriptenevents.c | 145 ++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/src/video/emscripten/SDL_emscriptenevents.c b/src/video/emscripten/SDL_emscriptenevents.c index 18d9117fc3..aeffef64cc 100644 --- a/src/video/emscripten/SDL_emscriptenevents.c +++ b/src/video/emscripten/SDL_emscriptenevents.c @@ -26,6 +26,7 @@ #include #include +#include "../../events/SDL_dropevents_c.h" #include "../../events/SDL_events_c.h" #include "../../events/SDL_keyboard_c.h" #include "../../events/SDL_touch_c.h" @@ -806,6 +807,144 @@ static void Emscripten_unset_pointer_event_callbacks(SDL_WindowData *data) }, data->canvas_id); } +// IF YOU CHANGE THIS STRUCTURE, YOU NEED TO UPDATE THE JAVASCRIPT THAT FILLS IT IN: makeDropEventCStruct, below. +typedef struct Emscripten_DropEvent +{ + int x; + int y; +} Emscripten_DropEvent; + +EMSCRIPTEN_KEEPALIVE void Emscripten_SendDragEvent(SDL_WindowData *window_data, const Emscripten_DropEvent *event) +{ + SDL_SendDropPosition(window_data->window, event->x, event->y); +} + +EMSCRIPTEN_KEEPALIVE void Emscripten_SendDragCompleteEvent(SDL_WindowData *window_data) +{ + SDL_SendDropComplete(window_data->window); +} + +EMSCRIPTEN_KEEPALIVE void Emscripten_SendDragTextEvent(SDL_WindowData *window_data, char *text) +{ + SDL_SendDropText(window_data->window, text); +} + +EMSCRIPTEN_KEEPALIVE void Emscripten_SendDragFileEvent(SDL_WindowData *window_data, char *filename) +{ + SDL_SendDropFile(window_data->window, NULL, filename); +} + +EM_JS_DEPS(dragndrop, "$writeArrayToMemory"); + +static void Emscripten_set_drag_event_callbacks(SDL_WindowData *data) +{ + MAIN_THREAD_EM_ASM({ + var target = document.querySelector(UTF8ToString($1)); + if (target) { + var data = $0; + + if (typeof(Module['SDL3']) === 'undefined') { + Module['SDL3'] = {}; + } + var SDL3 = Module['SDL3']; + + var makeDropEventCStruct = function(event) { + var ptr = 0; + ptr = _SDL_malloc($2); + if (ptr != 0) { + var idx = ptr >> 2; + var rect = target.getBoundingClientRect(); + HEAP32[idx++] = event.clientX - rect.left; + HEAP32[idx++] = event.clientY - rect.top; + } + return ptr; + }; + + SDL3.eventHandlerDropDragover = function(event) { + event.preventDefault(); + var d = makeDropEventCStruct(event); if (d != 0) { _Emscripten_SendDragEvent(data, d); _SDL_free(d); } + }; + target.addEventListener("dragover", SDL3.eventHandlerDropDragover); + + SDL3.drop_count = 0; + FS.mkdir("/tmp/filedrop"); + SDL3.eventHandlerDropDrop = function(event) { + event.preventDefault(); + if (event.dataTransfer.types.includes("text/plain")) { + let plain_text = stringToNewUTF8(event.dataTransfer.getData("text/plain")); + _Emscripten_SendDragTextEvent(data, plain_text); + _free(plain_text); + } else if (event.dataTransfer.types.includes("Files")) { + for (let i = 0; i < event.dataTransfer.files.length; i++) { + const file = event.dataTransfer.files.item(i); + const file_reader = new FileReader(); + file_reader.readAsArrayBuffer(file); + file_reader.onload = function(event) { + const fs_dropdir = `/tmp/filedrop/${SDL3.drop_count}`; + SDL3.drop_count += 1; + + const fs_filepath = `${fs_dropdir}/${file.name}`; + const c_fs_filepath = stringToNewUTF8(fs_filepath); + const contents_array8 = new Uint8Array(event.target.result); + + FS.mkdir(fs_dropdir); + var stream = FS.open(fs_filepath, "w"); + FS.write(stream, contents_array8, 0, contents_array8.length, 0); + FS.close(stream); + + _Emscripten_SendDragFileEvent(data, c_fs_filepath); + _free(c_fs_filepath); + _Emscripten_SendDragCompleteEvent(data); + }; + } + } + _Emscripten_SendDragCompleteEvent(data); + }; + target.addEventListener("drop", SDL3.eventHandlerDropDrop); + + SDL3.eventHandlerDropDragend = function(event) { + event.preventDefault(); + _Emscripten_SendDragCompleteEvent(data); + }; + target.addEventListener("dragend", SDL3.eventHandlerDropDragend); + target.addEventListener("dragleave", SDL3.eventHandlerDropDragend); + } + }, data, data->canvas_id, sizeof (Emscripten_DropEvent)); +} + +static void Emscripten_unset_drag_event_callbacks(SDL_WindowData *data) +{ + MAIN_THREAD_EM_ASM({ + var target = document.querySelector(UTF8ToString($0)); + if (target) { + var SDL3 = Module['SDL3']; + target.removeEventListener("dragleave", SDL3.eventHandlerDropDragend); + target.removeEventListener("dragend", SDL3.eventHandlerDropDragend); + target.removeEventListener("drop", SDL3.eventHandlerDropDrop); + SDL3.drop_count = undefined; + + function recursive_remove(dirpath) { + FS.readdir(dirpath).forEach((filename) => { + const p = `${dirpath}/${filename}`; + const p_s = FS.stat(p); + if (FS.isFile(p_s.mode)) { + FS.unlink(p); + } else if (FS.isDir(p)) { + recursive_remove(p); + } + }); + FS.rmdir(dirpath); + }("/tmp/filedrop"); + + FS.rmdir("/tmp/filedrop"); + target.removeEventListener("dragover", SDL3.eventHandlerDropDragover); + SDL3.eventHandlerDropDragover = undefined; + SDL3.eventHandlerDropDrop = undefined; + SDL3.eventHandlerDropDragend = undefined; + } + }, data->canvas_id); +} + void Emscripten_RegisterEventHandlers(SDL_WindowData *data) { const char *keyElement; @@ -852,12 +991,18 @@ void Emscripten_RegisterEventHandlers(SDL_WindowData *data) // !!! FIXME: currently Emscripten doesn't have a Pointer Events functions like emscripten_set_*_callback, but we should use those when they do: // !!! FIXME: https://github.com/emscripten-core/emscripten/issues/7278#issuecomment-2280024621 Emscripten_set_pointer_event_callbacks(data); + + // !!! FIXME: currently Emscripten doesn't have a Drop Events functions like emscripten_set_*_callback, but we should use those when they do: + Emscripten_set_drag_event_callbacks(data); } void Emscripten_UnregisterEventHandlers(SDL_WindowData *data) { const char *target; + // !!! FIXME: currently Emscripten doesn't have a Drop Events functions like emscripten_set_*_callback, but we should use those when they do: + Emscripten_unset_drag_event_callbacks(data); + // !!! FIXME: currently Emscripten doesn't have a Pointer Events functions like emscripten_set_*_callback, but we should use those when they do: // !!! FIXME: https://github.com/emscripten-core/emscripten/issues/7278#issuecomment-2280024621 Emscripten_unset_pointer_event_callbacks(data);