ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/clip_unix.cpp
Revision: 1.11
Committed: 2005-01-30T21:42:14Z (19 years, 9 months ago) by gbeauche
Branch: MAIN
Changes since 1.10: +1 -1 lines
Log Message:
Happy New Year!

File Contents

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