xevents.c revision 35c4bbdf
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/*
38 * Including any server header might define the macro _XSERVER64 on 64 bit machines.
39 * That macro must _NOT_ be defined for Xlib client code, otherwise bad things happen.
40 * So let's undef that macro if necessary.
41 */
42#ifdef _XSERVER64
43#undef _XSERVER64
44#endif
45
46#include <limits.h>
47#include <wchar.h>
48#include <X11/Xutil.h>
49#include <X11/Xatom.h>
50#include <X11/extensions/Xfixes.h>
51
52#include "winclipboard.h"
53#include "internal.h"
54
55/*
56 * Constants
57 */
58
59#define CLIP_NUM_SELECTIONS		2
60#define CLIP_OWN_NONE     		-1
61#define CLIP_OWN_PRIMARY		0
62#define CLIP_OWN_CLIPBOARD		1
63
64/*
65 * Global variables
66 */
67
68extern int xfixes_event_base;
69Bool fPrimarySelection = TRUE;
70
71/*
72 * Local variables
73 */
74
75static Window s_iOwners[CLIP_NUM_SELECTIONS] = { None, None };
76static const char *szSelectionNames[CLIP_NUM_SELECTIONS] =
77    { "PRIMARY", "CLIPBOARD" };
78
79static unsigned int lastOwnedSelectionIndex = CLIP_OWN_NONE;
80
81static void
82MonitorSelection(XFixesSelectionNotifyEvent * e, unsigned int i)
83{
84    /* Look for owned -> not owned transition */
85    if (None == e->owner && None != s_iOwners[i]) {
86        unsigned int other_index;
87
88        winDebug("MonitorSelection - %s - Going from owned to not owned.\n",
89                 szSelectionNames[i]);
90
91        /* If this selection is not owned, the other monitored selection must be the most
92           recently owned, if it is owned at all */
93        if (i == CLIP_OWN_PRIMARY)
94            other_index = CLIP_OWN_CLIPBOARD;
95        if (i == CLIP_OWN_CLIPBOARD)
96            other_index = CLIP_OWN_PRIMARY;
97        if (None != s_iOwners[other_index])
98            lastOwnedSelectionIndex = other_index;
99        else
100            lastOwnedSelectionIndex = CLIP_OWN_NONE;
101    }
102
103    /* Save last owned selection */
104    if (None != e->owner) {
105        lastOwnedSelectionIndex = i;
106    }
107
108    /* Save new selection owner or None */
109    s_iOwners[i] = e->owner;
110    winDebug("MonitorSelection - %s - Now owned by XID %lx\n",
111             szSelectionNames[i], e->owner);
112}
113
114Atom
115winClipboardGetLastOwnedSelectionAtom(ClipboardAtoms *atoms)
116{
117    if (lastOwnedSelectionIndex == CLIP_OWN_NONE)
118        return None;
119
120    if (lastOwnedSelectionIndex == CLIP_OWN_PRIMARY)
121        return XA_PRIMARY;
122
123    if (lastOwnedSelectionIndex == CLIP_OWN_CLIPBOARD)
124        return atoms->atomClipboard;
125
126    return None;
127}
128
129
130void
131winClipboardInitMonitoredSelections(void)
132{
133    /* Initialize static variables */
134    int i;
135    for (i = 0; i < CLIP_NUM_SELECTIONS; ++i)
136      s_iOwners[i] = None;
137
138    lastOwnedSelectionIndex = CLIP_OWN_NONE;
139}
140
141static int
142winClipboardSelectionNotifyTargets(HWND hwnd, Window iWindow, Display *pDisplay, ClipboardConversionData *data, ClipboardAtoms *atoms)
143{
144  Atom type;
145  int format;
146  unsigned long nitems;
147  unsigned long after;
148  Atom *prop;
149
150  /* Retrieve the selection data and delete the property */
151  int iReturn = XGetWindowProperty(pDisplay,
152                                   iWindow,
153                                   atoms->atomLocalProperty,
154                                   0,
155                                   INT_MAX,
156                                   True,
157                                   AnyPropertyType,
158                                   &type,
159                                   &format,
160                                   &nitems,
161                                   &after,
162                                   (unsigned char **)&prop);
163  if (iReturn != Success) {
164    ErrorF("winClipboardFlushXEvents - SelectionNotify - "
165           "XGetWindowProperty () failed, aborting: %d\n", iReturn);
166  } else {
167    int i;
168    data->targetList = malloc((nitems+1)*sizeof(Atom));
169
170    for (i = 0; i < nitems; i++)
171      {
172        Atom atom = prop[i];
173        char *pszAtomName = XGetAtomName(pDisplay, atom);
174        data->targetList[i] = atom;
175        winDebug("winClipboardFlushXEvents - SelectionNotify - target[%d] %ld = %s\n", i, atom, pszAtomName);
176        XFree(pszAtomName);
177      }
178
179    data->targetList[nitems] = 0;
180
181    XFree(prop);
182  }
183
184  return WIN_XEVENTS_NOTIFY_TARGETS;
185}
186
187/*
188 * Process any pending X events
189 */
190
191int
192winClipboardFlushXEvents(HWND hwnd,
193                         Window iWindow, Display * pDisplay, ClipboardConversionData *data, ClipboardAtoms *atoms)
194{
195    Atom atomClipboard = atoms->atomClipboard;
196    Atom atomLocalProperty = atoms->atomLocalProperty;
197    Atom atomUTF8String = atoms->atomUTF8String;
198    Atom atomCompoundText = atoms->atomCompoundText;
199    Atom atomTargets = atoms->atomTargets;
200
201    /* Process all pending events */
202    while (XPending(pDisplay)) {
203        XTextProperty xtpText = { 0 };
204        XEvent event;
205        XSelectionEvent eventSelection;
206        unsigned long ulReturnBytesLeft;
207        char *pszReturnData = NULL;
208        char *pszGlobalData = NULL;
209        int iReturn;
210        HGLOBAL hGlobal = NULL;
211        XICCEncodingStyle xiccesStyle;
212        char *pszConvertData = NULL;
213        char *pszTextList[2] = { NULL };
214        int iCount;
215        char **ppszTextList = NULL;
216        wchar_t *pwszUnicodeStr = NULL;
217        Bool fAbort = FALSE;
218        Bool fCloseClipboard = FALSE;
219        Bool fSetClipboardData = TRUE;
220
221        /* Get the next event - will not block because one is ready */
222        XNextEvent(pDisplay, &event);
223
224        /* Branch on the event type */
225        switch (event.type) {
226            /*
227             * SelectionRequest
228             */
229
230        case SelectionRequest:
231        {
232            char *pszAtomName = NULL;
233
234            winDebug("SelectionRequest - target %ld\n",
235                     event.xselectionrequest.target);
236
237            pszAtomName = XGetAtomName(pDisplay,
238                                       event.xselectionrequest.target);
239            winDebug("SelectionRequest - Target atom name %s\n", pszAtomName);
240            XFree(pszAtomName);
241            pszAtomName = NULL;
242        }
243
244            /* Abort if invalid target type */
245            if (event.xselectionrequest.target != XA_STRING
246                && event.xselectionrequest.target != atomUTF8String
247                && event.xselectionrequest.target != atomCompoundText
248                && event.xselectionrequest.target != atomTargets) {
249                /* Abort */
250                fAbort = TRUE;
251                goto winClipboardFlushXEvents_SelectionRequest_Done;
252            }
253
254            /* Handle targets type of request */
255            if (event.xselectionrequest.target == atomTargets) {
256                Atom atomTargetArr[] = { atomTargets,
257                    atomCompoundText,
258                    atomUTF8String,
259                    XA_STRING
260                };
261
262                /* Try to change the property */
263                iReturn = XChangeProperty(pDisplay,
264                                          event.xselectionrequest.requestor,
265                                          event.xselectionrequest.property,
266                                          XA_ATOM,
267                                          32,
268                                          PropModeReplace,
269                                          (unsigned char *) atomTargetArr,
270                                          (sizeof(atomTargetArr)
271                                           / sizeof(atomTargetArr[0])));
272                if (iReturn == BadAlloc
273                    || iReturn == BadAtom
274                    || iReturn == BadMatch
275                    || iReturn == BadValue || iReturn == BadWindow) {
276                    ErrorF("winClipboardFlushXEvents - SelectionRequest - "
277                           "XChangeProperty failed: %d\n", iReturn);
278                }
279
280                /* Setup selection notify xevent */
281                eventSelection.type = SelectionNotify;
282                eventSelection.send_event = True;
283                eventSelection.display = pDisplay;
284                eventSelection.requestor = event.xselectionrequest.requestor;
285                eventSelection.selection = event.xselectionrequest.selection;
286                eventSelection.target = event.xselectionrequest.target;
287                eventSelection.property = event.xselectionrequest.property;
288                eventSelection.time = event.xselectionrequest.time;
289
290                /*
291                 * Notify the requesting window that
292                 * the operation has completed
293                 */
294                iReturn = XSendEvent(pDisplay,
295                                     eventSelection.requestor,
296                                     False, 0L, (XEvent *) &eventSelection);
297                if (iReturn == BadValue || iReturn == BadWindow) {
298                    ErrorF("winClipboardFlushXEvents - SelectionRequest - "
299                           "XSendEvent () failed\n");
300                }
301                break;
302            }
303
304            /* Close clipboard if we have it open already */
305            if (GetOpenClipboardWindow() == hwnd) {
306                CloseClipboard();
307            }
308
309            /* Access the clipboard */
310            if (!OpenClipboard(hwnd)) {
311                ErrorF("winClipboardFlushXEvents - SelectionRequest - "
312                       "OpenClipboard () failed: %08x\n", (unsigned int)GetLastError());
313
314                /* Abort */
315                fAbort = TRUE;
316                goto winClipboardFlushXEvents_SelectionRequest_Done;
317            }
318
319            /* Indicate that clipboard was opened */
320            fCloseClipboard = TRUE;
321
322            /* Check that clipboard format is available */
323            if (data->fUseUnicode && !IsClipboardFormatAvailable(CF_UNICODETEXT)) {
324                static int count;       /* Hack to stop acroread spamming the log */
325                static HWND lasthwnd;   /* I've not seen any other client get here repeatedly? */
326
327                if (hwnd != lasthwnd)
328                    count = 0;
329                count++;
330                if (count < 6)
331                    ErrorF("winClipboardFlushXEvents - CF_UNICODETEXT is not "
332                           "available from Win32 clipboard.  Aborting %d.\n",
333                           count);
334                lasthwnd = hwnd;
335
336                /* Abort */
337                fAbort = TRUE;
338                goto winClipboardFlushXEvents_SelectionRequest_Done;
339            }
340            else if (!data->fUseUnicode && !IsClipboardFormatAvailable(CF_TEXT)) {
341                ErrorF("winClipboardFlushXEvents - CF_TEXT is not "
342                       "available from Win32 clipboard.  Aborting.\n");
343
344                /* Abort */
345                fAbort = TRUE;
346                goto winClipboardFlushXEvents_SelectionRequest_Done;
347            }
348
349            /* Setup the string style */
350            if (event.xselectionrequest.target == XA_STRING)
351                xiccesStyle = XStringStyle;
352#ifdef X_HAVE_UTF8_STRING
353            else if (event.xselectionrequest.target == atomUTF8String)
354                xiccesStyle = XUTF8StringStyle;
355#endif
356            else if (event.xselectionrequest.target == atomCompoundText)
357                xiccesStyle = XCompoundTextStyle;
358            else
359                xiccesStyle = XStringStyle;
360
361            /* Get a pointer to the clipboard text, in desired format */
362            if (data->fUseUnicode) {
363                /* Retrieve clipboard data */
364                hGlobal = GetClipboardData(CF_UNICODETEXT);
365            }
366            else {
367                /* Retrieve clipboard data */
368                hGlobal = GetClipboardData(CF_TEXT);
369            }
370            if (!hGlobal) {
371                ErrorF("winClipboardFlushXEvents - SelectionRequest - "
372                       "GetClipboardData () failed: %08x\n", (unsigned int)GetLastError());
373
374                /* Abort */
375                fAbort = TRUE;
376                goto winClipboardFlushXEvents_SelectionRequest_Done;
377            }
378            pszGlobalData = (char *) GlobalLock(hGlobal);
379
380            /* Convert the Unicode string to UTF8 (MBCS) */
381            if (data->fUseUnicode) {
382                int iConvertDataLen = WideCharToMultiByte(CP_UTF8,
383                                                      0,
384                                                      (LPCWSTR) pszGlobalData,
385                                                      -1, NULL, 0, NULL, NULL);
386                /* NOTE: iConvertDataLen includes space for null terminator */
387                pszConvertData = malloc(iConvertDataLen);
388                WideCharToMultiByte(CP_UTF8,
389                                    0,
390                                    (LPCWSTR) pszGlobalData,
391                                    -1,
392                                    pszConvertData,
393                                    iConvertDataLen, NULL, NULL);
394            }
395            else {
396                pszConvertData = strdup(pszGlobalData);
397            }
398
399            /* Convert DOS string to UNIX string */
400            winClipboardDOStoUNIX(pszConvertData, strlen(pszConvertData));
401
402            /* Setup our text list */
403            pszTextList[0] = pszConvertData;
404            pszTextList[1] = NULL;
405
406            /* Initialize the text property */
407            xtpText.value = NULL;
408            xtpText.nitems = 0;
409
410            /* Create the text property from the text list */
411            if (data->fUseUnicode) {
412#ifdef X_HAVE_UTF8_STRING
413                iReturn = Xutf8TextListToTextProperty(pDisplay,
414                                                      pszTextList,
415                                                      1, xiccesStyle, &xtpText);
416#endif
417            }
418            else {
419                iReturn = XmbTextListToTextProperty(pDisplay,
420                                                    pszTextList,
421                                                    1, xiccesStyle, &xtpText);
422            }
423            if (iReturn == XNoMemory || iReturn == XLocaleNotSupported) {
424                ErrorF("winClipboardFlushXEvents - SelectionRequest - "
425                       "X*TextListToTextProperty failed: %d\n", iReturn);
426
427                /* Abort */
428                fAbort = TRUE;
429                goto winClipboardFlushXEvents_SelectionRequest_Done;
430            }
431
432            /* Free the converted string */
433            free(pszConvertData);
434            pszConvertData = NULL;
435
436            /* Copy the clipboard text to the requesting window */
437            iReturn = XChangeProperty(pDisplay,
438                                      event.xselectionrequest.requestor,
439                                      event.xselectionrequest.property,
440                                      event.xselectionrequest.target,
441                                      8,
442                                      PropModeReplace,
443                                      xtpText.value, xtpText.nitems);
444            if (iReturn == BadAlloc || iReturn == BadAtom
445                || iReturn == BadMatch || iReturn == BadValue
446                || iReturn == BadWindow) {
447                ErrorF("winClipboardFlushXEvents - SelectionRequest - "
448                       "XChangeProperty failed: %d\n", iReturn);
449
450                /* Abort */
451                fAbort = TRUE;
452                goto winClipboardFlushXEvents_SelectionRequest_Done;
453            }
454
455            /* Release the clipboard data */
456            GlobalUnlock(hGlobal);
457            pszGlobalData = NULL;
458            fCloseClipboard = FALSE;
459            CloseClipboard();
460
461            /* Clean up */
462            XFree(xtpText.value);
463            xtpText.value = NULL;
464            xtpText.nitems = 0;
465
466            /* Setup selection notify event */
467            eventSelection.type = SelectionNotify;
468            eventSelection.send_event = True;
469            eventSelection.display = pDisplay;
470            eventSelection.requestor = event.xselectionrequest.requestor;
471            eventSelection.selection = event.xselectionrequest.selection;
472            eventSelection.target = event.xselectionrequest.target;
473            eventSelection.property = event.xselectionrequest.property;
474            eventSelection.time = event.xselectionrequest.time;
475
476            /* Notify the requesting window that the operation has completed */
477            iReturn = XSendEvent(pDisplay,
478                                 eventSelection.requestor,
479                                 False, 0L, (XEvent *) &eventSelection);
480            if (iReturn == BadValue || iReturn == BadWindow) {
481                ErrorF("winClipboardFlushXEvents - SelectionRequest - "
482                       "XSendEvent () failed\n");
483
484                /* Abort */
485                fAbort = TRUE;
486                goto winClipboardFlushXEvents_SelectionRequest_Done;
487            }
488
489 winClipboardFlushXEvents_SelectionRequest_Done:
490            /* Free allocated resources */
491            if (xtpText.value) {
492                XFree(xtpText.value);
493                xtpText.value = NULL;
494                xtpText.nitems = 0;
495            }
496            free(pszConvertData);
497            if (hGlobal && pszGlobalData)
498                GlobalUnlock(hGlobal);
499
500            /*
501             * Send a SelectionNotify event to the requesting
502             * client when we abort.
503             */
504            if (fAbort) {
505                /* Setup selection notify event */
506                eventSelection.type = SelectionNotify;
507                eventSelection.send_event = True;
508                eventSelection.display = pDisplay;
509                eventSelection.requestor = event.xselectionrequest.requestor;
510                eventSelection.selection = event.xselectionrequest.selection;
511                eventSelection.target = event.xselectionrequest.target;
512                eventSelection.property = None;
513                eventSelection.time = event.xselectionrequest.time;
514
515                /* Notify the requesting window that the operation is complete */
516                iReturn = XSendEvent(pDisplay,
517                                     eventSelection.requestor,
518                                     False, 0L, (XEvent *) &eventSelection);
519                if (iReturn == BadValue || iReturn == BadWindow) {
520                    /*
521                     * Should not be a problem if XSendEvent fails because
522                     * the client may simply have exited.
523                     */
524                    ErrorF("winClipboardFlushXEvents - SelectionRequest - "
525                           "XSendEvent () failed for abort event.\n");
526                }
527            }
528
529            /* Close clipboard if it was opened */
530            if (fCloseClipboard) {
531                fCloseClipboard = FALSE;
532                CloseClipboard();
533            }
534            break;
535
536            /*
537             * SelectionNotify
538             */
539
540        case SelectionNotify:
541            winDebug("winClipboardFlushXEvents - SelectionNotify\n");
542            {
543                char *pszAtomName;
544
545                pszAtomName = XGetAtomName(pDisplay,
546                                           event.xselection.selection);
547
548                winDebug
549                    ("winClipboardFlushXEvents - SelectionNotify - ATOM: %s\n",
550                     pszAtomName);
551                XFree(pszAtomName);
552            }
553
554            /*
555              SelectionNotify with property of None indicates either:
556
557              (i) Generated by the X server if no owner for the specified selection exists
558                  (perhaps it's disappeared on us mid-transaction), or
559              (ii) Sent by the selection owner when the requested selection conversion could
560                   not be performed or server errors prevented the conversion data being returned
561            */
562            if (event.xselection.property == None) {
563                    ErrorF("winClipboardFlushXEvents - SelectionNotify - "
564                           "Conversion to format %ld refused.\n",
565                           event.xselection.target);
566                    return WIN_XEVENTS_FAILED;
567                }
568
569            if (event.xselection.target == atomTargets) {
570              return winClipboardSelectionNotifyTargets(hwnd, iWindow, pDisplay, data, atoms);
571            }
572
573            /* Retrieve the selection data and delete the property */
574            iReturn = XGetWindowProperty(pDisplay,
575                                         iWindow,
576                                         atomLocalProperty,
577                                         0,
578                                         INT_MAX,
579                                         True,
580                                         AnyPropertyType,
581                                         &xtpText.encoding,
582                                         &xtpText.format,
583                                         &xtpText.nitems,
584                                         &ulReturnBytesLeft, &xtpText.value);
585            if (iReturn != Success) {
586                ErrorF("winClipboardFlushXEvents - SelectionNotify - "
587                       "XGetWindowProperty () failed, aborting: %d\n", iReturn);
588                goto winClipboardFlushXEvents_SelectionNotify_Done;
589            }
590
591            {
592                char *pszAtomName = NULL;
593
594                winDebug("SelectionNotify - returned data %lu left %lu\n",
595                         xtpText.nitems, ulReturnBytesLeft);
596                pszAtomName = XGetAtomName(pDisplay, xtpText.encoding);
597                winDebug("Notify atom name %s\n", pszAtomName);
598                XFree(pszAtomName);
599                pszAtomName = NULL;
600            }
601
602            if (data->fUseUnicode) {
603#ifdef X_HAVE_UTF8_STRING
604                /* Convert the text property to a text list */
605                iReturn = Xutf8TextPropertyToTextList(pDisplay,
606                                                      &xtpText,
607                                                      &ppszTextList, &iCount);
608#endif
609            }
610            else {
611                iReturn = XmbTextPropertyToTextList(pDisplay,
612                                                    &xtpText,
613                                                    &ppszTextList, &iCount);
614            }
615            if (iReturn == Success || iReturn > 0) {
616                /* Conversion succeeded or some unconvertible characters */
617                if (ppszTextList != NULL) {
618                    int i;
619                    int iReturnDataLen = 0;
620                    for (i = 0; i < iCount; i++) {
621                        iReturnDataLen += strlen(ppszTextList[i]);
622                    }
623                    pszReturnData = malloc(iReturnDataLen + 1);
624                    pszReturnData[0] = '\0';
625                    for (i = 0; i < iCount; i++) {
626                        strcat(pszReturnData, ppszTextList[i]);
627                    }
628                }
629                else {
630                    ErrorF("winClipboardFlushXEvents - SelectionNotify - "
631                           "X*TextPropertyToTextList list_return is NULL.\n");
632                    pszReturnData = malloc(1);
633                    pszReturnData[0] = '\0';
634                }
635            }
636            else {
637                ErrorF("winClipboardFlushXEvents - SelectionNotify - "
638                       "X*TextPropertyToTextList returned: ");
639                switch (iReturn) {
640                case XNoMemory:
641                    ErrorF("XNoMemory\n");
642                    break;
643                case XLocaleNotSupported:
644                    ErrorF("XLocaleNotSupported\n");
645                    break;
646                case XConverterNotFound:
647                    ErrorF("XConverterNotFound\n");
648                    break;
649                default:
650                    ErrorF("%d\n", iReturn);
651                    break;
652                }
653                pszReturnData = malloc(1);
654                pszReturnData[0] = '\0';
655            }
656
657            /* Free the data returned from XGetWindowProperty */
658            if (ppszTextList)
659                XFreeStringList(ppszTextList);
660            ppszTextList = NULL;
661            XFree(xtpText.value);
662            xtpText.value = NULL;
663            xtpText.nitems = 0;
664
665            /* Convert the X clipboard string to DOS format */
666            winClipboardUNIXtoDOS(&pszReturnData, strlen(pszReturnData));
667
668            if (data->fUseUnicode) {
669                /* Find out how much space needed to convert MBCS to Unicode */
670                int iUnicodeLen = MultiByteToWideChar(CP_UTF8,
671                                                  0,
672                                                  pszReturnData, -1, NULL, 0);
673
674                /* NOTE: iUnicodeLen includes space for null terminator */
675                pwszUnicodeStr = malloc(sizeof(wchar_t) * iUnicodeLen);
676                if (!pwszUnicodeStr) {
677                    ErrorF("winClipboardFlushXEvents - SelectionNotify "
678                           "malloc failed for pwszUnicodeStr, aborting.\n");
679
680                    /* Abort */
681                    fAbort = TRUE;
682                    goto winClipboardFlushXEvents_SelectionNotify_Done;
683                }
684
685                /* Do the actual conversion */
686                MultiByteToWideChar(CP_UTF8,
687                                    0,
688                                    pszReturnData,
689                                    -1, pwszUnicodeStr, iUnicodeLen);
690
691                /* Allocate global memory for the X clipboard data */
692                hGlobal = GlobalAlloc(GMEM_MOVEABLE,
693                                      sizeof(wchar_t) * iUnicodeLen);
694            }
695            else {
696                int iConvertDataLen = 0;
697                pszConvertData = strdup(pszReturnData);
698                iConvertDataLen = strlen(pszConvertData) + 1;
699
700                /* Allocate global memory for the X clipboard data */
701                hGlobal = GlobalAlloc(GMEM_MOVEABLE, iConvertDataLen);
702            }
703
704            free(pszReturnData);
705
706            /* Check that global memory was allocated */
707            if (!hGlobal) {
708                ErrorF("winClipboardFlushXEvents - SelectionNotify "
709                       "GlobalAlloc failed, aborting: %08x\n", (unsigned int)GetLastError());
710
711                /* Abort */
712                fAbort = TRUE;
713                goto winClipboardFlushXEvents_SelectionNotify_Done;
714            }
715
716            /* Obtain a pointer to the global memory */
717            pszGlobalData = GlobalLock(hGlobal);
718            if (pszGlobalData == NULL) {
719                ErrorF("winClipboardFlushXEvents - Could not lock global "
720                       "memory for clipboard transfer\n");
721
722                /* Abort */
723                fAbort = TRUE;
724                goto winClipboardFlushXEvents_SelectionNotify_Done;
725            }
726
727            /* Copy the returned string into the global memory */
728            if (data->fUseUnicode) {
729                wcscpy((wchar_t *)pszGlobalData, pwszUnicodeStr);
730                free(pwszUnicodeStr);
731                pwszUnicodeStr = NULL;
732            }
733            else {
734                strcpy(pszGlobalData, pszConvertData);
735                free(pszConvertData);
736                pszConvertData = NULL;
737            }
738
739            /* Release the pointer to the global memory */
740            GlobalUnlock(hGlobal);
741            pszGlobalData = NULL;
742
743            /* Push the selection data to the Windows clipboard */
744            if (data->fUseUnicode)
745                SetClipboardData(CF_UNICODETEXT, hGlobal);
746            else
747                SetClipboardData(CF_TEXT, hGlobal);
748
749            /* Flag that SetClipboardData has been called */
750            fSetClipboardData = FALSE;
751
752            /*
753             * NOTE: Do not try to free pszGlobalData, it is owned by
754             * Windows after the call to SetClipboardData ().
755             */
756
757 winClipboardFlushXEvents_SelectionNotify_Done:
758            /* Free allocated resources */
759            if (ppszTextList)
760                XFreeStringList(ppszTextList);
761            if (xtpText.value) {
762                XFree(xtpText.value);
763                xtpText.value = NULL;
764                xtpText.nitems = 0;
765            }
766            free(pszConvertData);
767            free(pwszUnicodeStr);
768            if (hGlobal && pszGlobalData)
769                GlobalUnlock(hGlobal);
770            if (fSetClipboardData) {
771                SetClipboardData(CF_UNICODETEXT, NULL);
772                SetClipboardData(CF_TEXT, NULL);
773            }
774            return WIN_XEVENTS_NOTIFY_DATA;
775
776        case SelectionClear:
777            winDebug("SelectionClear - doing nothing\n");
778            break;
779
780        case PropertyNotify:
781            break;
782
783        case MappingNotify:
784            break;
785
786        default:
787            if (event.type == XFixesSetSelectionOwnerNotify + xfixes_event_base) {
788                XFixesSelectionNotifyEvent *e =
789                    (XFixesSelectionNotifyEvent *) & event;
790
791                winDebug("winClipboardFlushXEvents - XFixesSetSelectionOwnerNotify\n");
792
793                /* Save selection owners for monitored selections, ignore other selections */
794                if ((e->selection == XA_PRIMARY) && fPrimarySelection) {
795                    MonitorSelection(e, CLIP_OWN_PRIMARY);
796                }
797                else if (e->selection == atomClipboard) {
798                    MonitorSelection(e, CLIP_OWN_CLIPBOARD);
799                }
800                else
801                    break;
802
803                /* Selection is being disowned */
804                if (e->owner == None) {
805                    winDebug
806                        ("winClipboardFlushXEvents - No window, returning.\n");
807                    break;
808                }
809
810                /*
811                   XXX: there are all kinds of wacky edge cases we might need here:
812                   - we own windows clipboard, but neither PRIMARY nor CLIPBOARD have an owner, so we should disown it?
813                   - root window is taking ownership?
814                 */
815
816                /* If we are the owner of the most recently owned selection, don't go all recursive :) */
817                if ((lastOwnedSelectionIndex != CLIP_OWN_NONE) &&
818                    (s_iOwners[lastOwnedSelectionIndex] == iWindow)) {
819                    winDebug("winClipboardFlushXEvents - Ownership changed to us, aborting.\n");
820                    break;
821                }
822
823                /* Close clipboard if we have it open already (possible? correct??) */
824                if (GetOpenClipboardWindow() == hwnd) {
825                    CloseClipboard();
826                }
827
828                /* Access the Windows clipboard */
829                if (!OpenClipboard(hwnd)) {
830                    ErrorF("winClipboardFlushXEvents - OpenClipboard () failed: %08x\n",
831                           (int) GetLastError());
832                    break;
833                }
834
835                /* Take ownership of the Windows clipboard */
836                if (!EmptyClipboard()) {
837                    ErrorF("winClipboardFlushXEvents - EmptyClipboard () failed: %08x\n",
838                           (int) GetLastError());
839                    break;
840                }
841
842                /* Advertise regular text and unicode */
843                SetClipboardData(CF_UNICODETEXT, NULL);
844                SetClipboardData(CF_TEXT, NULL);
845
846                /* Release the clipboard */
847                if (!CloseClipboard()) {
848                    ErrorF("winClipboardFlushXEvents - CloseClipboard () failed: %08x\n",
849                           (int) GetLastError());
850                    break;
851                }
852            }
853            /* XFixesSelectionWindowDestroyNotifyMask */
854            /* XFixesSelectionClientCloseNotifyMask */
855            else {
856                ErrorF("winClipboardFlushXEvents - unexpected event type %d\n",
857                       event.type);
858            }
859            break;
860        }
861    }
862
863    return WIN_XEVENTS_SUCCESS;
864}
865