summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2011-03-31 13:20:23 +0200
committerHans de Goede <hdegoede@redhat.com>2011-03-31 14:45:16 +0200
commite7c5f45256497ba0227e9c52b42831e19c8965a3 (patch)
treeb1fa008577caf559f26ba895df03261c13507d25
parent46154d26c4c1d7ff6cb4c1d00623e7e1b6ab8017 (diff)
Add support for multiple selections
Based on the initial patch by Marc-André Lureau <marcandre.lureau@redhat.com>.
-rw-r--r--vdagent-x11.c333
-rw-r--r--vdagent-x11.h15
-rw-r--r--vdagent.c9
-rw-r--r--vdagentd-proto.h8
-rw-r--r--vdagentd.c186
5 files changed, 364 insertions, 187 deletions
diff --git a/vdagent-x11.c b/vdagent-x11.c
index 34442c6..6a3094e 100644
--- a/vdagent-x11.c
+++ b/vdagent-x11.c
@@ -50,6 +50,7 @@ enum { owner_none, owner_guest, owner_client };
come in while we are still handling the current one. */
struct vdagent_x11_selection_request {
XEvent event;
+ uint8_t selection;
struct vdagent_x11_selection_request *next;
};
@@ -59,6 +60,7 @@ struct vdagent_x11_selection_request {
these one at a time. */
struct vdagent_x11_conversion_request {
Atom target;
+ uint8_t selection;
struct vdagent_x11_conversion_request *next;
};
@@ -106,16 +108,18 @@ struct vdagent_x11 {
int has_xfixes;
int xfixes_event_base;
int max_prop_size;
- int expected_targets_notifies;
- int expect_property_notify;
- int clipboard_owner;
- int clipboard_type_count;
- uint32_t clipboard_agent_types[256];
- Atom clipboard_x11_targets[256];
+ int expected_targets_notifies[256];
+ int clipboard_owner[256];
+ int clipboard_type_count[256];
+ uint32_t clipboard_agent_types[256][256];
+ Atom clipboard_x11_targets[256][256];
+ /* Data for conversion_req which is currently being processed */
struct vdagent_x11_conversion_request *conversion_req;
+ int expect_property_notify;
uint8_t *clipboard_data;
uint32_t clipboard_data_size;
uint32_t clipboard_data_space;
+ /* Data for selection_req which is currently being processed */
struct vdagent_x11_selection_request *selection_req;
uint8_t *selection_req_data;
uint32_t selection_req_data_pos;
@@ -134,7 +138,7 @@ static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11 *x11,
static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11,
Atom prop, struct vdagent_x11_selection_request *request);
static void vdagent_x11_set_clipboard_owner(struct vdagent_x11 *x11,
- int new_owner);
+ uint8_t selection, int new_owner);
struct vdagent_x11 *vdagent_x11_create(struct udscs_connection *vdagentd,
FILE *errfile, int verbose)
@@ -235,10 +239,15 @@ struct vdagent_x11 *vdagent_x11_create(struct udscs_connection *vdagentd,
void vdagent_x11_destroy(struct vdagent_x11 *x11)
{
+ uint8_t sel;
+
if (!x11)
return;
- vdagent_x11_set_clipboard_owner(x11, owner_none);
+ for (sel = 0; sel < VD_AGENT_CLIPBOARD_SELECTION_SECONDARY; ++sel) {
+ vdagent_x11_set_clipboard_owner(x11, sel, owner_none);
+ }
+
XCloseDisplay(x11->display);
free(x11);
}
@@ -265,46 +274,81 @@ static void vdagent_x11_next_conversion_request(struct vdagent_x11 *x11)
}
static void vdagent_x11_set_clipboard_owner(struct vdagent_x11 *x11,
- int new_owner)
+ uint8_t selection, int new_owner)
{
+ struct vdagent_x11_selection_request *prev_sel, *curr_sel, *next_sel;
+ struct vdagent_x11_conversion_request *prev_conv, *curr_conv, *next_conv;
+ int once;
+
/* Clear pending requests and clipboard data */
- if (x11->selection_req) {
- fprintf(x11->errfile,
- "selection requests pending on clipboard ownership change, "
- "clearing\n");
- while (x11->selection_req) {
- vdagent_x11_send_selection_notify(x11, None, x11->selection_req);
- vdagent_x11_next_selection_request(x11);
+ once = 1;
+ prev_sel = NULL;
+ next_sel = x11->selection_req;
+ while (next_sel) {
+ curr_sel = next_sel;
+ next_sel = curr_sel->next;
+ if (curr_sel->selection == selection) {
+ if (once) {
+ fprintf(x11->errfile,
+ "selection requests pending on clipboard ownership "
+ "change, clearing\n");
+ once = 0;
+ }
+ vdagent_x11_send_selection_notify(x11, None, curr_sel);
+ if (curr_sel == x11->selection_req) {
+ x11->selection_req = next_sel;
+ free(x11->selection_req_data);
+ x11->selection_req_data = NULL;
+ x11->selection_req_data_pos = 0;
+ x11->selection_req_data_size = 0;
+ x11->selection_req_atom = None;
+ } else {
+ prev_sel->next = next_sel;
+ }
+ free(curr_sel);
+ } else {
+ prev_sel = curr_sel;
}
}
- free(x11->selection_req_data);
- x11->selection_req_data = NULL;
- x11->selection_req_data_pos = 0;
- x11->selection_req_data_size = 0;
- x11->selection_req_atom = None;
- if (x11->conversion_req) {
- fprintf(x11->errfile,
- "client clipboard request pending on clipboard ownership "
- "change, clearing\n");
- while (x11->conversion_req) {
- udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
- VD_AGENT_CLIPBOARD_NONE, 0, NULL, 0);
- vdagent_x11_next_conversion_request(x11);
+
+ once = 1;
+ prev_conv = NULL;
+ next_conv = x11->conversion_req;
+ while (next_conv) {
+ curr_conv = next_conv;
+ next_conv = curr_conv->next;
+ if (curr_conv->selection == selection) {
+ if (once) {
+ fprintf(x11->errfile,
+ "client clipboard request pending on clipboard "
+ "ownership change, clearing\n");
+ once = 0;
+ }
+ udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, selection,
+ VD_AGENT_CLIPBOARD_NONE, NULL, 0);
+ if (curr_conv == x11->conversion_req) {
+ x11->conversion_req = next_conv;
+ x11->clipboard_data_size = 0;
+ x11->expect_property_notify = 0;
+ } else {
+ prev_conv->next = next_conv;
+ }
+ free(curr_conv);
+ } else {
+ prev_conv = curr_conv;
}
}
- x11->clipboard_data_size = 0;
- x11->expect_property_notify = 0;
if (new_owner == owner_none) {
/* When going from owner_guest to owner_none we need to send a
clipboard release message to the client */
- if (x11->clipboard_owner == owner_guest)
- udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_RELEASE, 0, 0,
- NULL, 0);
-
- x11->clipboard_type_count = 0;
+ if (x11->clipboard_owner[selection] == owner_guest) {
+ udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_RELEASE, selection,
+ 0, NULL, 0);
+ }
+ x11->clipboard_type_count[selection] = 0;
}
- x11->clipboard_owner = new_owner;
+ x11->clipboard_owner[selection] = new_owner;
}
static int vdagent_x11_get_clipboard_atom(struct vdagent_x11 *x11, uint8_t selection, Atom* clipboard)
@@ -322,7 +366,7 @@ static int vdagent_x11_get_clipboard_atom(struct vdagent_x11 *x11, uint8_t selec
}
static int vdagent_x11_get_clipboard_selection(struct vdagent_x11 *x11,
- XEvent *event, uint8_t *selection)
+ XEvent *event, uint8_t *selection)
{
Atom atom;
@@ -353,7 +397,7 @@ static int vdagent_x11_get_clipboard_selection(struct vdagent_x11 *x11,
static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
{
int handled = 0;
-
+ uint8_t selection;
if (event.type == x11->xfixes_event_base) {
union {
@@ -361,6 +405,10 @@ static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
XFixesSelectionNotifyEvent xfev;
} ev;
+ if (vdagent_x11_get_clipboard_selection(x11, &event, &selection)) {
+ return;
+ }
+
ev.ev = event;
switch (ev.xfev.subtype) {
case XFixesSetSelectionOwnerNotify:
@@ -387,16 +435,16 @@ static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
return;
/* If the clipboard owner is changed we no longer own it */
- vdagent_x11_set_clipboard_owner(x11, owner_none);
+ vdagent_x11_set_clipboard_owner(x11, selection, owner_none);
if (ev.xfev.owner == None)
return;
/* Request the supported targets from the new owner */
- XConvertSelection(x11->display, x11->clipboard_atom, x11->targets_atom,
+ XConvertSelection(x11->display, ev.xfev.selection, x11->targets_atom,
x11->targets_atom, x11->selection_window,
CurrentTime);
- x11->expected_targets_notifies++;
+ x11->expected_targets_notifies[selection]++;
return;
}
@@ -445,6 +493,10 @@ static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
case SelectionRequest: {
struct vdagent_x11_selection_request *req, *new_req;
+ if (vdagent_x11_get_clipboard_selection(x11, &event, &selection)) {
+ return;
+ }
+
new_req = malloc(sizeof(*new_req));
if (!new_req) {
fprintf(x11->errfile,
@@ -455,6 +507,7 @@ static void vdagent_x11_handle_event(struct vdagent_x11 *x11, XEvent event)
handled = 1;
new_req->event = event;
+ new_req->selection = selection;
new_req->next = NULL;
if (!x11->selection_req) {
@@ -532,8 +585,7 @@ static int vdagent_x11_get_selection(struct vdagent_x11 *x11, XEvent *event,
}
if (event->xselection.requestor != x11->selection_window ||
- event->xselection.selection != x11->clipboard_atom ||
- event->xselection.property != prop) {
+ event->xselection.property != prop) {
fprintf(x11->errfile, "SelectionNotify parameters mismatch\n");
goto exit;
}
@@ -682,60 +734,82 @@ static uint32_t vdagent_x11_target_to_type(struct vdagent_x11 *x11,
return VD_AGENT_CLIPBOARD_NONE;
}
-static Atom vdagent_x11_type_to_target(struct vdagent_x11 *x11, uint32_t type)
+static Atom vdagent_x11_type_to_target(struct vdagent_x11 *x11,
+ uint8_t selection, uint32_t type)
{
int i;
- for (i = 0; i < x11->clipboard_type_count; i++)
- if (x11->clipboard_agent_types[i] == type)
- return x11->clipboard_x11_targets[i];
-
+ for (i = 0; i < x11->clipboard_type_count[selection]; i++) {
+ if (x11->clipboard_agent_types[selection][i] == type) {
+ return x11->clipboard_x11_targets[selection][i];
+ }
+ }
fprintf(x11->errfile, "client requested unavailable type %u\n", type);
return None;
}
static void vdagent_x11_handle_conversion_request(struct vdagent_x11 *x11)
{
+ Atom clip;
+
if (!x11->conversion_req) {
return;
}
- XConvertSelection(x11->display, x11->clipboard_atom,
- x11->conversion_req->target,
- x11->clipboard_atom, x11->selection_window, CurrentTime);
+ vdagent_x11_get_clipboard_atom(x11, x11->conversion_req->selection, &clip);
+ XConvertSelection(x11->display, clip, x11->conversion_req->target,
+ clip, x11->selection_window, CurrentTime);
}
static void vdagent_x11_handle_selection_notify(struct vdagent_x11 *x11,
XEvent *event, int incr)
{
- int len = -1;
+ int len = 0;
unsigned char *data = NULL;
uint32_t type;
+ uint8_t selection;
+ Atom clip;
if (!x11->conversion_req) {
fprintf(x11->errfile, "SelectionNotify received without a target\n");
return;
}
-
- type = vdagent_x11_target_to_type(x11, x11->conversion_req->target);
- if (!incr &&
- event->xselection.target != x11->conversion_req->target &&
- event->xselection.target != x11->incr_atom)
- fprintf(x11->errfile, "Requested %s target got %s\n",
+ vdagent_x11_get_clipboard_atom(x11, x11->conversion_req->selection, &clip);
+
+ if (!incr) {
+ if (vdagent_x11_get_clipboard_selection(x11, event, &selection)) {
+ len = -1;
+ } else if (selection != x11->conversion_req->selection) {
+ fprintf(x11->errfile, "Requested data for selection %d, got %d\n",
+ (int)x11->conversion_req->selection, (int)selection);
+ len = -1;
+ }
+ if (event->xselection.target != x11->conversion_req->target &&
+ event->xselection.target != x11->incr_atom) {
+ fprintf(x11->errfile, "Requested %s target got %s\n",
vdagent_x11_get_atom_name(x11, x11->conversion_req->target),
vdagent_x11_get_atom_name(x11, event->xselection.target));
- else
+ len = -1;
+ }
+ }
+
+ selection = x11->conversion_req->selection;
+ type = vdagent_x11_target_to_type(x11, x11->conversion_req->target);
+ if (len == 0) { /* No errors so far */
len = vdagent_x11_get_selection(x11, event,
x11->conversion_req->target,
- x11->clipboard_atom, 8, &data, incr);
- if (len == 0) /* waiting for more data? */
- return;
+ clip, 8, &data, incr);
+ if (len == 0) { /* waiting for more data? */
+ return;
+ }
+ }
if (len == -1) {
type = VD_AGENT_CLIPBOARD_NONE;
len = 0;
}
- udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, type, 0, data, len);
+ udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA, selection, type,
+ data, len);
vdagent_x11_get_selection_free(x11, data, incr);
vdagent_x11_next_conversion_request(x11);
@@ -773,19 +847,26 @@ static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11,
{
int i, len;
Atom atom, *atoms = NULL;
+ uint8_t selection;
+ int *type_count;
- if (!x11->expected_targets_notifies) {
+ if (vdagent_x11_get_clipboard_selection(x11, event, &selection)) {
+ return;
+ }
+
+ if (!x11->expected_targets_notifies[selection]) {
fprintf(x11->errfile, "unexpected selection notify TARGETS\n");
return;
}
- x11->expected_targets_notifies--;
+ x11->expected_targets_notifies[selection]--;
/* If we have more targets_notifies pending, ignore this one, we
are only interested in the targets list of the current owner
(which is the last one we've requested a targets list from) */
- if (x11->expected_targets_notifies)
+ if (x11->expected_targets_notifies[selection]) {
return;
+ }
len = vdagent_x11_get_selection(x11, event, XA_ATOM, x11->targets_atom, 32,
(unsigned char **)&atoms, 0);
@@ -796,17 +877,18 @@ static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11,
len /= sizeof(Atom);
vdagent_x11_print_targets(x11, "received", atoms, len);
- x11->clipboard_type_count = 0;
+ type_count = &x11->clipboard_type_count[selection];
+ *type_count = 0;
for (i = 0; i < clipboard_format_count; i++) {
atom = atom_lists_overlap(x11->clipboard_formats[i].atoms, atoms,
x11->clipboard_formats[i].atom_count, len);
if (atom) {
- x11->clipboard_agent_types[x11->clipboard_type_count] =
+ x11->clipboard_agent_types[selection][*type_count] =
x11->clipboard_formats[i].type;
- x11->clipboard_x11_targets[x11->clipboard_type_count] = atom;
- x11->clipboard_type_count++;
- if (x11->clipboard_type_count ==
- sizeof(x11->clipboard_agent_types)/sizeof(uint32_t)) {
+ x11->clipboard_x11_targets[selection][*type_count] = atom;
+ (*type_count)++;
+ if (*type_count ==
+ sizeof(x11->clipboard_agent_types[0])/sizeof(uint32_t)) {
fprintf(x11->errfile,
"handle_targets_notify: too many types\n");
break;
@@ -814,11 +896,11 @@ static void vdagent_x11_handle_targets_notify(struct vdagent_x11 *x11,
}
}
- if (x11->clipboard_type_count) {
- udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_GRAB, 0, 0,
- (uint8_t *)x11->clipboard_agent_types,
- x11->clipboard_type_count * sizeof(uint32_t));
- vdagent_x11_set_clipboard_owner(x11, owner_guest);
+ if (*type_count) {
+ udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_GRAB, selection, 0,
+ (uint8_t *)x11->clipboard_agent_types[selection],
+ *type_count * sizeof(uint32_t));
+ vdagent_x11_set_clipboard_owner(x11, selection, owner_guest);
}
vdagent_x11_get_selection_free(x11, (unsigned char *)atoms, 0);
@@ -850,15 +932,16 @@ static void vdagent_x11_send_selection_notify(struct vdagent_x11 *x11,
}
}
-static void vdagent_x11_send_targets(struct vdagent_x11 *x11, XEvent *event)
+static void vdagent_x11_send_targets(struct vdagent_x11 *x11,
+ uint8_t selection, XEvent *event)
{
Atom prop, targets[256] = { x11->targets_atom, };
int i, j, k, target_count = 1;
- for (i = 0; i < x11->clipboard_type_count; i++) {
+ for (i = 0; i < x11->clipboard_type_count[selection]; i++) {
for (j = 0; j < clipboard_format_count; j++) {
if (x11->clipboard_formats[j].type !=
- x11->clipboard_agent_types[i])
+ x11->clipboard_agent_types[selection][i])
continue;
for (k = 0; k < x11->clipboard_formats[j].atom_count; k++) {
@@ -888,13 +971,15 @@ static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11)
{
XEvent *event;
uint32_t type = VD_AGENT_CLIPBOARD_NONE;
+ uint8_t selection;
if (!x11->selection_req)
return;
event = &x11->selection_req->event;
+ selection = x11->selection_req->selection;
- if (x11->clipboard_owner != owner_client) {
+ if (x11->clipboard_owner[selection] != owner_client) {
fprintf(x11->errfile,
"received selection request event for target %s, "
"while not owning client clipboard\n",
@@ -910,7 +995,7 @@ static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11)
}
if (event->xselectionrequest.target == x11->targets_atom) {
- vdagent_x11_send_targets(x11, event);
+ vdagent_x11_send_targets(x11, selection, event);
return;
}
@@ -920,7 +1005,8 @@ static void vdagent_x11_handle_selection_request(struct vdagent_x11 *x11)
return;
}
- udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_REQUEST, type, 0, NULL, 0);
+ udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_REQUEST, selection, type,
+ NULL, 0);
}
static void vdagent_x11_handle_property_delete_notify(struct vdagent_x11 *x11,
@@ -1033,24 +1119,27 @@ void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
vdagent_x11_do_read(x11);
}
-void vdagent_x11_clipboard_request(struct vdagent_x11 *x11, uint32_t type)
+void vdagent_x11_clipboard_request(struct vdagent_x11 *x11,
+ uint8_t selection, uint32_t type)
{
- Atom target;
+ Atom target, clip;
struct vdagent_x11_conversion_request *req, *new_req;
- if (x11->clipboard_owner != owner_guest) {
+ /* We don't use clip here, but we call get_clipboard_atom to verify
+ selection is valid */
+ if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) {
+ goto none;
+ }
+
+ if (x11->clipboard_owner[selection] != owner_guest) {
fprintf(x11->errfile,
"received clipboard req while not owning guest clipboard\n");
- udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
- VD_AGENT_CLIPBOARD_NONE, 0, NULL, 0);
- return;
+ goto none;
}
- target = vdagent_x11_type_to_target(x11, type);
+ target = vdagent_x11_type_to_target(x11, selection, type);
if (target == None) {
- udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
- VD_AGENT_CLIPBOARD_NONE, 0, NULL, 0);
- return;
+ goto none;
}
new_req = malloc(sizeof(*new_req));
@@ -1061,6 +1150,7 @@ void vdagent_x11_clipboard_request(struct vdagent_x11 *x11, uint32_t type)
}
new_req->target = target;
+ new_req->selection = selection;
new_req->next = NULL;
if (!x11->conversion_req) {
@@ -1077,29 +1167,41 @@ void vdagent_x11_clipboard_request(struct vdagent_x11 *x11, uint32_t type)
req = req->next;
req->next = new_req;
+ return;
+
+none:
+ udscs_write(x11->vdagentd, VDAGENTD_CLIPBOARD_DATA,
+ selection, VD_AGENT_CLIPBOARD_NONE, NULL, 0);
}
-void vdagent_x11_clipboard_grab(struct vdagent_x11 *x11, uint32_t *types,
- uint32_t type_count)
+void vdagent_x11_clipboard_grab(struct vdagent_x11 *x11, uint8_t selection,
+ uint32_t *types, uint32_t type_count)
{
- if (type_count > sizeof(x11->clipboard_agent_types)/sizeof(uint32_t)) {
+ Atom clip;
+
+ if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) {
+ return;
+ }
+
+ if (type_count > sizeof(x11->clipboard_agent_types[0])/sizeof(uint32_t)) {
fprintf(x11->errfile, "x11_clipboard_grab: too many types\n");
- type_count = sizeof(x11->clipboard_agent_types)/sizeof(uint32_t);
+ type_count = sizeof(x11->clipboard_agent_types[0])/sizeof(uint32_t);
}
- memcpy(x11->clipboard_agent_types, types, type_count * sizeof(uint32_t));
- x11->clipboard_type_count = type_count;
+ memcpy(x11->clipboard_agent_types[selection], types,
+ type_count * sizeof(uint32_t));
+ x11->clipboard_type_count[selection] = type_count;
- XSetSelectionOwner(x11->display, x11->clipboard_atom,
+ XSetSelectionOwner(x11->display, clip,
x11->selection_window, CurrentTime);
- vdagent_x11_set_clipboard_owner(x11, owner_client);
+ vdagent_x11_set_clipboard_owner(x11, selection, owner_client);
/* Flush output buffers and consume any pending events */
vdagent_x11_do_read(x11);
}
-void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint32_t type,
- uint8_t *data, uint32_t size)
+void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint8_t selection,
+ uint32_t type, uint8_t *data, uint32_t size)
{
Atom prop;
XEvent *event;
@@ -1126,9 +1228,15 @@ void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint32_t type,
event = &x11->selection_req->event;
type_from_event = vdagent_x11_target_to_type(x11,
event->xselectionrequest.target);
- if (type_from_event != type) {
- fprintf(x11->errfile, "expecting type %u clipboard data got %u\n",
- type_from_event, type);
+ if (type_from_event != type ||
+ selection != x11->selection_req->selection) {
+ if (type_from_event != type) {
+ fprintf(x11->errfile, "expecting type %u clipboard data got %u\n",
+ type_from_event, type);
+ } else {
+ fprintf(x11->errfile, "expecting data for selection %d got %d\n",
+ (int)x11->selection_req->selection, (int)selection);
+ }
vdagent_x11_send_selection_notify(x11, None, NULL);
free(data);
@@ -1167,17 +1275,22 @@ void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint32_t type,
vdagent_x11_do_read(x11);
}
-void vdagent_x11_clipboard_release(struct vdagent_x11 *x11)
+void vdagent_x11_clipboard_release(struct vdagent_x11 *x11, uint8_t selection)
{
XEvent event;
+ Atom clip;
+
+ if (vdagent_x11_get_clipboard_atom(x11, selection, &clip)) {
+ return;
+ }
- if (x11->clipboard_owner != owner_client) {
+ if (x11->clipboard_owner[selection] != owner_client) {
fprintf(x11->errfile,
"received clipboard release while not owning client clipboard\n");
return;
}
- XSetSelectionOwner(x11->display, x11->clipboard_atom, None, CurrentTime);
+ XSetSelectionOwner(x11->display, clip, None, CurrentTime);
/* Make sure we process the XFixesSetSelectionOwnerNotify event caused
by this, so we don't end up changing the clipboard owner to none, after
it has already been re-owned because this event is still pending. */
diff --git a/vdagent-x11.h b/vdagent-x11.h
index 10f27ee..800cbd0 100644
--- a/vdagent-x11.h
+++ b/vdagent-x11.h
@@ -36,12 +36,13 @@ int vdagent_x11_get_fd(struct vdagent_x11 *x11);
void vdagent_x11_do_read(struct vdagent_x11 *x11);
void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
- VDAgentMonitorsConfig *mon_config);
-void vdagent_x11_clipboard_grab(struct vdagent_x11 *x11, uint32_t *types,
- uint32_t type_count);
-void vdagent_x11_clipboard_request(struct vdagent_x11 *x11, uint32_t type);
-void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint32_t type,
- uint8_t *data, uint32_t size);
-void vdagent_x11_clipboard_release(struct vdagent_x11 *x11);
+ VDAgentMonitorsConfig *mon_config);
+void vdagent_x11_clipboard_grab(struct vdagent_x11 *x11, uint8_t selection,
+ uint32_t *types, uint32_t type_count);
+void vdagent_x11_clipboard_request(struct vdagent_x11 *x11,
+ uint8_t selection, uint32_t type);
+void vdagent_x11_clipboard_data(struct vdagent_x11 *x11, uint8_t selection,
+ uint32_t type, uint8_t *data, uint32_t size);
+void vdagent_x11_clipboard_release(struct vdagent_x11 *x11, uint8_t selection);
#endif
diff --git a/vdagent.c b/vdagent.c
index 04ce231..2a72adb 100644
--- a/vdagent.c
+++ b/vdagent.c
@@ -50,21 +50,22 @@ void daemon_read_complete(struct udscs_connection **connp,
free(data);
break;
case VDAGENTD_CLIPBOARD_REQUEST:
- vdagent_x11_clipboard_request(x11, header->arg1);
+ vdagent_x11_clipboard_request(x11, header->arg1, header->arg2);
free(data);
break;
case VDAGENTD_CLIPBOARD_GRAB:
- vdagent_x11_clipboard_grab(x11, (uint32_t *)data,
+ vdagent_x11_clipboard_grab(x11, header->arg1, (uint32_t *)data,
header->size / sizeof(uint32_t));
free(data);
break;
case VDAGENTD_CLIPBOARD_DATA:
- vdagent_x11_clipboard_data(x11, header->arg1, data, header->size);
+ vdagent_x11_clipboard_data(x11, header->arg1, header->arg2,
+ data, header->size);
/* vdagent_x11_clipboard_data takes ownership of the data (or frees
it immediately) */
break;
case VDAGENTD_CLIPBOARD_RELEASE:
- vdagent_x11_clipboard_release(x11);
+ vdagent_x11_clipboard_release(x11, header->arg1);
free(data);
break;
default:
diff --git a/vdagentd-proto.h b/vdagentd-proto.h
index 6636573..e570d2b 100644
--- a/vdagentd-proto.h
+++ b/vdagentd-proto.h
@@ -29,10 +29,10 @@ enum {
VDAGENTD_GUEST_XORG_RESOLUTION, /* client -> daemon */
VDAGENTD_MONITORS_CONFIG, /* daemon -> client, VDAgentMonitorsConfig
followed by num_monitors VDAgentMonConfig-s */
- VDAGENTD_CLIPBOARD_GRAB, /* data is array of supported types */
- VDAGENTD_CLIPBOARD_REQUEST, /* arg1 = type */
- VDAGENTD_CLIPBOARD_DATA, /* arg1 = type, data = data */
- VDAGENTD_CLIPBOARD_RELEASE, /* no data */
+ VDAGENTD_CLIPBOARD_GRAB, /* arg1: sel, data: array of supported types */
+ VDAGENTD_CLIPBOARD_REQUEST, /* arg1: selection, arg 2 = type */
+ VDAGENTD_CLIPBOARD_DATA, /* arg1: sel, arg 2: type, data: data */
+ VDAGENTD_CLIPBOARD_RELEASE, /* arg1: selection */
VDAGENTD_NO_MESSAGES /* Must always be last */
};
diff --git a/vdagentd.c b/vdagentd.c
index e857d76..21588c5 100644
--- a/vdagentd.c
+++ b/vdagentd.c
@@ -59,7 +59,7 @@ static uint32_t *capabilities = NULL;
static int capabilities_size = 0;
static const char *active_session = NULL;
static struct udscs_connection *active_session_conn = NULL;
-static int agent_owns_clipboard = 0;
+static int agent_owns_clipboard[256] = { 0, };
static FILE *logfile = NULL;
static int quit = 0;
static int retval = 0;
@@ -85,6 +85,7 @@ static void send_capabilities(struct vdagent_virtio_port *vport,
VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MONITORS_CONFIG);
VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_REPLY);
VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_BY_DEMAND);
+ VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_CLIPBOARD_SELECTION);
vdagent_virtio_port_write(vport, VDP_CLIENT_PORT,
VD_AGENT_ANNOUNCE_CAPABILITIES, 0,
@@ -151,10 +152,10 @@ static void do_client_capabilities(struct vdagent_virtio_port *vport,
}
static void do_client_clipboard(struct vdagent_virtio_port *vport,
- VDAgentMessage *message_header, uint8_t *message_data)
+ VDAgentMessage *message_header, uint8_t *data)
{
- uint32_t type = 0, arg1 = 0, size = 0;
- uint8_t *data = NULL;
+ uint32_t msg_type = 0, data_type = 0, size = message_header->size;
+ uint8_t selection = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD;
if (!active_session_conn) {
fprintf(logfile,
@@ -163,33 +164,43 @@ static void do_client_clipboard(struct vdagent_virtio_port *vport,
return;
}
+ if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
+ VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
+ selection = data[0];
+ data += 4;
+ size -= 4;
+ }
+
switch (message_header->type) {
case VD_AGENT_CLIPBOARD_GRAB:
- type = VDAGENTD_CLIPBOARD_GRAB;
- data = message_data;
- size = message_header->size;
- agent_owns_clipboard = 0;
+ msg_type = VDAGENTD_CLIPBOARD_GRAB;
+ agent_owns_clipboard[selection] = 0;
break;
case VD_AGENT_CLIPBOARD_REQUEST: {
- VDAgentClipboardRequest *req = (VDAgentClipboardRequest *)message_data;
- type = VDAGENTD_CLIPBOARD_REQUEST;
- arg1 = req->type;
+ VDAgentClipboardRequest *req = (VDAgentClipboardRequest *)data;
+ msg_type = VDAGENTD_CLIPBOARD_REQUEST;
+ data_type = req->type;
+ data = NULL;
+ size = 0;
break;
}
case VD_AGENT_CLIPBOARD: {
- VDAgentClipboard *clipboard = (VDAgentClipboard *)message_data;
- type = VDAGENTD_CLIPBOARD_DATA;
- arg1 = clipboard->type;
- size = message_header->size - sizeof(VDAgentClipboard);
+ VDAgentClipboard *clipboard = (VDAgentClipboard *)data;
+ msg_type = VDAGENTD_CLIPBOARD_DATA;
+ data_type = clipboard->type;
+ size = size - sizeof(VDAgentClipboard);
data = clipboard->data;
break;
}
case VD_AGENT_CLIPBOARD_RELEASE:
- type = VDAGENTD_CLIPBOARD_RELEASE;
+ msg_type = VDAGENTD_CLIPBOARD_RELEASE;
+ data = NULL;
+ size = 0;
break;
}
- udscs_write(active_session_conn, type, arg1, 0, data, size);
+ udscs_write(active_session_conn, msg_type, selection, data_type,
+ data, size);
}
int virtio_port_read_complete(
@@ -250,8 +261,13 @@ int virtio_port_read_complete(
case VD_AGENT_CLIPBOARD:
min_size = sizeof(VDAgentClipboard); break;
}
- if (message_header->size < min_size)
+ if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
+ VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
+ min_size += 4;
+ }
+ if (message_header->size < min_size) {
goto size_error;
+ }
do_client_clipboard(vport, message_header, data);
break;
default:
@@ -269,9 +285,13 @@ size_error:
}
/* vdagentd <-> vdagent communication handling */
-void do_agent_clipboard(struct udscs_connection *conn,
- struct udscs_message_header *header, const uint8_t *data)
+int do_agent_clipboard(struct udscs_connection *conn,
+ struct udscs_message_header *header, const uint8_t *data)
{
+ const uint8_t *msg;
+ uint8_t *buf = NULL, selection = header->arg1;
+ uint32_t msg_type = 0, data_type = -1, size = header->size;
+
if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
VD_AGENT_CAP_CLIPBOARD_BY_DEMAND))
goto error;
@@ -282,61 +302,91 @@ void do_agent_clipboard(struct udscs_connection *conn,
"which is not in the active session?\n");
goto error;
}
-
+
if (!virtio_port) {
fprintf(logfile,
"Clipboard request from agent but no client connection\n");
goto error;
}
+ if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
+ VD_AGENT_CAP_CLIPBOARD_SELECTION) &&
+ selection != VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD) {
+ goto error;
+ }
+
switch (header->type) {
case VDAGENTD_CLIPBOARD_GRAB:
- vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
- VD_AGENT_CLIPBOARD_GRAB, 0,
- data, header->size);
- agent_owns_clipboard = 1;
+ msg_type = VD_AGENT_CLIPBOARD_GRAB;
+ agent_owns_clipboard[selection] = 1;
break;
- case VDAGENTD_CLIPBOARD_REQUEST: {
- VDAgentClipboardRequest req = { .type = header->arg1 };
- vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
- VD_AGENT_CLIPBOARD_REQUEST, 0,
- (uint8_t *)&req, sizeof(req));
+ case VDAGENTD_CLIPBOARD_REQUEST:
+ msg_type = VD_AGENT_CLIPBOARD_REQUEST;
+ data_type = header->arg2;
+ size = 0;
+ break;
+ case VDAGENTD_CLIPBOARD_DATA:
+ msg_type = VD_AGENT_CLIPBOARD;
+ data_type = header->arg2;
+ break;
+ case VDAGENTD_CLIPBOARD_RELEASE:
+ msg_type = VD_AGENT_CLIPBOARD_RELEASE;
+ size = 0;
+ agent_owns_clipboard[selection] = 0;
break;
}
- case VDAGENTD_CLIPBOARD_DATA: {
- VDAgentClipboard *clipboard;
- uint32_t size = sizeof(*clipboard) + header->size;
- clipboard = calloc(1, size);
- if (!clipboard) {
- fprintf(logfile,
- "out of memory allocating clipboard (write)\n");
- return;
- }
- clipboard->type = header->arg1;
- memcpy(clipboard->data, data, header->size);
+ if (size != header->size) {
+ fprintf(logfile,
+ "unexpected extra data in clipboard msg, disconnecting agent\n");
+ return -1;
+ }
- vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
- VD_AGENT_CLIPBOARD, 0,
- (uint8_t *)clipboard, size);
- free(clipboard);
- break;
+ if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
+ VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
+ size += 4;
}
- case VDAGENTD_CLIPBOARD_RELEASE:
- vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
- VD_AGENT_CLIPBOARD_RELEASE, 0, NULL, 0);
- agent_owns_clipboard = 0;
- break;
+ if (data_type != -1) {
+ size += 4;
+ }
+ if (size != header->size) {
+ uint8_t *p;
+ buf = p = malloc(size);
+ if (!buf) {
+ fprintf(logfile, "out of memory allocating clipboard (write)\n");
+ return -1;
+ }
+ if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
+ VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
+ p[0] = selection;
+ p[1] = 0;
+ p[2] = 0;
+ p[3] = 0;
+ p += 4;
+ }
+ if (data_type != -1) {
+ uint32_t *u = (uint32_t *)p;
+ u[0] = data_type;
+ p += 4;
+ }
+ memcpy(p, data, header->size);
+ msg = buf;
+ } else {
+ msg = data;
}
- return;
+ vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT, msg_type,
+ 0, msg, size);
+ free(buf);
+ return 0;
error:
if (header->type == VDAGENTD_CLIPBOARD_REQUEST) {
/* Let the agent know no answer is coming */
udscs_write(conn, VDAGENTD_CLIPBOARD_DATA,
- VD_AGENT_CLIPBOARD_NONE, 0, NULL, 0);
+ selection, VD_AGENT_CLIPBOARD_NONE, NULL, 0);
}
+ return 0;
}
/* When we open the vdagent virtio channel, the server automatically goes into
@@ -346,7 +396,8 @@ error:
are met it sets the uinput tablet device's resolution and opens the virtio
channel (if it is not already open). If these conditions are not met, it
closes both. */
-static void check_xorg_resolution(void) {
+static void check_xorg_resolution(void)
+{
struct agent_data *agent_data = udscs_get_user_data(active_session_conn);
if (agent_data && agent_data->width) {
@@ -405,6 +456,19 @@ static int connection_matches_active_session(struct udscs_connection **connp,
return 1;
}
+void release_clipboards(void)
+{
+ uint8_t sel;
+
+ for (sel = 0; sel < VD_AGENT_CLIPBOARD_SELECTION_SECONDARY; ++sel) {
+ if (agent_owns_clipboard[sel] && virtio_port) {
+ vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
+ VD_AGENT_CLIPBOARD_RELEASE, 0, &sel, 1);
+ }
+ agent_owns_clipboard[sel] = 0;
+ }
+}
+
void update_active_session_connection(void)
{
struct udscs_connection *new_conn = NULL;
@@ -423,10 +487,7 @@ void update_active_session_connection(void)
active_session_conn = new_conn;
- if (agent_owns_clipboard && virtio_port)
- vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
- VD_AGENT_CLIPBOARD_RELEASE, 0, NULL, 0);
- agent_owns_clipboard = 0;
+ release_clipboards();
check_xorg_resolution();
}
@@ -491,7 +552,10 @@ void agent_read_complete(struct udscs_connection **connp,
case VDAGENTD_CLIPBOARD_REQUEST:
case VDAGENTD_CLIPBOARD_DATA:
case VDAGENTD_CLIPBOARD_RELEASE:
- do_agent_clipboard(*connp, header, data);
+ if (do_agent_clipboard(*connp, header, data)) {
+ udscs_destroy_connection(connp);
+ return;
+ }
break;
default:
fprintf(logfile, "unknown message from vdagent: %u, ignoring\n",
@@ -687,9 +751,7 @@ int main(int argc, char *argv[])
main_loop();
- if (agent_owns_clipboard && virtio_port)
- vdagent_virtio_port_write(virtio_port, VDP_CLIENT_PORT,
- VD_AGENT_CLIPBOARD_RELEASE, 0, NULL, 0);
+ release_clipboards();
vdagentd_uinput_destroy(&uinput);
vdagent_virtio_port_flush(&virtio_port);