[client] added streaming clipboard support for large transfers

This commit is contained in:
Geoffrey McRae 2020-12-03 12:01:51 +11:00
parent d700e19a32
commit e57f084c93
5 changed files with 139 additions and 47 deletions

View File

@ -38,6 +38,9 @@ struct state
LG_ClipboardDataFn dataFn; LG_ClipboardDataFn dataFn;
LG_ClipboardData type; LG_ClipboardData type;
bool incrStart;
unsigned int lowerBound;
// XFixes vars // XFixes vars
int eventBase; int eventBase;
int errorBase; int errorBase;
@ -77,7 +80,7 @@ static bool x11_cb_init(
this->display = wminfo->info.x11.display; this->display = wminfo->info.x11.display;
this->window = wminfo->info.x11.window; this->window = wminfo->info.x11.window;
this->aSelection = XInternAtom(this->display, "CLIPBOARD", False); this->aSelection = XInternAtom(this->display, "CLIPBOARD" , False);
this->aTargets = XInternAtom(this->display, "TARGETS" , False); this->aTargets = XInternAtom(this->display, "TARGETS" , False);
this->aSelData = XInternAtom(this->display, "SEL_DATA" , False); this->aSelData = XInternAtom(this->display, "SEL_DATA" , False);
this->aIncr = XInternAtom(this->display, "INCR" , False); this->aIncr = XInternAtom(this->display, "INCR" , False);
@ -225,6 +228,81 @@ static void x11_cb_xfixes_selection_notify(const XFixesSelectionNotifyEvent e)
return; return;
} }
static void x11_cb_selection_incr(const XPropertyEvent e)
{
Atom type;
int format;
unsigned long itemCount, after;
unsigned char *data;
if (XGetWindowProperty(
e.display,
e.window,
e.atom,
0, ~0L, // start and length
True, // delete the property
this->aIncr,
&type,
&format,
&itemCount,
&after,
&data) != Success)
{
DEBUG_INFO("GetProp Failed");
this->notifyFn(LG_CLIPBOARD_DATA_NONE, 0);
goto out;
}
LG_ClipboardData dataType;
for(dataType = 0; dataType < LG_CLIPBOARD_DATA_NONE; ++dataType)
if (this->aTypes[dataType] == type)
break;
if (dataType == LG_CLIPBOARD_DATA_NONE)
{
DEBUG_WARN("clipboard data (%s) not in a supported format",
XGetAtomName(this->display, type));
this->lowerBound = 0;
this->notifyFn(LG_CLIPBOARD_DATA_NONE, 0);
goto out;
}
if (this->incrStart)
{
this->notifyFn(dataType, this->lowerBound);
this->incrStart = false;
}
XFree(data);
data = NULL;
if (XGetWindowProperty(
e.display,
e.window,
e.atom,
0, ~0L, // start and length
True, // delete the property
type,
&type,
&format,
&itemCount,
&after,
&data) != Success)
{
DEBUG_ERROR("XGetWindowProperty Failed");
this->notifyFn(LG_CLIPBOARD_DATA_NONE, 0);
goto out;
}
this->dataFn(dataType, data, itemCount);
this->lowerBound -= itemCount;
out:
if (data)
XFree(data);
}
static void x11_cb_selection_notify(const XSelectionEvent e) static void x11_cb_selection_notify(const XSelectionEvent e)
{ {
if (e.property == None) if (e.property == None)
@ -235,9 +313,9 @@ static void x11_cb_selection_notify(const XSelectionEvent e)
unsigned long itemCount, after; unsigned long itemCount, after;
unsigned char *data; unsigned char *data;
XGetWindowProperty( if (XGetWindowProperty(
this->display, e.display,
this->window, e.requestor,
e.property, e.property,
0, ~0L, // start and length 0, ~0L, // start and length
True , // delete the property True , // delete the property
@ -246,12 +324,16 @@ static void x11_cb_selection_notify(const XSelectionEvent e)
&format, &format,
&itemCount, &itemCount,
&after, &after,
&data); &data) != Success)
{
this->notifyFn(LG_CLIPBOARD_DATA_NONE, 0);
goto out;
}
if (type == this->aIncr) if (type == this->aIncr)
{ {
DEBUG_WARN("fixme: large paste buffers are not yet supported"); this->incrStart = true;
this->notifyFn(LG_CLIPBOARD_DATA_NONE); this->lowerBound = *(unsigned int *)data;
goto out; goto out;
} }
@ -272,26 +354,31 @@ static void x11_cb_selection_notify(const XSelectionEvent e)
if (this->aTypes[n] == targets[i]) if (this->aTypes[n] == targets[i])
{ {
// we have a match, so send the notification // we have a match, so send the notification
this->notifyFn(n); this->notifyFn(n, 0);
goto out; goto out;
} }
} }
// no matches // no matches
this->notifyFn(LG_CLIPBOARD_DATA_NONE); this->notifyFn(LG_CLIPBOARD_DATA_NONE, 0);
goto out; goto out;
} }
if (e.property == this->aSelData) if (e.property == this->aSelData)
{ {
for(int i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i) LG_ClipboardData dataType;
if (this->aTypes[i] == type) for(dataType = 0; dataType < LG_CLIPBOARD_DATA_NONE; ++dataType)
if (this->aTypes[dataType] == type)
break;
if (dataType == LG_CLIPBOARD_DATA_NONE)
{ {
this->dataFn(i, data, itemCount); DEBUG_WARN("clipboard data (%s) not in a supported format",
XGetAtomName(this->display, type));
goto out; goto out;
} }
DEBUG_WARN("clipboard data (%s) not in a supported format", XGetAtomName(this->display, type)); this->dataFn(dataType, data, itemCount);
goto out; goto out;
} }
@ -318,6 +405,17 @@ static void x11_cb_wmevent(SDL_SysWMmsg * msg)
x11_cb_selection_notify(e.xselection); x11_cb_selection_notify(e.xselection);
break; break;
case PropertyNotify:
if (e.xproperty.display != this->display ||
e.xproperty.window != this->window ||
e.xproperty.atom != this->aSelData ||
e.xproperty.state != PropertyNewValue ||
this->lowerBound == 0)
break;
x11_cb_selection_incr(e.xproperty);
break;
default: default:
if (e.type == this->eventBase + XFixesSelectionNotify) if (e.type == this->eventBase + XFixesSelectionNotify)
{ {

View File

@ -38,7 +38,7 @@ LG_ClipboardData;
typedef void (* LG_ClipboardReplyFn )(void * opaque, const LG_ClipboardData type, uint8_t * data, uint32_t size); typedef void (* LG_ClipboardReplyFn )(void * opaque, const LG_ClipboardData type, uint8_t * data, uint32_t size);
typedef void (* LG_ClipboardRequestFn)(LG_ClipboardReplyFn replyFn, void * opaque); typedef void (* LG_ClipboardRequestFn)(LG_ClipboardReplyFn replyFn, void * opaque);
typedef void (* LG_ClipboardReleaseFn)(); typedef void (* LG_ClipboardReleaseFn)();
typedef void (* LG_ClipboardNotifyFn)(LG_ClipboardData type); typedef void (* LG_ClipboardNotifyFn)(LG_ClipboardData type, size_t size);
typedef void (* LG_ClipboardDataFn )(const LG_ClipboardData type, uint8_t * data, size_t size); typedef void (* LG_ClipboardDataFn )(const LG_ClipboardData type, uint8_t * data, size_t size);
typedef const char * (* LG_ClipboardGetName)(); typedef const char * (* LG_ClipboardGetName)();

View File

@ -644,7 +644,7 @@ void clipboardRelease()
spice_clipboard_release(); spice_clipboard_release();
} }
void clipboardNotify(const LG_ClipboardData type) void clipboardNotify(const LG_ClipboardData type, size_t size)
{ {
if (!params.clipboardToVM) if (!params.clipboardToVM)
return; return;
@ -655,7 +655,14 @@ void clipboardNotify(const LG_ClipboardData type)
return; return;
} }
spice_clipboard_grab(clipboard_type_to_spice_type(type)); state.cbType = clipboard_type_to_spice_type(type);
state.cbChunked = size > 0;
state.cbXfer = size;
spice_clipboard_grab(state.cbType);
if (size)
spice_clipboard_data_start(state.cbType, size);
} }
void clipboardData(const LG_ClipboardData type, uint8_t * data, size_t size) void clipboardData(const LG_ClipboardData type, uint8_t * data, size_t size)
@ -663,32 +670,17 @@ void clipboardData(const LG_ClipboardData type, uint8_t * data, size_t size)
if (!params.clipboardToVM) if (!params.clipboardToVM)
return; return;
uint8_t * buffer = data; if (state.cbChunked && size > state.cbXfer)
// unix2dos
if (type == LG_CLIPBOARD_DATA_TEXT)
{ {
// TODO: make this more memory efficent DEBUG_ERROR("refusing to send more then cbXfer bytes for chunked xfer");
size_t newSize = 0; size = state.cbXfer;
buffer = malloc(size * 2);
uint8_t * p = buffer;
for(uint32_t i = 0; i < size; ++i)
{
uint8_t c = data[i];
if (c == '\n')
{
*p++ = '\r';
++newSize;
}
*p++ = c;
++newSize;
}
size = newSize;
} }
spice_clipboard_data(clipboard_type_to_spice_type(type), buffer, (uint32_t)size); if (!state.cbChunked)
if (buffer != data) spice_clipboard_data_start(state.cbType, size);
free(buffer);
spice_clipboard_data(state.cbType, data, (uint32_t)size);
state.cbXfer -= size;
} }
void clipboardRequest(const LG_ClipboardReplyFn replyFn, void * opaque) void clipboardRequest(const LG_ClipboardReplyFn replyFn, void * opaque)

View File

@ -92,6 +92,8 @@ struct AppState
const LG_Clipboard * lgc; const LG_Clipboard * lgc;
SpiceDataType cbType; SpiceDataType cbType;
bool cbChunked;
size_t cbXfer;
struct ll * cbRequestList; struct ll * cbRequestList;
SDL_SysWMinfo wminfo; SDL_SysWMinfo wminfo;

@ -1 +1 @@
Subproject commit c83a899ffd99c2d7de458ce15a39e4dc13211076 Subproject commit 749395f1f5a806cd03d0b0816004d89a58dc8c38