1/*
2 *Copyright (C) 2003-2004 Harold L Hunt II All Rights Reserved.
3 *Copyright (C) Colin Harrison 2005-2008
4 *
5 *Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 *"Software"), to deal in the Software without restriction, including
8 *without limitation the rights to use, copy, modify, merge, publish,
9 *distribute, sublicense, and/or sell copies of the Software, and to
10 *permit persons to whom the Software is furnished to do so, subject to
11 *the following conditions:
12 *
13 *The above copyright notice and this permission notice shall be
14 *included in all copies or substantial portions of the Software.
15 *
16 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 *EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 *NONINFRINGEMENT. IN NO EVENT SHALL HAROLD L HUNT II BE LIABLE FOR
20 *ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
21 *CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 *WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 *
24 *Except as contained in this notice, the name of the copyright holder(s)
25 *and author(s) shall not be used in advertising or otherwise to promote
26 *the sale, use or other dealings in this Software without prior written
27 *authorization from the copyright holder(s) and author(s).
28 *
29 * Authors:	Harold L Hunt II
30 *              Colin Harrison
31 */
32
33#ifdef HAVE_XWIN_CONFIG_H
34#include <xwin-config.h>
35#endif
36
37#include <limits.h>
38#include <wchar.h>
39
40#include <xcb/xcb.h>
41#include <xcb/xfixes.h>
42
43#include "winclipboard.h"
44#include "internal.h"
45
46/*
47 * Constants
48 */
49
50#define CLIP_NUM_SELECTIONS		2
51#define CLIP_OWN_NONE			-1
52#define CLIP_OWN_PRIMARY		0
53#define CLIP_OWN_CLIPBOARD		1
54
55#define CP_ISO_8559_1 28591
56
57/*
58 * Global variables
59 */
60
61extern int xfixes_event_base;
62BOOL fPrimarySelection = TRUE;
63
64/*
65 * Local variables
66 */
67
68static xcb_window_t s_iOwners[CLIP_NUM_SELECTIONS] = { XCB_NONE, XCB_NONE };
69static const char *szSelectionNames[CLIP_NUM_SELECTIONS] =
70    { "PRIMARY", "CLIPBOARD" };
71
72static unsigned int lastOwnedSelectionIndex = CLIP_OWN_NONE;
73
74static void
75MonitorSelection(xcb_xfixes_selection_notify_event_t * e, unsigned int i)
76{
77    /* Look for owned -> not owned transition */
78    if ((XCB_NONE == e->owner) && (XCB_NONE != s_iOwners[i])) {
79        unsigned int other_index;
80
81        winDebug("MonitorSelection - %s - Going from owned to not owned.\n",
82                 szSelectionNames[i]);
83
84        /* If this selection is not owned, the other monitored selection must be the most
85           recently owned, if it is owned at all */
86        if (i == CLIP_OWN_PRIMARY)
87            other_index = CLIP_OWN_CLIPBOARD;
88        if (i == CLIP_OWN_CLIPBOARD)
89            other_index = CLIP_OWN_PRIMARY;
90        if (XCB_NONE != s_iOwners[other_index])
91            lastOwnedSelectionIndex = other_index;
92        else
93            lastOwnedSelectionIndex = CLIP_OWN_NONE;
94    }
95
96    /* Save last owned selection */
97    if (XCB_NONE != e->owner) {
98        lastOwnedSelectionIndex = i;
99    }
100
101    /* Save new selection owner or None */
102    s_iOwners[i] = e->owner;
103    winDebug("MonitorSelection - %s - Now owned by XID %x\n",
104             szSelectionNames[i], e->owner);
105}
106
107xcb_atom_t
108winClipboardGetLastOwnedSelectionAtom(ClipboardAtoms *atoms)
109{
110    if (lastOwnedSelectionIndex == CLIP_OWN_NONE)
111        return XCB_NONE;
112
113    if (lastOwnedSelectionIndex == CLIP_OWN_PRIMARY)
114        return XCB_ATOM_PRIMARY;
115
116    if (lastOwnedSelectionIndex == CLIP_OWN_CLIPBOARD)
117        return atoms->atomClipboard;
118
119    return XCB_NONE;
120}
121
122
123void
124winClipboardInitMonitoredSelections(void)
125{
126    /* Initialize static variables */
127    int i;
128    for (i = 0; i < CLIP_NUM_SELECTIONS; ++i)
129      s_iOwners[i] = XCB_NONE;
130
131    lastOwnedSelectionIndex = CLIP_OWN_NONE;
132}
133
134static char *get_atom_name(xcb_connection_t *conn, xcb_atom_t atom)
135{
136    char *ret;
137    xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name(conn, atom);
138    xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(conn, cookie, NULL);
139    if (!reply)
140        return NULL;
141    ret = malloc(xcb_get_atom_name_name_length(reply) + 1);
142    if (ret) {
143        memcpy(ret, xcb_get_atom_name_name(reply), xcb_get_atom_name_name_length(reply));
144        ret[xcb_get_atom_name_name_length(reply)] = '\0';
145    }
146    free(reply);
147    return ret;
148}
149
150static int
151winClipboardSelectionNotifyTargets(HWND hwnd, xcb_window_t iWindow, xcb_connection_t *conn, ClipboardConversionData *data, ClipboardAtoms *atoms)
152{
153  /* Retrieve the selection data and delete the property */
154  xcb_get_property_cookie_t cookie = xcb_get_property(conn,
155                                                      TRUE,
156                                                      iWindow,
157                                                      atoms->atomLocalProperty,
158                                                      XCB_GET_PROPERTY_TYPE_ANY,
159                                                      0,
160                                                      INT_MAX);
161  xcb_get_property_reply_t *reply = xcb_get_property_reply(conn, cookie, NULL);
162  if (!reply) {
163      ErrorF("winClipboardFlushXEvents - SelectionNotify - "
164             "XGetWindowProperty () failed\n");
165  } else {
166      xcb_atom_t *prop = xcb_get_property_value(reply);
167      int nitems = xcb_get_property_value_length(reply)/sizeof(xcb_atom_t);
168      int i;
169      data->targetList = malloc((nitems+1)*sizeof(xcb_atom_t));
170
171      for (i = 0; i < nitems; i++)
172          {
173              xcb_atom_t atom = prop[i];
174              char *pszAtomName = get_atom_name(conn, atom);
175              data->targetList[i] = atom;
176              winDebug("winClipboardFlushXEvents - SelectionNotify - target[%d] %d = %s\n", i, atom, pszAtomName);
177              free(pszAtomName);
178      }
179
180    data->targetList[nitems] = 0;
181
182    free(reply);
183  }
184
185  return WIN_XEVENTS_NOTIFY_TARGETS;
186}
187
188static int
189winClipboardSelectionNotifyData(HWND hwnd, xcb_window_t iWindow, xcb_connection_t *conn, ClipboardConversionData *data, ClipboardAtoms *atoms)
190{
191    xcb_atom_t encoding;
192    int format;
193    unsigned long int nitems;
194    unsigned long int after;
195    unsigned char *value;
196
197    unsigned char *xtpText_value;
198    xcb_atom_t xtpText_encoding;
199    int xtpText_nitems;
200
201    BOOL fSetClipboardData = TRUE;
202    char *pszReturnData = NULL;
203    UINT codepage;
204    wchar_t *pwszUnicodeStr = NULL;
205    HGLOBAL hGlobal = NULL;
206    char *pszGlobalData = NULL;
207
208    /* Retrieve the selection data and delete the property */
209    xcb_get_property_cookie_t cookie = xcb_get_property(conn,
210                                                        TRUE,
211                                                        iWindow,
212                                                        atoms->atomLocalProperty,
213                                                        XCB_GET_PROPERTY_TYPE_ANY,
214                                                        0,
215                                                        INT_MAX);
216    xcb_get_property_reply_t *reply = xcb_get_property_reply(conn, cookie, NULL);
217    if (!reply) {
218        ErrorF("winClipboardFlushXEvents - SelectionNotify - "
219               "XGetWindowProperty () failed\n");
220        goto winClipboardFlushXEvents_SelectionNotify_Done;
221    } else {
222        nitems = xcb_get_property_value_length(reply);
223        value =  xcb_get_property_value(reply);
224        after = reply->bytes_after;
225        encoding = reply->type;
226        format = reply->format;
227        // We assume format == 8 (i.e. data is a sequence of bytes).  It's not
228        // clear how anything else should be handled.
229        if (format != 8)
230            ErrorF("SelectionNotify: format is %d, proceeding as if it was 8\n", format);
231    }
232
233    {
234        char *pszAtomName;
235        winDebug("SelectionNotify - returned data %lu left %lu\n", nitems, after);
236        pszAtomName = get_atom_name(conn, encoding);
237        winDebug("Notify atom name %s\n", pszAtomName);
238        free(pszAtomName);
239    }
240
241    /* INCR reply indicates the start of a incremental transfer */
242    if (encoding == atoms->atomIncr) {
243        winDebug("winClipboardSelectionNotifyData: starting INCR, anticipated size %d\n", *(int *)value);
244        data->incrsize = 0;
245        data->incr = malloc(*(int *)value);
246        // XXX: if malloc failed, we have an error
247        return WIN_XEVENTS_SUCCESS;
248    }
249    else if (data->incr) {
250        /* If an INCR transfer is in progress ... */
251        if (nitems == 0) {
252            winDebug("winClipboardSelectionNotifyData: ending INCR, actual size %ld\n", data->incrsize);
253            /* a zero-length property indicates the end of the data */
254            xtpText_value = data->incr;
255            xtpText_encoding = encoding;
256            // XXX: The type of the converted selection is the type of the first
257            // partial property. The remaining partial properties must have the
258            // same type.
259            xtpText_nitems = data->incrsize;
260        }
261        else {
262            /* Otherwise, continue appending the INCR data */
263            winDebug("winClipboardSelectionNotifyData: INCR, %ld bytes\n", nitems);
264            data->incr = realloc(data->incr, data->incrsize + nitems);
265            memcpy(data->incr + data->incrsize, value, nitems);
266            data->incrsize = data->incrsize + nitems;
267            return WIN_XEVENTS_SUCCESS;
268        }
269    }
270    else {
271        /* Otherwise, the data is just contained in the property */
272        winDebug("winClipboardSelectionNotifyData: non-INCR, %ld bytes\n", nitems);
273        xtpText_value = value;
274        xtpText_encoding = encoding;
275        xtpText_nitems = nitems;
276    }
277
278    if (xtpText_encoding == atoms->atomUTF8String) {
279        pszReturnData = malloc(xtpText_nitems + 1);
280        memcpy(pszReturnData, xtpText_value, xtpText_nitems);
281        pszReturnData[xtpText_nitems] = 0;
282        codepage = CP_UTF8; // code page identifier for utf8
283    } else if (xtpText_encoding == XCB_ATOM_STRING) {
284        // STRING encoding is Latin1 (ISO8859-1) plus tab and newline
285        pszReturnData = malloc(xtpText_nitems + 1);
286        memcpy(pszReturnData, xtpText_value, xtpText_nitems);
287        pszReturnData[xtpText_nitems] = 0;
288        codepage = CP_ISO_8559_1; // code page identifier for iso-8559-1
289    } else if (xtpText_encoding == atoms->atomCompoundText) {
290        // COMPOUND_TEXT is complex, based on ISO 2022
291        ErrorF("SelectionNotify: data in COMPOUND_TEXT encoding which is not implemented, discarding\n");
292        pszReturnData = malloc(1);
293        pszReturnData[0] = '\0';
294    } else { // shouldn't happen as we accept no other encodings
295        pszReturnData = malloc(1);
296        pszReturnData[0] = '\0';
297    }
298
299    /* Free the data returned from xcb_get_property */
300    free(reply);
301
302    /* Free any INCR data */
303    if (data->incr) {
304        free(data->incr);
305        data->incr = NULL;
306        data->incrsize = 0;
307    }
308
309    /* Convert the X clipboard string to DOS format */
310    winClipboardUNIXtoDOS(&pszReturnData, strlen(pszReturnData));
311
312    /* Find out how much space needed when converted to UTF-16 */
313    int iUnicodeLen = MultiByteToWideChar(codepage, 0,
314                                          pszReturnData, -1, NULL, 0);
315
316    /* NOTE: iUnicodeLen includes space for null terminator */
317    pwszUnicodeStr = malloc(sizeof(wchar_t) * iUnicodeLen);
318    if (!pwszUnicodeStr) {
319        ErrorF("winClipboardFlushXEvents - SelectionNotify "
320               "malloc failed for pwszUnicodeStr, aborting.\n");
321
322        /* Abort */
323        goto winClipboardFlushXEvents_SelectionNotify_Done;
324    }
325
326    /* Do the actual conversion */
327    MultiByteToWideChar(codepage, 0,
328                        pszReturnData, -1, pwszUnicodeStr, iUnicodeLen);
329
330    /* Allocate global memory for the X clipboard data */
331    hGlobal = GlobalAlloc(GMEM_MOVEABLE, sizeof(wchar_t) * iUnicodeLen);
332
333    free(pszReturnData);
334
335    /* Check that global memory was allocated */
336    if (!hGlobal) {
337        ErrorF("winClipboardFlushXEvents - SelectionNotify "
338               "GlobalAlloc failed, aborting: %08x\n", (unsigned int)GetLastError());
339
340        /* Abort */
341        goto winClipboardFlushXEvents_SelectionNotify_Done;
342    }
343
344    /* Obtain a pointer to the global memory */
345    pszGlobalData = GlobalLock(hGlobal);
346    if (pszGlobalData == NULL) {
347        ErrorF("winClipboardFlushXEvents - Could not lock global "
348               "memory for clipboard transfer\n");
349
350        /* Abort */
351        goto winClipboardFlushXEvents_SelectionNotify_Done;
352    }
353
354    /* Copy the returned string into the global memory */
355    wcscpy((wchar_t *)pszGlobalData, pwszUnicodeStr);
356    free(pwszUnicodeStr);
357    pwszUnicodeStr = NULL;
358
359    /* Release the pointer to the global memory */
360    GlobalUnlock(hGlobal);
361    pszGlobalData = NULL;
362
363    /* Push the selection data to the Windows clipboard */
364    SetClipboardData(CF_UNICODETEXT, hGlobal);
365
366    /* Flag that SetClipboardData has been called */
367    fSetClipboardData = FALSE;
368
369    /*
370     * NOTE: Do not try to free pszGlobalData, it is owned by
371     * Windows after the call to SetClipboardData ().
372     */
373
374 winClipboardFlushXEvents_SelectionNotify_Done:
375    /* Free allocated resources */
376    free(pwszUnicodeStr);
377    if (hGlobal && pszGlobalData)
378        GlobalUnlock(hGlobal);
379    if (fSetClipboardData) {
380        SetClipboardData(CF_UNICODETEXT, NULL);
381        SetClipboardData(CF_TEXT, NULL);
382    }
383    return WIN_XEVENTS_NOTIFY_DATA;
384}
385
386/*
387 * Process any pending X events
388 */
389
390int
391winClipboardFlushXEvents(HWND hwnd,
392                         xcb_window_t iWindow, xcb_connection_t *conn,
393                         ClipboardConversionData *data, ClipboardAtoms *atoms)
394{
395    xcb_atom_t atomClipboard = atoms->atomClipboard;
396    xcb_atom_t atomUTF8String = atoms->atomUTF8String;
397    xcb_atom_t atomCompoundText = atoms->atomCompoundText;
398    xcb_atom_t atomTargets = atoms->atomTargets;
399
400    /* Process all pending events */
401    xcb_generic_event_t *event;
402    while ((event = xcb_poll_for_event(conn))) {
403        const char *pszGlobalData = NULL;
404        HGLOBAL hGlobal = NULL;
405        char *pszConvertData = NULL;
406        BOOL fAbort = FALSE;
407        BOOL fCloseClipboard = FALSE;
408
409        /* Branch on the event type */
410        switch (event->response_type & ~0x80) {
411        case XCB_SELECTION_REQUEST:
412        {
413            char *xtpText_value = NULL;
414            int xtpText_nitems;
415            UINT codepage;
416
417            xcb_selection_request_event_t *selection_request =  (xcb_selection_request_event_t *)event;
418        {
419            char *pszAtomName = NULL;
420
421            winDebug("SelectionRequest - target %d\n", selection_request->target);
422
423            pszAtomName = get_atom_name(conn, selection_request->target);
424            winDebug("SelectionRequest - Target atom name %s\n", pszAtomName);
425            free(pszAtomName);
426        }
427
428            /* Abort if invalid target type */
429            if (selection_request->target != XCB_ATOM_STRING
430                && selection_request->target != atomUTF8String
431                && selection_request->target != atomCompoundText
432                && selection_request->target != atomTargets) {
433                /* Abort */
434                fAbort = TRUE;
435                goto winClipboardFlushXEvents_SelectionRequest_Done;
436            }
437
438            /* Handle targets type of request */
439            if (selection_request->target == atomTargets) {
440                xcb_atom_t atomTargetArr[] =
441                    {
442                     atomTargets,
443                     atomUTF8String,
444                     XCB_ATOM_STRING,
445                     // atomCompoundText, not implemented (yet?)
446                    };
447
448                /* Try to change the property */
449                xcb_void_cookie_t cookie = xcb_change_property_checked(conn,
450                                          XCB_PROP_MODE_REPLACE,
451                                          selection_request->requestor,
452                                          selection_request->property,
453                                          XCB_ATOM_ATOM,
454                                          32,
455                                          ARRAY_SIZE(atomTargetArr),
456                                          (unsigned char *) atomTargetArr);
457                xcb_generic_error_t *error;
458                if ((error = xcb_request_check(conn, cookie))) {
459                    ErrorF("winClipboardFlushXEvents - SelectionRequest - "
460                           "xcb_change_property failed");
461                    free(error);
462                }
463
464                /* Setup selection notify xevent */
465                xcb_selection_notify_event_t eventSelection;
466                eventSelection.response_type = XCB_SELECTION_NOTIFY;
467                eventSelection.requestor = selection_request->requestor;
468                eventSelection.selection = selection_request->selection;
469                eventSelection.target = selection_request->target;
470                eventSelection.property = selection_request->property;
471                eventSelection.time = selection_request->time;
472
473                /*
474                 * Notify the requesting window that
475                 * the operation has completed
476                 */
477                cookie = xcb_send_event_checked(conn, FALSE,
478                                                eventSelection.requestor,
479                                                0, (char *) &eventSelection);
480                if ((error = xcb_request_check(conn, cookie))) {
481                    ErrorF("winClipboardFlushXEvents - SelectionRequest - "
482                           "xcb_send_event() failed\n");
483                }
484                break;
485            }
486
487            /* Close clipboard if we have it open already */
488            if (GetOpenClipboardWindow() == hwnd) {
489                CloseClipboard();
490            }
491
492            /* Access the clipboard */
493            if (!OpenClipboard(hwnd)) {
494                ErrorF("winClipboardFlushXEvents - SelectionRequest - "
495                       "OpenClipboard () failed: %08x\n", (unsigned int)GetLastError());
496
497                /* Abort */
498                fAbort = TRUE;
499                goto winClipboardFlushXEvents_SelectionRequest_Done;
500            }
501
502            /* Indicate that clipboard was opened */
503            fCloseClipboard = TRUE;
504
505            /* Check that clipboard format is available */
506            if (!IsClipboardFormatAvailable(CF_UNICODETEXT)) {
507                static int count;       /* Hack to stop acroread spamming the log */
508                static HWND lasthwnd;   /* I've not seen any other client get here repeatedly? */
509
510                if (hwnd != lasthwnd)
511                    count = 0;
512                count++;
513                if (count < 6)
514                    ErrorF("winClipboardFlushXEvents - CF_UNICODETEXT is not "
515                           "available from Win32 clipboard.  Aborting %d.\n",
516                           count);
517                lasthwnd = hwnd;
518
519                /* Abort */
520                fAbort = TRUE;
521                goto winClipboardFlushXEvents_SelectionRequest_Done;
522            }
523
524            /* Get a pointer to the clipboard text, in desired format */
525            /* Retrieve clipboard data */
526            hGlobal = GetClipboardData(CF_UNICODETEXT);
527
528            if (!hGlobal) {
529                ErrorF("winClipboardFlushXEvents - SelectionRequest - "
530                       "GetClipboardData () failed: %08x\n", (unsigned int)GetLastError());
531
532                /* Abort */
533                fAbort = TRUE;
534                goto winClipboardFlushXEvents_SelectionRequest_Done;
535            }
536            pszGlobalData = (char *) GlobalLock(hGlobal);
537
538            /* Convert to target string style */
539            if (selection_request->target == XCB_ATOM_STRING) {
540                codepage = CP_ISO_8559_1; // code page identifier for iso-8559-1
541            } else if (selection_request->target == atomUTF8String) {
542                codepage = CP_UTF8; // code page identifier for utf8
543            } else if (selection_request->target == atomCompoundText) {
544                // COMPOUND_TEXT is complex, not (yet) implemented
545                pszGlobalData = "COMPOUND_TEXT not implemented";
546                codepage = CP_UTF8; // code page identifier for utf8
547            }
548
549            /* Convert the UTF16 string to required encoding */
550            int iConvertDataLen = WideCharToMultiByte(codepage, 0,
551                                                      (LPCWSTR) pszGlobalData, -1,
552                                                      NULL, 0, NULL, NULL);
553            /* NOTE: iConvertDataLen includes space for null terminator */
554            pszConvertData = malloc(iConvertDataLen);
555            WideCharToMultiByte(codepage, 0,
556                                (LPCWSTR) pszGlobalData, -1,
557                                pszConvertData, iConvertDataLen, NULL, NULL);
558
559            /* Convert DOS string to UNIX string */
560            winClipboardDOStoUNIX(pszConvertData, strlen(pszConvertData));
561
562            xtpText_value = strdup(pszConvertData);
563            xtpText_nitems = strlen(pszConvertData);
564
565            /* data will fit into a single X request? (INCR not yet supported) */
566            {
567                uint32_t maxreqsize = xcb_get_maximum_request_length(conn);
568
569                /* covert to bytes and allow for allow for X_ChangeProperty request */
570                maxreqsize = maxreqsize*4 - 24;
571
572                if (xtpText_nitems > maxreqsize) {
573                    ErrorF("winClipboardFlushXEvents - clipboard data size %d greater than maximum %u\n", xtpText_nitems, maxreqsize);
574
575                    /* Abort */
576                    fAbort = TRUE;
577                    goto winClipboardFlushXEvents_SelectionRequest_Done;
578                }
579            }
580
581            /* Copy the clipboard text to the requesting window */
582            xcb_void_cookie_t cookie = xcb_change_property_checked(conn,
583                                      XCB_PROP_MODE_REPLACE,
584                                      selection_request->requestor,
585                                      selection_request->property,
586                                      selection_request->target,
587                                      8,
588                                      xtpText_nitems, xtpText_value);
589            xcb_generic_error_t *error;
590            if ((error = xcb_request_check(conn, cookie))) {
591                ErrorF("winClipboardFlushXEvents - SelectionRequest - "
592                       "xcb_change_property failed\n");
593
594                /* Abort */
595                fAbort = TRUE;
596                goto winClipboardFlushXEvents_SelectionRequest_Done;
597            }
598
599            /* Free the converted string */
600            free(pszConvertData);
601            pszConvertData = NULL;
602
603            /* Release the clipboard data */
604            GlobalUnlock(hGlobal);
605            pszGlobalData = NULL;
606            fCloseClipboard = FALSE;
607            CloseClipboard();
608
609            /* Clean up */
610            free(xtpText_value);
611            xtpText_value = NULL;
612
613            /* Setup selection notify event */
614            xcb_selection_notify_event_t eventSelection;
615            eventSelection.response_type = XCB_SELECTION_NOTIFY;
616            eventSelection.requestor = selection_request->requestor;
617            eventSelection.selection = selection_request->selection;
618            eventSelection.target = selection_request->target;
619            eventSelection.property = selection_request->property;
620            eventSelection.time = selection_request->time;
621
622            /* Notify the requesting window that the operation has completed */
623            cookie = xcb_send_event_checked(conn, FALSE,
624                                            eventSelection.requestor,
625                                            0, (char *) &eventSelection);
626            if ((error = xcb_request_check(conn, cookie))) {
627                ErrorF("winClipboardFlushXEvents - SelectionRequest - "
628                       "xcb_send_event() failed\n");
629
630                /* Abort */
631                fAbort = TRUE;
632                goto winClipboardFlushXEvents_SelectionRequest_Done;
633            }
634
635 winClipboardFlushXEvents_SelectionRequest_Done:
636            /* Free allocated resources */
637            if (xtpText_value) {
638                free(xtpText_value);
639            }
640            if (pszConvertData)
641                free(pszConvertData);
642            if (hGlobal && pszGlobalData)
643                GlobalUnlock(hGlobal);
644
645            /*
646             * Send a SelectionNotify event to the requesting
647             * client when we abort.
648             */
649            if (fAbort) {
650                /* Setup selection notify event */
651                eventSelection.response_type = XCB_SELECTION_NOTIFY;
652                eventSelection.requestor = selection_request->requestor;
653                eventSelection.selection = selection_request->selection;
654                eventSelection.target = selection_request->target;
655                eventSelection.property = XCB_NONE;
656                eventSelection.time = selection_request->time;
657
658                /* Notify the requesting window that the operation is complete */
659                cookie = xcb_send_event_checked(conn, FALSE,
660                                                eventSelection.requestor,
661                                                0, (char *) &eventSelection);
662                if ((error = xcb_request_check(conn, cookie))) {
663                    /*
664                     * Should not be a problem if XSendEvent fails because
665                     * the client may simply have exited.
666                     */
667                    ErrorF("winClipboardFlushXEvents - SelectionRequest - "
668                           "xcb_send_event() failed for abort event.\n");
669                }
670            }
671
672            /* Close clipboard if it was opened */
673            if (fCloseClipboard) {
674                fCloseClipboard = FALSE;
675                CloseClipboard();
676            }
677            break;
678        }
679
680        case XCB_SELECTION_NOTIFY:
681        {
682            xcb_selection_notify_event_t *selection_notify =  (xcb_selection_notify_event_t *)event;
683            winDebug("winClipboardFlushXEvents - SelectionNotify\n");
684            {
685                char *pszAtomName;
686                pszAtomName = get_atom_name(conn, selection_notify->selection);
687                winDebug("winClipboardFlushXEvents - SelectionNotify - ATOM: %s\n", pszAtomName);
688                free(pszAtomName);
689            }
690
691            /*
692              SelectionNotify with property of XCB_NONE indicates either:
693
694              (i) Generated by the X server if no owner for the specified selection exists
695                  (perhaps it's disappeared on us mid-transaction), or
696              (ii) Sent by the selection owner when the requested selection conversion could
697                   not be performed or server errors prevented the conversion data being returned
698            */
699            if (selection_notify->property == XCB_NONE) {
700                    ErrorF("winClipboardFlushXEvents - SelectionNotify - "
701                           "Conversion to format %d refused.\n",
702                           selection_notify->target);
703                    return WIN_XEVENTS_FAILED;
704                }
705
706            if (selection_notify->target == atomTargets) {
707              return winClipboardSelectionNotifyTargets(hwnd, iWindow, conn, data, atoms);
708            }
709
710            return winClipboardSelectionNotifyData(hwnd, iWindow, conn, data, atoms);
711        }
712
713        case XCB_SELECTION_CLEAR:
714            winDebug("SelectionClear - doing nothing\n");
715            break;
716
717        case XCB_PROPERTY_NOTIFY:
718        {
719            xcb_property_notify_event_t *property_notify = (xcb_property_notify_event_t *)event;
720
721            /* If INCR is in progress, collect the data */
722            if (data->incr &&
723                (property_notify->atom == atoms->atomLocalProperty) &&
724                (property_notify->state == XCB_PROPERTY_NEW_VALUE))
725                return winClipboardSelectionNotifyData(hwnd, iWindow, conn, data, atoms);
726
727            break;
728        }
729
730        case XCB_MAPPING_NOTIFY:
731            break;
732
733        case 0:
734            /* This is just laziness rather than making sure we used _checked everywhere */
735            {
736                xcb_generic_error_t *err = (xcb_generic_error_t *)event;
737                ErrorF("winClipboardFlushXEvents - Error code: %i, ID: 0x%08x, "
738                       "Major opcode: %i, Minor opcode: %i\n",
739                       err->error_code, err->resource_id,
740                       err->major_code, err->minor_code);
741            }
742            break;
743
744        default:
745            if ((event->response_type & ~0x80) == XCB_XFIXES_SELECTION_EVENT_SET_SELECTION_OWNER + xfixes_event_base) {
746                xcb_xfixes_selection_notify_event_t *e = (xcb_xfixes_selection_notify_event_t *)event;
747                winDebug("winClipboardFlushXEvents - XFixesSetSelectionOwnerNotify\n");
748
749                /* Save selection owners for monitored selections, ignore other selections */
750                if ((e->selection == XCB_ATOM_PRIMARY) && fPrimarySelection) {
751                    MonitorSelection(e, CLIP_OWN_PRIMARY);
752                }
753                else if (e->selection == atomClipboard) {
754                    MonitorSelection(e, CLIP_OWN_CLIPBOARD);
755                }
756                else
757                    break;
758
759                /* Selection is being disowned */
760                if (e->owner == XCB_NONE) {
761                    winDebug("winClipboardFlushXEvents - No window, returning.\n");
762                    break;
763                }
764
765                /*
766                   XXX: there are all kinds of wacky edge cases we might need here:
767                   - we own windows clipboard, but neither PRIMARY nor CLIPBOARD have an owner, so we should disown it?
768                   - root window is taking ownership?
769                 */
770
771                /* If we are the owner of the most recently owned selection, don't go all recursive :) */
772                if ((lastOwnedSelectionIndex != CLIP_OWN_NONE) &&
773                    (s_iOwners[lastOwnedSelectionIndex] == iWindow)) {
774                    winDebug("winClipboardFlushXEvents - Ownership changed to us, aborting.\n");
775                    break;
776                }
777
778                /* Close clipboard if we have it open already (possible? correct??) */
779                if (GetOpenClipboardWindow() == hwnd) {
780                    CloseClipboard();
781                }
782
783                /* Access the Windows clipboard */
784                if (!OpenClipboard(hwnd)) {
785                    ErrorF("winClipboardFlushXEvents - OpenClipboard () failed: %08x\n",
786                           (int) GetLastError());
787                    break;
788                }
789
790                /* Take ownership of the Windows clipboard */
791                if (!EmptyClipboard()) {
792                    ErrorF("winClipboardFlushXEvents - EmptyClipboard () failed: %08x\n",
793                           (int) GetLastError());
794                    break;
795                }
796
797                /* Advertise regular text and unicode */
798                SetClipboardData(CF_UNICODETEXT, NULL);
799                SetClipboardData(CF_TEXT, NULL);
800
801                /* Release the clipboard */
802                if (!CloseClipboard()) {
803                    ErrorF("winClipboardFlushXEvents - CloseClipboard () failed: %08x\n",
804                           (int) GetLastError());
805                    break;
806                }
807            }
808            /* XCB_XFIXES_SELECTION_EVENT_SELECTION_WINDOW_DESTROY */
809            /* XCB_XFIXES_SELECTION_EVENT_SELECTION_CLIENT_CLOSE */
810            else {
811                ErrorF("winClipboardFlushXEvents - unexpected event type %d\n",
812                       event->response_type);
813            }
814            break;
815        }
816
817        /* I/O errors etc. */
818        {
819            int e = xcb_connection_has_error(conn);
820            if (e) {
821                ErrorF("winClipboardFlushXEvents - Fatal error %d on xcb connection\n", e);
822                break;
823            }
824        }
825    }
826
827    return WIN_XEVENTS_SUCCESS;
828
829}
830