mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-09-06 19:38:14 +00:00
Support X11 incremental clipboard transfers.
This adds support for pasting large clipboard contents using the INCR property mechanism Fixes #8871
This commit is contained in:

committed by
Sam Lantinga

parent
652b34bd15
commit
6bb91c7c08
@@ -53,6 +53,8 @@ static Window GetWindow(SDL_VideoDevice *_this)
|
|||||||
data->clipboard_window = X11_XCreateWindow(dpy, parent, -10, -10, 1, 1, 0,
|
data->clipboard_window = X11_XCreateWindow(dpy, parent, -10, -10, 1, 1, 0,
|
||||||
CopyFromParent, InputOnly,
|
CopyFromParent, InputOnly,
|
||||||
CopyFromParent, 0, &xattr);
|
CopyFromParent, 0, &xattr);
|
||||||
|
|
||||||
|
X11_XSelectInput(dpy, data->clipboard_window, PropertyChangeMask);
|
||||||
X11_XFlush(data->display);
|
X11_XFlush(data->display);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,19 +98,64 @@ static int SetSelectionData(SDL_VideoDevice *_this, Atom selection, SDL_Clipboar
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *CloneDataBuffer(const void *buffer, size_t *len)
|
static void *CloneDataBuffer(const void *buffer, const size_t len)
|
||||||
{
|
{
|
||||||
void *clone = NULL;
|
void *clone = NULL;
|
||||||
if (*len > 0 && buffer) {
|
if (len > 0 && buffer) {
|
||||||
clone = SDL_malloc((*len)+sizeof(Uint32));
|
clone = SDL_malloc(len + sizeof(Uint32));
|
||||||
if (clone) {
|
if (clone) {
|
||||||
SDL_memcpy(clone, buffer, *len);
|
SDL_memcpy(clone, buffer, len);
|
||||||
SDL_memset((Uint8 *)clone + *len, 0, sizeof(Uint32));
|
SDL_memset((Uint8 *)clone + len, 0, sizeof(Uint32));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* original_buffer is considered unusable after the function is called.
|
||||||
|
*/
|
||||||
|
static void *AppendDataBuffer(void *original_buffer, const size_t old_len, const void *buffer, const size_t buffer_len)
|
||||||
|
{
|
||||||
|
void *resized_buffer;
|
||||||
|
|
||||||
|
if (buffer_len > 0 && buffer) {
|
||||||
|
resized_buffer = SDL_realloc(original_buffer, old_len + buffer_len + sizeof(Uint32));
|
||||||
|
if (resized_buffer) {
|
||||||
|
SDL_memcpy((Uint8 *)resized_buffer + old_len, buffer, buffer_len);
|
||||||
|
SDL_memset((Uint8 *)resized_buffer + old_len + buffer_len, 0, sizeof(Uint32));
|
||||||
|
}
|
||||||
|
|
||||||
|
return resized_buffer;
|
||||||
|
} else {
|
||||||
|
return original_buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SDL_bool WaitForSelection(SDL_VideoDevice *_this, Atom selection_type, SDL_bool *flag)
|
||||||
|
{
|
||||||
|
Uint64 waitStart;
|
||||||
|
Uint64 waitElapsed;
|
||||||
|
|
||||||
|
waitStart = SDL_GetTicks();
|
||||||
|
*flag = SDL_TRUE;
|
||||||
|
while (*flag) {
|
||||||
|
SDL_PumpEvents();
|
||||||
|
waitElapsed = SDL_GetTicks() - waitStart;
|
||||||
|
/* Wait one second for a selection response. */
|
||||||
|
if (waitElapsed > 1000) {
|
||||||
|
*flag = SDL_FALSE;
|
||||||
|
SDL_SetError("Selection timeout");
|
||||||
|
/* We need to set the selection text so that next time we won't
|
||||||
|
timeout, otherwise we will hang on every call to this function. */
|
||||||
|
SetSelectionData(_this, selection_type, SDL_ClipboardTextCallback, NULL,
|
||||||
|
text_mime_types, SDL_arraysize(text_mime_types), 0);
|
||||||
|
return SDL_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SDL_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
static void *GetSelectionData(SDL_VideoDevice *_this, Atom selection_type,
|
static void *GetSelectionData(SDL_VideoDevice *_this, Atom selection_type,
|
||||||
const char *mime_type, size_t *length)
|
const char *mime_type, size_t *length)
|
||||||
{
|
{
|
||||||
@@ -121,12 +168,11 @@ static void *GetSelectionData(SDL_VideoDevice *_this, Atom selection_type,
|
|||||||
int seln_format;
|
int seln_format;
|
||||||
unsigned long count;
|
unsigned long count;
|
||||||
unsigned long overflow;
|
unsigned long overflow;
|
||||||
Uint64 waitStart;
|
|
||||||
Uint64 waitElapsed;
|
|
||||||
|
|
||||||
SDLX11_ClipboardData *clipboard;
|
SDLX11_ClipboardData *clipboard;
|
||||||
void *data = NULL;
|
void *data = NULL;
|
||||||
unsigned char *src = NULL;
|
unsigned char *src = NULL;
|
||||||
|
SDL_bool incr_success = SDL_FALSE;
|
||||||
Atom XA_MIME = X11_XInternAtom(display, mime_type, False);
|
Atom XA_MIME = X11_XInternAtom(display, mime_type, False);
|
||||||
Atom XA_INCR = X11_XInternAtom(display, "INCR", False);
|
Atom XA_INCR = X11_XInternAtom(display, "INCR", False);
|
||||||
|
|
||||||
@@ -148,7 +194,7 @@ static void *GetSelectionData(SDL_VideoDevice *_this, Atom selection_type,
|
|||||||
|
|
||||||
if (clipboard->callback) {
|
if (clipboard->callback) {
|
||||||
const void *clipboard_data = clipboard->callback(clipboard->userdata, mime_type, length);
|
const void *clipboard_data = clipboard->callback(clipboard->userdata, mime_type, length);
|
||||||
data = CloneDataBuffer(clipboard_data, length);
|
data = CloneDataBuffer(clipboard_data, *length);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Request that the selection owner copy the data to our window */
|
/* Request that the selection owner copy the data to our window */
|
||||||
@@ -157,35 +203,55 @@ static void *GetSelectionData(SDL_VideoDevice *_this, Atom selection_type,
|
|||||||
X11_XConvertSelection(display, selection_type, XA_MIME, selection, owner,
|
X11_XConvertSelection(display, selection_type, XA_MIME, selection, owner,
|
||||||
CurrentTime);
|
CurrentTime);
|
||||||
|
|
||||||
/* When using synergy on Linux and when data has been put in the clipboard
|
if (WaitForSelection(_this, selection_type, &videodata->selection_waiting) == SDL_FALSE) {
|
||||||
on the remote (Windows anyway) machine then selection_waiting may never
|
|
||||||
be set to False. Time out after a while. */
|
|
||||||
waitStart = SDL_GetTicks();
|
|
||||||
videodata->selection_waiting = SDL_TRUE;
|
|
||||||
while (videodata->selection_waiting) {
|
|
||||||
SDL_PumpEvents();
|
|
||||||
waitElapsed = SDL_GetTicks() - waitStart;
|
|
||||||
/* Wait one second for a selection response. */
|
|
||||||
if (waitElapsed > 1000) {
|
|
||||||
videodata->selection_waiting = SDL_FALSE;
|
|
||||||
SDL_SetError("Selection timeout");
|
|
||||||
/* We need to set the selection text so that next time we won't
|
|
||||||
timeout, otherwise we will hang on every call to this function. */
|
|
||||||
SetSelectionData(_this, selection_type, SDL_ClipboardTextCallback, NULL,
|
|
||||||
text_mime_types, SDL_arraysize(text_mime_types), 0);
|
|
||||||
data = NULL;
|
data = NULL;
|
||||||
*length = 0;
|
*length = 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (X11_XGetWindowProperty(display, owner, selection, 0, INT_MAX / 4, False,
|
if (X11_XGetWindowProperty(display, owner, selection, 0, INT_MAX / 4, False,
|
||||||
XA_MIME, &seln_type, &seln_format, &count, &overflow, &src) == Success) {
|
XA_MIME, &seln_type, &seln_format, &count, &overflow, &src) == Success) {
|
||||||
if (seln_type == XA_MIME) {
|
if (seln_type == XA_MIME) {
|
||||||
*length = (size_t)count;
|
*length = (size_t)count;
|
||||||
data = CloneDataBuffer(src, length);
|
data = CloneDataBuffer(src, count);
|
||||||
} else if (seln_type == XA_INCR) {
|
} else if (seln_type == XA_INCR) {
|
||||||
/* FIXME: Need to implement the X11 INCR protocol */
|
while (1) {
|
||||||
/*SDL_Log("Need to implement the X11 INCR protocol");*/
|
// Only delete the property after being done with the previous "chunk".
|
||||||
|
X11_XDeleteProperty(display, owner, selection);
|
||||||
|
X11_XFlush(display);
|
||||||
|
|
||||||
|
if (WaitForSelection(_this, selection_type, &videodata->selection_incr_waiting) == SDL_FALSE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
X11_XFree(src);
|
||||||
|
if (X11_XGetWindowProperty(display, owner, selection, 0, INT_MAX / 4, False,
|
||||||
|
XA_MIME, &seln_type, &seln_format, &count, &overflow, &src) != Success) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0) {
|
||||||
|
incr_success = SDL_TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*length == 0) {
|
||||||
|
*length = (size_t)count;
|
||||||
|
data = CloneDataBuffer(src, count);
|
||||||
|
} else {
|
||||||
|
data = AppendDataBuffer(data, *length, src, count);
|
||||||
|
*length += (size_t)count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (incr_success == SDL_FALSE) {
|
||||||
|
SDL_free(data);
|
||||||
|
data = 0;
|
||||||
|
*length = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
X11_XFree(src);
|
X11_XFree(src);
|
||||||
}
|
}
|
||||||
|
@@ -764,6 +764,19 @@ static void X11_HandleClipboardEvent(SDL_VideoDevice *_this, const XEvent *xeven
|
|||||||
SDL_zerop(clipboard);
|
SDL_zerop(clipboard);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case PropertyNotify:
|
||||||
|
{
|
||||||
|
char *name_of_atom = X11_XGetAtomName(display, xevent->xproperty.atom);
|
||||||
|
|
||||||
|
if (SDL_strncmp(name_of_atom, "SDL_SELECTION", sizeof("SDL_SELECTION") - 1) == 0 && xevent->xproperty.state == PropertyNewValue) {
|
||||||
|
videodata->selection_incr_waiting = SDL_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name_of_atom) {
|
||||||
|
X11_XFree(name_of_atom);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -103,6 +103,7 @@ struct SDL_VideoData
|
|||||||
|
|
||||||
SDL_Scancode key_layout[256];
|
SDL_Scancode key_layout[256];
|
||||||
SDL_bool selection_waiting;
|
SDL_bool selection_waiting;
|
||||||
|
SDL_bool selection_incr_waiting;
|
||||||
|
|
||||||
SDL_bool broken_pointer_grab; /* true if XGrabPointer seems unreliable. */
|
SDL_bool broken_pointer_grab; /* true if XGrabPointer seems unreliable. */
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user