ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/clip_unix.cpp
Revision: 1.2
Committed: 2003-12-31T11:37:26Z (20 years, 6 months ago) by gbeauche
Branch: MAIN
Changes since 1.1: +582 -11 lines
Log Message:
Handle copy-paste between X11 and MacOS. X11 events handling code has to
be improved in copy mode (when we own the selection to service other clients).
Also note that older klipper has a tendency to request clipboard data
several times per second.

File Contents

# Content
1 /*
2 * clip_unix.cpp - Clipboard handling, Unix implementation
3 *
4 * SheepShaver (C) 1997-2003 Christian Bauer and Marc Hellwig
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "sysdeps.h"
22
23 #include <X11/Xlib.h>
24 #include <X11/Xatom.h>
25 #include <pthread.h>
26 #include <set>
27 #include <vector>
28
29 #include "macos_util.h"
30 #include "clip.h"
31 #include "prefs.h"
32 #include "cpu_emulation.h"
33 #include "main.h"
34 #include "emul_op.h"
35
36 #define DEBUG 0
37 #include "debug.h"
38
39 #ifndef NO_STD_NAMESPACE
40 using std::set;
41 using std::vector;
42 #endif
43
44
45 // Do we replace PutScrap()?
46 #define REPLACE_PUTSCRAP 1
47
48 // Do we replace GetScrap()?
49 #define REPLACE_GETSCRAP 1
50
51 // Do we want PutScrap() to ignore requests from klipper?
52 #define PUTSCRAP_IGNORES_KLIPPER 0
53
54 // Do we want GetScrap() to check for TIMESTAMP and optimize out clipboard syncs?
55 #define GETSCRAP_REQUESTS_TIMESTAMP 0
56
57 // Do we want GetScrap() to first check for TARGETS available from the clipboard?
58 #define GETSCRAP_REQUESTS_TARGETS 0
59
60
61 // From main_linux.cpp
62 extern Display *x_display;
63
64
65 // Conversion tables
66 static const uint8 mac2iso[0x80] = {
67 0xc4, 0xc5, 0xc7, 0xc9, 0xd1, 0xd6, 0xdc, 0xe1,
68 0xe0, 0xe2, 0xe4, 0xe3, 0xe5, 0xe7, 0xe9, 0xe8,
69 0xea, 0xeb, 0xed, 0xec, 0xee, 0xef, 0xf1, 0xf3,
70 0xf2, 0xf4, 0xf6, 0xf5, 0xfa, 0xf9, 0xfb, 0xfc,
71 0x2b, 0xb0, 0xa2, 0xa3, 0xa7, 0xb7, 0xb6, 0xdf,
72 0xae, 0xa9, 0x20, 0xb4, 0xa8, 0x23, 0xc6, 0xd8,
73 0x20, 0xb1, 0x3c, 0x3e, 0xa5, 0xb5, 0xf0, 0x53,
74 0x50, 0x70, 0x2f, 0xaa, 0xba, 0x4f, 0xe6, 0xf8,
75 0xbf, 0xa1, 0xac, 0x2f, 0x66, 0x7e, 0x44, 0xab,
76 0xbb, 0x2e, 0x20, 0xc0, 0xc3, 0xd5, 0x4f, 0x6f,
77 0x2d, 0x2d, 0x22, 0x22, 0x60, 0x27, 0xf7, 0x20,
78 0xff, 0x59, 0x2f, 0xa4, 0x3c, 0x3e, 0x66, 0x66,
79 0x23, 0xb7, 0x2c, 0x22, 0x25, 0xc2, 0xca, 0xc1,
80 0xcb, 0xc8, 0xcd, 0xce, 0xcf, 0xcc, 0xd3, 0xd4,
81 0x20, 0xd2, 0xda, 0xdb, 0xd9, 0x69, 0x5e, 0x7e,
82 0xaf, 0x20, 0xb7, 0xb0, 0xb8, 0x22, 0xb8, 0x20
83 };
84
85 static const uint8 iso2mac[0x80] = {
86 0xad, 0xb0, 0xe2, 0xc4, 0xe3, 0xc9, 0xa0, 0xe0,
87 0xf6, 0xe4, 0xde, 0xdc, 0xce, 0xb2, 0xb3, 0xb6,
88 0xb7, 0xd4, 0xd5, 0xd2, 0xd3, 0xa5, 0xd0, 0xd1,
89 0xf7, 0xaa, 0xdf, 0xdd, 0xcf, 0xba, 0xfd, 0xd9,
90 0xca, 0xc1, 0xa2, 0xa3, 0xdb, 0xb4, 0xbd, 0xa4,
91 0xac, 0xa9, 0xbb, 0xc7, 0xc2, 0xf0, 0xa8, 0xf8,
92 0xa1, 0xb1, 0xc3, 0xc5, 0xab, 0xb5, 0xa6, 0xe1,
93 0xfc, 0xc6, 0xbc, 0xc8, 0xf9, 0xda, 0xd7, 0xc0,
94 0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82,
95 0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec,
96 0xf5, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0xfb,
97 0xaf, 0xf4, 0xf2, 0xf3, 0x86, 0xfa, 0xb8, 0xa7,
98 0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xbe, 0x8d,
99 0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95,
100 0xfe, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6,
101 0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0xff, 0xb9, 0xd8
102 };
103
104 // Flag: Don't convert clipboard text
105 static bool no_clip_conversion;
106
107 // Flag for PutScrap(): the data was put by GetScrap(), don't bounce it back to the Be side
108 static bool we_put_this_data = false;
109
110 // X11 variables
111 static int screen; // Screen number
112 static Window rootwin, clip_win; // Root window and the clipboard window
113 static Atom xa_clipboard;
114 static Atom xa_targets;
115 static Atom xa_multiple;
116 static Atom xa_timestamp;
117 static Atom xa_atom_pair;
118
119 // Define a byte array (rewrite if it's a bottleneck)
120 struct ByteArray : public vector<uint8> {
121 void resize(int size) { reserve(size); vector<uint8>::resize(size); }
122 uint8 *data() { return &at(0); }
123 };
124
125 // Clipboard locks
126 #ifdef HAVE_PTHREADS
127 static pthread_mutex_t clip_lock = PTHREAD_MUTEX_INITIALIZER;
128 #define LOCK_CLIPBOARD pthread_mutex_lock(&clip_lock);
129 #define UNLOCK_CLIPBOARD pthread_mutex_unlock(&clip_lock);
130 #elif defined(HAVE_SPINLOCKS)
131 static spinlock_t clip_lock = SPIN_LOCK_UNLOCKED;
132 #define LOCK_CLIPBOARD spin_lock(&clip_lock)
133 #define UNLOCK_CLIPBOARD spin_unlock(&clip_lock)
134 #else
135 #define LOCK_CLIPBOARD
136 #define UNLOCK_CLIPBOARD
137 #endif
138
139 // Clipboard data
140 struct ClipboardData {
141 Time time;
142 uint32 type;
143 ByteArray data;
144 };
145 static ClipboardData clip_data;
146
147
148 /*
149 * Read an X11 property (hack from QT 3.1.2)
150 */
151
152 static inline int max_selection_incr(Display *dpy)
153 {
154 int max_request_size = 4 * XMaxRequestSize(dpy);
155 if (max_request_size > 4 * 65536)
156 max_request_size = 4 * 65536;
157 else if ((max_request_size -= 100) < 0)
158 max_request_size = 100;
159 return max_request_size;
160 }
161
162 static bool read_property(Display *dpy, Window win,
163 Atom property, bool deleteProperty,
164 ByteArray & buffer, int *size, Atom *type,
165 int *format, bool nullterm)
166 {
167 int maxsize = max_selection_incr(dpy);
168 unsigned long bytes_left;
169 unsigned long length;
170 unsigned char *data;
171 Atom dummy_type;
172 int dummy_format;
173
174 if (!type)
175 type = &dummy_type;
176 if (!format)
177 format = &dummy_format;
178
179 // Don't read anything but get the size of the property data
180 if (XGetWindowProperty(dpy, win, property, 0, 0, False,
181 AnyPropertyType, type, format, &length, &bytes_left, &data) != Success) {
182 buffer.clear();
183 return false;
184 }
185 XFree(data);
186
187 int offset = 0, buffer_offset = 0, format_inc = 1, proplen = bytes_left;
188
189 switch (*format) {
190 case 16:
191 format_inc = sizeof(short) / 2;
192 proplen *= format_inc;
193 break;
194 case 32:
195 format_inc = sizeof(long) / 4;
196 proplen *= format_inc;
197 break;
198 }
199
200 buffer.resize(proplen + (nullterm ? 1 : 0));
201 while (bytes_left) {
202 if (XGetWindowProperty(dpy, win, property, offset, maxsize / 4,
203 False, AnyPropertyType, type, format,
204 &length, &bytes_left, &data) != Success)
205 break;
206
207 offset += length / (32 / *format);
208 length *= format_inc * (*format / 8);
209
210 memcpy(buffer.data() + buffer_offset, data, length);
211 buffer_offset += length;
212 XFree(data);
213 }
214
215 if (nullterm)
216 buffer[buffer_offset] = '\0';
217
218 if (size)
219 *size = buffer_offset;
220
221 if (deleteProperty)
222 XDeleteProperty(dpy, win, property);
223
224 XFlush(dpy);
225 return true;
226 }
227
228
229 /*
230 * Timed wait for an SelectionNotify event
231 */
232
233 static const uint64 SELECTION_MAX_WAIT = 500000; // 500 ms
234 static volatile bool xselection_event_avail;
235 static XSelectionEvent xselection_event;
236
237 static bool wait_for_selection_notify_event(Display *dpy, Window win, int timeout)
238 {
239 uint64 start = GetTicks_usec();
240
241 LOCK_CLIPBOARD;
242 xselection_event_avail = false;
243 UNLOCK_CLIPBOARD;
244
245 // First wait a very short period of time < the VideoRefresh() resolution
246 struct timespec req = {0, 5000000};
247 nanosleep(&req, NULL);
248
249 // FIXME: Since handle_events() is in a separate thread, wait for
250 // a SelectionNotify event to occur and reported here. The
251 // resolution of this wait action should match that of VideoRefresh()
252 if (xselection_event_avail)
253 return true;
254
255 do {
256 // Wait
257 struct timespec req = {0, 16666667};
258 nanosleep(&req, NULL);
259
260 // Return immediately if the event is now available
261 if (xselection_event_avail)
262 return true;
263
264 } while ((GetTicks_usec() - start) < timeout);
265
266 return false;
267 }
268
269
270 /*
271 * Check for a "klipper" window which, in older versions (3.1), was
272 * polling and retrieving clipboard data several times per second.
273 */
274
275 static bool is_klipper_window_probe(Window w);
276 static bool is_klipper_window_check(Window w);
277 static bool (*is_klipper_window)(Window w) = is_klipper_window_probe;
278 static Window klipper_win = None;
279
280 static bool is_klipper_window_probe(Window w)
281 {
282 // We expect "klipper" to be within the first clients to request
283 // data from our clipboard
284 static int clients_countdown = 2;
285
286 bool found = false;
287 char *window_name;
288 if (XFetchName(x_display, w, &window_name) && !strcmp(window_name, "klipper")) {
289 D(bug(" found and ignore clipboard requests from klipper window id: 0x%08x\n", w));
290 klipper_win = w;
291 found = true;
292 }
293
294 if (found || --clients_countdown <= 0)
295 is_klipper_window = is_klipper_window_check;
296 }
297
298 static bool is_klipper_window_check(Window w)
299 {
300 return w == klipper_win;
301 }
302
303
304 /*
305 * Initialization
306 */
307
308 void ClipInit(void)
309 {
310 no_clip_conversion = PrefsFindBool("noclipconversion");
311
312 // Find screen and root window
313 screen = XDefaultScreen(x_display);
314 rootwin = XRootWindow(x_display, screen);
315
316 // Create fake window to receive selection events
317 clip_win = XCreateSimpleWindow(x_display, rootwin, 0, 0, 1, 1, 0, 0, 0);
318
319 // Initialize X11 atoms
320 xa_clipboard = XInternAtom(x_display, "CLIPBOARD", False);
321 xa_targets = XInternAtom(x_display, "TARGETS", False);
322 xa_multiple = XInternAtom(x_display, "MULTIPLE", False);
323 xa_timestamp = XInternAtom(x_display, "TIMESTAMP", False);
324 xa_atom_pair = XInternAtom(x_display, "ATOM_PAIR", False);
325 }
326
327
328 /*
329 * Deinitialization
330 */
331
332 void ClipExit(void)
333 {
334 // Close window
335 XDestroyWindow(x_display, clip_win);
336 }
337
338
339 /*
340 * Mac application wrote to clipboard
341 */
342
343 void PutScrap(uint32 type, void *scrap, int32 length)
344 {
345 D(bug("PutScrap type %08lx, data %p, length %ld\n", type, scrap, length));
346 if (!REPLACE_PUTSCRAP)
347 return;
348 if (we_put_this_data) {
349 we_put_this_data = false;
350 return;
351 }
352 if (length <= 0)
353 return;
354
355 bool did_putscrap = false;
356 switch (type) {
357 case FOURCC('T','E','X','T'):
358 D(bug(" clipping TEXT\n"));
359 clip_data.type = type;
360 clip_data.data.clear();
361 clip_data.data.reserve(length);
362
363 // Convert text from Mac charset to ISO-Latin1
364 uint8 *p = (uint8 *)scrap;
365 for (int i=0; i<length; i++) {
366 uint8 c = *p++;
367 if (c < 0x80) {
368 if (c == 13) // CR -> LF
369 c = 10;
370 } else if (!no_clip_conversion)
371 c = mac2iso[c & 0x7f];
372 clip_data.data.push_back(c);
373 }
374 did_putscrap = true;
375 break;
376 }
377
378 // Acquire selection ownership
379 if (did_putscrap) {
380 clip_data.time = CurrentTime;
381 while (XGetSelectionOwner(x_display, xa_clipboard) != clip_win)
382 XSetSelectionOwner(x_display, xa_clipboard, clip_win, clip_data.time);
383 }
384 }
385
386
387 /*
388 * Mac application reads clipboard
389 */
390
391 void GetScrap(void **handle, uint32 type, int32 offset)
392 {
393 D(bug("GetScrap handle %p, type %08x, offset %d\n", handle, type, offset));
394 if (!REPLACE_GETSCRAP)
395 return;
396
397 // Check TIMESTAMP
398 #if GETSCRAP_REQUESTS_TIMESTAMP
399 static Time last_timestamp = 0;
400 XConvertSelection(x_display, xa_clipboard, xa_timestamp, xa_clipboard, clip_win, CurrentTime);
401 if (wait_for_selection_notify_event(x_display, clip_win, SELECTION_MAX_WAIT) &&
402 xselection_event.property != None) {
403 ByteArray data;
404 if (read_property(x_display,
405 xselection_event.requestor, xselection_event.property,
406 true, data, 0, 0, 0, false)) {
407 Time timestamp = ((long *)data.data())[0];
408 if (timestamp == last_timestamp)
409 return;
410 }
411 }
412 #endif
413
414 // Get TARGETS available
415 #if GETSCRAP_REQUESTS_TARGETS
416 XConvertSelection(x_display, xa_clipboard, xa_targets, xa_clipboard, clip_win, CurrentTime);
417 if (!wait_for_selection_notify_event(x_display, clip_win, SELECTION_MAX_WAIT) ||
418 xselection_event.property == None)
419 return;
420
421 ByteArray data;
422 if (!read_property(x_display,
423 xselection_event.requestor, xselection_event.property,
424 true, data, 0, 0, 0, false))
425 return;
426 #endif
427
428 // Get appropriate format for requested data
429 Atom format = None;
430 #if GETSCRAP_REQUESTS_TARGETS
431 int n_atoms = data.size() / sizeof(long);
432 long *atoms = (long *)data.data();
433 for (int i = 0; i < n_atoms; i++) {
434 Atom target = atoms[i];
435 switch (type) {
436 case FOURCC('T','E','X','T'):
437 D(bug(" clipping TEXT\n"));
438 if (target == XA_STRING)
439 format = target;
440 break;
441 case FOURCC('P','I','C','T'):
442 break;
443 }
444 }
445 #else
446 switch (type) {
447 case FOURCC('T','E','X','T'):
448 D(bug(" clipping TEXT\n"));
449 format = XA_STRING;
450 break;
451 case FOURCC('P','I','C','T'):
452 break;
453 }
454 #endif
455 if (format == None)
456 return;
457
458 // Get the native clipboard data
459 XConvertSelection(x_display, xa_clipboard, format, xa_clipboard, clip_win, CurrentTime);
460 if (!wait_for_selection_notify_event(x_display, clip_win, SELECTION_MAX_WAIT) ||
461 xselection_event.property == None ||
462 !read_property(x_display,
463 xselection_event.requestor, xselection_event.property,
464 false, clip_data.data, 0, 0, 0, format == XA_STRING))
465 return;
466
467 clip_data.type = type;
468 clip_data.time = xselection_event.time;
469
470 // Allocate space for new scrap in MacOS side
471 M68kRegisters r;
472 r.d[0] = clip_data.data.size();
473 Execute68kTrap(0xa71e, &r); // NewPtrSysClear()
474 uint32 scrap_area = r.a[0];
475
476 if (scrap_area) {
477 switch (type) {
478 case FOURCC('T','E','X','T'):
479 // Convert text from ISO-Latin1 to Mac charset
480 uint8 *p = Mac2HostAddr(scrap_area);
481 for (int i = 0; i < clip_data.data.size(); i++) {
482 uint8 c = clip_data.data[i];
483 if (c < 0x80) {
484 if (c == 10) // LF -> CR
485 c = 13;
486 } else if (!no_clip_conversion)
487 c = iso2mac[c & 0x7f];
488 *p++ = c;
489 }
490 break;
491 }
492
493 // Add new data to clipboard
494 static uint8 proc[] = {
495 0x59, 0x8f, // subq.l #4,sp
496 0xa9, 0xfc, // ZeroScrap()
497 0x2f, 0x3c, 0, 0, 0, 0, // move.l #length,-(sp)
498 0x2f, 0x3c, 0, 0, 0, 0, // move.l #type,-(sp)
499 0x2f, 0x3c, 0, 0, 0, 0, // move.l #outbuf,-(sp)
500 0xa9, 0xfe, // PutScrap()
501 0x58, 0x8f, // addq.l #4,sp
502 M68K_RTS >> 8, M68K_RTS
503 };
504 uint32 proc_area = (uint32)proc; // FIXME: make sure 32-bit relocs are used
505 WriteMacInt32(proc_area + 6, clip_data.data.size());
506 WriteMacInt32(proc_area + 12, type);
507 WriteMacInt32(proc_area + 18, scrap_area);
508 we_put_this_data = true;
509 Execute68k(proc_area, &r);
510
511 // We are done with scratch memory
512 r.a[0] = scrap_area;
513 Execute68kTrap(0xa01f, &r); // DisposePtr
514 }
515 }
516
517
518 /*
519 * Handle X11 selection events
520 */
521
522 void ClipboardSelectionClear(XSelectionClearEvent *xev)
523 {
524 if (xev->selection != xa_clipboard)
525 return;
526
527 D(bug("Selection cleared, lost clipboard ownership\n"));
528 clip_data.type = 0;
529 clip_data.data.clear();
530 }
531
532 // Top level selection handler
533 static bool handle_selection(XSelectionRequestEvent *req, bool is_multiple);
534
535 static bool handle_selection_TIMESTAMP(XSelectionRequestEvent *req)
536 {
537 // 32-bit integer values are always passed as a long whatever is its size
538 long timestamp = clip_data.time;
539
540 // Change requestor property
541 XChangeProperty(x_display, req->requestor, req->property,
542 XA_INTEGER, 32,
543 PropModeReplace, (uint8 *)&timestamp, 1);
544
545 return true;
546 }
547
548 static bool handle_selection_TARGETS(XSelectionRequestEvent *req)
549 {
550 // Default supported targets
551 vector<long> targets;
552 targets.push_back(xa_targets);
553 targets.push_back(xa_multiple);
554 targets.push_back(xa_timestamp);
555
556 // Extra targets matchin current clipboard data
557 switch (clip_data.type) {
558 case FOURCC('T','E','X','T'):
559 targets.push_back(XA_STRING);
560 break;
561 case FOURCC('P','I','C','T'):
562 break;
563 }
564
565 // Change requestor property
566 XChangeProperty(x_display, req->requestor, req->property,
567 xa_targets, 32,
568 PropModeReplace, (uint8 *)&targets.at(0), targets.size());
569
570 return true;
571 }
572
573 static bool handle_selection_STRING(XSelectionRequestEvent *req)
574 {
575 // Make sure we have valid data to send though ICCCM compliant
576 // clients should have first requested TARGETS to identify
577 // this possibility.
578 if (clip_data.type != FOURCC('T','E','X','T'))
579 return false;
580
581 // Send the string, it's already encoded as ISO-8859-1
582 XChangeProperty(x_display, req->requestor, req->property,
583 XA_STRING, 8,
584 PropModeReplace, (uint8 *)clip_data.data.data(), clip_data.data.size());
585
586 return true;
587 }
588
589 static bool handle_selection_MULTIPLE(XSelectionRequestEvent *req)
590 {
591 Atom rtype;
592 int rformat;
593 ByteArray data;
594
595 if (!read_property(x_display, req->requestor, req->property,
596 false, data, 0, &rtype, &rformat, 0))
597 return false;
598
599 // rtype should be ATOM_PAIR but some clients don't honour that
600 if (rformat != 32)
601 return false;
602
603 struct AtomPair { long target; long property; };
604 AtomPair *atom_pairs = (AtomPair *)data.data();
605 int n_atom_pairs = data.size() / sizeof(AtomPair);
606
607 bool handled = true;
608 if (n_atom_pairs) {
609 // Setup a new XSelectionRequestEvent when servicing individual requests
610 XSelectionRequestEvent event;
611 memcpy(&event, req, sizeof(event));
612
613 for (int i = 0; i < n_atom_pairs; i++) {
614 Atom target = atom_pairs[i].target;
615 Atom property = atom_pairs[i].property;
616
617 // None is not a valid property
618 if (property == None)
619 continue;
620
621 // Service this request
622 event.target = target;
623 event.property = property;
624 if (!handle_selection(&event, true)) {
625 /* FIXME: ICCCM 2.6.2:
626
627 If the owner fails to convert the target named by an
628 atom in the MULTIPLE property, it should replace that
629 atom in the property with None.
630 */
631 handled = false;
632 }
633 }
634 }
635
636 return handled;
637 }
638
639 static bool handle_selection(XSelectionRequestEvent *req, bool is_multiple)
640 {
641 bool handled =false;
642
643 if (req->target == xa_timestamp)
644 handled = handle_selection_TIMESTAMP(req);
645 else if (req->target == xa_targets)
646 handled = handle_selection_TARGETS(req);
647 else if (req->target == XA_STRING)
648 handled = handle_selection_STRING(req);
649 else if (req->target == xa_multiple)
650 handled = handle_selection_MULTIPLE(req);
651
652 // Notify requestor only when we are done with his request
653 if (handled && !is_multiple) {
654 XEvent out_event;
655 out_event.xselection.type = SelectionNotify;
656 out_event.xselection.requestor = req->requestor;
657 out_event.xselection.selection = req->selection;
658 out_event.xselection.target = req->target;
659 out_event.xselection.property = handled ? req->property : None;
660 out_event.xselection.time = req->time;
661 XSendEvent(x_display, req->requestor, False, 0, &out_event);
662 }
663 }
664
665 void ClipboardSelectionRequest(XSelectionRequestEvent *req)
666 {
667 if (req->requestor == clip_win || req->selection != xa_clipboard)
668 return;
669
670 #if PUTSCRAP_IGNORES_KLIPPER
671 if (is_klipper_window(req->requestor))
672 return;
673 #endif
674
675 D(bug("Selection requested from 0x%lx to 0x%lx (%s) 0x%lx (%s)\n",
676 req->requestor,
677 req->selection,
678 XGetAtomName(req->display, req->selection),
679 req->target,
680 XGetAtomName(req->display, req->target)));
681
682 handle_selection(req, false);
683 }
684
685 void ClipboardSelectionNotify(XSelectionEvent *req)
686 {
687 if (req->requestor != clip_win || req->selection != xa_clipboard)
688 return;
689
690 D(bug("Selection is now available\n"));
691 memcpy(&xselection_event, req, sizeof(xselection_event));
692 LOCK_CLIPBOARD;
693 xselection_event_avail = true;
694 UNLOCK_CLIPBOARD;
695 }