mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-11-10 08:38:20 +00:00
[client] spice: implemented guest -> client clipboard sync
This commit is contained in:
parent
108369414e
commit
9dd4e4756b
@ -18,8 +18,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
//#define DEBUG_SPICE
|
|
||||||
|
|
||||||
#include "spice.h"
|
#include "spice.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
@ -42,6 +40,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include <spice/protocol.h>
|
#include <spice/protocol.h>
|
||||||
#include <spice/vd_agent.h>
|
#include <spice/vd_agent.h>
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include "messages.h"
|
#include "messages.h"
|
||||||
#include "rsa.h"
|
#include "rsa.h"
|
||||||
|
|
||||||
@ -57,7 +57,10 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#define DEBUG_KEYBOARD(fmt, args...) do {} while(0)
|
#define DEBUG_KEYBOARD(fmt, args...) do {} while(0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define SPICE_AGENT_TOKENS_MAX 10
|
// we don't really need flow control because we are all local
|
||||||
|
// instead do what the spice-gtk library does and provide the largest
|
||||||
|
// possible number
|
||||||
|
#define SPICE_AGENT_TOKENS_MAX ~0
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
@ -103,7 +106,8 @@ struct Spice
|
|||||||
union SpiceAddr addr;
|
union SpiceAddr addr;
|
||||||
|
|
||||||
bool hasAgent;
|
bool hasAgent;
|
||||||
uint32_t agentTokens;
|
uint32_t serverTokens;
|
||||||
|
uint32_t clientTokens;
|
||||||
uint32_t sessionID;
|
uint32_t sessionID;
|
||||||
uint32_t channelID;
|
uint32_t channelID;
|
||||||
struct SpiceChannel scMain;
|
struct SpiceChannel scMain;
|
||||||
@ -111,6 +115,9 @@ struct Spice
|
|||||||
|
|
||||||
struct SpiceKeyboard kb;
|
struct SpiceKeyboard kb;
|
||||||
struct SpiceMouse mouse;
|
struct SpiceMouse mouse;
|
||||||
|
|
||||||
|
bool cbSupported;
|
||||||
|
bool cbSelection;
|
||||||
};
|
};
|
||||||
|
|
||||||
// globals
|
// globals
|
||||||
@ -418,7 +425,7 @@ bool spice_on_main_channel_read()
|
|||||||
|
|
||||||
spice.sessionID = msg.session_id;
|
spice.sessionID = msg.session_id;
|
||||||
|
|
||||||
spice.agentTokens = msg.agent_tokens;
|
spice.serverTokens = msg.agent_tokens;
|
||||||
spice.hasAgent = msg.agent_connected;
|
spice.hasAgent = msg.agent_connected;
|
||||||
if (spice.hasAgent && !spice_agent_connect())
|
if (spice.hasAgent && !spice_agent_connect())
|
||||||
{
|
{
|
||||||
@ -515,7 +522,7 @@ bool spice_on_main_channel_read()
|
|||||||
}
|
}
|
||||||
|
|
||||||
spice.hasAgent = true;
|
spice.hasAgent = true;
|
||||||
spice.agentTokens = num_tokens;
|
spice.serverTokens = num_tokens;
|
||||||
if (!spice_agent_connect())
|
if (!spice_agent_connect())
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("failed to connect to spice agent");
|
DEBUG_ERROR("failed to connect to spice agent");
|
||||||
@ -849,8 +856,8 @@ bool spice_agent_connect()
|
|||||||
{
|
{
|
||||||
DEBUG_INFO("Spice agent available, sending start");
|
DEBUG_INFO("Spice agent available, sending start");
|
||||||
|
|
||||||
const uint32_t tokens = SPICE_AGENT_TOKENS_MAX;
|
spice.clientTokens = SPICE_AGENT_TOKENS_MAX;
|
||||||
if (!spice_write_msg(&spice.scMain, SPICE_MSGC_MAIN_AGENT_START, &tokens, sizeof(tokens)))
|
if (!spice_write_msg(&spice.scMain, SPICE_MSGC_MAIN_AGENT_START, &spice.clientTokens, sizeof(spice.clientTokens)))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("failed to send agent start message");
|
DEBUG_ERROR("failed to send agent start message");
|
||||||
return false;
|
return false;
|
||||||
@ -868,6 +875,14 @@ bool spice_agent_process()
|
|||||||
{
|
{
|
||||||
VDAgentMessage msg;
|
VDAgentMessage msg;
|
||||||
|
|
||||||
|
#pragma pack(push,1)
|
||||||
|
struct Selection
|
||||||
|
{
|
||||||
|
uint8_t selection;
|
||||||
|
uint8_t reserved[3];
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
if (!spice_read_nl(&spice.scMain, &msg, sizeof(msg)))
|
if (!spice_read_nl(&spice.scMain, &msg, sizeof(msg)))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("failed to read spice agent message");
|
DEBUG_ERROR("failed to read spice agent message");
|
||||||
@ -880,40 +895,157 @@ bool spice_agent_process()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.type == VD_AGENT_ANNOUNCE_CAPABILITIES)
|
switch(msg.type)
|
||||||
{
|
{
|
||||||
VDAgentAnnounceCapabilities *caps = (VDAgentAnnounceCapabilities *)malloc(msg.size);
|
case VD_AGENT_ANNOUNCE_CAPABILITIES:
|
||||||
memset(caps, 0, msg.size);
|
|
||||||
|
|
||||||
if (!spice_read_nl(&spice.scMain, caps, msg.size))
|
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("failed to read agent message payload");
|
VDAgentAnnounceCapabilities *caps = (VDAgentAnnounceCapabilities *)malloc(msg.size);
|
||||||
|
memset(caps, 0, msg.size);
|
||||||
|
|
||||||
|
if (!spice_read_nl(&spice.scMain, caps, msg.size))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("failed to read agent message payload");
|
||||||
|
free(caps);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int capsSize = VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(msg.size);
|
||||||
|
spice.cbSupported = VD_AGENT_HAS_CAPABILITY(caps->caps, capsSize, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND) ||
|
||||||
|
VD_AGENT_HAS_CAPABILITY(caps->caps, capsSize, VD_AGENT_CAP_CLIPBOARD_SELECTION);
|
||||||
|
spice.cbSelection = VD_AGENT_HAS_CAPABILITY(caps->caps, capsSize, VD_AGENT_CAP_CLIPBOARD_SELECTION);
|
||||||
|
|
||||||
|
if (spice.cbSupported)
|
||||||
|
DEBUG_INFO("clipboard capability detected");
|
||||||
|
|
||||||
|
if (caps->request && !spice_agent_send_caps(false))
|
||||||
|
{
|
||||||
|
free(caps);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
free(caps);
|
free(caps);
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int capsSize = VD_AGENT_CAPS_SIZE_FROM_MSG_SIZE(msg.size);
|
case VD_AGENT_CLIPBOARD:
|
||||||
if (VD_AGENT_HAS_CAPABILITY(caps->caps, capsSize, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND))
|
case VD_AGENT_CLIPBOARD_REQUEST:
|
||||||
|
case VD_AGENT_CLIPBOARD_GRAB:
|
||||||
|
case VD_AGENT_CLIPBOARD_RELEASE:
|
||||||
{
|
{
|
||||||
DEBUG_INFO("clipboard capability detected");
|
uint32_t remaining = msg.size;
|
||||||
|
if (spice.cbSelection)
|
||||||
|
{
|
||||||
|
struct Selection selection;
|
||||||
|
if (!spice_read_nl(&spice.scMain, &selection, sizeof(selection)))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("failed to read the clipboard selection");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
remaining -= sizeof(selection);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == VD_AGENT_CLIPBOARD_RELEASE)
|
||||||
|
{
|
||||||
|
DEBUG_PROTO("VD_AGENT_CLIPBOARD_RELEASE");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == VD_AGENT_CLIPBOARD || msg.type == VD_AGENT_CLIPBOARD_REQUEST)
|
||||||
|
{
|
||||||
|
uint32_t type;
|
||||||
|
if (!spice_read_nl(&spice.scMain, &type, sizeof(type)))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("failed to read the clipboard data type");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
remaining -= sizeof(type);
|
||||||
|
|
||||||
|
if (msg.type == VD_AGENT_CLIPBOARD)
|
||||||
|
{
|
||||||
|
DEBUG_PROTO("VD_AGENT_CLIPBOARD");
|
||||||
|
if (type != VD_AGENT_CLIPBOARD_UTF8_TEXT)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("for some reason we were sent a non text clipboard, this shouldn't happen");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char * text = malloc(remaining + 1);
|
||||||
|
if (!spice_read_nl(&spice.scMain, text, remaining))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("failed to read the clipboard data");
|
||||||
|
free(text);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
text[remaining] = '\0';
|
||||||
|
|
||||||
|
// dos2unix
|
||||||
|
char * out = malloc(remaining + 1);
|
||||||
|
char * p = out;
|
||||||
|
for(uint32_t i = 0; i < remaining; ++i)
|
||||||
|
{
|
||||||
|
char c = text[i];
|
||||||
|
if (c != '\r')
|
||||||
|
*p++ = c;
|
||||||
|
}
|
||||||
|
*p = '\0';
|
||||||
|
SDL_SetClipboardText(out);
|
||||||
|
|
||||||
|
free(text);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DEBUG_PROTO("VD_AGENT_CLIPBOARD_REQUEST");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DEBUG_PROTO("VD_AGENT_CLIPBOARD_GRAB");
|
||||||
|
if (remaining == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
uint32_t *types = malloc(remaining);
|
||||||
|
if (!spice_read_nl(&spice.scMain, types, remaining))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("failed to read the clipboard grab types");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// there is zero documentation on the types field, it might be a bitfield
|
||||||
|
// but for now we are going to assume it's not.
|
||||||
|
|
||||||
|
// for now we only support text
|
||||||
|
if (types[0] == VD_AGENT_CLIPBOARD_UTF8_TEXT)
|
||||||
|
{
|
||||||
|
if (spice.cbSelection)
|
||||||
|
{
|
||||||
|
// Windows doesnt support this, so until it's needed no point messing with it
|
||||||
|
DEBUG_ERROR("Fixme!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VDAgentClipboardRequest req;
|
||||||
|
req.type = VD_AGENT_CLIPBOARD_UTF8_TEXT;
|
||||||
|
|
||||||
|
if (!spice_agent_write_msg(VD_AGENT_CLIPBOARD_REQUEST, &req, sizeof(req)))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("failed to request clipboard data");
|
||||||
|
free(types);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(types);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
DEBUG_INFO("no clipboard capability detected");
|
|
||||||
|
|
||||||
|
|
||||||
if (caps->request && !spice_agent_send_caps(false))
|
|
||||||
{
|
|
||||||
free(caps);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(caps);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_WARN("unknown agent message type %d", msg.type);
|
DEBUG_WARN("unknown agent message type %d", msg.type);
|
||||||
spice_discard_nl(&spice.scMain, msg.size);
|
spice_discard_nl(&spice.scMain, msg.size);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user