📄 xclip.c
字号:
#endif
}
else if (event->target == This->xclip.format_unicode_atom)
{
/* Assuming text/unicode to be UTF-16 */
format = CF_UNICODETEXT;
}
else
{
DEBUG_CLIPBOARD(("Requested target unavailable. (It was not in TARGETS, so why did you ask for it?!)\n"));
xclip_refuse_selection(This, event);
return;
}
cliprdr_send_data_request(This, format);
This->xclip.selection_request = *event;
This->xclip.has_selection_request = True;
return; /* wait for data */
}
}
/* While this rdesktop holds ownership over the clipboard, it means the clipboard data
is offered by the RDP server (and when it is pasted inside RDP, there's no network
roundtrip).
This event (SelectionClear) symbolizes this rdesktop lost onwership of the clipboard
to some other X client. We should find out what clipboard formats this other
client offers and announce that to RDP. */
void
xclip_handle_SelectionClear(RDPCLIENT * This)
{
DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
xclip_notify_change(This);
xclip_probe_selections(This);
}
/* Called when any property changes in our window or the root window. */
void
xclip_handle_PropertyNotify(RDPCLIENT * This, XPropertyEvent * event)
{
unsigned long nitems;
unsigned long offset = 0;
unsigned long bytes_left = 1;
int format;
XWindowAttributes wa;
uint8 *data;
Atom type;
if (event->state == PropertyNewValue && This->xclip.waiting_for_INCR)
{
DEBUG_CLIPBOARD(("x_clip_handle_PropertyNotify: This->xclip.waiting_for_INCR != 0\n"));
while (bytes_left > 0)
{
/* Unlike the specification, we don't set the 'delete' arugment to True
since we slurp the INCR's chunks in even-smaller chunks of 4096 bytes. */
if ((XGetWindowProperty
(This->display, This->wnd, This->xclip.rdesktop_clipboard_target_atom, offset, 4096L,
False, AnyPropertyType, &type, &format, &nitems, &bytes_left,
&data) != Success))
{
XFree(data);
return;
}
if (nitems == 0)
{
/* INCR transfer finished */
XGetWindowAttributes(This->display, This->wnd, &wa);
XSelectInput(This->display, This->wnd,
(wa.your_event_mask ^ PropertyChangeMask));
XFree(data);
This->xclip.waiting_for_INCR = 0;
if (This->xclip.clip_buflen > 0)
{
if (!xclip_send_data_with_convert
(This, This->xclip.clip_buffer, This->xclip.clip_buflen, This->xclip.incr_target))
{
helper_cliprdr_send_empty_response(This);
}
xfree(This->xclip.clip_buffer);
This->xclip.clip_buffer = NULL;
This->xclip.clip_buflen = 0;
}
}
else
{
/* Another chunk in the INCR transfer */
offset += (nitems / 4); /* offset at which to begin the next slurp */
This->xclip.clip_buffer = xrealloc(This->xclip.clip_buffer, This->xclip.clip_buflen + nitems);
memcpy(This->xclip.clip_buffer + This->xclip.clip_buflen, data, nitems);
This->xclip.clip_buflen += nitems;
XFree(data);
}
}
XDeleteProperty(This->display, This->wnd, This->xclip.rdesktop_clipboard_target_atom);
return;
}
if ((event->atom == This->xclip.rdesktop_selection_notify_atom) &&
(event->window == DefaultRootWindow(This->display)))
xclip_probe_selections(This);
}
#endif
/* Called when the RDP server announces new clipboard data formats.
In response, we:
- take ownership over the clipboard
- declare those formats in their Windows native form
to other rdesktop instances on this X server */
void
ui_clip_format_announce(RDPCLIENT * This, uint8 * data, uint32 length)
{
This->xclip.acquire_time = This->last_gesturetime;
XSetSelectionOwner(This->display, This->xclip.primary_atom, This->wnd, This->xclip.acquire_time);
if (XGetSelectionOwner(This->display, This->xclip.primary_atom) != This->wnd)
warning("Failed to aquire ownership of PRIMARY clipboard\n");
XSetSelectionOwner(This->display, This->xclip.clipboard_atom, This->wnd, This->xclip.acquire_time);
if (XGetSelectionOwner(This->display, This->xclip.clipboard_atom) != This->wnd)
warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
if (This->xclip.formats_data)
xfree(This->xclip.formats_data);
This->xclip.formats_data = xmalloc(length);
memcpy(This->xclip.formats_data, data, length);
This->xclip.formats_data_length = length;
xclip_notify_change(This);
}
/* Called when the RDP server responds with clipboard data (after we've requested it). */
void
ui_clip_handle_data(RDPCLIENT * This, uint8 * data, uint32 length)
{
BOOL free_data = False;
if (length == 0)
{
xclip_refuse_selection(This, &This->xclip.selection_request);
This->xclip.has_selection_request = False;
return;
}
if (This->xclip.selection_request.target == This->xclip.format_string_atom || This->xclip.selection_request.target == XA_STRING)
{
/* We're expecting a CF_TEXT response */
uint8 *firstnull;
/* translate linebreaks */
crlf2lf(data, &length);
/* Only send data up to null byte, if any */
firstnull = (uint8 *) strchr((char *) data, '\0');
if (firstnull)
{
length = firstnull - data + 1;
}
}
#ifdef USE_UNICODE_CLIPBOARD
else if (This->xclip.selection_request.target == This->xclip.format_utf8_string_atom)
{
/* We're expecting a CF_UNICODETEXT response */
iconv_t cd = iconv_open("UTF-8", WINDOWS_CODEPAGE);
if (cd != (iconv_t) - 1)
{
size_t utf8_length = length * 2;
char *utf8_data = malloc(utf8_length);
size_t utf8_length_remaining = utf8_length;
char *utf8_data_remaining = utf8_data;
char *data_remaining = (char *) data;
size_t length_remaining = (size_t) length;
if (utf8_data == NULL)
{
iconv_close(cd);
return;
}
iconv(cd, (ICONV_CONST char **) &data_remaining, &length_remaining,
&utf8_data_remaining, &utf8_length_remaining);
iconv_close(cd);
free_data = True;
data = (uint8 *) utf8_data;
length = utf8_length - utf8_length_remaining;
}
}
else if (This->xclip.selection_request.target == This->xclip.format_unicode_atom)
{
/* We're expecting a CF_UNICODETEXT response, so what we're
receiving matches our requirements and there's no need
for further conversions. */
}
#endif
else if (This->xclip.selection_request.target == This->xclip.rdesktop_native_atom)
{
/* Pass as-is */
}
else
{
DEBUG_CLIPBOARD(("ui_clip_handle_data: BUG! I don't know how to convert selection target %s!\n", XGetAtomName(This->display, This->xclip.selection_request.target)));
xclip_refuse_selection(This, &This->xclip.selection_request);
This->xclip.has_selection_request = False;
return;
}
xclip_provide_selection(This, &This->xclip.selection_request, This->xclip.selection_request.target, 8, data, length - 1);
This->xclip.has_selection_request = False;
if (free_data)
free(data);
}
void
ui_clip_request_failed(RDPCLIENT * This)
{
xclip_refuse_selection(This, &This->xclip.selection_request);
This->xclip.has_selection_request = False;
}
void
ui_clip_request_data(RDPCLIENT * This, uint32 format)
{
Window primary_owner, clipboard_owner;
DEBUG_CLIPBOARD(("Request from server for format %d\n", format));
This->xclip.rdp_clipboard_request_format = format;
if (This->xclip.probing_selections)
{
DEBUG_CLIPBOARD(("ui_clip_request_data: Selection probe in progress. Cannot handle request.\n"));
helper_cliprdr_send_empty_response(This);
return;
}
xclip_clear_target_props(This);
if (This->xclip.rdesktop_is_selection_owner)
{
XChangeProperty(This->display, This->wnd, This->xclip.rdesktop_clipboard_target_atom,
XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);
XConvertSelection(This->display, This->xclip.primary_atom, This->xclip.rdesktop_native_atom,
This->xclip.rdesktop_clipboard_target_atom, This->wnd, CurrentTime);
return;
}
if (This->xclip.auto_mode)
primary_owner = XGetSelectionOwner(This->display, This->xclip.primary_atom);
else
primary_owner = None;
clipboard_owner = XGetSelectionOwner(This->display, This->xclip.clipboard_atom);
/* Both available */
if ((primary_owner != None) && (clipboard_owner != None))
{
This->xclip.primary_timestamp = 0;
This->xclip.clipboard_timestamp = 0;
XConvertSelection(This->display, This->xclip.primary_atom, This->xclip.timestamp_atom,
This->xclip.rdesktop_primary_timestamp_target_atom, This->wnd, CurrentTime);
XConvertSelection(This->display, This->xclip.clipboard_atom, This->xclip.timestamp_atom,
This->xclip.rdesktop_clipboard_timestamp_target_atom, This->wnd, CurrentTime);
return;
}
/* Just PRIMARY */
if (primary_owner != None)
{
XConvertSelection(This->display, This->xclip.primary_atom, This->xclip.targets_atom,
This->xclip.rdesktop_clipboard_target_atom, This->wnd, CurrentTime);
return;
}
/* Just CLIPBOARD */
if (clipboard_owner != None)
{
XConvertSelection(This->display, This->xclip.clipboard_atom, This->xclip.targets_atom,
This->xclip.rdesktop_clipboard_target_atom, This->wnd, CurrentTime);
return;
}
/* No data available */
helper_cliprdr_send_empty_response(This);
}
void
ui_clip_sync(RDPCLIENT * This)
{
xclip_probe_selections(This);
}
void
ui_clip_set_mode(RDPCLIENT * This, const char *optarg)
{
This->rdpclip = True;
if (str_startswith(optarg, "PRIMARYCLIPBOARD"))
This->xclip.auto_mode = True;
else if (str_startswith(optarg, "CLIPBOARD"))
This->xclip.auto_mode = False;
else
{
warning("Invalid clipboard mode '%s'.\n", optarg);
This->rdpclip = False;
}
}
void
xclip_init(RDPCLIENT * This)
{
if (!This->rdpclip)
return;
if (!cliprdr_init(This))
return;
This->xclip.primary_atom = XInternAtom(This->display, "PRIMARY", False);
This->xclip.clipboard_atom = XInternAtom(This->display, "CLIPBOARD", False);
This->xclip.targets_atom = XInternAtom(This->display, "TARGETS", False);
This->xclip.timestamp_atom = XInternAtom(This->display, "TIMESTAMP", False);
This->xclip.rdesktop_clipboard_target_atom =
XInternAtom(This->display, "_RDESKTOP_CLIPBOARD_TARGET", False);
This->xclip.rdesktop_primary_timestamp_target_atom =
XInternAtom(This->display, "_RDESKTOP_PRIMARY_TIMESTAMP_TARGET", False);
This->xclip.rdesktop_clipboard_timestamp_target_atom =
XInternAtom(This->display, "_RDESKTOP_CLIPBOARD_TIMESTAMP_TARGET", False);
This->xclip.incr_atom = XInternAtom(This->display, "INCR", False);
This->xclip.format_string_atom = XInternAtom(This->display, "STRING", False);
This->xclip.format_utf8_string_atom = XInternAtom(This->display, "UTF8_STRING", False);
This->xclip.format_unicode_atom = XInternAtom(This->display, "text/unicode", False);
/* rdesktop sets _RDESKTOP_SELECTION_NOTIFY on the root window when acquiring the clipboard.
Other interested rdesktops can use this to notify their server of the available formats. */
This->xclip.rdesktop_selection_notify_atom =
XInternAtom(This->display, "_RDESKTOP_SELECTION_NOTIFY", False);
XSelectInput(This->display, DefaultRootWindow(This->display), PropertyChangeMask);
This->xclip.probing_selections = False;
This->xclip.rdesktop_native_atom = XInternAtom(This->display, "_RDESKTOP_NATIVE", False);
This->xclip.rdesktop_clipboard_formats_atom =
XInternAtom(This->display, "_RDESKTOP_CLIPBOARD_FORMATS", False);
This->xclip.rdesktop_primary_owner_atom = XInternAtom(This->display, "_RDESKTOP_PRIMARY_OWNER", False);
This->xclip.rdesktop_clipboard_owner_atom = XInternAtom(This->display, "_RDESKTOP_CLIPBOARD_OWNER", False);
This->xclip.num_targets = 0;
This->xclip.targets[This->xclip.num_targets++] = This->xclip.targets_atom;
This->xclip.targets[This->xclip.num_targets++] = This->xclip.timestamp_atom;
This->xclip.targets[This->xclip.num_targets++] = This->xclip.rdesktop_native_atom;
This->xclip.targets[This->xclip.num_targets++] = This->xclip.rdesktop_clipboard_formats_atom;
#ifdef USE_UNICODE_CLIPBOARD
This->xclip.targets[This->xclip.num_targets++] = This->xclip.format_utf8_string_atom;
#endif
This->xclip.targets[This->xclip.num_targets++] = This->xclip.format_unicode_atom;
This->xclip.targets[This->xclip.num_targets++] = This->xclip.format_string_atom;
This->xclip.targets[This->xclip.num_targets++] = XA_STRING;
}
void
xclip_deinit(RDPCLIENT * This)
{
if (XGetSelectionOwner(This->display, This->xclip.primary_atom) == This->wnd)
XSetSelectionOwner(This->display, This->xclip.primary_atom, None, This->xclip.acquire_time);
if (XGetSelectionOwner(This->display, This->xclip.clipboard_atom) == This->wnd)
XSetSelectionOwner(This->display, This->xclip.clipboard_atom, None, This->xclip.acquire_time);
xclip_notify_change(This);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -