Wayland: Make data offer reading a generic utility

This will be needed for drag and drop reception as well.

(cherry picked from commit f010335b8c)
This commit is contained in:
Camilla Löwy 2022-03-29 19:00:18 +02:00
parent 4d0e98d90c
commit 50d5de7cd4
2 changed files with 73 additions and 75 deletions

View File

@ -266,7 +266,6 @@ typedef struct _GLFWlibraryWayland
int keyboardLastKey; int keyboardLastKey;
int keyboardLastScancode; int keyboardLastScancode;
char* clipboardString; char* clipboardString;
size_t clipboardSize;
int timerfd; int timerfd;
short int keycodes[256]; short int keycodes[256];
short int scancodes[GLFW_KEY_LAST + 1]; short int scancodes[GLFW_KEY_LAST + 1];

View File

@ -975,6 +975,70 @@ static void handleEvents(double* timeout)
} }
} }
// Reads the specified data offer as the specified MIME type
//
static char* readDataOfferAsString(struct wl_data_offer* offer, const char* mimeType)
{
int fds[2];
if (pipe2(fds, O_CLOEXEC) == -1)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Failed to create pipe for data offer: %s",
strerror(errno));
return NULL;
}
wl_data_offer_receive(offer, mimeType, fds[1]);
flushDisplay();
close(fds[1]);
char* string = NULL;
size_t size = 0;
size_t length = 0;
for (;;)
{
const size_t readSize = 4096;
const size_t requiredSize = length + readSize + 1;
if (requiredSize > size)
{
char* longer = realloc(string, requiredSize);
if (!longer)
{
_glfwInputError(GLFW_OUT_OF_MEMORY, NULL);
close(fds[0]);
return NULL;
}
string = longer;
size = requiredSize;
}
const ssize_t result = read(fds[0], string + length, readSize);
if (result == 0)
break;
else if (result == -1)
{
if (errno == EINTR)
continue;
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Failed to read from data offer pipe: %s",
strerror(errno));
close(fds[0]);
return NULL;
}
length += result;
}
close(fds[0]);
string[length] = '\0';
return string;
}
static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface, static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface,
int* which) int* which)
{ {
@ -2562,23 +2626,15 @@ void _glfwPlatformSetClipboardString(const char* string)
_glfw.wl.selectionSource = NULL; _glfw.wl.selectionSource = NULL;
} }
const size_t requiredSize = strlen(string) + 1; char* copy = _glfw_strdup(string);
if (requiredSize > _glfw.wl.clipboardSize) if (!copy)
{ {
free(_glfw.wl.clipboardString); _glfwInputError(GLFW_OUT_OF_MEMORY, NULL);
_glfw.wl.clipboardString = calloc(requiredSize, 1); return;
if (!_glfw.wl.clipboardString)
{
_glfwInputError(GLFW_OUT_OF_MEMORY,
"Wayland: Failed to allocate clipboard string");
return;
}
_glfw.wl.clipboardSize = requiredSize;
} }
// The argument may be a substring of the clipboard string free(_glfw.wl.clipboardString);
memmove(_glfw.wl.clipboardString, string, requiredSize); _glfw.wl.clipboardString = copy;
_glfw.wl.selectionSource = _glfw.wl.selectionSource =
wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager); wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager);
@ -2609,66 +2665,9 @@ const char* _glfwPlatformGetClipboardString(void)
if (_glfw.wl.selectionSource) if (_glfw.wl.selectionSource)
return _glfw.wl.clipboardString; return _glfw.wl.clipboardString;
int fds[2]; free(_glfw.wl.clipboardString);
_glfw.wl.clipboardString =
if (pipe2(fds, O_CLOEXEC) == -1) readDataOfferAsString(_glfw.wl.selectionOffer, "text/plain;charset=utf-8");
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Failed to create clipboard pipe: %s",
strerror(errno));
return NULL;
}
wl_data_offer_receive(_glfw.wl.selectionOffer, "text/plain;charset=utf-8", fds[1]);
flushDisplay();
close(fds[1]);
size_t length = 0;
for (;;)
{
// Grow the clipboard if we need to paste something bigger, there is no
// shrink operation yet.
const size_t readSize = 4096;
const size_t requiredSize = length + readSize + 1;
if (requiredSize > _glfw.wl.clipboardSize)
{
char* string = realloc(_glfw.wl.clipboardString, requiredSize);
if (!string)
{
_glfwInputError(GLFW_OUT_OF_MEMORY,
"Wayland: Failed to grow clipboard string");
close(fds[0]);
return NULL;
}
_glfw.wl.clipboardString = string;
_glfw.wl.clipboardSize = requiredSize;
}
// Then read from the fd to the clipboard, handling all known errors.
const ssize_t result = read(fds[0], _glfw.wl.clipboardString + length, readSize);
if (result == 0)
break;
else if (result == -1)
{
if (errno == EINTR)
continue;
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: Failed to read from clipboard pipe: %s",
strerror(errno));
close(fds[0]);
return NULL;
}
length += result;
}
close(fds[0]);
_glfw.wl.clipboardString[length] = '\0';
return _glfw.wl.clipboardString; return _glfw.wl.clipboardString;
} }