/[rdesktop]/sourceforge.net/trunk/rdesktop/xclip.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /sourceforge.net/trunk/rdesktop/xclip.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1025 - (show annotations)
Mon Nov 7 13:15:19 2005 UTC (18 years, 7 months ago) by forsberg
File MIME type: text/plain
File size: 12991 byte(s)
Applied patch 1349027 by Ilya Konstantinov.

Generalizes code for sending clipboard format announces to RDP side,
and uses new code in appropriate places.

1 /* -*- c-basic-offset: 8 -*-
2 rdesktop: A Remote Desktop Protocol client.
3 Protocol services - Clipboard functions
4 Copyright (C) Erik Forsberg <forsberg@cendio.se> 2003
5 Copyright (C) Matthew Chapman 2003
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include <X11/Xlib.h>
23 #include <X11/Xatom.h>
24 #include "rdesktop.h"
25
26 #define NUM_TARGETS 6
27
28 extern Display *g_display;
29 extern Window g_wnd;
30 extern Time g_last_gesturetime;
31
32 static Atom clipboard_atom, primary_atom, targets_atom, timestamp_atom;
33 static Atom rdesktop_clipboard_target_atom, rdesktop_clipboard_formats_atom, incr_atom;
34 static XSelectionRequestEvent selection_request;
35 static Atom targets[NUM_TARGETS];
36 static int have_primary = 0;
37 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
88 xclip_provide_selection(XSelectionRequestEvent * req, Atom type, unsigned int format, uint8 * data,
89 uint32 length)
90 {
91 XEvent xev;
92
93 XChangeProperty(g_display, req->requestor, req->property,
94 type, format, PropModeReplace, data, length);
95
96 xev.xselection.type = SelectionNotify;
97 xev.xselection.serial = 0;
98 xev.xselection.send_event = True;
99 xev.xselection.requestor = req->requestor;
100 xev.xselection.selection = req->selection;
101 xev.xselection.target = req->target;
102 xev.xselection.property = req->property;
103 xev.xselection.time = req->time;
104 XSendEvent(g_display, req->requestor, False, NoEventMask, &xev);
105 }
106
107 #ifndef MAKE_PROTO
108 void
109 xclip_handle_SelectionNotify(XSelectionEvent * event)
110 {
111 unsigned long nitems, bytes_left;
112 XWindowAttributes wa;
113 Atom type, best_target, text_target;
114 Atom *supported_targets;
115 int res, i, format;
116 uint8 *data;
117
118 if (event->property == None)
119 goto fail;
120
121 DEBUG_CLIPBOARD(("xclip_handle_SelectionNotify: selection=%s, target=%s, property=%s\n",
122 XGetAtomName(g_display, event->selection),
123 XGetAtomName(g_display, event->target),
124 XGetAtomName(g_display, event->property)));
125
126 if (event->property == None)
127 goto fail;
128
129 res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
130 0, XMaxRequestSize(g_display), True, AnyPropertyType,
131 &type, &format, &nitems, &bytes_left, &data);
132
133 if (res != Success)
134 {
135 DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
136 goto fail;
137 }
138
139 /* Negotiate target format */
140 if (event->target == targets_atom)
141 {
142 /* FIXME: We should choose format here based on what the server wanted */
143 best_target = XA_STRING;
144 if (type != None)
145 {
146 supported_targets = (Atom *) data;
147 text_target = XInternAtom(g_display, "TEXT", False);
148 for (i = 0; i < nitems; i++)
149 {
150 DEBUG_CLIPBOARD(("Target %d: %s\n", i,
151 XGetAtomName(g_display, supported_targets[i])));
152 if (supported_targets[i] == text_target)
153 {
154 DEBUG_CLIPBOARD(("Other party supports TEXT, choosing that as best_target\n"));
155 best_target = text_target;
156 break;
157 }
158 }
159 XFree(data);
160 }
161
162 XConvertSelection(g_display, primary_atom, best_target,
163 rdesktop_clipboard_target_atom, g_wnd, event->time);
164 return;
165 }
166
167 if (type == incr_atom)
168 {
169 DEBUG_CLIPBOARD(("Received INCR.\n"));
170
171 XGetWindowAttributes(g_display, g_wnd, &wa);
172 if ((wa.your_event_mask | PropertyChangeMask) != wa.your_event_mask)
173 {
174 XSelectInput(g_display, g_wnd, (wa.your_event_mask | PropertyChangeMask));
175 }
176 XDeleteProperty(g_display, g_wnd, type);
177 XFree(data);
178 g_waiting_for_INCR = 1;
179
180 if ((XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom, 0,
181 4096L, True, AnyPropertyType,
182 &type, &format, &nitems, &bytes_left, &data) != Success))
183 {
184 DEBUG_CLIPBOARD(("XGetWindowProperty failed.\n"));
185 goto fail;
186 }
187 else
188 {
189 uint8 *translated_data;
190 uint32 length = nitems;
191
192 translated_data = lf2crlf(data, &length);
193
194 g_clip_buffer = (uint8 *) xmalloc(length);
195 strncpy((char *) g_clip_buffer, (char *) translated_data, length);
196 xfree(translated_data);
197 g_clip_buflen = length;
198
199 XFree(data);
200 return;
201 }
202 }
203
204 /* Translate linebreaks, but only if not getting data from
205 other rdesktop instance */
206 if (event->target != rdesktop_clipboard_formats_atom)
207 {
208 uint8 *translated_data;
209 uint32 length = nitems;
210
211 DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));
212 translated_data = lf2crlf(data, &length);
213 cliprdr_send_data(translated_data, length + 1);
214 xfree(translated_data); /* Not the same thing as XFree! */
215 }
216 else
217 {
218 cliprdr_send_data(data, nitems + 1);
219 }
220 XFree(data);
221
222 if (!rdesktop_is_selection_owner)
223 cliprdr_send_simple_native_format_announce(CF_TEXT);
224 return;
225
226 fail:
227 cliprdr_send_data(NULL, 0);
228 }
229
230 void
231 xclip_handle_SelectionRequest(XSelectionRequestEvent * event)
232 {
233 unsigned long nitems, bytes_left;
234 unsigned char *prop_return;
235 uint32 *wanted_format;
236 int format, res;
237 Atom type;
238
239 DEBUG_CLIPBOARD(("xclip_handle_SelectionRequest: selection=%s, target=%s, property=%s\n",
240 XGetAtomName(g_display, event->selection),
241 XGetAtomName(g_display, event->target),
242 XGetAtomName(g_display, event->property)));
243
244 if (event->target == targets_atom)
245 {
246 xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, NUM_TARGETS);
247 return;
248 }
249 else if (event->target == timestamp_atom)
250 {
251 xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & g_last_gesturetime, 1);
252 return;
253 }
254 else if (event->target == rdesktop_clipboard_formats_atom)
255 {
256 res = XGetWindowProperty(g_display, event->requestor,
257 rdesktop_clipboard_target_atom, 0, 1, True, XA_INTEGER,
258 &type, &format, &nitems, &bytes_left, &prop_return);
259 wanted_format = (uint32 *) prop_return;
260 format = (res == Success) ? *wanted_format : CF_TEXT;
261 /* FIXME: Need to free returned data? */
262 }
263 else
264 {
265 format = CF_TEXT;
266 }
267
268 cliprdr_send_data_request(format);
269 selection_request = *event;
270 /* wait for data */
271 }
272
273 void
274 xclip_handle_SelectionClear(void)
275 {
276 DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
277 have_primary = 0;
278 XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);
279 cliprdr_send_simple_native_format_announce(CF_TEXT);
280 }
281
282 void
283 xclip_handle_PropertyNotify(XPropertyEvent * event)
284 {
285 unsigned long nitems, bytes_left;
286 int format, res;
287 XWindowAttributes wa;
288 uint8 *data;
289 Atom type;
290
291 if (event->state == PropertyNewValue && g_waiting_for_INCR)
292 {
293 DEBUG_CLIPBOARD(("x_clip_handle_PropertyNotify: g_waiting_for_INCR != 0\n"));
294 if ((XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom, 0,
295 4096L, True, AnyPropertyType,
296 &type, &format, &nitems, &bytes_left, &data) != Success))
297 {
298 XFree(data);
299 return;
300 }
301
302 if (nitems == 0)
303 {
304 XGetWindowAttributes(g_display, g_wnd, &wa);
305 XSelectInput(g_display, g_wnd, (wa.your_event_mask ^ PropertyChangeMask));
306 XFree(data);
307 g_waiting_for_INCR = 0;
308
309 if (g_clip_buflen > 0)
310 {
311 cliprdr_send_data(g_clip_buffer, g_clip_buflen + 1);
312
313 if (!rdesktop_is_selection_owner)
314 cliprdr_send_simple_native_format_announce(CF_TEXT);
315
316 xfree(g_clip_buffer);
317 g_clip_buffer = 0;
318 g_clip_buflen = 0;
319 }
320 }
321 else
322 {
323 uint8 *translated_data;
324 uint32 length = nitems;
325 uint8 *tmp;
326
327 DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));
328 translated_data = lf2crlf(data, &length);
329
330 tmp = xmalloc(length + g_clip_buflen);
331 strncpy((char *) tmp, (char *) g_clip_buffer, g_clip_buflen);
332 xfree(g_clip_buffer);
333
334 strncpy((char *) (tmp + g_clip_buflen), (char *) translated_data, length);
335 xfree(translated_data);
336
337 g_clip_buffer = tmp;
338 g_clip_buflen += length;
339
340 XFree(data);
341 return;
342 }
343 }
344
345 if (event->atom != rdesktop_clipboard_formats_atom)
346 return;
347
348 if (have_primary) /* from us */
349 return;
350
351 if (event->state == PropertyNewValue)
352 {
353 res = XGetWindowProperty(g_display, DefaultRootWindow(g_display),
354 rdesktop_clipboard_formats_atom, 0,
355 XMaxRequestSize(g_display), False, XA_STRING, &type,
356 &format, &nitems, &bytes_left, &data);
357
358 if ((res == Success) && (nitems > 0))
359 {
360 cliprdr_send_native_format_announce(data, nitems);
361 rdesktop_is_selection_owner = 1;
362 return;
363 }
364 }
365
366 /* PropertyDelete, or XGetWindowProperty failed */
367 cliprdr_send_simple_native_format_announce(CF_TEXT);
368 rdesktop_is_selection_owner = 0;
369 }
370 #endif
371
372
373 void
374 ui_clip_format_announce(uint8 * data, uint32 length)
375 {
376 XSetSelectionOwner(g_display, primary_atom, g_wnd, g_last_gesturetime);
377 if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)
378 {
379 warning("Failed to aquire ownership of PRIMARY clipboard\n");
380 return;
381 }
382
383 have_primary = 1;
384 XChangeProperty(g_display, DefaultRootWindow(g_display),
385 rdesktop_clipboard_formats_atom, XA_STRING, 8, PropModeReplace, data,
386 length);
387
388 XSetSelectionOwner(g_display, clipboard_atom, g_wnd, g_last_gesturetime);
389 if (XGetSelectionOwner(g_display, clipboard_atom) != g_wnd)
390 warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
391 }
392
393
394 void
395 ui_clip_handle_data(uint8 * data, uint32 length)
396 {
397 if (selection_request.target != rdesktop_clipboard_formats_atom)
398 {
399 uint8 *firstnull;
400
401 /* translate linebreaks */
402 crlf2lf(data, &length);
403
404 /* Only send data up to null byte, if any */
405 firstnull = (uint8 *) strchr((char *) data, '\0');
406 if (firstnull)
407 {
408 length = firstnull - data + 1;
409 }
410 }
411
412 xclip_provide_selection(&selection_request, XA_STRING, 8, data, length - 1);
413 }
414
415 void
416 ui_clip_request_data(uint32 format)
417 {
418 Window selectionowner;
419
420 DEBUG_CLIPBOARD(("Request from server for format %d\n", format));
421
422 if (rdesktop_is_selection_owner)
423 {
424 XChangeProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
425 XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);
426
427 XConvertSelection(g_display, primary_atom, rdesktop_clipboard_formats_atom,
428 rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
429 return;
430 }
431
432 selectionowner = XGetSelectionOwner(g_display, primary_atom);
433 if (selectionowner != None)
434 {
435 XConvertSelection(g_display, primary_atom, targets_atom,
436 rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
437 return;
438 }
439
440 /* No PRIMARY, try CLIPBOARD */
441 selectionowner = XGetSelectionOwner(g_display, clipboard_atom);
442 if (selectionowner != None)
443 {
444 XConvertSelection(g_display, clipboard_atom, targets_atom,
445 rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
446 return;
447 }
448
449 /* No data available */
450 cliprdr_send_data(NULL, 0);
451 }
452
453 void
454 ui_clip_sync(void)
455 {
456 cliprdr_send_simple_native_format_announce(CF_TEXT);
457 }
458
459
460 void
461 xclip_init(void)
462 {
463 if (!cliprdr_init())
464 return;
465
466 primary_atom = XInternAtom(g_display, "PRIMARY", False);
467 clipboard_atom = XInternAtom(g_display, "CLIPBOARD", False);
468 targets_atom = XInternAtom(g_display, "TARGETS", False);
469 timestamp_atom = XInternAtom(g_display, "TIMESTAMP", False);
470 rdesktop_clipboard_target_atom =
471 XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);
472 incr_atom = XInternAtom(g_display, "INCR", False);
473 targets[0] = targets_atom;
474 targets[1] = XInternAtom(g_display, "TEXT", False);
475 targets[2] = XInternAtom(g_display, "STRING", False);
476 targets[3] = XInternAtom(g_display, "text/unicode", False);
477 targets[4] = XInternAtom(g_display, "TIMESTAMP", False);
478 targets[5] = XA_STRING;
479
480 /* rdesktop sets _RDESKTOP_CLIPBOARD_FORMATS on the root window when acquiring the clipboard.
481 Other interested rdesktops can use this to notify their server of the available formats. */
482 rdesktop_clipboard_formats_atom =
483 XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False);
484 XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask);
485 }

  ViewVC Help
Powered by ViewVC 1.1.26