/[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 941 - (show annotations)
Tue Aug 2 09:19:24 2005 UTC (18 years, 10 months ago) by astrand
File MIME type: text/plain
File size: 12885 byte(s)
Indent fixes.

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((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 /* 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 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)
345 return;
346
347 if (have_primary) /* from us */
348 return;
349
350 if (event->state == PropertyNewValue)
351 {
352 res = XGetWindowProperty(g_display, DefaultRootWindow(g_display),
353 rdesktop_clipboard_formats_atom, 0,
354 XMaxRequestSize(g_display), False, XA_STRING, &type,
355 &format, &nitems, &bytes_left, &data);
356
357 if ((res == Success) && (nitems > 0))
358 {
359 cliprdr_send_native_format_announce(data, nitems);
360 rdesktop_is_selection_owner = 1;
361 return;
362 }
363 }
364
365 /* PropertyDelete, or XGetWindowProperty failed */
366 cliprdr_send_text_format_announce();
367 rdesktop_is_selection_owner = 0;
368 }
369
370
371 void
372 ui_clip_format_announce(uint8 * data, uint32 length)
373 {
374 XSetSelectionOwner(g_display, primary_atom, g_wnd, g_last_gesturetime);
375 if (XGetSelectionOwner(g_display, primary_atom) != g_wnd)
376 {
377 warning("Failed to aquire ownership of PRIMARY clipboard\n");
378 return;
379 }
380
381 have_primary = 1;
382 XChangeProperty(g_display, DefaultRootWindow(g_display),
383 rdesktop_clipboard_formats_atom, XA_STRING, 8, PropModeReplace, data,
384 length);
385
386 XSetSelectionOwner(g_display, clipboard_atom, g_wnd, g_last_gesturetime);
387 if (XGetSelectionOwner(g_display, clipboard_atom) != g_wnd)
388 warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
389 }
390
391
392 void
393 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);
411 }
412
413 void
414 ui_clip_request_data(uint32 format)
415 {
416 Window selectionowner;
417
418 DEBUG_CLIPBOARD(("Request from server for format %d\n", format));
419
420 if (rdesktop_is_selection_owner)
421 {
422 XChangeProperty(g_display, g_wnd, rdesktop_clipboard_target_atom,
423 XA_INTEGER, 32, PropModeReplace, (unsigned char *) &format, 1);
424
425 XConvertSelection(g_display, primary_atom, rdesktop_clipboard_formats_atom,
426 rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
427 return;
428 }
429
430 selectionowner = XGetSelectionOwner(g_display, primary_atom);
431 if (selectionowner != None)
432 {
433 XConvertSelection(g_display, primary_atom, targets_atom,
434 rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
435 return;
436 }
437
438 /* No PRIMARY, try CLIPBOARD */
439 selectionowner = XGetSelectionOwner(g_display, clipboard_atom);
440 if (selectionowner != None)
441 {
442 XConvertSelection(g_display, clipboard_atom, targets_atom,
443 rdesktop_clipboard_target_atom, g_wnd, CurrentTime);
444 return;
445 }
446
447 /* No data available */
448 cliprdr_send_data(NULL, 0);
449 }
450
451 void
452 ui_clip_sync(void)
453 {
454 cliprdr_send_text_format_announce();
455 }
456
457
458 void
459 xclip_init(void)
460 {
461 if (!cliprdr_init())
462 return;
463
464 primary_atom = XInternAtom(g_display, "PRIMARY", False);
465 clipboard_atom = XInternAtom(g_display, "CLIPBOARD", False);
466 targets_atom = XInternAtom(g_display, "TARGETS", False);
467 timestamp_atom = XInternAtom(g_display, "TIMESTAMP", False);
468 rdesktop_clipboard_target_atom =
469 XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_TARGET", False);
470 incr_atom = XInternAtom(g_display, "INCR", False);
471 targets[0] = targets_atom;
472 targets[1] = XInternAtom(g_display, "TEXT", False);
473 targets[2] = XInternAtom(g_display, "STRING", False);
474 targets[3] = XInternAtom(g_display, "text/unicode", False);
475 targets[4] = XInternAtom(g_display, "TIMESTAMP", False);
476 targets[5] = XA_STRING;
477
478 /* rdesktop sets _RDESKTOP_CLIPBOARD_FORMATS on the root window when acquiring the clipboard.
479 Other interested rdesktops can use this to notify their server of the available formats. */
480 rdesktop_clipboard_formats_atom =
481 XInternAtom(g_display, "_RDESKTOP_CLIPBOARD_FORMATS", False);
482 XSelectInput(g_display, DefaultRootWindow(g_display), PropertyChangeMask);
483 }

  ViewVC Help
Powered by ViewVC 1.1.26