36 |
static int have_primary = 0; |
static int have_primary = 0; |
37 |
static int rdesktop_is_selection_owner = 0; |
static int rdesktop_is_selection_owner = 0; |
38 |
|
|
39 |
|
static int g_waiting_for_INCR = 0; |
40 |
|
static uint8 *g_clip_buffer = 0; |
41 |
|
static uint32 g_clip_buflen = 0; |
42 |
|
|
43 |
|
/* Replace CR-LF to LF (well, rather removing all CR:s) This is done |
44 |
|
in-place. The length is updated. Handles embedded nulls */ |
45 |
|
static void |
46 |
|
crlf2lf(uint8 * data, uint32 * length) |
47 |
|
{ |
48 |
|
uint8 *dst, *src; |
49 |
|
src = dst = data; |
50 |
|
while (src < data + *length) |
51 |
|
{ |
52 |
|
if (*src != '\x0d') |
53 |
|
*dst++ = *src; |
54 |
|
src++; |
55 |
|
} |
56 |
|
*length = dst - data; |
57 |
|
} |
58 |
|
|
59 |
|
/* Translate LF to CR-LF. To do this, we must allocate more memory. |
60 |
|
The length is updated. */ |
61 |
|
static uint8 * |
62 |
|
lf2crlf(uint8 * data, uint32 * length) |
63 |
|
{ |
64 |
|
uint8 *result, *p, *o; |
65 |
|
|
66 |
|
/* Worst case: Every char is LF */ |
67 |
|
result = xmalloc(*length * 2); |
68 |
|
|
69 |
|
p = data; |
70 |
|
o = result; |
71 |
|
|
72 |
|
while (p < data + *length) |
73 |
|
{ |
74 |
|
if (*p == '\x0a') |
75 |
|
*o++ = '\x0d'; |
76 |
|
*o++ = *p++; |
77 |
|
} |
78 |
|
*length = o - result; |
79 |
|
|
80 |
|
/* Convenience */ |
81 |
|
*o++ = '\0'; |
82 |
|
|
83 |
|
return result; |
84 |
|
} |
85 |
|
|
86 |
|
|
87 |
static void |
static void |
88 |
xclip_provide_selection(XSelectionRequestEvent * req, Atom type, unsigned int format, uint8 * data, |
xclip_provide_selection(XSelectionRequestEvent * req, Atom type, unsigned int format, uint8 * data, |
89 |
uint32 length) |
uint32 length) |
108 |
xclip_handle_SelectionNotify(XSelectionEvent * event) |
xclip_handle_SelectionNotify(XSelectionEvent * event) |
109 |
{ |
{ |
110 |
unsigned long nitems, bytes_left; |
unsigned long nitems, bytes_left; |
111 |
|
XWindowAttributes wa; |
112 |
Atom type, best_target, text_target; |
Atom type, best_target, text_target; |
113 |
Atom *supported_targets; |
Atom *supported_targets; |
114 |
int res, i, format; |
int res, i, format; |
135 |
goto fail; |
goto fail; |
136 |
} |
} |
137 |
|
|
138 |
|
/* Negotiate target format */ |
139 |
if (event->target == targets_atom) |
if (event->target == targets_atom) |
140 |
{ |
{ |
141 |
/* FIXME: We should choose format here based on what the server wanted */ |
/* FIXME: We should choose format here based on what the server wanted */ |
152 |
{ |
{ |
153 |
DEBUG_CLIPBOARD(("Other party supports TEXT, choosing that as best_target\n")); |
DEBUG_CLIPBOARD(("Other party supports TEXT, choosing that as best_target\n")); |
154 |
best_target = text_target; |
best_target = text_target; |
155 |
|
break; |
156 |
} |
} |
157 |
} |
} |
158 |
XFree(data); |
XFree(data); |
165 |
|
|
166 |
if (type == incr_atom) |
if (type == incr_atom) |
167 |
{ |
{ |
168 |
warning("We don't support INCR transfers at this time. Try cutting less data.\n"); |
DEBUG_CLIPBOARD(("Received INCR.\n")); |
169 |
goto fail; |
|
170 |
|
XGetWindowAttributes(g_display, g_wnd, &wa); |
171 |
|
if ((wa.your_event_mask | PropertyChangeMask) != wa.your_event_mask) |
172 |
|
{ |
173 |
|
XSelectInput(g_display, g_wnd, (wa.your_event_mask | PropertyChangeMask)); |
174 |
|
} |
175 |
|
XDeleteProperty(g_display, g_wnd, type); |
176 |
|
XFree(data); |
177 |
|
g_waiting_for_INCR = 1; |
178 |
|
|
179 |
|
if ((XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom, 0, |
180 |
|
4096L, True, AnyPropertyType, |
181 |
|
&type, &format, &nitems, &bytes_left, &data) != Success)) |
182 |
|
{ |
183 |
|
DEBUG_CLIPBOARD(("XGetWindowProperty failed.\n")); |
184 |
|
goto fail; |
185 |
|
} |
186 |
|
else |
187 |
|
{ |
188 |
|
uint8 *translated_data; |
189 |
|
uint32 length = nitems; |
190 |
|
|
191 |
|
translated_data = lf2crlf(data, &length); |
192 |
|
|
193 |
|
g_clip_buffer = (uint8 *) xmalloc(length); |
194 |
|
strncpy((char*)g_clip_buffer, (char*)translated_data, length); |
195 |
|
xfree(translated_data); |
196 |
|
g_clip_buflen = length; |
197 |
|
|
198 |
|
XFree(data); |
199 |
|
return; |
200 |
|
} |
201 |
} |
} |
202 |
|
|
203 |
cliprdr_send_data(data, nitems + 1); |
/* Translate linebreaks, but only if not getting data from |
204 |
|
other rdesktop instance */ |
205 |
|
if (event->target != rdesktop_clipboard_formats_atom) |
206 |
|
{ |
207 |
|
uint8 *translated_data; |
208 |
|
uint32 length = nitems; |
209 |
|
|
210 |
|
DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n")); |
211 |
|
translated_data = lf2crlf(data, &length); |
212 |
|
cliprdr_send_data(translated_data, length + 1); |
213 |
|
xfree(translated_data); /* Not the same thing as XFree! */ |
214 |
|
} |
215 |
|
else |
216 |
|
{ |
217 |
|
cliprdr_send_data(data, nitems + 1); |
218 |
|
} |
219 |
XFree(data); |
XFree(data); |
220 |
|
|
221 |
if (!rdesktop_is_selection_owner) |
if (!rdesktop_is_selection_owner) |
230 |
xclip_handle_SelectionRequest(XSelectionRequestEvent * event) |
xclip_handle_SelectionRequest(XSelectionRequestEvent * event) |
231 |
{ |
{ |
232 |
unsigned long nitems, bytes_left; |
unsigned long nitems, bytes_left; |
233 |
|
unsigned char *prop_return; |
234 |
uint32 *wanted_format; |
uint32 *wanted_format; |
235 |
int format, res; |
int format, res; |
236 |
Atom type; |
Atom type; |
254 |
{ |
{ |
255 |
res = XGetWindowProperty(g_display, event->requestor, |
res = XGetWindowProperty(g_display, event->requestor, |
256 |
rdesktop_clipboard_target_atom, 0, 1, True, XA_INTEGER, |
rdesktop_clipboard_target_atom, 0, 1, True, XA_INTEGER, |
257 |
&type, &format, &nitems, &bytes_left, |
&type, &format, &nitems, &bytes_left, &prop_return); |
258 |
(unsigned char **) &wanted_format); |
wanted_format = (uint32 *) prop_return; |
259 |
format = (res == Success) ? *wanted_format : CF_TEXT; |
format = (res == Success) ? *wanted_format : CF_TEXT; |
260 |
|
/* FIXME: Need to free returned data? */ |
261 |
} |
} |
262 |
else |
else |
263 |
{ |
{ |
283 |
{ |
{ |
284 |
unsigned long nitems, bytes_left; |
unsigned long nitems, bytes_left; |
285 |
int format, res; |
int format, res; |
286 |
|
XWindowAttributes wa; |
287 |
uint8 *data; |
uint8 *data; |
288 |
Atom type; |
Atom type; |
289 |
|
|
290 |
|
if (event->state == PropertyNewValue && g_waiting_for_INCR) |
291 |
|
{ |
292 |
|
DEBUG_CLIPBOARD(("x_clip_handle_PropertyNotify: g_waiting_for_INCR != 0\n")); |
293 |
|
if ((XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom, 0, |
294 |
|
4096L, True, AnyPropertyType, |
295 |
|
&type, &format, &nitems, &bytes_left, &data) != Success)) |
296 |
|
{ |
297 |
|
XFree(data); |
298 |
|
return; |
299 |
|
} |
300 |
|
|
301 |
|
if (nitems == 0) |
302 |
|
{ |
303 |
|
XGetWindowAttributes(g_display, g_wnd, &wa); |
304 |
|
XSelectInput(g_display, g_wnd, (wa.your_event_mask ^ PropertyChangeMask)); |
305 |
|
XFree(data); |
306 |
|
g_waiting_for_INCR = 0; |
307 |
|
|
308 |
|
if (g_clip_buflen > 0) |
309 |
|
{ |
310 |
|
cliprdr_send_data(g_clip_buffer, g_clip_buflen + 1); |
311 |
|
|
312 |
|
if (!rdesktop_is_selection_owner) |
313 |
|
cliprdr_send_text_format_announce(); |
314 |
|
|
315 |
|
xfree(g_clip_buffer); |
316 |
|
g_clip_buffer = 0; |
317 |
|
g_clip_buflen = 0; |
318 |
|
} |
319 |
|
} |
320 |
|
else |
321 |
|
{ |
322 |
|
uint8 *translated_data; |
323 |
|
uint32 length = nitems; |
324 |
|
uint8 *tmp; |
325 |
|
|
326 |
|
DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n")); |
327 |
|
translated_data = lf2crlf(data, &length); |
328 |
|
|
329 |
|
tmp = xmalloc(length + g_clip_buflen); |
330 |
|
strncpy((char*)tmp, (char*)g_clip_buffer, g_clip_buflen); |
331 |
|
xfree(g_clip_buffer); |
332 |
|
|
333 |
|
strncpy((char*)(tmp + g_clip_buflen), (char*)translated_data, length); |
334 |
|
xfree(translated_data); |
335 |
|
|
336 |
|
g_clip_buffer = tmp; |
337 |
|
g_clip_buflen += length; |
338 |
|
|
339 |
|
XFree(data); |
340 |
|
return; |
341 |
|
} |
342 |
|
} |
343 |
|
|
344 |
if (event->atom != rdesktop_clipboard_formats_atom) |
if (event->atom != rdesktop_clipboard_formats_atom) |
345 |
return; |
return; |
346 |
|
|
369 |
|
|
370 |
|
|
371 |
void |
void |
372 |
ui_clip_format_announce(char *data, uint32 length) |
ui_clip_format_announce(uint8 * data, uint32 length) |
373 |
{ |
{ |
374 |
XSetSelectionOwner(g_display, primary_atom, g_wnd, g_last_gesturetime); |
XSetSelectionOwner(g_display, primary_atom, g_wnd, g_last_gesturetime); |
375 |
if (XGetSelectionOwner(g_display, primary_atom) != g_wnd) |
if (XGetSelectionOwner(g_display, primary_atom) != g_wnd) |
390 |
|
|
391 |
|
|
392 |
void |
void |
393 |
ui_clip_handle_data(char *data, uint32 length) |
ui_clip_handle_data(uint8 * data, uint32 length) |
394 |
{ |
{ |
395 |
|
if (selection_request.target != rdesktop_clipboard_formats_atom) |
396 |
|
{ |
397 |
|
uint8 *firstnull; |
398 |
|
|
399 |
|
/* translate linebreaks */ |
400 |
|
crlf2lf(data, &length); |
401 |
|
|
402 |
|
/* Only send data up to null byte, if any */ |
403 |
|
firstnull = (uint8 *) strchr((char *) data, '\0'); |
404 |
|
if (firstnull) |
405 |
|
{ |
406 |
|
length = firstnull - data + 1; |
407 |
|
} |
408 |
|
} |
409 |
|
|
410 |
xclip_provide_selection(&selection_request, XA_STRING, 8, data, length - 1); |
xclip_provide_selection(&selection_request, XA_STRING, 8, data, length - 1); |
411 |
} |
} |
412 |
|
|
470 |
incr_atom = XInternAtom(g_display, "INCR", False); |
incr_atom = XInternAtom(g_display, "INCR", False); |
471 |
targets[0] = targets_atom; |
targets[0] = targets_atom; |
472 |
targets[1] = XInternAtom(g_display, "TEXT", False); |
targets[1] = XInternAtom(g_display, "TEXT", False); |
473 |
targets[2] = XInternAtom(g_display, "UTF8_STRING", False); |
targets[2] = XInternAtom(g_display, "STRING", False); |
474 |
targets[3] = XInternAtom(g_display, "text/unicode", False); |
targets[3] = XInternAtom(g_display, "text/unicode", False); |
475 |
targets[4] = XInternAtom(g_display, "TIMESTAMP", False); |
targets[4] = XInternAtom(g_display, "TIMESTAMP", False); |
476 |
targets[5] = XA_STRING; |
targets[5] = XA_STRING; |