Wayland: Improve handling of pending data offers

The code assumed that all data offers were selections that supported
plaintext UTF-8.

The initial data offer events are now handled almost tolerably.  Only
selection data offers are used for clipboard string and only if they
provide plaintext UTF-8.  Drag and drop data offers are now rejected as
soon as they enter a surface.

Related to #2040
This commit is contained in:
Camilla Löwy 2022-03-23 19:58:33 +01:00
parent 89d3ea8d69
commit 8d87be1268
4 changed files with 93 additions and 25 deletions

View File

@ -315,6 +315,8 @@ information on what to include when reporting a bug.
- [Wayland] Bugfix: Data source creation error would cause double free at termination
- [Wayland] Bugfix: Partial writes of clipboard string would cause beginning to repeat
- [Wayland] Bugfix: Some errors would cause clipboard string transfer to hang
- [Wayland] Bugfix: Drag and drop data was misinterpreted as clipboard string
- [Wayland] Bugfix: MIME type matching was not performed for clipboard string
- [POSIX] Removed use of deprecated function `gettimeofday`
- [POSIX] Bugfix: `CLOCK_MONOTONIC` was not correctly tested for or enabled
- [WGL] Disabled the DWM swap interval hack for Windows 8 and later (#1072)

View File

@ -704,6 +704,16 @@ static void dataOfferHandleOffer(void* userData,
struct wl_data_offer* offer,
const char* mimeType)
{
for (unsigned int i = 0; i < _glfw.wl.offerCount; i++)
{
if (_glfw.wl.offers[i].offer == offer)
{
if (strcmp(mimeType, "text/plain;charset=utf-8") == 0)
_glfw.wl.offers[i].text_plain_utf8 = GLFW_TRUE;
break;
}
}
}
static const struct wl_data_offer_listener dataOfferListener = {
@ -714,11 +724,19 @@ static void dataDeviceHandleDataOffer(void* userData,
struct wl_data_device* device,
struct wl_data_offer* offer)
{
if (_glfw.wl.dataOffer)
wl_data_offer_destroy(_glfw.wl.dataOffer);
_GLFWofferWayland* offers =
_glfw_realloc(_glfw.wl.offers, _glfw.wl.offerCount + 1);
if (!offers)
{
_glfwInputError(GLFW_OUT_OF_MEMORY, NULL);
return;
}
_glfw.wl.dataOffer = offer;
wl_data_offer_add_listener(_glfw.wl.dataOffer, &dataOfferListener, NULL);
_glfw.wl.offers = offers;
_glfw.wl.offerCount++;
_glfw.wl.offers[_glfw.wl.offerCount - 1] = (_GLFWofferWayland) { offer };
wl_data_offer_add_listener(offer, &dataOfferListener, NULL);
}
static void dataDeviceHandleEnter(void* userData,
@ -729,6 +747,19 @@ static void dataDeviceHandleEnter(void* userData,
wl_fixed_t y,
struct wl_data_offer* offer)
{
for (unsigned int i = 0; i < _glfw.wl.offerCount; i++)
{
if (_glfw.wl.offers[i].offer == offer)
{
_glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1];
_glfw.wl.offerCount--;
// We don't yet handle drag and drop
wl_data_offer_accept(offer, serial, NULL);
wl_data_offer_destroy(offer);
break;
}
}
}
static void dataDeviceHandleLeave(void* userData,
@ -753,6 +784,26 @@ static void dataDeviceHandleSelection(void* userData,
struct wl_data_device* device,
struct wl_data_offer* offer)
{
if (_glfw.wl.selectionOffer)
{
wl_data_offer_destroy(_glfw.wl.selectionOffer);
_glfw.wl.selectionOffer = NULL;
}
for (unsigned int i = 0; i < _glfw.wl.offerCount; i++)
{
if (_glfw.wl.offers[i].offer == offer)
{
if (_glfw.wl.offers[i].text_plain_utf8)
_glfw.wl.selectionOffer = offer;
else
wl_data_offer_destroy(offer);
_glfw.wl.offers[i] = _glfw.wl.offers[_glfw.wl.offerCount - 1];
_glfw.wl.offerCount--;
break;
}
}
}
static const struct wl_data_device_listener dataDeviceListener = {
@ -1409,6 +1460,11 @@ void _glfwTerminateWayland(void)
_glfw.wl.cursor.handle = NULL;
}
for (unsigned int i = 0; i < _glfw.wl.offerCount; i++)
wl_data_offer_destroy(_glfw.wl.offers[i].offer);
_glfw_free(_glfw.wl.offers);
if (_glfw.wl.cursorSurface)
wl_surface_destroy(_glfw.wl.cursorSurface);
if (_glfw.wl.subcompositor)
@ -1423,12 +1479,12 @@ void _glfwTerminateWayland(void)
zxdg_decoration_manager_v1_destroy(_glfw.wl.decorationManager);
if (_glfw.wl.wmBase)
xdg_wm_base_destroy(_glfw.wl.wmBase);
if (_glfw.wl.dataSource)
wl_data_source_destroy(_glfw.wl.dataSource);
if (_glfw.wl.selectionOffer)
wl_data_offer_destroy(_glfw.wl.selectionOffer);
if (_glfw.wl.selectionSource)
wl_data_source_destroy(_glfw.wl.selectionSource);
if (_glfw.wl.dataDevice)
wl_data_device_destroy(_glfw.wl.dataDevice);
if (_glfw.wl.dataOffer)
wl_data_offer_destroy(_glfw.wl.dataOffer);
if (_glfw.wl.dataDeviceManager)
wl_data_device_manager_destroy(_glfw.wl.dataDeviceManager);
if (_glfw.wl.pointer)

View File

@ -219,6 +219,12 @@ typedef struct _GLFWdecorationWayland
struct wp_viewport* viewport;
} _GLFWdecorationWayland;
typedef struct _GLFWofferWayland
{
struct wl_data_offer* offer;
GLFWbool text_plain_utf8;
} _GLFWofferWayland;
// Wayland-specific per-window data
//
typedef struct _GLFWwindowWayland
@ -281,8 +287,6 @@ typedef struct _GLFWlibraryWayland
struct wl_keyboard* keyboard;
struct wl_data_device_manager* dataDeviceManager;
struct wl_data_device* dataDevice;
struct wl_data_offer* dataOffer;
struct wl_data_source* dataSource;
struct xdg_wm_base* wmBase;
struct zxdg_decoration_manager_v1* decorationManager;
struct wp_viewporter* viewporter;
@ -290,6 +294,12 @@ typedef struct _GLFWlibraryWayland
struct zwp_pointer_constraints_v1* pointerConstraints;
struct zwp_idle_inhibit_manager_v1* idleInhibitManager;
_GLFWofferWayland* offers;
unsigned int offerCount;
struct wl_data_offer* selectionOffer;
struct wl_data_source* selectionSource;
int compositorVersion;
int seatVersion;

View File

@ -1578,7 +1578,7 @@ static void dataSourceHandleTarget(void* userData,
struct wl_data_source* source,
const char* mimeType)
{
if (_glfw.wl.dataSource != source)
if (_glfw.wl.selectionSource != source)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Unknown clipboard data source");
@ -1595,7 +1595,7 @@ static void dataSourceHandleSend(void* userData,
size_t len = strlen(string);
int ret;
if (_glfw.wl.dataSource != source)
if (_glfw.wl.selectionSource != source)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Unknown clipboard data source");
@ -1643,14 +1643,14 @@ static void dataSourceHandleCancelled(void* userData,
{
wl_data_source_destroy(source);
if (_glfw.wl.dataSource != source)
if (_glfw.wl.selectionSource != source)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Unknown clipboard data source");
return;
}
_glfw.wl.dataSource = NULL;
_glfw.wl.selectionSource = NULL;
}
static const struct wl_data_source_listener dataSourceListener = {
@ -1661,10 +1661,10 @@ static const struct wl_data_source_listener dataSourceListener = {
void _glfwSetClipboardStringWayland(const char* string)
{
if (_glfw.wl.dataSource)
if (_glfw.wl.selectionSource)
{
wl_data_source_destroy(_glfw.wl.dataSource);
_glfw.wl.dataSource = NULL;
wl_data_source_destroy(_glfw.wl.selectionSource);
_glfw.wl.selectionSource = NULL;
}
char* copy = _glfw_strdup(string);
@ -1678,9 +1678,9 @@ void _glfwSetClipboardStringWayland(const char* string)
_glfw_free(_glfw.wl.clipboardSendString);
_glfw.wl.clipboardSendString = copy;
_glfw.wl.dataSource =
_glfw.wl.selectionSource =
wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager);
if (!_glfw.wl.dataSource)
if (!_glfw.wl.selectionSource)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Failed to create clipboard data source");
@ -1688,12 +1688,12 @@ void _glfwSetClipboardStringWayland(const char* string)
_glfw.wl.clipboardSendString = NULL;
return;
}
wl_data_source_add_listener(_glfw.wl.dataSource,
wl_data_source_add_listener(_glfw.wl.selectionSource,
&dataSourceListener,
NULL);
wl_data_source_offer(_glfw.wl.dataSource, "text/plain;charset=utf-8");
wl_data_source_offer(_glfw.wl.selectionSource, "text/plain;charset=utf-8");
wl_data_device_set_selection(_glfw.wl.dataDevice,
_glfw.wl.dataSource,
_glfw.wl.selectionSource,
_glfw.wl.serial);
}
@ -1719,14 +1719,14 @@ const char* _glfwGetClipboardStringWayland(void)
int ret;
size_t len = 0;
if (!_glfw.wl.dataOffer)
if (!_glfw.wl.selectionOffer)
{
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
"Wayland: No clipboard data available");
return NULL;
}
if (_glfw.wl.dataSource)
if (_glfw.wl.selectionSource)
return _glfw.wl.clipboardSendString;
ret = pipe2(fds, O_CLOEXEC);
@ -1738,7 +1738,7 @@ const char* _glfwGetClipboardStringWayland(void)
return NULL;
}
wl_data_offer_receive(_glfw.wl.dataOffer, "text/plain;charset=utf-8", fds[1]);
wl_data_offer_receive(_glfw.wl.selectionOffer, "text/plain;charset=utf-8", fds[1]);
flushDisplay();
close(fds[1]);