/[rdesktop]/sourceforge.net/trunk/rdesktop/cliprdr.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/cliprdr.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 388 - (show annotations)
Fri Jun 6 09:26:11 2003 UTC (21 years ago) by forsberg
File MIME type: text/plain
File size: 13788 byte(s)
Use last button or keypress, either up or down, as event time when aquiring
PRIMARY (and CLIPBOARD).

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
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include <X11/Xlib.h>
22 #include <X11/Xatom.h>
23 #include "rdesktop.h"
24
25 extern BOOL encryption;
26 extern Display *display;
27 extern Window wnd;
28 extern Time last_gesturetime;
29
30 // static Time selection_timestamp;
31 static Atom clipboard_atom, primary_atom, targets_atom, timestamp_atom;
32 static Atom rdesktop_clipboard_target_atom;
33 static cliprdr_dataformat *server_formats = NULL;
34 static uint16 num_server_formats = 0;
35 static XSelectionEvent selection_event;
36
37 static void
38 cliprdr_print_server_formats(void)
39 {
40 #ifdef WITH_DEBUG_CLIPBOARD
41 cliprdr_dataformat *this;
42 uint16 i = 0;
43 this = server_formats;
44 DEBUG_CLIPBOARD(("There should be %d server formats.\n", num_server_formats));
45 while (NULL != this)
46 {
47 DEBUG_CLIPBOARD(("Format code %d\n", this->identifier));
48 i++;
49 this = this->next;
50 }
51 DEBUG_CLIPBOARD(("There were %d server formats.\n", i));
52 #endif
53 }
54 /*
55 static void
56 cliprdr_set_selection_timestamp(void)
57 {
58 XEvent xev;
59 DEBUG_CLIPBOARD(("Changing a property in order to get a timestamp\n"));
60 fflush(stdout);
61 XChangeProperty(display, wnd, rdesktop_clipboard_target_atom,
62 XA_ATOM, 32, PropModeAppend, 0, 0);
63 DEBUG_CLIPBOARD(("Waiting for PropertyChange on wnd\n"));
64 fflush(stdout);
65 XWindowEvent(display, wnd,
66 PropertyChangeMask, &xev);
67 DEBUG_CLIPBOARD(("Setting selection_timestamp\n"));
68 fflush(stdout);
69 selection_timestamp = xev.xproperty.time;
70 }
71 */
72
73 static void
74 cliprdr_send_format_announce(void)
75 {
76 DEBUG_CLIPBOARD(("Sending format announce\n"));
77
78 STREAM s;
79 int number_of_formats = 1;
80 s = sec_init(encryption ? SEC_ENCRYPT : 0, number_of_formats*36+12+4+4);
81 out_uint32_le(s, number_of_formats*36+12);
82 out_uint32_le(s, 0x13);
83 out_uint16_le(s, 2);
84 out_uint16_le(s, 0);
85 out_uint32_le(s, number_of_formats*36);
86
87 // out_uint32_le(s, 0xd); // FIXME: This is a rather bogus unicode text description..
88 // rdp_out_unistr(s, "", 16);
89 // out_uint8s(s, 32);
90
91
92 out_uint32_le(s, 1); // FIXME: This is a rather bogus text description..
93 out_uint8s(s, 32);
94
95 out_uint32_le(s, 0);
96
97 s_mark_end(s);
98 sec_send_to_channel(s, encryption ? SEC_ENCRYPT : 0, 1005); // FIXME: Don't hardcode channel!
99 }
100
101
102 static void
103 cliprdr_send_empty_datapacket(void)
104 {
105 STREAM out;
106 out = sec_init(encryption ? SEC_ENCRYPT : 0,
107 20);
108 out_uint32_le(out, 12);
109 out_uint32_le(out, 0x13);
110 out_uint16_le(out, 5);
111 out_uint16_le(out, 1);
112 out_uint32_le(out, 0);
113 /* Insert null string here? */
114 out_uint32_le(out, 0);
115 s_mark_end(out);
116
117 sec_send_to_channel(out, encryption ? SEC_ENCRYPT : 0, 1005); // FIXME: Don't hardcode channel!
118 }
119
120
121 void
122 cliprdr_handle_SelectionNotify(XSelectionEvent *event)
123 {
124
125 unsigned char *data;
126 unsigned long nitems, bytes_left;
127 int res;
128
129 int format;
130 Atom type_return;
131 Atom best_target;
132
133 STREAM out;
134
135 DEBUG_CLIPBOARD(("cliprdr_handle_SelectionNotify\n"));
136
137 if (None == event->property) {
138 cliprdr_send_empty_datapacket();
139 return; /* Selection failed */
140 }
141
142 DEBUG_CLIPBOARD(("selection: %s, target: %s, property: %s\n",
143 XGetAtomName(display, event->selection),
144 XGetAtomName(display, event->target),
145 XGetAtomName(display, event->property)));
146
147 if (targets_atom == event->target) {
148 /* Response to TARGETS request. Let's find the target
149 we want and request that */
150 res = XGetWindowProperty(display, wnd,
151 rdesktop_clipboard_target_atom,
152 0L, 4096L, False, AnyPropertyType,
153 &type_return,
154 &format, &nitems, &bytes_left, &data);
155
156 if (Success != res)
157 {
158 DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
159 cliprdr_send_empty_datapacket();
160 return;
161 }
162
163 if (None == type_return)
164 /* The owner might no support TARGETS. Just try
165 STRING */
166 best_target = XA_STRING;
167 else
168 {
169 /* FIXME: We should choose format here based
170 on what the server wanted */
171 best_target = XInternAtom(display, "TEXT", False);
172
173
174 }
175
176 XConvertSelection(display, primary_atom,
177 best_target,
178 rdesktop_clipboard_target_atom,
179 wnd, event->time);
180
181 }
182 else /* Other clipboard data */
183 {
184
185 res = XGetWindowProperty(display, wnd,
186 rdesktop_clipboard_target_atom,
187 0L, 4096L, False, AnyPropertyType,
188 &type_return,
189 &format, &nitems, &bytes_left, &data);
190
191 if (Success != res)
192 {
193 DEBUG_CLIPBOARD(("XGetWindowProperty failed!\n"));
194 cliprdr_send_empty_datapacket();
195 return;
196 }
197
198 /* We need to handle INCR as well */
199
200 out = sec_init(encryption ? SEC_ENCRYPT : 0,
201 20+nitems);
202 out_uint32_le(out, 12+nitems);
203 out_uint32_le(out, 0x13);
204 out_uint16_le(out, 5);
205 out_uint16_le(out, 1);
206 out_uint32_le(out, nitems);
207 out_uint8p(out, data, nitems);
208 /* Insert null string here? */
209 out_uint32_le(out, 0);
210 s_mark_end(out);
211
212 sec_send_to_channel(out, encryption ? SEC_ENCRYPT : 0, 1005); // FIXME: Don't hardcode channel!
213
214 cliprdr_send_format_announce();
215
216 }
217
218
219 }
220
221 void
222 cliprdr_handle_SelectionClear(void)
223 {
224 DEBUG_CLIPBOARD(("cliprdr_handle_SelectionClear\n"));
225 cliprdr_send_format_announce();
226 }
227
228
229 static void
230 cliprdr_request_clipboard_data(uint32 formatcode)
231 {
232 STREAM s;
233 s = sec_init(encryption ? SEC_ENCRYPT : 0, 24);
234 out_uint32_le(s, 16);
235 out_uint32_le(s, 0x13);
236 out_uint16_le(s, 4);
237 out_uint16_le(s, 0);
238 out_uint32_le(s, 4); // Remaining length
239 out_uint32_le(s, formatcode);
240 out_uint32_le(s, 0); // Unknown. Garbage pad?
241
242 s_mark_end(s);
243
244 sec_send_to_channel(s, encryption ? SEC_ENCRYPT : 0, 1005); // FIXME: Don't hardcode channel!
245 }
246
247
248 void
249 cliprdr_handle_SelectionRequest(XSelectionRequestEvent *xevent)
250 {
251
252 Atom *targets;
253 int res;
254
255 XSelectionEvent xev;
256 DEBUG_CLIPBOARD(("cliprdr_handle_SelectionRequest\n"));
257 DEBUG_CLIPBOARD(("Requestor window id 0x%x ",
258 (unsigned)xevent->requestor));
259 if (clipboard_atom == xevent->selection) {
260 DEBUG_CLIPBOARD(("wants CLIPBOARD\n"));
261 }
262 if (primary_atom == xevent->selection) {
263 DEBUG_CLIPBOARD(("wants PRIMARY\n"));
264 }
265 DEBUG_CLIPBOARD(("Target is %s (0x%x), property is %s (0x%x)\n",
266 XGetAtomName(display, xevent->target),
267 (unsigned)xevent->target,
268 XGetAtomName(display, xevent->property),
269 (unsigned)xevent->property));
270
271 xev.type = SelectionNotify;
272 xev.serial = 0;
273 xev.send_event = True;
274 xev.requestor = xevent->requestor;
275 xev.selection = xevent->selection;
276 xev.target = xevent->target;
277 xev.property = xevent->property;
278 xev.time = xevent->time;
279
280 if (targets_atom == xevent->target)
281 {
282 DEBUG_CLIPBOARD(("TARGETS requested, sending list..\n"));
283 targets = xmalloc(4*sizeof(Atom));
284 targets[0] = xevent->target;
285 targets[1] = XInternAtom(display, "TEXT", True);
286 targets[2] = XInternAtom(display, "UTF8_STRING", True);
287 targets[3] = XInternAtom(display, "TIMESTAMP", True);
288 res = XChangeProperty(display,
289 xevent->requestor,
290 xevent->property,
291 XA_ATOM,
292 32,
293 PropModeAppend,
294 (unsigned char *)targets,
295 3);
296
297 res = XSendEvent(display,
298 xevent->requestor,
299 False,
300 NoEventMask,
301 (XEvent *)&xev);
302 return;
303 } else if (timestamp_atom == xevent->target)
304 {
305 DEBUG_CLIPBOARD(("TIMESTAMP requested... sending 0x%x\n",
306 (unsigned)last_gesturetime));
307 res = XChangeProperty(display,
308 xevent->requestor,
309 xevent->property,
310 XA_INTEGER,
311 32,
312 PropModeAppend,
313 (unsigned char *)&last_gesturetime,
314 1);
315 res = XSendEvent(display,
316 xevent->requestor,
317 False,
318 NoEventMask,
319 (XEvent *)&xev);
320 } else /* Some other target */
321 {
322 cliprdr_request_clipboard_data(CF_TEXT);
323 memcpy(&selection_event, &xev, sizeof(xev));
324 /* Return and wait for data, handled by
325 cliprdr_handle_server_data */
326 }
327 }
328
329
330 static void
331 cliprdr_ack_format_list(void)
332 {
333 STREAM s;
334 s = sec_init(encryption ? SEC_ENCRYPT : 0, 20);
335 out_uint32_le(s, 12);
336 out_uint32_le(s, 0x13);
337 out_uint16_le(s, 3);
338 out_uint16_le(s, 1);
339 out_uint32_le(s, 0);
340 out_uint32_le(s, 0x0000c0da);
341
342 s_mark_end(s);
343
344 sec_send_to_channel(s, encryption ? SEC_ENCRYPT : 0, 1005); // FIXME: Don't hardcode channel!
345 }
346
347
348
349
350
351 static void
352 cliprdr_register_server_formats(STREAM s)
353 {
354 uint32 remaining_length, pad;
355 uint16 num_formats;
356 cliprdr_dataformat *this, *next;
357
358 DEBUG_CLIPBOARD(("cliprdr_register_server_formats\n"));
359 in_uint32_le(s, remaining_length);
360
361 num_formats = remaining_length / 36;
362 if (NULL != server_formats) {
363 this = server_formats;
364 next = this->next;
365 while (NULL != next) {
366 xfree(this);
367 this = NULL;
368 this = next;
369 next = this->next;
370 }
371 }
372 this = xmalloc(sizeof(cliprdr_dataformat));
373 this->next = NULL;
374 server_formats = this;
375 num_server_formats = num_formats;
376 while (1 < num_formats) {
377 in_uint32_le(s, this->identifier);
378 in_uint8a(s, this->textual_description, 32);
379 DEBUG_CLIPBOARD(("Stored format description with numeric id %d\n",
380 this->identifier));
381 this-> next = xmalloc(sizeof(cliprdr_dataformat));
382 this = this->next;
383 num_formats--;
384 }
385 in_uint32_le(s, this->identifier);
386 DEBUG_CLIPBOARD(("Stored format description with numeric id %d\n", this->identifier));
387 in_uint8a(s, this->textual_description, 32);
388 this -> next = NULL;
389 in_uint32_le(s, pad);
390 cliprdr_print_server_formats();
391 }
392
393 static void
394 cliprdr_select_X_clipboards(void)
395 {
396 XSetSelectionOwner(display, primary_atom, wnd, last_gesturetime);
397 if (wnd != XGetSelectionOwner(display, primary_atom))
398 {
399 warning("Failed to aquire ownership of PRIMARY clipboard\n");
400 }
401 XSetSelectionOwner(display, clipboard_atom, wnd, last_gesturetime);
402 if (wnd != XGetSelectionOwner(display, clipboard_atom))
403 {
404 warning("Failed to aquire ownership of CLIPBOARD clipboard\n");
405 }
406
407 }
408
409
410
411
412
413 static void
414 cliprdr_handle_first_handshake(STREAM s)
415 {
416 uint32 remaining_length, pad;
417 in_uint32_le(s, remaining_length);
418 in_uint32_le(s, pad);
419 DEBUG_CLIPBOARD(("Remaining length in first handshake frm server is %d, pad is %d\n",
420 remaining_length, pad));
421 cliprdr_send_format_announce();
422 }
423
424 void cliprdr_handle_server_data(uint32 length, STREAM s)
425 {
426 uint32 remaining_length;
427 char *data;
428 in_uint32_le(s, remaining_length);
429 data = s->p;
430 XChangeProperty(display,
431 selection_event.requestor,
432 selection_event.property,
433 XInternAtom(display, "STRING", False),
434 8,
435 PropModeAppend,
436 data,
437 remaining_length);
438
439 XSendEvent(display,
440 selection_event.requestor,
441 False,
442 NoEventMask,
443 (XEvent *)&selection_event);
444
445
446 }
447
448 void cliprdr_handle_server_data_request(STREAM s)
449 {
450 Window selectionowner;
451 uint32 remaining_length;
452 uint32 wanted_formatcode, pad;
453
454 in_uint32_le(s, remaining_length);
455 in_uint32_le(s, wanted_formatcode);
456 in_uint32_le(s, pad);
457
458 /* FIXME: Check that we support this formatcode */
459
460 DEBUG_CLIPBOARD(("Request from server for format %d\n",
461 wanted_formatcode));
462
463 selectionowner = XGetSelectionOwner(display, primary_atom);
464
465 if (None != selectionowner)
466 {
467
468 /* FIXME: Perhaps we should check if we are the owner? */
469
470 XConvertSelection(display, primary_atom,
471 targets_atom,
472 rdesktop_clipboard_target_atom,
473 wnd, CurrentTime);
474
475 /* The rest of the transfer is handled in
476 cliprdr_handle_SelectionNotify */
477
478 } else
479 {
480 DEBUG_CLIPBOARD(("There were no owner for PRIMARY, sending empty string\n")); // FIXME: Should we always send an empty string?
481
482 cliprdr_send_empty_datapacket();
483 }
484
485
486 }
487
488
489 void cliprdr_callback(STREAM s)
490 {
491 uint32 length, flags;
492 uint16 ptype0, ptype1;
493 DEBUG_CLIPBOARD(("cliprdr_callback called, clipboard data:\n"));
494 #ifdef WITH_DEBUG_CLIPBOARD
495 hexdump(s->p, s->end - s->p);
496 #endif
497 in_uint32_le(s, length);
498 in_uint32_le(s, flags);
499
500 DEBUG_CLIPBOARD(("length is %d, flags are %d\n", length, flags));
501
502 if (flags & 0x03 || flags & 0x01) /* Single-write op or first-packet-of-several op */
503 {
504 in_uint16_le(s, ptype0);
505 in_uint16_le(s, ptype1);
506 DEBUG_CLIPBOARD(("ptype0 is %d, ptype1 is %d\n", ptype0, ptype1));
507 if (1 == ptype0 && 0 == ptype1) {
508 cliprdr_handle_first_handshake(s);
509 return;
510 } else if (3 == ptype0 && 1 == ptype1)
511 {
512 // Acknowledgment on our format announce. Do we care? Not right now.
513 // There is a strange pad in this packet that we might need some time,
514 // but probably not.
515 DEBUG_CLIPBOARD(("Received format announce ACK\n"));
516 return;
517
518 } else if (2 == ptype0 && 0 == ptype1)
519 {
520 cliprdr_register_server_formats(s);
521 cliprdr_select_X_clipboards();
522 cliprdr_ack_format_list();
523 return;
524 } else if (5 == ptype0 && 1 == ptype1)
525 {
526 cliprdr_handle_server_data(length, s);
527 } else if (4 == ptype0 && 0 == ptype1)
528 {
529 cliprdr_handle_server_data_request(s);
530 }
531
532
533 }
534 }
535
536
537 void cliprdr_init(void)
538 {
539 primary_atom = XInternAtom(display, "PRIMARY", False);
540 clipboard_atom = XInternAtom(display, "CLIPBOARD", False);
541 targets_atom = XInternAtom(display, "TARGETS", False);
542 timestamp_atom = XInternAtom(display, "TIMESTAMP", False);
543 rdesktop_clipboard_target_atom = XInternAtom(display, "_RDESKTOP_CLIPBOARD_TARGET", False);
544 }

  ViewVC Help
Powered by ViewVC 1.1.26