/[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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1026 - (hide annotations)
Wed Nov 9 15:15:28 2005 UTC (18 years, 7 months ago) by forsberg
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 matthewc 432 /* -*- 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 jsorg71 450 extern Display *g_display;
29     extern Window g_wnd;
30     extern Time g_last_gesturetime;
31 matthewc 432
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 astrand 913 static int g_waiting_for_INCR = 0;
40     static uint8 *g_clip_buffer = 0;
41     static uint32 g_clip_buflen = 0;
42 astrand 551
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 matthewc 432 static void
46 astrand 551 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 astrand 435 xclip_provide_selection(XSelectionRequestEvent * req, Atom type, unsigned int format, uint8 * data,
89     uint32 length)
90 matthewc 432 {
91     XEvent xev;
92    
93 jsorg71 450 XChangeProperty(g_display, req->requestor, req->property,
94 matthewc 432 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 jsorg71 450 XSendEvent(g_display, req->requestor, False, NoEventMask, &xev);
105 matthewc 432 }
106    
107 astrand 942 #ifndef MAKE_PROTO
108 astrand 462 void
109 matthewc 432 xclip_handle_SelectionNotify(XSelectionEvent * event)
110     {
111     unsigned long nitems, bytes_left;
112 astrand 913 XWindowAttributes wa;
113 matthewc 432 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 jsorg71 450 XGetAtomName(g_display, event->selection),
123     XGetAtomName(g_display, event->target),
124     XGetAtomName(g_display, event->property)));
125 matthewc 432
126     if (event->property == None)
127     goto fail;
128    
129 jsorg71 450 res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
130 forsberg 1026 0, XMaxRequestSize(g_display), False, AnyPropertyType,
131 matthewc 432 &type, &format, &nitems, &bytes_left, &data);
132    
133     if (res != Success)
134     {
135     DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
136     goto fail;
137     }
138    
139 forsberg 1026
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 astrand 551 /* Negotiate target format */
158 matthewc 432 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 jsorg71 450 text_target = XInternAtom(g_display, "TEXT", False);
166 matthewc 432 for (i = 0; i < nitems; i++)
167     {
168 astrand 435 DEBUG_CLIPBOARD(("Target %d: %s\n", i,
169 jsorg71 450 XGetAtomName(g_display, supported_targets[i])));
170 matthewc 432 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 astrand 551 break;
175 matthewc 432 }
176     }
177     XFree(data);
178     }
179    
180 jsorg71 450 XConvertSelection(g_display, primary_atom, best_target,
181     rdesktop_clipboard_target_atom, g_wnd, event->time);
182 matthewc 432 return;
183     }
184    
185 astrand 551 /* 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 matthewc 432 XFree(data);
202    
203     if (!rdesktop_is_selection_owner)
204 forsberg 1025 cliprdr_send_simple_native_format_announce(CF_TEXT);
205 matthewc 432 return;
206    
207 forsberg 1026 fail:
208     XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
209     XFree(data);
210 matthewc 432 cliprdr_send_data(NULL, 0);
211     }
212    
213 astrand 462 void
214 astrand 435 xclip_handle_SelectionRequest(XSelectionRequestEvent * event)
215 matthewc 432 {
216     unsigned long nitems, bytes_left;
217 astrand 465 unsigned char *prop_return;
218 matthewc 432 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 jsorg71 450 XGetAtomName(g_display, event->selection),
224     XGetAtomName(g_display, event->target),
225     XGetAtomName(g_display, event->property)));
226 matthewc 432
227     if (event->target == targets_atom)
228     {
229 astrand 435 xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, NUM_TARGETS);
230 matthewc 432 return;
231     }
232     else if (event->target == timestamp_atom)
233     {
234 jsorg71 450 xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & g_last_gesturetime, 1);
235 matthewc 432 return;
236     }
237     else if (event->target == rdesktop_clipboard_formats_atom)
238     {
239 jsorg71 450 res = XGetWindowProperty(g_display, event->requestor,
240 astrand 435 rdesktop_clipboard_target_atom, 0, 1, True, XA_INTEGER,
241 astrand 468 &type, &format, &nitems, &bytes_left, &prop_return);
242 astrand 465 wanted_format = (uint32 *) prop_return;
243 matthewc 432 format = (res == Success) ? *wanted_format : CF_TEXT;
244 astrand 465 /* FIXME: Need to free returned data? */
245 matthewc 432 }
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 astrand 462 void
257 matthewc 432 xclip_handle_SelectionClear(void)
258     {
259     DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
260     have_primary = 0;
261 jsorg71 450 XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);
262 forsberg 1025 cliprdr_send_simple_native_format_announce(CF_TEXT);
263 matthewc 432 }
264    
265 astrand 462 void
266 astrand 435 xclip_handle_PropertyNotify(XPropertyEvent * event)
267 matthewc 432 {
268 forsberg 1026 unsigned long nitems;
269     unsigned long offset = 0;
270     unsigned long bytes_left = 1;
271 matthewc 432 int format, res;
272 astrand 913 XWindowAttributes wa;
273 matthewc 432 uint8 *data;
274     Atom type;
275    
276 astrand 913 if (event->state == PropertyNewValue && g_waiting_for_INCR)
277     {
278     DEBUG_CLIPBOARD(("x_clip_handle_PropertyNotify: g_waiting_for_INCR != 0\n"));
279    
280 forsberg 1026 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 astrand 913
289 forsberg 1026 if (nitems == 0)
290 astrand 913 {
291 forsberg 1026 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 astrand 913
296 forsberg 1026 if (g_clip_buflen > 0)
297     {
298     cliprdr_send_data(g_clip_buffer, g_clip_buflen + 1);
299 astrand 913
300 forsberg 1026 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 astrand 913 }
308 forsberg 1026 else
309     {
310     uint8 *translated_data;
311     uint32 length = nitems;
312     uint8 *tmp;
313 astrand 913
314 forsberg 1026 offset += (length/4);
315     DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));
316     translated_data = lf2crlf(data, &length);
317 astrand 913
318 forsberg 1026 tmp = xmalloc(length + g_clip_buflen);
319     strncpy((char *) tmp, (char *) g_clip_buffer, g_clip_buflen);
320     xfree(g_clip_buffer);
321 astrand 913
322 forsberg 1026 strncpy((char *) (tmp + g_clip_buflen), (char *) translated_data, length);
323     xfree(translated_data);
324 astrand 913
325 forsberg 1026 g_clip_buffer = tmp;
326     g_clip_buflen += length;
327 astrand 913
328 forsberg 1026 XFree(data);
329     }
330 astrand 913 }
331 forsberg 1026 XDeleteProperty(g_display, g_wnd, rdesktop_clipboard_target_atom);
332 astrand 913 }
333    
334 matthewc 432 if (event->atom != rdesktop_clipboard_formats_atom)
335     return;
336    
337 astrand 435 if (have_primary) /* from us */
338 matthewc 432 return;
339    
340     if (event->state == PropertyNewValue)
341     {
342 jsorg71 450 res = XGetWindowProperty(g_display, DefaultRootWindow(g_display),
343 astrand 435 rdesktop_clipboard_formats_atom, 0,
344 astrand 456 XMaxRequestSize(g_display), False, XA_STRING, &type,
345     &format, &nitems, &bytes_left, &data);
346 matthewc 432
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 forsberg 1025 cliprdr_send_simple_native_format_announce(CF_TEXT);
357 matthewc 432 rdesktop_is_selection_owner = 0;
358     }
359 astrand 942 #endif
360 matthewc 432
361    
362     void
363 astrand 542 ui_clip_format_announce(uint8 * data, uint32 length)
364 matthewc 432 {
365 jsorg71 450 XSetSelectionOwner(g_display, primary_atom, g_wnd, g_last_gesturetime);
366     if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)
367 matthewc 432 {
368     warning("Failed to aquire ownership of PRIMARY clipboard\n");
369     return;
370     }
371    
372     have_primary = 1;
373 jsorg71 450 XChangeProperty(g_display, DefaultRootWindow(g_display),
374 astrand 435 rdesktop_clipboard_formats_atom, XA_STRING, 8, PropModeReplace, data,
375     length);
376 matthewc 432
377 jsorg71 450 XSetSelectionOwner(g_display, clipboard_atom, g_wnd, g_last_gesturetime);
378     if (XGetSelectionOwner(g_display, clipboard_atom) != g_wnd)
379 matthewc 432 warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
380     }
381    
382    
383     void
384 astrand 542 ui_clip_handle_data(uint8 * data, uint32 length)
385 matthewc 432 {
386 astrand 551 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 astrand 608 firstnull = (uint8 *) strchr((char *) data, '\0');
395 astrand 551 if (firstnull)
396     {
397     length = firstnull - data + 1;
398     }
399     }
400    
401 astrand 435 xclip_provide_selection(&selection_request, XA_STRING, 8, data, length - 1);
402 matthewc 432 }
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 jsorg71 450 XChangeProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
414 matthewc 432 XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);
415    
416 jsorg71 450 XConvertSelection(g_display, primary_atom, rdesktop_clipboard_formats_atom,
417     rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
418 matthewc 432 return;
419     }
420    
421 jsorg71 450 selectionowner = XGetSelectionOwner(g_display, primary_atom);
422 matthewc 432 if (selectionowner != None)
423     {
424 jsorg71 450 XConvertSelection(g_display, primary_atom, targets_atom,
425     rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
426 matthewc 432 return;
427     }
428    
429     /* No PRIMARY, try CLIPBOARD */
430 jsorg71 450 selectionowner = XGetSelectionOwner(g_display, clipboard_atom);
431 matthewc 432 if (selectionowner != None)
432     {
433 jsorg71 450 XConvertSelection(g_display, clipboard_atom, targets_atom,
434     rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
435 matthewc 432 return;
436     }
437    
438     /* No data available */
439     cliprdr_send_data(NULL, 0);
440     }
441    
442     void
443     ui_clip_sync(void)
444     {
445 forsberg 1025 cliprdr_send_simple_native_format_announce(CF_TEXT);
446 matthewc 432 }
447    
448    
449     void
450     xclip_init(void)
451     {
452     if (!cliprdr_init())
453     return;
454    
455 jsorg71 450 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 astrand 456 rdesktop_clipboard_target_atom =
460     XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);
461 jsorg71 450 incr_atom = XInternAtom(g_display, "INCR", False);
462 matthewc 432 targets[0] = targets_atom;
463 jsorg71 450 targets[1] = XInternAtom(g_display, "TEXT", False);
464 forsberg 707 targets[2] = XInternAtom(g_display, "STRING", False);
465 jsorg71 450 targets[3] = XInternAtom(g_display, "text/unicode", False);
466     targets[4] = XInternAtom(g_display, "TIMESTAMP", False);
467 matthewc 432 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 astrand 435 rdesktop_clipboard_formats_atom =
472 jsorg71 450 XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False);
473     XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask);
474 matthewc 432 }

  ViewVC Help
Powered by ViewVC 1.1.26