mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-11-25 14:57:20 +00:00
[client] added streaming clipboard support for large transfers
This commit is contained in:
parent
d700e19a32
commit
e57f084c93
@ -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,10 +80,10 @@ 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);
|
||||||
this->aCurSelection = BadValue;
|
this->aCurSelection = BadValue;
|
||||||
this->releaseFn = releaseFn;
|
this->releaseFn = releaseFn;
|
||||||
this->notifyFn = notifyFn;
|
this->notifyFn = notifyFn;
|
||||||
@ -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)
|
||||||
this->dataFn(i, data, itemCount);
|
break;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_WARN("clipboard data (%s) not in a supported format", XGetAtomName(this->display, type));
|
if (dataType == LG_CLIPBOARD_DATA_NONE)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("clipboard data (%s) not in a supported format",
|
||||||
|
XGetAtomName(this->display, type));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
|
@ -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)();
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user