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.
This commit is contained in:
Campbell Barton 2024-02-16 14:26:58 +11:00
parent eafecb2bc3
commit 82c753a940
4 changed files with 95 additions and 62 deletions

View File

@ -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;

View File

@ -12,6 +12,7 @@
#include <cstdlib>
#include <cstring>
#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);

View File

@ -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);

View File

@ -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<char *>(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<std::string> uris;
const std::string_view data = std::string_view(data_buf, data_buf_len);
std::vector<std::string_view> 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<GHOST_TStringArray *>(
malloc(sizeof(GHOST_TStringArray)));
flist->count = int(uris.size());
flist->strings = static_cast<uint8_t **>(malloc(uris.size() * sizeof(uint8_t *)));
for (size_t i = 0; i < uris.size(); i++) {
flist->strings[i] = reinterpret_cast<uint8_t *>(
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<GHOST_TStringArray *>(
malloc(sizeof(GHOST_TStringArray)));
flist->count = int(uris.size());
flist->strings = static_cast<uint8_t **>(malloc(uris.size() * sizeof(uint8_t *)));
for (size_t i = 0; i < uris.size(); i++) {
flist->strings[i] = reinterpret_cast<uint8_t *>(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<char *>(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();
}