/[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 913 - (show annotations)
Mon Jun 27 12:18:48 2005 UTC (18 years, 11 months ago) by astrand
File MIME type: text/plain
File size: 12821 byte(s)
Support the clipboard INCR protocol: Applied patch from Burt Holzman.

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 void
108 xclip_handle_SelectionNotify(XSelectionEvent * event)
109 {
110 unsigned long nitems, bytes_left;
111 XWindowAttributes wa;
112 Atom type, best_target, text_target;
113 Atom *supported_targets;
114 int res, i, format;
115 uint8 *data;
116
117 if (event->property == None)
118 goto fail;
119
120 DEBUG_CLIPBOARD(("xclip_handle_SelectionNotify: selection=%s, target=%s, property=%s\n",
121 XGetAtomName(g_display, event->selection),
122 XGetAtomName(g_display, event->target),
123 XGetAtomName(g_display, event->property)));
124
125 if (event->property == None)
126 goto fail;
127
128 res = XGetWindowProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
129 0, XMaxRequestSize(g_display), True, AnyPropertyType,
130 &type, &format, &nitems, &bytes_left, &data);
131
132 if (res != Success)
133 {
134 DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
135 goto fail;
136 }
137
138 /* Negotiate target format */
139 if (event->target == targets_atom)
140 {
141 /* FIXME: We should choose format here based on what the server wanted */
142 best_target = XA_STRING;
143 if (type != None)
144 {
145 supported_targets = (Atom *) data;
146 text_target = XInternAtom(g_display, "TEXT", False);
147 for (i = 0; i < nitems; i++)
148 {
149 DEBUG_CLIPBOARD(("Target %d: %s\n", i,
150 XGetAtomName(g_display, supported_targets[i])));
151 if (supported_targets[i] == text_target)
152 {
153 DEBUG_CLIPBOARD(("Other party supports TEXT, choosing that as best_target\n"));
154 best_target = text_target;
155 break;
156 }
157 }
158 XFree(data);
159 }
160
161 XConvertSelection(g_display, primary_atom, best_target,
162 rdesktop_clipboard_target_atom, g_wnd, event->time);
163 return;
164 }
165
166 if (type == incr_atom)
167 {
168 DEBUG_CLIPBOARD(("Received INCR.\n"));
169
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(g_clip_buffer, translated_data, length);
195 xfree(translated_data);
196 g_clip_buflen = length;
197
198 XFree(data);
199 return;
200 }
201 }
202
203 /* 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);
220
221 if (!rdesktop_is_selection_owner)
222 cliprdr_send_text_format_announce();
223 return;
224
225 fail:
226 cliprdr_send_data(NULL, 0);
227 }
228
229 void
230 xclip_handle_SelectionRequest(XSelectionRequestEvent * event)
231 {
232 unsigned long nitems, bytes_left;
233 unsigned char *prop_return;
234 uint32 *wanted_format;
235 int format, res;
236 Atom type;
237
238 DEBUG_CLIPBOARD(("xclip_handle_SelectionRequest: selection=%s, target=%s, property=%s\n",
239 XGetAtomName(g_display, event->selection),
240 XGetAtomName(g_display, event->target),
241 XGetAtomName(g_display, event->property)));
242
243 if (event->target == targets_atom)
244 {
245 xclip_provide_selection(event, XA_ATOM, 32, (uint8 *) & targets, NUM_TARGETS);
246 return;
247 }
248 else if (event->target == timestamp_atom)
249 {
250 xclip_provide_selection(event, XA_INTEGER, 32, (uint8 *) & g_last_gesturetime, 1);
251 return;
252 }
253 else if (event->target == rdesktop_clipboard_formats_atom)
254 {
255 res = XGetWindowProperty(g_display, event->requestor,
256 rdesktop_clipboard_target_atom, 0, 1, True, XA_INTEGER,
257 &type, &format, &nitems, &bytes_left, &prop_return);
258 wanted_format = (uint32 *) prop_return;
259 format = (res == Success) ? *wanted_format : CF_TEXT;
260 /* FIXME: Need to free returned data? */
261 }
262 else
263 {
264 format = CF_TEXT;
265 }
266
267 cliprdr_send_data_request(format);
268 selection_request = *event;
269 /* wait for data */
270 }
271
272 void
273 xclip_handle_SelectionClear(void)
274 {
275 DEBUG_CLIPBOARD(("xclip_handle_SelectionClear\n"));
276 have_primary = 0;
277 XDeleteProperty(g_display, DefaultRootWindow(g_display), rdesktop_clipboard_formats_atom);
278 cliprdr_send_text_format_announce();
279 }
280
281 void
282 xclip_handle_PropertyNotify(XPropertyEvent * event)
283 {
284 unsigned long nitems, bytes_left;
285 int format, res;
286 XWindowAttributes wa;
287 uint8 *data;
288 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
325 DEBUG_CLIPBOARD(("Translating linebreaks before sending data\n"));
326 translated_data = lf2crlf(data, &length);
327
328 uint8 *tmp = xmalloc(length + g_clip_buflen);
329 strncpy(tmp, g_clip_buffer, g_clip_buflen);
330 xfree(g_clip_buffer);
331
332 strncpy(tmp + g_clip_buflen, translated_data, length);
333 xfree(translated_data);
334
335 g_clip_buffer = tmp;
336 g_clip_buflen += length;
337
338 XFree(data);
339 return;
340 }
341 }
342
343 if (event->atom != rdesktop_clipboard_formats_atom)
344 return;
345
346 if (have_primary) /* from us */
347 return;
348
349 if (event->state == PropertyNewValue)
350 {
351 res = XGetWindowProperty(g_display, DefaultRootWindow(g_display),
352 rdesktop_clipboard_formats_atom, 0,
353 XMaxRequestSize(g_display), False, XA_STRING, &type,
354 &format, &nitems, &bytes_left, &data);
355
356 if ((res == Success) && (nitems > 0))
357 {
358 cliprdr_send_native_format_announce(data, nitems);
359 rdesktop_is_selection_owner = 1;
360 return;
361 }
362 }
363
364 /* PropertyDelete, or XGetWindowProperty failed */
365 cliprdr_send_text_format_announce();
366 rdesktop_is_selection_owner = 0;
367 }
368
369
370 void
371 ui_clip_format_announce(uint8 * data, uint32 length)
372 {
373 XSetSelectionOwner(g_display, primary_atom, g_wnd, g_last_gesturetime);
374 if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)
375 {
376 warning("Failed to aquire ownership of PRIMARY clipboard\n");
377 return;
378 }
379
380 have_primary = 1;
381 XChangeProperty(g_display, DefaultRootWindow(g_display),
382 rdesktop_clipboard_formats_atom, XA_STRING, 8, PropModeReplace, data,
383 length);
384
385 XSetSelectionOwner(g_display, clipboard_atom, g_wnd, g_last_gesturetime);
386 if (XGetSelectionOwner(g_display, clipboard_atom) != g_wnd)
387 warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
388 }
389
390
391 void
392 ui_clip_handle_data(uint8 * data, uint32 length)
393 {
394 if (selection_request.target != rdesktop_clipboard_formats_atom)
395 {
396 uint8 *firstnull;
397
398 /* translate linebreaks */
399 crlf2lf(data, &length);
400
401 /* Only send data up to null byte, if any */
402 firstnull = (uint8 *) strchr((char *) data, '\0');
403 if (firstnull)
404 {
405 length = firstnull - data + 1;
406 }
407 }
408
409 xclip_provide_selection(&selection_request, XA_STRING, 8, data, length - 1);
410 }
411
412 void
413 ui_clip_request_data(uint32 format)
414 {
415 Window selectionowner;
416
417 DEBUG_CLIPBOARD(("Request from server for format %d\n", format));
418
419 if (rdesktop_is_selection_owner)
420 {
421 XChangeProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
422 XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);
423
424 XConvertSelection(g_display, primary_atom, rdesktop_clipboard_formats_atom,
425 rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
426 return;
427 }
428
429 selectionowner = XGetSelectionOwner(g_display, primary_atom);
430 if (selectionowner != None)
431 {
432 XConvertSelection(g_display, primary_atom, targets_atom,
433 rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
434 return;
435 }
436
437 /* No PRIMARY, try CLIPBOARD */
438 selectionowner = XGetSelectionOwner(g_display, clipboard_atom);
439 if (selectionowner != None)
440 {
441 XConvertSelection(g_display, clipboard_atom, targets_atom,
442 rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
443 return;
444 }
445
446 /* No data available */
447 cliprdr_send_data(NULL, 0);
448 }
449
450 void
451 ui_clip_sync(void)
452 {
453 cliprdr_send_text_format_announce();
454 }
455
456
457 void
458 xclip_init(void)
459 {
460 if (!cliprdr_init())
461 return;
462
463 primary_atom = XInternAtom(g_display, "PRIMARY", False);
464 clipboard_atom = XInternAtom(g_display, "CLIPBOARD", False);
465 targets_atom = XInternAtom(g_display, "TARGETS", False);
466 timestamp_atom = XInternAtom(g_display, "TIMESTAMP", False);
467 rdesktop_clipboard_target_atom =
468 XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);
469 incr_atom = XInternAtom(g_display, "INCR", False);
470 targets[0] = targets_atom;
471 targets[1] = XInternAtom(g_display, "TEXT", False);
472 targets[2] = XInternAtom(g_display, "STRING", False);
473 targets[3] = XInternAtom(g_display, "text/unicode", False);
474 targets[4] = XInternAtom(g_display, "TIMESTAMP", False);
475 targets[5] = XA_STRING;
476
477 /* rdesktop sets _RDESKTOP_CLIPBOARD_FORMATS on the root window when acquiring the clipboard.
478 Other interested rdesktops can use this to notify their server of the available formats. */
479 rdesktop_clipboard_formats_atom =
480 XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False);
481 XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask);
482 }

  ViewVC Help
Powered by ViewVC 1.1.26