diff options
author | Hans de Goede <hdegoede@redhat.com> | 2011-03-31 13:20:23 +0200 |
---|---|---|
committer | Hans de Goede <hdegoede@redhat.com> | 2011-03-31 14:45:16 +0200 |
commit | e7c5f45256497ba0227e9c52b42831e19c8965a3 (patch) | |
tree | b1fa008577caf559f26ba895df03261c13507d25 | |
parent | 46154d26c4c1d7ff6cb4c1d00623e7e1b6ab8017 (diff) |
Add support for multiple selections
Based on the initial patch by Marc-André Lureau <marcandre.lureau@redhat.com>.
-rw-r--r-- | vdagent-x11.c | 333 | ||||
-rw-r--r-- | vdagent-x11.h | 15 | ||||
-rw-r--r-- | vdagent.c | 9 | ||||
-rw-r--r-- | vdagentd-proto.h | 8 | ||||
-rw-r--r-- | vdagentd.c | 186 |
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 @@ -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 */ }; @@ -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); |