From 82c753a940ddbf386e7683969bd0aa95ab3b5f30 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 16 Feb 2024 14:26:58 +1100 Subject: [PATCH] GHOST/Wayland: drag & drop cleanup, minor changes - Don't generate a drop event when the drop data failed to read. - Remove redundant drop-buffer duplication. --- intern/ghost/intern/GHOST_DropTargetX11.cc | 3 +- intern/ghost/intern/GHOST_PathUtils.cc | 12 +- intern/ghost/intern/GHOST_PathUtils.hh | 5 +- intern/ghost/intern/GHOST_SystemWayland.cc | 137 +++++++++++++-------- 4 files changed, 95 insertions(+), 62 deletions(-) diff --git a/intern/ghost/intern/GHOST_DropTargetX11.cc b/intern/ghost/intern/GHOST_DropTargetX11.cc index 505cd07ab23..45edd553886 100644 --- a/intern/ghost/intern/GHOST_DropTargetX11.cc +++ b/intern/ghost/intern/GHOST_DropTargetX11.cc @@ -102,7 +102,8 @@ GHOST_DropTargetX11::~GHOST_DropTargetX11() char *GHOST_DropTargetX11::FileUrlDecode(const char *fileUrl) { if (strncmp(fileUrl, "file://", 7) == 0) { - return GHOST_URL_decode_alloc(fileUrl + 7); + const char *file = fileUrl + 7; + return GHOST_URL_decode_alloc(file, strlen(file)); } return nullptr; diff --git a/intern/ghost/intern/GHOST_PathUtils.cc b/intern/ghost/intern/GHOST_PathUtils.cc index 4c51a803871..b53b2a85cba 100644 --- a/intern/ghost/intern/GHOST_PathUtils.cc +++ b/intern/ghost/intern/GHOST_PathUtils.cc @@ -12,6 +12,7 @@ #include #include +#include "GHOST_Debug.hh" #include "GHOST_PathUtils.hh" #include "GHOST_Types.h" @@ -24,9 +25,10 @@ using DecodeState_e = enum DecodeState_e { STATE_CONVERTING }; -void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src) +void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src, const int buf_src_len) { - const uint buf_src_len = strlen(buf_src); + GHOST_ASSERT(strnlen(buf_src, buf_src_len) == buf_src_len, "Incorrect length"); + DecodeState_e state = STATE_SEARCH; uint ascii_character; @@ -85,12 +87,12 @@ void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src) } } -char *GHOST_URL_decode_alloc(const char *buf_src) +char *GHOST_URL_decode_alloc(const char *buf_src, const int buf_src_len) { /* Assume one character of encoded URL can be expanded to 4 chars max. */ - const size_t decoded_size_max = 4 * strlen(buf_src) + 1; + const size_t decoded_size_max = 4 * buf_src_len + 1; char *buf_dst = (char *)malloc(decoded_size_max); - GHOST_URL_decode(buf_dst, decoded_size_max, buf_src); + GHOST_URL_decode(buf_dst, decoded_size_max, buf_src, buf_src_len); const size_t decoded_size = strlen(buf_dst) + 1; if (decoded_size != decoded_size_max) { char *buf_dst_trim = (char *)malloc(decoded_size); diff --git a/intern/ghost/intern/GHOST_PathUtils.hh b/intern/ghost/intern/GHOST_PathUtils.hh index 941e405344a..bc8a2a894b3 100644 --- a/intern/ghost/intern/GHOST_PathUtils.hh +++ b/intern/ghost/intern/GHOST_PathUtils.hh @@ -14,12 +14,13 @@ * \param buf_dst: Buffer for decoded URL. * \param buf_dst_maxlen: Size of output buffer. * \param buf_src: Input encoded buffer to be decoded. + * \param buf_src_len: The length of `buf_src` to use. */ -void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src); +void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src, int buf_src_len); /** * A version of #GHOST_URL_decode that allocates the string & returns it. * * \param buf_src: Input encoded buffer to be decoded. * \return The decoded output buffer. */ -char *GHOST_URL_decode_alloc(const char *buf_src); +char *GHOST_URL_decode_alloc(const char *buf_src, int buf_src_len); diff --git a/intern/ghost/intern/GHOST_SystemWayland.cc b/intern/ghost/intern/GHOST_SystemWayland.cc index fd7f6c5a515..02508f4135a 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cc +++ b/intern/ghost/intern/GHOST_SystemWayland.cc @@ -2926,6 +2926,9 @@ static char *read_buffer_from_data_offer(GWL_DataOffer *data_offer, } close(pipefd[0]); } + else { + *r_len = 0; + } return buf; } @@ -3259,20 +3262,18 @@ static void data_device_handle_drop(void *data, wl_data_device * /*wl_data_devic CLOG_INFO(LOG, 2, "drop mime_recieve=%s", mime_receive); - auto read_uris_fn = [](GWL_Seat *const seat, - GWL_DataOffer *data_offer, - wl_surface *wl_surface_window, - const char *mime_receive) { + auto read_drop_data_fn = [](GWL_Seat *const seat, + GWL_DataOffer *data_offer, + wl_surface *wl_surface_window, + const char *mime_receive) { const uint64_t event_ms = seat->system->getMilliSeconds(); const wl_fixed_t xy[2] = {UNPACK2(data_offer->dnd.xy)}; size_t data_buf_len = 0; const char *data_buf = read_buffer_from_data_offer( data_offer, mime_receive, nullptr, false, &data_buf_len); - std::string data = data_buf ? std::string(data_buf, data_buf_len) : ""; - free(const_cast(data_buf)); - CLOG_INFO(LOG, 2, "drop_read_uris mime_receive=%s, data=%s", mime_receive, data.c_str()); + CLOG_INFO(LOG, 2, "read_drop_data mime_receive=%s, data_len=%zu", mime_receive, data_buf_len); wl_data_offer_finish(data_offer->wl.id); wl_data_offer_destroy(data_offer->wl.id); @@ -3283,69 +3284,97 @@ static void data_device_handle_drop(void *data, wl_data_device * /*wl_data_devic delete data_offer; data_offer = nullptr; - GHOST_SystemWayland *const system = seat->system; + /* Don't generate a drop event if the data could not be read, + * an error will have been logged. */ + if (data_buf != nullptr) { + GHOST_TDragnDropTypes ghost_dnd_type = GHOST_kDragnDropTypeUnknown; + void *ghost_dnd_data = nullptr; - if (mime_receive == ghost_wl_mime_text_uri) { - const char file_proto[] = "file://"; - /* NOTE: some applications CRLF (`\r\n`) GTK3 for e.g. & others don't `pcmanfm-qt`. - * So support both, once `\n` is found, strip the preceding `\r` if found. */ - const char lf = '\n'; + /* Failure to receive drop data . */ + if (mime_receive == ghost_wl_mime_text_uri) { + const char file_proto[] = "file://"; + /* NOTE: some applications CRLF (`\r\n`) GTK3 for e.g. & others don't `pcmanfm-qt`. + * So support both, once `\n` is found, strip the preceding `\r` if found. */ + const char lf = '\n'; - GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_window); - std::vector uris; + const std::string_view data = std::string_view(data_buf, data_buf_len); + std::vector uris; - size_t pos = 0; - while (pos != std::string::npos) { - pos = data.find(file_proto, pos); - if (pos == std::string::npos) { - break; + size_t pos = 0; + while (pos != std::string::npos) { + pos = data.find(file_proto, pos); + if (pos == std::string::npos) { + break; + } + const size_t start = pos + sizeof(file_proto) - 1; + pos = data.find(lf, pos); + + size_t end = pos; + if (UNLIKELY(end == std::string::npos)) { + /* Note that most well behaved file managers will add a trailing newline, + * Gnome's web browser (44.3) doesn't, so support reading up until the last byte. */ + end = data.size(); + } + /* Account for 'CRLF' case. */ + if (data[end - 1] == '\r') { + end -= 1; + } + + std::string_view data_substr = data.substr(start, end - start); + uris.push_back(data_substr); + CLOG_INFO(LOG, + 2, + "read_drop_data pos=%zu, text_uri=\"%.*s\"", + start, + int(data_substr.size()), + data_substr.data()); } - const size_t start = pos + sizeof(file_proto) - 1; - pos = data.find(lf, pos); - size_t end = pos; - if (UNLIKELY(end == std::string::npos)) { - /* Note that most well behaved file managers will add a trailing newline, - * Gnome's web browser (44.3) doesn't, so support reading up until the last byte. */ - end = data.size(); + GHOST_TStringArray *flist = static_cast( + malloc(sizeof(GHOST_TStringArray))); + flist->count = int(uris.size()); + flist->strings = static_cast(malloc(uris.size() * sizeof(uint8_t *))); + for (size_t i = 0; i < uris.size(); i++) { + flist->strings[i] = reinterpret_cast( + GHOST_URL_decode_alloc(uris[i].data(), uris[i].size())); } - /* Account for 'CRLF' case. */ - if (data[end - 1] == '\r') { - end -= 1; - } - uris.push_back(data.substr(start, end - start)); - CLOG_INFO(LOG, 2, "drop_read_uris pos=%zu, text_uri=\"%s\"", start, uris.back().c_str()); + + CLOG_INFO(LOG, 2, "read_drop_data file_count=%d", flist->count); + ghost_dnd_type = GHOST_kDragnDropTypeFilenames; + ghost_dnd_data = flist; + } + else if (ELEM(mime_receive, ghost_wl_mime_text_plain, ghost_wl_mime_text_utf8)) { + /* TODO: enable use of internal functions 'txt_insert_buf' and + * 'text_update_edited' to behave like dropped text was pasted. */ + CLOG_INFO(LOG, 2, "read_drop_data, unhandled!"); } - GHOST_TStringArray *flist = static_cast( - malloc(sizeof(GHOST_TStringArray))); - flist->count = int(uris.size()); - flist->strings = static_cast(malloc(uris.size() * sizeof(uint8_t *))); - for (size_t i = 0; i < uris.size(); i++) { - flist->strings[i] = reinterpret_cast(GHOST_URL_decode_alloc(uris[i].c_str())); + if (ghost_dnd_type != GHOST_kDragnDropTypeUnknown) { + GHOST_SystemWayland *const system = seat->system; + GHOST_WindowWayland *win = ghost_wl_surface_user_data(wl_surface_window); + const int event_xy[2] = {WL_FIXED_TO_INT_FOR_WINDOW_V2(win, xy)}; + + system->pushEvent_maybe_pending(new GHOST_EventDragnDrop(event_ms, + GHOST_kEventDraggingDropDone, + ghost_dnd_type, + win, + UNPACK2(event_xy), + ghost_dnd_data)); + + wl_display_roundtrip(system->wl_display_get()); + } + else { + CLOG_INFO(LOG, 2, "read_drop_data, unhandled!"); } - CLOG_INFO(LOG, 2, "drop_read_uris_fn file_count=%d", flist->count); - const int event_xy[2] = {WL_FIXED_TO_INT_FOR_WINDOW_V2(win, xy)}; - system->pushEvent_maybe_pending(new GHOST_EventDragnDrop(event_ms, - GHOST_kEventDraggingDropDone, - GHOST_kDragnDropTypeFilenames, - win, - UNPACK2(event_xy), - flist)); + free(const_cast(data_buf)); } - else if (ELEM(mime_receive, ghost_wl_mime_text_plain, ghost_wl_mime_text_utf8)) { - /* TODO: enable use of internal functions 'txt_insert_buf' and - * 'text_update_edited' to behave like dropped text was pasted. */ - CLOG_INFO(LOG, 2, "drop_read_uris_fn (text_plain, text_utf8), unhandled!"); - } - wl_display_roundtrip(system->wl_display_get()); }; /* Pass in `seat->wl_surface_window_focus_dnd` instead of accessing it from `seat` since the * leave callback (#data_device_handle_leave) will clear the value once this function starts. */ std::thread read_thread( - read_uris_fn, seat, data_offer, seat->wl.surface_window_focus_dnd, mime_receive); + read_drop_data_fn, seat, data_offer, seat->wl.surface_window_focus_dnd, mime_receive); read_thread.detach(); }