ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/clip_unix.cpp
Revision: 1.12
Committed: 2005-06-19T15:50:52Z (19 years, 4 months ago) by gbeauche
Branch: MAIN
Changes since 1.11: +2 -1 lines
Log Message:
Only close clipboard hidden window only if created. This fixes errors on
early exit from the GUI.

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 gbeauche 1.12 if (clip_win)
300     XDestroyWindow(x_display, clip_win);
301 cebix 1.1 }
302    
303    
304     /*
305     * Mac application wrote to clipboard
306     */
307    
308     void PutScrap(uint32 type, void *scrap, int32 length)
309     {
310 gbeauche 1.10 D(bug("PutScrap type %08lx, data %p, length %ld\n", type, scrap, length));
311     if (we_put_this_data) {
312     we_put_this_data = false;
313     return;
314     }
315 cebix 1.1 if (length <= 0)
316     return;
317    
318 gbeauche 1.10 XDisplayLock();
319     do_putscrap(type, scrap, length);
320     XDisplayUnlock();
321     }
322    
323     static void do_putscrap(uint32 type, void *scrap, int32 length)
324     {
325     clip_data.type = None;
326 cebix 1.1 switch (type) {
327 gbeauche 1.10 case FOURCC('T','E','X','T'): {
328     D(bug(" clipping TEXT\n"));
329     clip_data.type = XA_STRING;
330     clip_data.data.clear();
331     clip_data.data.reserve(length);
332    
333     // Convert text from Mac charset to ISO-Latin1
334     uint8 *p = (uint8 *)scrap;
335     for (int i=0; i<length; i++) {
336     uint8 c = *p++;
337     if (c < 0x80) {
338     if (c == 13) // CR -> LF
339     c = 10;
340     } else if (!no_clip_conversion)
341     c = mac2iso[c & 0x7f];
342     clip_data.data.push_back(c);
343     }
344     break;
345     }
346    
347     case FOURCC('s','t','y','l'): {
348     D(bug(" clipping styl\n"));
349     uint16 *p = (uint16 *)scrap;
350     uint16 n = ntohs(*p++);
351     D(bug(" %d styles (%d bytes)\n", n, length));
352     for (int i = 0; i < n; i++) {
353     uint32 offset = ntohl(*(uint32 *)p); p += 2;
354     uint16 line_height = ntohs(*p++);
355     uint16 font_ascent = ntohs(*p++);
356     uint16 font_family = ntohs(*p++);
357     uint16 style_code = ntohs(*p++);
358     uint16 char_size = ntohs(*p++);
359     uint16 r = ntohs(*p++);
360     uint16 g = ntohs(*p++);
361     uint16 b = ntohs(*p++);
362     D(bug(" offset=%d, height=%d, font ascent=%d, id=%d, style=%x, size=%d, RGB=%x/%x/%x\n",
363     offset, line_height, font_ascent, font_family, style_code, char_size, r, g, b));
364     }
365     break;
366     }
367     }
368    
369     // Acquire selection ownership
370     if (clip_data.type != None) {
371     clip_data.time = CurrentTime;
372     while (XGetSelectionOwner(x_display, xa_clipboard) != clip_win)
373     XSetSelectionOwner(x_display, xa_clipboard, clip_win, clip_data.time);
374     }
375     }
376    
377    
378     /*
379     * Mac application reads clipboard
380     */
381    
382     void GetScrap(void **handle, uint32 type, int32 offset)
383     {
384     D(bug("GetScrap handle %p, type %08x, offset %d\n", handle, type, offset));
385    
386     XDisplayLock();
387     do_getscrap(handle, type, offset);
388     XDisplayUnlock();
389     }
390    
391     static void do_getscrap(void **handle, uint32 type, int32 offset)
392     {
393     ByteArray data;
394     XEvent event;
395    
396     // If we own the selection, the data is already available on MacOS side
397     if (XGetSelectionOwner(x_display, xa_clipboard) == clip_win)
398     return;
399    
400     // Check TIMESTAMP
401     #if GETSCRAP_REQUESTS_TIMESTAMP
402     static Time last_timestamp = 0;
403     XConvertSelection(x_display, xa_clipboard, xa_timestamp, xa_clipboard, clip_win, CurrentTime);
404     if (wait_for_selection_notify_event(x_display, clip_win, &event, SELECTION_MAX_WAIT) &&
405     event.xselection.property != None &&
406     read_property(x_display,
407     event.xselection.requestor, event.xselection.property,
408     true, data, 0, 0, 0, false)) {
409     Time timestamp = ((long *)data.data())[0];
410     if (timestamp <= last_timestamp)
411     return;
412     }
413     last_timestamp = CurrentTime;
414     #endif
415    
416     // Get TARGETS available
417     #if GETSCRAP_REQUESTS_TARGETS
418     XConvertSelection(x_display, xa_clipboard, xa_targets, xa_clipboard, clip_win, CurrentTime);
419     if (!wait_for_selection_notify_event(x_display, clip_win, &event, SELECTION_MAX_WAIT) ||
420     event.xselection.property == None ||
421     !read_property(x_display,
422     event.xselection.requestor, event.xselection.property,
423     true, data, 0, 0, 0, false))
424     return;
425     #endif
426    
427     // Get appropriate format for requested data
428     Atom format = None;
429     #if GETSCRAP_REQUESTS_TARGETS
430     int n_atoms = data.size() / sizeof(long);
431     long *atoms = (long *)data.data();
432     for (int i = 0; i < n_atoms; i++) {
433     Atom target = atoms[i];
434     D(bug(" target %08x (%s)\n", target, XGetAtomName(x_display, target)));
435     switch (type) {
436 cebix 1.4 case FOURCC('T','E','X','T'):
437 cebix 1.1 D(bug(" clipping TEXT\n"));
438 gbeauche 1.10 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, &event, SELECTION_MAX_WAIT) ||
461     event.xselection.property == None ||
462     !read_property(x_display,
463     event.xselection.requestor, event.xselection.property,
464     false, data, 0, 0, 0, format == XA_STRING))
465     return;
466    
467     // Allocate space for new scrap in MacOS side
468     M68kRegisters r;
469     r.d[0] = data.size();
470     Execute68kTrap(0xa71e, &r); // NewPtrSysClear()
471     uint32 scrap_area = r.a[0];
472 cebix 1.1
473 gbeauche 1.10 if (scrap_area) {
474     switch (type) {
475     case FOURCC('T','E','X','T'):
476     // Convert text from ISO-Latin1 to Mac charset
477     uint8 *p = Mac2HostAddr(scrap_area);
478     for (int i = 0; i < data.size(); i++) {
479     uint8 c = data[i];
480 cebix 1.1 if (c < 0x80) {
481 gbeauche 1.10 if (c == 10) // LF -> CR
482     c = 13;
483 cebix 1.3 } else if (!no_clip_conversion)
484 gbeauche 1.10 c = iso2mac[c & 0x7f];
485     *p++ = c;
486     }
487     break;
488     }
489    
490     // Add new data to clipboard
491     static uint8 proc[] = {
492     0x59, 0x8f, // subq.l #4,sp
493     0xa9, 0xfc, // ZeroScrap()
494     0x2f, 0x3c, 0, 0, 0, 0, // move.l #length,-(sp)
495     0x2f, 0x3c, 0, 0, 0, 0, // move.l #type,-(sp)
496     0x2f, 0x3c, 0, 0, 0, 0, // move.l #outbuf,-(sp)
497     0xa9, 0xfe, // PutScrap()
498     0x58, 0x8f, // addq.l #4,sp
499     M68K_RTS >> 8, M68K_RTS
500     };
501     r.d[0] = sizeof(proc);
502     Execute68kTrap(0xa71e, &r); // NewPtrSysClear()
503     uint32 proc_area = r.a[0];
504    
505     // The procedure is run-time generated because it must lays in
506     // Mac address space. This is mandatory for "33-bit" address
507     // space optimization on 64-bit platforms because the static
508     // proc[] array is not remapped
509     Host2Mac_memcpy(proc_area, proc, sizeof(proc));
510     WriteMacInt32(proc_area + 6, data.size());
511     WriteMacInt32(proc_area + 12, type);
512     WriteMacInt32(proc_area + 18, scrap_area);
513     we_put_this_data = true;
514     Execute68k(proc_area, &r);
515    
516     // We are done with scratch memory
517     r.a[0] = proc_area;
518     Execute68kTrap(0xa01f, &r); // DisposePtr
519     r.a[0] = scrap_area;
520     Execute68kTrap(0xa01f, &r); // DisposePtr
521     }
522     }
523    
524    
525     /*
526     * Handle X11 selection events
527     */
528    
529     void ClipboardSelectionClear(XSelectionClearEvent *xev)
530     {
531     if (xev->selection != xa_clipboard)
532     return;
533    
534     D(bug("Selection cleared, lost clipboard ownership\n"));
535     clip_data.type = None;
536     clip_data.data.clear();
537     }
538    
539     // Top level selection handler
540     static bool handle_selection(XSelectionRequestEvent *req, bool is_multiple);
541    
542     static bool handle_selection_TIMESTAMP(XSelectionRequestEvent *req)
543     {
544     // 32-bit integer values are always passed as a long whatever is its size
545     long timestamp = clip_data.time;
546    
547     // Change requestor property
548     XChangeProperty(x_display, req->requestor, req->property,
549     XA_INTEGER, 32,
550     PropModeReplace, (uint8 *)&timestamp, 1);
551    
552     return true;
553     }
554    
555     static bool handle_selection_TARGETS(XSelectionRequestEvent *req)
556     {
557     // Default supported targets
558     vector<long> targets;
559     targets.push_back(xa_targets);
560     targets.push_back(xa_multiple);
561     targets.push_back(xa_timestamp);
562    
563     // Extra targets matchin current clipboard data
564     if (clip_data.type != None)
565     targets.push_back(clip_data.type);
566    
567     // Change requestor property
568     XChangeProperty(x_display, req->requestor, req->property,
569     xa_targets, 32,
570     PropModeReplace, (uint8 *)&targets[0], targets.size());
571    
572     return true;
573     }
574    
575     static bool handle_selection_STRING(XSelectionRequestEvent *req)
576     {
577     // Make sure we have valid data to send though ICCCM compliant
578     // clients should have first requested TARGETS to identify
579     // this possibility.
580     if (clip_data.type != XA_STRING)
581     return false;
582    
583     // Send the string, it's already encoded as ISO-8859-1
584     XChangeProperty(x_display, req->requestor, req->property,
585     XA_STRING, 8,
586     PropModeReplace, (uint8 *)clip_data.data.data(), clip_data.data.size());
587    
588     return true;
589     }
590    
591     static bool handle_selection_MULTIPLE(XSelectionRequestEvent *req)
592     {
593     Atom rtype;
594     int rformat;
595     ByteArray data;
596    
597     if (!read_property(x_display, req->requestor, req->property,
598     false, data, 0, &rtype, &rformat, 0))
599     return false;
600    
601     // rtype should be ATOM_PAIR but some clients don't honour that
602     if (rformat != 32)
603     return false;
604    
605     struct AtomPair { long target; long property; };
606     AtomPair *atom_pairs = (AtomPair *)data.data();
607     int n_atom_pairs = data.size() / sizeof(AtomPair);
608    
609     bool handled = true;
610     if (n_atom_pairs) {
611     // Setup a new XSelectionRequestEvent when servicing individual requests
612     XSelectionRequestEvent event;
613     memcpy(&event, req, sizeof(event));
614    
615     for (int i = 0; i < n_atom_pairs; i++) {
616     Atom target = atom_pairs[i].target;
617     Atom property = atom_pairs[i].property;
618    
619     // None is not a valid property
620     if (property == None)
621     continue;
622    
623     // Service this request
624     event.target = target;
625     event.property = property;
626     if (!handle_selection(&event, true)) {
627     /* FIXME: ICCCM 2.6.2:
628    
629     If the owner fails to convert the target named by an
630     atom in the MULTIPLE property, it should replace that
631     atom in the property with None.
632     */
633     handled = false;
634 cebix 1.1 }
635 gbeauche 1.10 }
636     }
637    
638     return handled;
639     }
640 cebix 1.1
641 gbeauche 1.10 static bool handle_selection(XSelectionRequestEvent *req, bool is_multiple)
642     {
643     bool handled =false;
644    
645     if (req->target == xa_timestamp)
646     handled = handle_selection_TIMESTAMP(req);
647     else if (req->target == xa_targets)
648     handled = handle_selection_TARGETS(req);
649     else if (req->target == XA_STRING)
650     handled = handle_selection_STRING(req);
651     else if (req->target == xa_multiple)
652     handled = handle_selection_MULTIPLE(req);
653    
654     // Notify requestor only when we are done with his request
655     if (handled && !is_multiple) {
656     XEvent out_event;
657     out_event.xselection.type = SelectionNotify;
658     out_event.xselection.requestor = req->requestor;
659     out_event.xselection.selection = req->selection;
660     out_event.xselection.target = req->target;
661     out_event.xselection.property = handled ? req->property : None;
662     out_event.xselection.time = req->time;
663     XSendEvent(x_display, req->requestor, False, 0, &out_event);
664 cebix 1.1 }
665     }
666 gbeauche 1.10
667     void ClipboardSelectionRequest(XSelectionRequestEvent *req)
668     {
669     if (req->requestor == clip_win || req->selection != xa_clipboard)
670     return;
671    
672     D(bug("Selection requested from 0x%lx to 0x%lx (%s) 0x%lx (%s)\n",
673     req->requestor,
674     req->selection,
675     XGetAtomName(req->display, req->selection),
676     req->target,
677     XGetAtomName(req->display, req->target)));
678    
679     handle_selection(req, false);
680     }