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, 10 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

# User Rev Content
1 cebix 1.1 /*
2     * clip_unix.cpp - Clipboard handling, Unix implementation
3     *
4 gbeauche 1.2 * SheepShaver (C) 1997-2003 Christian Bauer and Marc Hellwig
5 cebix 1.1 *
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 gbeauche 1.2 #include <X11/Xatom.h>
25     #include <pthread.h>
26     #include <set>
27     #include <vector>
28 cebix 1.1
29     #include "macos_util.h"
30     #include "clip.h"
31 gbeauche 1.2 #include "prefs.h"
32     #include "cpu_emulation.h"
33     #include "main.h"
34     #include "emul_op.h"
35 cebix 1.1
36     #define DEBUG 0
37     #include "debug.h"
38    
39 gbeauche 1.2 #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 cebix 1.1
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 gbeauche 1.2 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 cebix 1.1
304     /*
305     * Initialization
306     */
307    
308     void ClipInit(void)
309     {
310 gbeauche 1.2 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 cebix 1.1 }
326    
327    
328     /*
329     * Deinitialization
330     */
331    
332     void ClipExit(void)
333     {
334 gbeauche 1.2 // Close window
335     XDestroyWindow(x_display, clip_win);
336 cebix 1.1 }
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 gbeauche 1.2 if (!REPLACE_PUTSCRAP)
347     return;
348     if (we_put_this_data) {
349     we_put_this_data = false;
350     return;
351     }
352 cebix 1.1 if (length <= 0)
353     return;
354    
355 gbeauche 1.2 bool did_putscrap = false;
356 cebix 1.1 switch (type) {
357     case FOURCC('T','E','X','T'):
358     D(bug(" clipping TEXT\n"));
359 gbeauche 1.2 clip_data.type = type;
360     clip_data.data.clear();
361     clip_data.data.reserve(length);
362 cebix 1.1
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 gbeauche 1.2 } else if (!no_clip_conversion)
371 cebix 1.1 c = mac2iso[c & 0x7f];
372 gbeauche 1.2 clip_data.data.push_back(c);
373 cebix 1.1 }
374 gbeauche 1.2 did_putscrap = true;
375     break;
376     }
377 cebix 1.1
378 gbeauche 1.2 // 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 cebix 1.1 }
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 gbeauche 1.2 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 cebix 1.1 switch (type) {
447 gbeauche 1.2 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 cebix 1.1 case FOURCC('T','E','X','T'):
479 gbeauche 1.2 // 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 cebix 1.1 break;
491 gbeauche 1.2 }
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 cebix 1.1 }
515 gbeauche 1.2 }
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 cebix 1.1 }