/[rdesktop]/jpeg/rdesktop/trunk/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 /jpeg/rdesktop/trunk/xclip.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1026 - (show annotations)
Wed Nov 9 15:15:28 2005 UTC (18 years, 6 months ago) by forsberg
Original Path: sourceforge.net/trunk/rdesktop/xclip.c
File MIME type: text/plain
File size: 12819 byte(s)
Applied patch from Burt Holzman <burt at fnal gov>, which fixes problems with
the INCR support, used when doing clipboard operations with a lot of data.

With this patch, I can copy and then paste a 32 page document from
oowriter->winword.

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), False, 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
140 if (type == incr_atom)
141 {
142 DEBUG_CLIPBOARD(("Received INCR.\n"));
143
144 XGetWindowAttributes(g_display, g_wnd, &wa);
145 if ((wa.your_event_mask | PropertyChangeMask) != wa.your_event_mask)
146 {
147 XSelectInput(g_display, g_wnd, (wa.your_event_mask | PropertyChangeMask));
148 }
149 XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
150 XFree(data);
151 g_waiting_for_INCR = 1;
152 return;
153 }
154
155 XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
156
157 /* Negotiate target format */
158 if (event->target == targets_atom)
159 {
160 /* FIXME: We should choose format here based on what the server wanted */
161 best_target = XA_STRING;
162 if (type != None)
163 {
164 supported_targets = (Atom *) data;
165 text_target = XInternAtom(g_display, "TEXT", False);
166 for (i = 0; i < nitems; i++)
167 {
168 DEBUG_CLIPBOARD(("Target %d: %s\n", i,
169 XGetAtomName(g_display, supported_targets[i])));
170 if (supported_targets[i] == text_target)
171 {
172 DEBUG_CLIPBOARD(("Other party supports TEXT, choosing that as best_target\n"));
173 best_target = text_target;
174 break;
175 }
176 }
177 XFree(data);
178 }
179
180 XConvertSelection(g_display, primary_atom, best_target,
181 rdesktop_clipboard_target_atom, g_wnd, event->time);
182 return;
183 }
184
185 /* Translate linebreaks, but only if not getting data from
186 other rdesktop instance */
187 if (event->target != rdesktop_clipboard_formats_atom)
188 {
189 uint8 *translated_data;
190 uint32 length = nitems;
191
192 DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));
193 translated_data = lf2crlf(data, &length);
194 cliprdr_send_data(translated_data, length + 1);
195 xfree(translated_data); /* Not the same thing as XFree! */
196 }
197 else
198 {
199 cliprdr_send_data(data, nitems + 1);
200 }
201 XFree(data);
202
203 if (!rdesktop_is_selection_owner)
204 cliprdr_send_simple_native_format_announce(CF_TEXT);
205 return;
206
207 fail:
208 XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
209 XFree(data);
210 cliprdr_send_data(NULL, 0);
211 }
212
213 void
214 xclip_handle_SelectionRequest(XSelectionRequestEvent * event)
215 {
216 unsigned long nitems, bytes_left;
217 unsigned char *prop_return;
218 uint32 *wanted_format;
219 int format, res;
220 Atom type;
221
222 DEBUG_CLIPBOARD(("xclip_handle_SelectionRequest: selection=%s, target=%s, property=%s\n",
223 XGetAtomName(g_display, event->selection),
224 XGetAtomName(g_display, event->target),
225 XGetAtomName(g_display, event->property)));
226
227 if (event->target == targets_atom)
228 {
229 xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, NUM_TARGETS);
230 return;
231 }
232 else if (event->target == timestamp_atom)
233 {
234 xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & g_last_gesturetime, 1);
235 return;
236 }
237 else if (event->target == rdesktop_clipboard_formats_atom)
238 {
239 res = XGetWindowProperty(g_display, event->requestor,
240 rdesktop_clipboard_target_atom, 0, 1, True, XA_INTEGER,
241 &type, &format, &nitems, &bytes_left, &prop_return);
242 wanted_format = (uint32 *) prop_return;
243 format = (res == Success) ? *wanted_format : CF_TEXT;
244 /* FIXME: Need to free returned data? */
245 }
246 else
247 {
248 format = CF_TEXT;
249 }
250
251 cliprdr_send_data_request(format);
252 selection_request = *event;
253 /* wait for data */
254 }
255
256 void
257 xclip_handle_SelectionClear(void)
258 {
259 DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
260 have_primary = 0;
261 XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);
262 cliprdr_send_simple_native_format_announce(CF_TEXT);
263 }
264
265 void
266 xclip_handle_PropertyNotify(XPropertyEvent * event)
267 {
268 unsigned long nitems;
269 unsigned long offset = 0;
270 unsigned long bytes_left = 1;
271 int format, res;
272 XWindowAttributes wa;
273 uint8 *data;
274 Atom type;
275
276 if (event->state == PropertyNewValue && g_waiting_for_INCR)
277 {
278 DEBUG_CLIPBOARD(("x_clip_handle_PropertyNotify: g_waiting_for_INCR != 0\n"));
279
280 while (bytes_left > 0) {
281 if ((XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom, offset,
282 4096L, False, AnyPropertyType,
283 &type, &format, &nitems, &bytes_left, &data) != Success))
284 {
285 XFree(data);
286 return;
287 }
288
289 if (nitems == 0)
290 {
291 XGetWindowAttributes(g_display, g_wnd, &wa);
292 XSelectInput(g_display, g_wnd, (wa.your_event_mask ^ PropertyChangeMask));
293 XFree(data);
294 g_waiting_for_INCR = 0;
295
296 if (g_clip_buflen > 0)
297 {
298 cliprdr_send_data(g_clip_buffer, g_clip_buflen + 1);
299
300 if (!rdesktop_is_selection_owner)
301 cliprdr_send_simple_native_format_announce(CF_TEXT);
302
303 xfree(g_clip_buffer);
304 g_clip_buffer = 0;
305 g_clip_buflen = 0;
306 }
307 }
308 else
309 {
310 uint8 *translated_data;
311 uint32 length = nitems;
312 uint8 *tmp;
313
314 offset += (length/4);
315 DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));
316 translated_data = lf2crlf(data, &length);
317
318 tmp = xmalloc(length + g_clip_buflen);
319 strncpy((char *) tmp, (char *) g_clip_buffer, g_clip_buflen);
320 xfree(g_clip_buffer);
321
322 strncpy((char *) (tmp + g_clip_buflen), (char *) translated_data, length);
323 xfree(translated_data);
324
325 g_clip_buffer = tmp;
326 g_clip_buflen += length;
327
328 XFree(data);
329 }
330 }
331 XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
332 }
333
334 if (event->atom != rdesktop_clipboard_formats_atom)
335 return;
336
337 if (have_primary) /* from us */
338 return;
339
340 if (event->state == PropertyNewValue)
341 {
342 res = XGetWindowProperty(g_display, DefaultRootWindow(g_display),
343 rdesktop_clipboard_formats_atom, 0,
344 XMaxRequestSize(g_display), False, XA_STRING, &type,
345 &format, &nitems, &bytes_left, &data);
346
347 if ((res == Success) && (nitems > 0))
348 {
349 cliprdr_send_native_format_announce(data, nitems);
350 rdesktop_is_selection_owner = 1;
351 return;
352 }
353 }
354
355 /* PropertyDelete, or XGetWindowProperty failed */
356 cliprdr_send_simple_native_format_announce(CF_TEXT);
357 rdesktop_is_selection_owner = 0;
358 }
359 #endif
360
361
362 void
363 ui_clip_format_announce(uint8 * data, uint32 length)
364 {
365 XSetSelectionOwner(g_display, primary_atom, g_wnd, g_last_gesturetime);
366 if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)
367 {
368 warning("Failed to aquire ownership of PRIMARY clipboard\n");
369 return;
370 }
371
372 have_primary = 1;
373 XChangeProperty(g_display, DefaultRootWindow(g_display),
374 rdesktop_clipboard_formats_atom, XA_STRING, 8, PropModeReplace, data,
375 length);
376
377 XSetSelectionOwner(g_display, clipboard_atom, g_wnd, g_last_gesturetime);
378 if (XGetSelectionOwner(g_display, clipboard_atom) != g_wnd)
379 warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
380 }
381
382
383 void
384 ui_clip_handle_data(uint8 * data, uint32 length)
385 {
386 if (selection_request.target != rdesktop_clipboard_formats_atom)
387 {
388 uint8 *firstnull;
389
390 /* translate linebreaks */
391 crlf2lf(data, &length);
392
393 /* Only send data up to null byte, if any */
394 firstnull = (uint8 *) strchr((char *) data, '\0');
395 if (firstnull)
396 {
397 length = firstnull - data + 1;
398 }
399 }
400
401 xclip_provide_selection(&selection_request, XA_STRING, 8, data, length - 1);
402 }
403
404 void
405 ui_clip_request_data(uint32 format)
406 {
407 Window selectionowner;
408
409 DEBUG_CLIPBOARD(("Request from server for format %d\n", format));
410
411 if (rdesktop_is_selection_owner)
412 {
413 XChangeProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
414 XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);
415
416 XConvertSelection(g_display, primary_atom, rdesktop_clipboard_formats_atom,
417 rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
418 return;
419 }
420
421 selectionowner = XGetSelectionOwner(g_display, primary_atom);
422 if (selectionowner != None)
423 {
424 XConvertSelection(g_display, primary_atom, targets_atom,
425 rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
426 return;
427 }
428
429 /* No PRIMARY, try CLIPBOARD */
430 selectionowner = XGetSelectionOwner(g_display, clipboard_atom);
431 if (selectionowner != None)
432 {
433 XConvertSelection(g_display, clipboard_atom, targets_atom,
434 rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
435 return;
436 }
437
438 /* No data available */
439 cliprdr_send_data(NULL, 0);
440 }
441
442 void
443 ui_clip_sync(void)
444 {
445 cliprdr_send_simple_native_format_announce(CF_TEXT);
446 }
447
448
449 void
450 xclip_init(void)
451 {
452 if (!cliprdr_init())
453 return;
454
455 primary_atom = XInternAtom(g_display, "PRIMARY", False);
456 clipboard_atom = XInternAtom(g_display, "CLIPBOARD", False);
457 targets_atom = XInternAtom(g_display, "TARGETS", False);
458 timestamp_atom = XInternAtom(g_display, "TIMESTAMP", False);
459 rdesktop_clipboard_target_atom =
460 XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);
461 incr_atom = XInternAtom(g_display, "INCR", False);
462 targets[0] = targets_atom;
463 targets[1] = XInternAtom(g_display, "TEXT", False);
464 targets[2] = XInternAtom(g_display, "STRING", False);
465 targets[3] = XInternAtom(g_display, "text/unicode", False);
466 targets[4] = XInternAtom(g_display, "TIMESTAMP", False);
467 targets[5] = XA_STRING;
468
469 /* rdesktop sets _RDESKTOP_CLIPBOARD_FORMATS on the root window when acquiring the clipboard.
470 Other interested rdesktops can use this to notify their server of the available formats. */
471 rdesktop_clipboard_formats_atom =
472 XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False);
473 XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask);
474 }

  ViewVC Help
Powered by ViewVC 1.1.26